diff --git a/PythonGUI_apps/DataBrowser.py b/PythonGUI_apps/DataBrowser.py index 250e42a..3dea4ec 100644 --- a/PythonGUI_apps/DataBrowser.py +++ b/PythonGUI_apps/DataBrowser.py @@ -19,6 +19,9 @@ from PLQE_analysis import plqe_analysis from H5_Pkl import h5_pkl_view, h5_view_and_plot from Image_analysis import Image_analysis +from Table import Table_widget +from Export_Windows import Multi_Trace_Exporter + pg.mkQApp() #pg.setConfigOption('background', 'w') @@ -38,7 +41,8 @@ def __init__(self): self.ui = WindowTemplate() self.ui.setupUi(self) self.ui.select_comboBox.addItems(["Lifetime Analysis", "Spectrum Analysis", "FLIM Analysis", - "UV-Vis Analysis", "PLQE Analysis", "H5 View/Plot", "H5/PKL Viewer", "Image Analysis"]) + "UV-Vis Analysis", "PLQE Analysis", "H5 View/Plot", "H5/PKL Viewer", "Image Analysis", "Table View", + "Mulit-Trace Exporter"]) self.ui.load_pushButton.clicked.connect(self.load_app) self.show() @@ -72,7 +76,12 @@ def load_app(self): elif analysis_software == "Image Analysis": self.image_window = Image_analysis.MainWindow() self.image_window.show() - + elif analysis_software == "Table View": + self.table_widget = Table_widget.MainWindow() + self.table_widget.show() + elif analysis_software == "Mulit-Trace Exporter": + self.trace_exporter = Multi_Trace_Exporter.MainWindow() + self.trace_exporter.show() def run(): diff --git a/PythonGUI_apps/DataBrowser.spec b/PythonGUI_apps/DataBrowser.spec index 372f922..1c2b037 100644 --- a/PythonGUI_apps/DataBrowser.spec +++ b/PythonGUI_apps/DataBrowser.spec @@ -1,10 +1,10 @@ -# -*- mode: python -*- +# -*- mode: python ; coding: utf-8 -*- block_cipher = None -a = Analysis(['C:\\Users\\lindat18\\Dropbox\\Ginger_Lab\\Data_Analysis\\PythonGUI_apps\\DataBrowser.py'], - pathex=['C:\\Users\\lindat18\\Dropbox\\Ginger_Lab\\Data_Analysis\\PythonGUI_apps'], +a = Analysis(['DataBrowser.py'], + pathex=['E:\\QT_projects\\Python_GUI_apps\\PythonGUI_apps'], binaries=[], datas=[], hiddenimports=['ipykernel.datapub'], @@ -33,4 +33,5 @@ coll = COLLECT(exe, a.datas, strip=False, upx=True, + upx_exclude=[], name='DataBrowser') diff --git a/PythonGUI_apps/Export_Windows/Export_window.py b/PythonGUI_apps/Export_Windows/Export_window.py new file mode 100644 index 0000000..0e5cc63 --- /dev/null +++ b/PythonGUI_apps/Export_Windows/Export_window.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" +Created on Sun Nov 3 20:43:12 2019 + +@author: sarth +""" +from pathlib import Path +import pyqtgraph as pg +from pyqtgraph.Qt import QtCore, QtGui + +"""Export Images GUI""" +base_path = Path(__file__).parent +ui_file_path = (base_path / "export_fig_gui.ui").resolve() +exportFig_WindowTemplate, exportFig_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) + +class ExportFigureWindow(exportFig_TemplateBaseClass): + + export_fig_signal = QtCore.pyqtSignal() + + def __init__(self): + exportFig_TemplateBaseClass.__init__(self) + + self.ui = exportFig_WindowTemplate() + self.ui.setupUi(self) + self.ui.cmap_comboBox.addItems(['viridis', 'plasma', 'inferno', 'magma', + 'cividis','Greys', 'Purples', 'Blues', + 'Greens', 'Oranges', 'Reds', 'YlOrBr', + 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', + 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', + 'YlGn', 'binary', 'gist_yarg', 'gist_gray', + 'gray', 'bone', 'pink', 'spring', 'summer', + 'autumn', 'winter', 'cool', 'Wistia', 'hot', + 'afmhot', 'gist_heat', 'copper', 'rainbow', 'jet']) + self.ui.cbar_checkBox.stateChanged.connect(self.cbar_title_state) + self.ui.exportFig_pushButton.clicked.connect(self.export) + self.show() + + def cbar_title_state(self): + if self.ui.cbar_checkBox.isChecked(): + self.ui.cbar_label.setEnabled(True) + else: + self.ui.cbar_label.setEnabled(False) + + def export(self): + self.export_fig_signal.emit() + self.close() + +"""Export plot GUI""" +ui_file_path = (base_path / "export_plot.ui").resolve() +export_WindowTemplate, export_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) + +class ExportPlotWindow(export_TemplateBaseClass): + + export_fig_signal = QtCore.pyqtSignal() + + def __init__(self): + export_TemplateBaseClass.__init__(self) + + self.ui = export_WindowTemplate() + self.ui.setupUi(self) + #self.ui.traceColor_comboBox.addItems(["C0","C1","C2","C3","C4","C5","C6","C7", "r", "g", "b", "y", "k"]) + #self.ui.fitColor_comboBox.addItems(["k", "r", "b", "y", "g","C0","C1","C2","C3","C4","C5","C6","C7"]) + self.ui.export_pushButton.clicked.connect(self.export) + #self.ui.legend_checkBox.stateChanged.connect(self.legend_title) + self.show() + + #def legend_title(self): + # if self.ui.legend_checkBox.isChecked(): + # self.ui.legend1_lineEdit.setEnabled(True) + # self.ui.legend2_lineEdit.setEnabled(True) + # else: + # self.ui.legend1_lineEdit.setEnabled(False) + # self.ui.legend2_lineEdit.setEnabled(False) + + def export(self): + self.export_fig_signal.emit() + self.close() \ No newline at end of file diff --git a/PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.py b/PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.py new file mode 100644 index 0000000..5d5555d --- /dev/null +++ b/PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.py @@ -0,0 +1,147 @@ +import pyqtgraph as pg +from pathlib import Path +from pyqtgraph.Qt import QtCore, QtGui, QtWidgets +try: + from Lifetime_analysis.read_ph_phd import read_picoharp_phd, get_x_y +except Exception as e: + print(e) +import matplotlib.pyplot as plt + +"""Recylce params for plotting""" +plt.rc('xtick', labelsize = 20) +plt.rc('xtick.major', pad = 3) +plt.rc('ytick', labelsize = 20) +plt.rc('lines', lw = 2.5, markersize = 7.5) +plt.rc('legend', fontsize = 20) +plt.rc('axes', linewidth=3.5) + +pg.mkQApp() + +base_path = Path(__file__).parent +file_path = (base_path / "Multi_Trace_Exporter.ui").resolve() + +uiFile = file_path + +WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) + +class MainWindow(TemplateBaseClass): + + def __init__(self): + super(TemplateBaseClass, self).__init__() + + # Create the main window + self.ui = WindowTemplate() + self.ui.setupUi(self) + + self.temp_layout = pg.GraphicsLayoutWidget() + + # file system tree + self.fs_model = QtWidgets.QFileSystemModel() + self.fs_model.setRootPath(QtCore.QDir.currentPath()) + self.ui.treeView.setModel(self.fs_model) + self.ui.treeView.setIconSize(QtCore.QSize(25,25)) + self.ui.treeView.setSortingEnabled(True) + + self.tree_selectionModel = self.ui.treeView.selectionModel() + self.tree_selectionModel.selectionChanged.connect(self.on_treeview_selection_change) + + self.ui.comboBox.currentIndexChanged.connect(self.add_trace_to_temp_plot) + self.ui.add_pushButton.clicked.connect(self.add_trace_to_mem) + self.ui.export_pushButton.clicked.connect(self.pub_ready_plot_export) + + self.x_i = [] + self.y_i = [] + self.x_mem = [] + self.y_mem = [] + self.legend = [] + + self.show() + + def on_treeview_selection_change(self): + try: + fname = self.fs_model.filePath(self.tree_selectionModel.currentIndex()) + _ , ext = fname.rsplit('.',1) + + self.ui.comboBox.clear() + self.ui.textBrowser.clear() + self.x_i = [] + self.y_i = [] + + if ext in ['phd']: + self.parser = read_picoharp_phd(fname) + curve_list = [] + + for i in range(self.parser.no_of_curves()): + curve_list.append("Curve "+str(i)) + x, y = get_x_y(i, self.parser, smooth_trace=self.ui.smooth_checkBox.isChecked(), boxwidth=self.ui.smooth_spinBox.value()) + self.x_i.append(x) + self.y_i.append(y) + + self.ui.comboBox.addItems(curve_list) + self.ui.textBrowser.setText(str(self.parser.info())) + + else: + self.ui.textBrowser.setText(str("Select a PicoHarp File")) + except Exception as e: + print(e) + + def add_trace_to_temp_plot(self): + try: + #self.temp_layout = pg.GraphicsLayoutWidget() + self.temp_layout.clear() + self.temp_plot = self.temp_layout.addPlot(title = "Current Selection") + self.temp_plot.plot(self.x_i[self.ui.comboBox.currentIndex()], self.y_i[self.ui.comboBox.currentIndex()], pen='r') + self.temp_plot.setLogMode(False, True) + self.temp_layout.show() + except Exception as e: + print(e) + + def add_trace_to_mem(self): + try: + self.x_mem.append(self.x_i[self.ui.comboBox.currentIndex()]) + self.y_mem.append(self.y_i[self.ui.comboBox.currentIndex()]) + self.legend.append(self.ui.lineEdit.text()) + except Exception as e: + print(e) + + def pub_ready_plot_export(self): + try: + filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") + + plt.figure(figsize=(8,6)) + plt.tick_params(direction='out', length=8, width=3.5) + for i in range(len(self.x_mem)): + if self.ui.Normalize_checkBox.isChecked(): + plt.plot(self.x_mem[i], self.y_mem[i]/max(self.y_mem[i]), label=str(self.legend[i])) + else: + plt.plot(self.x_mem[i], self.y_mem[i], label=str(self.legend[i])) + + plt.yscale('log') + plt.xlabel("Time (ns)", fontsize=20, fontweight='bold') + plt.ylabel("Intensity (norm.)", fontsize=20, fontweight='bold') + plt.legend() + plt.tight_layout() + + plt.savefig(filename[0],bbox_inches='tight', dpi=300) + plt.close() + + self.clear_memory() + + except Exception as e: + print(e) + pass + + def clear_memory(self): + self.x_mem = [] + self.y_mem = [] + self.legend = [] + + + + +def run(): + win = MainWindow() + QtGui.QApplication.instance().exec_() + return win + +#run() \ No newline at end of file diff --git a/PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.ui b/PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.ui new file mode 100644 index 0000000..7b59ef1 --- /dev/null +++ b/PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.ui @@ -0,0 +1,85 @@ + + + MainWindow + + + + 0 + 0 + 1108 + 1063 + + + + MainWindow + + + + + + + + + + Smoothen Trace + + + + + + + Enter Trace Legend Here + + + + + + + Add + + + + + + + Normalize (for export) + + + + + + + 1 + + + + + + + Export + + + + + + + + + + + + + + + 0 + 0 + 1108 + 38 + + + + + + + + diff --git a/PythonGUI_apps/Export_Windows/__init__.py b/PythonGUI_apps/Export_Windows/__init__.py new file mode 100644 index 0000000..7c68785 --- /dev/null +++ b/PythonGUI_apps/Export_Windows/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- \ No newline at end of file diff --git a/PythonGUI_apps/Export_Windows/export_fig_gui.ui b/PythonGUI_apps/Export_Windows/export_fig_gui.ui new file mode 100644 index 0000000..e80e065 --- /dev/null +++ b/PythonGUI_apps/Export_Windows/export_fig_gui.ui @@ -0,0 +1,171 @@ + + + ExportFigure + + + + 0 + 0 + 420 + 369 + + + + Form + + + + + + + 15 + + + + 1000000000 + + + + + + + + 15 + + + + Color Bar Label + + + + + + + + 15 + + + + Data Channel to Save + + + + + + + + 15 + + + + + + + + + 15 + + + + + + + + + 15 + + + + ColorMap + + + + + + + + 15 + + + + 1000000000 + + + + + + + + 15 + + + + ColorBar Min + + + + + + + + 15 + + + + ColorBar Max + + + + + + + false + + + + 15 + + + + + + + + + 15 + + + + Export Figure + + + + + + + + 15 + + + + Reversed + + + + + + + + 15 + + + + + + + + + + + + diff --git a/PythonGUI_apps/Export_Windows/export_plot.ui b/PythonGUI_apps/Export_Windows/export_plot.ui new file mode 100644 index 0000000..840264f --- /dev/null +++ b/PythonGUI_apps/Export_Windows/export_plot.ui @@ -0,0 +1,186 @@ + + + Form + + + + 0 + 0 + 930 + 435 + + + + Form + + + + + + + 10 + + + + 4 + + + -100.000000000000000 + + + 10000000000000000000000.000000000000000 + + + 1.500000000000000 + + + + + + + + 10 + + + + Lower + + + + + + + + 10 + + + + Lower + + + + + + + + 10 + + + + Upper + + + + + + + + 15 + + + + Export Graph + + + + + + + + 12 + + + + Y limits + + + + + + + + 10 + + + + Upper + + + + + + + + 12 + + + + X limits + + + + + + + + 10 + + + + 4 + + + -10000.000000000000000 + + + 1000000000000000000.000000000000000 + + + 0.010000000000000 + + + + + + + + 10 + + + + 4 + + + -10000000.000000000000000 + + + 100000000000.000000000000000 + + + + + + + + 10 + + + + 4 + + + -1000000000.000000000000000 + + + 10000000000000.000000000000000 + + + 10000.000000000000000 + + + + + + + + diff --git a/PythonGUI_apps/FLIM_analysis/FLIM_plot.py b/PythonGUI_apps/FLIM_analysis/FLIM_plot.py index 8a0124f..5f5744f 100644 --- a/PythonGUI_apps/FLIM_analysis/FLIM_plot.py +++ b/PythonGUI_apps/FLIM_analysis/FLIM_plot.py @@ -13,8 +13,15 @@ sys.path.append(os.path.abspath('../Lifetime_analysis')) sys.path.append(os.path.abspath('../Spectrum_analysis')) +sys.path.append(os.path.abspath('../H5_Pkl')) +sys.path.append(os.path.abspath('../Export_Windows')) from Lifetime_analysis import Lifetime_plot_fit from Spectrum_analysis import Spectra_plot_fit +from H5_Pkl import h5_pkl_view +try: + from Export_window import ExportFigureWindow +except: + from Export_Windows.Export_window import ExportFigureWindow # local modules pg.mkQApp() @@ -48,7 +55,7 @@ def __init__(self): self.ui.load_scan_pushButton.clicked.connect(self.open_file) self.ui.plot_intensity_sums_pushButton.clicked.connect(self.plot_intensity_sums) self.ui.plot_raw_hist_data_pushButton.clicked.connect(self.plot_raw_scan) - self.ui.save_intensities_image_pushButton.clicked.connect(self.save_intensities_image) + self.ui.save_intensities_image_pushButton.clicked.connect(self.export_window) self.ui.save_intensities_array_pushButton.clicked.connect(self.save_intensities_array) self.ui.compare_checkBox.stateChanged.connect(self.switch_compare) self.ui.intensity_sums_viewBox.roi.sigRegionChanged.connect(self.line_profile_update_plot) @@ -62,17 +69,30 @@ def __init__(self): def open_file(self): """ Open FLIM scan file """ try: - self.filename = QtWidgets.QFileDialog.getOpenFileName(self, filter="Scan files (*.pkl *.h5)") + self.filename = QtWidgets.QFileDialog.getOpenFileName(self, filter="Scan files (*.pkl *.h5 *.txt)") if ".pkl" in self.filename[0]: self.flim_scan_file = pickle.load(open(self.filename[0], 'rb')) self.scan_file_type = "pkl" + self.launch_h5_pkl_viewer() + self.get_data_params() elif ".h5" in self.filename[0]: self.flim_scan_file = h5py.File(self.filename[0], 'r') self.scan_file_type = "h5" - self.get_data_params() + self.launch_h5_pkl_viewer() + self.get_data_params() + elif ".txt" in self.filename[0]: + self.intensity_sums = np.loadtxt(self.filename[0]).T + self.stepsize_window = StepSizeWindow() + self.stepsize_window.stepsize_signal.connect(self.get_stepsize) + self.scan_file_type = "txt" # self.pkl_file = pickle.load(open(self.filename[0], 'rb')) except Exception as err: print(format(err)) + + def launch_h5_pkl_viewer(self): + """ Launches H5/PKL viewer to give an insight into the data and its structure""" + viewer_window = h5_pkl_view.H5PklView(sys.argv) + viewer_window.settings['data_filename'] = self.filename[0] def import_pkl_to_convert(self): """ Open pkl file to convert to h5 """ @@ -81,6 +101,14 @@ def import_pkl_to_convert(self): self.ui.result_textBrowser.append("Done Loading - .pkl to convert") except: pass + + def get_stepsize(self): + """ Get step size from user input -- specfically written for loading + txt files from legacy labview code, but can also be run on txt file + saved using the new FLIM acquistion code """ + self.stepsize = self.stepsize_window.ui.stepsize_doubleSpinBox.value() + self.x_step_size = self.stepsize + self.y_step_size = self.stepsize def get_data_params(self): @@ -106,14 +134,19 @@ def get_data_params(self): def plot_intensity_sums(self): try: - self.hist_data = np.reshape(self.hist_data, newshape=(self.hist_data.shape[0], self.numb_x_pixels*self.numb_y_pixels)) - self.intensity_sums = np.sum(self.hist_data, axis=0) #sum intensities for each pixel - self.intensity_sums = np.reshape(self.intensity_sums, newshape=(self.numb_x_pixels, self.numb_y_pixels)) + if self.scan_file_type is "pkl" or self.scan_file_type is "h5": + pg.setConfigOption('imageAxisOrder', 'row-major') + self.hist_data = np.reshape(self.hist_data, newshape=(self.hist_data.shape[0], self.numb_x_pixels*self.numb_y_pixels)) + self.intensity_sums = np.sum(self.hist_data, axis=0) #sum intensities for each pixel + self.intensity_sums = np.reshape(self.intensity_sums, newshape=(self.numb_x_pixels, self.numb_y_pixels)) + else: + pg.setConfigOption('imageAxisOrder', 'col-major') self.ui.intensity_sums_viewBox.view.invertY(False) # stop y axis invert self.ui.intensity_sums_viewBox.setImage(self.intensity_sums, scale= (self.x_step_size, self.y_step_size)) - self.ui.intensity_sums_viewBox.roi.setSize([self.x_scan_size, self.y_step_size]) #line roi + if self.scan_file_type is "pkl" or self.scan_file_type is "h5": + self.ui.intensity_sums_viewBox.roi.setSize([self.x_scan_size, self.y_step_size]) #line roi scale = pg.ScaleBar(size=1,suffix='um') scale.setParentItem(self.ui.intensity_sums_viewBox.view) scale.anchor((1, 1), (1, 1), offset=(-30, -30)) @@ -234,6 +267,12 @@ def on_analyze_lifetime(self): self.lifetime_window.opened_from_flim = True self.lifetime_window.hist_data_from_flim = np.asarray(self.get_raw_hist_curve(0)) self.lifetime_window.ui.Result_textBrowser.setText("Data successfully loaded from FLIM analysis.") + + def export_window(self): + self.export_window = ExportFigureWindow() + self.export_window.ui.vmin_spinBox.setValue(np.min(self.intensity_sums)) + self.export_window.ui.vmax_spinBox.setValue(np.max(self.intensity_sums)) + self.export_window.export_fig_signal.connect(self.save_intensities_image) def save_intensities_image(self): try: @@ -241,7 +280,18 @@ def save_intensities_image(self): filename_ext = os.path.basename(self.filename[0]) filename = os.path.splitext(filename_ext)[0] #get filename without extension save_to = folder + "\\" + filename + "_intensity_sums.png" - cpm.plot_confocal(self.intensity_sums, FLIM_adjust=False, stepsize=np.abs(self.x_step_size)) + if self.export_window.ui.reverse_checkBox.isChecked(): + colormap = str(self.export_window.ui.cmap_comboBox.currentText())+"_r" + else: + colormap = str(self.export_window.ui.cmap_comboBox.currentText()) + if self.export_window.ui.cbar_checkBox.isChecked(): + label = str(self.export_window.ui.cbar_label.text()) + else: + label = "PL Intensity (a.u.)" + cpm.plot_confocal(self.intensity_sums, FLIM_adjust=False, + stepsize=np.abs(self.x_step_size),cmap=colormap, + cbar_label=label, vmin=self.export_window.ui.vmin_spinBox.value(), + vmax=self.export_window.ui.vmax_spinBox.value()) plt.savefig(save_to, bbox_inches='tight', dpi=300) except Exception as e: print(format(e)) @@ -292,6 +342,28 @@ def close_application(self): else: pass +"""Skip rows GUI""" +ui_file_path = (base_path / "step_size_labview_files.ui").resolve() +stepsize_WindowTemplate, stepsize_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) + +class StepSizeWindow(stepsize_TemplateBaseClass): + + stepsize_signal = QtCore.pyqtSignal() #signal to help with pass info back to MainWindow + + def __init__(self): + stepsize_TemplateBaseClass.__init__(self) + + # Create the param window + self.ui = stepsize_WindowTemplate() + self.ui.setupUi(self) + self.ui.done_pushButton.clicked.connect(self.done) + self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) + self.show() + + def done(self): + self.stepsize_signal.emit() + self.close() + """Run the Main Window""" def run(): win = MainWindow() diff --git a/PythonGUI_apps/FLIM_analysis/step_size_labview_files.ui b/PythonGUI_apps/FLIM_analysis/step_size_labview_files.ui new file mode 100644 index 0000000..2078fb7 --- /dev/null +++ b/PythonGUI_apps/FLIM_analysis/step_size_labview_files.ui @@ -0,0 +1,61 @@ + + + Form + + + + 0 + 0 + 255 + 75 + + + + Form + + + + + + + 12 + + + + Step Size (um) + + + + + + + Done! + + + + + + + + 12 + + + + 4 + + + 100.000000000000000 + + + 0.100000000000000 + + + 0.100000000000000 + + + + + + + + diff --git a/PythonGUI_apps/H5_Pkl/h5_tree.py b/PythonGUI_apps/H5_Pkl/h5_tree.py index c087f78..6d82566 100644 --- a/PythonGUI_apps/H5_Pkl/h5_tree.py +++ b/PythonGUI_apps/H5_Pkl/h5_tree.py @@ -40,6 +40,7 @@ def on_change_data_filename(self, fname=None): self.f = h5py.File(fname, 'r') self.on_new_search_text() self.databrowser.ui.statusbar.showMessage("") + return self.f except Exception as err: msg = "Failed to load %s:\n%s" %(fname, err) diff --git a/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py b/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py index 760cd46..18f3635 100644 --- a/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py +++ b/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py @@ -73,7 +73,7 @@ def on_change_data_filename(self): try: fname = self.settings.data_filename.val if os.path.isfile(fname): - self.h5treeview.on_change_data_filename(fname) + self.f = self.h5treeview.on_change_data_filename(fname) self.ui.dataview_placeholder.hide() self.h5treeview.ui.show() except: @@ -96,8 +96,12 @@ def plot_dataset(self): elif self.dataset_shape == 2 and self.ui.image_radioButton.isChecked(): self.data_img.setImage(data) elif self.dataset_shape == 3: - x_start = self.ui.imageView_x_start_spinBox.value() - x_end = self.ui.imageView_x_end_spinBox.value() + if self.f['Cube/Info/Cube'].attrs['AcqMode'] == b'Hyperspectral Acquisition': # This works for our PhotonEtc. Hyperspectral Camera output + x_start = int(self.f['Cube/Info/Cube'].attrs['LowerWavelength']) + x_end = int(self.f['Cube/Info/Cube'].attrs['UpperWavelength']) + else: + x_start = self.ui.imageView_x_start_spinBox.value() + x_end = self.ui.imageView_x_end_spinBox.value() num_points = self.dataset.shape[0] x_values = np.linspace(x_start, x_end, num_points) #scale x axis self.ui.data_imageView.setImage(data, xvals=x_values) diff --git a/PythonGUI_apps/H5_Pkl/pkl_tree.py b/PythonGUI_apps/H5_Pkl/pkl_tree.py index 3a24c76..e60a85e 100644 --- a/PythonGUI_apps/H5_Pkl/pkl_tree.py +++ b/PythonGUI_apps/H5_Pkl/pkl_tree.py @@ -3,6 +3,7 @@ import h5py import pickle import numpy as np +import lmfit class PklTreeSearchView(DataBrowserView): @@ -85,11 +86,13 @@ def traverse_dict(self, dictionary, previous_dict, level): self.tree_str += indent + "|> {}/
".format(print_string) level += 1 previous_dict = dictionary[key] - self.traverse_dict(dictionary[key], previous_dict, level) + self.traverse_dict(dictionary[key], previous_dict, level) else: value = dictionary[key] if type(value) == np.ndarray or type(value)==np.memmap: value = str(value.shape) + " " + str(value.dtype) + elif type(value) == lmfit.model.ModelResult: + value = "lmfit.model.ModelResult" # if type(value) == list and len(value) > 5: ##account for data stored in lists # value = str(np.asarray(value).shape) + " " + str(type(value[0])) diff --git a/PythonGUI_apps/Lifetime_analysis/Lifetime_analysis_gui_layout.ui b/PythonGUI_apps/Lifetime_analysis/Lifetime_analysis_gui_layout.ui index e80df57..24b97b0 100644 --- a/PythonGUI_apps/Lifetime_analysis/Lifetime_analysis_gui_layout.ui +++ b/PythonGUI_apps/Lifetime_analysis/Lifetime_analysis_gui_layout.ui @@ -6,8 +6,8 @@ 0 0 - 1490 - 1201 + 2620 + 1676 @@ -925,6 +925,23 @@ + + + + 4 + + + 9999999.000000000000000 + + + + + + + 0 + + + @@ -932,13 +949,23 @@ - + SRV (cm/s) + + + + 100000.000000000000000 + + + 8000.000000000000000 + + + @@ -946,58 +973,64 @@ - - + + - SRV1 = SRV2 + Surface Lifetime (ns) - - + + - Calculate + Average Lifetime (ns) - - - - 100000.000000000000000 - - - 8000.000000000000000 + + + + Calculate - - + + - Surface Lifetime (ns) + SRV (cm/s) - - + + 0 - - + + + + + 75 + true + + - Average Lifetime (ns) + SRV1 = SRV2 - - - - 4 + + + + + 75 + true + - - 9999999.000000000000000 + + SRV1 = 0 @@ -1038,32 +1071,24 @@ Export Settings - - + + - Export data + For Figure: - - - - - 12 - - + + - Save with Fit - - - true + Export Fit Data - + - Clear export data + Clear Export Memory @@ -1081,6 +1106,27 @@ + + + + For Data: + + + + + + + Enter Legend Here... + + + + + + + Add trace to memory + + + @@ -1181,6 +1227,9 @@ + + false + 12 @@ -1251,6 +1300,36 @@ + + + + + 12 + + + + Smooth Data + + + + + + + false + + + + 12 + + + + 1 + + + 1000 + + + @@ -1261,8 +1340,8 @@ 0 0 - 1490 - 21 + 2620 + 38 diff --git a/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py b/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py index fb9bca5..9437f9f 100644 --- a/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py +++ b/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py @@ -16,6 +16,12 @@ import numpy as np import matplotlib.pyplot as plt +sys.path.append(os.path.abspath('../Export_Windows')) +try: + from Export_window import ExportPlotWindow +except: + from Export_Windows.Export_window import ExportPlotWindow + # local module imports try: from Lifetime_analysis.Fit_functions import stretch_exp_fit, double_exp_fit, single_exp_fit @@ -67,7 +73,7 @@ def __init__(self): self.ui.plot_pushButton.clicked.connect(self.plot) self.ui.fit_pushButton.clicked.connect(self.call_fit_and_plot) self.ui.clear_pushButton.clicked.connect(self.clear_plot) - self.ui.export_plot_pushButton.clicked.connect(self.pub_ready_plot_export) + self.ui.export_plot_pushButton.clicked.connect(self.export_window)#pub_ready_plot_export self.ui.calculate_srv_pushButton.clicked.connect(self.calculate_srv) self.ui.log_checkBox.stateChanged.connect(self.make_semilog) @@ -75,8 +81,10 @@ def __init__(self): self.ui.FittingFunc_comboBox.currentTextChanged.connect(self.switch_function_tab) self.ui.FittingMethod_comboBox.currentTextChanged.connect(self.switch_init_params_groupBox) self.ui.separate_irf_checkBox.stateChanged.connect(self.switch_open_irf) + self.ui.add_to_mem_pushButton.clicked.connect(self.add_trace_to_mem) self.ui.export_data_pushButton.clicked.connect(self.export_data) self.ui.clear_export_data_pushButton.clicked.connect(self.clear_export_data) + self.ui.smoothData_checkBox.stateChanged.connect(self.smooth_trace_enabled) #set up plot color button self.plot_color_button = pg.ColorButton(color=(255,0,0)) @@ -87,6 +95,11 @@ def __init__(self): self.file = None self.out = None # output file after fitting self.data_list = [] + self.fit_lifetime_called = False + self.x_mem = [] # containers for adding x data to memory + self.y_mem = [] # containers for adding y data to memory + self.best_fit_mem = [] # containers for adding best fit data to memory + self.legend = [] # containers for adding legend to memory #variables accounting for data received from FLIM analysis self.opened_from_flim = False #switched to True in FLIM_plot when "analyze lifetime" clicked @@ -102,6 +115,7 @@ def open_file(self): if ".csv" in self.filename[0] or ".txt" in self.filename[0]: #if txt or csv, prompt user to enter # of rows to skip self.skip_rows_window = SkipRowsWindow() self.skip_rows_window.skip_rows_signal.connect(self.open_with_skip_rows_window) + self.ui.Res_comboBox.setEnabled(True) else: self.file = read_picoharp_phd(self.filename[0]) self.opened_from_flim = False @@ -123,6 +137,7 @@ def open_irf_file(self): if ".txt" in self.irf_filename[0] or ".csv" in self.irf_filename[0]: self.irf_skip_rows_window = SkipRowsWindow() self.irf_skip_rows_window.skip_rows_signal.connect(self.open_irf_with_skip_rows_window) + self.ui.Res_comboBox.setEnabled(True) else: self.irf_file = read_picoharp_phd(self.irf_filename[0]) except: @@ -187,13 +202,19 @@ def plot_color_changed(self): """ Grab new plot_color when color button value is changed """ self.plot_color = self.plot_color_button.color() + def smooth_trace_enabled(self): + """Enable smooth spin box when smooth data is checked""" + if self.ui.smoothData_checkBox.isChecked(): + self.ui.smoothData_spinBox.setEnabled(True) + else: + self.ui.smoothData_spinBox.setEnabled(False) + def acquire_settings(self, mode="data"): """ Acquire data or irf from channel specified in spinbox. mode -- string specifying whether to use data or irf channel (default "data") """ - self.resolution = float(self.ui.Res_comboBox.currentText()) if mode == "data": channel = int(self.ui.Data_channel_spinBox.value()) elif mode == "irf": @@ -207,14 +228,23 @@ def acquire_settings(self, mode="data"): y = self.irf_file.get_curve(channel)[1] else: #otherwise, get data/irf from data file y = self.file[:,channel] + + self.resolution = float(self.ui.Res_comboBox.currentText()) except: res, y = self.file.get_curve(channel) - # TO DO - check if res read in is the same as selected time_window = int(np.floor(self.file.get_time_window_in_ns(channel))) y = y[0:time_window] + self.resolution = res length = np.shape(y)[0] - x = np.arange(0, length, 1) * self.resolution + x = np.arange(0, length*self.resolution, self.resolution, np.float) + + if self.ui.smoothData_checkBox.isChecked() and mode=="data": + y = np.convolve(y, np.ones(self.ui.smoothData_spinBox.value())/self.ui.smoothData_spinBox.value(), mode="same") + + if self.ui.normalize_checkBox.isChecked(): + y = y / np.amax(y) + return x,y except Exception as e: @@ -226,10 +256,9 @@ def plot(self): x, y = self.hist_data_from_flim else: x,y = self.acquire_settings() #get data - if self.ui.normalize_checkBox.isChecked(): - y = y / np.amax(y) self.ui.plot.plot(x, y, clear=self.ui.clear_plot_checkBox.isChecked(), pen=pg.mkPen(self.plot_color)) + self.fit_lifetime_called = False try: self.ui.Result_textBrowser.setText("Integral Counts :\n" "{:.2E}".format( @@ -318,7 +347,8 @@ def fit_and_plot(self): #add fit params to data_list self.data_list.append("Data Channel: " + str(self.ui.Data_channel_spinBox.value()) + "\n" + self.ui.Result_textBrowser.toPlainText()) - + self.fit_lifetime_called = True + self.ui.plot.setLabel('left', 'Intensity', units='a.u.') self.ui.plot.setLabel('bottom', 'Time (ns)') return self.out @@ -415,6 +445,10 @@ def fit_and_plot_with_irf(self): "\ntau2 = %.5f ns" "\nnoise = %.5f counts" %(bestfit_params[0], bestfit_params[1], bestfit_params[2], bestfit_params[3], bestfit_params[4])) #TODO - once tau_avg implemented, set average lifetime spinbox to tau_avg value + if bestfit_params[3] > bestfit_params[1]: + self.ui.average_lifetime_spinBox.setValue(bestfit_params[3]) + elif bestfit_params[1] > bestfit_params[3]: + self.ui.average_lifetime_spinBox.setValue(bestfit_params[1]) elif fit_func == "Single Exponential": #single exponential tab a_bounds = (self.ui.se_a_min_spinBox.value(), self.ui.se_a_max_spinBox.value()) @@ -441,7 +475,7 @@ def fit_and_plot_with_irf(self): #add fit params to data_list self.data_list.append("Data Channel: " + str(self.ui.Data_channel_spinBox.value()) + "\n" + self.ui.Result_textBrowser.toPlainText()) - + self.fit_lifetime_called = True except Exception as e: self.ui.Result_textBrowser.append(format(e)) @@ -465,12 +499,11 @@ def calculate_srv (self): self.thickness = self.ui.thickness_spinBox.value()*1e-7 # convert to cm self.diffusion_coeffecient = self.ui.diffusion_coefficient_spinBox.value() # in cm2/s - if self.ui.srv1_srv2_checkBox.isChecked(): - self.srv = self.thickness / (2*((1e-9*self.surface_lifetime) - ((1/self.diffusion_coeffecient)*((self.thickness/np.pi)**2)) )) - else: - self.srv = self.thickness / ((1e-9*self.surface_lifetime) - ((4/self.diffusion_coeffecient)*((self.thickness/np.pi)**2)) ) + self.srv1_srv2_equal = self.thickness / (2*((1e-9*self.surface_lifetime) - ((1/self.diffusion_coeffecient)*((self.thickness/np.pi)**2)) )) + self.srv1_zero = self.thickness / ((1e-9*self.surface_lifetime) - ((4/self.diffusion_coeffecient)*((self.thickness/np.pi)**2)) ) - self.ui.srv_label.setText(str(self.srv)) + self.ui.srv1_srv2_equal_label.setText(str(self.srv1_srv2_equal)) + self.ui.srv1_zero_label.setText(str(self.srv1_zero)) def get_srv_string(self): """ Get info from SRV Calculation groupbox as string """ @@ -479,12 +512,13 @@ def get_srv_string(self): + "\nBulk Lifetime (ns): " + str(self.ui.bulk_lifetime_spinBox.value()) \ + "\nThickness (nm): " + str(self.ui.thickness_spinBox.value()) \ + "\nDiffusion Coefficient (cm2/s): " + str(self.ui.diffusion_coefficient_spinBox.value()) - if self.ui.srv1_srv2_checkBox.isChecked(): - srv_string += "\nSRV1 = SRV2" - else: - srv_string += "\nSRV1 = 0" - srv_string += "\nSurface Lifetime (ns): " + self.ui.surface_lifetime_label.text() \ - + "\nSRV (cm/s): " + self.ui.srv_label.text() + srv_string += "\nSurface Lifetime (ns): " + self.ui.surface_lifetime_label.text() + + srv_string += "\nSRV1 = SRV2"\ + + "\nSRV (cm/s): " + self.ui.srv1_srv2_equal_label.text() + + srv_string += "\nSRV1 = 0"\ + + "\nSRV (cm/s): " + self.ui.srv1_zero_label.text() return srv_string def export_data(self): @@ -507,27 +541,60 @@ def export_data(self): def clear_export_data(self): self.data_list = [] + self.clean_up_after_fig_export() + + def clean_up_after_fig_export(self): + self.x_mem = [] + self.y_mem = [] + self.legend = [] + self.best_fit_mem = [] + + def add_trace_to_mem(self): + try: + if self.fit_lifetime_called == True: + self.x_mem.append(self.out[:,0]) + self.y_mem.append(self.out[:,1]) + self.best_fit_mem.append(self.out[:,2]) + else: + self.x_mem.append(self.acquire_settings()[0]) + self.y_mem.append(self.acquire_settings()[1]) + self.legend.append(self.ui.lineEdit.text()) + except Exception as e: + print(e) + + def export_window(self): + self.exportplotwindow = ExportPlotWindow() + self.exportplotwindow.export_fig_signal.connect(self.pub_ready_plot_export) def pub_ready_plot_export(self): try: - filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") + if self.x_mem == []: + self.ui.result_textBrowser.setText("Add traces to memory first!") - plt.figure(figsize=(8,6)) - plt.tick_params(direction='out', length=8, width=3.5) - if self.ui.save_w_fit_checkBox.isChecked(): - plt.plot(self.out[:,0],self.out[:,1]/np.max(self.out[:,1])) - plt.plot(self.out[:,0],self.out[:,2]/np.max(self.out[:,1]),'k') else: - plt.plot(self.acquire_settings()[0],self.acquire_settings()[1]/np.max(self.acquire_settings()[1])) - plt.yscale('log') - plt.xlabel("Time (ns)", fontsize=20, fontweight='bold') - plt.ylabel("Intensity (norm.)", fontsize=20, fontweight='bold') - plt.tight_layout() - - plt.savefig(filename[0],bbox_inches='tight', dpi=300) - plt.close() + filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") + + plt.figure(figsize=(8,6)) + plt.tick_params(direction='out', length=8, width=3.5) + for i in range(len(self.x_mem)): + plt.plot(self.x_mem[i], self.y_mem[i], label=str(self.legend[i])) + if self.fit_lifetime_called == True: + plt.plot(self.x_mem[i], self.best_fit_mem[i],'k--') + + plt.yscale('log') + plt.xlabel("Time (ns)", fontsize=20, fontweight='bold') + plt.ylabel("Intensity (norm.)", fontsize=20, fontweight='bold') + plt.legend() + plt.tight_layout() + plt.xlim([self.exportplotwindow.ui.lowerX_spinBox.value(),self.exportplotwindow.ui.upperX_spinBox.value()]) + plt.ylim([self.exportplotwindow.ui.lowerY_spinBox.value(),self.exportplotwindow.ui.upperY_doubleSpinBox.value()]) + + plt.savefig(filename[0],bbox_inches='tight', dpi=300) + plt.close() + self.clean_up_after_fig_export() - except: + except Exception as e: + self.ui.Result_textBrowser.append(format(e)) pass def close_application(self): @@ -561,6 +628,7 @@ def done(self): self.skip_rows_signal.emit() self.close() + def run(): win = MainWindow() QtGui.QApplication.instance().exec_() diff --git a/PythonGUI_apps/Lifetime_analysis/read_ph_phd.py b/PythonGUI_apps/Lifetime_analysis/read_ph_phd.py index 1c536a4..1d35203 100644 --- a/PythonGUI_apps/Lifetime_analysis/read_ph_phd.py +++ b/PythonGUI_apps/Lifetime_analysis/read_ph_phd.py @@ -5,8 +5,11 @@ @author: Sarthak """ -import picoharp_phd -import numpy +try: + from Lifetime_analysis import picoharp_phd +except: + import picoharp_phd +import numpy as np import matplotlib.pyplot as plt #import sys @@ -17,53 +20,35 @@ def read_picoharp_phd(datafile): parser = picoharp_phd.PicoharpParser(datafile) return parser -def phd_to_csv(datafile): - parser = read_picoharp_phd(datafile) - name, ext = datafile.rsplit('.', 1) - - total_curves = parser.no_of_curves() - y = [] - for i in range(total_curves): - res, curve = parser.get_curve(i) - time_window = int(numpy.floor(parser.get_time_window_in_ns(curve_no))) - curve = curve[0:time_window] - y.append(curve) - -parser = picoharp_phd.PicoharpParser(datafile) -name, ext = datafile.rsplit('.', 1) - -#for i in range(parser.no_of_curves()): +#def phd_to_csv(datafile, return_df = False): +# parser = read_picoharp_phd(datafile) +# name, ext = datafile.rsplit('.', 1) # -# res, curve1 = parser.get_curve(0) -# res, curve2 = parser.get_curve(1) -# res, curve3 = parser.get_curve(2) -# res, curve4 = parser.get_curve(3) -# res, curve5 = parser.get_curve(4) -## res, curve2 = parser.get_curve(1) -# size = len(curve1) - -curve_no = 3 - -res, curve1 = parser.get_curve(curve_no) -time_window = int(numpy.floor(parser.get_time_window_in_ns(curve_no))) -curve1 = curve1[0:time_window] -size = len(curve1) -X = numpy.arange(0, size*res, res, numpy.float) - -plt.figure() -plt.plot(X,curve1) -plt.yscale('log') -plt.ylim([1,1e4]) -#csvname = '%s.csv' % name -#csv = open(csvname, 'w') -# -#for x, y1, y2, y3, y4, y5 in zip(X, curve1, curve2, curve3, curve4, curve5): -# csv.write('%f,%d,%d,%d,%d,%d\n' % (x, y1, y2, y3,y4,y5)) -# -#csv.close() -# -#print('Saved %s.' % csvname) +# total_curves = parser.no_of_curves() +# y = [] +# for i in range(total_curves): +# res, curve = parser.get_curve(i) +# time_window = int(np.floor(parser.get_time_window_in_ns(curve_no))) +# curve = curve[0:time_window] +# y.append(curve) +# +# df = pd.DataFrame(y) +# df.T.to_csv(name+".csv", index=False, header=False) +# if return_df == True: +# return df.T +def smooth(curve, boxwidth): + sm_curve = np.convolve(curve, np.ones(boxwidth)/boxwidth, mode="same") + return sm_curve -#if __name__ == '__main__': -# main() +def get_x_y(curve_no, parser, smooth_trace = False, boxwidth = 3): + + assert type(parser) == picoharp_phd.PicoharpParser, 'must be picoharp parser' + res, curve = parser.get_curve(curve_no) + time_window = int(np.floor(parser.get_time_window_in_ns(curve_no))) + curve = curve[0:time_window] + size = len(curve) + x = np.arange(0, size*res, res, np.float) + if smooth_trace == True: + curve = smooth(curve, boxwidth=boxwidth) + return x,curve diff --git a/PythonGUI_apps/PLQE_analysis/column_selection_gui.ui b/PythonGUI_apps/PLQE_analysis/column_selection_gui.ui new file mode 100644 index 0000000..c20125c --- /dev/null +++ b/PythonGUI_apps/PLQE_analysis/column_selection_gui.ui @@ -0,0 +1,107 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Select number of columns + + + + + + + Data preview + + + + + + + + Select data columns + + + + + + + + Ref + + + + + + + Inpath + + + + + + + Outpath + + + + + + + 2 + + + + + + + 3 + + + + + + + 4 + + + + + + + Done + + + + + + + + + + + + + + + + + 0 + 0 + 800 + 31 + + + + + + + + diff --git a/PythonGUI_apps/PLQE_analysis/plqe_analysis.py b/PythonGUI_apps/PLQE_analysis/plqe_analysis.py index a560746..3e42a4f 100644 --- a/PythonGUI_apps/PLQE_analysis/plqe_analysis.py +++ b/PythonGUI_apps/PLQE_analysis/plqe_analysis.py @@ -5,9 +5,7 @@ from pyqtgraph import exporters from pyqtgraph.Qt import QtCore, QtGui, QtWidgets import matplotlib.pyplot as plt - import numpy as np -import time # local modules @@ -30,138 +28,185 @@ plt.rc('axes', linewidth = 3.5) class MainWindow(TemplateBaseClass): - - def __init__(self): - super(TemplateBaseClass, self).__init__() - - # Create the main window - self.ui = WindowTemplate() - self.ui.setupUi(self) - - #setup uv vis plot - self.plot = self.ui.plotWidget.getPlotItem() - self.plot.setTitle(title="Wavelength vs. Intensity") - self.plot.setLabel('bottom', 'Wavelength', unit='nm') - self.plot.setLabel('left', 'Intensity', unit='a.u.') - self.plot.setLogMode(x=None, y=1) - - #setup line rois for laser and emission - self.laser_region = pg.LinearRegionItem(brush=QtGui.QBrush(QtGui.QColor(255, 0, 0, 50))) - self.laser_region.sigRegionChanged.connect(self.update_laser_spinBoxes) - self.emission_region = pg.LinearRegionItem() - self.emission_region.sigRegionChanged.connect(self.update_emission_spinBoxes) - self.laser_region.setRegion((200, 400)) - self.emission_region.setRegion((700, 800)) - - #setup ui signals - self.ui.load_data_pushButton.clicked.connect(self.open_data_file) - self.ui.plot_pushButton.clicked.connect(self.plot_intensity) - self.ui.clear_pushButton.clicked.connect(self.clear) - self.ui.calculate_plqe_pushButton.clicked.connect(self.calculate_plqe) - self.ui.laser_start_spinBox.valueChanged.connect(self.update_laser_region) - self.ui.laser_stop_spinBox.valueChanged.connect(self.update_laser_region) - self.ui.emission_start_spinBox.valueChanged.connect(self.update_emission_region) - self.ui.emission_stop_spinBox.valueChanged.connect(self.update_emission_region) - - self.show() - - def open_data_file(self): - """ Open data file """ - try: - self.filename = QtWidgets.QFileDialog.getOpenFileName(self) - self.data = np.loadtxt(self.filename[0], delimiter = '\t', skiprows = 1) - self.nm = np.copy(self.data[:,0]) - self.ref_data = np.copy(self.data[:,1]) - self.inpath_data = np.copy(self.data[:,2]) - self.outpath_data = np.copy(self.data[:,3]) - except Exception as err: - print(format(err)) - - def update_laser_spinBoxes(self): - """ Update laser spinboxes based on line rois """ - self.laser_start, self.laser_stop = self.laser_region.getRegion() - self.ui.laser_start_spinBox.setValue(self.laser_start) - self.ui.laser_stop_spinBox.setValue(self.laser_stop) - - - def update_emission_spinBoxes(self): - """ Update emission spinboxes based on line rois """ - self.emission_start, self.emission_stop = self.emission_region.getRegion() - self.ui.emission_start_spinBox.setValue(self.emission_start) - self.ui.emission_stop_spinBox.setValue(self.emission_stop) - - def update_laser_region(self): - """ Update laser line rois based on spinboxes """ - laser_start = self.ui.laser_start_spinBox.value() - laser_stop = self.ui.laser_stop_spinBox.value() - self.laser_region.setRegion((laser_start, laser_stop)) - - def update_emission_region(self): - """ Update emission line rois based on spinboxes """ - emission_start = self.ui.emission_start_spinBox.value() - emission_stop = self.ui.emission_stop_spinBox.value() - self.emission_region.setRegion((emission_start, emission_stop)) - - def plot_intensity(self): - try: - self.plot.plot(self.nm, self.inpath_data) - self.plot.addItem(self.laser_region, ignoreBounds=True) - self.plot.addItem(self.emission_region, ignoreBounds=True) - except Exception as err: - print(format(err)) - - def find_nearest(self,array,value): - idx = (np.abs(array-value)).argmin() - return idx - - def calculate_plqe(self): - - nm_interp_step = 1 - nm_interp_start = np.ceil(self.nm[0] / nm_interp_step) * nm_interp_step - nm_interp_stop = np.floor(self.nm[len(self.nm) - 1] / nm_interp_step) * nm_interp_step - nm_interp = np.arange(nm_interp_start, nm_interp_stop + nm_interp_step, nm_interp_step) - - ref_interp = np.interp(nm_interp, self.nm, self.ref_data) - - - inpath_interp = np.interp(nm_interp, self.nm, self.inpath_data) - outpath_interp = np.interp(nm_interp, self.nm, self.outpath_data) - - - """L_x is area under laser profile for experiment x""" - """P_x_ is area under emission profile for experiment x""" - - - #plt.semilogy(nm, a1_outpath_data[:,1]) - - emission_start_idx = self.find_nearest(nm_interp, self.emission_start) - emission_stop_idx = self.find_nearest(nm_interp, self.emission_stop) - - laser_start_idx = self.find_nearest(nm_interp, self.laser_start) - laser_stop_idx = self.find_nearest(nm_interp, self.laser_stop) - - la = np.trapz(ref_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) - lb = np.trapz(outpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) - lc = np.trapz(inpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) - - pa = np.trapz(ref_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) - pb = np.trapz(outpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) - pc = np.trapz(inpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) - - absorb = 1.0 - (lc / lb) - - plqe = 100 * (pc - ((1.0 - absorb) * pb)) / (la * absorb) - #print('PLQE Percent = %.3f' %(plqe)) - #return plqe - self.ui.plqe_label.setText("%.3f" %(plqe)) - - def clear(self): - self.plot.clear() + + def __init__(self): + super(TemplateBaseClass, self).__init__() + + # Create the main window + self.ui = WindowTemplate() + self.ui.setupUi(self) + + #setup uv vis plot + self.plot = self.ui.plotWidget.getPlotItem() + self.plot.setTitle(title="Wavelength vs. Intensity") + self.plot.setLabel('bottom', 'Wavelength', unit='nm') + self.plot.setLabel('left', 'Intensity', unit='a.u.') + self.plot.setLogMode(x=None, y=1) + + #setup line rois for laser and emission + self.laser_region = pg.LinearRegionItem(brush=QtGui.QBrush(QtGui.QColor(255, 0, 0, 50))) + self.laser_region.sigRegionChanged.connect(self.update_laser_spinBoxes) + self.emission_region = pg.LinearRegionItem() + self.emission_region.sigRegionChanged.connect(self.update_emission_spinBoxes) + self.laser_region.setRegion((200, 400)) + self.emission_region.setRegion((700, 800)) + + #setup ui signals + self.ui.load_data_pushButton.clicked.connect(self.open_data_file) + self.ui.plot_pushButton.clicked.connect(self.plot_intensity) + self.ui.clear_pushButton.clicked.connect(self.clear) + self.ui.calculate_plqe_pushButton.clicked.connect(self.calculate_plqe) + self.ui.laser_start_spinBox.valueChanged.connect(self.update_laser_region) + self.ui.laser_stop_spinBox.valueChanged.connect(self.update_laser_region) + self.ui.emission_start_spinBox.valueChanged.connect(self.update_emission_region) + self.ui.emission_stop_spinBox.valueChanged.connect(self.update_emission_region) + + self.show() + + def open_data_file(self): + """ Open data file """ + try: + self.filename = QtWidgets.QFileDialog.getOpenFileName(self) + #self.data = np.loadtxt(self.filename[0], delimiter = '\t', skiprows = 1) + if ".txt" in self.filename[0]: + self.data = np.loadtxt(self.filename[0], delimiter = '\t', skiprows = 1) + elif ".csv" in self.filename[0]: + self.data = np.loadtxt(self.filename[0], delimiter = ',', skiprows = 1) + elif ".qua" in self.filename[0]:#TODO: Include a Pop-up window for input for skipping header + self.data = np.genfromtxt(self.filename[0], delimiter = '\t', skip_header = 28) + self.cs_window = ColSelectionWindow(self.data) + self.cs_window.col_selection_signal.connect(self.open_with_col_selection) + self.nm = np.copy(self.data[:,0]) + #self.ref_data = np.copy(self.data[:,1]) + #self.inpath_data = np.copy(self.data[:,2]) + #self.outpath_data = np.copy(self.data[:,3]) + except Exception as err: + print(format(err)) + + def open_with_col_selection(self): + ref_data_col = self.cs_window.ui.ref_spinBox.value() - 1 #subtract since spinboxes refer to column num and not index + inpath_data_col = self.cs_window.ui.inpath_spinBox.value() - 1 + outpath_data_col = self.cs_window.ui.outpath_spinBox.value() - 1 + self.ref_data = np.copy(self.data[:,ref_data_col]) + self.inpath_data = np.copy(self.data[:,inpath_data_col]) + self.outpath_data = np.copy(self.data[:,outpath_data_col]) + + def update_laser_spinBoxes(self): + """ Update laser spinboxes based on line rois """ + self.laser_start, self.laser_stop = self.laser_region.getRegion() + self.ui.laser_start_spinBox.setValue(self.laser_start) + self.ui.laser_stop_spinBox.setValue(self.laser_stop) + + + def update_emission_spinBoxes(self): + """ Update emission spinboxes based on line rois """ + self.emission_start, self.emission_stop = self.emission_region.getRegion() + self.ui.emission_start_spinBox.setValue(self.emission_start) + self.ui.emission_stop_spinBox.setValue(self.emission_stop) + + def update_laser_region(self): + """ Update laser line rois based on spinboxes """ + laser_start = self.ui.laser_start_spinBox.value() + laser_stop = self.ui.laser_stop_spinBox.value() + self.laser_region.setRegion((laser_start, laser_stop)) + + def update_emission_region(self): + """ Update emission line rois based on spinboxes """ + emission_start = self.ui.emission_start_spinBox.value() + emission_stop = self.ui.emission_stop_spinBox.value() + self.emission_region.setRegion((emission_start, emission_stop)) + + def plot_intensity(self): + try: + self.plot.plot(self.nm, self.inpath_data, pen='r') + self.plot.addItem(self.laser_region, ignoreBounds=True) + self.plot.addItem(self.emission_region, ignoreBounds=True) + except Exception as err: + print(format(err)) + + def find_nearest(self,array,value): + idx = (np.abs(array-value)).argmin() + return idx + + def calculate_plqe(self): + + nm_interp_step = 1 + nm_interp_start = np.ceil(self.nm[0] / nm_interp_step) * nm_interp_step + nm_interp_stop = np.floor(self.nm[len(self.nm) - 1] / nm_interp_step) * nm_interp_step + nm_interp = np.arange(nm_interp_start, nm_interp_stop + nm_interp_step, nm_interp_step) + + ref_interp = np.interp(nm_interp, self.nm, self.ref_data) + + + inpath_interp = np.interp(nm_interp, self.nm, self.inpath_data) + outpath_interp = np.interp(nm_interp, self.nm, self.outpath_data) + + + """L_x is area under laser profile for experiment x""" + """P_x_ is area under emission profile for experiment x""" + + + #plt.semilogy(nm, a1_outpath_data[:,1]) + + emission_start_idx = self.find_nearest(nm_interp, self.emission_start) + emission_stop_idx = self.find_nearest(nm_interp, self.emission_stop) + + laser_start_idx = self.find_nearest(nm_interp, self.laser_start) + laser_stop_idx = self.find_nearest(nm_interp, self.laser_stop) + + la = np.trapz(ref_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) + lb = np.trapz(outpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) + lc = np.trapz(inpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) + + pa = np.trapz(ref_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) + pb = np.trapz(outpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) + pc = np.trapz(inpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) + + absorb = 1.0 - (lc / lb) + + plqe = 100 * (pc - ((1.0 - absorb) * pb)) / (la * absorb) + #print('PLQE Percent = %.3f' %(plqe)) + #return plqe + self.ui.plqe_label.setText("%.3f" %(plqe)) + + def clear(self): + self.plot.clear() + +"""Table view GUI""" +ui_file_path = (base_path / "column_selection_gui.ui").resolve() +col_selection_WindowTemplate, col_selection_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) + +class ColSelectionWindow(col_selection_TemplateBaseClass): + + col_selection_signal = QtCore.pyqtSignal() #signal to help with pass info back to MainWindow + + def __init__(self, data): + col_selection_TemplateBaseClass.__init__(self) + # Create the param window + self.ui = col_selection_WindowTemplate() + self.ui.setupUi(self) + self.ui.done_pushButton.clicked.connect(self.done) + + self.table_widget = pg.TableWidget() + self.ui.data_preview_groupBox.layout().addWidget(self.table_widget) + + self.table_widget.setData(data) + + #self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) + self.show() + + def done(self): + self.col_selection_signal.emit() + self.ui.textBrowser.setText("Data successfully loaded.") + #self.close() + + def closeEvent(self, event): + self.col_selection_signal.emit() """Run the Main Window""" def run(): - win = MainWindow() - QtGui.QApplication.instance().exec_() - return win + win = MainWindow() + QtGui.QApplication.instance().exec_() + return win #run() diff --git a/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py b/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py index fba0a86..a96f782 100644 --- a/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py +++ b/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py @@ -23,10 +23,11 @@ class Spectra_Fit(object): ref: reference spectrum (both x and y-axis) for background correction """ - def __init__(self, data, ref, wlref = None): + def __init__(self, data, ref, wlref = None, fit_in_eV = True): self.data = data self.ref = ref self.wlref = wlref + self.fit_in_eV = fit_in_eV def background_correction(self): """Return the background corrected spectrum""" @@ -37,8 +38,12 @@ def background_correction(self): if self.wlref is not None: wlref = self.wlref[:,1] y = y/wlref - - # y = y - np.mean(y[(x>600) & (x<700)]) # removing any remaining bckgrnd + + if self.fit_in_eV is True: + x = np.sort(1240/self.data[:, 0]) # converting to eV and sorting in ascending order + y = [y[i] for i in np.argsort(1240/x)] # sorting argument of y acc to x sorting index + # need to do this because data is collected in wavelength + return [x,y] class Single_Gaussian(Spectra_Fit): @@ -66,12 +71,16 @@ def gaussian_model(self): # return result #770 760 780 sigma 15 def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): x,y = self.background_correction() + if self.fit_in_eV is True: + peak_pos = 1240/peak_pos + sigma = 1240/sigma + min_max_range = np.sort(1240/np.asarray(min_max_range)) gmodel = GaussianModel(prefix = 'g1_') # calling gaussian model pars = gmodel.guess(y, x=x) # parameters - center, width, height pars['g1_center'].set(peak_pos, min=min_max_range[0], max=min_max_range[1]) pars['g1_sigma'].set(sigma) result = gmodel.fit(y, pars, x=x, nan_policy='propagate') - return result #770 760 780 sigma 15 + return result class Single_Lorentzian(Spectra_Fit): """Fit a single Lorentzian to the spectrum @@ -87,17 +96,13 @@ def lorentzian_model(self): pars = lmodel.guess(y, x=x) # parameters - center, width, height result = lmodel.fit(y, pars, x=x, nan_policy='propagate') return result - - # def lorentzian_model_w_lims(self, center_min = None, center_max = None): - # x,y = self.background_correction() - # lmodel = LorentzianModel(prefix = 'l1_') # calling lorentzian model - # pars = lmodel.guess(y, x=x) # parameters - center, width, height - # pars['l1_center'].set(min = center_min, max = center_max) - # result = lmodel.fit(y, pars, x=x, nan_policy='propagate') - # return result def lorentzian_model_w_lims(self, peak_pos, sigma, min_max_range): x,y = self.background_correction() + if self.fit_in_eV is True: + peak_pos = 1240/peak_pos + sigma = 1240/sigma + min_max_range = np.sort(1240/np.asarray(min_max_range)) lmodel = LorentzianModel(prefix = 'l1_') # calling lorentzian model pars = lmodel.guess(y, x=x) # parameters - center, width, height pars['l1_center'].set(peak_pos, min = min_max_range[0], max = min_max_range[1]) @@ -132,6 +137,10 @@ def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): #min_max_range - list containing lists of min and max for peak center. [ [min1, max1], [min2, max2] ] x,y = self.background_correction() + if self.fit_in_eV is True: + peak_pos = 1240/np.asarray(peak_pos) + sigma = 1240/np.asarray(sigma) + min_max_range = np.sort(1240/np.asarray(min_max_range)) gmodel_1 = GaussianModel(prefix='g1_') # calling gaussian model pars = gmodel_1.guess(y, x=x) # parameters - center, width, height pars['g1_center'].set(peak_pos[0], min = min_max_range[0][0], max = min_max_range[0][1]) @@ -141,7 +150,7 @@ def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): gmodel_2 = GaussianModel(prefix='g2_') pars.update(gmodel_2.make_params()) # update parameters - center, width, height pars['g2_center'].set(peak_pos[1], min = min_max_range[1][0], max = min_max_range[1][1]) - pars['g2_sigma'].set(sigma[1], min = composite_pars['g1_sigma'].value) + pars['g2_sigma'].set(sigma[1], min = pars['g1_sigma'].value) pars['g2_amplitude'].set(min = 0) gmodel = gmodel_1 + gmodel_2 @@ -155,8 +164,8 @@ class Multi_Gaussian(Spectra_Fit): # self.num_of_gaussians = num_of_gaussians # self.peak_pos = peak_pos # self.min_max_range = min_max_range - def __init__(self, data, ref, num_of_gaussians, wlref=None): - Spectra_Fit.__init__(self, data, ref, wlref) + def __init__(self, data, ref, num_of_gaussians, wlref=None, fit_in_eV = True): + Spectra_Fit.__init__(self, data, ref, wlref, fit_in_eV=True) self.num_of_gaussians = num_of_gaussians def gaussian_model(self): @@ -197,6 +206,11 @@ def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): assert self.num_of_gaussians == len(self.peak_pos), ("Number of gaussians must be equal to the number of peak positions") assert len(self.min_max_range) == len(self.peak_pos), ("Number of bounds on the range must be equal to the number of peak positions") + if self.fit_in_eV is True: + peak_pos = 1240/np.asarray(peak_pos) + sigma = 1240/np.asarray(sigma) + min_max_range = np.sort(1240/np.asarray(min_max_range)) + for i in range(self.num_of_gaussians): diff --git a/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py b/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py index a5196c8..69f182b 100644 --- a/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py +++ b/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py @@ -1,795 +1,1020 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Mar 27 16:50:26 2019 - -@author: Sarthak -""" - -# system imports -import sys -import h5py -from pathlib import Path -import os.path -import pyqtgraph as pg -from pyqtgraph.Qt import QtCore, QtGui, QtWidgets#, QColorDialog -import numpy as np -import matplotlib.pyplot as plt -import pickle -import time -from lmfit.models import GaussianModel -from scipy import interpolate -import customplotting.mscope as cpm -# local modules -try: - from Spectra_fit_funcs import Spectra_Fit, Single_Gaussian, Single_Lorentzian, Double_Gaussian, Multi_Gaussian -except: - from Spectrum_analysis.Spectra_fit_funcs import Spectra_Fit, Single_Gaussian, Single_Lorentzian, Double_Gaussian, Multi_Gaussian - - -"""Recylce params for plotting""" -plt.rc('xtick', labelsize = 20) -plt.rc('xtick.major', pad = 3) -plt.rc('ytick', labelsize = 20) -plt.rc('lines', lw = 1.5, markersize = 7.5) -plt.rc('legend', fontsize = 20) -plt.rc('axes', linewidth=3.5) - -pg.mkQApp() -pg.setConfigOption('background', 'w') - - -base_path = Path(__file__).parent -file_path = (base_path / "Spectra_plot_fit_gui.ui").resolve() - -uiFile = file_path - -WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) - -def updateDelay(scale, time): - """ Hack fix for scalebar inaccuracy""" - QtCore.QTimer.singleShot(time, scale.updateBar) - -class MainWindow(TemplateBaseClass): - - def __init__(self): - pg.setConfigOption('imageAxisOrder', 'row-major') - super(TemplateBaseClass, self).__init__() - - # Create the main window - self.ui = WindowTemplate() - self.ui.setupUi(self) - - # self.ui.fitFunc_comboBox.addItems(["Single Gaussian","Single Lorentzian", "Double Gaussian", "Multiple Gaussians"]) -# self.ui.actionExit.triggered.connect(self.close_application) - - ##setup ui signals - self.ui.importSpec_pushButton.clicked.connect(self.open_file) - self.ui.importBck_pushButton.clicked.connect(self.open_bck_file) - self.ui.importWLRef_pushButton.clicked.connect(self.open_wlref_file) - - self.ui.load_spectra_scan_pushButton.clicked.connect(self.open_spectra_scan_file) - self.ui.load_bck_file_pushButton.clicked.connect(self.open_spectra_bck_file) - self.ui.load_fitted_scan_pushButton.clicked.connect(self.open_fit_scan_file) - - self.ui.plot_pushButton.clicked.connect(self.plot) - self.ui.plot_fit_scan_pushButton.clicked.connect(self.plot_fit_scan) - self.ui.plot_raw_scan_pushButton.clicked.connect(self.plot_raw_scan) - self.ui.plot_intensity_sums_pushButton.clicked.connect(self.plot_intensity_sums) - - self.ui.fit_pushButton.clicked.connect(self.fit_and_plot) - self.ui.fit_scan_pushButton.clicked.connect(self.fit_and_plot_scan) - # self.ui.config_fit_params_pushButton.clicked.connect(self.configure_fit_params) - self.ui.clear_pushButton.clicked.connect(self.clear_plot) - self.ui.export_single_figure_pushButton.clicked.connect(self.pub_ready_plot_export) - self.ui.export_scan_figure_pushButton.clicked.connect(self.pub_ready_plot_export) - - self.ui.import_pkl_pushButton.clicked.connect(self.open_pkl_file) - self.ui.data_txt_pushButton.clicked.connect(self.pkl_data_to_txt) - self.ui.scan_params_txt_pushButton.clicked.connect(self.pkl_params_to_txt) - - self.ui.pkl_to_h5_pushButton.clicked.connect(self.pkl_to_h5) - - self.ui.tabWidget.currentChanged.connect(self.switch_overall_tab) - self.ui.fitFunc_comboBox.currentTextChanged.connect(self.switch_bounds_and_guess_tab) - self.ui.adjust_param_checkBox.stateChanged.connect(self.switch_adjust_param) - - self.ui.export_data_pushButton.clicked.connect(self.export_data) - self.ui.clear_export_data_pushButton.clicked.connect(self.clear_export_data) - - # for i in reversed(range(self.ui.bounds_groupBox.layout().count())): - # self.ui.bounds_groupBox.layout().itemAt(i).widget().deleteLater() - #self.ui.single_bounds_page.layout().addWidget(QtWidgets.QPushButton("test")) - - self.file = None - self.bck_file = None - self.wlref_file = None - self.x = None - self.y = None - self.out = None # output file after fitting - - # Peak parameters if adjust params is selected - self.center_min = None - self.center_max = None - - #variables accounting for data received from FLIM analysis - self.opened_from_flim = False #switched to True in FLIM_plot when "analyze lifetime" clicked - self.sum_data_from_flim = [] - - #container for data to append to txt file - self.data_list = [] - - self.show() - - """ Open Single Spectrum files """ - def open_file(self): - try: - self.single_spec_filename = QtWidgets.QFileDialog.getOpenFileName(self) - try: - self.file = np.loadtxt(self.single_spec_filename[0], skiprows = 16, delimiter='\t') - except: - self.file = np.genfromtxt(self.single_spec_filename[0], skip_header=1, skip_footer=3, delimiter='\t') - self.opened_from_flim = False - except: - pass - - def open_bck_file(self): - try: - filename = QtWidgets.QFileDialog.getOpenFileName(self) - try: - self.bck_file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t') - except: - self.bck_file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t') - except Exception as e: - self.ui.result_textBrowser.append(str(e)) - pass - - def open_wlref_file(self): - try: - filename = QtWidgets.QFileDialog.getOpenFileName(self) - try: - self.wlref_file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t') - except: - self.wlref_file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t') - except: - pass - - """Open Scan Files""" - def open_spectra_scan_file(self): - try: - filename = QtWidgets.QFileDialog.getOpenFileName(self, filter="Scan files (*.pkl *.h5)") - if ".pkl" in filename[0]: - self.spec_scan_file = pickle.load(open(filename[0], 'rb')) - self.scan_file_type = "pkl" - elif ".h5" in filename[0]: - self.spec_scan_file = h5py.File(filename[0], 'r') - self.scan_file_type = "h5" - self.get_data_params() - self.ui.result_textBrowser2.append("Done Loading - Spectra Scan File") - except Exception as e: - self.ui.result_textBrowser2.append(str(e)) - pass - - def open_spectra_bck_file(self): - try: - filename = QtWidgets.QFileDialog.getOpenFileName(self) - self.bck_file = np.loadtxt(filename[0])#, skiprows=1, delimiter=None) - self.ui.result_textBrowser2.append("Done Loading - Background File") - except Exception as e: - self.ui.result_textBrowser2.append(str(e)) - pass - - def open_fit_scan_file(self): - try: - filename = QtWidgets.QFileDialog.getOpenFileName(self) - self.fit_scan_file = pickle.load(open(filename[0], 'rb')) - self.ui.result_textBrowser2.append("Done Loading - Scan Fit File") - except Exception as e: - self.ui.result_textBrowser2.append(str(e)) - pass - - def open_pkl_file(self): - """ Open pkl file to convert to txt """ - try: - self.pkl_to_convert = QtWidgets.QFileDialog.getOpenFileName(self) - except: - pass - - def switch_overall_tab(self): - """ Enable/disable fit settings on right depending on current tab """ - if self.ui.tabWidget.currentIndex() == 0: - self.ui.fitting_settings_groupBox.setEnabled(True) - self.ui.fit_pushButton.setEnabled(True) - self.ui.fit_scan_pushButton.setEnabled(True) - self.ui.scan_fit_settings_groupBox.setEnabled(False) - elif self.ui.tabWidget.currentIndex() == 1: - self.ui.fitting_settings_groupBox.setEnabled(False) - self.ui.fit_pushButton.setEnabled(False) - self.ui.fit_scan_pushButton.setEnabled(True) - self.ui.scan_fit_settings_groupBox.setEnabled(True) - elif self.ui.tabWidget.currentIndex() == 2: - self.ui.fitting_settings_groupBox.setEnabled(False) - self.ui.fit_pushButton.setEnabled(False) - self.ui.fit_scan_pushButton.setEnabled(False) - self.ui.scan_fit_settings_groupBox.setEnabled(False) - - """ Single spectrum functions """ - def switch_bounds_and_guess_tab(self): - """ Show the appropriate bounds and initial guess params based on fit function """ - fit_func = self.ui.fitFunc_comboBox.currentText() - if fit_func == "Single Gaussian" or fit_func == "Single Lorentzian": - self.ui.n_label.setEnabled(False) - self.ui.n_spinBox.setEnabled(False) - self.ui.bounds_stackedWidget.setCurrentIndex(0) - self.ui.guess_stackedWidget.setCurrentIndex(0) - self.ui.plot_components_checkBox.setEnabled(False) - self.ui.n_spinBox.setValue(1) - elif fit_func == "Double Gaussian": - self.ui.n_label.setEnabled(False) - self.ui.n_spinBox.setEnabled(False) - self.ui.bounds_stackedWidget.setCurrentIndex(1) - self.ui.guess_stackedWidget.setCurrentIndex(1) - self.ui.plot_components_checkBox.setEnabled(True) - self.ui.n_spinBox.setValue(2) - elif fit_func == "Triple Gaussian": - self.ui.n_label.setEnabled(False) - self.ui.n_spinBox.setEnabled(False) - self.ui.bounds_stackedWidget.setCurrentIndex(2) - self.ui.guess_stackedWidget.setCurrentIndex(2) - self.ui.plot_components_checkBox.setEnabled(True) - self.ui.n_spinBox.setValue(3) - - def switch_adjust_param(self): - """ Enable bounds and initial guess only when adjust parameters is checked """ - checked = self.ui.adjust_param_checkBox.isChecked() - self.ui.bounds_groupBox.setEnabled(checked) - self.ui.guess_groupBox.setEnabled(checked) - - def check_loaded_files(self): - """ - Check if 'subtract background' or 'white light correction' is checked - and if required files have been loaded. - """ - if self.ui.subtract_bck_radioButton.isChecked() and self.bck_file is None: - self.ui.result_textBrowser.setText("You need to load a background file.") - elif self.wlref_file is not None and self.ui.WLRef_checkBox.isChecked() == False: - self.ui.result_textBrowser.setText("You need to check the White Light Correction option!") - elif self.wlref_file is None and self.ui.WLRef_checkBox.isChecked(): - self.ui.result_textBrowser.setText("You need to load a White Light Ref file.") - else: - return True - - def plot(self): - try: - if self.opened_from_flim: - flim_data = self.sum_data_from_flim.T - interp = interpolate.interp1d(flim_data[:,0], flim_data[:,1]) - x_range = [flim_data[:,0][0], flim_data[:,0][-1]] - xnew = np.linspace(x_range[0], x_range[1], 100 ) - ynew = interp(xnew) - self.file = np.zeros((xnew.shape[0], 2)) - self.file[:,0] = xnew - self.file[:,1] = ynew - self.x = xnew - self.y = ynew - - elif self.file is None: #elif - self.ui.result_textBrowser.setText("You need to load a data file.") - else: - self.x = self.file[:,0] - self.y = self.file[:,1] - - if self.check_loaded_files == True: #check the following conditions if all required files have been provided - if self.ui.subtract_bck_radioButton.isChecked() == True and self.ui.WLRef_checkBox.isChecked() == False: - bck_y = self.bck_file[:,1] - self.y = self.y - bck_y - elif self.ui.subtract_bck_radioButton.isChecked() == False and self.ui.WLRef_checkBox.isChecked() == True: - wlref_y = self.wlref_file[:,1] - self.y = (self.y)/wlref_y - - elif self.ui.subtract_bck_radioButton.isChecked() == True and self.ui.WLRef_checkBox.isChecked() == True: - bck_y = self.bck_file[:,1] - wlref_y = self.wlref_file[:,1] - self.y = (self.y-bck_y)/wlref_y - - - if self.ui.norm_checkBox.isChecked(): - self.normalize() - - self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r') - - except Exception as err: - pass - self.ui.plot.setLabel('left', 'Intensity', units='a.u.') - self.ui.plot.setLabel('bottom', 'Wavelength (nm)') - - def normalize(self): - self.y = (self.y) / np.amax(self.y) - - def clear_plot(self): - self.ui.plot.clear() - self.ui.result_textBrowser.clear() - - def clear_check(self): - if self.ui.clear_checkBox.isChecked() == True: - return True - elif self.ui.clear_checkBox.isChecked() == False: - return False - - def fit_and_plot(self): - fit_func = self.ui.fitFunc_comboBox.currentText() - - try: - self.plot() - if self.opened_from_flim: - self.file = np.zeros((self.x.shape[0], 2)) - self.file[:,0] = self.x - self.file[:,1] = self.y - - if self.ui.plot_without_bck_radioButton.isChecked(): #if plot w/o bck, create dummy bck_file - self.bck_file = np.zeros(shape=(self.file.shape[0], 2)) - self.bck_file[:,0] = self.file[:,0] - - # if self.ui.subtract_bck_radioButton.isChecked() == False: - # self.ui.result_textBrowser.setText("You need to check the subtract background option!") - if self.check_loaded_files is None: - pass - else: - - if fit_func == "Single Gaussian": #and self.ui.subtract_bck_radioButton.isChecked() == True: - single_gauss = Single_Gaussian(self.file, self.bck_file, wlref=self.wlref_file) - if self.ui.adjust_param_checkBox.isChecked(): - center1_min = self.ui.single_peakcenter1_min_spinBox.value() - center1_max = self.ui.single_peakcenter1_max_spinBox.value() - center1_guess = self.ui.single_peakcenter1_guess_spinBox.value() - sigma1_guess = self.ui.single_sigma1_guess_spinBox.value() - self.result = single_gauss.gaussian_model_w_lims(center1_guess, sigma1_guess, - [center1_min, center1_max]) - else: - self.result = single_gauss.gaussian_model() - self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r') - self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k') - self.ui.result_textBrowser.setText(self.result.fit_report()) - - elif fit_func == "Single Lorentzian": #and self.ui.subtract_bck_radioButton.isChecked() == True: - single_lorentzian = Single_Lorentzian(self.file, self.bck_file, wlref=self.wlref_file) - - if self.ui.adjust_param_checkBox.isChecked(): - center1_min = self.ui.single_peakcenter1_min_spinBox.value() - center1_max = self.ui.single_peakcenter1_max_spinBox.value() - center1_guess = self.ui.single_peakcenter1_guess_spinBox.value() - sigma1_guess = self.ui.single_sigma1_guess_spinBox.value() - self.result = single_lorentzian.lorentzian_model_w_lims(center1_guess, sigma1_guess, - [center1_min, center1_max]) - else: - self.result = single_lorentzian.lorentzian_model() - self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r') - self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k') - self.ui.result_textBrowser.setText(self.result.fit_report()) - - elif fit_func == "Double Gaussian": #and self.ui.subtract_bck_radioButton.isChecked() == True: - double_gauss = Double_Gaussian(self.file, self.bck_file, wlref=self.wlref_file) - if self.ui.adjust_param_checkBox.isChecked(): - center1_min = self.ui.double_peakcenter1_min_spinBox.value() - center1_max = self.ui.double_peakcenter1_max_spinBox.value() - center2_min = self.ui.double_peakcenter2_min_spinBox.value() - center2_max = self.ui.double_peakcenter2_max_spinBox.value() - center1_guess = self.ui.double_peakcenter1_guess_spinBox.value() - sigma1_guess = self.ui.double_sigma1_guess_spinBox.value() - center2_guess = self.ui.double_peakcenter2_guess_spinBox.value() - sigma2_guess = self.ui.double_sigma2_guess_spinBox.value() - - peak_pos = [center1_guess, center2_guess] - sigma = [sigma1_guess, sigma2_guess] - min_max_range = [ [center1_min, center1_max], [center2_min, center2_max] ] - self.result = double_gauss.gaussian_model_w_lims(peak_pos, sigma, min_max_range) - - else: - self.result = double_gauss.gaussian_model() - - self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r') - self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k') - if self.ui.plot_components_checkBox.isChecked(): - comps = self.result.eval_components(x=self.x) - self.ui.plot.plot(self.x, comps['g1_'], pen='b', clear=False) - self.ui.plot.plot(self.x, comps['g2_'], pen='g', clear=False) - - self.ui.result_textBrowser.setText(self.result.fit_report()) - - elif fit_func == "Triple Gaussian": #and self.ui.subtract_bck_radioButton.isChecked() == True: - #currently only works for triple gaussian (n=3) - multiple_gauss = Multi_Gaussian(self.file, self.bck_file, 3, wlref=self.wlref_file) - if self.ui.adjust_param_checkBox.isChecked(): - center1_min = self.ui.multi_peakcenter1_min_spinBox.value() - center1_max = self.ui.multi_peakcenter1_max_spinBox.value() - center2_min = self.ui.multi_peakcenter2_min_spinBox.value() - center2_max = self.ui.multi_peakcenter2_max_spinBox.value() - center3_min = self.ui.multi_peakcenter3_min_spinBox.value() - center3_max = self.ui.multi_peakcenter3_max_spinBox.value() - center1_guess = self.ui.multi_peakcenter1_guess_spinBox.value() - sigma1_guess = self.ui.multi_sigma1_guess_spinBox.value() - center2_guess = self.ui.multi_peakcenter2_guess_spinBox.value() - sigma2_guess = self.ui.multi_sigma2_guess_spinBox.value() - center3_guess = self.ui.multi_peakcenter3_guess_spinBox.value() - sigma3_guess = self.ui.multi_sigma3_guess_spinBox.value() - num_gaussians = 3 - peak_pos = [center1_guess, center2_guess, center3_guess] - sigma = [sigma1_guess, sigma2_guess, sigma3_guess] - min_max_range = [ [center1_min, center1_max], [center2_min, center2_max], [center3_min, center3_max] ] - - self.result = multiple_gauss.gaussian_model_w_lims(peak_pos, sigma, min_max_range) - else: - self.result = multiple_gauss.gaussian_model() - - self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r') - self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k') - if self.ui.plot_components_checkBox.isChecked(): - comps = self.result.eval_components(x=self.x) - self.ui.plot.plot(self.x, comps['g1_'], pen='b', clear=False) - self.ui.plot.plot(self.x, comps['g2_'], pen='g', clear=False) - self.ui.plot.plot(self.x, comps['g3_'], pen='c', clear=False) - self.ui.result_textBrowser.setText(self.result.fit_report()) - - self.data_list.append(self.ui.result_textBrowser.toPlainText()) - - except Exception as e: - self.ui.result_textBrowser.append(str(e)) - - def pub_ready_plot_export(self): - filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") - try: - try: - data = self.spec_scan_file - param_selection = str(self.ui.comboBox.currentText()) - if param_selection == 'pk_pos': label = 'PL Peak Position (n.m.)' - elif param_selection == 'fwhm': label = 'PL FWHM (n.m.)' - cpm.plot_confocal(self.img, figsize=(10,10), stepsize = data['Scan Parameters']['X step size (um)'], cmap="seismic", cbar_label=label) - plt.savefig(filename[0],bbox_inches='tight', dpi=300) - plt.close() - except: - plt.figure(figsize=(8,6)) - plt.tick_params(direction='out', length=8, width=3.5) - plt.plot(self.x, self.y) - plt.plot(self.x, self.result.best_fit,'k') - plt.xlabel("Wavelength (nm)", fontsize=20, fontweight='bold') - plt.ylabel("Intensity (a.u.)", fontsize=20, fontweight='bold') - plt.tight_layout() - - plt.savefig(filename[0],bbox_inches='tight', dpi=300) - plt.close() - - except AttributeError: - self.ui.result_textBrowser.setText("Need to fit the data first!") - - def export_data(self): - """ Save fit params and srv calculations stored in data_list as .txt """ - folder = os.path.dirname(self.single_spec_filename[0]) - filename_ext = os.path.basename(self.single_spec_filename[0]) - filename = os.path.splitext(filename_ext)[0] #get filename without extension - - path = folder + "/" + filename + "_fit_results.txt" - if not os.path.exists(path): - file = open(path, "w+") - else: - file = open(path, "a+") - - for i in range(len(self.data_list)): - file.write(self.data_list[i] + "\n\n") - - self.data_list = [] - file.close() - - def clear_export_data(self): - self.data_list = [] - - - """ Scan spectra functions """ - def get_data_params(self): - data = self.spec_scan_file - if self.scan_file_type == "pkl": - self.intensities = data['Intensities'] - self.wavelengths = data['Wavelengths'] - # try: - self.x_scan_size = data['Scan Parameters']['X scan size (um)'] - self.y_scan_size = data['Scan Parameters']['Y scan size (um)'] - self.x_step_size = data['Scan Parameters']['X step size (um)'] - self.y_step_size = data['Scan Parameters']['Y step size (um)'] - # except: # TODO test and debug loading pkl file w/o scan parameters - # self.configure_scan_params() - # while not hasattr(self, "scan_params_entered"): - # pass - # self.x_scan_size = self.param_window.ui.x_scan_size_spinBox.value() - # self.y_scan_size = self.param_window.ui.y_scan_size_spinBox.value() - # self.x_step_size = self.param_window.ui.x_step_size_spinBox.value() - # self.y_step_size = self.param_window.ui.y_step_size_spinBox.value() - - else: #run this if scan file is h5 - self.x_scan_size = data['Scan Parameters'].attrs['X scan size (um)'] - self.y_scan_size = data['Scan Parameters'].attrs['Y scan size (um)'] - self.x_step_size = data['Scan Parameters'].attrs['X step size (um)'] - self.y_step_size = data['Scan Parameters'].attrs['Y step size (um)'] - self.intensities = data['Intensities'][()] #get dataset values - self.wavelengths = data['Wavelengths'][()] - - self.numb_x_pixels = int(self.x_scan_size/self.x_step_size) - self.numb_y_pixels = int(self.y_scan_size/self.y_step_size) - - """Open param window and get peak center range values and assign it to variables to use later""" - # def configure_scan_params(self): - # self.param_window = ParamWindow() - # self.param_window.peak_range.connect(self.peak_range) - - # def peak_range(self, peaks): - # self.center_min = peaks[0] - # self.center_max = peaks[1] - - def plot_fit_scan(self): - try: - if self.ui.use_raw_scan_settings.isChecked(): - num_x = self.numb_x_pixels - num_y =self.numb_y_pixels - else: - num_x = self.ui.num_x_spinBox.value() - num_y = self.ui.num_y_spinBox.value() - - numb_of_points = num_x * num_y #75*75 - - fwhm = np.zeros(shape=(numb_of_points,1)) - pk_pos = np.zeros(shape=(numb_of_points,1)) -# pk_pos_plus = np.zeros(shape=(numb_of_points,1)) -# pk_pos_minus = np.zeros(shape=(numb_of_points,1)) - sigma = np.zeros(shape=(numb_of_points,1)) - height = np.zeros(shape=(numb_of_points,1)) - - for i in range(numb_of_points): - fwhm[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_fwhm'] - pk_pos[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_center'] - sigma[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_sigma'] - height[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_height'] - - newshape = (num_x, num_y) - - param_selection = str(self.ui.comboBox.currentText()) - self.img = np.reshape(eval(param_selection), newshape) - - if self.ui.use_raw_scan_settings.isChecked(): - self.ui.fit_scan_viewbox.setImage(self.img, scale= - (self.x_step_size, - self.y_step_size)) - scale = pg.ScaleBar(size=2,suffix='um') - scale.setParentItem(self.ui.fit_scan_viewbox.view) - scale.anchor((1, 1), (1, 1), offset=(-30, -30)) - self.ui.fit_scan_viewbox.view.sigRangeChanged.connect(lambda: updateDelay(scale, 10)) - else: - self.ui.fit_scan_viewbox.setImage(self.img) - - self.ui.fit_scan_viewbox.view.invertY(False) - - except Exception as e: - self.ui.result_textBrowser2.append(str(e)) - pass - - def plot_raw_scan(self): - try: - # TODO test line scan plots - - intensities = self.intensities.T #this is only there because of how we are saving the data in the app - intensities = np.reshape(intensities, newshape=(2048,self.numb_x_pixels, self.numb_y_pixels)) - self.ui.raw_scan_viewbox.setImage(intensities, scale= - (self.x_step_size, - self.y_step_size), xvals=self.wavelengths) - - #roi_plot = self.ui.raw_scan_viewBox.getRoiPlot() - #roi_plot.plot(data['Wavelengths'], intensities) - self.ui.raw_scan_viewbox.view.invertY(False) - scale = pg.ScaleBar(size=2,suffix='um') - scale.setParentItem(self.ui.raw_scan_viewbox.view) - scale.anchor((1, 1), (1, 1), offset=(-30, -30)) - self.ui.raw_scan_viewbox.view.sigRangeChanged.connect(lambda: updateDelay(scale, 10)) - - except Exception as e: - self.ui.result_textBrowser2.append(str(e)) - - def plot_intensity_sums(self): - try: - # TODO test line scan plots - - #intensities = np.reshape(intensities, newshape=(2048, numb_pixels_X*numb_pixels_Y)) - - sums = np.sum(self.intensities, axis=-1) - sums = np.reshape(sums, newshape=(self.numb_x_pixels, self.numb_y_pixels)) - - self.ui.intensity_sums_viewBox.setImage(sums, scale= - (self.x_step_size, - self.y_step_size)) - self.ui.intensity_sums_viewBox.view.invertY(False) - - scale = pg.ScaleBar(size=2,suffix='um') - scale.setParentItem(self.ui.intensity_sums_viewBox.view) - scale.anchor((1, 1), (1, 1), offset=(-30, -30)) - self.ui.intensity_sums_viewbox.view.sigRangeChanged.connect(lambda: updateDelay(scale, 10)) - - except Exception as e: - self.ui.result_textBrowser2.append(str(e)) - - - def fit_and_plot_scan(self): -# self.ui.result_textBrowser.append("Starting Scan Fitting") - print("Starting Scan Fitting") - - try: - """Define starting and stopping wavelength values here""" - start_nm = int(self.ui.start_nm_spinBox.value()) - stop_nm = int(self.ui.stop_nm_spinBox.value()) - - ref = self.bck_file - index = (ref[:,0]>start_nm) & (ref[:,0]start_nm) & (xstart_nm) & (ref[:,0]start_nm) & (x 0 0 - 1728 - 1052 + 2115 + 1483 @@ -127,10 +127,13 @@ - - - - Peak Center 1 (nm) + + + + 9999.000000000000000 + + + 780.000000000000000 @@ -144,13 +147,10 @@ - - - - 9999.000000000000000 - - - 780.000000000000000 + + + + Peak Center 1 (nm) @@ -408,13 +408,10 @@ - - - - 9999.000000000000000 - - - 770.000000000000000 + + + + Sigma 1 (nm) @@ -425,10 +422,13 @@ - - - - Sigma 1 (nm) + + + + 9999.000000000000000 + + + 770.000000000000000 @@ -612,6 +612,21 @@ + + + + + 10 + + + + Fit/Plot in eV + + + true + + + @@ -624,8 +639,21 @@ + + + + false + + + Save Entire Fit Model (File will be large and will take longer) + + + + + false + 10 @@ -759,43 +787,39 @@ Load Settings - - + + 10 - Background -File + Spectrum File - - + + 10 - Normalize + Export Figure - - + + 10 - Clear Plots Everytime - - - true + Clear Export Memory @@ -811,28 +835,39 @@ File - - + + 10 - For Single Spectrum + White Light Ref File - - + + + + + 10 + + + + Enter Legend Here... + + + + + 10 - White Light -Ref File + For Single Spectrum @@ -851,41 +886,43 @@ Ref File - - + + 10 - Spectrum -File + Export Fit Data - - + + 10 - Export data + Add Trace to Memory - - + + 10 + + Plot without Background + - + @@ -899,53 +936,83 @@ File - - + + 10 - Export Publication -Ready Figure + Background File - - + + 10 - Plot without Background + Clear Plots Everytime + + + true - - + + 10 - false - Clear Plot + Normalize - - + + 10 + false - Clear export data + Clear Plot + + + + + + + Export Settings + + + + + + + For Data + + + + + + + For Figure + + + + + + + Plot Control @@ -953,7 +1020,13 @@ Ready Figure - + + + + 10 + + + @@ -962,61 +1035,178 @@ Ready Figure Scan Spectra Data - - - - - - - - - 15 - - - - For Raw Scan Data - - - - - - - - 12 - - - - Plot - - - - - - - - - - 0 - 0 - - - + + + + + - 0 - 300 + 210 + 16777215 + + + 12 + + + + Load Settings + + + + + + + 12 + + + + Spectra Scan +File + + + + + + + + 12 + + + + Background +File + + + + + + + + 12 + + + + Load Only + Fit File + + + + + + + + 12 + + + + For Scan Data + + + + + + + + + + + 12 + + + + + + + + + 12 + + + + Export Fitted +Scan + - - - - + + + + 12 + + + + Launch Data Viewer + + + + + + + + 12 + + + + After Fitting Scan Data + + + + + + + + 12 + + + + Intensity Sums + + + + + + + + 12 + + + + For Raw Scan Data + + + + + + + + 12 + + + + Plot + + + + + + + + 12 + + + + Plot + + + + - 15 + 12 @@ -1029,82 +1219,63 @@ Ready Figure fwhm - - - sigma - - - - - height - - - - - - - 0 - 0 - - - - - 0 - 300 - - - - - - - - 2000 - - - 100 + + + + + 12 + - - - - - # X points + Plot - - + + + + + 12 + + - # Y points + Export Settings - - + + 12 - Plot + Analyze Spectra Fits - - - - 2000 + + + + + 12 + - - 100 + + For Fit Scale - + + + + 12 + + Use Raw Scan Settings @@ -1113,162 +1284,52 @@ Ready Figure - - + + - 15 + 12 - After Fitting Scan Data + # X points - - - - - - - - - - - 15 - - - - Intensity Sums - - - - - - - - 12 - - - - Plot - - - - + + + + + 12 + + + + # Y points + + - - - - - 0 - 0 - + + + + 2000 - - - 0 - 300 - + + 100 + + + + + + + 2000 + + + 100 - - - - Qt::Horizontal - - - - - - - - 210 - 16777215 - - - - - 15 - - - - Load Settings - - - - - - - 12 - - - - For Scan Data - - - - - - - - 12 - - - - Spectra Scan -File - - - - - - - - 12 - - - - Background -File - - - - - - - - 12 - - - - Load Only - Fit File - - - - - - - - 12 - - - - Export Fitted -Scan - - - - - - - - - @@ -1363,6 +1424,24 @@ Scan Import .pkl file + + + + 390 + 20 + 327 + 43 + + + + + 12 + + + + Launch Data Viewer + + @@ -1373,25 +1452,13 @@ Scan 0 0 - 1728 - 21 + 2115 + 38 - - - ImageView - QGraphicsView -
pyqtgraph
-
- - PlotWidget - QGraphicsView -
pyqtgraph
-
-
diff --git a/PythonGUI_apps/Spectrum_analysis/analyze_fit_results.ui b/PythonGUI_apps/Spectrum_analysis/analyze_fit_results.ui new file mode 100644 index 0000000..7669c5a --- /dev/null +++ b/PythonGUI_apps/Spectrum_analysis/analyze_fit_results.ui @@ -0,0 +1,91 @@ + + + Form + + + + 0 + 0 + 664 + 136 + + + + Analze Fit Results + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 12 + + + + Check! + + + + + + + + 12 + + + + 1000000000 + + + + + + + + 12 + + + + Result number: + + + Qt::AlignCenter + + + + + + + + 15 + + + + Analyze Fit Results + + + Qt::AlignCenter + + + + + + + + + + + + + diff --git a/PythonGUI_apps/Spectrum_analysis/peak_bounds_input.ui b/PythonGUI_apps/Spectrum_analysis/peak_bounds_input.ui deleted file mode 100644 index c532068..0000000 --- a/PythonGUI_apps/Spectrum_analysis/peak_bounds_input.ui +++ /dev/null @@ -1,122 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 359 - 600 - - - - MainWindow - - - - - - - - - - 15 - - - - Peak Center - - - - - - - - 12 - - - - Minimum (nm) - - - - - - - - 12 - - - - 300.000000000000000 - - - 1000.000000000000000 - - - 500.000000000000000 - - - - - - - - 12 - - - - Maximum (nm) - - - - - - - - 12 - - - - 300.000000000000000 - - - 1000.000000000000000 - - - 600.000000000000000 - - - - - - - - - - 15 - - - - Done! - - - - - - - - - 0 - 0 - 359 - 38 - - - - - - - - diff --git a/PythonGUI_apps/Spectrum_analysis/pyqtgraph_MATPLOTLIBWIDGET.py b/PythonGUI_apps/Spectrum_analysis/pyqtgraph_MATPLOTLIBWIDGET.py new file mode 100644 index 0000000..ba2f59c --- /dev/null +++ b/PythonGUI_apps/Spectrum_analysis/pyqtgraph_MATPLOTLIBWIDGET.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Sep 2 13:02:50 2019 + +@author: Sarthak +""" + +from pyqtgraph.Qt import QtGui, QtCore, USE_PYSIDE, USE_PYQT5 +import matplotlib + +#if not USE_PYQT5: +# if USE_PYSIDE: +# matplotlib.rcParams['backend.qt4']='PySide' +# +# from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +# from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar +#else: +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar + +from matplotlib.figure import Figure + +if matplotlib.get_backend() is not 'Qt5Agg': + matplotlib.use('Qt5Agg') + +class MatplotlibWidget(QtGui.QWidget): + """ + Implements a Matplotlib figure inside a QWidget. + Use getFigure() and redraw() to interact with matplotlib. + + Example:: + + mw = MatplotlibWidget() + subplot = mw.getFigure().add_subplot(111) + subplot.plot(x,y) + mw.draw() + """ + + def __init__(self, size=(5.0, 4.0), dpi=100): + QtGui.QWidget.__init__(self) + self.fig = Figure(size, dpi=dpi) + self.canvas = FigureCanvas(self.fig) + self.canvas.setParent(self) + self.toolbar = NavigationToolbar(self.canvas, self) + + self.vbox = QtGui.QVBoxLayout() + self.vbox.addWidget(self.toolbar) + self.vbox.addWidget(self.canvas) + + self.setLayout(self.vbox) + + def getFigure(self): + return self.fig + + def draw(self): + self.canvas.draw() \ No newline at end of file diff --git a/PythonGUI_apps/Table/Table_widget.py b/PythonGUI_apps/Table/Table_widget.py new file mode 100644 index 0000000..4c7b447 --- /dev/null +++ b/PythonGUI_apps/Table/Table_widget.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Sep 2 17:04:49 2019 + +@author: Sarthak +""" + +from pathlib import Path +import pyqtgraph as pg +from pyqtgraph.python2_3 import asUnicode +from pyqtgraph.Qt import QtCore, QtGui + + +pg.mkQApp() +pg.setConfigOption('background', 'w') + + +base_path = Path(__file__).parent +file_path = (base_path / "Table_widget_gui.ui").resolve() + +uiFile = file_path + +WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) + +class MainWindow(TemplateBaseClass): + + def __init__(self): + super(TemplateBaseClass, self).__init__() + + # Create the main window + self.ui = WindowTemplate() + self.ui.setupUi(self) + + self.clear() + + self.ui.clear_pushButton.clicked.connect(self.clear) + self.ui.add_row_pushButton.clicked.connect(self.add_row) + self.ui.add_column_pushButton.clicked.connect(self.add_column) + + """Saving and Copying --- implemented from pyqtgraph TableWidget""" + self.contextMenu = QtGui.QMenu() + self.contextMenu.addAction('Copy Selection').triggered.connect(self.copySel) + self.contextMenu.addAction('Copy All').triggered.connect(self.copyAll) + self.contextMenu.addAction('Save Selection').triggered.connect(self.saveSel) + self.contextMenu.addAction('Save All').triggered.connect(self.saveAll) + + self.show() + + def clear(self): + self.ui.tableWidget.clear() + self.verticalHeadersSet = False + self.horizontalHeadersSet = False + + def add_row(self): + row_position = self.ui.tableWidget.rowCount() + self.ui.tableWidget.insertRow(row_position) + + def add_column(self): + column_position = self.ui.tableWidget.columnCount() + self.ui.tableWidget.insertColumn(column_position) + + def save_table(self):# Needs to be implemented + print(self.ui.tableWidget.currentItem().text()) + + def serialize(self, useSelection=False): + """Convert entire table (or just selected area) into tab-separated text values""" + if useSelection: + selection = self.ui.tableWidget.selectedRanges()[0] + rows = list(range(selection.topRow(), + selection.bottomRow() + 1)) + columns = list(range(selection.leftColumn(), + selection.rightColumn() + 1)) + else: + rows = list(range(self.ui.tableWidget.rowCount())) + columns = list(range(self.ui.tableWidget.columnCount())) + + data = [] + if self.horizontalHeadersSet: + row = [] + if self.verticalHeadersSet: + row.append(asUnicode('')) + + for c in columns: + row.append(asUnicode(self.ui.tableWidget.horizontalHeaderItem(c).text())) + data.append(row) + + for r in rows: + row = [] + if self.verticalHeadersSet: + row.append(asUnicode(self.ui.tableWidget.verticalHeaderItem(r).text())) + for c in columns: + item = self.ui.tableWidget.item(r, c) + if item is not None: + row.append(asUnicode(item.text())) + else: + row.append(asUnicode('')) + data.append(row) + + s = '' + for row in data: + s += ('\t'.join(row) + '\n') + return s + + + def copySel(self): + """Copy selected data to clipboard.""" + QtGui.QApplication.clipboard().setText(self.serialize(useSelection=True)) + + def copyAll(self): + """Copy all data to clipboard.""" + QtGui.QApplication.clipboard().setText(self.serialize(useSelection=False)) + + + def saveSel(self): + """Save selected data to file.""" + self.save(self.serialize(useSelection=True)) + + + def saveAll(self): + """Save all data to file.""" + self.save(self.serialize(useSelection=False)) + + + def save(self, data): + fileName = QtGui.QFileDialog.getSaveFileName(self, "Save As..", "", "Tab-separated values (*.tsv)") + if fileName == '': + return + open(fileName[0], 'w').write(data) + + def contextMenuEvent(self, ev): + self.contextMenu.popup(ev.globalPos()) + + def keyPressEvent(self, ev): + if ev.key() == QtCore.Qt.Key_C and ev.modifiers() == QtCore.Qt.ControlModifier: + ev.accept() + self.copySel() +# else: +# QtGui.QTableWidget.keyPressEvent(self, ev) +"""Run the Main Window""" +def run(): + win = MainWindow() + QtGui.QApplication.instance().exec_() + return win + +#run() \ No newline at end of file diff --git a/PythonGUI_apps/Table/Table_widget_gui.ui b/PythonGUI_apps/Table/Table_widget_gui.ui new file mode 100644 index 0000000..d5fae7e --- /dev/null +++ b/PythonGUI_apps/Table/Table_widget_gui.ui @@ -0,0 +1,70 @@ + + + Form + + + + 0 + 0 + 658 + 438 + + + + TableWidget + + + + + + Add Row + + + + + + + Add Column + + + + + + + Clear + + + + + + + 12 + + + 6 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PythonGUI_apps/Table/__init__.py b/PythonGUI_apps/Table/__init__.py new file mode 100644 index 0000000..1a21e72 --- /dev/null +++ b/PythonGUI_apps/Table/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Sep 2 17:52:35 2019 + +@author: Sarthak +""" + diff --git a/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py index 44e2ffb..6302a40 100644 --- a/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py +++ b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py @@ -52,6 +52,8 @@ def __init__(self): self.correction_region.setRegion((self.correction_region_min, self.correction_region_max)) #setup uv vis ui signals + self.ui.correct_for_scattering_checkBox.stateChanged.connect(self.scattering_checkBox_state) + self.scatter_corrected = False self.ui.actionLoad_data.triggered.connect(self.open_data_file) self.ui.plot_absorbance_pushButton.clicked.connect(self.plot_absorbance) self.ui.clear_uvvis_pushButton.clicked.connect(self.clear_uvvis) @@ -76,7 +78,7 @@ def __init__(self): def open_data_file(self): try: self.filename = QtWidgets.QFileDialog.getOpenFileName(self) - self.data = np.loadtxt(self.filename[0], delimiter = ',', skiprows = 1) + self.data = np.loadtxt(self.filename[0], delimiter = ',', skiprows = 2) self.Wavelength = self.data[:,0] # in nm self.Absorbance = self.data[:,1] except Exception as err: @@ -85,14 +87,28 @@ def open_data_file(self): def update_correction_region(self): """ Update correction region variables from region """ self.correction_region_min, self.correction_region_max = self.correction_region.getRegion() + + def scattering_checkBox_state(self): + if self.ui.correct_for_scattering_checkBox.isChecked(): + self.scatter_corrected = True + self.ui.mean_radioButton.setEnabled(True) + self.ui.fourth_orderpoly_radioButton.setEnabled(True) + else: + self.scatter_corrected = False + self.ui.mean_radioButton.setEnabled(False) + self.ui.fourth_orderpoly_radioButton.setEnabled(False) def plot_absorbance(self): try: - self.scatter_corrected = False self.plotted_absorbance = self.Absorbance #by default set to original absorbance data - if self.ui.correct_for_scattering_checkBox.isChecked(): #if checked, correct absorbance data - self.scatter_corrected = True - self.plotted_absorbance = self.Absorbance - np.mean(self.Absorbance[(self.Wavelength>self.correction_region_min) & (self.Wavelengthself.correction_region_min) & (self.Wavelengthself.correction_region_min) & (self.Wavelengthself.correction_region_min) & (self.Wavelengthself.correction_region_min) & (self.Wavelengthself.correction_region_min) & (self.Wavelengthself.correction_region_min) & (self.Wavelength self.hv_min) & (self.hv < self.hv_max) model = np.polyfit(self.hv[self.index], self.Alpha_hv[self.index], 1) self.Alpha_hv_fit = self.hv * model[0] + model[1] #This is the linear fit diff --git a/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui index c3289df..85bd935 100644 --- a/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui +++ b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui @@ -23,27 +23,57 @@ - + + + + Scattering Correction + + + + Plot - - + + + + false + - Correct for scattering + 4th Order Polynomial + + + true - + Clear + + + + Correct for scattering + + + + + + + false + + + Simple Mean + + + diff --git a/README.md b/README.md index a1cea03..a000a87 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,14 @@ _**Python is not required to use GLabViz**_ (see **How to use?**) The primary users for this Python package application are Ginger Lab members at the University of Washington, Seattle but is licensed under MIT License and open for everyone to use. ## Includes + * **Fluorescence Lifetime Analysis** * Analyze lifetime * Fit data with or without IRF * Fit with stretched, single, or double exponential functions by diff_ev or fmin_tnc * Calculate surface recombination velocity * Export graph and fit results + * **Spectra Analysis** * Analyze single spectrum * Fit with or without background and white light @@ -26,26 +28,32 @@ The primary users for this Python package application are Ginger Lab members at * Plot fitted scan by pk_pos, fwhm, sigma, or height * Export fitted scan * .pkl to .txt, .pkl to .h5 converters + * **Fluorescence Lifetime Imaging Microscopy (FLIM) Data Analysis** * Load lifetime scans in .h5 or .pkl files * Plot histogram intensity sums and analyze PSF * Export intensities array and intensities image * Plot raw histogram data and analyze lifetime * Compare lifetime in two different regions + * **Photluminescence Quantum Efficiency (PLQE) Analysis** * Plot PLQE data * Calculate PLQE + * **UV-Vis Data Analysis** * Plot UV-Vis data * Correct UV-Vis data for scattering * Plot Tauc data * Calculate bandgap * Export UV-Vis and Tauc plots + * **General *H5* View and Plot** * Load .h5 file to view file structure * Plot datasets as a graph or an image + * **H5 and PKL File Viewer** * Load .h5 or .pkl file to view file structure + * **Image Analysis** * Load image on SPOT or Pixera settings, or specify pixel size * Handle RGB and greyscale images