├── .gitignore ├── LICENSE ├── PythonGUI_apps ├── DataBrowser.py ├── DataBrowser.spec ├── DataBrowser_GUI.ui ├── Export_Windows │ ├── Export_window.py │ ├── Multi_Trace_Exporter.py │ ├── Multi_Trace_Exporter.ui │ ├── __init__.py │ ├── export_fig_gui.ui │ └── export_plot.ui ├── FLIM_analysis │ ├── FLIM_plot.py │ ├── flim_plot_gui.ui │ └── step_size_labview_files.ui ├── H5_Pkl │ ├── h5_pkl_view.py │ ├── h5_pkl_view_gui.ui │ ├── h5_tree.py │ ├── h5_view_and_plot.py │ ├── h5_view_and_plot_gui.ui │ └── pkl_tree.py ├── Image_analysis │ ├── Image_analysis.py │ └── image_analysis_gui.ui ├── Lifetime_analysis │ ├── Fit_functions.py │ ├── Fit_functions_with_irf.py │ ├── Lifetime_analysis_gui_layout.ui │ ├── Lifetime_plot_fit.py │ ├── __init__.py │ ├── picoharp_phd.py │ ├── read_ph_phd.py │ └── skip_rows.ui ├── OceanOptics_acquire │ ├── OO_PZstageScan_acquire_gui.ui │ ├── OO_acquire_gui.ui │ └── OceanOptics_acquire_plot.py ├── PLQE_analysis │ ├── column_selection_gui.ui │ ├── plqe_analysis.py │ └── plqe_analysis_gui.ui ├── Spectrum_analysis │ ├── Spectra_fit_funcs.py │ ├── Spectra_plot_fit.py │ ├── Spectra_plot_fit_gui.ui │ ├── __init__.py │ ├── analyze_fit_results.ui │ ├── pyqtgraph_MATPLOTLIBWIDGET.py │ └── scan_params_input.ui ├── Table │ ├── Table_widget.py │ ├── Table_widget_gui.ui │ └── __init__.py ├── UV_Vis_analysis │ ├── uv_vis_analysis.py │ └── uv_vis_analysis_gui.ui └── __init__.py ├── README.md ├── Screenshots ├── GLabViz_FLIM_analysis_1.PNG ├── GLabViz_FLIM_analysis_2.png ├── GLabViz_Image_analysis_1.png ├── GLabViz_Lifetime_analysis_2.png ├── GLabViz_Spectrum_analysis_1.PNG ├── GLabViz_Spectrum_analysis_1.png ├── GLabViz_Spectrum_analysis_2.png ├── GLabViz_UVvis_analysis_1.PNG ├── GLabViz_h5_ViewPlot_analysis_1.PNG ├── GLabViz_h5_ViewPlot_analysis_2.PNG └── GLabViz_interface_1.png └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 SarthakJariwala 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PythonGUI_apps/DataBrowser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 10 14:41:08 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | # system imports 9 | import sys 10 | from pathlib import Path 11 | 12 | import pyqtgraph as pg 13 | from pyqtgraph.Qt import QtGui, QtCore 14 | 15 | from Lifetime_analysis import Lifetime_plot_fit 16 | from Spectrum_analysis import Spectra_plot_fit 17 | from FLIM_analysis import FLIM_plot 18 | from UV_Vis_analysis import uv_vis_analysis 19 | from PLQE_analysis import plqe_analysis 20 | from H5_Pkl import h5_pkl_view, h5_view_and_plot 21 | from Image_analysis import Image_analysis 22 | from Table import Table_widget 23 | from Export_Windows import Multi_Trace_Exporter 24 | 25 | pg.mkQApp() 26 | #pg.setConfigOption('background', 'w') 27 | 28 | base_path = Path(__file__).parent 29 | file_path = (base_path / "DataBrowser_GUI.ui").resolve() 30 | 31 | uiFile = file_path 32 | 33 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 34 | 35 | class MainWindow(TemplateBaseClass): 36 | 37 | def __init__(self): 38 | TemplateBaseClass.__init__(self) 39 | 40 | # Create the main window 41 | self.ui = WindowTemplate() 42 | self.ui.setupUi(self) 43 | self.ui.select_comboBox.addItems(["Lifetime Analysis", "Spectrum Analysis", "FLIM Analysis", 44 | "UV-Vis Analysis", "PLQE Analysis", "H5 View/Plot", "H5/PKL Viewer", "Image Analysis", "Table View", 45 | "Mulit-Trace Exporter"]) 46 | self.ui.load_pushButton.clicked.connect(self.load_app) 47 | 48 | self.show() 49 | 50 | 51 | def load_app(self): 52 | 53 | analysis_software = self.ui.select_comboBox.currentText() 54 | 55 | if analysis_software == "Lifetime Analysis": 56 | self.lifetime_window = Lifetime_plot_fit.MainWindow() 57 | self.lifetime_window.show() 58 | elif analysis_software == "Spectrum Analysis": 59 | self.spectrum_window = Spectra_plot_fit.MainWindow() 60 | self.spectrum_window.show() 61 | elif analysis_software == "FLIM Analysis": 62 | self.flim_window = FLIM_plot.MainWindow() 63 | self.flim_window.show() 64 | elif analysis_software == "UV-Vis Analysis": 65 | self.uv_vis_window = uv_vis_analysis.MainWindow() 66 | self.uv_vis_window.show() 67 | elif analysis_software == "PLQE Analysis": 68 | self.plqe_window = plqe_analysis.MainWindow() 69 | self.plqe_window.show() 70 | elif analysis_software == "H5 View/Plot": 71 | app = h5_view_and_plot.H5ViewPlot(sys.argv) 72 | #sys.exit(app.exec_()) 73 | elif analysis_software == "H5/PKL Viewer": 74 | app = h5_pkl_view.H5PklView(sys.argv) 75 | #sys.exit(app.exec_()) 76 | elif analysis_software == "Image Analysis": 77 | self.image_window = Image_analysis.MainWindow() 78 | self.image_window.show() 79 | elif analysis_software == "Table View": 80 | self.table_widget = Table_widget.MainWindow() 81 | self.table_widget.show() 82 | elif analysis_software == "Mulit-Trace Exporter": 83 | self.trace_exporter = Multi_Trace_Exporter.MainWindow() 84 | self.trace_exporter.show() 85 | 86 | 87 | def run(): 88 | app = QtGui.QApplication(sys.argv)#.instance() 89 | app.setStyle("Fusion") 90 | win = MainWindow() 91 | sys.exit(app.exec_()) 92 | return 93 | 94 | run() -------------------------------------------------------------------------------- /PythonGUI_apps/DataBrowser.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | block_cipher = None 4 | 5 | 6 | a = Analysis(['DataBrowser.py'], 7 | pathex=['E:\\QT_projects\\Python_GUI_apps\\PythonGUI_apps'], 8 | binaries=[], 9 | datas=[], 10 | hiddenimports=['ipykernel.datapub'], 11 | hookspath=[], 12 | runtime_hooks=[], 13 | excludes=[], 14 | win_no_prefer_redirects=False, 15 | win_private_assemblies=False, 16 | cipher=block_cipher, 17 | noarchive=False) 18 | pyz = PYZ(a.pure, a.zipped_data, 19 | cipher=block_cipher) 20 | exe = EXE(pyz, 21 | a.scripts, 22 | [], 23 | exclude_binaries=True, 24 | name='DataBrowser', 25 | debug=False, 26 | bootloader_ignore_signals=False, 27 | strip=False, 28 | upx=True, 29 | console=True ) 30 | coll = COLLECT(exe, 31 | a.binaries, 32 | a.zipfiles, 33 | a.datas, 34 | strip=False, 35 | upx=True, 36 | upx_exclude=[], 37 | name='DataBrowser') 38 | -------------------------------------------------------------------------------- /PythonGUI_apps/DataBrowser_GUI.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 435 10 | 221 11 | 12 | 13 | 14 | GLabViz - DataBrowser 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 23 | 75 24 | true 25 | 26 | 27 | 28 | GLabViz 29 | 30 | 31 | Qt::AlignCenter 32 | 33 | 34 | 35 | 36 | 37 | 38 | Qt::Vertical 39 | 40 | 41 | 42 | 20 43 | 40 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 15 53 | 54 | 55 | 56 | Analysis Tool : 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 15 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 15 74 | 75 | 76 | 77 | Load 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 0 87 | 0 88 | 435 89 | 21 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /PythonGUI_apps/Export_Windows/Export_window.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Nov 3 20:43:12 2019 4 | 5 | @author: sarth 6 | """ 7 | from pathlib import Path 8 | import pyqtgraph as pg 9 | from pyqtgraph.Qt import QtCore, QtGui 10 | 11 | """Export Images GUI""" 12 | base_path = Path(__file__).parent 13 | ui_file_path = (base_path / "export_fig_gui.ui").resolve() 14 | exportFig_WindowTemplate, exportFig_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) 15 | 16 | class ExportFigureWindow(exportFig_TemplateBaseClass): 17 | 18 | export_fig_signal = QtCore.pyqtSignal() 19 | 20 | def __init__(self): 21 | exportFig_TemplateBaseClass.__init__(self) 22 | 23 | self.ui = exportFig_WindowTemplate() 24 | self.ui.setupUi(self) 25 | self.ui.cmap_comboBox.addItems(['viridis', 'plasma', 'inferno', 'magma', 26 | 'cividis','Greys', 'Purples', 'Blues', 27 | 'Greens', 'Oranges', 'Reds', 'YlOrBr', 28 | 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', 29 | 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 30 | 'YlGn', 'binary', 'gist_yarg', 'gist_gray', 31 | 'gray', 'bone', 'pink', 'spring', 'summer', 32 | 'autumn', 'winter', 'cool', 'Wistia', 'hot', 33 | 'afmhot', 'gist_heat', 'copper', 'rainbow', 'jet']) 34 | self.ui.cbar_checkBox.stateChanged.connect(self.cbar_title_state) 35 | self.ui.exportFig_pushButton.clicked.connect(self.export) 36 | self.show() 37 | 38 | def cbar_title_state(self): 39 | if self.ui.cbar_checkBox.isChecked(): 40 | self.ui.cbar_label.setEnabled(True) 41 | else: 42 | self.ui.cbar_label.setEnabled(False) 43 | 44 | def export(self): 45 | self.export_fig_signal.emit() 46 | self.close() 47 | 48 | """Export plot GUI""" 49 | ui_file_path = (base_path / "export_plot.ui").resolve() 50 | export_WindowTemplate, export_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) 51 | 52 | class ExportPlotWindow(export_TemplateBaseClass): 53 | 54 | export_fig_signal = QtCore.pyqtSignal() 55 | 56 | def __init__(self): 57 | export_TemplateBaseClass.__init__(self) 58 | 59 | self.ui = export_WindowTemplate() 60 | self.ui.setupUi(self) 61 | #self.ui.traceColor_comboBox.addItems(["C0","C1","C2","C3","C4","C5","C6","C7", "r", "g", "b", "y", "k"]) 62 | #self.ui.fitColor_comboBox.addItems(["k", "r", "b", "y", "g","C0","C1","C2","C3","C4","C5","C6","C7"]) 63 | self.ui.export_pushButton.clicked.connect(self.export) 64 | #self.ui.legend_checkBox.stateChanged.connect(self.legend_title) 65 | self.show() 66 | 67 | #def legend_title(self): 68 | # if self.ui.legend_checkBox.isChecked(): 69 | # self.ui.legend1_lineEdit.setEnabled(True) 70 | # self.ui.legend2_lineEdit.setEnabled(True) 71 | # else: 72 | # self.ui.legend1_lineEdit.setEnabled(False) 73 | # self.ui.legend2_lineEdit.setEnabled(False) 74 | 75 | def export(self): 76 | self.export_fig_signal.emit() 77 | self.close() -------------------------------------------------------------------------------- /PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.py: -------------------------------------------------------------------------------- 1 | import pyqtgraph as pg 2 | from pathlib import Path 3 | from pyqtgraph.Qt import QtCore, QtGui, QtWidgets 4 | try: 5 | from Lifetime_analysis.read_ph_phd import read_picoharp_phd, get_x_y 6 | except Exception as e: 7 | print(e) 8 | import matplotlib.pyplot as plt 9 | 10 | """Recylce params for plotting""" 11 | plt.rc('xtick', labelsize = 20) 12 | plt.rc('xtick.major', pad = 3) 13 | plt.rc('ytick', labelsize = 20) 14 | plt.rc('lines', lw = 2.5, markersize = 7.5) 15 | plt.rc('legend', fontsize = 20) 16 | plt.rc('axes', linewidth=3.5) 17 | 18 | pg.mkQApp() 19 | 20 | base_path = Path(__file__).parent 21 | file_path = (base_path / "Multi_Trace_Exporter.ui").resolve() 22 | 23 | uiFile = file_path 24 | 25 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 26 | 27 | class MainWindow(TemplateBaseClass): 28 | 29 | def __init__(self): 30 | super(TemplateBaseClass, self).__init__() 31 | 32 | # Create the main window 33 | self.ui = WindowTemplate() 34 | self.ui.setupUi(self) 35 | 36 | self.temp_layout = pg.GraphicsLayoutWidget() 37 | 38 | # file system tree 39 | self.fs_model = QtWidgets.QFileSystemModel() 40 | self.fs_model.setRootPath(QtCore.QDir.currentPath()) 41 | self.ui.treeView.setModel(self.fs_model) 42 | self.ui.treeView.setIconSize(QtCore.QSize(25,25)) 43 | self.ui.treeView.setSortingEnabled(True) 44 | 45 | self.tree_selectionModel = self.ui.treeView.selectionModel() 46 | self.tree_selectionModel.selectionChanged.connect(self.on_treeview_selection_change) 47 | 48 | self.ui.comboBox.currentIndexChanged.connect(self.add_trace_to_temp_plot) 49 | self.ui.add_pushButton.clicked.connect(self.add_trace_to_mem) 50 | self.ui.export_pushButton.clicked.connect(self.pub_ready_plot_export) 51 | 52 | self.x_i = [] 53 | self.y_i = [] 54 | self.x_mem = [] 55 | self.y_mem = [] 56 | self.legend = [] 57 | 58 | self.show() 59 | 60 | def on_treeview_selection_change(self): 61 | try: 62 | fname = self.fs_model.filePath(self.tree_selectionModel.currentIndex()) 63 | _ , ext = fname.rsplit('.',1) 64 | 65 | self.ui.comboBox.clear() 66 | self.ui.textBrowser.clear() 67 | self.x_i = [] 68 | self.y_i = [] 69 | 70 | if ext in ['phd']: 71 | self.parser = read_picoharp_phd(fname) 72 | curve_list = [] 73 | 74 | for i in range(self.parser.no_of_curves()): 75 | curve_list.append("Curve "+str(i)) 76 | x, y = get_x_y(i, self.parser, smooth_trace=self.ui.smooth_checkBox.isChecked(), boxwidth=self.ui.smooth_spinBox.value()) 77 | self.x_i.append(x) 78 | self.y_i.append(y) 79 | 80 | self.ui.comboBox.addItems(curve_list) 81 | self.ui.textBrowser.setText(str(self.parser.info())) 82 | 83 | else: 84 | self.ui.textBrowser.setText(str("Select a PicoHarp File")) 85 | except Exception as e: 86 | print(e) 87 | 88 | def add_trace_to_temp_plot(self): 89 | try: 90 | #self.temp_layout = pg.GraphicsLayoutWidget() 91 | self.temp_layout.clear() 92 | self.temp_plot = self.temp_layout.addPlot(title = "Current Selection") 93 | self.temp_plot.plot(self.x_i[self.ui.comboBox.currentIndex()], self.y_i[self.ui.comboBox.currentIndex()], pen='r') 94 | self.temp_plot.setLogMode(False, True) 95 | self.temp_layout.show() 96 | except Exception as e: 97 | print(e) 98 | 99 | def add_trace_to_mem(self): 100 | try: 101 | self.x_mem.append(self.x_i[self.ui.comboBox.currentIndex()]) 102 | self.y_mem.append(self.y_i[self.ui.comboBox.currentIndex()]) 103 | self.legend.append(self.ui.lineEdit.text()) 104 | except Exception as e: 105 | print(e) 106 | 107 | def pub_ready_plot_export(self): 108 | try: 109 | filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") 110 | 111 | plt.figure(figsize=(8,6)) 112 | plt.tick_params(direction='out', length=8, width=3.5) 113 | for i in range(len(self.x_mem)): 114 | if self.ui.Normalize_checkBox.isChecked(): 115 | plt.plot(self.x_mem[i], self.y_mem[i]/max(self.y_mem[i]), label=str(self.legend[i])) 116 | else: 117 | plt.plot(self.x_mem[i], self.y_mem[i], label=str(self.legend[i])) 118 | 119 | plt.yscale('log') 120 | plt.xlabel("Time (ns)", fontsize=20, fontweight='bold') 121 | plt.ylabel("Intensity (norm.)", fontsize=20, fontweight='bold') 122 | plt.legend() 123 | plt.tight_layout() 124 | 125 | plt.savefig(filename[0],bbox_inches='tight', dpi=300) 126 | plt.close() 127 | 128 | self.clear_memory() 129 | 130 | except Exception as e: 131 | print(e) 132 | pass 133 | 134 | def clear_memory(self): 135 | self.x_mem = [] 136 | self.y_mem = [] 137 | self.legend = [] 138 | 139 | 140 | 141 | 142 | def run(): 143 | win = MainWindow() 144 | QtGui.QApplication.instance().exec_() 145 | return win 146 | 147 | #run() -------------------------------------------------------------------------------- /PythonGUI_apps/Export_Windows/Multi_Trace_Exporter.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1108 10 | 1063 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Smoothen Trace 25 | 26 | 27 | 28 | 29 | 30 | 31 | Enter Trace Legend Here 32 | 33 | 34 | 35 | 36 | 37 | 38 | Add 39 | 40 | 41 | 42 | 43 | 44 | 45 | Normalize (for export) 46 | 47 | 48 | 49 | 50 | 51 | 52 | 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | Export 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 0 75 | 0 76 | 1108 77 | 38 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /PythonGUI_apps/Export_Windows/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /PythonGUI_apps/Export_Windows/export_fig_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ExportFigure 4 | 5 | 6 | 7 | 0 8 | 0 9 | 420 10 | 369 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 15 22 | 23 | 24 | 25 | 1000000000 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 15 34 | 35 | 36 | 37 | Color Bar Label 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 15 46 | 47 | 48 | 49 | Data Channel to Save 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 15 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 15 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 15 76 | 77 | 78 | 79 | ColorMap 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 15 88 | 89 | 90 | 91 | 1000000000 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 15 100 | 101 | 102 | 103 | ColorBar Min 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 15 112 | 113 | 114 | 115 | ColorBar Max 116 | 117 | 118 | 119 | 120 | 121 | 122 | false 123 | 124 | 125 | 126 | 15 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 15 136 | 137 | 138 | 139 | Export Figure 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 15 148 | 149 | 150 | 151 | Reversed 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 15 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /PythonGUI_apps/Export_Windows/export_plot.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 930 10 | 435 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 10 22 | 23 | 24 | 25 | 4 26 | 27 | 28 | -100.000000000000000 29 | 30 | 31 | 10000000000000000000000.000000000000000 32 | 33 | 34 | 1.500000000000000 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 10 43 | 44 | 45 | 46 | Lower 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 10 55 | 56 | 57 | 58 | Lower 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 10 67 | 68 | 69 | 70 | Upper 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 15 79 | 80 | 81 | 82 | Export Graph 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 12 91 | 92 | 93 | 94 | Y limits 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 10 103 | 104 | 105 | 106 | Upper 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 12 115 | 116 | 117 | 118 | X limits 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 10 127 | 128 | 129 | 130 | 4 131 | 132 | 133 | -10000.000000000000000 134 | 135 | 136 | 1000000000000000000.000000000000000 137 | 138 | 139 | 0.010000000000000 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 10 148 | 149 | 150 | 151 | 4 152 | 153 | 154 | -10000000.000000000000000 155 | 156 | 157 | 100000000000.000000000000000 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 10 166 | 167 | 168 | 169 | 4 170 | 171 | 172 | -1000000000.000000000000000 173 | 174 | 175 | 10000000000000.000000000000000 176 | 177 | 178 | 10000.000000000000000 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /PythonGUI_apps/FLIM_analysis/flim_plot_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 738 10 | 876 11 | 12 | 13 | 14 | FLIM Analysis 15 | 16 | 17 | 18 | 19 | 20 | 0 21 | 22 | 23 | 24 | Analysis 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Load Scan 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Plot 42 | 43 | 44 | 45 | 46 | 47 | 48 | Save intensities array 49 | 50 | 51 | 52 | 53 | 54 | 55 | Save intensities image 56 | 57 | 58 | 59 | 60 | 61 | 62 | Analyze PSF 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 500 73 | 300 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 12 83 | 84 | 85 | 86 | Histogram Intensity Sums 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 12 95 | 96 | 97 | 98 | Raw Histogram Data 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 500 107 | 300 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | Plot 118 | 119 | 120 | 121 | 122 | 123 | 124 | Analyze lifetime 125 | 126 | 127 | 128 | 129 | 130 | 131 | Compare ROIs 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | .pkl to .h5 144 | 145 | 146 | 147 | 148 | 20 149 | 20 150 | 171 151 | 34 152 | 153 | 154 | 155 | Import .pkl file 156 | 157 | 158 | 159 | 160 | 161 | 20 162 | 80 163 | 171 164 | 34 165 | 166 | 167 | 168 | .pkl to .h5 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | ImageView 179 | QGraphicsView 180 |
pyqtgraph
181 |
182 |
183 | 184 | 185 |
186 | -------------------------------------------------------------------------------- /PythonGUI_apps/FLIM_analysis/step_size_labview_files.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 255 10 | 75 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 12 22 | 23 | 24 | 25 | Step Size (um) 26 | 27 | 28 | 29 | 30 | 31 | 32 | Done! 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 12 41 | 42 | 43 | 44 | 4 45 | 46 | 47 | 100.000000000000000 48 | 49 | 50 | 0.100000000000000 51 | 52 | 53 | 0.100000000000000 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /PythonGUI_apps/H5_Pkl/h5_pkl_view.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, absolute_import 2 | from ScopeFoundry import BaseApp 3 | from ScopeFoundry.helper_funcs import load_qt_ui_file, sibling_path 4 | from collections import OrderedDict 5 | import os 6 | from qtpy import QtCore, QtWidgets, QtGui 7 | import pyqtgraph as pg 8 | import pyqtgraph.dockarea as dockarea 9 | import numpy as np 10 | from ScopeFoundry.logged_quantity import LQCollection 11 | from scipy.stats import spearmanr 12 | import argparse 13 | from .h5_tree import H5TreeSearchView 14 | from .pkl_tree import PklTreeSearchView 15 | 16 | pg.setConfigOption('imageAxisOrder', 'row-major') 17 | 18 | class H5PklView(BaseApp): 19 | 20 | name = "h5_pkl_view" 21 | 22 | def __init__(self, argv): 23 | BaseApp.__init__(self, argv) 24 | self.setup() 25 | parser = argparse.ArgumentParser() 26 | for lq in self.settings.as_list(): 27 | parser.add_argument("--" + lq.name) 28 | args = parser.parse_args() 29 | for lq in self.settings.as_list(): 30 | if lq.name in args: 31 | val = getattr(args,lq.name) 32 | if val is not None: 33 | lq.update_value(val) 34 | 35 | def setup(self): 36 | self.ui_filename = sibling_path(__file__, "h5_pkl_view_gui.ui") 37 | self.ui = load_qt_ui_file(self.ui_filename) 38 | self.ui.show() 39 | self.ui.raise_() 40 | 41 | self.views = OrderedDict() 42 | 43 | self.settings.New('data_filename', dtype='file') 44 | self.settings.New('auto_select_view',dtype=bool, initial=True) 45 | self.settings.New('view_name', dtype=str, initial='0', choices=('0',)) 46 | 47 | self.settings.data_filename.add_listener(self.on_change_data_filename) 48 | 49 | # UI Connections/ 50 | self.settings.data_filename.connect_to_browse_widgets(self.ui.data_filename_lineEdit, 51 | self.ui.data_filename_browse_pushButton) 52 | 53 | # set views 54 | self.h5treeview = H5TreeSearchView(self) 55 | self.load_view(self.h5treeview) 56 | self.pkltreeview = PklTreeSearchView(self) 57 | self.load_view(self.pkltreeview) 58 | 59 | self.settings.view_name.add_listener(self.on_change_view_name) 60 | 61 | self.current_view = None 62 | 63 | self.ui.show() 64 | 65 | def load_view(self, new_view): 66 | # add to views dict 67 | self.views[new_view.name] = new_view 68 | 69 | self.ui.dataview_page.layout().addWidget(new_view.ui) 70 | new_view.ui.hide() 71 | 72 | # update choices for view_name 73 | self.settings.view_name.change_choice_list(list(self.views.keys())) 74 | return new_view 75 | 76 | def on_change_data_filename(self): 77 | #Handle file change 78 | try: 79 | fname = self.settings.data_filename.val 80 | if not self.settings['auto_select_view']: 81 | self.current_view.on_change_data_filename(fname) 82 | else: 83 | view_name = self.auto_select_view(fname) 84 | if self.current_view is None or view_name != self.current_view.name: 85 | # update view (automatically calls on_change_data_filename) 86 | self.settings['view_name'] = view_name 87 | else: 88 | # force update 89 | if os.path.isfile(fname): 90 | self.current_view.on_change_data_filename(fname) 91 | except: 92 | pass 93 | 94 | def on_change_view_name(self): 95 | #Handle view change - happens when filetype changes 96 | self.ui.dataview_placeholder.hide() 97 | previous_view = self.current_view 98 | 99 | self.current_view = self.views[self.settings['view_name']] 100 | # hide current view 101 | # (handle the initial case where previous_view is None ) 102 | if previous_view: 103 | previous_view.ui.hide() 104 | 105 | # show new view 106 | self.current_view.ui.show() 107 | 108 | # set datafile for new (current) view 109 | fname = self.settings['data_filename'] 110 | if os.path.isfile(fname): 111 | self.current_view.on_change_data_filename(self.settings['data_filename']) 112 | 113 | def auto_select_view(self, fname): 114 | #return the name of the last supported view for the given fname 115 | for view_name, view in list(self.views.items())[::-1]: 116 | if view.is_file_supported(fname): 117 | return view_name 118 | # return default file_info view if no others work 119 | return "h5_tree_search" 120 | 121 | # if __name__ == '__main__': 122 | # import sys 123 | # app = H5PklView(sys.argv) 124 | # sys.exit(app.exec_()) 125 | -------------------------------------------------------------------------------- /PythonGUI_apps/H5_Pkl/h5_pkl_view_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 856 10 | 925 11 | 12 | 13 | 14 | H5/pkl View 15 | 16 | 17 | 18 | 19 | 20 | 21 | Qt::Horizontal 22 | 23 | 24 | 25 | File 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | File: 35 | 36 | 37 | 38 | 39 | 40 | 41 | Browse... 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | 0 52 | 53 | 54 | 55 | Data View 56 | 57 | 58 | 59 | 60 | 61 | Once a file is loaded, data will show up here. 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 0 79 | 0 80 | 856 81 | 21 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /PythonGUI_apps/H5_Pkl/h5_tree.py: -------------------------------------------------------------------------------- 1 | from ScopeFoundry.data_browser import DataBrowserView 2 | from qtpy import QtWidgets 3 | import h5py 4 | 5 | class H5TreeSearchView(DataBrowserView): 6 | 7 | name = 'h5_tree_search' 8 | 9 | def is_file_supported(self, fname): 10 | return ('.h5' in fname) 11 | 12 | def setup(self): 13 | #self.settings.New('search_text', dtype=str, initial="") 14 | 15 | self.ui = QtWidgets.QWidget() 16 | self.ui.setLayout(QtWidgets.QVBoxLayout()) 17 | self.search_lineEdit = QtWidgets.QLineEdit() 18 | self.search_lineEdit.setPlaceholderText("Search") 19 | self.tree_textEdit = QtWidgets.QTextEdit("") 20 | 21 | self.ui.layout().addWidget(self.search_lineEdit) 22 | self.ui.layout().addWidget(self.tree_textEdit) 23 | 24 | #self.settings.search_text.connect_to_widget(self.search_lineEdit) 25 | #self.settings.search_text.add_listener(self.on_new_search_text) 26 | self.search_text = "" 27 | 28 | self.search_lineEdit.textChanged.connect(self.on_new_search_text) 29 | 30 | def on_change_data_filename(self, fname=None): 31 | """ Handle file change """ 32 | self.tree_textEdit.setText("loading {}".format(fname)) 33 | try: 34 | #if using h5_plot_and_view 35 | if hasattr(self.databrowser.ui, "dataset_listWidget"): 36 | self.dataset_dict = {} 37 | self.databrowser.ui.dataset_listWidget.clear() 38 | 39 | self.fname = fname 40 | self.f = h5py.File(fname, 'r') 41 | self.on_new_search_text() 42 | self.databrowser.ui.statusbar.showMessage("") 43 | return self.f 44 | 45 | except Exception as err: 46 | msg = "Failed to load %s:\n%s" %(fname, err) 47 | self.databrowser.ui.statusbar.showMessage(msg) 48 | self.tree_textEdit.setText(msg) 49 | raise(err) 50 | 51 | def on_new_search_text(self, x=None): 52 | if x is not None: 53 | self.search_text = x.lower() 54 | old_scroll_pos = self.tree_textEdit.verticalScrollBar().value() 55 | self.tree_str = "" 56 | self.f.visititems(self._visitfunc) 57 | 58 | self.tree_text_html = \ 59 | """{}
60 |
61 | {} 62 |
63 | """.format(self.fname, self.tree_str) 64 | 65 | self.tree_textEdit.setText(self.tree_text_html) 66 | self.tree_textEdit.verticalScrollBar().setValue(old_scroll_pos) 67 | 68 | def _visitfunc(self, name, node): 69 | level = len(name.split('/')) 70 | indent = ' '*4*(level-1) 71 | 72 | #indent = ''.format(level*4) 73 | localname = name.split('/')[-1] 74 | 75 | #search_text = self.settings['search_text'].lower() 76 | search_text = self.search_text 77 | if search_text and (search_text in localname.lower()): #highlight terms that contain search text 78 | localname = """{}""".format(localname) 79 | 80 | if isinstance(node, h5py.Group): 81 | self.tree_str += indent +"|> {}/
".format(localname) 82 | elif isinstance(node, h5py.Dataset): 83 | self.tree_str += indent +"|D {}: {} {}
".format(localname, node.shape, node.dtype) 84 | 85 | #if using h5_plot_and_view 86 | if hasattr(self.databrowser.ui, "dataset_listWidget"): 87 | item_name = "{}: {} {}".format(localname, node.shape, node.dtype) 88 | self.databrowser.ui.dataset_listWidget.addItem(item_name) 89 | if not hasattr(self, "dataset_dict"): 90 | self.dataset_dict = {} 91 | self.dataset_dict[item_name] = node 92 | 93 | 94 | for key, val in node.attrs.items(): #highlight terms that contain search text 95 | if search_text: 96 | if search_text in str(key).lower(): 97 | key = """{}""".format(key) 98 | if search_text in str(val).lower(): 99 | val = """{}""".format(val) 100 | self.tree_str += indent+"     |- {} = {}
".format(key, val) -------------------------------------------------------------------------------- /PythonGUI_apps/H5_Pkl/h5_view_and_plot.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, absolute_import 2 | from ScopeFoundry import BaseApp 3 | from ScopeFoundry.helper_funcs import load_qt_ui_file, sibling_path 4 | from collections import OrderedDict 5 | import os 6 | from qtpy import QtCore, QtWidgets, QtGui 7 | import pyqtgraph as pg 8 | import pyqtgraph.dockarea as dockarea 9 | import numpy as np 10 | from ScopeFoundry.logged_quantity import LQCollection 11 | from scipy.stats import spearmanr 12 | import argparse 13 | from .h5_tree import H5TreeSearchView 14 | from .pkl_tree import PklTreeSearchView 15 | 16 | 17 | 18 | class H5ViewPlot(BaseApp): 19 | 20 | name = "h5_view_plot" 21 | 22 | def __init__(self, argv): 23 | pg.setConfigOption('imageAxisOrder', 'row-major') 24 | BaseApp.__init__(self, argv) 25 | self.setup() 26 | parser = argparse.ArgumentParser() 27 | for lq in self.settings.as_list(): 28 | parser.add_argument("--" + lq.name) 29 | args = parser.parse_args() 30 | for lq in self.settings.as_list(): 31 | if lq.name in args: 32 | val = getattr(args,lq.name) 33 | if val is not None: 34 | lq.update_value(val) 35 | 36 | def setup(self): 37 | self.ui_filename = sibling_path(__file__, "h5_view_and_plot_gui.ui") 38 | self.ui = load_qt_ui_file(self.ui_filename) 39 | self.ui.show() 40 | self.ui.raise_() 41 | 42 | self.settings.New('data_filename', dtype='file') 43 | 44 | self.settings.data_filename.add_listener(self.on_change_data_filename) 45 | 46 | self.settings.New('view_name', dtype=str, initial='0', choices=('0',)) 47 | 48 | # UI Connections 49 | self.settings.data_filename.connect_to_browse_widgets(self.ui.data_filename_lineEdit, 50 | self.ui.data_filename_browse_pushButton) 51 | self.ui.plot_pushButton.clicked.connect(self.plot_dataset) 52 | self.ui.dataset_listWidget.currentItemChanged.connect(self.on_data_selection) 53 | self.ui.plot_radioButton.toggled.connect(self.update_data_widget) 54 | self.ui.image_radioButton.toggled.connect(self.update_data_widget) 55 | 56 | #set up image item for 2d array 57 | self.data_img_layout = pg.GraphicsLayoutWidget() 58 | self.ui.imageItem_page.layout().addWidget(self.data_img_layout) 59 | self.data_img_layout = self.data_img_layout.addViewBox() 60 | self.data_img = pg.ImageItem() 61 | self.data_img_layout.addItem(self.data_img) 62 | 63 | #set up image view for 3d array 64 | self.ui.data_imageView.getView().invertY(False) 65 | 66 | self.h5treeview = H5TreeSearchView(self) 67 | self.ui.dataview_page.layout().addWidget(self.h5treeview.ui) 68 | self.h5treeview.ui.hide() 69 | self.ui.show() 70 | 71 | def on_change_data_filename(self): 72 | """ Handle file change """ 73 | try: 74 | fname = self.settings.data_filename.val 75 | if os.path.isfile(fname): 76 | self.f = self.h5treeview.on_change_data_filename(fname) 77 | self.ui.dataview_placeholder.hide() 78 | self.h5treeview.ui.show() 79 | except: 80 | pass 81 | 82 | def plot_dataset(self): 83 | """ Plot data set depending on dataset shape and plot type option. """ 84 | self.plot = self.ui.data_plotWidget.getPlotItem() 85 | self.plot.clear() 86 | 87 | data = self.dataset[()] 88 | if self.dataset_shape == 1: 89 | x_start = self.ui.plotWidget_x_start_spinBox.value() 90 | x_end = self.ui.plotWidget_x_end_spinBox.value() 91 | num_points = self.dataset.shape[0] 92 | x_values = np.linspace(x_start, x_end, num_points) 93 | self.plot.plot(x_values, data) 94 | elif self.dataset_shape == 2 and self.ui.plot_radioButton.isChecked(): 95 | self.plot.plot(data[0], data[1]) # TODO check and test this 96 | elif self.dataset_shape == 2 and self.ui.image_radioButton.isChecked(): 97 | self.data_img.setImage(data) 98 | elif self.dataset_shape == 3: 99 | if self.f['Cube/Info/Cube'].attrs['AcqMode'] == b'Hyperspectral Acquisition': # This works for our PhotonEtc. Hyperspectral Camera output 100 | x_start = int(self.f['Cube/Info/Cube'].attrs['LowerWavelength']) 101 | x_end = int(self.f['Cube/Info/Cube'].attrs['UpperWavelength']) 102 | else: 103 | x_start = self.ui.imageView_x_start_spinBox.value() 104 | x_end = self.ui.imageView_x_end_spinBox.value() 105 | num_points = self.dataset.shape[0] 106 | x_values = np.linspace(x_start, x_end, num_points) #scale x axis 107 | self.ui.data_imageView.setImage(data, xvals=x_values) 108 | 109 | def on_data_selection(self): 110 | """ Handle dataset selection """ 111 | try: 112 | dataset_name = self.ui.dataset_listWidget.currentItem().text() 113 | self.dataset = self.h5treeview.dataset_dict[dataset_name] 114 | self.dataset_shape = len(self.dataset[()].shape) 115 | self.update_data_widget() 116 | if self.dataset_shape == 1: 117 | self.ui.plot_type_groupBox.setEnabled(False) 118 | self.ui.plot_radioButton.setChecked(True) 119 | elif self.dataset_shape == 2: 120 | self.ui.plot_type_groupBox.setEnabled(True) 121 | elif self.dataset_shape == 3: 122 | self.ui.plot_type_groupBox.setEnabled(False) 123 | self.ui.image_radioButton.setChecked(True) 124 | except: 125 | pass 126 | 127 | def update_data_widget(self): 128 | """ Decide which widget to display based on dataset shape and plot type option. """ 129 | if self.dataset_shape == 1: 130 | self.ui.data_stackedWidget.setCurrentIndex(0) 131 | elif self.dataset_shape == 2 and self.ui.plot_radioButton.isChecked(): 132 | self.ui.data_stackedWidget.setCurrentIndex(0) 133 | elif self.dataset_shape == 2 and self.ui.image_radioButton.isChecked(): 134 | self.ui.data_stackedWidget.setCurrentIndex(1) 135 | elif self.dataset_shape == 3: 136 | self.ui.data_stackedWidget.setCurrentIndex(2) 137 | 138 | # if __name__ == '__main__': 139 | # import sys 140 | # app = H5ViewPlot(sys.argv) 141 | # sys.exit(app.exec_()) -------------------------------------------------------------------------------- /PythonGUI_apps/H5_Pkl/h5_view_and_plot_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 856 10 | 925 11 | 12 | 13 | 14 | H5 View and Plot 15 | 16 | 17 | 18 | 19 | 20 | 21 | Qt::Horizontal 22 | 23 | 24 | 25 | File 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | File: 35 | 36 | 37 | 38 | 39 | 40 | 41 | Browse... 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | 0 52 | 53 | 54 | 55 | Data View 56 | 57 | 58 | 59 | 60 | 61 | Once a file is loaded, data will show up here. 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | true 70 | 71 | 72 | Plot 73 | 74 | 75 | 76 | 77 | 78 | Datasets 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | Plot data 91 | 92 | 93 | 94 | 95 | 96 | 0 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 203 110 | 16777215 111 | 112 | 113 | 114 | 9999999.000000000000000 115 | 116 | 117 | 0.000000000000000 118 | 119 | 120 | 121 | 122 | 123 | 124 | X start 125 | 126 | 127 | 128 | 129 | 130 | 131 | 9999999.000000000000000 132 | 133 | 134 | 135 | 136 | 137 | 138 | X end 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 9999999.000000000000000 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 203 168 | 16777215 169 | 170 | 171 | 172 | 9999999.000000000000000 173 | 174 | 175 | 0.000000000000000 176 | 177 | 178 | 179 | 180 | 181 | 182 | X start 183 | 184 | 185 | 186 | 187 | 188 | 189 | X end 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | Plot 203 | 204 | 205 | 206 | 207 | 208 | 209 | true 210 | 211 | 212 | Type 213 | 214 | 215 | 216 | 217 | 218 | true 219 | 220 | 221 | Plot 222 | 223 | 224 | true 225 | 226 | 227 | 228 | 229 | 230 | 231 | true 232 | 233 | 234 | Image 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 0 258 | 0 259 | 856 260 | 21 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | PlotWidget 269 | QGraphicsView 270 |
pyqtgraph
271 |
272 | 273 | ImageView 274 | QGraphicsView 275 |
pyqtgraph
276 |
277 |
278 | 279 | 280 |
281 | -------------------------------------------------------------------------------- /PythonGUI_apps/H5_Pkl/pkl_tree.py: -------------------------------------------------------------------------------- 1 | from ScopeFoundry.data_browser import DataBrowserView 2 | from qtpy import QtWidgets 3 | import h5py 4 | import pickle 5 | import numpy as np 6 | import lmfit 7 | 8 | class PklTreeSearchView(DataBrowserView): 9 | 10 | name = 'pkl_tree_search' 11 | 12 | def is_file_supported(self, fname): 13 | return ('.pkl' in fname) 14 | 15 | def setup(self): 16 | #self.settings.New('search_text', dtype=str, initial="") 17 | 18 | self.ui = QtWidgets.QWidget() 19 | self.ui.setLayout(QtWidgets.QVBoxLayout()) 20 | self.search_lineEdit = QtWidgets.QLineEdit() 21 | self.search_lineEdit.setPlaceholderText("Search") 22 | self.tree_textEdit = QtWidgets.QTextEdit("") 23 | 24 | self.ui.layout().addWidget(self.search_lineEdit) 25 | self.ui.layout().addWidget(self.tree_textEdit) 26 | 27 | #self.settings.search_text.connect_to_widget(self.search_lineEdit) 28 | #self.settings.search_text.add_listener(self.on_new_search_text) 29 | self.search_text = "" 30 | 31 | self.search_lineEdit.textChanged.connect(self.on_new_search_text) 32 | 33 | def on_change_data_filename(self, fname=None): 34 | """ Handle file change """ 35 | self.tree_textEdit.setText("loading {}".format(fname)) 36 | try: 37 | self.fname = fname 38 | #self.f = h5py.File(fname, 'r') 39 | self.dictionary = pickle.load(open(self.fname, 'rb')) 40 | self.on_new_search_text() 41 | self.databrowser.ui.statusbar.showMessage("") 42 | 43 | except Exception as err: 44 | msg = "Failed to load %s:\n%s" %(fname, err) 45 | self.databrowser.ui.statusbar.showMessage(msg) 46 | self.tree_textEdit.setText(msg) 47 | raise(err) 48 | 49 | def on_new_search_text(self, x=None): 50 | if x is not None: 51 | self.search_text = x.lower() 52 | old_scroll_pos = self.tree_textEdit.verticalScrollBar().value() 53 | self.tree_str = "" 54 | #self.f.visititems(self._visitfunc) 55 | self.traverse_dict(self.dictionary, self.dictionary, 0) 56 | 57 | 58 | self.tree_text_html = \ 59 | """{}
60 |
61 | {} 62 |
63 | """.format(self.fname, self.tree_str) 64 | 65 | self.tree_textEdit.setText(self.tree_text_html) 66 | self.tree_textEdit.verticalScrollBar().setValue(old_scroll_pos) 67 | 68 | def traverse_dict(self, dictionary, previous_dict, level): 69 | """ 70 | Visit all values in the dictionary and its subdictionaries. 71 | 72 | dictionary -- dictionary to traverse 73 | previous_dict -- dictionary one level up 74 | level -- track how far to indent 75 | """ 76 | for key in dictionary: 77 | if key not in previous_dict: 78 | level -=1 79 | indent = " "*4*(level) 80 | 81 | if type(dictionary[key]) == dict: 82 | print_string = key 83 | if self.search_text and self.search_text in print_string: 84 | self.tree_str += indent + """{}""".format(print_string) 85 | else: 86 | self.tree_str += indent + "|> {}/
".format(print_string) 87 | level += 1 88 | previous_dict = dictionary[key] 89 | self.traverse_dict(dictionary[key], previous_dict, level) 90 | else: 91 | value = dictionary[key] 92 | if type(value) == np.ndarray or type(value)==np.memmap: 93 | value = str(value.shape) + " " + str(value.dtype) 94 | elif type(value) == lmfit.model.ModelResult: 95 | value = "lmfit.model.ModelResult" 96 | # if type(value) == list and len(value) > 5: ##account for data stored in lists 97 | # value = str(np.asarray(value).shape) + " " + str(type(value[0])) 98 | 99 | print_string = key + " = " + str(value) 100 | if self.search_text and self.search_text in print_string: 101 | self.tree_str += indent + """{}""".format(print_string) 102 | else: 103 | self.tree_str += indent + "|- {}
".format(print_string) -------------------------------------------------------------------------------- /PythonGUI_apps/Image_analysis/Image_analysis.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | import os.path 4 | import pyqtgraph as pg 5 | from pyqtgraph.Qt import QtCore, QtGui, QtWidgets#, QColorDialog 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | from PIL import Image 9 | 10 | # local modules 11 | 12 | pg.mkQApp() 13 | 14 | 15 | base_path = Path(__file__).parent 16 | file_path = (base_path / "image_analysis_gui.ui").resolve() 17 | 18 | uiFile = file_path 19 | 20 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 21 | 22 | def updateDelay(scale, time): 23 | """ Hack fix for scalebar inaccuracy""" 24 | QtCore.QTimer.singleShot(time, scale.updateBar) 25 | 26 | class MainWindow(TemplateBaseClass): 27 | 28 | def __init__(self): 29 | pg.setConfigOption('imageAxisOrder', 'col-major') 30 | super(TemplateBaseClass, self).__init__() 31 | 32 | # Create the main window 33 | self.ui = WindowTemplate() 34 | self.ui.setupUi(self) 35 | 36 | #setup image plot 37 | self.image_plot_layout=pg.GraphicsLayoutWidget() 38 | self.ui.image_groupBox.layout().addWidget(self.image_plot_layout) 39 | self.image_plot = self.image_plot_layout.addPlot() 40 | self.img_item = pg.ImageItem() 41 | self.image_plot.addItem(self.img_item) 42 | self.image_plot_view = self.image_plot.getViewBox() 43 | 44 | #setup lookup table 45 | self.hist_lut = pg.HistogramLUTItem() 46 | self.image_plot_layout.addItem(self.hist_lut) 47 | 48 | #region of interest - allows user to select scan area 49 | self.roi = pg.ROI([0,0],[10, 10], movable=True) 50 | self.roi.addScaleHandle([1, 1], [0, 0]) 51 | self.roi.addRotateHandle([0, 0], [1, 1]) 52 | self.roi.translateSnap = True 53 | self.roi.scaleSnap = True 54 | self.roi.sigRegionChanged.connect(self.line_profile_update_plot) 55 | self.image_plot.addItem(self.roi) 56 | 57 | #setup rgb plot 58 | self.rgb_plot_layout=pg.GraphicsLayoutWidget() 59 | self.ui.rgb_plot_groupBox.layout().addWidget(self.rgb_plot_layout) 60 | self.rgb_plot = self.rgb_plot_layout.addPlot() 61 | 62 | #set up ui signals 63 | self.ui.load_image_pushButton.clicked.connect(self.load_image) 64 | self.ui.custom_pixel_size_checkBox.stateChanged.connect(self.switch_custom_pixel_size) 65 | self.ui.update_settings_pushButton.clicked.connect(self.reload_image) 66 | self.ui.spot_radioButton.toggled.connect(self.update_camera) 67 | 68 | self.update_camera() #initialize camera pixel size 69 | self.update_scaling_factor() #initialize scaling_factor 70 | self.show() 71 | 72 | #row major. invert y false, rotate false 73 | def load_image(self): 74 | """ 75 | Prompts the user to select a text file containing image data. 76 | """ 77 | try: 78 | file = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', os.getcwd()) 79 | self.original_image = Image.open(file[0]) 80 | self.original_image = self.original_image.rotate(-90, expand=True) #correct image orientation 81 | self.resize_to_scaling_factor(self.original_image) 82 | except Exception as err: 83 | print(format(err)) 84 | 85 | def resize_to_scaling_factor(self, image): 86 | """ 87 | Handles loading of image according to scaling_factor 88 | """ 89 | self.update_scaling_factor() 90 | 91 | if self.ui.spot_radioButton.isChecked() and self.ui.resize_image_checkBox.isChecked(): 92 | image = self.original_image.resize((round(image.size[0]*self.scaling_factor), round(image.size[1]*self.scaling_factor))) 93 | self.image_plot.getAxis("bottom").setScale(scale = 1) 94 | self.image_plot.getAxis("left").setScale(scale = 1) 95 | else: 96 | image = self.original_image 97 | self.image_plot.getAxis("bottom").setScale(scale = self.scaling_factor) 98 | self.image_plot.getAxis("left").setScale(scale = self.scaling_factor) 99 | 100 | if self.ui.greyscale_checkBox.isChecked(): 101 | image = image.convert("L") #convert to greyscale 102 | 103 | self.image_array = np.array(image) 104 | width = self.image_array.shape[0] 105 | height = self.image_array.shape[1] 106 | 107 | try: 108 | #set image bounds with qrect 109 | self.img_item_rect = QtCore.QRectF(0, 0, width, height) 110 | self.img_item.setImage(image=self.image_array) 111 | self.img_item.setRect(self.img_item_rect) 112 | 113 | # if self.ui.greyscale_checkBox.isChecked(): 114 | # self.hist_lut.setImageItem(self.img_item) 115 | 116 | if self.ui.vertical_radioButton.isChecked(): 117 | roi_height = self.scaling_factor * height 118 | self.roi.setSize([width, roi_height]) 119 | elif self.ui.horizontal_radioButton.isChecked(): 120 | roi_height = self.scaling_factor * width 121 | self.roi.setSize([roi_height, height]) 122 | self.roi.setAngle(0) 123 | self.roi.setPos(0, 0) 124 | self.line_profile_update_plot() 125 | except: 126 | pass 127 | 128 | def line_profile_update_plot(self): 129 | """ Handle line profile for intensity sum viewbox """ 130 | self.rgb_plot.clear() 131 | 132 | # Extract image data from ROI 133 | data, coords = self.roi.getArrayRegion(self.image_array, self.img_item, returnMappedCoords=True) 134 | if data is None: 135 | return 136 | 137 | if self.ui.vertical_radioButton.isChecked(): 138 | x_values = coords[0,:,0] 139 | elif self.ui.horizontal_radioButton.isChecked(): 140 | x_values = coords[1,0,:] 141 | 142 | if self.ui.pixera_radioButton.isChecked() or (self.ui.spot_radioButton.isChecked() and not self.ui.resize_image_checkBox.isChecked()): 143 | x_values = x_values * self.scaling_factor 144 | 145 | #calculate average along columns in region 146 | if len(data.shape) <= 2: #if grayscale, average intensities 147 | if self.ui.vertical_radioButton.isChecked(): 148 | avg_to_plot = np.mean(data, axis=-1) 149 | elif self.ui.horizontal_radioButton.isChecked(): 150 | avg_to_plot = np.mean(data, axis=0) 151 | try: 152 | self.rgb_plot.plot(x_values, avg_to_plot) 153 | except: 154 | pass 155 | elif len(data.shape) > 2: #if rgb arrays, plot individual components 156 | r_values = data[:,:,0] 157 | g_values = data[:,:,1] 158 | b_values = data[:,:,2] 159 | if self.ui.vertical_radioButton.isChecked(): 160 | r_avg = np.mean(r_values, axis=-1) #average red values across columns 161 | g_avg = np.mean(g_values, axis=-1) #average green values 162 | b_avg = np.mean(b_values, axis=-1) #average blue values 163 | elif self.ui.horizontal_radioButton.isChecked(): 164 | r_avg = np.mean(r_values, axis=0) 165 | g_avg = np.mean(g_values, axis=0) 166 | b_avg = np.mean(b_values, axis=0) 167 | try: 168 | self.rgb_plot.plot(x_values, r_avg, pen='r') 169 | self.rgb_plot.plot(x_values, g_avg, pen='g') 170 | self.rgb_plot.plot(x_values, b_avg, pen='b') 171 | except Exception as e: 172 | pass 173 | 174 | def update_scaling_factor(self): 175 | """ 176 | Calculate scaling factor 177 | """ 178 | if self.ui.custom_pixel_size_checkBox.isChecked(): 179 | self.camera_pixel_size = self.ui.custom_pixel_size_spinBox.value() 180 | self.scaling_factor = self.camera_pixel_size 181 | else: 182 | self.scaling_factor = self.camera_pixel_size/int(self.ui.magnification_comboBox.currentText()) 183 | self.roi.snapSize = self.scaling_factor #roi snaps to multiples of scaling_factor 184 | 185 | def reload_image(self): 186 | if hasattr(self, "original_image"): 187 | self.resize_to_scaling_factor(self.original_image) #resize image, sets up roi 188 | 189 | def switch_custom_pixel_size(self): 190 | checked = self.ui.custom_pixel_size_checkBox.isChecked() 191 | self.ui.custom_pixel_size_spinBox.setEnabled(checked) 192 | self.ui.magnification_comboBox.setEnabled(not checked) 193 | 194 | def update_camera(self): 195 | if self.ui.spot_radioButton.isChecked(): 196 | self.camera_pixel_size = 7.4 197 | self.ui.greyscale_checkBox.setChecked(False) 198 | self.ui.resize_image_checkBox.setEnabled(True) 199 | self.update_scaling_factor() 200 | elif self.ui.pixera_radioButton.isChecked(): 201 | self.camera_pixel_size = 3 202 | self.ui.greyscale_checkBox.setChecked(True) 203 | self.ui.resize_image_checkBox.setEnabled(False) 204 | self.update_scaling_factor() 205 | 206 | def close_application(self): 207 | choice = QtGui.QMessageBox.question(self, 'EXIT!', 208 | "Do you want to exit the app?", 209 | QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) 210 | if choice == QtGui.QMessageBox.Yes: 211 | sys.exit() 212 | else: 213 | pass 214 | 215 | """Run the Main Window""" 216 | def run(): 217 | win = MainWindow() 218 | QtGui.QApplication.instance().exec_() 219 | return win 220 | 221 | #Uncomment below if you want to run this as standalone 222 | #run() -------------------------------------------------------------------------------- /PythonGUI_apps/Image_analysis/image_analysis_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1029 10 | 743 11 | 12 | 13 | 14 | Image Analysis 15 | 16 | 17 | QTabWidget::Triangular 18 | 19 | 20 | 21 | 22 | 23 | 24 | Settings 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Greyscale image 38 | 39 | 40 | 41 | 42 | 43 | 44 | Load image 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 50 53 | 54 | 55 | 56 | 57 | 75 58 | 59 | 60 | 61 | 62 | 100 63 | 64 | 65 | 66 | 67 | 150 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Magnification 76 | 77 | 78 | 79 | 80 | 81 | 82 | Update settings 83 | 84 | 85 | 86 | 87 | 88 | 89 | false 90 | 91 | 92 | 93 | 94 | 95 | 96 | Custom pixel size (um) 97 | 98 | 99 | 100 | 101 | 102 | 103 | Camera 104 | 105 | 106 | 107 | 108 | 109 | SPOT 110 | 111 | 112 | true 113 | 114 | 115 | 116 | 117 | 118 | 119 | Pixera 120 | 121 | 122 | 123 | 124 | 125 | 126 | Resize image 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | Direction to average pixels 137 | 138 | 139 | 140 | 141 | 142 | Vertical 143 | 144 | 145 | true 146 | 147 | 148 | 149 | 150 | 151 | 152 | Horizontal 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 0 169 | 0 170 | 171 | 172 | 173 | 174 | 600 175 | 0 176 | 177 | 178 | 179 | Image 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | RGB Plot 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 0 198 | 0 199 | 1029 200 | 31 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /PythonGUI_apps/Lifetime_analysis/Fit_functions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Mar 29 11:21:59 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | import numpy as np 9 | from scipy.optimize import differential_evolution 10 | from scipy.special import gamma 11 | 12 | def stretch_exp_fit(TRPL, t, Tc = (0,1e5), Beta = (0,1), A = (0,1e6), noise=(0,1e6)): 13 | 14 | def exp_stretch(t, tc, beta, a, noise): 15 | return ((a * np.exp(-((1.0 / tc) * t) ** beta)) + noise) 16 | 17 | def avg_tau_from_exp_stretch(tc, beta): 18 | return (tc / beta) * gamma(1.0 / beta) 19 | 20 | def Diff_Ev_Fit_SE(TRPL): 21 | TRPL = TRPL 22 | 23 | def residuals(params):#params are the parameters to be adjusted by differential evolution or leastsq, interp is the data to compare to the model. 24 | #Variable Rates 25 | tc = params[0] 26 | beta = params[1] 27 | a = params[2] 28 | noise = params[3] 29 | 30 | PL_sim = exp_stretch(t,tc,beta,a, noise) 31 | 32 | Resid= np.sum(((PL_sim-TRPL)**2)/(np.sqrt(TRPL)**2)) 33 | return Resid #returns the difference between the PL data and simulated data 34 | 35 | bounds = [Tc, Beta, A, noise] 36 | 37 | result = differential_evolution(residuals, bounds) 38 | return result.x 39 | 40 | p = Diff_Ev_Fit_SE(TRPL) 41 | 42 | tc = p[0] 43 | beta = p[1] 44 | a = p[2] 45 | noise = p[3] 46 | 47 | PL_fit = exp_stretch(t,tc,beta,a, noise) 48 | 49 | avg_tau = avg_tau_from_exp_stretch(tc,beta) 50 | 51 | return tc, beta, a, avg_tau, PL_fit, noise 52 | 53 | def double_exp_fit(TRPL, t, tau1_bounds=(0,1000), a1_bounds=(0,1e6), tau2_bounds=(0,10000), a2_bounds=(0,1e5), noise=(0,1e6)): 54 | 55 | def single_exp(t, tau, a): 56 | return (a * np.exp(-((1.0 / tau)*t))) 57 | 58 | def double_exp(t, tau1, a1, tau2, a2, noise): 59 | return ((single_exp(t, tau1, a1)) + (single_exp(t, tau2, a2)) + noise) 60 | 61 | def avg_tau_from_double_exp(tau1, a1, tau2, a2): 62 | return (((tau1*a1) + (tau2*a2))/(a1+a2)) 63 | 64 | def Diff_Ev_Fit_DE(TRPL): 65 | TRPL = TRPL 66 | 67 | def residuals(params):#params are the parameters to be adjusted by differential evolution or leastsq, interp is the data to compare to the model. 68 | #Variable Rates 69 | tau1 = params[0] 70 | a1 = params[1] 71 | tau2 = params[2] 72 | a2 = params[3] 73 | noise = params[4] 74 | 75 | PL_sim = double_exp(t,tau1, a1, tau2, a2, noise) 76 | 77 | Resid= np.sum(((PL_sim-TRPL)**2)/(np.sqrt(TRPL)**2)) 78 | return Resid #returns the difference between the PL data and simulated data 79 | 80 | bounds = [tau1_bounds, a1_bounds, tau2_bounds, a2_bounds, noise] 81 | 82 | result = differential_evolution(residuals, bounds) 83 | return result.x 84 | 85 | p = Diff_Ev_Fit_DE(TRPL) 86 | 87 | tau1 = p[0] 88 | a1 = p[1] 89 | tau2 = p[2] 90 | a2 = p[3] 91 | noise = p[4] 92 | 93 | PL_fit = double_exp(t, tau1, a1, tau2, a2, noise) 94 | 95 | avg_tau = avg_tau_from_double_exp(tau1, a1, tau2, a2) 96 | 97 | return tau1, a1, tau2, a2, avg_tau, PL_fit, noise 98 | 99 | def single_exp_fit(TRPL, t, tau_bounds=(0,10000), a_bounds=(0,1e6), noise=(0,1e6)): 100 | 101 | def single_exp(t, tau, a, noise): 102 | return (a * np.exp(-((1.0 / tau)*t) ) + noise) 103 | 104 | def Diff_Ev_Fit_singleExp(TRPL): 105 | TRPL = TRPL 106 | 107 | def residuals(params):#params are the parameters to be adjusted by differential evolution or leastsq, interp is the data to compare to the model. 108 | #Variable Rates 109 | tau = params[0] 110 | a = params[1] 111 | noise = params[2] 112 | 113 | PL_sim = single_exp(t, tau, a, noise) 114 | 115 | Resid= np.sum(((PL_sim-TRPL)**2)/(np.sqrt(TRPL)**2)) 116 | return Resid #returns the difference between the PL data and simulated data 117 | 118 | bounds = [tau_bounds, a_bounds, noise] 119 | 120 | result = differential_evolution(residuals, bounds) 121 | return result.x 122 | 123 | p = Diff_Ev_Fit_singleExp(TRPL) 124 | 125 | tau = p[0] 126 | a = p[1] 127 | noise = p[2] 128 | 129 | PL_fit = single_exp(t, tau, a, noise) 130 | 131 | return tau, a, PL_fit, noise 132 | -------------------------------------------------------------------------------- /PythonGUI_apps/Lifetime_analysis/Fit_functions_with_irf.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.optimize import fmin_tnc, differential_evolution 4 | from scipy.special import gamma 5 | from scipy.signal import fftconvolve 6 | from scipy.integrate import odeint 7 | 8 | 9 | """Fit TCSPC data to a model by reconvolution with the IRF 10 | Convolution is done in time domain with np.convolve() 11 | Convolution can be done in the frequency domain of np.convolve() is replaced by scipy.signal.fftconvolve() 12 | 13 | For a good tutorial on numerical convolution, see section 13.1 of Numerical Recipes: 14 | 15 | Press, W. H.; Teukolsky, S. A.; Vetterling, W. T.; Flannery, B. P., 16 | Numerical Recipes 3rd Edition: The Art of Scientific Computing. 3 ed.; 17 | Cambridge University Press: New York, 2007 18 | 19 | ***Note that algorithm given in Numerical Recipes does convolution in the frequency 20 | domain using the FFT. However the discussion of convolution in 13.1 applies to the time 21 | domain and can be used to understand this Python code.*** 22 | 23 | -MZ, 2/2017 24 | """ 25 | 26 | def convolve_sig_resp(signal_array, response_array, t_array, tstep): 27 | 28 | def normalize_response(response_array, t_array): 29 | area = np.trapz(response_array, x = t_array) 30 | return response_array / area 31 | 32 | def array_zeropad_neg(array, pad_length): 33 | 34 | return np.pad(array, (pad_length, 0), 'constant', constant_values = (0,0)) 35 | 36 | def array_zeropad_pos(array, pad_length): 37 | return np.pad(array, (0, pad_length), 'constant', constant_values = (0,0)) 38 | 39 | # def array_symmetricpad_neg(array, pad_length): 40 | # 41 | # return np.pad(array, (pad_length, 0), 'symmetric') 42 | 43 | def signal_and_resp_forconv(signal_array, response_array): 44 | resp_pad_negtime = array_zeropad_neg(normalize_response(response_array, t_array), len(response_array) - 1) 45 | sig_pad_negtime = array_zeropad_neg(signal_array, len(signal_array) - 1) 46 | sig_pad_postime = array_zeropad_pos(sig_pad_negtime, len(response_array)) 47 | return [resp_pad_negtime, sig_pad_postime] 48 | 49 | resp, sig = signal_and_resp_forconv(signal_array, response_array) 50 | convolution = tstep * fftconvolve(sig, resp, mode = 'same')#np.convolve(resp, sig, mode = 'same') 51 | 52 | return convolution[len(signal_array) - 1 : (2*len(signal_array)) - 1] 53 | 54 | def convolution_plusnoise(signal_array, response_array, t_array, tstep, noiselevel): 55 | return convolve_sig_resp(signal_array, response_array, t_array, tstep) + noiselevel 56 | 57 | def herz_ode(t, n0, params): 58 | a = params[0] 59 | k1 = params[1] 60 | k2 = params[2] 61 | k3 = params[3] 62 | def odefun(n, t, k1, k2, k3): 63 | dndt = -(k1 * n) - (k2 * (n ** 2.0)) - (k3 * (n ** 3.0)) 64 | return dndt 65 | ode_sol = odeint(odefun, n0, t, args = (k1, k2, k3))[:,0] 66 | pl = k2 * (ode_sol ** 2.0) 67 | return a*pl 68 | 69 | def fit_herz_ode_global_3traces_fmin_tnc(t1, t2, t3, tstep, d1, d2, d3, irf, init_params, bounds, n0array): 70 | time_array1 = t1 71 | time_array2 = t2 72 | time_array3 = t3 73 | data_array1 = d1 74 | data_array2 = d2 75 | data_array3 = d3 76 | n0 = n0array[0] 77 | n1 = n0array[1] 78 | n2 = n0array[2] 79 | def min_fit_decay(params): 80 | #Minimize chi-squre for data set with Poisson distribution () 81 | 82 | a0 = params[0] 83 | a1 = params[1] 84 | a2 = params[2] 85 | k1 = params[3] 86 | k2 = params[4] 87 | k3 = params[5] 88 | noise1 = params[6] 89 | noise2 = params[7] 90 | noise3 = params[8] 91 | decaymodel1 = herz_ode(time_array1, n0, np.array([a0,k1,k2,k3])) 92 | decaymodel2 = herz_ode(time_array2, n1, np.array([a1,k1,k2,k3])) 93 | decaymodel3 = herz_ode(time_array3, n2, np.array([a2,k1,k2,k3])) 94 | model1 = convolution_plusnoise(decaymodel1, irf, time_array1, tstep, noise1) 95 | model2 = convolution_plusnoise(decaymodel2, irf, time_array2, tstep, noise2) 96 | 97 | model3 = convolution_plusnoise(decaymodel3, irf, time_array3, tstep, noise3) 98 | 99 | data_fit_idx1 = np.nonzero(data_array1) 100 | data_fit_idx2 = np.nonzero(data_array2) 101 | data_fit_idx3 = np.nonzero(data_array3) 102 | data_array_fit1 = data_array1[data_fit_idx1] 103 | data_array_fit2 = data_array2[data_fit_idx2] 104 | data_array_fit3 = data_array3[data_fit_idx3] 105 | 106 | model_fit1 = model1[data_fit_idx1] 107 | model_fit2 = model2[data_fit_idx2] 108 | model_fit3 = model3[data_fit_idx3] 109 | 110 | min1 = np.sum(((data_array_fit1 - model_fit1)** 2.0) / (np.sqrt(data_array_fit1) ** 2.0)) 111 | min2 = np.sum(((data_array_fit2 - model_fit2)** 2.0) / (np.sqrt(data_array_fit2) ** 2.0)) 112 | min3 = np.sum(((data_array_fit3 - model_fit3)** 2.0) / (np.sqrt(data_array_fit3) ** 2.0)) 113 | return np.sqrt((min1 ** 2.0) + (min2 ** 2.0) + (min3 ** 2.0)) 114 | bestfit_params = fmin_tnc(min_fit_decay, init_params, approx_grad = True, bounds = bounds)[0] 115 | def bestfit_decay(params): 116 | a0 = params[0] 117 | a1 = params[1] 118 | a2 = params[2] 119 | k1 = params[3] 120 | k2 = params[4] 121 | k3 = params[5] 122 | noise1 = params[6] 123 | # noise2 = params[7] 124 | # noise3 = params[8] 125 | decaymodel1 = herz_ode(time_array, n0, np.array([a0,k1,k2,k3])) 126 | decaymodel2 = herz_ode(time_array, n1, np.array([a1,k1,k2,k3])) 127 | decaymodel3 = herz_ode(time_array, n2, np.array([a2,k1,k2,k3])) 128 | 129 | model1 = convolution_plusnoise(decaymodel1, irf, time_array, tstep, noise1) 130 | model2 = convolution_plusnoise(decaymodel2, irf, time_array, tstep, noise2) 131 | model3 = convolution_plusnoise(decaymodel3, irf, time_array, tstep, noise3) 132 | return [model1, model2, model3] 133 | 134 | bestfit_model = bestfit_decay(bestfit_params) 135 | # plt.figure() 136 | # plt.ylabel('PL Counts') 137 | # plt.xlabel('Time (ns)') 138 | # plt.semilogy(time_array1, data_array1,'b', label = 'Data') 139 | # plt.semilogy(time_array1, bestfit_model[0], 'r', label = 'Fit') 140 | # plt.semilogy(time_array2, data_array2,'b', label = 'Data') 141 | # plt.semilogy(time_array2, bestfit_model[1], 'r', label = 'Fit') 142 | # plt.semilogy(time_array3, data_array3,'b', label = 'Data') 143 | # plt.semilogy(time_array3, bestfit_model[2], 'r', label = 'Fit') 144 | # plt.legend(loc = 'best') 145 | return bestfit_params, bestfit_model, data_array, time_array, irf 146 | 147 | def multi_exp(t, params, num_exp): 148 | exp_array = np.empty((len(t), num_exp)) 149 | 150 | i = 0 151 | while (i" % (self.res, len(self.data)) 199 | 200 | 201 | def timefmt(t): 202 | d = datetime.datetime.fromtimestamp(t) 203 | return d.strftime("%a %b %d %H:%M:%S %Y") 204 | 205 | 206 | class PicoharpParser(object): 207 | _ready = False 208 | 209 | def __init__(self, filename): 210 | if isinstance(filename, (str)): # , unicode)): 211 | filename = open(filename, mode="rb") 212 | self.f = filename 213 | self._prepare() 214 | 215 | def _prepare(self): 216 | self.f.seek(0) 217 | 218 | header = self._header = _read(self.f, TxtHdr) 219 | """SJ commented this --- it was giving ParseError""" 220 | # _validate_header(header) 221 | 222 | bin_header = self._bin_header = _read(self.f, BinHdr) 223 | 224 | self._boards = [] 225 | for i in range(bin_header.NumberOfBoards): 226 | self._boards.append(_read(self.f, BoardHdr)) 227 | 228 | self._curves = [] 229 | for i in range(bin_header.Curves): 230 | self._curves.append(_read(self.f, CurveHdr)) 231 | 232 | def header(self): 233 | return [(k, getattr(self._header, k)) for k, t in self._header._fields_] 234 | 235 | def no_of_curves(self): 236 | return self._bin_header.Curves 237 | 238 | def get_curve(self, n): 239 | header = self._curves[n] 240 | res = header.Resolution 241 | 242 | self.f.seek(header.DataOffset) 243 | array = np.fromfile(self.f, c_uint, header.Channels) 244 | 245 | return res, array 246 | 247 | def get_all_curves(self): 248 | all_curves = [] 249 | for i in range(len(self._curves)): 250 | all_curves.append(self.get_curve(i)) 251 | 252 | return all_curves 253 | 254 | def get_time_window_in_ns(self, curve_no): 255 | curve = self._curves[curve_no] 256 | rep_rate = curve.InpRate0 257 | res, _ = self.get_curve(curve_no) 258 | time_window_s = (1 / rep_rate) / res # in seconds 259 | 260 | return time_window_s * 1e9 # in nannoseconds 261 | 262 | def get_integral_counts(self, curve_no): 263 | curve = self._curves[curve_no] 264 | integral_counts = curve.IntegralCount 265 | 266 | return integral_counts 267 | 268 | def info(self): 269 | txthdr = self._header 270 | binhdr = self._bin_header 271 | boards = self._boards 272 | curves = self._curves 273 | r = [] 274 | w = r.append 275 | yesno = lambda x: "true" if x else "false" 276 | 277 | w("Ident : %s" % txthdr.Ident) 278 | w("Format Version : %s" % txthdr.FormatVersion) 279 | w("Creator Name : %s" % txthdr.CreatorName) 280 | w("Creator Version : %s" % txthdr.CreatorVersion) 281 | w("Time of Creation : %s" % txthdr.FileTime) 282 | w("File Comment : %s" % txthdr.CommentField) 283 | 284 | w("No of Curves : %s" % binhdr.Curves) 285 | w("Bits per HistoBin: %s" % binhdr.BitsPerHistoBin) 286 | w("RoutingChannels : %s" % binhdr.RoutingChannels) 287 | w("No of Boards : %s" % binhdr.NumberOfBoards) 288 | w("Active Curve : %s" % binhdr.ActiveCurve) 289 | w("Measurement Mode : %s" % binhdr.MeasMode) 290 | w("Sub-Mode : %s" % binhdr.SubMode) 291 | w("Range No : %s" % binhdr.RangeNo) 292 | w("Offset : %s" % binhdr.Offset) 293 | w("AcquisitionTime : %s" % binhdr.Tacq) 294 | w("Stop at : %s" % binhdr.StopAt) 295 | w("Stop on Ovfl. : %s" % binhdr.StopOnOvfl) 296 | w("Restart : %s" % binhdr.Restart) 297 | w("DispLinLog : %s" % binhdr.DispLinLog) 298 | w("DispTimeAxisFrom : %s" % binhdr.DispTimeFrom) 299 | w("DispTimeAxisTo : %s" % binhdr.DispTimeTo) 300 | w("DispCountAxisFrom: %s" % binhdr.DispCountsFrom) 301 | w("DispCountAxisTo : %s" % binhdr.DispCountsTo) 302 | 303 | for i in range(DISPCURVES): 304 | w("---------------------") 305 | w("Curve No %s" % i) 306 | w(" MapTo : %s" % binhdr.DispCurves[i].MapTo) 307 | w(" Show : %s" % yesno(binhdr.DispCurves[i].Show)) 308 | w("---------------------") 309 | 310 | for i in range(3): 311 | w("---------------------") 312 | w("Parameter No %s" % i) 313 | w(" Start : %f" % binhdr.Params[i].Start) 314 | w(" Step : %f" % binhdr.Params[i].Step) 315 | w(" End : %f" % binhdr.Params[i].End) 316 | w("---------------------") 317 | 318 | w("Repeat Mode : %d" % binhdr.RepeatMode) 319 | w("Repeats per Curve: %d" % binhdr.RepeatsPerCurve) 320 | w("Repeat Time : %d" % binhdr.RepeatTime) 321 | w("Repeat wait Time : %d" % binhdr.RepeatWaitTime) 322 | w("Script Name : %s" % binhdr.ScriptName) 323 | 324 | for i, board in enumerate(boards): 325 | w("---------------------") 326 | w("Board No %d" % i) 327 | w(" HardwareIdent : %s" % board.HardwareIdent) 328 | w(" HardwareVersion : %s" % board.HardwareVersion) 329 | w(" HardwareSerial : %d" % board.HardwareSerial) 330 | w(" SyncDivider : %d" % board.SyncDivider) 331 | w(" CFDZeroCross0 : %d" % board.CFDZeroCross0) 332 | w(" CFDLevel0 : %d" % board.CFDLevel0) 333 | w(" CFDZeroCross1 : %d" % board.CFDZeroCross1) 334 | w(" CFDLevel1 : %d" % board.CFDLevel1) 335 | w(" Resolution : %.6f" % board.Resolution) 336 | 337 | if board.RouterModelCode: 338 | w(" RouterModelCode : %d" % board.RouterModelCode) 339 | w(" RouterEnabled : %d" % board.RouterEnabled) 340 | 341 | w(" RtChan1_InputType : %d" % board.RtChan1_InputType) 342 | w(" RtChan1_InputLevel : %d" % board.RtChan1_InputLevel) 343 | w(" RtChan1_InputEdge : %d" % board.RtChan1_InputEdge) 344 | w(" RtChan1_CFDPresent : %d" % board.RtChan1_CFDPresent) 345 | w(" RtChan1_CFDLevel : %d" % board.RtChan1_CFDLevel) 346 | w(" RtChan1_CFDZeroCross : %d" % board.RtChan1_CFDZeroCross) 347 | 348 | w(" RtChan2_InputType : %d" % board.RtChan2_InputType) 349 | w(" RtChan2_InputLevel : %d" % board.RtChan2_InputLevel) 350 | w(" RtChan2_InputEdge : %d" % board.RtChan2_InputEdge) 351 | w(" RtChan2_CFDPresent : %d" % board.RtChan2_CFDPresent) 352 | w(" RtChan2_CFDLevel : %d" % board.RtChan2_CFDLevel) 353 | w(" RtChan2_CFDZeroCross : %d" % board.RtChan2_CFDZeroCross) 354 | 355 | w(" RtChan3_InputType : %d" % board.RtChan3_InputType) 356 | w(" RtChan3_InputLevel : %d" % board.RtChan3_InputLevel) 357 | w(" RtChan3_InputEdge : %d" % board.RtChan3_InputEdge) 358 | w(" RtChan3_CFDPresent : %d" % board.RtChan3_CFDPresent) 359 | w(" RtChan3_CFDLevel : %d" % board.RtChan3_CFDLevel) 360 | w(" RtChan3_CFDZeroCross : %d" % board.RtChan3_CFDZeroCross) 361 | 362 | w(" RtChan4_InputType : %d" % board.RtChan4_InputType) 363 | w(" RtChan4_InputLevel : %d" % board.RtChan4_InputLevel) 364 | w(" RtChan4_InputEdge : %d" % board.RtChan4_InputEdge) 365 | w(" RtChan4_CFDPresent : %d" % board.RtChan4_CFDPresent) 366 | w(" RtChan4_CFDLevel : %d" % board.RtChan4_CFDLevel) 367 | w(" RtChan4_CFDZeroCross : %d" % board.RtChan4_CFDZeroCross) 368 | 369 | w("---------------------") 370 | 371 | for i, curve in enumerate(curves): 372 | w("---------------------") 373 | w("Curve Index : %d" % curve.CurveIndex) 374 | w("Time of Recording : %s" % timefmt(curve.TimeOfRecording)) 375 | w("HardwareIdent : %s" % curve.HardwareIdent) 376 | w("HardwareVersion : %s" % curve.HardwareVersion) 377 | w("HardwareSerial : %d" % curve.HardwareSerial) 378 | w("SyncDivider : %d" % curve.SyncDivider) 379 | w("CFDZeroCross0 : %d" % curve.CFDZeroCross0) 380 | w("CFDLevel0 : %d" % curve.CFDLevel0) 381 | w("CFDZeroCross1 : %d" % curve.CFDZeroCross1) 382 | w("CFDLevel1 : %d" % curve.CFDLevel1) 383 | w("Offset : %d" % curve.Offset) 384 | w("RoutingChannel : %d" % curve.RoutingChannel) 385 | w("ExtDevices : %d" % curve.ExtDevices) 386 | w("Meas. Mode : %d" % curve.MeasMode) 387 | w("Sub-Mode : %d" % curve.SubMode) 388 | w("Par. 1 : %f" % curve.P1) 389 | w("Par. 2 : %.6f" % curve.P2) 390 | w("Par. 3 : %.6f" % curve.P3) 391 | w("Range No : %d" % curve.RangeNo) 392 | w("Resolution : %f" % curve.Resolution) 393 | w("Channels : %d" % curve.Channels) 394 | w("Acq. Time : %d" % curve.Tacq) 395 | w("Stop after : %d" % curve.StopAfter) 396 | w("Stop Reason : %d" % curve.StopReason) 397 | w("InpRate0 : %d" % curve.InpRate0) 398 | w("InpRate1 : %d" % curve.InpRate1) 399 | w("HistCountRate : %d" % curve.HistCountRate) 400 | w("IntegralCount : %d" % curve.IntegralCount) 401 | w("reserved : %d" % curve.reserved) 402 | w("dataoffset : %d" % curve.DataOffset) 403 | 404 | if curve.RouterModelCode: 405 | w("RouterModelCode : %d" % curve.RouterModelCode) 406 | w("RouterEnabled : %d" % curve.RouterEnabled) 407 | w("RtChan_InputType : %d" % curve.RtChan_InputType) 408 | w("RtChan_InputLevel : %d" % curve.RtChan_InputLevel) 409 | w("RtChan_InputEdge : %d" % curve.RtChan_InputEdge) 410 | w("RtChan_CFDPresent : %d" % curve.RtChan_CFDPresent) 411 | w("RtChan_CFDLevel : %d" % curve.RtChan_CFDLevel) 412 | w("RtChan_CFDZeroCross : %d" % curve.RtChan_CFDZeroCross) 413 | 414 | return "\n".join(r) 415 | -------------------------------------------------------------------------------- /PythonGUI_apps/Lifetime_analysis/read_ph_phd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 5 15:52:05 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | try: 9 | from Lifetime_analysis import picoharp_phd 10 | except: 11 | import picoharp_phd 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | #import sys 15 | 16 | #datafile = "E:/Python code/APTES_APTMS.phd" 17 | 18 | 19 | def read_picoharp_phd(datafile): 20 | parser = picoharp_phd.PicoharpParser(datafile) 21 | return parser 22 | 23 | #def phd_to_csv(datafile, return_df = False): 24 | # parser = read_picoharp_phd(datafile) 25 | # name, ext = datafile.rsplit('.', 1) 26 | # 27 | # total_curves = parser.no_of_curves() 28 | # y = [] 29 | # for i in range(total_curves): 30 | # res, curve = parser.get_curve(i) 31 | # time_window = int(np.floor(parser.get_time_window_in_ns(curve_no))) 32 | # curve = curve[0:time_window] 33 | # y.append(curve) 34 | # 35 | # df = pd.DataFrame(y) 36 | # df.T.to_csv(name+".csv", index=False, header=False) 37 | # if return_df == True: 38 | # return df.T 39 | 40 | def smooth(curve, boxwidth): 41 | sm_curve = np.convolve(curve, np.ones(boxwidth)/boxwidth, mode="same") 42 | return sm_curve 43 | 44 | def get_x_y(curve_no, parser, smooth_trace = False, boxwidth = 3): 45 | 46 | assert type(parser) == picoharp_phd.PicoharpParser, 'must be picoharp parser' 47 | res, curve = parser.get_curve(curve_no) 48 | time_window = int(np.floor(parser.get_time_window_in_ns(curve_no))) 49 | curve = curve[0:time_window] 50 | size = len(curve) 51 | x = np.arange(0, size*res, res, np.float) 52 | if smooth_trace == True: 53 | curve = smooth(curve, boxwidth=boxwidth) 54 | return x,curve 55 | -------------------------------------------------------------------------------- /PythonGUI_apps/Lifetime_analysis/skip_rows.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 413 10 | 94 11 | 12 | 13 | 14 | Enter rows to skip 15 | 16 | 17 | 18 | 19 | 20 | Rows to skip 21 | 22 | 23 | 24 | 25 | 26 | 27 | 10 28 | 29 | 30 | 31 | 32 | 33 | 34 | Done 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /PythonGUI_apps/OceanOptics_acquire/OO_acquire_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1206 10 | 668 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 15 25 | 26 | 27 | 28 | Acquire Settings 29 | 30 | 31 | 32 | 33 | 34 | 35 | 15 36 | 37 | 38 | 39 | Save Settings 40 | 41 | 42 | 43 | 44 | 45 | 46 | 12 47 | 48 | 49 | 50 | Configure Folder to Save 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 12 59 | 60 | 61 | 62 | Save Every Spectrum 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 12 71 | 72 | 73 | 74 | Save Single Spectrum 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 12 86 | 87 | 88 | 89 | Correct Dark Counts 90 | 91 | 92 | true 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 15 101 | 102 | 103 | 104 | Close Connection 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 12 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 15 122 | 123 | 124 | 125 | ALWAYS CLOSE CONNECTION!!! 126 | BEFORE EXITING APP 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 12 135 | 136 | 137 | 138 | Intg. Time (ms) 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 12 147 | 148 | 149 | 150 | Scans to Average 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 10 159 | 160 | 161 | 162 | 3 163 | 164 | 165 | 3000 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 10 174 | 175 | 176 | 177 | 1 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 15 186 | 187 | 188 | 189 | Live 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 12 198 | 199 | 200 | 201 | Connect to Spectrometer 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 0 223 | 0 224 | 1206 225 | 21 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | PlotWidget 234 | QGraphicsView 235 |
pyqtgraph
236 |
237 |
238 | 239 | 240 |
241 | -------------------------------------------------------------------------------- /PythonGUI_apps/OceanOptics_acquire/OceanOptics_acquire_plot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Mar 27 16:50:26 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | import pyqtgraph as pg 9 | from pyqtgraph.Qt import QtCore, QtGui, QtWidgets#, QColorDialog 10 | import numpy as np 11 | import pickle 12 | import sys 13 | import seabreeze.spectrometers as sb 14 | from pipython import GCSDevice 15 | import time 16 | 17 | pg.mkQApp() 18 | pg.setConfigOption('background', 'w') 19 | 20 | #uiFile = "OO_acquire_gui.ui" "OO_PZstageScan_acquire_gui" 21 | uiFile = "OO_PZstageScan_acquire_gui.ui" 22 | 23 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 24 | 25 | class MainWindow(TemplateBaseClass): 26 | 27 | def __init__(self): 28 | TemplateBaseClass.__init__(self) 29 | 30 | # Create the main window 31 | self.ui = WindowTemplate() 32 | self.ui.setupUi(self) 33 | 34 | self.ui.connect_checkBox.stateChanged.connect(self._handle_spectrometer_connection) 35 | self.ui.live_pushButton.clicked.connect(self.live) 36 | 37 | self.ui.path_to_folder_pushButton.clicked.connect(self.save_file_location) 38 | self.ui.save_single_spec_pushButton.clicked.connect(self.save_single_spec) 39 | 40 | self.ui.init_stage_pushButton.clicked.connect(self.init_piezo_stage) 41 | self.ui.show_curr_pos_pushButton.clicked.connect(self.current_piezo_stage_pos) 42 | self.ui.center_stage_pushButton.clicked.connect(self.center_piezo) 43 | self.ui.abs_mov_pushButton.clicked.connect(self.abs_mov) 44 | self.ui.rel_mov_pushButton.clicked.connect(self.rel_mov) 45 | self.ui.estimate_scan_time_pushButton.clicked.connect(self.estimate_scan_time) 46 | self.ui.start_x_y_sacan_pushButton.clicked.connect(self.x_y_scan) 47 | 48 | # self.ui.clear_pushButton.clicked.connect(self.clear_plot) 49 | self.pi_device = None 50 | self.spec = None 51 | 52 | self.ui.status_textBrowser.setText("Welcome!\nGUI Initiated!") 53 | 54 | self.show() 55 | 56 | def _handle_spectrometer_connection(self): 57 | if self.ui.connect_checkBox.isChecked(): 58 | self.connect_spectrometer() 59 | else: 60 | self.close_connection() 61 | 62 | def connect_spectrometer(self): 63 | if self.spec is None: 64 | devices = sb.list_devices() 65 | self.spec = sb.Spectrometer(devices[0]) 66 | self.ui.status_textBrowser.append("Ocean Optics Device Connected!\n\n Device:\n\n"+str(sb.list_devices()[0])) 67 | else: 68 | self.ui.status_textBrowser.append("Already Connected") 69 | 70 | def init_piezo_stage(self): 71 | if self.pi_device is None: 72 | self.pi_device = GCSDevice("E-710") # Creates a Controller instant 73 | self.pi_device.ConnectNIgpib(board=0,device=4) # Connect to GPIB board 74 | self.ui.status_textBrowser.append('Connected: {}'.format(self.pi_device.qIDN().strip())) 75 | # print('connected: {}'.format(self.pi_device.qIDN().strip())) 76 | 77 | self.axes = self.pi_device.axes[0:2] # selecting x and y axis of the stage 78 | 79 | self.pi_device.INI() 80 | self.pi_device.REF(axes=self.axes) 81 | 82 | self.pi_device.SVO(axes=self.axes, values=[True,True]) # Turn on servo control for both axes 83 | self.ui.status_textBrowser.append("Current Stage Position:\n{}".format(self.pi_device.qPOS(axes=self.axes))) 84 | # print(self.pi_device.qPOS(axes=self.axes)) 85 | else: 86 | self.ui.status_textBrowser.append("Piezo Stage Is Already Initialized!") 87 | 88 | def center_piezo(self): 89 | if self.pi_device is None: 90 | self.init_piezo_stage() 91 | self.pi_device.MOV(axes=self.axes, values=[50,50]) 92 | self.ui.status_textBrowser.append("Piezo Stage Centered: [50x,50y]") 93 | 94 | def current_piezo_stage_pos(self): 95 | if self.pi_device is None: 96 | self.init_piezo_stage() 97 | self.ui.status_textBrowser.append("Current Stage Position:\n{}".format(self.pi_device.qPOS(axes=self.axes))) 98 | 99 | def abs_mov(self): 100 | if self.pi_device is None: 101 | self.init_piezo_stage() 102 | x_abs_pos = self.ui.x_abs_doubleSpinBox.value() 103 | y_abs_pos = self.ui.y_abs_doubleSpinBox.value() 104 | self.pi_device.MOV(axes=self.axes, values=[x_abs_pos,y_abs_pos]) 105 | 106 | def rel_mov(self): 107 | if self.pi_device is None: 108 | self.init_piezo_stage() 109 | x_rel_pos = self.ui.x_rel_doubleSpinBox.value() 110 | y_rel_pos = self.ui.y_rel_doubleSpinBox.value() 111 | self.pi_device.MVR(axes=self.axes, values=[x_rel_pos,y_rel_pos]) 112 | 113 | def estimate_scan_time(self): 114 | x_scan_size = self.ui.x_size_doubleSpinBox.value() 115 | y_scan_size = self.ui.y_size_doubleSpinBox.value() 116 | 117 | x_step = self.ui.x_step_doubleSpinBox.value() 118 | y_step = self.ui.y_step_doubleSpinBox.value() 119 | 120 | if y_scan_size == 0: 121 | y_scan_size = 1 122 | 123 | if x_scan_size == 0: 124 | x_scan_size = 1 125 | 126 | if y_step == 0: 127 | y_step = 1 128 | 129 | if x_step == 0: 130 | x_step = 1 131 | 132 | y_range = int(np.ceil(y_scan_size/y_step)) 133 | x_range = int(np.ceil(x_scan_size/x_step)) 134 | 135 | total_points = x_range*y_range 136 | 137 | intg_time_ms = self.ui.intg_time_spinBox.value() 138 | scans_to_avg = self.ui.scan_to_avg_spinBox.value() 139 | 140 | total_time = total_points*(intg_time_ms*1e-3)*(scans_to_avg) # in seconds 141 | 142 | self.ui.status_textBrowser.append("Estimated scan time: "+str(np.float16(total_time/60))+" mins") 143 | 144 | def x_y_scan(self): 145 | if self.pi_device is None: 146 | self.init_piezo_stage() 147 | 148 | if self.spec is None: 149 | self.ui.status_textBrowser.append("Spectrometer not connected!\nForce connecting the spectrometer...") 150 | self.connect_spectrometer() 151 | 152 | start_time = time.time() 153 | x_start = self.ui.x_start_doubleSpinBox.value() 154 | y_start = self.ui.y_start_doubleSpinBox.value() 155 | 156 | x_scan_size = self.ui.x_size_doubleSpinBox.value() 157 | y_scan_size = self.ui.y_size_doubleSpinBox.value() 158 | 159 | x_step = self.ui.x_step_doubleSpinBox.value() 160 | y_step = self.ui.y_step_doubleSpinBox.value() 161 | 162 | if y_scan_size == 0: 163 | y_scan_size = 1 164 | 165 | if x_scan_size == 0: 166 | x_scan_size = 1 167 | 168 | if y_step == 0: 169 | y_step = 1 170 | 171 | if x_step == 0: 172 | x_step = 1 173 | 174 | y_range = int(np.ceil(y_scan_size/y_step)) 175 | x_range = int(np.ceil(x_scan_size/x_step)) 176 | 177 | # Define empty array for saving intensities 178 | data_array = np.zeros(shape=(x_range*y_range,2048)) 179 | 180 | self.ui.status_textBrowser.append("Starting Scan...") 181 | # Move to the starting position 182 | self.pi_device.MOV(axes=self.axes, values=[x_start,y_start]) 183 | 184 | self.ui.status_textBrowser.append("Scan in Progress...") 185 | 186 | k = 0 187 | for i in range(y_range): 188 | for j in range(x_range): 189 | # print(self.pi_device.qPOS(axes=self.axes)) 190 | 191 | self._read_spectrometer() 192 | data_array[k,:] = self.y 193 | self.ui.plot.plot(self.spec.wavelengths(), self.y, pen='r', clear=True) 194 | pg.QtGui.QApplication.processEvents() 195 | 196 | self.pi_device.MVR(axes=self.axes[0], values=[x_step]) 197 | 198 | self.ui.progressBar.setValue(100*((k+1)/(x_range*y_range))) 199 | k+=1 200 | # TODO 201 | # if statement needs to be modified to keep the stage at the finish y-pos for line scans in x, and same for y 202 | if i == y_range-1: # this if statement is there to keep the stage at the finish position (in x) and not bring it back like we were doing during the scan 203 | self.pi_device.MVR(axes=self.axes[1], values=[y_step]) 204 | else: 205 | self.pi_device.MVR(axes=self.axes, values=[-x_scan_size, y_step]) 206 | 207 | self.ui.status_textBrowser.append("Scan Complete!\nSaving Data...") 208 | 209 | save_dict = {"Wavelengths": self.spec.wavelengths(), "Intensities": data_array, 210 | "Scan Parameters":{"X scan start (um)": x_start, "Y scan start (um)": y_start, 211 | "X scan size (um)": x_scan_size, "Y scan size (um)": y_scan_size, 212 | "X step size (um)": x_step, "Y step size (um)": y_step}, 213 | "OceanOptics Parameters":{"Integration Time (ms)": self.ui.intg_time_spinBox.value(), 214 | "Scans Averages": self.ui.scan_to_avg_spinBox.value(), 215 | "Correct Dark Counts": self.ui.correct_dark_counts_checkBox.isChecked()} 216 | } 217 | 218 | pickle.dump(save_dict, open(self.save_folder+"/"+self.ui.lineEdit.text()+"_raw_PL_spectra_data.pkl", "wb")) 219 | 220 | self.ui.status_textBrowser.append("Data saved!\nTotal time taken:"+str(np.float16((time.time()-start_time)/60))+" mins") 221 | 222 | def save_file_location(self): 223 | self.save_folder = QtWidgets.QFileDialog.getExistingDirectory(self, caption="Select Folder") 224 | 225 | def save_single_spec(self): 226 | save_array = np.zeros(shape=(2048,2)) 227 | self._read_spectrometer() 228 | save_array[:,1] = self.y 229 | save_array[:,0] = self.spec.wavelengths() 230 | 231 | np.savetxt(self.save_folder+"/"+self.ui.lineEdit.text()+".txt", save_array, fmt = '%.5f', 232 | header = 'Wavelength (nm), Intensity (counts)', delimiter = ' ') 233 | 234 | def live(self): 235 | save_array = np.zeros(shape=(2048,2)) 236 | 237 | self.ui.plot.setLabel('left', 'Intensity', units='a.u.') 238 | self.ui.plot.setLabel('bottom', 'Wavelength', units='nm') 239 | j = 0 240 | while self.spec is not None:#self.ui.connect_checkBox.isChecked(): # this while loop works! 241 | self._read_spectrometer() 242 | save_array[:,1] = self.y 243 | 244 | self.ui.plot.plot(self.spec.wavelengths(), self.y, pen='r', clear=True) 245 | 246 | if self.ui.save_every_spec_checkBox.isChecked(): 247 | save_array[:,0] = self.spec.wavelengths() 248 | np.savetxt(self.save_folder+"/"+self.ui.lineEdit.text()+str(j)+".txt", save_array, fmt = '%.5f', 249 | header = 'Wavelength (nm), Intensity (counts)', delimiter = ' ') 250 | 251 | pg.QtGui.QApplication.processEvents() 252 | j += 1 253 | 254 | def _read_spectrometer(self): 255 | if self.spec is not None: 256 | 257 | intg_time_ms = self.ui.intg_time_spinBox.value() 258 | self.spec.integration_time_micros(intg_time_ms*1e3) 259 | 260 | scans_to_avg = self.ui.scan_to_avg_spinBox.value() 261 | Int_array = np.zeros(shape=(2048,scans_to_avg)) 262 | 263 | for i in range(scans_to_avg): #software average 264 | data = self.spec.spectrum(correct_dark_counts=self.ui.correct_dark_counts_checkBox.isChecked()) 265 | Int_array[:,i] = data[1] 266 | self.y = np.mean(Int_array, axis=-1) 267 | 268 | else: 269 | self.ui.status_textBrowser.append("Connect to Spectrometer!") 270 | raise Exception("Must connect to spectrometer first!") 271 | 272 | 273 | def close_connection(self): 274 | if self.spec is not None: 275 | self.spec.close() 276 | self.ui.status_textBrowser.append("Ocean Optics Device Disconnected") 277 | del self.spec 278 | self.spec = None 279 | 280 | def close_application(self): 281 | choice = QtGui.QMessageBox.question(self, 'EXIT!', 282 | "Do you want to exit the app?", 283 | QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) 284 | if choice == QtGui.QMessageBox.Yes: 285 | sys.exit() 286 | else: 287 | pass 288 | 289 | 290 | win = MainWindow() 291 | 292 | 293 | ## Start Qt event loop unless running in interactive mode or using pyside. 294 | if __name__ == '__main__': 295 | import sys 296 | if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): 297 | QtGui.QApplication.instance().exec_() 298 | -------------------------------------------------------------------------------- /PythonGUI_apps/PLQE_analysis/column_selection_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | Select number of columns 15 | 16 | 17 | 18 | 19 | 20 | 21 | Data preview 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Select data columns 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Ref 38 | 39 | 40 | 41 | 42 | 43 | 44 | Inpath 45 | 46 | 47 | 48 | 49 | 50 | 51 | Outpath 52 | 53 | 54 | 55 | 56 | 57 | 58 | 2 59 | 60 | 61 | 62 | 63 | 64 | 65 | 3 66 | 67 | 68 | 69 | 70 | 71 | 72 | 4 73 | 74 | 75 | 76 | 77 | 78 | 79 | Done 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 0 97 | 0 98 | 800 99 | 31 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /PythonGUI_apps/PLQE_analysis/plqe_analysis.py: -------------------------------------------------------------------------------- 1 | # system imports 2 | from pathlib import Path 3 | import os.path 4 | import pyqtgraph as pg 5 | from pyqtgraph import exporters 6 | from pyqtgraph.Qt import QtCore, QtGui, QtWidgets 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | 10 | # local modules 11 | 12 | pg.mkQApp() 13 | pg.setConfigOption('background', 'w') 14 | 15 | base_path = Path(__file__).parent 16 | file_path = (base_path / "plqe_analysis_gui.ui").resolve() 17 | 18 | uiFile = file_path 19 | 20 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 21 | 22 | """params for plotting""" 23 | plt.rc('xtick', labelsize = 20) 24 | plt.rc('xtick.major', pad = 3) 25 | plt.rc('ytick', labelsize = 20) 26 | plt.rc('lines', lw = 1.5, markersize = 7.5) 27 | plt.rc('legend', fontsize = 20) 28 | plt.rc('axes', linewidth = 3.5) 29 | 30 | class MainWindow(TemplateBaseClass): 31 | 32 | def __init__(self): 33 | super(TemplateBaseClass, self).__init__() 34 | 35 | # Create the main window 36 | self.ui = WindowTemplate() 37 | self.ui.setupUi(self) 38 | 39 | #setup uv vis plot 40 | self.plot = self.ui.plotWidget.getPlotItem() 41 | self.plot.setTitle(title="Wavelength vs. Intensity") 42 | self.plot.setLabel('bottom', 'Wavelength', unit='nm') 43 | self.plot.setLabel('left', 'Intensity', unit='a.u.') 44 | self.plot.setLogMode(x=None, y=1) 45 | 46 | #setup line rois for laser and emission 47 | self.laser_region = pg.LinearRegionItem(brush=QtGui.QBrush(QtGui.QColor(255, 0, 0, 50))) 48 | self.laser_region.sigRegionChanged.connect(self.update_laser_spinBoxes) 49 | self.emission_region = pg.LinearRegionItem() 50 | self.emission_region.sigRegionChanged.connect(self.update_emission_spinBoxes) 51 | self.laser_region.setRegion((200, 400)) 52 | self.emission_region.setRegion((700, 800)) 53 | 54 | #setup ui signals 55 | self.ui.load_data_pushButton.clicked.connect(self.open_data_file) 56 | self.ui.plot_pushButton.clicked.connect(self.plot_intensity) 57 | self.ui.clear_pushButton.clicked.connect(self.clear) 58 | self.ui.calculate_plqe_pushButton.clicked.connect(self.calculate_plqe) 59 | self.ui.laser_start_spinBox.valueChanged.connect(self.update_laser_region) 60 | self.ui.laser_stop_spinBox.valueChanged.connect(self.update_laser_region) 61 | self.ui.emission_start_spinBox.valueChanged.connect(self.update_emission_region) 62 | self.ui.emission_stop_spinBox.valueChanged.connect(self.update_emission_region) 63 | 64 | self.show() 65 | 66 | def open_data_file(self): 67 | """ Open data file """ 68 | try: 69 | self.filename = QtWidgets.QFileDialog.getOpenFileName(self) 70 | #self.data = np.loadtxt(self.filename[0], delimiter = '\t', skiprows = 1) 71 | if ".txt" in self.filename[0]: 72 | self.data = np.loadtxt(self.filename[0], delimiter = '\t', skiprows = 1) 73 | elif ".csv" in self.filename[0]: 74 | self.data = np.loadtxt(self.filename[0], delimiter = ',', skiprows = 1) 75 | elif ".qua" in self.filename[0]:#TODO: Include a Pop-up window for input for skipping header 76 | self.data = np.genfromtxt(self.filename[0], delimiter = '\t', skip_header = 28) 77 | self.cs_window = ColSelectionWindow(self.data) 78 | self.cs_window.col_selection_signal.connect(self.open_with_col_selection) 79 | self.nm = np.copy(self.data[:,0]) 80 | #self.ref_data = np.copy(self.data[:,1]) 81 | #self.inpath_data = np.copy(self.data[:,2]) 82 | #self.outpath_data = np.copy(self.data[:,3]) 83 | except Exception as err: 84 | print(format(err)) 85 | 86 | def open_with_col_selection(self): 87 | ref_data_col = self.cs_window.ui.ref_spinBox.value() - 1 #subtract since spinboxes refer to column num and not index 88 | inpath_data_col = self.cs_window.ui.inpath_spinBox.value() - 1 89 | outpath_data_col = self.cs_window.ui.outpath_spinBox.value() - 1 90 | self.ref_data = np.copy(self.data[:,ref_data_col]) 91 | self.inpath_data = np.copy(self.data[:,inpath_data_col]) 92 | self.outpath_data = np.copy(self.data[:,outpath_data_col]) 93 | 94 | def update_laser_spinBoxes(self): 95 | """ Update laser spinboxes based on line rois """ 96 | self.laser_start, self.laser_stop = self.laser_region.getRegion() 97 | self.ui.laser_start_spinBox.setValue(self.laser_start) 98 | self.ui.laser_stop_spinBox.setValue(self.laser_stop) 99 | 100 | 101 | def update_emission_spinBoxes(self): 102 | """ Update emission spinboxes based on line rois """ 103 | self.emission_start, self.emission_stop = self.emission_region.getRegion() 104 | self.ui.emission_start_spinBox.setValue(self.emission_start) 105 | self.ui.emission_stop_spinBox.setValue(self.emission_stop) 106 | 107 | def update_laser_region(self): 108 | """ Update laser line rois based on spinboxes """ 109 | laser_start = self.ui.laser_start_spinBox.value() 110 | laser_stop = self.ui.laser_stop_spinBox.value() 111 | self.laser_region.setRegion((laser_start, laser_stop)) 112 | 113 | def update_emission_region(self): 114 | """ Update emission line rois based on spinboxes """ 115 | emission_start = self.ui.emission_start_spinBox.value() 116 | emission_stop = self.ui.emission_stop_spinBox.value() 117 | self.emission_region.setRegion((emission_start, emission_stop)) 118 | 119 | def plot_intensity(self): 120 | try: 121 | self.plot.plot(self.nm, self.inpath_data, pen='r') 122 | self.plot.addItem(self.laser_region, ignoreBounds=True) 123 | self.plot.addItem(self.emission_region, ignoreBounds=True) 124 | except Exception as err: 125 | print(format(err)) 126 | 127 | def find_nearest(self,array,value): 128 | idx = (np.abs(array-value)).argmin() 129 | return idx 130 | 131 | def calculate_plqe(self): 132 | 133 | nm_interp_step = 1 134 | nm_interp_start = np.ceil(self.nm[0] / nm_interp_step) * nm_interp_step 135 | nm_interp_stop = np.floor(self.nm[len(self.nm) - 1] / nm_interp_step) * nm_interp_step 136 | nm_interp = np.arange(nm_interp_start, nm_interp_stop + nm_interp_step, nm_interp_step) 137 | 138 | ref_interp = np.interp(nm_interp, self.nm, self.ref_data) 139 | 140 | 141 | inpath_interp = np.interp(nm_interp, self.nm, self.inpath_data) 142 | outpath_interp = np.interp(nm_interp, self.nm, self.outpath_data) 143 | 144 | 145 | """L_x is area under laser profile for experiment x""" 146 | """P_x_ is area under emission profile for experiment x""" 147 | 148 | 149 | #plt.semilogy(nm, a1_outpath_data[:,1]) 150 | 151 | emission_start_idx = self.find_nearest(nm_interp, self.emission_start) 152 | emission_stop_idx = self.find_nearest(nm_interp, self.emission_stop) 153 | 154 | laser_start_idx = self.find_nearest(nm_interp, self.laser_start) 155 | laser_stop_idx = self.find_nearest(nm_interp, self.laser_stop) 156 | 157 | la = np.trapz(ref_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) 158 | lb = np.trapz(outpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) 159 | lc = np.trapz(inpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx]) 160 | 161 | pa = np.trapz(ref_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) 162 | pb = np.trapz(outpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) 163 | pc = np.trapz(inpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx]) 164 | 165 | absorb = 1.0 - (lc / lb) 166 | 167 | plqe = 100 * (pc - ((1.0 - absorb) * pb)) / (la * absorb) 168 | #print('PLQE Percent = %.3f' %(plqe)) 169 | #return plqe 170 | self.ui.plqe_label.setText("%.3f" %(plqe)) 171 | 172 | def clear(self): 173 | self.plot.clear() 174 | 175 | """Table view GUI""" 176 | ui_file_path = (base_path / "column_selection_gui.ui").resolve() 177 | col_selection_WindowTemplate, col_selection_TemplateBaseClass = pg.Qt.loadUiType(ui_file_path) 178 | 179 | class ColSelectionWindow(col_selection_TemplateBaseClass): 180 | 181 | col_selection_signal = QtCore.pyqtSignal() #signal to help with pass info back to MainWindow 182 | 183 | def __init__(self, data): 184 | col_selection_TemplateBaseClass.__init__(self) 185 | # Create the param window 186 | self.ui = col_selection_WindowTemplate() 187 | self.ui.setupUi(self) 188 | self.ui.done_pushButton.clicked.connect(self.done) 189 | 190 | self.table_widget = pg.TableWidget() 191 | self.ui.data_preview_groupBox.layout().addWidget(self.table_widget) 192 | 193 | self.table_widget.setData(data) 194 | 195 | #self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) 196 | self.show() 197 | 198 | def done(self): 199 | self.col_selection_signal.emit() 200 | self.ui.textBrowser.setText("Data successfully loaded.") 201 | #self.close() 202 | 203 | def closeEvent(self, event): 204 | self.col_selection_signal.emit() 205 | 206 | """Run the Main Window""" 207 | def run(): 208 | win = MainWindow() 209 | QtGui.QApplication.instance().exec_() 210 | return win 211 | 212 | #run() 213 | -------------------------------------------------------------------------------- /PythonGUI_apps/PLQE_analysis/plqe_analysis_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 575 10 | 524 11 | 12 | 13 | 14 | PLQE Analysis 15 | 16 | 17 | 18 | 19 | 20 | PLQE 21 | 22 | 23 | 24 | 25 | 26 | Laser start 27 | 28 | 29 | 30 | 31 | 32 | 33 | 9999.000000000000000 34 | 35 | 36 | 200.000000000000000 37 | 38 | 39 | 40 | 41 | 42 | 43 | Laser stop 44 | 45 | 46 | 47 | 48 | 49 | 50 | 9999.000000000000000 51 | 52 | 53 | 400.000000000000000 54 | 55 | 56 | 57 | 58 | 59 | 60 | Emission start 61 | 62 | 63 | 64 | 65 | 66 | 67 | 9999.000000000000000 68 | 69 | 70 | 700.000000000000000 71 | 72 | 73 | 74 | 75 | 76 | 77 | Emission stop 78 | 79 | 80 | 81 | 82 | 83 | 84 | 9999.000000000000000 85 | 86 | 87 | 800.000000000000000 88 | 89 | 90 | 91 | 92 | 93 | 94 | PLQE Percent 95 | 96 | 97 | 98 | 99 | 100 | 101 | 0 102 | 103 | 104 | 105 | 106 | 107 | 108 | Calculate PLQE 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Clear 119 | 120 | 121 | 122 | 123 | 124 | 125 | Plot 126 | 127 | 128 | 129 | 130 | 131 | 132 | Load data 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | PlotWidget 144 | QGraphicsView 145 |
pyqtgraph
146 |
147 |
148 | 149 | 150 |
151 | -------------------------------------------------------------------------------- /PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 31 15:46:54 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | # -*- coding: utf-8 -*- 9 | """ 10 | Created on Thu Jan 31 20:48:03 2019 11 | 12 | @author: Sarthak 13 | """ 14 | import numpy as np 15 | from lmfit.models import GaussianModel, LorentzianModel 16 | 17 | class Spectra_Fit(object): 18 | """ 19 | Fit a spectrum. 20 | 21 | Attributes: 22 | data: spectrum data (x-axis and y-axis) 23 | ref: reference spectrum (both x and y-axis) for background correction 24 | """ 25 | 26 | def __init__(self, data, ref, wlref = None, fit_in_eV = True): 27 | self.data = data 28 | self.ref = ref 29 | self.wlref = wlref 30 | self.fit_in_eV = fit_in_eV 31 | 32 | def background_correction(self): 33 | """Return the background corrected spectrum""" 34 | x = self.data[:, 0] # wavelengths 35 | y = self.data[:, 1] # intensity 36 | yref = self.ref[:, 1] 37 | y = y - yref # background correction 38 | if self.wlref is not None: 39 | wlref = self.wlref[:,1] 40 | y = y/wlref 41 | 42 | if self.fit_in_eV is True: 43 | x = np.sort(1240/self.data[:, 0]) # converting to eV and sorting in ascending order 44 | y = [y[i] for i in np.argsort(1240/x)] # sorting argument of y acc to x sorting index 45 | # need to do this because data is collected in wavelength 46 | 47 | return [x,y] 48 | 49 | class Single_Gaussian(Spectra_Fit): 50 | """Fit a single gaussian to the spectrum 51 | 52 | Attributes: 53 | data: spectrum data (x-axis and y-axis) 54 | ref: reference spectrum (both x and y-axis) for background correction 55 | """ 56 | 57 | def gaussian_model(self): 58 | x,y = self.background_correction() 59 | gmodel = GaussianModel(prefix = 'g1_') # calling gaussian model 60 | pars = gmodel.guess(y, x=x) # parameters - center, width, height 61 | result = gmodel.fit(y, pars, x=x, nan_policy='propagate') 62 | return result 63 | 64 | # def gaussian_model_w_lims(self, center_initial_guess=None, sigma_initial_guess=None, center_min=None, center_max=None): 65 | # x,y = self.background_correction() 66 | # gmodel = GaussianModel(prefix = 'g1_') # calling gaussian model 67 | # pars = gmodel.guess(y, x=x) # parameters - center, width, height 68 | # pars['g1_center'].set(center_initial_guess, min=center_min, max=center_max) 69 | # pars['g1_sigma'].set(sigma_initial_guess) 70 | # result = gmodel.fit(y, pars, x=x, nan_policy='propagate') 71 | # return result #770 760 780 sigma 15 72 | def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): 73 | x,y = self.background_correction() 74 | if self.fit_in_eV is True: 75 | peak_pos = 1240/peak_pos 76 | sigma = 1240/sigma 77 | min_max_range = np.sort(1240/np.asarray(min_max_range)) 78 | gmodel = GaussianModel(prefix = 'g1_') # calling gaussian model 79 | pars = gmodel.guess(y, x=x) # parameters - center, width, height 80 | pars['g1_center'].set(peak_pos, min=min_max_range[0], max=min_max_range[1]) 81 | pars['g1_sigma'].set(sigma) 82 | result = gmodel.fit(y, pars, x=x, nan_policy='propagate') 83 | return result 84 | 85 | class Single_Lorentzian(Spectra_Fit): 86 | """Fit a single Lorentzian to the spectrum 87 | 88 | Attributes: 89 | data: spectrum data (x-axis and y-axis) 90 | ref: reference spectrum (both x and y-axis) for background correction 91 | """ 92 | 93 | def lorentzian_model(self): 94 | x,y = self.background_correction() 95 | lmodel = LorentzianModel(prefix = 'l1_') # calling lorentzian model 96 | pars = lmodel.guess(y, x=x) # parameters - center, width, height 97 | result = lmodel.fit(y, pars, x=x, nan_policy='propagate') 98 | return result 99 | 100 | def lorentzian_model_w_lims(self, peak_pos, sigma, min_max_range): 101 | x,y = self.background_correction() 102 | if self.fit_in_eV is True: 103 | peak_pos = 1240/peak_pos 104 | sigma = 1240/sigma 105 | min_max_range = np.sort(1240/np.asarray(min_max_range)) 106 | lmodel = LorentzianModel(prefix = 'l1_') # calling lorentzian model 107 | pars = lmodel.guess(y, x=x) # parameters - center, width, height 108 | pars['l1_center'].set(peak_pos, min = min_max_range[0], max = min_max_range[1]) 109 | pars['l1_sigma'].set(sigma) 110 | result = lmodel.fit(y, pars, x=x, nan_policy='propagate') 111 | return result 112 | 113 | class Double_Gaussian(Spectra_Fit): 114 | """Fit two gaussians to the spectrum 115 | 116 | Attributes: 117 | data: spectrum data (x-axis and y-axis) 118 | ref: reference spectrum (both x and y-axis) for background correction 119 | """ 120 | 121 | def gaussian_model(self): 122 | 123 | x,y = self.background_correction() 124 | gmodel_1 = GaussianModel(prefix='g1_') # calling gaussian model 125 | pars = gmodel_1.guess(y, x=x) # parameters - center, width, height 126 | 127 | gmodel_2 = GaussianModel(prefix='g2_') 128 | pars.update(gmodel_2.make_params()) # update parameters - center, width, height 129 | 130 | gmodel = gmodel_1 + gmodel_2 131 | result = gmodel.fit(y, pars, x=x, nan_policy='propagate') 132 | return result 133 | 134 | def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): 135 | #center_initial_guesses - list containing initial guesses for peak centers. [center_guess1, center_guess2] 136 | #sigma_initial_guesses - list containing initial guesses for sigma. [sigma1, sigma2] 137 | #min_max_range - list containing lists of min and max for peak center. [ [min1, max1], [min2, max2] ] 138 | 139 | x,y = self.background_correction() 140 | if self.fit_in_eV is True: 141 | peak_pos = 1240/np.asarray(peak_pos) 142 | sigma = 1240/np.asarray(sigma) 143 | min_max_range = np.sort(1240/np.asarray(min_max_range)) 144 | gmodel_1 = GaussianModel(prefix='g1_') # calling gaussian model 145 | pars = gmodel_1.guess(y, x=x) # parameters - center, width, height 146 | pars['g1_center'].set(peak_pos[0], min = min_max_range[0][0], max = min_max_range[0][1]) 147 | pars['g1_sigma'].set(sigma[0]) 148 | pars['g1_amplitude'].set(min=0) 149 | 150 | gmodel_2 = GaussianModel(prefix='g2_') 151 | pars.update(gmodel_2.make_params()) # update parameters - center, width, height 152 | pars['g2_center'].set(peak_pos[1], min = min_max_range[1][0], max = min_max_range[1][1]) 153 | pars['g2_sigma'].set(sigma[1], min = pars['g1_sigma'].value) 154 | pars['g2_amplitude'].set(min = 0) 155 | 156 | gmodel = gmodel_1 + gmodel_2 157 | result = gmodel.fit(y, pars, x=x, nan_policy='propagate') 158 | return result 159 | 160 | class Multi_Gaussian(Spectra_Fit): 161 | 162 | # def __init__(self, data, ref, num_of_gaussians, peak_pos, sigma, min_max_range): 163 | # Spectra_Fit.__init__(self, data, ref) 164 | # self.num_of_gaussians = num_of_gaussians 165 | # self.peak_pos = peak_pos 166 | # self.min_max_range = min_max_range 167 | def __init__(self, data, ref, num_of_gaussians, wlref=None, fit_in_eV = True): 168 | Spectra_Fit.__init__(self, data, ref, wlref, fit_in_eV=True) 169 | self.num_of_gaussians = num_of_gaussians 170 | 171 | def gaussian_model(self): 172 | composite_model = None 173 | composite_pars = None 174 | 175 | x,y = self.background_correction() 176 | 177 | for i in range(self.num_of_gaussians): 178 | 179 | model = GaussianModel(prefix='g'+str(i+1)+'_') 180 | 181 | if composite_pars is None: 182 | composite_pars = model.guess(y, x=x) 183 | # composite_pars = model.make_params() 184 | 185 | else: 186 | composite_pars.update(model.make_params()) 187 | 188 | if composite_model is None: 189 | composite_model = model 190 | else: 191 | composite_model += model 192 | 193 | result = composite_model.fit(y, composite_pars, x=x, nan_policy='propagate') 194 | return result 195 | 196 | def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range): 197 | self.peak_pos = peak_pos 198 | self.sigma = sigma 199 | self.min_max_range = min_max_range 200 | 201 | composite_model = None 202 | composite_pars = None 203 | 204 | x,y = self.background_correction() 205 | 206 | assert self.num_of_gaussians == len(self.peak_pos), ("Number of gaussians must be equal to the number of peak positions") 207 | 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") 208 | 209 | if self.fit_in_eV is True: 210 | peak_pos = 1240/np.asarray(peak_pos) 211 | sigma = 1240/np.asarray(sigma) 212 | min_max_range = np.sort(1240/np.asarray(min_max_range)) 213 | 214 | 215 | for i in range(self.num_of_gaussians): 216 | 217 | model = GaussianModel(prefix='g'+str(i+1)+'_') 218 | 219 | if composite_pars is None: 220 | composite_pars = model.guess(y, x=x) 221 | # composite_pars = model.make_params() 222 | composite_pars['g'+str(i+1)+'_center'].set(self.peak_pos[i], 223 | min = self.min_max_range[0][0], max = self.min_max_range[0][1]) 224 | composite_pars['g'+str(i+1)+'_sigma'].set(self.sigma[i]) 225 | composite_pars['g'+str(i+1)+'_amplitude'].set(min = 0) 226 | 227 | else: 228 | composite_pars.update(model.make_params()) 229 | composite_pars['g'+str(i+1)+'_center'].set(self.peak_pos[i], 230 | min = self.min_max_range[i][0], max = self.min_max_range[i][1]) 231 | composite_pars['g'+str(i+1)+'_sigma'].set(self.sigma[i], min = composite_pars['g1_sigma'].value) 232 | composite_pars['g'+str(i+1)+'_amplitude'].set(min = 0) 233 | 234 | 235 | if composite_model is None: 236 | composite_model = model 237 | else: 238 | composite_model += model 239 | 240 | result = composite_model.fit(y, composite_pars, x=x, nan_policy='propagate') 241 | return result -------------------------------------------------------------------------------- /PythonGUI_apps/Spectrum_analysis/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function -------------------------------------------------------------------------------- /PythonGUI_apps/Spectrum_analysis/analyze_fit_results.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 664 10 | 136 11 | 12 | 13 | 14 | Analze Fit Results 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | QFrame::StyledPanel 23 | 24 | 25 | QFrame::Raised 26 | 27 | 28 | 29 | 30 | 31 | 32 | 12 33 | 34 | 35 | 36 | Check! 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 12 45 | 46 | 47 | 48 | 1000000000 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 12 57 | 58 | 59 | 60 | Result number: 61 | 62 | 63 | Qt::AlignCenter 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 15 72 | 73 | 74 | 75 | Analyze Fit Results 76 | 77 | 78 | Qt::AlignCenter 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /PythonGUI_apps/Spectrum_analysis/pyqtgraph_MATPLOTLIBWIDGET.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Sep 2 13:02:50 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | from pyqtgraph.Qt import QtGui, QtCore, USE_PYSIDE, USE_PYQT5 9 | import matplotlib 10 | 11 | #if not USE_PYQT5: 12 | # if USE_PYSIDE: 13 | # matplotlib.rcParams['backend.qt4']='PySide' 14 | # 15 | # from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 16 | # from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar 17 | #else: 18 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas 19 | from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar 20 | 21 | from matplotlib.figure import Figure 22 | 23 | if matplotlib.get_backend() is not 'Qt5Agg': 24 | matplotlib.use('Qt5Agg') 25 | 26 | class MatplotlibWidget(QtGui.QWidget): 27 | """ 28 | Implements a Matplotlib figure inside a QWidget. 29 | Use getFigure() and redraw() to interact with matplotlib. 30 | 31 | Example:: 32 | 33 | mw = MatplotlibWidget() 34 | subplot = mw.getFigure().add_subplot(111) 35 | subplot.plot(x,y) 36 | mw.draw() 37 | """ 38 | 39 | def __init__(self, size=(5.0, 4.0), dpi=100): 40 | QtGui.QWidget.__init__(self) 41 | self.fig = Figure(size, dpi=dpi) 42 | self.canvas = FigureCanvas(self.fig) 43 | self.canvas.setParent(self) 44 | self.toolbar = NavigationToolbar(self.canvas, self) 45 | 46 | self.vbox = QtGui.QVBoxLayout() 47 | self.vbox.addWidget(self.toolbar) 48 | self.vbox.addWidget(self.canvas) 49 | 50 | self.setLayout(self.vbox) 51 | 52 | def getFigure(self): 53 | return self.fig 54 | 55 | def draw(self): 56 | self.canvas.draw() -------------------------------------------------------------------------------- /PythonGUI_apps/Spectrum_analysis/scan_params_input.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 542 10 | 211 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | X scan size 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Y scan size 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | X step size 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Y step size 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Done 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /PythonGUI_apps/Table/Table_widget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Sep 2 17:04:49 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | from pathlib import Path 9 | import pyqtgraph as pg 10 | from pyqtgraph.python2_3 import asUnicode 11 | from pyqtgraph.Qt import QtCore, QtGui 12 | 13 | 14 | pg.mkQApp() 15 | pg.setConfigOption('background', 'w') 16 | 17 | 18 | base_path = Path(__file__).parent 19 | file_path = (base_path / "Table_widget_gui.ui").resolve() 20 | 21 | uiFile = file_path 22 | 23 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 24 | 25 | class MainWindow(TemplateBaseClass): 26 | 27 | def __init__(self): 28 | super(TemplateBaseClass, self).__init__() 29 | 30 | # Create the main window 31 | self.ui = WindowTemplate() 32 | self.ui.setupUi(self) 33 | 34 | self.clear() 35 | 36 | self.ui.clear_pushButton.clicked.connect(self.clear) 37 | self.ui.add_row_pushButton.clicked.connect(self.add_row) 38 | self.ui.add_column_pushButton.clicked.connect(self.add_column) 39 | 40 | """Saving and Copying --- implemented from pyqtgraph TableWidget""" 41 | self.contextMenu = QtGui.QMenu() 42 | self.contextMenu.addAction('Copy Selection').triggered.connect(self.copySel) 43 | self.contextMenu.addAction('Copy All').triggered.connect(self.copyAll) 44 | self.contextMenu.addAction('Save Selection').triggered.connect(self.saveSel) 45 | self.contextMenu.addAction('Save All').triggered.connect(self.saveAll) 46 | 47 | self.show() 48 | 49 | def clear(self): 50 | self.ui.tableWidget.clear() 51 | self.verticalHeadersSet = False 52 | self.horizontalHeadersSet = False 53 | 54 | def add_row(self): 55 | row_position = self.ui.tableWidget.rowCount() 56 | self.ui.tableWidget.insertRow(row_position) 57 | 58 | def add_column(self): 59 | column_position = self.ui.tableWidget.columnCount() 60 | self.ui.tableWidget.insertColumn(column_position) 61 | 62 | def save_table(self):# Needs to be implemented 63 | print(self.ui.tableWidget.currentItem().text()) 64 | 65 | def serialize(self, useSelection=False): 66 | """Convert entire table (or just selected area) into tab-separated text values""" 67 | if useSelection: 68 | selection = self.ui.tableWidget.selectedRanges()[0] 69 | rows = list(range(selection.topRow(), 70 | selection.bottomRow() + 1)) 71 | columns = list(range(selection.leftColumn(), 72 | selection.rightColumn() + 1)) 73 | else: 74 | rows = list(range(self.ui.tableWidget.rowCount())) 75 | columns = list(range(self.ui.tableWidget.columnCount())) 76 | 77 | data = [] 78 | if self.horizontalHeadersSet: 79 | row = [] 80 | if self.verticalHeadersSet: 81 | row.append(asUnicode('')) 82 | 83 | for c in columns: 84 | row.append(asUnicode(self.ui.tableWidget.horizontalHeaderItem(c).text())) 85 | data.append(row) 86 | 87 | for r in rows: 88 | row = [] 89 | if self.verticalHeadersSet: 90 | row.append(asUnicode(self.ui.tableWidget.verticalHeaderItem(r).text())) 91 | for c in columns: 92 | item = self.ui.tableWidget.item(r, c) 93 | if item is not None: 94 | row.append(asUnicode(item.text())) 95 | else: 96 | row.append(asUnicode('')) 97 | data.append(row) 98 | 99 | s = '' 100 | for row in data: 101 | s += ('\t'.join(row) + '\n') 102 | return s 103 | 104 | 105 | def copySel(self): 106 | """Copy selected data to clipboard.""" 107 | QtGui.QApplication.clipboard().setText(self.serialize(useSelection=True)) 108 | 109 | def copyAll(self): 110 | """Copy all data to clipboard.""" 111 | QtGui.QApplication.clipboard().setText(self.serialize(useSelection=False)) 112 | 113 | 114 | def saveSel(self): 115 | """Save selected data to file.""" 116 | self.save(self.serialize(useSelection=True)) 117 | 118 | 119 | def saveAll(self): 120 | """Save all data to file.""" 121 | self.save(self.serialize(useSelection=False)) 122 | 123 | 124 | def save(self, data): 125 | fileName = QtGui.QFileDialog.getSaveFileName(self, "Save As..", "", "Tab-separated values (*.tsv)") 126 | if fileName == '': 127 | return 128 | open(fileName[0], 'w').write(data) 129 | 130 | def contextMenuEvent(self, ev): 131 | self.contextMenu.popup(ev.globalPos()) 132 | 133 | def keyPressEvent(self, ev): 134 | if ev.key() == QtCore.Qt.Key_C and ev.modifiers() == QtCore.Qt.ControlModifier: 135 | ev.accept() 136 | self.copySel() 137 | # else: 138 | # QtGui.QTableWidget.keyPressEvent(self, ev) 139 | """Run the Main Window""" 140 | def run(): 141 | win = MainWindow() 142 | QtGui.QApplication.instance().exec_() 143 | return win 144 | 145 | #run() -------------------------------------------------------------------------------- /PythonGUI_apps/Table/Table_widget_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 658 10 | 438 11 | 12 | 13 | 14 | TableWidget 15 | 16 | 17 | 18 | 19 | 20 | Add Row 21 | 22 | 23 | 24 | 25 | 26 | 27 | Add Column 28 | 29 | 30 | 31 | 32 | 33 | 34 | Clear 35 | 36 | 37 | 38 | 39 | 40 | 41 | 12 42 | 43 | 44 | 6 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /PythonGUI_apps/Table/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Sep 2 17:52:35 2019 4 | 5 | @author: Sarthak 6 | """ 7 | 8 | -------------------------------------------------------------------------------- /PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py: -------------------------------------------------------------------------------- 1 | # system imports 2 | from pathlib import Path 3 | import os.path 4 | import pyqtgraph as pg 5 | from pyqtgraph import exporters 6 | from pyqtgraph.Qt import QtCore, QtGui, QtWidgets 7 | import matplotlib.pyplot as plt 8 | 9 | import numpy as np 10 | import time 11 | 12 | # local modules 13 | 14 | pg.mkQApp() 15 | pg.setConfigOption('background', 'w') 16 | 17 | base_path = Path(__file__).parent 18 | file_path = (base_path / "uv_vis_analysis_gui.ui").resolve() 19 | 20 | uiFile = file_path 21 | 22 | WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile) 23 | 24 | """params for plotting""" 25 | plt.rc('xtick', labelsize = 20) 26 | plt.rc('xtick.major', pad = 3) 27 | plt.rc('ytick', labelsize = 20) 28 | plt.rc('lines', lw = 1.5, markersize = 7.5) 29 | plt.rc('legend', fontsize = 20) 30 | plt.rc('axes', linewidth = 3.5) 31 | 32 | class MainWindow(TemplateBaseClass): 33 | 34 | def __init__(self): 35 | super(TemplateBaseClass, self).__init__() 36 | 37 | # Create the main window 38 | self.ui = WindowTemplate() 39 | self.ui.setupUi(self) 40 | 41 | #setup uv vis plot 42 | self.absorbance_plot_layout = pg.GraphicsLayoutWidget() 43 | self.ui.absorbance_plot_container.layout().addWidget(self.absorbance_plot_layout) 44 | self.absorbance_plot = self.absorbance_plot_layout.addPlot(title="Wavelengths vs. Absorbance") 45 | self.absorbance_plot.setLabel('bottom', 'Wavelength', unit='nm') 46 | self.absorbance_plot.setLabel('left', 'Absorbance', unit='a.u.') 47 | 48 | #setup correction region for uv vis 49 | self.correction_region = pg.LinearRegionItem() 50 | self.correction_region_min = 600 51 | self.correction_region_max = 900 52 | self.correction_region.setRegion((self.correction_region_min, self.correction_region_max)) 53 | 54 | #setup uv vis ui signals 55 | self.ui.correct_for_scattering_checkBox.stateChanged.connect(self.scattering_checkBox_state) 56 | self.scatter_corrected = False 57 | self.ui.actionLoad_data.triggered.connect(self.open_data_file) 58 | self.ui.plot_absorbance_pushButton.clicked.connect(self.plot_absorbance) 59 | self.ui.clear_uvvis_pushButton.clicked.connect(self.clear_uvvis) 60 | self.ui.export_uv_vis_pushButton.clicked.connect(self.export_uv_vis) 61 | self.correction_region.sigRegionChanged.connect(self.update_correction_region) 62 | 63 | #setup tauc plot 64 | self.tauc_plot_layout = pg.GraphicsLayoutWidget() 65 | self.ui.tauc_plot_container.layout().addWidget(self.tauc_plot_layout) 66 | self.tauc_plot = self.tauc_plot_layout.addPlot(title="Tauc plot fit") 67 | self.tauc_plot.setLabel('bottom', 'hv', unit='ev') 68 | y_label = '(ahv)' + chr(0x00B2) #char is superscripted 2 69 | self.tauc_plot.setLabel('left', y_label) 70 | 71 | #setup tauc ui signals 72 | self.ui.plot_tauc_pushButton.clicked.connect(self.plot_tauc) 73 | self.ui.clear_tauc_pushButton.clicked.connect(self.clear_tauc) 74 | self.ui.export_tauc_pushButton.clicked.connect(self.export_tauc) 75 | 76 | self.show() 77 | 78 | def open_data_file(self): 79 | try: 80 | self.filename = QtWidgets.QFileDialog.getOpenFileName(self) 81 | self.data = np.loadtxt(self.filename[0], delimiter = ',', skiprows = 2) 82 | self.Wavelength = self.data[:,0] # in nm 83 | self.Absorbance = self.data[:,1] 84 | except Exception as err: 85 | print(format(err)) 86 | 87 | def update_correction_region(self): 88 | """ Update correction region variables from region """ 89 | self.correction_region_min, self.correction_region_max = self.correction_region.getRegion() 90 | 91 | def scattering_checkBox_state(self): 92 | if self.ui.correct_for_scattering_checkBox.isChecked(): 93 | self.scatter_corrected = True 94 | self.ui.mean_radioButton.setEnabled(True) 95 | self.ui.fourth_orderpoly_radioButton.setEnabled(True) 96 | else: 97 | self.scatter_corrected = False 98 | self.ui.mean_radioButton.setEnabled(False) 99 | self.ui.fourth_orderpoly_radioButton.setEnabled(False) 100 | 101 | def plot_absorbance(self): 102 | try: 103 | self.plotted_absorbance = self.Absorbance #by default set to original absorbance data 104 | if self.scatter_corrected == True: 105 | if self.ui.fourth_orderpoly_radioButton.isChecked(): 106 | p = (np.polyfit(self.Wavelength[(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.Wavelength self.hv_min) & (self.hv < self.hv_max) 128 | model = np.polyfit(self.hv[self.index], self.Alpha_hv[self.index], 1) 129 | self.Alpha_hv_fit = self.hv * model[0] + model[1] #This is the linear fit 130 | self.tauc_plot.plot(self.hv, self.Alpha_hv, pen='r') 131 | self.tauc_plot.plot(self.hv, self.Alpha_hv_fit, pen='k') 132 | self.tauc_plot.setXRange(1,2) 133 | self.tauc_plot.setYRange(0, np.max(self.Alpha_hv[self.index]) + 1) 134 | 135 | self.Eg = - model[1]/model[0] 136 | self.ui.bandgap_label.setText(str(self.Eg)) 137 | except: 138 | pass 139 | 140 | def clear_tauc(self): 141 | self.tauc_plot.clear() 142 | 143 | def export_uv_vis(self): 144 | """ Export publication ready uv vis figure """ 145 | try: 146 | filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") 147 | plt.figure(figsize=(8,6)) 148 | plt.tick_params(direction='out', length=8, width=3.5) 149 | plt.plot(self.Wavelength, self.plotted_absorbance, linewidth = 3, color = 'r') 150 | if self.scatter_corrected: 151 | plt.xlim(self.correction_region_min, self.correction_region_max) 152 | plt.ylim(0, np.max(self.plotted_absorbance[(self.Wavelength>self.correction_region_min)]) +0.5) 153 | else: 154 | plt.xlim(self.correction_region_min, self.correction_region_max) 155 | plt.ylim(0, np.max(self.plotted_absorbance[(self.Wavelength>self.correction_region_min)] +0.5)) 156 | plt.xlabel('Wavelength (nm)', fontsize = 20) 157 | plt.ylabel('Absorbance (a.u.)', fontsize = 20) 158 | plt.savefig(filename[0],bbox_inches='tight', dpi=300) 159 | plt.close() 160 | except: 161 | pass 162 | 163 | def export_tauc(self): 164 | """ Export publication ready tauc figure""" 165 | try: 166 | filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION") 167 | plt.figure(figsize=(8,6)) 168 | plt.tick_params(direction='out', length=8, width=3.5) 169 | plt.plot(self.hv, self.Alpha_hv, linewidth = 3, color = 'r') 170 | plt.plot(self.hv, self.Alpha_hv_fit, linewidth = 2, color = 'black') 171 | plt.xlim(1,2) 172 | plt.ylim(0, np.max(self.Alpha_hv[self.index]) + 1) 173 | plt.xlabel('h$\\nu$ (eV)', fontsize = 20) 174 | plt.ylabel('($\\alpha$h$\\nu$)$^2$', fontsize = 20) 175 | #plt.title(Plot_title, fontsize = 20) 176 | 177 | plt.text(1.2, 1.2, r'E$_{g}$ = %.2f eV'%self.Eg, fontsize = 15) 178 | plt.tight_layout() 179 | plt.savefig(filename[0],bbox_inches='tight', dpi=300) 180 | plt.close() 181 | except: 182 | pass 183 | 184 | """Run the Main Window""" 185 | def run(): 186 | win = MainWindow() 187 | QtGui.QApplication.instance().exec_() 188 | return win -------------------------------------------------------------------------------- /PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 742 10 | 924 11 | 12 | 13 | 14 | UV-Vis Analysis 15 | 16 | 17 | 18 | 19 | 20 | 21 | UV-Vis plot 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Scattering Correction 30 | 31 | 32 | 33 | 34 | 35 | 36 | Plot 37 | 38 | 39 | 40 | 41 | 42 | 43 | false 44 | 45 | 46 | 4th Order Polynomial 47 | 48 | 49 | true 50 | 51 | 52 | 53 | 54 | 55 | 56 | Clear 57 | 58 | 59 | 60 | 61 | 62 | 63 | Correct for scattering 64 | 65 | 66 | 67 | 68 | 69 | 70 | false 71 | 72 | 73 | Simple Mean 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 0 84 | 0 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | Export UV-Vis plot 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Tauc plot 104 | 105 | 106 | true 107 | 108 | 109 | false 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | hv min (ev) 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 180 126 | 0 127 | 128 | 129 | 130 | -9999999.000000000000000 131 | 132 | 133 | 9999999.000000000000000 134 | 135 | 136 | 0.010000000000000 137 | 138 | 139 | 1.500000000000000 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 180 148 | 0 149 | 150 | 151 | 152 | -9999999.000000000000000 153 | 154 | 155 | 9999999.000000000000000 156 | 157 | 158 | 0.010000000000000 159 | 160 | 161 | 1.800000000000000 162 | 163 | 164 | 165 | 166 | 167 | 168 | hv max (ev) 169 | 170 | 171 | 172 | 173 | 174 | 175 | Plot 176 | 177 | 178 | 179 | 180 | 181 | 182 | Clear 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 0 193 | 0 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | Bandgap (ev): 205 | 206 | 207 | 208 | 209 | 210 | 211 | 0 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 16777215 220 | 16777215 221 | 222 | 223 | 224 | Export tauc plot 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 0 246 | 0 247 | 742 248 | 21 249 | 250 | 251 | 252 | 253 | File 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | Load data 263 | 264 | 265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /PythonGUI_apps/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GLabViz 2 | Graphical User Interface (GUI) Python apps written in python using qt and [pyqtgrpah](http://www.pyqtgraph.org/) for quick analysis of scientific data. It also includes the ability to convert data to *H5* if needed. 3 | 4 | _**Python is not required to use GLabViz**_ (see **How to use?**) 5 | 6 | [**DOWNLOAD HERE**](https://github.com/SarthakJariwala/Python_GUI_apps/releases) 7 | 8 | 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. 9 | 10 | ## Includes 11 | 12 | * **Fluorescence Lifetime Analysis** 13 | * Analyze lifetime 14 | * Fit data with or without IRF 15 | * Fit with stretched, single, or double exponential functions by diff_ev or fmin_tnc 16 | * Calculate surface recombination velocity 17 | * Export graph and fit results 18 | 19 | * **Spectra Analysis** 20 | * Analyze single spectrum 21 | * Fit with or without background and white light 22 | * Fit with single Lorentzian, single Gaussian, double Gaussian, triple Gaussian models 23 | * Export graph and fit results 24 | * Analyze spectra scan 25 | * Load spectra scan data in .h5 or .pkl files 26 | * Plot raw scan data 27 | * Plot scan intensity sums 28 | * Plot fitted scan by pk_pos, fwhm, sigma, or height 29 | * Export fitted scan 30 | * .pkl to .txt, .pkl to .h5 converters 31 | 32 | * **Fluorescence Lifetime Imaging Microscopy (FLIM) Data Analysis** 33 | * Load lifetime scans in .h5 or .pkl files 34 | * Plot histogram intensity sums and analyze PSF 35 | * Export intensities array and intensities image 36 | * Plot raw histogram data and analyze lifetime 37 | * Compare lifetime in two different regions 38 | 39 | * **Photluminescence Quantum Efficiency (PLQE) Analysis** 40 | * Plot PLQE data 41 | * Calculate PLQE 42 | 43 | * **UV-Vis Data Analysis** 44 | * Plot UV-Vis data 45 | * Correct UV-Vis data for scattering 46 | * Plot Tauc data 47 | * Calculate bandgap 48 | * Export UV-Vis and Tauc plots 49 | 50 | * **General *H5* View and Plot** 51 | * Load .h5 file to view file structure 52 | * Plot datasets as a graph or an image 53 | 54 | * **H5 and PKL File Viewer** 55 | * Load .h5 or .pkl file to view file structure 56 | 57 | * **Image Analysis** 58 | * Load image on SPOT or Pixera settings, or specify pixel size 59 | * Handle RGB and greyscale images 60 | * Select magnification 61 | * Color profile horizontally or vertically 62 | 63 | ## Screenshots 64 | ### Welcome Screen 65 | ![Welcome Screen](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_interface_1.png) 66 | ### Lifetime Analysis 67 | ![Lifetime Analysis](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_Lifetime_analysis_2.png) 68 | ### Spectra Analysis 69 | ![Single Spectrum](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_Spectrum_analysis_1.png) 70 | ![Scan Data](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_Spectrum_analysis_2.png) 71 | ### FLIM Analysis 72 | ![FLIM Analysis](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_FLIM_analysis_2.png) 73 | ### UV-Vis Analysis 74 | ![UV-Vis Analysis](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_UVvis_analysis_1.PNG) 75 | ### H5 & Pkl View 76 | ![H5-pkl-viewer](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_h5_ViewPlot_analysis_1.PNG) 77 | ### H5 Quick Plot 78 | ![h5- quick plot](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_h5_ViewPlot_analysis_2.PNG) 79 | ### Image Analysis 80 | ![Image Analysis](https://github.com/SarthakJariwala/Python_GUI_apps/blob/master/Screenshots/GLabViz_Image_analysis_1.png) 81 | 82 | ## How to use? 83 | ### Standalone App - without Python or any dependencies (_only for Windows users, comming soon for Mac users_) 84 | * Under the [releases](https://github.com/SarthakJariwala/Python_GUI_apps/releases) page, download the latest release of the _**DataBrowser**_ zip file 85 | * Extract the zip file and run _**DataBrowser.exe**_ 86 | ### With Python and its dependencies 87 | ``` 88 | git clone https://github.com/SarthakJariwala/Python_GUI_apps.git 89 | ``` 90 | * Install all dependencies 91 | * Run the application by double-clicking DataBrowser.py. 92 | * OR Run it from command-line while in the PythonGUI_apps folder: 93 | ``` 94 | python DataBrowser.py 95 | ``` 96 | 97 | #### Dependencies 98 | * [ScopeFoundry](https://github.com/ScopeFoundry/ScopeFoundry) 99 | * [pyqtgraph](http://www.pyqtgraph.org/) 100 | * numpy 101 | * pyqt 102 | * qtpy 103 | * h5py 104 | * matplotlib 105 | * scipy 106 | * [lmfit](https://lmfit.github.io/lmfit-py/) 107 | * [customplotting](https://github.com/SarthakJariwala/Custom-Plotting) 108 | 109 | #### Installing dependencies from command-line 110 | ``` 111 | conda install numpy pyqt qtpy h5py pyqtgraph 112 | pip install ScopeFoundry 113 | pip install matplotlib scipy lmfit customplotting==0.1.4.dev0 114 | ``` 115 | -------------------------------------------------------------------------------- /Screenshots/GLabViz_FLIM_analysis_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_FLIM_analysis_1.PNG -------------------------------------------------------------------------------- /Screenshots/GLabViz_FLIM_analysis_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_FLIM_analysis_2.png -------------------------------------------------------------------------------- /Screenshots/GLabViz_Image_analysis_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_Image_analysis_1.png -------------------------------------------------------------------------------- /Screenshots/GLabViz_Lifetime_analysis_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_Lifetime_analysis_2.png -------------------------------------------------------------------------------- /Screenshots/GLabViz_Spectrum_analysis_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_Spectrum_analysis_1.PNG -------------------------------------------------------------------------------- /Screenshots/GLabViz_Spectrum_analysis_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_Spectrum_analysis_1.png -------------------------------------------------------------------------------- /Screenshots/GLabViz_Spectrum_analysis_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_Spectrum_analysis_2.png -------------------------------------------------------------------------------- /Screenshots/GLabViz_UVvis_analysis_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_UVvis_analysis_1.PNG -------------------------------------------------------------------------------- /Screenshots/GLabViz_h5_ViewPlot_analysis_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_h5_ViewPlot_analysis_1.PNG -------------------------------------------------------------------------------- /Screenshots/GLabViz_h5_ViewPlot_analysis_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_h5_ViewPlot_analysis_2.PNG -------------------------------------------------------------------------------- /Screenshots/GLabViz_interface_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SarthakJariwala/Python_GUI_apps/be78d92568e430c551ccf2a081521a0a81def20d/Screenshots/GLabViz_interface_1.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.16.1 2 | asteval==0.9.14 3 | attrs==19.1.0 4 | backcall==0.1.0 5 | bleach==3.3.0 6 | bokeh==1.3.2 7 | certifi==2019.6.16 8 | Click==7.0 9 | cloudpickle==1.2.1 10 | cmarkgfm==0.4.2 11 | colorama==0.4.1 12 | customplotting==0.1.4.dev0 13 | cycler==0.10.0 14 | cytoolz==0.10.0 15 | dask==2.2.0 16 | decorator==4.4.0 17 | defusedxml==0.6.0 18 | distributed==2.2.0 19 | entrypoints==0.3 20 | fsspec==0.4.0 21 | gwyfile==0.2.0 22 | h5py==2.9.0 23 | heapdict==1.0.0 24 | igor==0.3 25 | imageio==2.5.0 26 | ipykernel==5.1.1 27 | ipython==7.7.0 28 | ipython-genutils==0.2.0 29 | ipywidgets==7.5.0 30 | jedi==0.14.1 31 | Jinja2==2.10.1 32 | joblib==0.13.2 33 | jsonschema==3.0.1 34 | jupyter-client==5.3.1 35 | jupyter-core==4.5.0 36 | kiwisolver==1.1.0 37 | llvmlite==0.29.0 38 | lmfit==0.9.13 39 | locket==0.2.0 40 | MarkupSafe==1.1.1 41 | matplotlib==3.1.1 42 | matplotlib-scalebar==0.6.0 43 | mistune==0.8.4 44 | mkl-fft==1.0.12 45 | mkl-random==1.0.2 46 | mkl-service==2.0.2 47 | msgpack==0.6.1 48 | nbconvert==5.5.0 49 | nbformat==4.4.0 50 | networkx==2.3 51 | notebook==6.1.5 52 | numba==0.45.1 53 | numpy==1.16.4 54 | numpy-groupies==0.9.7 55 | olefile==0.46 56 | opencv-python==4.1.0.25 57 | packaging==19.0 58 | pandas==0.25.0 59 | pandocfilters==1.4.2 60 | parso==0.5.1 61 | partd==1.0.0 62 | pefile==2019.4.18 63 | pickleshare==0.7.5 64 | Pillow==8.1.1 65 | pkginfo==1.4.2 66 | prometheus-client==0.7.1 67 | prompt-toolkit==2.0.9 68 | psutil==5.6.6 69 | pycroscopy==0.59.8 70 | Pygments==2.4.2 71 | PyInstaller==3.5 72 | pyparsing==2.4.0 73 | PyQt5==5.9 74 | PyQt5-sip==4.19.18 75 | pyqtgraph==0.10.0 76 | pyreadline==2.1 77 | pyrsistent==0.14.11 78 | python-dateutil==2.8.0 79 | pytz==2019.1 80 | pyUSID==0.0.6.1 81 | PyWavelets==1.0.3 82 | pywin32-ctypes==0.2.0 83 | pywinpty==0.5.5 84 | PyYAML==5.1.1 85 | pyzmq==18.0.2 86 | qtconsole==4.5.1 87 | QtPy==1.8.0 88 | readme-renderer==22.0 89 | requests-toolbelt==0.8.0 90 | scikit-image==0.15.0 91 | scikit-learn==0.21.2 92 | scipy==1.3.0 93 | ScopeFoundry==1.1.1 94 | Send2Trash==1.5.0 95 | sip==4.19.8 96 | six==1.12.0 97 | sortedcontainers==2.1.0 98 | tblib==1.4.0 99 | terminado==0.8.2 100 | testpath==0.4.2 101 | toolz==0.10.0 102 | tornado==6.0.3 103 | traitlets==4.3.2 104 | twine==1.12.1 105 | uncertainties==3.1.1 106 | wcwidth==0.1.7 107 | webencodings==0.5.1 108 | widgetsnbextension==3.5.0 109 | wincertstore==0.2 110 | xlrd==1.2.0 111 | zict==1.0.0 112 | --------------------------------------------------------------------------------