from PyQt6.QtWidgets import QApplication, QMainWindow, QFileDialog,QTableWidgetItem, QMessageBox
from PyQt6 import uic
import PyQt6.QtCore as QtCore
import PyQt6.QtGui as QtGui
from pathlib import Path
import sys, traceback
import autodeer as ad
import numbers
import numpy as np
import datetime
# =============================================================================
#
# General functions and classes
#
# =============================================================================
[docs]
def create_save_name(SampleName:str, experiment:str, timestamp=True, ProjectName=None, Comment=None):
timestamp = datetime.datetime.now().strftime(r'%Y%m%d_%H%M_')
fullname = f"{timestamp}"
if ProjectName is not None or ProjectName != "":
fullname += f"_({ProjectName})"
fullname += f"_{SampleName}_{experiment}"
if Comment is not None or Comment != "":
fullname += f"_{Comment}"
return fullname
[docs]
def getCIstring(Uncert, precision=2):
try:
return f"({Uncert.ci(95)[0]:.{precision}f},{Uncert.ci(95)[1]:.{precision}f})"
except ValueError:
return "frozen"
[docs]
def load_epr_file(Object, store_location):
filename, _= QFileDialog.getOpenFileName(
Object,"Select a File", Object.current_folder,"Data (*.DTA *.mat *.h5)")
if filename:
path = Path(filename)
filename_edit = str(path)
dataset = ad.eprload(filename_edit)
Object.current_data[store_location] = dataset
[docs]
def get_sequence_rows(Sequence, names:list):
rows = []
for param in names:
attr = getattr(Sequence, param)
if isinstance(attr, ad.Parameter):
unit = attr.unit
else:
unit = None
if isinstance(attr, ad.Parameter):
rows.append({"Parameter":param, "Value":f"{attr.value:.2f}", "Unit":unit})
else:
rows.append({"Parameter":param, "Value":f"{attr:.2f}", "Unit":None})
[docs]
def fill_table(table, headers, rows, rowcount=None):
if rowcount is None:
table.setRowCount(len(rows))
else:
table.setRowCount(rowcount)
# Fill table from dictionary rows
for i, row in enumerate(rows):
for j, key in enumerate(headers):
if key in row.keys():
entry = row[key]
if isinstance(entry, numbers.Number):
item = QTableWidgetItem(f"{entry:.3f}")
elif isinstance(entry, np.ndarray):
item = QTableWidgetItem(np.array2string(entry, precision=3, separator=',',suppress_small=True,threshold=4))
else:
item = QTableWidgetItem(str(row[key]))
table.setItem(i, j, item)
else:
item = QTableWidgetItem('')
table.setItem(i, j, item)
[docs]
def read_table(table):
rows = table.rowCount()
columns = table.columnCount()
headers = [str(table.horizontalHeaderItem(i).text()) for i in range(columns)]
data = {}
for row in range(rows):
row_data = {}
for col in range(columns):
try:
row_data[headers[col]] = table.item(row,col).data()
except TypeError:
row_data[headers[col]] = table.item(row,col).text()
except AttributeError:
if col ==0:
break
row_data[headers[col]] = None
else:
data[row_data[headers[0]]] = row_data
return data
[docs]
def list_str_to_type(list, type=int):
new_list = []
for item in list:
new_list.append(type(item))
return new_list
[docs]
def str_to_list_type(string, type=int):
list = string.split(',')
new_list = []
for item in list:
new_list.append(type(item))
return new_list
[docs]
def pyqt_table_from_dict(table, dict):
headers = list(dict[list(dict.keys())[0]].keys())
rows = []
for key in dict.keys():
rows.append(dict[key])
fill_table(table, headers, rows)
[docs]
def param_in_us(param):
if not isinstance(param, ad.Parameter):
raise TypeError(f"Expected Parameter, got {type(param)}")
if param.unit == 'us':
return param.value
elif param.unit == 'ns':
return param.value/1000
[docs]
def param_in_MHz(param):
if not isinstance(param, ad.Parameter):
raise TypeError(f"Expected Parameter, got {type(param)}")
if param.unit == 'MHz':
return param.value
elif param.unit == 'GHz':
return param.value*1000
elif param.unit == 'kHz':
return param.value/1000
elif param.unit == 'Hz':
return param.value/1000000
[docs]
def test_SNR(Application, data):
"""Raises an error box if the SNR of the signal is less than 1.
Parameters
----------
data : _type_
_description_
"""
if data.epr.SNR < 1:
QMessageBox.about(Application,'ERORR!', 'Signal to Noise ratio is less than 1. Please check the data and try again.')
return False
else:
return True
# =============================================================================
#
# Multi-threading functions and classes
#
# =============================================================================
[docs]
class WorkerSignals(QtCore.QObject):
'''
Defines the signals available from a running worker thread.
Supported signals are:
finished
No data
error
tuple (exctype, value, traceback.format_exc() )
result
object data returned from processing, anything
progress
int indicating % progress
'''
[docs]
finished = QtCore.pyqtSignal()
[docs]
error = QtCore.pyqtSignal(tuple)
[docs]
result = QtCore.pyqtSignal(object)
[docs]
class Worker(QtCore.QRunnable):
'''
Worker thread
Inherits from QRunnable to handler worker thread setup, signals and wrap-up.
:param callback: The function callback to run on this worker thread. Supplied args and
kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function
'''
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
# Store constructor arguments (re-used for processing)
[docs]
self.signals = WorkerSignals()
# # Add the callback to our kwargs
# self.kwargs['progress_callback'] = self.signals.progress
@QtCore.pyqtSlot()
[docs]
def run(self):
'''
Initialise the runner function with passed args, kwargs.
'''
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
else:
self.signals.result.emit(result) # Return the result of the processing
finally:
self.signals.finished.emit() # Done