diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/PythonGUI_apps/DataBrowser.py b/PythonGUI_apps/DataBrowser.py
index 5599146..2ab2b25 100644
--- a/PythonGUI_apps/DataBrowser.py
+++ b/PythonGUI_apps/DataBrowser.py
@@ -6,6 +6,7 @@
"""
# system imports
+import sys
from pathlib import Path
import pyqtgraph as pg
@@ -13,6 +14,10 @@
from Lifetime_analysis import Lifetime_plot_fit
from Spectrum_analysis import Spectra_plot_fit
+from FLIM_analysis import FLIM_plot
+from UV_Vis_analysis import uv_vis_analysis
+from PLQE_analysis import plqe_analysis
+from H5_Pkl import h5_pkl_view, h5_view_and_plot
pg.mkQApp()
pg.setConfigOption('background', 'w')
@@ -25,37 +30,49 @@
WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile)
class MainWindow(TemplateBaseClass):
-
- def __init__(self):
- TemplateBaseClass.__init__(self)
-
- # Create the main window
- self.ui = WindowTemplate()
- self.ui.setupUi(self)
- self.ui.select_comboBox.addItems(["Lifetime Analysis", "Spectrum Analysis", "UV-Vis Analysis"])
- self.ui.load_pushButton.clicked.connect(self.load_app)
-
- self.show()
-
-
- def load_app(self):
-
- analysis_software = self.ui.select_comboBox.currentText()
-
- if analysis_software == "Lifetime Analysis":
- self.lifetime_window = Lifetime_plot_fit.MainWindow()
- self.lifetime_window.show()
-
- elif analysis_software == "Spectrum Analysis":
- self.spectrum_window = Spectra_plot_fit.MainWindow()
- self.spectrum_window.show()
-
- else:
- print("not yet linked -- coming soon")
+
+ def __init__(self):
+ TemplateBaseClass.__init__(self)
+
+ # Create the main window
+ self.ui = WindowTemplate()
+ self.ui.setupUi(self)
+ self.ui.select_comboBox.addItems(["Lifetime Analysis", "Spectrum Analysis", "FLIM Analysis",
+ "UV-Vis Analysis", "PLQE Analysis", "H5 View/Plot", "H5/PKL Viewer"])
+ self.ui.load_pushButton.clicked.connect(self.load_app)
+
+ self.show()
+
+
+ def load_app(self):
+
+ analysis_software = self.ui.select_comboBox.currentText()
+
+ if analysis_software == "Lifetime Analysis":
+ self.lifetime_window = Lifetime_plot_fit.MainWindow()
+ self.lifetime_window.show()
+ elif analysis_software == "Spectrum Analysis":
+ self.spectrum_window = Spectra_plot_fit.MainWindow()
+ self.spectrum_window.show()
+ elif analysis_software == "FLIM Analysis":
+ self.flim_window = FLIM_plot.MainWindow()
+ self.flim_window.show()
+ elif analysis_software == "UV-Vis Analysis":
+ self.uv_vis_window = uv_vis_analysis.MainWindow()
+ self.uv_vis_window.show()
+ elif analysis_software == "PLQE Analysis":
+ self.plqe_window = plqe_analysis.MainWindow()
+ self.plqe_window.show()
+ elif analysis_software == "H5 View/Plot":
+ app = h5_view_and_plot.H5ViewPlot(sys.argv)
+ #sys.exit(app.exec_())
+ elif analysis_software == "H5/PKL Viewer":
+ app = h5_pkl_view.H5PklView(sys.argv)
+ #sys.exit(app.exec_())
def run():
- win = MainWindow()
- QtGui.QApplication.instance().exec_()
- return
+ win = MainWindow()
+ QtGui.QApplication.instance().exec_()
+ return
run()
\ No newline at end of file
diff --git a/PythonGUI_apps/DataBrowser.spec b/PythonGUI_apps/DataBrowser.spec
new file mode 100644
index 0000000..d04cc9d
--- /dev/null
+++ b/PythonGUI_apps/DataBrowser.spec
@@ -0,0 +1,36 @@
+# -*- mode: python -*-
+
+block_cipher = None
+
+
+a = Analysis(['DataBrowser.py'],
+ pathex=['C:\\Users\\lindat18\\Dropbox\\Ginger_Lab\\Data_Analysis\\PythonGUI_apps'],
+ binaries=[],
+ datas=[],
+ hiddenimports=[],
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=[],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher,
+ noarchive=False)
+pyz = PYZ(a.pure, a.zipped_data,
+ cipher=block_cipher)
+exe = EXE(pyz,
+ a.scripts,
+ [],
+ exclude_binaries=True,
+ name='DataBrowser',
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ console=True )
+coll = COLLECT(exe,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ strip=False,
+ upx=True,
+ name='DataBrowser')
diff --git a/PythonGUI_apps/FLIM_analysis/FLIM_plot.py b/PythonGUI_apps/FLIM_analysis/FLIM_plot.py
new file mode 100644
index 0000000..a3d3ea3
--- /dev/null
+++ b/PythonGUI_apps/FLIM_analysis/FLIM_plot.py
@@ -0,0 +1,251 @@
+import sys
+import h5py
+from pathlib import Path
+import os.path
+import pyqtgraph as pg
+from pyqtgraph.Qt import QtCore, QtGui, QtWidgets#, QColorDialog
+import numpy as np
+import matplotlib.pyplot as plt
+import pickle
+import time
+from lmfit.models import GaussianModel
+import customplotting.mscope as cpm
+# local modules
+
+pg.mkQApp()
+pg.setConfigOption('background', 'w')
+pg.setConfigOption('imageAxisOrder', 'row-major')
+
+base_path = Path(__file__).parent
+file_path = (base_path / "flim_plot_gui.ui").resolve()
+
+uiFile = file_path
+
+WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile)
+
+class MainWindow(TemplateBaseClass):
+
+ def __init__(self):
+ super(TemplateBaseClass, self).__init__()
+
+ # Create the main window
+ self.ui = WindowTemplate()
+ self.ui.setupUi(self)
+
+ #set up ui signals
+ self.ui.load_scan_pushButton.clicked.connect(self.open_pkl_file)
+ self.ui.plot_intensity_sums_pushButton.clicked.connect(self.plot_intensity_sums)
+ self.ui.plot_raw_hist_data_pushButton.clicked.connect(self.plot_raw_scan)
+ self.ui.save_intensities_image_pushButton.clicked.connect(self.save_intensities_image)
+ self.ui.save_intensities_array_pushButton.clicked.connect(self.save_intensities_array)
+ self.ui.compare_checkBox.stateChanged.connect(self.switch_compare)
+ self.ui.intensity_sums_viewBox.roi.sigRegionChanged.connect(self.line_profile_update_plot)
+ self.ui.import_pkl_pushButton.clicked.connect(self.import_pkl_to_convert)
+ self.ui.pkl_to_h5_pushButton.clicked.connect(self.pkl_to_h5)
+
+ self.show()
+
+ def open_pkl_file(self):
+ """ Open FLIM scan file """
+ try:
+ self.filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.pkl_file = pickle.load(open(self.filename[0], 'rb'))
+ except Exception as err:
+ print(format(err))
+
+ def import_pkl_to_convert(self):
+ """ Open pkl file to convert to h5 """
+ try:
+ self.pkl_to_convert = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.ui.result_textBrowser.append("Done Loading - .pkl to convert")
+ except:
+ pass
+
+ def plot_intensity_sums(self):
+ try:
+ data = self.pkl_file
+ self.numb_pixels_X = int((data['Scan Parameters']['X scan size (um)'])/(data['Scan Parameters']['X step size (um)']))
+ self.numb_pixels_Y = int((data['Scan Parameters']['Y scan size (um)'])/(data['Scan Parameters']['Y step size (um)']))
+ self.x_step_size = float(data['Scan Parameters']['X step size (um)'])
+ self.x_scan_size = float(data['Scan Parameters']['X scan size (um)'])
+ self.y_step_size = float(data['Scan Parameters']['Y step size (um)'])
+ self.y_scan_size = float(data['Scan Parameters']['Y scan size (um)'])
+
+ hist_data = data["Histogram data"]
+ hist_data = np.reshape(hist_data, newshape=(hist_data.shape[0], self.numb_pixels_X*self.numb_pixels_Y))
+ self.intensity_sums = np.sum(hist_data, axis=0) #sum intensities for each pixel
+ self.intensity_sums = np.reshape(self.intensity_sums, newshape=(self.numb_pixels_X, self.numb_pixels_Y))
+ self.ui.intensity_sums_viewBox.view.invertY(False) # stop y axis invert
+ self.ui.intensity_sums_viewBox.setImage(self.intensity_sums, scale=
+ (data['Scan Parameters']['X step size (um)'],
+ data['Scan Parameters']['Y step size (um)']))
+ self.ui.intensity_sums_viewBox.roi.setSize([self.x_scan_size, self.y_step_size]) #line roi
+ scale = pg.ScaleBar(size=1,suffix='um')
+ scale.setParentItem(self.ui.intensity_sums_viewBox.view)
+ scale.anchor((1, 1), (1, 1), offset=(-30, -30))
+ except Exception as err:
+ print(format(err))
+
+ def line_profile_update_plot(self):
+ """ Handle line profile for intensity sum viewbox """
+ if hasattr(self, "intensity_sums"):
+ roiPlot = self.ui.intensity_sums_viewBox.getRoiPlot()
+ roiPlot.clear()
+ roi = self.ui.intensity_sums_viewBox.roi
+
+ image = self.ui.intensity_sums_viewBox.getProcessedImage()
+
+ # Extract image data from ROI
+ axes = (self.ui.intensity_sums_viewBox.axes['x'], self.ui.intensity_sums_viewBox.axes['y'])
+ data, coords = roi.getArrayRegion(image.view(np.ndarray), self.ui.intensity_sums_viewBox.imageItem, axes, returnMappedCoords=True)
+
+ #calculate sums along columns in region
+ sums_to_plot = np.sum(data, axis=0)
+
+ #get scan x-coordinates in region
+ x_values = coords[1][0]
+
+ try:
+ roiPlot.plot(x_values, sums_to_plot)
+ except:
+ pass
+
+ def plot_raw_scan(self):
+ try:
+ data = self.pkl_file
+ self.numb_pixels_X = int((data['Scan Parameters']['X scan size (um)'])/(data['Scan Parameters']['X step size (um)']))
+ self.numb_pixels_Y = int((data['Scan Parameters']['Y scan size (um)'])/(data['Scan Parameters']['Y step size (um)']))
+ self.x_step_size = float(data['Scan Parameters']['X step size (um)'])
+ self.x_scan_size = float(data['Scan Parameters']['X scan size (um)'])
+ self.y_step_size = float(data['Scan Parameters']['Y step size (um)'])
+ self.y_scan_size = float(data['Scan Parameters']['Y scan size (um)'])
+ # TODO test line scan plots
+ hist_data = data['Histogram data']
+
+ self.hist_image = np.reshape(hist_data, newshape=(hist_data.shape[0],self.numb_pixels_X,self.numb_pixels_Y))
+ time_data = data['Time data']
+ self.times = time_data[:, 0, 0]*1e-3
+ self.ui.raw_hist_data_viewBox.view.invertY(False) # stops y-axis invert
+ self.ui.raw_hist_data_viewBox.setImage(self.hist_image, scale=
+ (data['Scan Parameters']['X step size (um)'],
+ data['Scan Parameters']['Y step size (um)']), xvals=self.times)
+ self.ui.raw_hist_data_viewBox.roi.setSize([self.x_scan_size, self.y_scan_size])
+ # if self.ui.compare_checkBox.isChecked():
+ # self.ui.imv2.setImage(self.hist_image, scale= (data['Scan Parameters']['X step size (um)'],
+ # data['Scan Parameters']['Y step size (um)']), xvals=self.times)
+ self.switch_compare()
+ self.ui.raw_hist_data_viewBox.ui.roiBtn.clicked.connect(self.switch_compare)
+ scale = pg.ScaleBar(size=1,suffix='um')
+ scale.setParentItem(self.ui.raw_hist_data_viewBox.view)
+ scale.anchor((1, 1), (1, 1), offset=(-30, -30))
+
+ except Exception as err:
+ print(format(err))
+
+ def switch_compare(self):
+ """
+ Handles compare checkbox. If checked, show second ROI on raw histogram data that user can use for comparison to first ROI.
+ """
+ if self.ui.compare_checkBox.isChecked() and hasattr(self, "hist_image"):
+ if not hasattr(self, "roi2"): #create roi if doesn't exist yet
+ self.roi2 = pg.ROI(pos=[0,0], size=[int(self.x_scan_size/2), int(self.y_scan_size/2)], movable=True, pen='r')
+ self.roi2.addScaleHandle([1, 1], [0, 0])
+ self.roi2.addRotateHandle([0, 0], [1, 1])
+ self.roi2.sigRegionChanged.connect(self.update_roi2_plot)
+ self.ui.raw_hist_data_viewBox.addItem(self.roi2)
+ self.update_roi2_plot()
+ self.roi2.hide()
+ self.roi2_plot.hide()
+ if self.ui.raw_hist_data_viewBox.ui.roiBtn.isChecked():
+ self.roi2.show()
+ self.roi2_plot.show()
+ else:
+ self.roi2.hide()
+ self.roi2_plot.hide()
+ else: #if not checked, hide roi
+ if hasattr(self, "roi2"):
+ self.roi2.hide()
+ self.roi2_plot.hide()
+
+ def update_roi2_plot(self):
+ """ Update plot corresponding to second roi """
+ #Adapted from pyqtgraph imageview sourcecode
+
+ image = self.ui.raw_hist_data_viewBox.getProcessedImage()
+
+ # Extract image data from ROI
+ axes = (self.ui.raw_hist_data_viewBox.axes['x'], self.ui.raw_hist_data_viewBox.axes['y'])
+ data, coords = self.roi2.getArrayRegion(image.view(np.ndarray), self.ui.raw_hist_data_viewBox.imageItem, axes, returnMappedCoords=True)
+ if data is None:
+ return
+
+ # Average data within entire ROI for each frame
+ data = data.mean(axis=max(axes)).mean(axis=min(axes))
+ xvals = self.ui.raw_hist_data_viewBox.tVals
+ if hasattr(self, "roi2_plot"):
+ self.roi2_plot.clear()
+ self.roi2_plot = self.ui.raw_hist_data_viewBox.getRoiPlot().plot(xvals, data, pen='r')
+
+ def save_intensities_image(self):
+ try:
+ filename_ext = os.path.basename(self.filename[0])
+ filename = os.path.splitext(filename_ext)[0] #get filename without extension
+ save_to = os.getcwd() + "\\" + filename + "_intensity_sums.png"
+ cpm.plot_confocal(self.intensity_sums, stepsize=np.abs(self.pkl_file['Scan Parameters']['X step size (um)']))
+ cpm.plt.savefig(save_to, bbox_inches='tight', dpi=300)
+ except:
+ pass
+
+ def save_intensities_array(self):
+ try:
+ filename_ext = os.path.basename(self.filename[0])
+ filename = os.path.splitext(filename_ext)[0] #get filename without extension
+ save_to = os.getcwd() + "\\" + filename + "_intensity_sums.txt"
+ np.savetxt(save_to, self.intensity_sums.T, fmt='%f') #save transposed intensity sums, as original array handles x in cols and y in rows
+ except:
+ pass
+
+ def pkl_to_h5(self):
+ #Convert scan .pkl file into h5
+ try:
+ folder = os.path.dirname(self.pkl_to_convert[0])
+ filename_ext = os.path.basename(self.pkl_to_convert[0])
+ filename = os.path.splitext(filename_ext)[0] #get filename without extension
+ pkl_file = pickle.load(open(self.pkl_to_convert[0], 'rb'))
+
+ h5_filename = folder + "/" + filename + ".h5"
+ h5_file = h5py.File(h5_filename, "w")
+ self.traverse_dict_into_h5(pkl_file, h5_file)
+ except Exception as err:
+ print(format(err))
+
+ def traverse_dict_into_h5(self, dictionary, h5_output):
+ #Create an h5 file using .pkl with scan data and params
+ for key in dictionary:
+ if type(dictionary[key]) == dict: #if subdictionary, create a group
+ group = h5_output.create_group(key)
+ previous_dict = dictionary[key]
+ self.traverse_dict_into_h5(dictionary[key], group) #traverse subdictionary
+ else:
+ if key == "Histogram data" or key == "Time data":
+ h5_output.create_dataset(key, data=dictionary[key])
+ else:
+ h5_output.attrs[key] = dictionary[key] #if not dataset, create attribute
+
+ def close_application(self):
+ choice = QtGui.QMessageBox.question(self, 'EXIT!',
+ "Do you want to exit the app?",
+ QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
+ if choice == QtGui.QMessageBox.Yes:
+ sys.exit()
+ else:
+ pass
+
+"""Run the Main Window"""
+def run():
+ win = MainWindow()
+ QtGui.QApplication.instance().exec_()
+ return win
+
+#Uncomment below if you want to run this as standalone
+#run()
\ No newline at end of file
diff --git a/PythonGUI_apps/FLIM_analysis/flim_plot_gui.ui b/PythonGUI_apps/FLIM_analysis/flim_plot_gui.ui
new file mode 100644
index 0000000..5daa01e
--- /dev/null
+++ b/PythonGUI_apps/FLIM_analysis/flim_plot_gui.ui
@@ -0,0 +1,171 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 738
+ 876
+
+
+
+ Form
+
+
+ -
+
+
+ 0
+
+
+
+ Analysis
+
+
+
-
+
+
-
+
+
+ Load Scan
+
+
+
+ -
+
+
-
+
+
+ Plot
+
+
+
+ -
+
+
+ Save intensities array
+
+
+
+ -
+
+
+ Save intensities image
+
+
+
+
+
+ -
+
+
+
+ 500
+ 300
+
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Histogram Intensity Sums
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Raw Histogram Data
+
+
+
+ -
+
+
+
+ 500
+ 300
+
+
+
+
+ -
+
+
-
+
+
+ Plot
+
+
+
+ -
+
+
+ Compare ROIs
+
+
+
+
+
+
+
+
+
+
+
+ .pkl to .h5
+
+
+
+
+ 20
+ 20
+ 171
+ 34
+
+
+
+ Import .pkl file
+
+
+
+
+
+ 20
+ 80
+ 171
+ 34
+
+
+
+ .pkl to .h5
+
+
+
+
+
+
+
+
+
+ ImageView
+ QGraphicsView
+
+
+
+
+
+
diff --git a/PythonGUI_apps/H5_Pkl/h5_pkl_view.py b/PythonGUI_apps/H5_Pkl/h5_pkl_view.py
new file mode 100644
index 0000000..7be99a5
--- /dev/null
+++ b/PythonGUI_apps/H5_Pkl/h5_pkl_view.py
@@ -0,0 +1,124 @@
+from __future__ import division, print_function, absolute_import
+from ScopeFoundry import BaseApp
+from ScopeFoundry.helper_funcs import load_qt_ui_file, sibling_path
+from collections import OrderedDict
+import os
+from qtpy import QtCore, QtWidgets, QtGui
+import pyqtgraph as pg
+import pyqtgraph.dockarea as dockarea
+import numpy as np
+from ScopeFoundry.logged_quantity import LQCollection
+from scipy.stats import spearmanr
+import argparse
+from .h5_tree import H5TreeSearchView
+from .pkl_tree import PklTreeSearchView
+
+pg.setConfigOption('imageAxisOrder', 'row-major')
+
+class H5PklView(BaseApp):
+
+ name = "h5_pkl_view"
+
+ def __init__(self, argv):
+ BaseApp.__init__(self, argv)
+ self.setup()
+ parser = argparse.ArgumentParser()
+ for lq in self.settings.as_list():
+ parser.add_argument("--" + lq.name)
+ args = parser.parse_args()
+ for lq in self.settings.as_list():
+ if lq.name in args:
+ val = getattr(args,lq.name)
+ if val is not None:
+ lq.update_value(val)
+
+ def setup(self):
+ self.ui_filename = sibling_path(__file__, "h5_pkl_view_gui.ui")
+ self.ui = load_qt_ui_file(self.ui_filename)
+ self.ui.show()
+ self.ui.raise_()
+
+ self.views = OrderedDict()
+
+ self.settings.New('data_filename', dtype='file')
+ self.settings.New('auto_select_view',dtype=bool, initial=True)
+ self.settings.New('view_name', dtype=str, initial='0', choices=('0',))
+
+ self.settings.data_filename.add_listener(self.on_change_data_filename)
+
+ # UI Connections/
+ self.settings.data_filename.connect_to_browse_widgets(self.ui.data_filename_lineEdit,
+ self.ui.data_filename_browse_pushButton)
+
+ # set views
+ self.h5treeview = H5TreeSearchView(self)
+ self.load_view(self.h5treeview)
+ self.pkltreeview = PklTreeSearchView(self)
+ self.load_view(self.pkltreeview)
+
+ self.settings.view_name.add_listener(self.on_change_view_name)
+
+ self.current_view = None
+
+ self.ui.show()
+
+ def load_view(self, new_view):
+ # add to views dict
+ self.views[new_view.name] = new_view
+
+ self.ui.dataview_page.layout().addWidget(new_view.ui)
+ new_view.ui.hide()
+
+ # update choices for view_name
+ self.settings.view_name.change_choice_list(list(self.views.keys()))
+ return new_view
+
+ def on_change_data_filename(self):
+ #Handle file change
+ try:
+ fname = self.settings.data_filename.val
+ if not self.settings['auto_select_view']:
+ self.current_view.on_change_data_filename(fname)
+ else:
+ view_name = self.auto_select_view(fname)
+ if self.current_view is None or view_name != self.current_view.name:
+ # update view (automatically calls on_change_data_filename)
+ self.settings['view_name'] = view_name
+ else:
+ # force update
+ if os.path.isfile(fname):
+ self.current_view.on_change_data_filename(fname)
+ except:
+ pass
+
+ def on_change_view_name(self):
+ #Handle view change - happens when filetype changes
+ self.ui.dataview_placeholder.hide()
+ previous_view = self.current_view
+
+ self.current_view = self.views[self.settings['view_name']]
+ # hide current view
+ # (handle the initial case where previous_view is None )
+ if previous_view:
+ previous_view.ui.hide()
+
+ # show new view
+ self.current_view.ui.show()
+
+ # set datafile for new (current) view
+ fname = self.settings['data_filename']
+ if os.path.isfile(fname):
+ self.current_view.on_change_data_filename(self.settings['data_filename'])
+
+ def auto_select_view(self, fname):
+ #return the name of the last supported view for the given fname
+ for view_name, view in list(self.views.items())[::-1]:
+ if view.is_file_supported(fname):
+ return view_name
+ # return default file_info view if no others work
+ return "h5_tree_search"
+
+# if __name__ == '__main__':
+# import sys
+# app = H5PklView(sys.argv)
+# sys.exit(app.exec_())
diff --git a/PythonGUI_apps/H5_Pkl/h5_pkl_view_gui.ui b/PythonGUI_apps/H5_Pkl/h5_pkl_view_gui.ui
new file mode 100644
index 0000000..08ec7ff
--- /dev/null
+++ b/PythonGUI_apps/H5_Pkl/h5_pkl_view_gui.ui
@@ -0,0 +1,89 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 856
+ 925
+
+
+
+ ScopeFoundry Data Browser
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ File
+
+
+
-
+
+
+ -
+
+
+ File:
+
+
+
+ -
+
+
+ Browse...
+
+
+
+ -
+
+
+ true
+
+
+ 0
+
+
+
+ Data View
+
+
+
-
+
+
+ Once a file is loaded, data will show up here.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PythonGUI_apps/H5_Pkl/h5_tree.py b/PythonGUI_apps/H5_Pkl/h5_tree.py
new file mode 100644
index 0000000..c087f78
--- /dev/null
+++ b/PythonGUI_apps/H5_Pkl/h5_tree.py
@@ -0,0 +1,99 @@
+from ScopeFoundry.data_browser import DataBrowserView
+from qtpy import QtWidgets
+import h5py
+
+class H5TreeSearchView(DataBrowserView):
+
+ name = 'h5_tree_search'
+
+ def is_file_supported(self, fname):
+ return ('.h5' in fname)
+
+ def setup(self):
+ #self.settings.New('search_text', dtype=str, initial="")
+
+ self.ui = QtWidgets.QWidget()
+ self.ui.setLayout(QtWidgets.QVBoxLayout())
+ self.search_lineEdit = QtWidgets.QLineEdit()
+ self.search_lineEdit.setPlaceholderText("Search")
+ self.tree_textEdit = QtWidgets.QTextEdit("")
+
+ self.ui.layout().addWidget(self.search_lineEdit)
+ self.ui.layout().addWidget(self.tree_textEdit)
+
+ #self.settings.search_text.connect_to_widget(self.search_lineEdit)
+ #self.settings.search_text.add_listener(self.on_new_search_text)
+ self.search_text = ""
+
+ self.search_lineEdit.textChanged.connect(self.on_new_search_text)
+
+ def on_change_data_filename(self, fname=None):
+ """ Handle file change """
+ self.tree_textEdit.setText("loading {}".format(fname))
+ try:
+ #if using h5_plot_and_view
+ if hasattr(self.databrowser.ui, "dataset_listWidget"):
+ self.dataset_dict = {}
+ self.databrowser.ui.dataset_listWidget.clear()
+
+ self.fname = fname
+ self.f = h5py.File(fname, 'r')
+ self.on_new_search_text()
+ self.databrowser.ui.statusbar.showMessage("")
+
+ except Exception as err:
+ msg = "Failed to load %s:\n%s" %(fname, err)
+ self.databrowser.ui.statusbar.showMessage(msg)
+ self.tree_textEdit.setText(msg)
+ raise(err)
+
+ def on_new_search_text(self, x=None):
+ if x is not None:
+ self.search_text = x.lower()
+ old_scroll_pos = self.tree_textEdit.verticalScrollBar().value()
+ self.tree_str = ""
+ self.f.visititems(self._visitfunc)
+
+ self.tree_text_html = \
+ """{}
+
+ {}
+
+ """.format(self.fname, self.tree_str)
+
+ self.tree_textEdit.setText(self.tree_text_html)
+ self.tree_textEdit.verticalScrollBar().setValue(old_scroll_pos)
+
+ def _visitfunc(self, name, node):
+ level = len(name.split('/'))
+ indent = ' '*4*(level-1)
+
+ #indent = ''.format(level*4)
+ localname = name.split('/')[-1]
+
+ #search_text = self.settings['search_text'].lower()
+ search_text = self.search_text
+ if search_text and (search_text in localname.lower()): #highlight terms that contain search text
+ localname = """{}""".format(localname)
+
+ if isinstance(node, h5py.Group):
+ self.tree_str += indent +"|> {}/
".format(localname)
+ elif isinstance(node, h5py.Dataset):
+ self.tree_str += indent +"|D {}: {} {}
".format(localname, node.shape, node.dtype)
+
+ #if using h5_plot_and_view
+ if hasattr(self.databrowser.ui, "dataset_listWidget"):
+ item_name = "{}: {} {}".format(localname, node.shape, node.dtype)
+ self.databrowser.ui.dataset_listWidget.addItem(item_name)
+ if not hasattr(self, "dataset_dict"):
+ self.dataset_dict = {}
+ self.dataset_dict[item_name] = node
+
+
+ for key, val in node.attrs.items(): #highlight terms that contain search text
+ if search_text:
+ if search_text in str(key).lower():
+ key = """{}""".format(key)
+ if search_text in str(val).lower():
+ val = """{}""".format(val)
+ self.tree_str += indent+" |- {} = {}
".format(key, val)
\ No newline at end of file
diff --git a/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py b/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py
new file mode 100644
index 0000000..4b162f6
--- /dev/null
+++ b/PythonGUI_apps/H5_Pkl/h5_view_and_plot.py
@@ -0,0 +1,136 @@
+from __future__ import division, print_function, absolute_import
+from ScopeFoundry import BaseApp
+from ScopeFoundry.helper_funcs import load_qt_ui_file, sibling_path
+from collections import OrderedDict
+import os
+from qtpy import QtCore, QtWidgets, QtGui
+import pyqtgraph as pg
+import pyqtgraph.dockarea as dockarea
+import numpy as np
+from ScopeFoundry.logged_quantity import LQCollection
+from scipy.stats import spearmanr
+import argparse
+from .h5_tree import H5TreeSearchView
+from .pkl_tree import PklTreeSearchView
+
+pg.setConfigOption('imageAxisOrder', 'row-major')
+
+class H5ViewPlot(BaseApp):
+
+ name = "h5_view_plot"
+
+ def __init__(self, argv):
+ BaseApp.__init__(self, argv)
+ self.setup()
+ parser = argparse.ArgumentParser()
+ for lq in self.settings.as_list():
+ parser.add_argument("--" + lq.name)
+ args = parser.parse_args()
+ for lq in self.settings.as_list():
+ if lq.name in args:
+ val = getattr(args,lq.name)
+ if val is not None:
+ lq.update_value(val)
+
+ def setup(self):
+ self.ui_filename = sibling_path(__file__, "h5_view_and_plot_gui.ui")
+ self.ui = load_qt_ui_file(self.ui_filename)
+ self.ui.show()
+ self.ui.raise_()
+
+ self.settings.New('data_filename', dtype='file')
+
+ self.settings.data_filename.add_listener(self.on_change_data_filename)
+
+ self.settings.New('view_name', dtype=str, initial='0', choices=('0',))
+
+ # UI Connections
+ self.settings.data_filename.connect_to_browse_widgets(self.ui.data_filename_lineEdit,
+ self.ui.data_filename_browse_pushButton)
+ self.ui.plot_pushButton.clicked.connect(self.plot_dataset)
+ self.ui.dataset_listWidget.currentItemChanged.connect(self.on_data_selection)
+ self.ui.plot_radioButton.toggled.connect(self.update_data_widget)
+ self.ui.image_radioButton.toggled.connect(self.update_data_widget)
+
+ #set up image item for 2d array
+ self.data_img_layout = pg.GraphicsLayoutWidget()
+ self.ui.imageItem_page.layout().addWidget(self.data_img_layout)
+ self.data_img_layout = self.data_img_layout.addViewBox()
+ self.data_img = pg.ImageItem()
+ self.data_img_layout.addItem(self.data_img)
+
+ #set up image view for 3d array
+ self.ui.data_imageView.getView().invertY(False)
+
+ self.h5treeview = H5TreeSearchView(self)
+ self.ui.dataview_page.layout().addWidget(self.h5treeview.ui)
+ self.h5treeview.ui.hide()
+ self.ui.show()
+
+ def on_change_data_filename(self):
+ """ Handle file change """
+ try:
+ fname = self.settings.data_filename.val
+ if os.path.isfile(fname):
+ self.h5treeview.on_change_data_filename(fname)
+ self.ui.dataview_placeholder.hide()
+ self.h5treeview.ui.show()
+ except:
+ pass
+
+ def plot_dataset(self):
+ """ Plot data set depending on dataset shape and plot type option. """
+ self.plot = self.ui.data_plotWidget.getPlotItem()
+ self.plot.clear()
+
+ data = self.dataset[()]
+ if self.dataset_shape == 1:
+ x_start = self.ui.plotWidget_x_start_spinBox.value()
+ x_end = self.ui.plotWidget_x_end_spinBox.value()
+ num_points = self.dataset.shape[0]
+ x_values = np.linspace(x_start, x_end, num_points)
+ self.plot.plot(x_values, data)
+ elif self.dataset_shape == 2 and self.ui.plot_radioButton.isChecked():
+ self.plot.plot(data[0], data[1]) # TODO check and test this
+ elif self.dataset_shape == 2 and self.ui.image_radioButton.isChecked():
+ self.data_img.setImage(data)
+ elif self.dataset_shape == 3:
+ x_start = self.ui.imageView_x_start_spinBox.value()
+ x_end = self.ui.imageView_x_end_spinBox.value()
+ num_points = self.dataset.shape[0]
+ x_values = np.linspace(x_start, x_end, num_points) #scale x axis
+ self.ui.data_imageView.setImage(data, xvals=x_values)
+
+ def on_data_selection(self):
+ """ Handle dataset selection """
+ try:
+ dataset_name = self.ui.dataset_listWidget.currentItem().text()
+ self.dataset = self.h5treeview.dataset_dict[dataset_name]
+ self.dataset_shape = len(self.dataset[()].shape)
+ self.update_data_widget()
+ if self.dataset_shape == 1:
+ self.ui.plot_type_groupBox.setEnabled(False)
+ self.ui.plot_radioButton.setChecked(True)
+ elif self.dataset_shape == 2:
+ self.ui.plot_type_groupBox.setEnabled(True)
+ elif self.dataset_shape == 3:
+ self.ui.plot_type_groupBox.setEnabled(False)
+ self.ui.image_radioButton.setChecked(True)
+ except:
+ pass
+
+ def update_data_widget(self):
+ """ Decide which widget to display based on dataset shape and plot type option. """
+ if self.dataset_shape == 1:
+ self.ui.data_stackedWidget.setCurrentIndex(0)
+ elif self.dataset_shape == 2 and self.ui.plot_radioButton.isChecked():
+ self.ui.data_stackedWidget.setCurrentIndex(0)
+ elif self.dataset_shape == 2 and self.ui.image_radioButton.isChecked():
+ self.ui.data_stackedWidget.setCurrentIndex(1)
+ elif self.dataset_shape == 3:
+ self.ui.data_stackedWidget.setCurrentIndex(2)
+
+# if __name__ == '__main__':
+# import sys
+# app = H5ViewPlot(sys.argv)
+# sys.exit(app.exec_())
\ No newline at end of file
diff --git a/PythonGUI_apps/H5_Pkl/h5_view_and_plot_gui.ui b/PythonGUI_apps/H5_Pkl/h5_view_and_plot_gui.ui
new file mode 100644
index 0000000..de399cb
--- /dev/null
+++ b/PythonGUI_apps/H5_Pkl/h5_view_and_plot_gui.ui
@@ -0,0 +1,280 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 856
+ 925
+
+
+
+ ScopeFoundry Data Browser
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ File
+
+
+
-
+
+
+ -
+
+
+ File:
+
+
+
+ -
+
+
+ Browse...
+
+
+
+ -
+
+
+ true
+
+
+ 0
+
+
+
+ Data View
+
+
+
-
+
+
+ Once a file is loaded, data will show up here.
+
+
+
+
+
+
+
+ true
+
+
+ Plot
+
+
+ -
+
+
+ Datasets
+
+
+
-
+
+
+
+
+
+ -
+
+
+ Plot data
+
+
+
-
+
+
+ 0
+
+
+
+
-
+
+
+ -
+
+
-
+
+
+
+ 203
+ 16777215
+
+
+
+ 9999999.000000000000000
+
+
+ 0.000000000000000
+
+
+
+ -
+
+
+ X start
+
+
+
+ -
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ X end
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+
+ 203
+ 16777215
+
+
+
+ 9999999.000000000000000
+
+
+ 0.000000000000000
+
+
+
+ -
+
+
+ X start
+
+
+
+ -
+
+
+ X end
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Plot
+
+
+
+ -
+
+
+ true
+
+
+ Type
+
+
+
-
+
+
+ true
+
+
+ Plot
+
+
+ true
+
+
+
+ -
+
+
+ true
+
+
+ Image
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+ ImageView
+ QGraphicsView
+
+
+
+
+
+
diff --git a/PythonGUI_apps/H5_Pkl/pkl_tree.py b/PythonGUI_apps/H5_Pkl/pkl_tree.py
new file mode 100644
index 0000000..3a24c76
--- /dev/null
+++ b/PythonGUI_apps/H5_Pkl/pkl_tree.py
@@ -0,0 +1,100 @@
+from ScopeFoundry.data_browser import DataBrowserView
+from qtpy import QtWidgets
+import h5py
+import pickle
+import numpy as np
+
+class PklTreeSearchView(DataBrowserView):
+
+ name = 'pkl_tree_search'
+
+ def is_file_supported(self, fname):
+ return ('.pkl' in fname)
+
+ def setup(self):
+ #self.settings.New('search_text', dtype=str, initial="")
+
+ self.ui = QtWidgets.QWidget()
+ self.ui.setLayout(QtWidgets.QVBoxLayout())
+ self.search_lineEdit = QtWidgets.QLineEdit()
+ self.search_lineEdit.setPlaceholderText("Search")
+ self.tree_textEdit = QtWidgets.QTextEdit("")
+
+ self.ui.layout().addWidget(self.search_lineEdit)
+ self.ui.layout().addWidget(self.tree_textEdit)
+
+ #self.settings.search_text.connect_to_widget(self.search_lineEdit)
+ #self.settings.search_text.add_listener(self.on_new_search_text)
+ self.search_text = ""
+
+ self.search_lineEdit.textChanged.connect(self.on_new_search_text)
+
+ def on_change_data_filename(self, fname=None):
+ """ Handle file change """
+ self.tree_textEdit.setText("loading {}".format(fname))
+ try:
+ self.fname = fname
+ #self.f = h5py.File(fname, 'r')
+ self.dictionary = pickle.load(open(self.fname, 'rb'))
+ self.on_new_search_text()
+ self.databrowser.ui.statusbar.showMessage("")
+
+ except Exception as err:
+ msg = "Failed to load %s:\n%s" %(fname, err)
+ self.databrowser.ui.statusbar.showMessage(msg)
+ self.tree_textEdit.setText(msg)
+ raise(err)
+
+ def on_new_search_text(self, x=None):
+ if x is not None:
+ self.search_text = x.lower()
+ old_scroll_pos = self.tree_textEdit.verticalScrollBar().value()
+ self.tree_str = ""
+ #self.f.visititems(self._visitfunc)
+ self.traverse_dict(self.dictionary, self.dictionary, 0)
+
+
+ self.tree_text_html = \
+ """{}
+
+ {}
+
+ """.format(self.fname, self.tree_str)
+
+ self.tree_textEdit.setText(self.tree_text_html)
+ self.tree_textEdit.verticalScrollBar().setValue(old_scroll_pos)
+
+ def traverse_dict(self, dictionary, previous_dict, level):
+ """
+ Visit all values in the dictionary and its subdictionaries.
+
+ dictionary -- dictionary to traverse
+ previous_dict -- dictionary one level up
+ level -- track how far to indent
+ """
+ for key in dictionary:
+ if key not in previous_dict:
+ level -=1
+ indent = " "*4*(level)
+
+ if type(dictionary[key]) == dict:
+ print_string = key
+ if self.search_text and self.search_text in print_string:
+ self.tree_str += indent + """{}""".format(print_string)
+ else:
+ self.tree_str += indent + "|> {}/
".format(print_string)
+ level += 1
+ previous_dict = dictionary[key]
+ self.traverse_dict(dictionary[key], previous_dict, level)
+ else:
+ value = dictionary[key]
+ if type(value) == np.ndarray or type(value)==np.memmap:
+ value = str(value.shape) + " " + str(value.dtype)
+ # if type(value) == list and len(value) > 5: ##account for data stored in lists
+ # value = str(np.asarray(value).shape) + " " + str(type(value[0]))
+
+ print_string = key + " = " + str(value)
+ if self.search_text and self.search_text in print_string:
+ self.tree_str += indent + """{}""".format(print_string)
+ else:
+ self.tree_str += indent + "|- {}
".format(print_string)
\ No newline at end of file
diff --git a/PythonGUI_apps/Lifetime_analysis/Fit_functions_with_irf.py b/PythonGUI_apps/Lifetime_analysis/Fit_functions_with_irf.py
new file mode 100644
index 0000000..5b454af
--- /dev/null
+++ b/PythonGUI_apps/Lifetime_analysis/Fit_functions_with_irf.py
@@ -0,0 +1,329 @@
+import numpy as np
+import matplotlib.pyplot as plt
+from scipy.optimize import fmin_tnc, differential_evolution
+from scipy.special import gamma
+from scipy.signal import fftconvolve
+from scipy.integrate import odeint
+
+
+"""Fit TCSPC data to a model by reconvolution with the IRF
+Convolution is done in time domain with np.convolve()
+Convolution can be done in the frequency domain of np.convolve() is replaced by scipy.signal.fftconvolve()
+
+For a good tutorial on numerical convolution, see section 13.1 of Numerical Recipes:
+
+Press, W. H.; Teukolsky, S. A.; Vetterling, W. T.; Flannery, B. P.,
+Numerical Recipes 3rd Edition: The Art of Scientific Computing. 3 ed.;
+Cambridge University Press: New York, 2007
+
+***Note that algorithm given in Numerical Recipes does convolution in the frequency
+domain using the FFT. However the discussion of convolution in 13.1 applies to the time
+domain and can be used to understand this Python code.***
+
+-MZ, 2/2017
+"""
+
+def convolve_sig_resp(signal_array, response_array, t_array, tstep):
+
+ def normalize_response(response_array, t_array):
+ area = np.trapz(response_array, x = t_array)
+ return response_array / area
+
+ def array_zeropad_neg(array, pad_length):
+
+ return np.pad(array, (pad_length, 0), 'constant', constant_values = (0,0))
+
+ def array_zeropad_pos(array, pad_length):
+ return np.pad(array, (0, pad_length), 'constant', constant_values = (0,0))
+
+# def array_symmetricpad_neg(array, pad_length):
+#
+# return np.pad(array, (pad_length, 0), 'symmetric')
+
+ def signal_and_resp_forconv(signal_array, response_array):
+ resp_pad_negtime = array_zeropad_neg(normalize_response(response_array, t_array), len(response_array) - 1)
+ sig_pad_negtime = array_zeropad_neg(signal_array, len(signal_array) - 1)
+ sig_pad_postime = array_zeropad_pos(sig_pad_negtime, len(response_array))
+ return [resp_pad_negtime, sig_pad_postime]
+
+ resp, sig = signal_and_resp_forconv(signal_array, response_array)
+ convolution = tstep * fftconvolve(sig, resp, mode = 'same')#np.convolve(resp, sig, mode = 'same')
+
+ return convolution[len(signal_array) - 1 : (2*len(signal_array)) - 1]
+
+def convolution_plusnoise(signal_array, response_array, t_array, tstep, noiselevel):
+ return convolve_sig_resp(signal_array, response_array, t_array, tstep) + noiselevel
+
+def herz_ode(t, n0, params):
+ a = params[0]
+ k1 = params[1]
+ k2 = params[2]
+ k3 = params[3]
+ def odefun(n, t, k1, k2, k3):
+ dndt = -(k1 * n) - (k2 * (n ** 2.0)) - (k3 * (n ** 3.0))
+ return dndt
+ ode_sol = odeint(odefun, n0, t, args = (k1, k2, k3))[:,0]
+ pl = k2 * (ode_sol ** 2.0)
+ return a*pl
+
+def fit_herz_ode_global_3traces_fmin_tnc(t1, t2, t3, tstep, d1, d2, d3, irf, init_params, bounds, n0array):
+ time_array1 = t1
+ time_array2 = t2
+ time_array3 = t3
+ data_array1 = d1
+ data_array2 = d2
+ data_array3 = d3
+ n0 = n0array[0]
+ n1 = n0array[1]
+ n2 = n0array[2]
+ def min_fit_decay(params):
+ #Minimize chi-squre for data set with Poisson distribution ()
+
+ a0 = params[0]
+ a1 = params[1]
+ a2 = params[2]
+ k1 = params[3]
+ k2 = params[4]
+ k3 = params[5]
+ noise1 = params[6]
+ noise2 = params[7]
+ noise3 = params[8]
+ decaymodel1 = herz_ode(time_array1, n0, np.array([a0,k1,k2,k3]))
+ decaymodel2 = herz_ode(time_array2, n1, np.array([a1,k1,k2,k3]))
+ decaymodel3 = herz_ode(time_array3, n2, np.array([a2,k1,k2,k3]))
+ model1 = convolution_plusnoise(decaymodel1, irf, time_array1, tstep, noise1)
+ model2 = convolution_plusnoise(decaymodel2, irf, time_array2, tstep, noise2)
+
+ model3 = convolution_plusnoise(decaymodel3, irf, time_array3, tstep, noise3)
+
+ data_fit_idx1 = np.nonzero(data_array1)
+ data_fit_idx2 = np.nonzero(data_array2)
+ data_fit_idx3 = np.nonzero(data_array3)
+ data_array_fit1 = data_array1[data_fit_idx1]
+ data_array_fit2 = data_array2[data_fit_idx2]
+ data_array_fit3 = data_array3[data_fit_idx3]
+
+ model_fit1 = model1[data_fit_idx1]
+ model_fit2 = model2[data_fit_idx2]
+ model_fit3 = model3[data_fit_idx3]
+
+ min1 = np.sum(((data_array_fit1 - model_fit1)** 2.0) / (np.sqrt(data_array_fit1) ** 2.0))
+ min2 = np.sum(((data_array_fit2 - model_fit2)** 2.0) / (np.sqrt(data_array_fit2) ** 2.0))
+ min3 = np.sum(((data_array_fit3 - model_fit3)** 2.0) / (np.sqrt(data_array_fit3) ** 2.0))
+ return np.sqrt((min1 ** 2.0) + (min2 ** 2.0) + (min3 ** 2.0))
+ bestfit_params = fmin_tnc(min_fit_decay, init_params, approx_grad = True, bounds = bounds)[0]
+ def bestfit_decay(params):
+ a0 = params[0]
+ a1 = params[1]
+ a2 = params[2]
+ k1 = params[3]
+ k2 = params[4]
+ k3 = params[5]
+ noise1 = params[6]
+ # noise2 = params[7]
+ # noise3 = params[8]
+ decaymodel1 = herz_ode(time_array, n0, np.array([a0,k1,k2,k3]))
+ decaymodel2 = herz_ode(time_array, n1, np.array([a1,k1,k2,k3]))
+ decaymodel3 = herz_ode(time_array, n2, np.array([a2,k1,k2,k3]))
+
+ model1 = convolution_plusnoise(decaymodel1, irf, time_array, tstep, noise1)
+ model2 = convolution_plusnoise(decaymodel2, irf, time_array, tstep, noise2)
+ model3 = convolution_plusnoise(decaymodel3, irf, time_array, tstep, noise3)
+ return [model1, model2, model3]
+
+ bestfit_model = bestfit_decay(bestfit_params)
+ # plt.figure()
+ # plt.ylabel('PL Counts')
+ # plt.xlabel('Time (ns)')
+ # plt.semilogy(time_array1, data_array1,'b', label = 'Data')
+ # plt.semilogy(time_array1, bestfit_model[0], 'r', label = 'Fit')
+ # plt.semilogy(time_array2, data_array2,'b', label = 'Data')
+ # plt.semilogy(time_array2, bestfit_model[1], 'r', label = 'Fit')
+ # plt.semilogy(time_array3, data_array3,'b', label = 'Data')
+ # plt.semilogy(time_array3, bestfit_model[2], 'r', label = 'Fit')
+ # plt.legend(loc = 'best')
+ return bestfit_params, bestfit_model, data_array, time_array, irf
+
+def multi_exp(t, params, num_exp):
+ exp_array = np.empty((len(t), num_exp))
+
+ i = 0
+ while (iMainWindow
-
+
+ -
+
+
-
+
+
+ true
+
+
+
+ 10
+
+
+
+ Fitting Controls
+
+
+ false
+
+
+
-
+
+
-
+
+
+
+ 10
+
+
+
+ Fit with IRF
+
+
+ true
+
+
+ false
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Fitting Function
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 10
+
+
+
+ Fitting method
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+
-
+
+
+ true
+
+
+
+ 10
+
+
+
+ Bounds
+
+
+
-
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.100000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 0.900000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ tc (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ beta
+
+
+
+ -
+
+
+ noise
+
+
+
+ -
+
+
+ 4
+
+
+ 1.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ a
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ min
+
+
+
+ -
+
+
+ max
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 10
+
+
+
+ Initial Guess
+
+
+
-
+
+
+ a
+
+
+
+ -
+
+
+ noise
+
+
+
+ -
+
+
+ beta
+
+
+
+ -
+
+
+ tc (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 5.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 1.000000000000000
+
+
+ 0.500000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 0.100000000000000
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Bounds
+
+
+
-
+
+
+ min
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ max
+
+
+
+ -
+
+
+ a1
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ noise
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 300.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ tau2 (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ a2
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ tau1 (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10.000000000000000
+
+
+
+
+
+
+ -
+
+
+ Initial Guess
+
+
+
-
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ a1
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 20.000000000000000
+
+
+
+ -
+
+
+ a2
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ tau2 (ns)
+
+
+
+ -
+
+
+ noise
+
+
+
+ -
+
+
+ tau1 (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.000000000000000
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Bounds
+
+
+
-
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ max
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ noise
+
+
+
+ -
+
+
+ a
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ tau (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 10000.000000000000000
+
+
+
+ -
+
+
+ min
+
+
+
+
+
+
+ -
+
+
+ Initial Guess
+
+
+
-
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ a
+
+
+
+ -
+
+
+ tau (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ noise
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+ 0.100000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Get Lifetime!
+
+
+
+ -
+
+
+ true
+
+
+
+ 10
+
+
+
+ Calculate SRV
+
+
+ true
+
+
+ false
+
+
+
-
+
+
+ Bulk Lifetime (ns)
+
+
+
+ -
+
+
+ Thickness (nm)
+
+
+
+ -
+
+
+ 100000.000000000000000
+
+
+ 250.000000000000000
+
+
+
+ -
+
+
+ 1000000.000000000000000
+
+
+ 3.000000000000000
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+ SRV (cm/s)
+
+
+
+ -
+
+
+ Diffusion Coefficient (cm2/s)
+
+
+
+ -
+
+
+ SRV1 = SRV2
+
+
+
+ -
+
+
+ Calculate
+
+
+
+ -
+
+
+ 100000.000000000000000
+
+
+ 8000.000000000000000
+
+
+
+ -
+
+
+ Surface Lifetime (ns)
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+ Average Lifetime (ns)
+
+
+
+ -
+
+
+ 4
+
+
+ 9999999.000000000000000
+
+
+
+
+
+
+
+
-
-
+
+
+
+ 2
+ 0
+
+
+
-
@@ -29,108 +1027,57 @@
Settings
-
-
-
+
-
+
12
- Fitting Controls
+ Export Settings
-
-
-
-
+
+
-
+
12
- Fitting function
+ Save with Fit
-
-
- -
-
-
- -
-
-
- Configure Fit Settings
+
+ true
- -
-
+
-
+
12
+ 50
+ false
- Get Lifetime!
-
-
-
- -
-
-
-
- 12
-
+ Export figure
- -
-
-
-
- 12
-
-
-
- Export Plot Settings
+
-
+
+
+ Export data
-
-
-
-
-
-
- 12
- 50
- false
-
-
-
- Export Publication
-Ready Figure!
-
-
-
- -
-
-
-
- 12
-
-
-
- Save with Fit
-
-
- true
-
-
-
-
- -
+
-
@@ -141,7 +1088,7 @@ Ready Figure!
Plot Controls
-
-
+
-
@@ -155,19 +1102,21 @@ Ready Figure!
- -
-
-
-
- 12
-
+
-
+
+
+ Y Axis Log
+
+
+ -
+
- Y axis Log
+ Plot color
- -
+
-
@@ -180,9 +1129,37 @@ Ready Figure!
+ -
+
+
+
+
+ -
+
+
+ Normalize
+
+
+
+ -
+
+
+ Clear plot everytime
+
+
+
+ -
+
+
+
+ 12
+
+
+
+
-
@@ -195,7 +1172,7 @@ Ready Figure!
- -
+
-
@@ -207,27 +1184,63 @@ Ready Figure!
- -
-
+
-
+
+
+
+ 12
+
+
+
+ 512
+
+
+ 1
+
+
+
+ -
+
12
- Channel No
+ IRF Channel No
-
-
+
12
-
- 512
+
+ Data Channel No
+
+
+
+ -
+
+
+
+ 12
+
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Load separate IRF file
@@ -242,7 +1255,7 @@ Ready Figure!
0
0
1985
- 38
+ 31
@@ -271,6 +1285,14 @@ Ready Figure!
Save
+
+
+ false
+
+
+ Open IRF File
+
+
diff --git a/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py b/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py
index 5cf9ace..ec792f2 100644
--- a/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py
+++ b/PythonGUI_apps/Lifetime_analysis/Lifetime_plot_fit.py
@@ -7,6 +7,7 @@
# system imports
import sys
+import os
from pathlib import Path
# module imports
@@ -19,8 +20,10 @@
try:
from Lifetime_analysis.Fit_functions import stretch_exp_fit, double_exp_fit, single_exp_fit
from Lifetime_analysis.picoharp_phd import read_picoharp_phd
+ from Lifetime_analysis.Fit_functions_with_irf import fit_exp_stretch_diffev, fit_exp_stretch_fmin_tnc, fit_multi_exp_diffev, fit_multi_exp_fmin_tnc
except:
from Fit_functions import stretch_exp_fit, double_exp_fit, single_exp_fit
+ from Fit_functions_with_irf import fit_exp_stretch_diffev, fit_exp_stretch_fmin_tnc, fit_multi_exp_diffev, fit_multi_exp_fmin_tnc
from picoharp_phd import read_picoharp_phd
"""Recylce params for plotting"""
@@ -52,31 +55,59 @@ def __init__(self):
self.ui.setupUi(self)
self.ui.Res_comboBox.addItems(["0.004","0.008","0.016","0.032","0.064","0.128","0.256","0.512"])
self.ui.FittingFunc_comboBox.addItems(["Stretched Exponential","Double Exponential", "Single Exponential"])
+ self.ui.FittingMethod_comboBox.addItems(["diff_ev", "fmin_tnc"])
+ #set up file menu
self.ui.actionOpen.triggered.connect(self.open_file)
+ self.ui.actionOpen_IRF_File.triggered.connect(self.open_irf_file)
self.ui.actionSave.triggered.connect(self.save_file)
self.ui.actionExit.triggered.connect(self.close_application)
+ #set up ui signals
self.ui.plot_pushButton.clicked.connect(self.plot)
- self.ui.log_pushButton.clicked.connect(self.make_semilog)
- self.ui.fit_pushButton.clicked.connect(self.fit_and_plot)
+ self.ui.fit_pushButton.clicked.connect(self.call_fit_and_plot)
self.ui.clear_pushButton.clicked.connect(self.clear_plot)
self.ui.export_plot_pushButton.clicked.connect(self.pub_ready_plot_export)
-
+ self.ui.calculate_srv_pushButton.clicked.connect(self.calculate_srv)
+
+ self.ui.log_checkBox.stateChanged.connect(self.make_semilog)
+ self.ui.fit_with_irf_checkBox.stateChanged.connect(self.switch_fit_settings)
+ self.ui.FittingFunc_comboBox.currentTextChanged.connect(self.switch_function_tab)
+ self.ui.FittingMethod_comboBox.currentTextChanged.connect(self.switch_init_params_groupBox)
+ self.ui.separate_irf_checkBox.stateChanged.connect(self.switch_open_irf)
+ self.ui.export_data_pushButton.clicked.connect(self.export_data)
+
+ #set up plot color button
+ self.plot_color_button = pg.ColorButton(color=(255,0,0))
+ self.ui.plot_color_button_container.layout().addWidget(self.plot_color_button)
+ self.plot_color = self.plot_color_button.color()
+ self.plot_color_button.sigColorChanged.connect(self.plot_color_changed)
+
self.file = None
self.out = None # output file after fitting
-
+ self.data_list = []
self.show()
def open_file(self):
+ """ Open data file """
# try:
- filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.filename = QtWidgets.QFileDialog.getOpenFileName(self)
try:
- self.file = np.loadtxt(filename[0], skiprows=10)
+ self.file = np.loadtxt(self.filename[0], skiprows=10)
# except ValueError:
# self.file = np.loadtxt(filename[0], skiprows=10)
except UnicodeDecodeError:
- self.file = read_picoharp_phd(filename[0])
+ self.file = read_picoharp_phd(self.filename[0])
+ except:
+ pass
+
+ def open_irf_file(self):
+ """ Open file with irf - enabled if 'load separate irf' is checled """
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ try:
+ self.irf_file = np.loadtxt(filename[0], skiprows=10)
+ except UnicodeDecodeError:
+ self.irf_file = read_picoharp_phd(filename[0])
except:
pass
@@ -86,13 +117,72 @@ def save_file(self):
np.savetxt(filename[0], self.out, fmt = '%.5f', header = 'Time, Raw_PL, Sim_PL', delimiter = ' ')
except:
pass
+
+ def switch_open_irf(self):
+ """ Handle 'load separate irf' checkbox """
+ self.ui.actionOpen_IRF_File.setEnabled(self.ui.separate_irf_checkBox.isChecked())
+
+ def switch_fit_settings(self):
+ """ Enable bounds/initial guess groupboxes only when 'Fit with IRF' is checked """
+ checked = self.ui.fit_with_irf_checkBox.isChecked()
+ for func in "str de se".split(" "):
+ boundsGb = eval("self.ui."+func+"_bounds_groupBox")
+ #initGb = eval("self.ui."+func+"_init_groupBox")
+ boundsGb.setEnabled(checked)
+ #initGb.setEnabled(checked)
+ if checked == True:
+ self.switch_init_params_groupBox()
+ else:
+ initGb = eval("self.ui."+func+"_init_groupBox")
+ initGb.setEnabled(checked)
+ self.ui.FittingMethod_comboBox.setEnabled(checked)
+
+ def switch_function_tab(self):
+ """ Switch bounds groupbox contents depending on selected fit function """
+ fitting_func = self.ui.FittingFunc_comboBox.currentText()
+ if fitting_func == "Stretched Exponential":
+ self.ui.fitting_params_stackedWidget.setCurrentIndex(0)
+ elif fitting_func == "Double Exponential":
+ self.ui.fitting_params_stackedWidget.setCurrentIndex(1)
+ elif fitting_func == "Single Exponential":
+ self.ui.fitting_params_stackedWidget.setCurrentIndex(2)
+
+ def switch_init_params_groupBox(self):
+ """ Enable initial guess groupbox only when fmin_tnc fit method selected """
+ if self.ui.FittingMethod_comboBox.currentText() == "diff_ev":
+ for func in "str de se".split(" "):
+ initGb = eval("self.ui."+func+"_init_groupBox")
+ initGb.setEnabled(False)
+ #initGb.setEnabled(checked)
+ elif self.ui.FittingMethod_comboBox.currentText() == "fmin_tnc":
+ for func in "str de se".split(" "):
+ initGb = eval("self.ui."+func+"_init_groupBox")
+ initGb.setEnabled(True)
+
+ def plot_color_changed(self):
+ """ Grab new plot_color when color button value is changed """
+ self.plot_color = self.plot_color_button.color()
- def acquire_settings(self):
- resolution = float(self.ui.Res_comboBox.currentText())
- channel = int(self.ui.Channel_spinBox.value())
+ def acquire_settings(self, mode="data"):
+ """
+ Acquire data or irf from channel specified in spinbox.
+
+ mode -- string specifying whether to use data or irf channel (default "data")
+ """
+ self.resolution = float(self.ui.Res_comboBox.currentText())
+ if mode == "data":
+ channel = int(self.ui.Data_channel_spinBox.value())
+ elif mode == "irf":
+ channel = int(self.ui.irf_channel_spinBox.value())
try:
- try:
- y = self.file[:,channel]
+ try: #
+ if self.ui.separate_irf_checkBox.isChecked() and mode=="irf": #if separate irf, get from irf file
+ try:
+ y = self.irf_file[:,channel]
+ except:
+ y = self.irf_file.get_curve(channel)[1]
+ else: #otherwise, get data/irf from data file
+ y = self.file[:,channel]
except:
res, y = self.file.get_curve(channel)
# TO DO - check if res read in is the same as selected
@@ -100,7 +190,7 @@ def acquire_settings(self):
y = y[0:time_window]
length = np.shape(y)[0]
- x = np.arange(0, length, 1) * resolution
+ x = np.arange(0, length, 1) * self.resolution
return x,y
except Exception as e:
@@ -108,8 +198,12 @@ def acquire_settings(self):
def plot(self):
try:
- x,y = self.acquire_settings()
- self.ui.plot.plot(x, y, clear=False, pen='r')
+ x,y = self.acquire_settings() #get data
+ if self.ui.normalize_checkBox.isChecked():
+ y = y / np.amax(y)
+
+ self.ui.plot.plot(x, y, clear=self.ui.clear_plot_checkBox.isChecked(), pen=pg.mkPen(self.plot_color))
+
try:
self.ui.Result_textBrowser.setText("Integral Counts :\n" "{:.2E}".format(
self.file.get_integral_counts(int(self.ui.Channel_spinBox.value()))))
@@ -120,79 +214,258 @@ def plot(self):
self.ui.plot.setLabel('left', 'Intensity', units='a.u.')
self.ui.plot.setLabel('bottom', 'Time (ns)')
-
def make_semilog(self):
- self.ui.plot.setLogMode(False,True)
+ """ Switch y-log on/off """
+ self.ui.plot.setLogMode(False,self.ui.log_checkBox.isChecked())
def clear_plot(self):
self.ui.plot.clear()
self.ui.Result_textBrowser.clear()
def fit_and_plot(self):
+ """ Fit and plot without IRF """
try:
- x,y = self.acquire_settings()
-
- y_norm = y/np.max(y)
+ x,y = self.acquire_settings() #get data
+ y_norm = y/np.max(y) #normalized y
+
# find the max intensity in the array and start things from there
find_max_int = np.nonzero(y_norm == 1)
y = y[np.asscalar(find_max_int[0]):]
-
- resolution = float(self.ui.Res_comboBox.currentText())
- # x = x[np.asscalar(find_max_int[0]):]
- x = np.arange(0, len(y), 1) * resolution
-
+ x = x[np.asscalar(find_max_int[0]):]
+
t = x
-
- time_fit = t
+ time_fit = t
TRPL_interp = np.interp(time_fit, t, y)
fit_func = self.ui.FittingFunc_comboBox.currentText()
- self.ui.plot.plot(t, y, clear=True, pen='r')
+ self.ui.plot.plot(t, y, clear=self.ui.clear_plot_checkBox.isChecked(), pen=pg.mkPen(self.plot_color))
- if fit_func == "Stretched Exponential":
+ if fit_func == "Stretched Exponential": #stretch exponential tab
tc, beta, a, avg_tau, PL_fit = stretch_exp_fit(TRPL_interp, t)
self.out = np.empty((len(t), 3))
self.out[:,0] = t #time
self.out[:,1] = TRPL_interp #Raw PL
self.out[:,2] = PL_fit # PL fit
- self.ui.plot.plot(t, PL_fit, clear=False, pen='k')
+ self.ui.plot.plot(t, PL_fit, clear=self.ui.clear_plot_checkBox.isChecked(), pen='k')
self.ui.Result_textBrowser.setText("Fit Results:\n\nFit Function: Stretched Exponential"
+ "\nFit Method: " + "diff_ev" + #TODO : change when diff_ev and fmin_tnc implemented for non-irf
"\nAverage Lifetime = " + str(avg_tau)+ " ns"
"\nCharacteristic Tau = " + str(tc)+" ns"
"\nBeta = "+str(beta))
+ self.ui.average_lifetime_spinBox.setValue(avg_tau)
- elif fit_func == "Double Exponential":
+ elif fit_func == "Double Exponential": #double exponential tab
tau1, a1, tau2, a2, avg_tau, PL_fit = double_exp_fit(TRPL_interp, t)
self.out = np.empty((len(t), 3))
self.out[:,0] = t #time
self.out[:,1] = TRPL_interp #Raw PL
self.out[:,2] = PL_fit # PL fit
- self.ui.plot.plot(t, PL_fit, clear=False, pen='k')
+ self.ui.plot.plot(t, PL_fit, clear=self.ui.clear_plot_checkBox.isChecked(), pen='k')
self.ui.Result_textBrowser.setText("Fit Results:\n\nFit Function: Double Exponential"
+ "\nFit Method: " + "diff_ev" +
"\nAverage Lifetime = " + str(avg_tau)+ " ns"
"\nTau 1 = " + str(tau1)+" ns"
"\nA 1 = " + str(a1)+
"\nTau 2 = " + str(tau2)+" ns"
"\nA 2 = " + str(a2))
+ #TODO - once tau_avg implemented, set average lifetime spinbox to tau_avg value
- elif fit_func == "Single Exponential":
+ elif fit_func == "Single Exponential": #single exponential tab
tau, a, PL_fit = single_exp_fit(TRPL_interp, t)
self.out = np.empty((len(t), 3))
self.out[:,0] = t #time
self.out[:,1] = TRPL_interp #Raw PL
self.out[:,2] = PL_fit # PL fit
- self.ui.plot.plot(t, PL_fit, clear=False, pen='k')
+ self.ui.plot.plot(t, PL_fit, clear=self.ui.clear_plot_checkBox.isChecked(), pen='k')
self.ui.Result_textBrowser.setText("Fit Results:\n\nFit Function: Single Exponential"
+ "\nFit Method: " + "diff_ev" +
"\nLifetime = " + str(tau)+ " ns"
"\nA = " + str(a))
-
+ self.ui.average_lifetime_spinBox.setValue(tau)
+
+ #add fit params to data_list
+ self.data_list.append("Data Channel: " + str(self.ui.Data_channel_spinBox.value()) + "\n" + self.ui.Result_textBrowser.toPlainText())
+
self.ui.plot.setLabel('left', 'Intensity', units='a.u.')
self.ui.plot.setLabel('bottom', 'Time (ns)')
return self.out
- except:
- pass
+ except Exception as err:
+ exc_type, exc_obj, exc_tb = sys.exc_info()
+ print(exc_type, exc_tb.tb_lineno)
+
+ def fit_and_plot_with_irf(self):
+ """ Fit and plot with IRF """
+ try:
+ x,y = self.acquire_settings() #get data
+ _, irf_counts = self.acquire_settings(mode="irf") #get irf counts
+
+ #make sure Irf and data have the same length
+ if len(y) != len(irf_counts):
+ y = y[0:min(len(y), len(irf_counts))]
+ irf_counts = irf_counts[0:min(len(y), len(irf_counts))]
+ x = x[0:min(len(y), len(irf_counts))]
+
+ y_norm = y/np.max(y) #normalized y
+ irf_norm = irf_counts/np.amax(irf_counts) #normalized irf
+
+ t = x
+ time_fit = t
+ y = y_norm
+ irf_counts = irf_norm
+
+ TRPL_interp = np.interp(time_fit, t, y)
+
+ fit_func = self.ui.FittingFunc_comboBox.currentText()
+ self.ui.plot.plot(t, y, clear=self.ui.clear_plot_checkBox.isChecked(), pen=pg.mkPen(self.plot_color))
+ if fit_func == "Stretched Exponential": #stretched exponential tab
+ tc_bounds = (self.ui.str_tc_min_spinBox.value(), self.ui.str_tc_max_spinBox.value()) #(0, 10000)
+ a_bounds = (self.ui.str_a_min_spinBox.value(), self.ui.str_a_max_spinBox.value())#(0.9, 1.1)
+ beta_bounds = (self.ui.str_beta_min_spinBox.value(), self.ui.str_beta_max_spinBox.value())#(0,1)
+ noise_bounds = (self.ui.str_noise_min_spinBox.value(), self.ui.str_noise_max_spinBox.value())#(0, 1e4)
+ stretch_exp_bounds = [tc_bounds, beta_bounds, a_bounds, noise_bounds]
+ stretch_exp_init_params = [self.ui.str_tc_init_spinBox.value(), self.ui.str_a_init_spinBox.value(), self.ui.str_beta_init_spinBox.value(), self.ui.str_noise_init_spinBox.value()]
+
+ #tc, beta, a, avg_tau, PL_fit = stretch_exp_fit(TRPL_interp, t)
+# resolution = float(self.ui.Res_comboBox.currentText())
+ if self.ui.FittingMethod_comboBox.currentText() == "diff_ev":
+ bestfit_params, t_avg, bestfit_model, data_array, time_array, irf = fit_exp_stretch_diffev(t, self.resolution, TRPL_interp, irf_counts, stretch_exp_bounds)
+ else: #if fmin_tnc fitting method selected
+ bestfit_params, t_avg, bestfit_model, data_array, time_array, irf = fit_exp_stretch_fmin_tnc(t, self.resolution, TRPL_interp, irf_counts, stretch_exp_init_params, stretch_exp_bounds)
+ self.out = np.empty((len(t), 3))
+ self.out[:,0] = t #time
+ self.out[:,1] = TRPL_interp #Raw PL
+ self.out[:,2] = bestfit_model # PL fit
+ self.ui.plot.plot(t, bestfit_model, clear=self.ui.clear_plot_checkBox.isChecked(), pen='k')
+ self.ui.Result_textBrowser.setText("Fit Results:\n\nFit Function: Stretched Exponential with IRF"
+ "\nFit Method: "+ self.ui.FittingMethod_comboBox.currentText() +
+ "\ntau_avg = %.5f ns"
+ "\nbeta = %.5f"
+ "\ntau_c = %.5f ns"
+ "\na = %.5f \nnoise = %.5f counts" %(t_avg, bestfit_params[1], bestfit_params[0], bestfit_params[2], bestfit_params[3]))
+ #self.effective_lifetime = t_avg
+ self.ui.average_lifetime_spinBox.setValue(t_avg)
+
+ elif fit_func == "Double Exponential": #double exponential tab
+ a1_bounds = (self.ui.de_a1_min_spinBox.value(), self.ui.de_a1_max_spinBox.value())
+ tau1_bounds = (self.ui.de_tau1_min_spinBox.value(), self.ui.de_tau1_max_spinBox.value())
+ a2_bounds = (self.ui.de_a2_min_spinBox.value(), self.ui.de_a2_max_spinBox.value())
+ tau2_bounds = (self.ui.de_tau2_min_spinBox.value(), self.ui.de_tau2_max_spinBox.value())
+ noise_bounds = (self.ui.de_noise_min_spinBox.value(), self.ui.de_noise_max_spinBox.value())
+ double_exp_bounds = [a1_bounds, tau1_bounds, a2_bounds, tau2_bounds, noise_bounds]
+ double_exp_init_params = [self.ui.de_a1_init_spinBox.value(), self.ui.de_tau1_init_spinBox.value(), self.ui.de_a2_init_spinBox.value(),
+ self.ui.de_tau2_init_spinBox.value(), self.ui.de_noise_init_spinBox.value()]
+
+ if self.ui.FittingMethod_comboBox.currentText() == "diff_ev":
+ bestfit_params, bestfit_model, data_array, time_array, irf = fit_multi_exp_diffev(t, self.resolution, TRPL_interp, irf_counts, double_exp_bounds, 2)
+ #bestfit_params, bestfit_model, data_array, time_array, irf = fit_multi_exp_diffev(t, resolution, TRPL_interp, irf_counts, double_exp_init_bounds, 2)
+ else:
+ bestfit_params, bestfit_model, data_array, time_array, irf = fit_multi_exp_fmin_tnc(t, self.resolution, TRPL_interp, irf_counts, double_exp_init_params, double_exp_bounds, 2)
+ self.out = np.empty((len(t), 3))
+ self.out[:,0] = t #time
+ self.out[:,1] = TRPL_interp #Raw PL
+ self.out[:,2] = bestfit_model # PL fit
+ self.ui.plot.plot(t, bestfit_model, clear=self.ui.clear_plot_checkBox.isChecked(), pen='k')
+ self.ui.Result_textBrowser.setText("Fit Results:\n\nFit Function: Double Exponential with IRF"
+ "\nFit Method: "+ self.ui.FittingMethod_comboBox.currentText() +
+ "\na1 = %.5f"
+ "\ntau1 = %.5f ns"
+ "\na2 = %.5f"
+ "\ntau2 = %.5f ns"
+ "\nnoise = %.5f counts" %(bestfit_params[0], bestfit_params[1], bestfit_params[2], bestfit_params[3], bestfit_params[4]))
+ #TODO - once tau_avg implemented, set average lifetime spinbox to tau_avg value
+
+ elif fit_func == "Single Exponential": #single exponential tab
+ a_bounds = (self.ui.se_a_min_spinBox.value(), self.ui.se_a_max_spinBox.value())
+ tau_bounds = (self.ui.se_tau_min_spinBox.value(), self.ui.se_tau_max_spinBox.value())
+ noise_bounds = (self.ui.se_noise_min_spinBox.value(), self.ui.se_noise_max_spinBox.value())
+ single_exp_bounds = [a_bounds, tau_bounds, noise_bounds]
+ single_exp_init_params = [self.ui.se_a_init_spinBox.value(), self.ui.se_tau_init_spinBox.value(), self.ui.se_noise_init_spinBox.value()]
+
+ if self.ui.FittingMethod_comboBox.currentText() == "diff_ev":
+ bestfit_params, bestfit_model, data_array, time_array, irf = fit_multi_exp_diffev(t, self.resolution, TRPL_interp, irf_counts, single_exp_bounds, 1)
+ else:
+ bestfit_params, bestfit_model, data_array, time_array, irf = fit_multi_exp_fmin_tnc(t, self.resolution, TRPL_interp, irf_counts, single_exp_init_params, single_exp_bounds, 1)
+ self.out = np.empty((len(t), 3))
+ self.out[:,0] = t #time
+ self.out[:,1] = TRPL_interp #Raw PL
+ self.out[:,2] = bestfit_model # PL fit
+ self.ui.plot.plot(t, bestfit_model, clear=self.ui.clear_plot_checkBox.isChecked(), pen='k')
+ self.ui.Result_textBrowser.setText("Fit Results:\n\nFit Function: Single Exponential with IRF"
+ "\nFit Method: "+ self.ui.FittingMethod_comboBox.currentText() +
+ "\na = %.5f"
+ "\ntau = %.5f ns"
+ "\nnoise = %.5f counts" %(bestfit_params[0], bestfit_params[1], bestfit_params[2]))
+ self.ui.average_lifetime_spinBox.setValue(bestfit_params[1]) #set spinbox to tau value
+
+ #add fit params to data_list
+ self.data_list.append("Data Channel: " + str(self.ui.Data_channel_spinBox.value()) + "\n" + self.ui.Result_textBrowser.toPlainText())
+
+ except Exception as err:
+ exc_type, exc_obj, exc_tb = sys.exc_info()
+ print(exc_type, exc_tb.tb_lineno)
+
+ def call_fit_and_plot(self):
+ if self.ui.fit_with_irf_checkBox.isChecked():
+ self.fit_and_plot_with_irf()
+ else:
+ self.fit_and_plot()
+ if self.ui.calculate_srv_groupBox.isChecked():
+ self.calculate_srv() #calculate srv on plot
+ self.data_list.append(self.get_srv_string()) #add srv params to data_list
+ def calculate_surface_lifetime(self):
+ effective_lifetime = self.ui.average_lifetime_spinBox.value()
+ self.bulk_lifetime = self.ui.bulk_lifetime_spinBox.value() # in ns
+ self.surface_lifetime = (effective_lifetime * self.bulk_lifetime)/(self.bulk_lifetime - effective_lifetime)
+ self.ui.surface_lifetime_label.setText(str(self.surface_lifetime))
+
+ def calculate_srv (self):
+ self.calculate_surface_lifetime()
+ self.thickness = self.ui.thickness_spinBox.value()*1e-7 # convert to cm
+ self.diffusion_coeffecient = self.ui.diffusion_coefficient_spinBox.value() # in cm2/s
+
+ if self.ui.srv1_srv2_checkBox.isChecked():
+ self.srv = self.thickness / (2*((1e-9*self.surface_lifetime) - ((1/self.diffusion_coeffecient)*((self.thickness/np.pi)**2)) ))
+ else:
+ self.srv = self.thickness / ((1e-9*self.surface_lifetime) - ((4/self.diffusion_coeffecient)*((self.thickness/np.pi)**2)) )
+
+ self.ui.srv_label.setText(str(self.srv))
+
+ def get_srv_string(self):
+ """ Get info from SRV Calculation groupbox as string """
+ srv_string = "SRV Calculation:"\
+ + "\nAverage Lifetime (ns): " + str(self.ui.average_lifetime_spinBox.value()) \
+ + "\nBulk Lifetime (ns): " + str(self.ui.bulk_lifetime_spinBox.value()) \
+ + "\nThickness (nm): " + str(self.ui.thickness_spinBox.value()) \
+ + "\nDiffusion Coefficient (cm2/s): " + str(self.ui.diffusion_coefficient_spinBox.value())
+ if self.ui.srv1_srv2_checkBox.isChecked():
+ srv_string += "\nSRV1 = SRV2"
+ else:
+ srv_string += "\nSRV1 = 0"
+ srv_string += "\nSurface Lifetime (ns): " + self.ui.surface_lifetime_label.text() \
+ + "\nSRV (cm/s): " + self.ui.srv_label.text()
+ return srv_string
+
+ def export_data(self):
+ """ Save fit params and srv calculations stored in data_list as .txt """
+ folder = os.path.dirname(self.filename[0])
+ filename_ext = os.path.basename(self.filename[0])
+ filename = os.path.splitext(filename_ext)[0] #get filename without extension
+
+ path = folder + "/" + filename + "_fit_results.txt"
+ if not os.path.exists(path):
+ file = open(path, "w+")
+ else:
+ file = open(path, "a+")
+
+ for i in range(len(self.data_list)):
+ file.write(self.data_list[i] + "\n\n")
+
+ self.data_list = []
+ file.close()
+
+
def pub_ready_plot_export(self):
try:
filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION")
@@ -215,7 +488,6 @@ def pub_ready_plot_export(self):
except:
pass
-
def close_application(self):
choice = QtGui.QMessageBox.question(self, 'EXIT!',
"Do you want to exit the app?",
@@ -224,7 +496,6 @@ def close_application(self):
sys.exit()
else:
pass
-
def run():
win = MainWindow()
diff --git a/PythonGUI_apps/Lifetime_analysis/__pycache__/Fit_functions.cpython-36.pyc b/PythonGUI_apps/Lifetime_analysis/__pycache__/Fit_functions.cpython-36.pyc
deleted file mode 100644
index ece4395..0000000
Binary files a/PythonGUI_apps/Lifetime_analysis/__pycache__/Fit_functions.cpython-36.pyc and /dev/null differ
diff --git a/PythonGUI_apps/PLQE_analysis/plqe_analysis.py b/PythonGUI_apps/PLQE_analysis/plqe_analysis.py
new file mode 100644
index 0000000..a560746
--- /dev/null
+++ b/PythonGUI_apps/PLQE_analysis/plqe_analysis.py
@@ -0,0 +1,167 @@
+# system imports
+from pathlib import Path
+import os.path
+import pyqtgraph as pg
+from pyqtgraph import exporters
+from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
+import matplotlib.pyplot as plt
+
+import numpy as np
+import time
+
+# local modules
+
+pg.mkQApp()
+pg.setConfigOption('background', 'w')
+
+base_path = Path(__file__).parent
+file_path = (base_path / "plqe_analysis_gui.ui").resolve()
+
+uiFile = file_path
+
+WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile)
+
+"""params for plotting"""
+plt.rc('xtick', labelsize = 20)
+plt.rc('xtick.major', pad = 3)
+plt.rc('ytick', labelsize = 20)
+plt.rc('lines', lw = 1.5, markersize = 7.5)
+plt.rc('legend', fontsize = 20)
+plt.rc('axes', linewidth = 3.5)
+
+class MainWindow(TemplateBaseClass):
+
+ def __init__(self):
+ super(TemplateBaseClass, self).__init__()
+
+ # Create the main window
+ self.ui = WindowTemplate()
+ self.ui.setupUi(self)
+
+ #setup uv vis plot
+ self.plot = self.ui.plotWidget.getPlotItem()
+ self.plot.setTitle(title="Wavelength vs. Intensity")
+ self.plot.setLabel('bottom', 'Wavelength', unit='nm')
+ self.plot.setLabel('left', 'Intensity', unit='a.u.')
+ self.plot.setLogMode(x=None, y=1)
+
+ #setup line rois for laser and emission
+ self.laser_region = pg.LinearRegionItem(brush=QtGui.QBrush(QtGui.QColor(255, 0, 0, 50)))
+ self.laser_region.sigRegionChanged.connect(self.update_laser_spinBoxes)
+ self.emission_region = pg.LinearRegionItem()
+ self.emission_region.sigRegionChanged.connect(self.update_emission_spinBoxes)
+ self.laser_region.setRegion((200, 400))
+ self.emission_region.setRegion((700, 800))
+
+ #setup ui signals
+ self.ui.load_data_pushButton.clicked.connect(self.open_data_file)
+ self.ui.plot_pushButton.clicked.connect(self.plot_intensity)
+ self.ui.clear_pushButton.clicked.connect(self.clear)
+ self.ui.calculate_plqe_pushButton.clicked.connect(self.calculate_plqe)
+ self.ui.laser_start_spinBox.valueChanged.connect(self.update_laser_region)
+ self.ui.laser_stop_spinBox.valueChanged.connect(self.update_laser_region)
+ self.ui.emission_start_spinBox.valueChanged.connect(self.update_emission_region)
+ self.ui.emission_stop_spinBox.valueChanged.connect(self.update_emission_region)
+
+ self.show()
+
+ def open_data_file(self):
+ """ Open data file """
+ try:
+ self.filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.data = np.loadtxt(self.filename[0], delimiter = '\t', skiprows = 1)
+ self.nm = np.copy(self.data[:,0])
+ self.ref_data = np.copy(self.data[:,1])
+ self.inpath_data = np.copy(self.data[:,2])
+ self.outpath_data = np.copy(self.data[:,3])
+ except Exception as err:
+ print(format(err))
+
+ def update_laser_spinBoxes(self):
+ """ Update laser spinboxes based on line rois """
+ self.laser_start, self.laser_stop = self.laser_region.getRegion()
+ self.ui.laser_start_spinBox.setValue(self.laser_start)
+ self.ui.laser_stop_spinBox.setValue(self.laser_stop)
+
+
+ def update_emission_spinBoxes(self):
+ """ Update emission spinboxes based on line rois """
+ self.emission_start, self.emission_stop = self.emission_region.getRegion()
+ self.ui.emission_start_spinBox.setValue(self.emission_start)
+ self.ui.emission_stop_spinBox.setValue(self.emission_stop)
+
+ def update_laser_region(self):
+ """ Update laser line rois based on spinboxes """
+ laser_start = self.ui.laser_start_spinBox.value()
+ laser_stop = self.ui.laser_stop_spinBox.value()
+ self.laser_region.setRegion((laser_start, laser_stop))
+
+ def update_emission_region(self):
+ """ Update emission line rois based on spinboxes """
+ emission_start = self.ui.emission_start_spinBox.value()
+ emission_stop = self.ui.emission_stop_spinBox.value()
+ self.emission_region.setRegion((emission_start, emission_stop))
+
+ def plot_intensity(self):
+ try:
+ self.plot.plot(self.nm, self.inpath_data)
+ self.plot.addItem(self.laser_region, ignoreBounds=True)
+ self.plot.addItem(self.emission_region, ignoreBounds=True)
+ except Exception as err:
+ print(format(err))
+
+ def find_nearest(self,array,value):
+ idx = (np.abs(array-value)).argmin()
+ return idx
+
+ def calculate_plqe(self):
+
+ nm_interp_step = 1
+ nm_interp_start = np.ceil(self.nm[0] / nm_interp_step) * nm_interp_step
+ nm_interp_stop = np.floor(self.nm[len(self.nm) - 1] / nm_interp_step) * nm_interp_step
+ nm_interp = np.arange(nm_interp_start, nm_interp_stop + nm_interp_step, nm_interp_step)
+
+ ref_interp = np.interp(nm_interp, self.nm, self.ref_data)
+
+
+ inpath_interp = np.interp(nm_interp, self.nm, self.inpath_data)
+ outpath_interp = np.interp(nm_interp, self.nm, self.outpath_data)
+
+
+ """L_x is area under laser profile for experiment x"""
+ """P_x_ is area under emission profile for experiment x"""
+
+
+ #plt.semilogy(nm, a1_outpath_data[:,1])
+
+ emission_start_idx = self.find_nearest(nm_interp, self.emission_start)
+ emission_stop_idx = self.find_nearest(nm_interp, self.emission_stop)
+
+ laser_start_idx = self.find_nearest(nm_interp, self.laser_start)
+ laser_stop_idx = self.find_nearest(nm_interp, self.laser_stop)
+
+ la = np.trapz(ref_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx])
+ lb = np.trapz(outpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx])
+ lc = np.trapz(inpath_interp[laser_start_idx: laser_stop_idx], x = nm_interp[laser_start_idx:laser_stop_idx])
+
+ pa = np.trapz(ref_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx])
+ pb = np.trapz(outpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx])
+ pc = np.trapz(inpath_interp[emission_start_idx:emission_stop_idx], x = nm_interp[emission_start_idx:emission_stop_idx])
+
+ absorb = 1.0 - (lc / lb)
+
+ plqe = 100 * (pc - ((1.0 - absorb) * pb)) / (la * absorb)
+ #print('PLQE Percent = %.3f' %(plqe))
+ #return plqe
+ self.ui.plqe_label.setText("%.3f" %(plqe))
+
+ def clear(self):
+ self.plot.clear()
+
+"""Run the Main Window"""
+def run():
+ win = MainWindow()
+ QtGui.QApplication.instance().exec_()
+ return win
+
+#run()
diff --git a/PythonGUI_apps/PLQE_analysis/plqe_analysis_gui.ui b/PythonGUI_apps/PLQE_analysis/plqe_analysis_gui.ui
new file mode 100644
index 0000000..1caeccf
--- /dev/null
+++ b/PythonGUI_apps/PLQE_analysis/plqe_analysis_gui.ui
@@ -0,0 +1,150 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 575
+ 524
+
+
+
+ Form
+
+
+ -
+
+
+ PLQE
+
+
+
-
+
+
+ Laser start
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 200.000000000000000
+
+
+
+ -
+
+
+ Laser stop
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 400.000000000000000
+
+
+
+ -
+
+
+ Emission start
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 700.000000000000000
+
+
+
+ -
+
+
+ Emission stop
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 800.000000000000000
+
+
+
+ -
+
+
+ PLQE Percent
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+ Calculate PLQE
+
+
+
+
+
+
+ -
+
+
+ Clear
+
+
+
+ -
+
+
+ Plot
+
+
+
+ -
+
+
+ Load data
+
+
+
+ -
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+
+
+
diff --git a/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py b/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py
index 6323804..fba0a86 100644
--- a/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py
+++ b/PythonGUI_apps/Spectrum_analysis/Spectra_fit_funcs.py
@@ -56,13 +56,22 @@ def gaussian_model(self):
result = gmodel.fit(y, pars, x=x, nan_policy='propagate')
return result
- def gaussian_model_w_lims(self, center_min=None, center_max=None):
+ # def gaussian_model_w_lims(self, center_initial_guess=None, sigma_initial_guess=None, center_min=None, center_max=None):
+ # x,y = self.background_correction()
+ # gmodel = GaussianModel(prefix = 'g1_') # calling gaussian model
+ # pars = gmodel.guess(y, x=x) # parameters - center, width, height
+ # pars['g1_center'].set(center_initial_guess, min=center_min, max=center_max)
+ # pars['g1_sigma'].set(sigma_initial_guess)
+ # result = gmodel.fit(y, pars, x=x, nan_policy='propagate')
+ # return result #770 760 780 sigma 15
+ def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range):
x,y = self.background_correction()
gmodel = GaussianModel(prefix = 'g1_') # calling gaussian model
pars = gmodel.guess(y, x=x) # parameters - center, width, height
- pars['g1_center'].set(min=center_min, max=center_max)
+ pars['g1_center'].set(peak_pos, min=min_max_range[0], max=min_max_range[1])
+ pars['g1_sigma'].set(sigma)
result = gmodel.fit(y, pars, x=x, nan_policy='propagate')
- return result
+ return result #770 760 780 sigma 15
class Single_Lorentzian(Spectra_Fit):
"""Fit a single Lorentzian to the spectrum
@@ -79,11 +88,20 @@ def lorentzian_model(self):
result = lmodel.fit(y, pars, x=x, nan_policy='propagate')
return result
- def lorentzian_model_w_lims(self, center_min = None, center_max = None):
+ # def lorentzian_model_w_lims(self, center_min = None, center_max = None):
+ # x,y = self.background_correction()
+ # lmodel = LorentzianModel(prefix = 'l1_') # calling lorentzian model
+ # pars = lmodel.guess(y, x=x) # parameters - center, width, height
+ # pars['l1_center'].set(min = center_min, max = center_max)
+ # result = lmodel.fit(y, pars, x=x, nan_policy='propagate')
+ # return result
+
+ def lorentzian_model_w_lims(self, peak_pos, sigma, min_max_range):
x,y = self.background_correction()
lmodel = LorentzianModel(prefix = 'l1_') # calling lorentzian model
pars = lmodel.guess(y, x=x) # parameters - center, width, height
- pars['l1_center'].set(min = center_min, max = center_max)
+ pars['l1_center'].set(peak_pos, min = min_max_range[0], max = min_max_range[1])
+ pars['l1_sigma'].set(sigma)
result = lmodel.fit(y, pars, x=x, nan_policy='propagate')
return result
@@ -96,18 +114,35 @@ class Double_Gaussian(Spectra_Fit):
"""
def gaussian_model(self):
+
+ x,y = self.background_correction()
+ gmodel_1 = GaussianModel(prefix='g1_') # calling gaussian model
+ pars = gmodel_1.guess(y, x=x) # parameters - center, width, height
+
+ gmodel_2 = GaussianModel(prefix='g2_')
+ pars.update(gmodel_2.make_params()) # update parameters - center, width, height
+
+ gmodel = gmodel_1 + gmodel_2
+ result = gmodel.fit(y, pars, x=x, nan_policy='propagate')
+ return result
+
+ def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range):
+ #center_initial_guesses - list containing initial guesses for peak centers. [center_guess1, center_guess2]
+ #sigma_initial_guesses - list containing initial guesses for sigma. [sigma1, sigma2]
+ #min_max_range - list containing lists of min and max for peak center. [ [min1, max1], [min2, max2] ]
+
x,y = self.background_correction()
gmodel_1 = GaussianModel(prefix='g1_') # calling gaussian model
pars = gmodel_1.guess(y, x=x) # parameters - center, width, height
- pars['g1_center'].set(800, min = 795, max = 820)
- pars['g1_sigma'].set(15)
+ pars['g1_center'].set(peak_pos[0], min = min_max_range[0][0], max = min_max_range[0][1])
+ pars['g1_sigma'].set(sigma[0])
pars['g1_amplitude'].set(min=0)
gmodel_2 = GaussianModel(prefix='g2_')
pars.update(gmodel_2.make_params()) # update parameters - center, width, height
- pars['g2_center'].set(767, min = 760, max = 775)
+ pars['g2_center'].set(peak_pos[1], min = min_max_range[1][0], max = min_max_range[1][1])
+ pars['g2_sigma'].set(sigma[1], min = composite_pars['g1_sigma'].value)
pars['g2_amplitude'].set(min = 0)
- pars['g2_sigma'].set(min = pars['g1_sigma'].value)
gmodel = gmodel_1 + gmodel_2
result = gmodel.fit(y, pars, x=x, nan_policy='propagate')
@@ -115,13 +150,45 @@ def gaussian_model(self):
class Multi_Gaussian(Spectra_Fit):
- def __init__(self, data, ref, num_of_gaussians, peak_pos, min_max_range):
- Spectra_Fit.__init__(self, data, ref)
+ # def __init__(self, data, ref, num_of_gaussians, peak_pos, sigma, min_max_range):
+ # Spectra_Fit.__init__(self, data, ref)
+ # self.num_of_gaussians = num_of_gaussians
+ # self.peak_pos = peak_pos
+ # self.min_max_range = min_max_range
+ def __init__(self, data, ref, num_of_gaussians, wlref=None):
+ Spectra_Fit.__init__(self, data, ref, wlref)
self.num_of_gaussians = num_of_gaussians
+
+ def gaussian_model(self):
+ composite_model = None
+ composite_pars = None
+
+ x,y = self.background_correction()
+
+ for i in range(self.num_of_gaussians):
+
+ model = GaussianModel(prefix='g'+str(i+1)+'_')
+
+ if composite_pars is None:
+ composite_pars = model.guess(y, x=x)
+# composite_pars = model.make_params()
+
+ else:
+ composite_pars.update(model.make_params())
+
+ if composite_model is None:
+ composite_model = model
+ else:
+ composite_model += model
+
+ result = composite_model.fit(y, composite_pars, x=x, nan_policy='propagate')
+ return result
+
+ def gaussian_model_w_lims(self, peak_pos, sigma, min_max_range):
self.peak_pos = peak_pos
+ self.sigma = sigma
self.min_max_range = min_max_range
- def multi_gaussian(self):
composite_model = None
composite_pars = None
@@ -140,14 +207,14 @@ def multi_gaussian(self):
# composite_pars = model.make_params()
composite_pars['g'+str(i+1)+'_center'].set(self.peak_pos[i],
min = self.min_max_range[0][0], max = self.min_max_range[0][1])
- composite_pars['g'+str(i+1)+'_sigma'].set(15)
+ composite_pars['g'+str(i+1)+'_sigma'].set(self.sigma[i])
composite_pars['g'+str(i+1)+'_amplitude'].set(min = 0)
else:
composite_pars.update(model.make_params())
composite_pars['g'+str(i+1)+'_center'].set(self.peak_pos[i],
- min = self.min_max_range[1][0], max = self.min_max_range[1][1])
- composite_pars['g'+str(i+1)+'_sigma'].set(min = composite_pars['g1_sigma'].value)
+ min = self.min_max_range[i][0], max = self.min_max_range[i][1])
+ composite_pars['g'+str(i+1)+'_sigma'].set(self.sigma[i], min = composite_pars['g1_sigma'].value)
composite_pars['g'+str(i+1)+'_amplitude'].set(min = 0)
diff --git a/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py b/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py
index 24dc421..005746a 100644
--- a/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py
+++ b/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit.py
@@ -7,18 +7,22 @@
# system imports
import sys
+import h5py
from pathlib import Path
-
+import os.path
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets#, QColorDialog
import numpy as np
import matplotlib.pyplot as plt
-
+import pickle
+import time
+from lmfit.models import GaussianModel
+import customplotting.mscope as cpm
# local modules
try:
- from Spectra_fit_funcs import Spectra_Fit, Single_Gaussian, Single_Lorentzian
+ from Spectra_fit_funcs import Spectra_Fit, Single_Gaussian, Single_Lorentzian, Double_Gaussian, Multi_Gaussian
except:
- from Spectrum_analysis.Spectra_fit_funcs import Spectra_Fit, Single_Gaussian, Single_Lorentzian
+ from Spectrum_analysis.Spectra_fit_funcs import Spectra_Fit, Single_Gaussian, Single_Lorentzian, Double_Gaussian, Multi_Gaussian
"""Recylce params for plotting"""
@@ -31,6 +35,7 @@
pg.mkQApp()
pg.setConfigOption('background', 'w')
+pg.setConfigOption('imageAxisOrder', 'row-major')
base_path = Path(__file__).parent
file_path = (base_path / "Spectra_plot_fit_gui.ui").resolve()
@@ -40,245 +45,651 @@
WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile)
class MainWindow(TemplateBaseClass):
-
- def __init__(self):
- super(TemplateBaseClass, self).__init__()
-
- # Create the main window
- self.ui = WindowTemplate()
- self.ui.setupUi(self)
-
- self.ui.fitFunc_comboBox.addItems(["Single Gaussian","Single Lorentzian", "Double Gaussian", "Multiple Gaussians"])
-
-# self.ui.actionSave.triggered.connect(self.save_file)
+
+ def __init__(self):
+ super(TemplateBaseClass, self).__init__()
+
+ # Create the main window
+ self.ui = WindowTemplate()
+ self.ui.setupUi(self)
+
+ # self.ui.fitFunc_comboBox.addItems(["Single Gaussian","Single Lorentzian", "Double Gaussian", "Multiple Gaussians"])
# self.ui.actionExit.triggered.connect(self.close_application)
-
- self.ui.importSpec_pushButton.clicked.connect(self.open_file)
- self.ui.importBck_pushButton.clicked.connect(self.open_bck_file)
- self.ui.importWLRef_pushButton.clicked.connect(self.open_wlref_file)
- self.ui.plot_pushButton.clicked.connect(self.plot)
- self.ui.fit_pushButton.clicked.connect(self.fit_and_plot)
- self.ui.config_fit_params_pushButton.clicked.connect(self.configure_fit_params)
- self.ui.clear_pushButton.clicked.connect(self.clear_plot)
- self.ui.export_fig_pushButton.clicked.connect(self.pub_ready_plot_export)
-
- self.file = None
- self.bck_file = None
- self.wlref_file = None
- self.x = None
- self.y = None
- self.out = None # output file after fitting
-
- # Peak parameters if adjust params is selected
- self.center_min = None
- self.center_max = None
-
- self.show()
-
- def open_file(self):
- try:
- filename = QtWidgets.QFileDialog.getOpenFileName(self)
- try:
- self.file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t')
- except:
- self.file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t')
- except:
- pass
-
- def open_bck_file(self):
- try:
- filename = QtWidgets.QFileDialog.getOpenFileName(self)
- try:
- self.bck_file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t')
- except:
- self.bck_file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t')
- except:
- pass
-
- def open_wlref_file(self):
- try:
- filename = QtWidgets.QFileDialog.getOpenFileName(self)
- try:
- self.wlref_file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t')
- except:
- self.wlref_file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t')
- except:
- pass
-
- def save_file(self):
- try:
- filename = QtWidgets.QFileDialog.getSaveFileName(self)
- np.savetxt(filename[0], self.out, fmt = '%.5f', header = 'Time, Raw_PL, Sim_PL', delimiter = ' ')
- except:
- pass
-
- def plot(self):
- try:
- self.x = self.file[:,0]
- self.y = self.file[:,1]
-
- if self.ui.subtract_bck_checkBox.isChecked() == True and self.ui.WLRef_checkBox.isChecked() == False:
- bck_y = self.bck_file[:,1]
- self.y = self.y - bck_y
-
- elif self.ui.subtract_bck_checkBox.isChecked() == False and self.ui.WLRef_checkBox.isChecked() == True:
- wlref_y = self.wlref_file[:,1]
- self.y = (self.y)/wlref_y
-
- elif self.ui.subtract_bck_checkBox.isChecked() == True and self.ui.WLRef_checkBox.isChecked() == True:
- bck_y = self.bck_file[:,1]
- wlref_y = self.wlref_file[:,1]
- self.y = (self.y-bck_y)/wlref_y
-
-
- if self.ui.norm_checkBox.isChecked():
- self.normalize()
-
- self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
-
- except:
- pass
- self.ui.plot.setLabel('left', 'Intensity', units='a.u.')
- self.ui.plot.setLabel('bottom', 'Wavelength (nm)')
-
-
- def normalize(self):
- self.y = (self.y) / np.amax(self.y)
-
- def clear_plot(self):
- self.ui.plot.clear()
- self.ui.result_textBrowser.clear()
-
- def clear_check(self):
- if self.ui.clear_checkBox.isChecked() == True:
- return True
- elif self.ui.clear_checkBox.isChecked() == False:
- return False
-
- """Open param window and get peak center range values and assign it to variables to use later"""
- def configure_fit_params(self):
- self.param_window = ParamWindow()
- self.param_window.peak_range.connect(self.peak_range)
-
- def peak_range(self, peaks):
- self.center_min = peaks[0]
- self.center_max = peaks[1]
-
-
- def fit_and_plot(self):
- fit_func = self.ui.fitFunc_comboBox.currentText()
-
- try:
-
- if self.ui.subtract_bck_checkBox.isChecked() == False:
- self.ui.result_textBrowser.setText("You need to check the subtract background option!")
-
- elif self.wlref_file is not None and self.ui.WLRef_checkBox.isChecked() == False:
- self.ui.result_textBrowser.setText("You need to check the White Light Correction option!")
-
- else:
- if fit_func == "Single Gaussian" and self.ui.subtract_bck_checkBox.isChecked() == True:
-
- single_gauss = Single_Gaussian(self.file, self.bck_file, wlref=self.wlref_file)
-
- if self.ui.adjust_param_checkBox.isChecked():
- self.result = single_gauss.gaussian_model_w_lims(
- center_min=self.center_min, center_max=self.center_max)
- else:
- self.result = single_gauss.gaussian_model()
- self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
- self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k')
- self.ui.result_textBrowser.setText(self.result.fit_report())
-
- elif fit_func == "Single Lorentzian" and self.ui.subtract_bck_checkBox.isChecked() == True:
-
- single_lorentzian = Single_Lorentzian(self.file, self.bck_file, wlref=self.wlref_file)
-
- if self.ui.adjust_param_checkBox.isChecked():
- self.result = single_lorentzian.lorentzian_model_w_lims(
- center_min = self.center_min, center_max = self.center_max)
- else:
- self.result = single_lorentzian.lorentzian_model()
- self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
- self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k')
- self.ui.result_textBrowser.setText(self.result.fit_report())
-
- elif fit_func == "Double Gaussian" and self.ui.subtract_bck_checkBox.isChecked() == True:
- self.ui.result_textBrowser.setText("Not Implemented Yet!")
-
- elif fit_func == "Multiple Gaussians" and self.ui.subtract_bck_checkBox.isChecked() == True:
- self.ui.result_textBrowser.setText("Not Implemented Yet!")
-
- except Exception as e:
- self.ui.result_textBrowser.setText(str(e))
-
-
- def pub_ready_plot_export(self):
- filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION")
- try:
- plt.figure(figsize=(8,6))
- plt.tick_params(direction='out', length=8, width=3.5)
- plt.plot(self.x, self.y)
- plt.plot(self.x, self.result.best_fit,'k')
- plt.xlabel("Wavelength (nm)", fontsize=20, fontweight='bold')
- plt.ylabel("Intensity (a.u.)", fontsize=20, fontweight='bold')
- plt.tight_layout()
-
- plt.savefig(filename[0],bbox_inches='tight', dpi=300)
- plt.close()
-
- except AttributeError:
- self.ui.result_textBrowser.setText("Need to fit the data first!")
-
-
- def close_application(self):
- choice = QtGui.QMessageBox.question(self, 'EXIT!',
- "Do you want to exit the app?",
- QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
- if choice == QtGui.QMessageBox.Yes:
- sys.exit()
- else:
- pass
-
-
+
+ ##setup ui signals
+ self.ui.importSpec_pushButton.clicked.connect(self.open_file)
+ self.ui.importBck_pushButton.clicked.connect(self.open_bck_file)
+ self.ui.importWLRef_pushButton.clicked.connect(self.open_wlref_file)
+ self.ui.load_spectra_scan_pushButton.clicked.connect(self.open_spectra_scan_file)
+ self.ui.load_bck_file_pushButton.clicked.connect(self.open_spectra_bck_file)
+ self.ui.load_fitted_scan_pushButton.clicked.connect(self.open_fit_scan_file)
+
+ self.ui.plot_pushButton.clicked.connect(self.plot)
+ self.ui.plot_fit_scan_pushButton.clicked.connect(self.plot_fit_scan)
+ self.ui.plot_raw_scan_pushButton.clicked.connect(self.plot_raw_scan)
+ self.ui.plot_intensity_sums_pushButton.clicked.connect(self.plot_intensity_sums)
+
+ self.ui.fit_pushButton.clicked.connect(self.fit_and_plot)
+ self.ui.fit_scan_pushButton.clicked.connect(self.fit_and_plot_scan)
+ # self.ui.config_fit_params_pushButton.clicked.connect(self.configure_fit_params)
+ self.ui.clear_pushButton.clicked.connect(self.clear_plot)
+ self.ui.export_fig_pushButton.clicked.connect(self.pub_ready_plot_export)
+
+ self.ui.import_pkl_pushButton.clicked.connect(self.open_pkl_file)
+ self.ui.data_txt_pushButton.clicked.connect(self.pkl_data_to_txt)
+ self.ui.scan_params_txt_pushButton.clicked.connect(self.pkl_params_to_txt)
+
+ self.ui.pkl_to_h5_pushButton.clicked.connect(self.pkl_to_h5)
+
+ self.ui.tabWidget.currentChanged.connect(self.switch_overall_tab)
+ self.ui.fitFunc_comboBox.currentTextChanged.connect(self.switch_bounds_and_guess_tab)
+ self.ui.adjust_param_checkBox.stateChanged.connect(self.switch_adjust_param)
+
+ # for i in reversed(range(self.ui.bounds_groupBox.layout().count())):
+ # self.ui.bounds_groupBox.layout().itemAt(i).widget().deleteLater()
+ #self.ui.single_bounds_page.layout().addWidget(QtWidgets.QPushButton("test"))
+
+ self.file = None
+ self.bck_file = None
+ self.wlref_file = None
+ self.x = None
+ self.y = None
+ self.out = None # output file after fitting
+
+ # Peak parameters if adjust params is selected
+ self.center_min = None
+ self.center_max = None
+
+ self.show()
+
+ """ Open Single Spectrum files """
+ def open_file(self):
+ try:
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ try:
+ self.file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t')
+ except:
+ self.file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t')
+ except:
+ pass
+
+ def open_bck_file(self):
+ try:
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ try:
+ self.bck_file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t')
+ except:
+ self.bck_file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t')
+ except Exception as e:
+ self.ui.result_textBrowser.append(str(e))
+ pass
+
+ def open_wlref_file(self):
+ try:
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ try:
+ self.wlref_file = np.loadtxt(filename[0], skiprows = 16, delimiter='\t')
+ except:
+ self.wlref_file = np.genfromtxt(filename[0], skip_header=1, skip_footer=3, delimiter='\t')
+ except:
+ pass
+
+ """Open Scan Files"""
+ def open_spectra_scan_file(self):
+ try:
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.spec_scan_file = pickle.load(open(filename[0], 'rb'))
+ self.ui.result_textBrowser2.append("Done Loading - Spectra Scan File")
+ except Exception as e:
+ self.ui.result_textBrowser2.append(str(e))
+ pass
+
+ def open_spectra_bck_file(self):
+ try:
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.bck_file = np.loadtxt(filename[0])#, skiprows=1, delimiter=None)
+ self.ui.result_textBrowser2.append("Done Loading - Background File")
+ except Exception as e:
+ self.ui.result_textBrowser2.append(str(e))
+ pass
+
+ def open_fit_scan_file(self):
+ try:
+ filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.fit_scan_file = pickle.load(open(filename[0], 'rb'))
+ self.ui.result_textBrowser2.append("Done Loading - Scan Fit File")
+ except Exception as e:
+ self.ui.result_textBrowser2.append(str(e))
+ pass
+
+ def open_pkl_file(self):
+ """ Open pkl file to convert to txt """
+ try:
+ self.pkl_to_convert = QtWidgets.QFileDialog.getOpenFileName(self)
+ except:
+ pass
+
+ def switch_overall_tab(self):
+ """ Enable/disable fit settings on right depending on current tab """
+ if self.ui.tabWidget.currentIndex() == 0:
+ self.ui.fitting_settings_groupBox.setEnabled(True)
+ self.ui.fit_pushButton.setEnabled(True)
+ self.ui.fit_scan_pushButton.setEnabled(True)
+ self.ui.scan_fit_settings_groupBox.setEnabled(False)
+ elif self.ui.tabWidget.currentIndex() == 1:
+ self.ui.fitting_settings_groupBox.setEnabled(False)
+ self.ui.fit_pushButton.setEnabled(False)
+ self.ui.fit_scan_pushButton.setEnabled(True)
+ self.ui.scan_fit_settings_groupBox.setEnabled(True)
+ elif self.ui.tabWidget.currentIndex() == 2:
+ self.ui.fitting_settings_groupBox.setEnabled(False)
+ self.ui.fit_pushButton.setEnabled(False)
+ self.ui.fit_scan_pushButton.setEnabled(False)
+ self.ui.scan_fit_settings_groupBox.setEnabled(False)
+
+ """ Single spectrum functions """
+ def switch_bounds_and_guess_tab(self):
+ """ Show the appropriate bounds and initial guess params based on fit function """
+ fit_func = self.ui.fitFunc_comboBox.currentText()
+ if fit_func == "Single Gaussian" or fit_func == "Single Lorentzian":
+ self.ui.n_label.setEnabled(False)
+ self.ui.n_spinBox.setEnabled(False)
+ self.ui.bounds_stackedWidget.setCurrentIndex(0)
+ self.ui.guess_stackedWidget.setCurrentIndex(0)
+ self.ui.plot_components_checkBox.setEnabled(False)
+ self.ui.n_spinBox.setValue(1)
+ elif fit_func == "Double Gaussian":
+ self.ui.n_label.setEnabled(False)
+ self.ui.n_spinBox.setEnabled(False)
+ self.ui.bounds_stackedWidget.setCurrentIndex(1)
+ self.ui.guess_stackedWidget.setCurrentIndex(1)
+ self.ui.plot_components_checkBox.setEnabled(True)
+ self.ui.n_spinBox.setValue(2)
+ elif fit_func == "Triple Gaussian":
+ self.ui.n_label.setEnabled(False)
+ self.ui.n_spinBox.setEnabled(False)
+ self.ui.bounds_stackedWidget.setCurrentIndex(2)
+ self.ui.guess_stackedWidget.setCurrentIndex(2)
+ self.ui.plot_components_checkBox.setEnabled(True)
+ self.ui.n_spinBox.setValue(3)
+
+ def switch_adjust_param(self):
+ """ Enable bounds and initial guess only when adjust parameters is checked """
+ checked = self.ui.adjust_param_checkBox.isChecked()
+ self.ui.bounds_groupBox.setEnabled(checked)
+ self.ui.guess_groupBox.setEnabled(checked)
+
+ def plot(self):
+ try:
+ self.x = self.file[:,0]
+ self.y = self.file[:,1]
+
+ if self.ui.subtract_bck_checkBox.isChecked() == True and self.ui.WLRef_checkBox.isChecked() == False:
+ bck_y = self.bck_file[:,1]
+ self.y = self.y - bck_y
+
+ elif self.ui.subtract_bck_checkBox.isChecked() == False and self.ui.WLRef_checkBox.isChecked() == True:
+ wlref_y = self.wlref_file[:,1]
+ self.y = (self.y)/wlref_y
+
+ elif self.ui.subtract_bck_checkBox.isChecked() == True and self.ui.WLRef_checkBox.isChecked() == True:
+ bck_y = self.bck_file[:,1]
+ wlref_y = self.wlref_file[:,1]
+ self.y = (self.y-bck_y)/wlref_y
+
+
+ if self.ui.norm_checkBox.isChecked():
+ self.normalize()
+
+ self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
+
+ except Exception as err:
+ pass
+ self.ui.plot.setLabel('left', 'Intensity', units='a.u.')
+ self.ui.plot.setLabel('bottom', 'Wavelength (nm)')
+
+ def normalize(self):
+ self.y = (self.y) / np.amax(self.y)
+
+ def clear_plot(self):
+ self.ui.plot.clear()
+ self.ui.result_textBrowser.clear()
+
+ def clear_check(self):
+ if self.ui.clear_checkBox.isChecked() == True:
+ return True
+ elif self.ui.clear_checkBox.isChecked() == False:
+ return False
+
+ """Open param window and get peak center range values and assign it to variables to use later"""
+ # def configure_fit_params(self):
+ # self.param_window = ParamWindow()
+ # self.param_window.peak_range.connect(self.peak_range)
+
+ def peak_range(self, peaks):
+ self.center_min = peaks[0]
+ self.center_max = peaks[1]
+
+
+ def fit_and_plot(self):
+ fit_func = self.ui.fitFunc_comboBox.currentText()
+
+ try:
+
+ if self.ui.subtract_bck_checkBox.isChecked() == False:
+ self.ui.result_textBrowser.setText("You need to check the subtract background option!")
+
+ elif self.wlref_file is not None and self.ui.WLRef_checkBox.isChecked() == False:
+ self.ui.result_textBrowser.setText("You need to check the White Light Correction option!")
+
+ else:
+ if fit_func == "Single Gaussian" and self.ui.subtract_bck_checkBox.isChecked() == True:
+ single_gauss = Single_Gaussian(self.file, self.bck_file, wlref=self.wlref_file)
+ if self.ui.adjust_param_checkBox.isChecked():
+ center1_min = self.ui.single_peakcenter1_min_spinBox.value()
+ center1_max = self.ui.single_peakcenter1_max_spinBox.value()
+ center1_guess = self.ui.single_peakcenter1_guess_spinBox.value()
+ sigma1_guess = self.ui.single_sigma1_guess_spinBox.value()
+ self.result = single_gauss.gaussian_model_w_lims(center1_guess, sigma1_guess,
+ [center1_min, center1_max])
+ else:
+ self.result = single_gauss.gaussian_model()
+ self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
+ self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k')
+ self.ui.result_textBrowser.setText(self.result.fit_report())
+
+ elif fit_func == "Single Lorentzian" and self.ui.subtract_bck_checkBox.isChecked() == True:
+ single_lorentzian = Single_Lorentzian(self.file, self.bck_file, wlref=self.wlref_file)
+
+ if self.ui.adjust_param_checkBox.isChecked():
+ center1_min = self.ui.single_peakcenter1_min_spinBox.value()
+ center1_max = self.ui.single_peakcenter1_max_spinBox.value()
+ center1_guess = self.ui.single_peakcenter1_guess_spinBox.value()
+ sigma1_guess = self.ui.single_sigma1_guess_spinBox.value()
+ self.result = single_lorentzian.lorentzian_model_w_lims(center1_guess, sigma1_guess,
+ [center1_min, center1_max])
+ else:
+ self.result = single_lorentzian.lorentzian_model()
+ self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
+ self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k')
+ self.ui.result_textBrowser.setText(self.result.fit_report())
+
+ elif fit_func == "Double Gaussian" and self.ui.subtract_bck_checkBox.isChecked() == True:
+ double_gauss = Double_Gaussian(self.file, self.bck_file, wlref=self.wlref_file)
+ if self.ui.adjust_param_checkBox.isChecked():
+ center1_min = self.ui.double_peakcenter1_min_spinBox.value()
+ center1_max = self.ui.double_peakcenter1_max_spinBox.value()
+ center2_min = self.ui.double_peakcenter2_min_spinBox.value()
+ center2_max = self.ui.double_peakcenter2_max_spinBox.value()
+ center1_guess = self.ui.double_peakcenter1_guess_spinBox.value()
+ sigma1_guess = self.ui.double_sigma1_guess_spinBox.value()
+ center2_guess = self.ui.double_peakcenter2_guess_spinBox.value()
+ sigma2_guess = self.ui.double_sigma2_guess_spinBox.value()
+
+ peak_pos = [center1_guess, center2_guess]
+ sigma = [sigma1_guess, sigma2_guess]
+ min_max_range = [ [center1_min, center1_max], [center2_min, center2_max] ]
+ self.result = double_gauss.gaussian_model_w_lims(peak_pos, sigma, min_max_range)
+
+ else:
+ self.result = double_gauss.gaussian_model()
+
+ self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
+ self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k')
+ if self.ui.plot_components_checkBox.isChecked():
+ comps = self.result.eval_components(x=self.x)
+ self.ui.plot.plot(self.x, comps['g1_'], pen='b', clear=False)
+ self.ui.plot.plot(self.x, comps['g2_'], pen='g', clear=False)
+
+ self.ui.result_textBrowser.setText(self.result.fit_report())
+
+ elif fit_func == "Triple Gaussians" and self.ui.subtract_bck_checkBox.isChecked() == True:
+ #currently only works for triple gaussian (n=3)
+ multiple_gauss = Multi_Gaussian(self.file, self.bck_file, 3, wlref=self.wlref_file)
+ if self.ui.adjust_param_checkBox.isChecked():
+ center1_min = self.ui.multi_peakcenter1_min_spinBox.value()
+ center1_max = self.ui.multi_peakcenter1_max_spinBox.value()
+ center2_min = self.ui.multi_peakcenter2_min_spinBox.value()
+ center2_max = self.ui.multi_peakcenter2_max_spinBox.value()
+ center3_min = self.ui.multi_peakcenter3_min_spinBox.value()
+ center3_max = self.ui.multi_peakcenter3_max_spinBox.value()
+ center1_guess = self.ui.multi_peakcenter1_guess_spinBox.value()
+ sigma1_guess = self.ui.multi_sigma1_guess_spinBox.value()
+ center2_guess = self.ui.multi_peakcenter2_guess_spinBox.value()
+ sigma2_guess = self.ui.multi_sigma2_guess_spinBox.value()
+ center3_guess = self.ui.multi_peakcenter3_guess_spinBox.value()
+ sigma3_guess = self.ui.multi_sigma3_guess_spinBox.value()
+ num_gaussians = 3
+ peak_pos = [center1_guess, center2_guess, center3_guess]
+ sigma = [sigma1_guess, sigma2_guess, sigma3_guess]
+ min_max_range = [ [center1_min, center1_max], [center2_min, center2_max], [center3_min, center3_max] ]
+
+ self.result = multiple_gauss.gaussian_model_w_lims(peak_pos, sigma, min_max_range)
+ else:
+ self.result = multiple_gauss.gaussian_model()
+
+ self.ui.plot.plot(self.x, self.y, clear=self.clear_check(), pen='r')
+ self.ui.plot.plot(self.x, self.result.best_fit, clear=False, pen='k')
+ if self.ui.plot_components_checkBox.isChecked():
+ comps = self.result.eval_components(x=self.x)
+ self.ui.plot.plot(self.x, comps['g1_'], pen='b', clear=False)
+ self.ui.plot.plot(self.x, comps['g2_'], pen='g', clear=False)
+ self.ui.plot.plot(self.x, comps['g3_'], pen='c', clear=False)
+ self.ui.result_textBrowser.setText(self.result.fit_report())
+
+
+ except Exception as e:
+ self.ui.result_textBrowser.setText(str(e))
+
+ def pub_ready_plot_export(self):
+ filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION")
+ try:
+ try:
+ data = self.spec_scan_file
+ param_selection = str(self.ui.comboBox.currentText())
+ if param_selection == 'pk_pos': label = 'PL Peak Position (n.m.)'
+ elif param_selection == 'fwhm': label = 'PL FWHM (n.m.)'
+ cpm.plot_confocal(self.img, figsize=(10,10), stepsize = data['Scan Parameters']['X step size (um)'], cmap="seismic", cbar_label=label)
+ plt.savefig(filename[0],bbox_inches='tight', dpi=300)
+ plt.close()
+ except:
+ plt.figure(figsize=(8,6))
+ plt.tick_params(direction='out', length=8, width=3.5)
+ plt.plot(self.x, self.y)
+ plt.plot(self.x, self.result.best_fit,'k')
+ plt.xlabel("Wavelength (nm)", fontsize=20, fontweight='bold')
+ plt.ylabel("Intensity (a.u.)", fontsize=20, fontweight='bold')
+ plt.tight_layout()
+
+ plt.savefig(filename[0],bbox_inches='tight', dpi=300)
+ plt.close()
+
+ except AttributeError:
+ self.ui.result_textBrowser.setText("Need to fit the data first!")
+
+
+ """ Scan spectra functions """
+ def plot_fit_scan(self):
+ try:
+ if self.ui.use_raw_scan_settings.isChecked():
+ data = self.spec_scan_file
+ num_x = int((data['Scan Parameters']['X scan size (um)'])/(data['Scan Parameters']['X step size (um)']))
+ num_y = int((data['Scan Parameters']['Y scan size (um)'])/(data['Scan Parameters']['Y step size (um)']))
+ else:
+ num_x = self.ui.num_x_spinBox.value()
+ num_y = self.ui.num_y_spinBox.value()
+
+ numb_of_points = num_x * num_y #75*75
+
+ fwhm = np.zeros(shape=(numb_of_points,1))
+ pk_pos = np.zeros(shape=(numb_of_points,1))
+# pk_pos_plus = np.zeros(shape=(numb_of_points,1))
+# pk_pos_minus = np.zeros(shape=(numb_of_points,1))
+ sigma = np.zeros(shape=(numb_of_points,1))
+ height = np.zeros(shape=(numb_of_points,1))
+
+ for i in range(numb_of_points):
+ fwhm[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_fwhm']
+ pk_pos[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_center']
+ sigma[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_sigma']
+ height[i, 0] = self.fit_scan_file['result_'+str(i)].values['g1_height']
+
+ newshape = (num_x, num_y)
+
+ param_selection = str(self.ui.comboBox.currentText())
+ self.img = np.reshape(eval(param_selection), newshape)
+
+ if self.ui.use_raw_scan_settings.isChecked():
+ self.ui.fit_scan_viewbox.setImage(self.img, scale=
+ (data['Scan Parameters']['X step size (um)'],
+ data['Scan Parameters']['Y step size (um)']))
+ scale = pg.ScaleBar(size=2,suffix='um')
+ scale.setParentItem(self.ui.fit_scan_viewbox.view)
+ scale.anchor((1, 1), (1, 1), offset=(-30, -30))
+ else:
+ self.ui.fit_scan_viewbox.setImage(self.img)
+
+ self.ui.fit_scan_viewbox.view.invertY(False)
+
+ except Exception as e:
+ self.ui.result_textBrowser2.append(str(e))
+ pass
+
+ def plot_raw_scan(self):
+ try:
+ data = self.spec_scan_file
+ numb_pixels_X = int((data['Scan Parameters']['X scan size (um)'])/(data['Scan Parameters']['X step size (um)']))
+ numb_pixels_Y = int((data['Scan Parameters']['Y scan size (um)'])/(data['Scan Parameters']['Y step size (um)']))
+ # TODO test line scan plots
+
+ intensities = data['Intensities'].T #this is only there because of how we are saving the data in the app
+
+ intensities = np.reshape(intensities, newshape=(2048,numb_pixels_X,numb_pixels_Y))
+
+ wavelengths = data['Wavelengths']
+
+ self.ui.raw_scan_viewbox.view.invertY(False)
+ self.ui.raw_scan_viewbox.setImage(intensities, scale=
+ (data['Scan Parameters']['X step size (um)'],
+ data['Scan Parameters']['Y step size (um)']), xvals=wavelengths)
+
+
+ #roi_plot = self.ui.raw_scan_viewBox.getRoiPlot()
+ #roi_plot.plot(data['Wavelengths'], intensities)
+ scale = pg.ScaleBar(size=2,suffix='um')
+ scale.setParentItem(self.ui.raw_scan_viewbox.view)
+ scale.anchor((1, 1), (1, 1), offset=(-30, -30))
+
+ except Exception as e:
+ self.ui.result_textBrowser2.append(str(e))
+
+ def plot_intensity_sums(self):
+ try:
+ data = self.spec_scan_file
+ numb_pixels_X = int((data['Scan Parameters']['X scan size (um)'])/(data['Scan Parameters']['X step size (um)']))
+ numb_pixels_Y = int((data['Scan Parameters']['Y scan size (um)'])/(data['Scan Parameters']['Y step size (um)']))
+ # TODO test line scan plots
+
+ intensities = data['Intensities']
+
+ #intensities = np.reshape(intensities, newshape=(2048, numb_pixels_X*numb_pixels_Y))
+
+ sums = np.sum(intensities, axis=-1)
+ sums = np.reshape(sums, newshape=(numb_pixels_X, numb_pixels_Y))
+
+ self.ui.intensity_sums_viewBox.setImage(sums, scale=
+ (data['Scan Parameters']['X step size (um)'],
+ data['Scan Parameters']['Y step size (um)']))
+ self.ui.intensity_sums_viewBox.view.invertY(False)
+
+ scale = pg.ScaleBar(size=2,suffix='um')
+ scale.setParentItem(self.ui.intensity_sums_viewBox.view)
+ scale.anchor((1, 1), (1, 1), offset=(-30, -30))
+
+ except Exception as e:
+ self.ui.result_textBrowser2.append(str(e))
+
+
+ def fit_and_plot_scan(self):
+# self.ui.result_textBrowser.append("Starting Scan Fitting")
+ print("Starting Scan Fitting")
+
+ try:
+ """Define starting and stopping wavelength values here"""
+ start_nm = int(self.ui.start_nm_spinBox.value())
+ stop_nm = int(self.ui.stop_nm_spinBox.value())
+
+ ref = self.bck_file
+ index = (ref[:,0]>start_nm) & (ref[:,0]start_nm) & (x
0
0
- 1816
- 1562
+ 1728
+ 1052
+
+
+ 0
+ 0
+
+
MainWindow
-
+
-
-
+
+
-
+
+
+
+ 12
+
+
+
+ Fit Settings
+
+
+
-
+
+
+ false
+
+
+
+ 10
+
+
+
+ n:
+
+
+
+ -
+
+
+ false
+
+
+
+ 10
+
+
+
+ 1
+
+
+ 1
+
+
+
+ -
+
+
+ false
+
+
+
+ 10
+
+
+
+ Plot components (>1 Gaussian)
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 10
+
+
+
+ Bounds
+
+
+
-
+
+
+ 0
+
+
+
+
-
+
+
+
+
+
+
+ -
+
+
+ max
+
+
+
+ -
+
+
+ min
+
+
+
+ -
+
+
+ Peak Center 1 (nm)
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 760.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 780.000000000000000
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ min
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 760.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 820.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 795.000000000000000
+
+
+
+ -
+
+
+ Peak Center 2 (nm)
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 775.000000000000000
+
+
+
+ -
+
+
+ max
+
+
+
+ -
+
+
+ Peak Center 1 (nm)
+
+
+
+
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 760.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 820.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 775.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 755.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 740.000000000000000
+
+
+
+ -
+
+
+ Peak Center 1 (nm)
+
+
+
+ -
+
+
+ max
+
+
+
+ -
+
+
+ Peak Center 2 (nm)
+
+
+
+ -
+
+
+ Peak Center 3 (nm)
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 795.000000000000000
+
+
+
+ -
+
+
+ min
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Adjust Parameters
+
+
+
+ -
+
+
+
+ 10
+
+
+
-
+
+ Single Gaussian
+
+
+ -
+
+ Single Lorentzian
+
+
+ -
+
+ Double Gaussian
+
+
+ -
+
+ Triple Gaussian
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 10
+
+
+
+ Initial Guess
+
+
+
-
+
+
+ 0
+
+
+
+
-
+
+
+ 9999.000000000000000
+
+
+ 770.000000000000000
+
+
+
+ -
+
+
+ Peak Center 1 (nm)
+
+
+
+ -
+
+
+ Sigma 1 (nm)
+
+
+
+ -
+
+
+ 15.000000000000000
+
+
+
+
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 767.000000000000000
+
+
+
+ -
+
+
+ 15.000000000000000
+
+
+
+ -
+
+
+ Peak Center 1 (nm)
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 800.000000000000000
+
+
+
+ -
+
+
+ Sigma 1 (nm)
+
+
+
+ -
+
+
+ Peak Center 2 (nm)
+
+
+
+ -
+
+
+ Sigma 2 (nm)
+
+
+
+ -
+
+
+ 15.000000000000000
+
+
+
+
+
+
+
+ -
+
+
+ Peak Center 2 (nm)
+
+
+
+ -
+
+
+ Sigma 2 (nm)
+
+
+
+ -
+
+
+ Peak Center 3 (nm)
+
+
+
+ -
+
+
+ Sigma 3 (nm)
+
+
+
+ -
+
+
+ Peak Center 1 (nm)
+
+
+
+ -
+
+
+ Sigma 1 (nm)
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 800.000000000000000
+
+
+
+ -
+
+
+ 15.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 767.000000000000000
+
+
+
+ -
+
+
+ 15.000000000000000
+
+
+
+ -
+
+
+ 9999.000000000000000
+
+
+ 750.000000000000000
+
+
+
+ -
+
+
+ 15.000000000000000
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Fit Single Spectrum
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Fit Entire Scan
+
+
+
+ -
+
+
+ false
+
+
+
+ 12
+
+
+
+ Scan Fit Settings
+
+
+
-
+
+
+
+ 10
+
+
+
+ Stop at (nm):
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ 300
+
+
+ 1100
+
+
+ 600
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Start at (nm):
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Qt::ImhDigitsOnly
+
+
+ 300
+
+
+ 1100
+
+
+ 900
+
+
+
+
+
+
+
-
-
+
15
-
- Settings
+
+ 0
-
-
-
-
-
-
- 12
-
-
-
- Spectrum
+
+ false
+
+
+
+ Single Spectrum
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 350
+ 16777215
+
+
+
+
+ 12
+
+
+
+ Load Settings
+
+
+
-
+
+
+
+ 10
+
+
+
+ For Single Spectrum
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Spectrum
File
-
-
-
- -
-
-
-
- 12
-
-
-
- Background
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Background
File
-
-
-
- -
-
-
-
- 12
-
-
-
- White Light
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ White Light
Ref File
-
-
-
- -
-
-
-
- 12
-
-
-
- Subtract Background
-
-
-
- -
-
-
-
- 12
-
-
-
- Correct for White Light
-
-
-
- -
-
-
-
- 12
-
-
-
- Normalize
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 12
- 50
- false
-
-
-
- Plot
-
-
-
- -
-
-
-
- 12
-
-
-
- Clear Plots Everytime
-
-
- true
-
-
-
- -
-
-
-
- 12
- false
-
-
-
- Clear Plot
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 12
-
-
-
- Fitting Settings
-
-
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Subtract Background
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Correct for White Light
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Normalize
+
+
+
+ -
+
+
+
+ 10
+ 50
+ false
+
+
+
+ Plot
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Clear Plots Everytime
+
+
+ true
+
+
+
+ -
+
+
+
+ 10
+ false
+
+
+
+ Clear Plot
+
+
+
+ -
+
+
+
+ 10
+
+
+
+ Export Publication
+Ready Figure
+
+
+
+ -
+
+
+
+ 10
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ Scan Spectra Data
+
+
+ -
+
+
-
+
+
-
+
+
+
+ 15
+
+
+
+ For Raw Scan Data
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Plot
+
+
+
+
+
-
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 300
+
+
+
+
+
+
+ -
+
+
-
+
- 12
+ 15
+
-
+
+ pk_pos
+
+
+ -
+
+ fwhm
+
+
+ -
+
+ sigma
+
+
+ -
+
+ height
+
+
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 300
+
+
+
+
+ -
+
+
+ 2000
+
+
+ 100
+
+
+
+ -
+
- Configure Fit Params
+ # X points
- -
-
+
-
+
+
+ # Y points
+
+
+
+ -
+
12
- Adjust Parameters
+ Plot
- -
-
+
-
+
+
+ 2000
+
+
+ 100
+
+
+
+ -
+
+
+ Use Raw Scan Settings
+
+
+ true
+
+
+
+ -
+
- 12
+ 15
- Fit
+ After Fitting Scan Data
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
+
+ -
+
+
-
+
+
-
+
+
+
+ 15
+
+
+
+ Intensity Sums
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Plot
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 300
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 210
+ 16777215
+
+
+
+
+ 15
+
+
+
+ Load Settings
+
+
+
-
+
+
+
+ 12
+
+
+
+ For Scan Data
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Spectra Scan
+File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Background
+File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Load Only
+ Fit File
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ .pkl conversion
+
+
+
+
+ 10
+ 70
+ 351
+ 251
+
+
+
+ txt
+
+
+ -
+
+
+
+ 12
+
+
+
+ Data to .txt
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Scan params to .txt
+
+
+
+
+
+
+
+
+ 390
+ 70
+ 321
+ 251
+
+
+
+ h5
+
+
+
+
+ 50
+ 80
+ 221
+ 41
+
-
-
- -
-
12
- Export Publication
-Ready Figure
+ .pkl to .h5
-
-
-
-
- -
-
-
-
- 10
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 15
-
-
-
- Fit Results
-
+
+
+
+
+ 20
+ 20
+ 323
+ 40
+
+
+
+
+ 12
+
+
+
+ Import .pkl file
+
+
+
@@ -296,8 +1321,8 @@ Ready Figure
0
0
- 1816
- 21
+ 1728
+ 31
@@ -309,6 +1334,11 @@ Ready Figure
QGraphicsView
+
+ ImageView
+ QGraphicsView
+
+
diff --git a/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit_gui_old.ui b/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit_gui_old.ui
new file mode 100644
index 0000000..2ec3fe0
--- /dev/null
+++ b/PythonGUI_apps/Spectrum_analysis/Spectra_plot_fit_gui_old.ui
@@ -0,0 +1,807 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1430
+ 885
+
+
+
+ MainWindow
+
+
+
+
+
+ 1135
+ 30
+ 281
+ 311
+
+
+
+
+ 15
+
+
+
+ Fitting Settings
+
+
+ -
+
+
+
+ 12
+
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Configure Fit Params
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Start at (nm):
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ 300
+
+
+ 1100
+
+
+ 600
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Stop at (nm):
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ 300
+
+
+ 1100
+
+
+ 900
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Adjust Parameters
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Fit Single Spectrum
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Fit Entire Scan
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Export Publication
+Ready Figure
+
+
+
+
+
+
+
+
+ 1140
+ 350
+ 281
+ 481
+
+
+
+
+ 10
+
+
+
+
+
+
+ 10
+ 10
+ 1153
+ 605
+
+
+
+
+ 15
+
+
+
+ 1
+
+
+ false
+
+
+
+ Single Spectrum
+
+
+
+
+ 20
+ 20
+ 204
+ 431
+
+
+
+
+ 15
+
+
+
+ Load Settings
+
+
+ -
+
+
+
+ 12
+
+
+
+ For Single Spectrum
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Spectrum
+File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Background
+File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ White Light
+Ref File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Subtract Background
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Correct for White Light
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Normalize
+
+
+
+ -
+
+
+
+ 12
+ 50
+ false
+
+
+
+ Plot
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Clear Plots Everytime
+
+
+ true
+
+
+
+ -
+
+
+
+ 12
+ false
+
+
+
+ Clear Plot
+
+
+
+
+
+
+
+
+ 250
+ 50
+ 851
+ 401
+
+
+
+
+
+
+ 250
+ 20
+ 178
+ 24
+
+
+
+
+ 15
+
+
+
+ For Single Spectrum
+
+
+
+
+
+ Scan Spectra Data
+
+
+
+
+ 200
+ 60
+ 521
+ 331
+
+
+
+
+
+
+ 220
+ 20
+ 181
+ 24
+
+
+
+
+ 15
+
+
+
+ For Raw Scan Data
+
+
+
+
+
+ 400
+ 20
+ 101
+ 31
+
+
+
+
+ 12
+
+
+
+ Plot
+
+
+
+
+
+ 200
+ 460
+ 521
+ 331
+
+
+
+
+
+
+ 560
+ 410
+ 81
+ 31
+
+
+
+
+ 12
+
+
+
+ Plot
+
+
+
+
+
+ 210
+ 410
+ 211
+ 31
+
+
+
+
+ 15
+
+
+
+ After Fitting Scan Data
+
+
+
+
+
+ 420
+ 410
+ 131
+ 31
+
+
+
+
+ 15
+
+
+ -
+
+ pk_pos
+
+
+ -
+
+ fwhm
+
+
+ -
+
+ sigma
+
+
+ -
+
+ height
+
+
+
+
+
+
+ 0
+ 20
+ 181
+ 251
+
+
+
+
+ 15
+
+
+
+ Load Settings
+
+
+ -
+
+
+
+ 12
+
+
+
+ Background
+File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Spectra Scan
+File
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ For Scan Data
+
+
+
+ -
+
+
+
+ 12
+
+
+
+ Load Only
+ Fit File
+
+
+
+
+
+
+
+
+ 800
+ 450
+ 141
+ 41
+
+
+
+ # X points
+
+
+
+
+
+ 800
+ 500
+ 141
+ 41
+
+
+
+ # Y points
+
+
+
+
+
+ 950
+ 460
+ 81
+ 31
+
+
+
+ 2000
+
+
+ 100
+
+
+
+
+
+ 950
+ 510
+ 81
+ 31
+
+
+
+ 2000
+
+
+ 100
+
+
+
+
+
+ 800
+ 570
+ 231
+ 41
+
+
+
+ Use Raw Scan Settings
+
+
+ true
+
+
+
+
+
+ 760
+ 20
+ 211
+ 31
+
+
+
+
+ 15
+
+
+
+ Intensity Sums
+
+
+
+
+
+ 970
+ 20
+ 101
+ 31
+
+
+
+
+ 12
+
+
+
+ Plot
+
+
+
+
+
+ 580
+ 60
+ 521
+ 331
+
+
+
+
+
+
+ pkl to txt
+
+
+
+
+ 10
+ 20
+ 281
+ 201
+
+
+
+ Convert
+
+
+
+
+ 10
+ 40
+ 241
+ 31
+
+
+
+
+ 12
+
+
+
+ Import .pkl file
+
+
+
+
+
+ 10
+ 90
+ 241
+ 31
+
+
+
+
+ 12
+
+
+
+ Data to .txt
+
+
+
+
+
+ 10
+ 140
+ 241
+ 34
+
+
+
+
+ 12
+
+
+
+ Scan params to .txt
+
+
+
+
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+ ImageView
+ QGraphicsView
+
+
+
+
+
+
diff --git a/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py
new file mode 100644
index 0000000..44e2ffb
--- /dev/null
+++ b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis.py
@@ -0,0 +1,172 @@
+# system imports
+from pathlib import Path
+import os.path
+import pyqtgraph as pg
+from pyqtgraph import exporters
+from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
+import matplotlib.pyplot as plt
+
+import numpy as np
+import time
+
+# local modules
+
+pg.mkQApp()
+pg.setConfigOption('background', 'w')
+
+base_path = Path(__file__).parent
+file_path = (base_path / "uv_vis_analysis_gui.ui").resolve()
+
+uiFile = file_path
+
+WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile)
+
+"""params for plotting"""
+plt.rc('xtick', labelsize = 20)
+plt.rc('xtick.major', pad = 3)
+plt.rc('ytick', labelsize = 20)
+plt.rc('lines', lw = 1.5, markersize = 7.5)
+plt.rc('legend', fontsize = 20)
+plt.rc('axes', linewidth = 3.5)
+
+class MainWindow(TemplateBaseClass):
+
+ def __init__(self):
+ super(TemplateBaseClass, self).__init__()
+
+ # Create the main window
+ self.ui = WindowTemplate()
+ self.ui.setupUi(self)
+
+ #setup uv vis plot
+ self.absorbance_plot_layout = pg.GraphicsLayoutWidget()
+ self.ui.absorbance_plot_container.layout().addWidget(self.absorbance_plot_layout)
+ self.absorbance_plot = self.absorbance_plot_layout.addPlot(title="Wavelengths vs. Absorbance")
+ self.absorbance_plot.setLabel('bottom', 'Wavelength', unit='nm')
+ self.absorbance_plot.setLabel('left', 'Absorbance', unit='a.u.')
+
+ #setup correction region for uv vis
+ self.correction_region = pg.LinearRegionItem()
+ self.correction_region_min = 600
+ self.correction_region_max = 900
+ self.correction_region.setRegion((self.correction_region_min, self.correction_region_max))
+
+ #setup uv vis ui signals
+ self.ui.actionLoad_data.triggered.connect(self.open_data_file)
+ self.ui.plot_absorbance_pushButton.clicked.connect(self.plot_absorbance)
+ self.ui.clear_uvvis_pushButton.clicked.connect(self.clear_uvvis)
+ self.ui.export_uv_vis_pushButton.clicked.connect(self.export_uv_vis)
+ self.correction_region.sigRegionChanged.connect(self.update_correction_region)
+
+ #setup tauc plot
+ self.tauc_plot_layout = pg.GraphicsLayoutWidget()
+ self.ui.tauc_plot_container.layout().addWidget(self.tauc_plot_layout)
+ self.tauc_plot = self.tauc_plot_layout.addPlot(title="Tauc plot fit")
+ self.tauc_plot.setLabel('bottom', 'hv', unit='ev')
+ y_label = '(ahv)' + chr(0x00B2) #char is superscripted 2
+ self.tauc_plot.setLabel('left', y_label)
+
+ #setup tauc ui signals
+ self.ui.plot_tauc_pushButton.clicked.connect(self.plot_tauc)
+ self.ui.clear_tauc_pushButton.clicked.connect(self.clear_tauc)
+ self.ui.export_tauc_pushButton.clicked.connect(self.export_tauc)
+
+ self.show()
+
+ def open_data_file(self):
+ try:
+ self.filename = QtWidgets.QFileDialog.getOpenFileName(self)
+ self.data = np.loadtxt(self.filename[0], delimiter = ',', skiprows = 1)
+ self.Wavelength = self.data[:,0] # in nm
+ self.Absorbance = self.data[:,1]
+ except Exception as err:
+ print(format(err))
+
+ def update_correction_region(self):
+ """ Update correction region variables from region """
+ self.correction_region_min, self.correction_region_max = self.correction_region.getRegion()
+
+ def plot_absorbance(self):
+ try:
+ self.scatter_corrected = False
+ self.plotted_absorbance = self.Absorbance #by default set to original absorbance data
+ if self.ui.correct_for_scattering_checkBox.isChecked(): #if checked, correct absorbance data
+ self.scatter_corrected = True
+ self.plotted_absorbance = self.Absorbance - np.mean(self.Absorbance[(self.Wavelength>self.correction_region_min) & (self.Wavelength self.hv_min) & (self.hv < self.hv_max)
+ model = np.polyfit(self.hv[self.index], self.Alpha_hv[self.index], 1)
+ self.Alpha_hv_fit = self.hv * model[0] + model[1] #This is the linear fit
+ self.tauc_plot.plot(self.hv, self.Alpha_hv, pen='r')
+ self.tauc_plot.plot(self.hv, self.Alpha_hv_fit, pen='k')
+ self.tauc_plot.setXRange(1,2)
+ self.tauc_plot.setYRange(0, np.max(self.Alpha_hv[self.index]) + 1)
+
+ self.Eg = - model[1]/model[0]
+ self.ui.bandgap_label.setText(str(self.Eg))
+ except:
+ pass
+
+ def clear_tauc(self):
+ self.tauc_plot.clear()
+
+ def export_uv_vis(self):
+ """ Export publication ready uv vis figure """
+ try:
+ filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION")
+ plt.figure(figsize=(8,6))
+ plt.tick_params(direction='out', length=8, width=3.5)
+ plt.plot(self.Wavelength, self.plotted_absorbance, linewidth = 3, color = 'r')
+ if self.scatter_corrected:
+ plt.xlim(self.correction_region_min, self.correction_region_max)
+ plt.ylim(0, np.max(self.plotted_absorbance[(self.Wavelength>self.correction_region_min)]) +0.5)
+ else:
+ plt.xlim(self.correction_region_min, self.correction_region_max)
+ plt.ylim(0, np.max(self.plotted_absorbance[(self.Wavelength>self.correction_region_min)] +0.5))
+ plt.xlabel('Wavelength (nm)', fontsize = 20)
+ plt.ylabel('Absorbance (a.u.)', fontsize = 20)
+ plt.savefig(filename[0],bbox_inches='tight', dpi=300)
+ plt.close()
+ except:
+ pass
+
+ def export_tauc(self):
+ """ Export publication ready tauc figure"""
+ try:
+ filename = QtWidgets.QFileDialog.getSaveFileName(self,caption="Filename with EXTENSION")
+ plt.figure(figsize=(8,6))
+ plt.tick_params(direction='out', length=8, width=3.5)
+ plt.plot(self.hv, self.Alpha_hv, linewidth = 3, color = 'r')
+ plt.plot(self.hv, self.Alpha_hv_fit, linewidth = 2, color = 'black')
+ plt.xlim(1,2)
+ plt.ylim(0, np.max(self.Alpha_hv[self.index]) + 1)
+ plt.xlabel('h$\\nu$ (eV)', fontsize = 20)
+ plt.ylabel('($\\alpha$h$\\nu$)$^2$', fontsize = 20)
+ #plt.title(Plot_title, fontsize = 20)
+
+ plt.text(1.2, 1.2, r'E$_{g}$ = %.2f eV'%self.Eg, fontsize = 15)
+ plt.tight_layout()
+ plt.savefig(filename[0],bbox_inches='tight', dpi=300)
+ plt.close()
+ except:
+ pass
+
+"""Run the Main Window"""
+def run():
+ win = MainWindow()
+ QtGui.QApplication.instance().exec_()
+ return win
\ No newline at end of file
diff --git a/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui
new file mode 100644
index 0000000..6cb2989
--- /dev/null
+++ b/PythonGUI_apps/UV_Vis_analysis/uv_vis_analysis_gui.ui
@@ -0,0 +1,238 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 742
+ 924
+
+
+
+ MainWindow
+
+
+
+ -
+
+
+ UV-Vis plot
+
+
+
-
+
+
-
+
+
+ Plot
+
+
+
+ -
+
+
+ Correct for scattering
+
+
+
+ -
+
+
+ Clear
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+ -
+
+
+ Export UV-Vis plot
+
+
+
+
+
+
+ -
+
+
+ Tauc plot
+
+
+ true
+
+
+ false
+
+
+
-
+
+
-
+
+
+ hv min (ev)
+
+
+
+ -
+
+
+
+ 180
+ 0
+
+
+
+ -9999999.000000000000000
+
+
+ 9999999.000000000000000
+
+
+ 0.010000000000000
+
+
+ 1.500000000000000
+
+
+
+ -
+
+
+
+ 180
+ 0
+
+
+
+ -9999999.000000000000000
+
+
+ 9999999.000000000000000
+
+
+ 0.010000000000000
+
+
+ 1.800000000000000
+
+
+
+ -
+
+
+ hv max (ev)
+
+
+
+ -
+
+
+ Plot
+
+
+
+ -
+
+
+ Clear
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+ -
+
+
-
+
+
+ Bandgap (ev):
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Export tauc plot
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Load data
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 064b128..aeac6f0 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,28 @@
# Python_GUI_apps
-GUI Python apps
+GUI Python apps
+
+## Dependencies
+- scopefoundry
+- numpy
+- pyqt
+- qtpy
+- h5py
+- pyqtgraph
+- matplotlib
+- scipy
+- lmfit
+- customplotting
+
+## Installing dependencies from command-line
+```
+conda install numpy pyqt qtpy h5py pyqtgraph
+pip install git+git://github.com/ScopeFoundry/ScopeFoundry.git
+pip install matplotlib scipy lmfit customplotting==0.1.4.dev0
+```
+
+## Run instructions
+After setup, you can run the application by double-clicking DataBrowser.py.
+You can also run it from command-line while in the PythonGUI_apps folder:
+```
+python DataBrowser.py
+```