Source code for main

"""Module for analysis of SMS data from HDF5 files

Bertus van Heerden and Joshua Botha
University of Pretoria
2020
"""

from __future__ import annotations

[docs] __docformat__ = "NumPy"
# import csv import os import sys from platform import system import ctypes import importlib from PyQt5.QtCore import Qt, QThreadPool, pyqtSlot, QRegExp from PyQt5.QtGui import QIcon, QRegExpValidator # , QResizeEvent from PyQt5.QtWidgets import ( QMainWindow, QProgressBar, QFileDialog, QMessageBox, QInputDialog, QApplication, QStyleFactory, ) # , QTreeWidget from PyQt5 import uic import pyqtgraph as pg from typing import Union import time from multiprocessing import Process, freeze_support from threading import Lock from controllers import ( IntController, LifetimeController, GroupingController, SpectraController, RasterScanController, AntibunchingController, FilteringController, ) from thread_tasks import OpenFile from threads import ProcessThread from tree_model import DatasetTreeNode, DatasetTreeModel # import save_analysis from settings_dialog import SettingsDialog, Settings # try: # import pkg_resources.py2_warn # except ImportError: # pass import smsh5 from generate_sums import CPSums from custom_dialogs import TimedMessageBox import file_manager as fm from my_logger import setup_logger from file_conversion import ConvertFileDialog from exporting import ExportWorker, DATAFRAME_FORMATS from save_and_load import SaveAnalysisWorker, LoadAnalysisWorker from selection import RangeSelectionDialog import smsh5_file_reader
[docs] SMS_VERSION = "0.7.7"
# TODO: Needs to rather be reworked not to use recursion, but rather a loop of some sort sys.setrecursionlimit(1000 * 10)
[docs] main_window_file = fm.path(name="main_window.ui", file_type=fm.Type.UI)
UI_Main_Window, _ = uic.loadUiType(main_window_file)
[docs] logger = setup_logger(__name__, is_main=True)
# noinspection PyUnresolvedReferences
[docs] class MainWindow(QMainWindow, UI_Main_Window): """ Class for Full SMS application that returns QMainWindow object. This class uses a *.ui that is automatically converted to a *.py script upon exectution. """ def __init__(self): """Initialise MainWindow object. Creates and populates QMainWindow object as described by main_window.ui """
[docs] self.threadpool = QThreadPool()
logger.info( f"Multi-threading with maximum {self.threadpool.maxThreadCount()} threads" )
[docs] self.active_threads = []
[docs] self.confidence_index = {0: 99, 1: 95, 2: 90, 3: 69}
if system() == "Windows": logger.info("System -> Windows") elif system() == "Darwin": logger.info("System -> Unix/Linus") os.environ["QT_MAC_WANTS_LAYER"] = "1" else: logger.info("System -> Other") QMainWindow.__init__(self) UI_Main_Window.__init__(self) self.setupUi(self) self.setWindowIcon(QIcon(fm.path("Full-SMS.ico", fm.Type.Icons))) self.tabWidget.setCurrentIndex(0) self.tabGroupingMode.setCurrentIndex(0) self.setWindowTitle("Full SMS") pg.setConfigOption("antialias", True) # pg.setConfigOption('leftButtonPan', False)
[docs] self.settings_dialog = SettingsDialog(self, get_saved_settings=True)
[docs] self.settings = self.settings_dialog.settings
# self.intensity_lifetime_normalization_dialog = \ # FilteringNormalizationDialog(main_window=self) self.chbInt_Disp_Resolved.hide() self.chbInt_Disp_Photon_Bursts.hide() self.chbInt_Disp_Grouped.hide() self.chbInt_Disp_Using_Groups.hide() self.chbInt_Show_Groups.setEnabled(False) self.chbInt_Show_Global_Groups.setEnabled(False)
[docs] self.intensity_controller = IntController(main_window=self)
[docs] self.lifetime_controller = LifetimeController(main_window=self)
[docs] self.grouping_controller = GroupingController(main_widow=self)
[docs] self.spectra_controller = SpectraController(main_window=self)
[docs] self.raster_scan_controller = RasterScanController(main_window=self)
[docs] self.antibunch_controller = AntibunchingController( self, corr_widget=self.pgAntibunching_PlotWidget, corr_sum_widget=self.pgAntibunching_Sum_PlotWidget, )
# self.antibunch_controller = AntibunchingController(self, corr_widget=self.pgAntibunching_PlotWidget, # corr_sum_widget=None) a_c = self.antibunch_controller self.btnCorrCurrent.clicked.connect(a_c.gui_correlate_current) self.btnCorrSelected.clicked.connect(a_c.gui_correlate_selected) self.btnCorrAll.clicked.connect(a_c.gui_correlate_all) self.spbBinSizeCorr.valueChanged.connect(a_c.rebin_corrs)
[docs] self.filtering_controller = FilteringController(main_window=self)
# reg_exp = QRegExp("[+-]?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?") # reg_val = QRegExpValidator(reg_exp) # self.spbCorrDiff.setValidator(reg_val) self.btnSubBackground.clicked.connect(self.spectra_controller.gui_sub_bkg) self.actionOpen_h5.triggered.connect(self.act_open_h5) self.actionSave_Selected.triggered.connect(self.act_save_selected) self.actionSave_Analysis.triggered.connect(self.act_save_analysis) self.actionSelect_All.triggered.connect(self.act_select_all) self.actionInvert_Selection.triggered.connect(self.act_invert_selection) self.actionDeselect_All.triggered.connect(self.act_deselect_all) self.actionTrim_Dead_Traces.triggered.connect(self.act_trim) self.actionSwitch_All.triggered.connect(self.act_switch_all) self.actionSwitch_Selected.triggered.connect(self.act_switch_selected) self.actionSet_Startpoint.triggered.connect(self.act_set_startpoint) self.actionConvert_file.triggered.connect(self.convert_file_dialog) self.actionRange_Selection.triggered.connect(self.range_selection) self.actionSettings.triggered.connect(self.act_open_settings_dialog) self.actionFiltering_Normalization.triggered.connect( self.act_filtering_and_normalization_dialog ) self.actionDetect_Remove_Bursts_Current.triggered.connect( self.act_detect_remove_bursts_current ) self.actionDetect_Remove_Bursts_Selected.triggered.connect( self.act_detect_remove_bursts_selected ) self.actionDetect_Remove_Bursts_All.triggered.connect( self.act_detect_remove_bursts_all ) self.actionRemove_Bursts_Current.triggered.connect( self.act_remove_bursts_current ) self.actionRemove_Bursts_Selected.triggered.connect( self.act_remove_bursts_selected ) self.actionRemove_Bursts_All.triggered.connect(self.act_remove_bursts_all) self.actionRestore_Bursts_Current.triggered.connect( self.act_restore_bursts_current ) self.actionRestore_Bursts_Selected.triggered.connect( self.act_restore_bursts_selected ) self.actionRestore_Bursts_All.triggered.connect(self.act_restore_bursts_all) self.actionClose.triggered.connect(self.close_file) self.chbGroup_Use_ROI.stateChanged.connect(self.gui_group_use_roi) self.btnEx_Current.clicked.connect(self.gui_export_current) self.btnEx_Selected.clicked.connect(self.gui_export_selected) self.btnEx_All.clicked.connect(self.gui_export_all) self.chbEx_Plot_Intensity.clicked.connect(self.gui_plot_intensity_clicked) self.chbEx_Plot_Lifetimes.clicked.connect(self.gui_plot_lifetime_clicked) self.chbEx_DF_Traces.stateChanged.connect(self.set_export_options) self.chbEx_DF_Levels.stateChanged.connect(self.set_export_options) self.chbEx_DF_Grouped_Levels.stateChanged.connect(self.set_export_options) self.btnSelectAllExport.clicked.connect(self.select_all_export_options) self.btnSelectAllExport_Plots.clicked.connect( self.select_all_plots_export_options ) self.btnSelectAllExport_DataFrames.clicked.connect( self.select_all_dataframes_export_options ) self.chbInt_Select_Groups.stateChanged.connect( lambda: self.gui_select_groups_changed(self.chbInt_Select_Groups) ) self.chbLifetime_Select_Groups.stateChanged.connect( lambda: self.gui_select_groups_changed(self.chbLifetime_Select_Groups) ) self.lblGrouping_ROI.setVisible(False) self.cmbEx_DataFrame_Format.addItems(DATAFRAME_FORMATS) # Create and connect model for dataset tree
[docs] self.treemodel = DatasetTreeModel()
self.treeViewParticles.setModel(self.treemodel) # Connect the tree selection to data display self.treeViewParticles.selectionModel().currentChanged.connect( self.display_data ) self.treemodel.dataChanged.connect(self.selection_changed) self.treeViewParticles.clicked.connect(self.tree_view_clicked) # self.treeViewParticles.keyPressEvent().connect(self.tree_view_key_press)
[docs] self._root_was_checked = False
self.comboSelectCard.currentIndexChanged.connect(self.card_selected)
[docs] self.part_nodes = list()
[docs] self.part_index = list()
[docs] self.tauparam = None
[docs] self.ampparam = None
[docs] self.shift = None
[docs] self.decaybg = None
[docs] self.irfbg = None
[docs] self.start = None
[docs] self.end = None
[docs] self.addopt = None
self.statusBar().showMessage("Ready...")
[docs] self.progress = QProgressBar(self)
self.progress.setMinimumSize(170, 19) self.progress.setVisible(False) self.progress.setValue(0) # Range of values is from 0 to 100 self.statusBar().addPermanentWidget(self.progress)
[docs] self.current_progress = float()
[docs] self.data_loaded = False
[docs] self.irf_loaded = False
# self._current_level = None self.tabWidget.currentChanged.connect(self.tab_change) self.tabGroupingMode.currentChanged.connect(self.tab_change) # self.tabGroupingMode.currentChanged.connect(lambda: self.grouping_controller.plot_group_bic())
[docs] self.current_dataset = None
[docs] self.current_particle = None
self.reset_gui() self.repaint()
[docs] self.lock = None
"""####################################### ######## GUI Housekeeping Methods ######## #######################################"""
[docs] def after_show(self): # self.pgSpectra.resize(self.tabSpectra.size().height(), # self.tabSpectra.size().height() - self.btnSubBackground.size().height() - 40) # QEvent. # QTimer.singleShot(1000) self.calc_store_sums() for i in range(100): time.sleep(1) print(i) pass
# def resizeEvent(self, a0: QResizeEvent): # if self.tabSpectra.size().height() <= self.tabSpectra.size().width(): # self.pgSpectra.resize(self.tabSpectra.size().height(), # self.tabSpectra.size().height() - self.btnSubBackground.size().height() - 40) # else: # self.pgSpectra.resize(self.tabSpectra.size().width(), # self.tabSpectra.size().width() - 40) # pass
[docs] def sums_file_check(self) -> bool: should_calc = False sums_path = fm.path(name="all_sums.pickle", file_type=fm.Type.Data) if (not os.path.exists(sums_path)) and (not os.path.isfile(sums_path)): self.status_message( "Calculating change point sums, this may take several minutes." ) should_calc = True return should_calc
[docs] def calc_store_sums(self) -> None: """ Check if the all_sums.pickle file exists, and if it doesn't creates it """ create_all_sums = CPSums(only_pickle=True, n_min=10, n_max=1000) del create_all_sums self.status_message("Ready...")
[docs] def gui_export_current(self): self.gui_export(mode="current")
[docs] def gui_export_selected(self): self.gui_export(mode="selected")
[docs] def gui_export_all(self): self.gui_export(mode="all")
[docs] def gui_select_groups_changed(self, chb_changed): chb_other = ( self.chbLifetime_Select_Groups if chb_changed is self.chbInt_Select_Groups else self.chbInt_Select_Groups ) chb_other.setChecked(chb_changed.isChecked())
[docs] def act_detect_remove_bursts_current(self): self.detect_remove_bursts(mode="current")
[docs] def act_detect_remove_bursts_selected(self): self.detect_remove_bursts(mode="selected")
[docs] def act_detect_remove_bursts_all(self): self.detect_remove_bursts(mode="all")
[docs] def act_remove_bursts_current(self): self.remove_bursts(mode="current", confirm=False)
[docs] def act_remove_bursts_selected(self): self.remove_bursts(mode="selected", confirm=False)
[docs] def act_remove_bursts_all(self): self.remove_bursts(mode="all", confirm=False)
[docs] def act_restore_bursts_current(self): self.restore_bursts(mode="current")
[docs] def act_restore_bursts_selected(self): self.restore_bursts(mode="selected")
[docs] def act_restore_bursts_all(self): self.restore_bursts(mode="all")
[docs] def set_bin_size(self, bin_size: int): self.spbBinSize.setValue(bin_size)
[docs] def act_open_settings_dialog(self): self.settings_dialog.exec()
[docs] def act_filtering_and_normalization_dialog(self): self.intensity_lifetime_normalization_dialog.exec()
[docs] def gui_group_use_roi(self): if self.data_loaded: use_roi = self.chbGroup_Use_ROI.isChecked() for particle in self.current_dataset.particles: particle.ahca.use_roi_for_grouping = use_roi
[docs] def act_open_h5(self): """Allows the user to point to a h5 file and then starts a thread that reads and loads the file.""" logger.info("Performing Open H5 Action") last_opened_file = fm.path( name="last_opened.txt", file_type=fm.Type.ResourcesRoot ) last_opened_path = "" if os.path.exists(last_opened_file) and os.path.isfile(last_opened_file): with open(last_opened_file, "r") as file: last_opened_path = file.read() if not os.path.isdir(last_opened_path): last_opened_path = "" file_path = QFileDialog.getOpenFileName( self, "Open HDF5 file", last_opened_path, "HDF5 files (*.h5)" ) did_open = False loading_analysis = False if os.path.exists(file_path[0][:-2] + "smsa") and os.path.isfile( file_path[0][:-2] + "smsa" ): msg_box = QMessageBox(parent=self) msg_box.setWindowTitle("Load analysis?") msg_box.setText("Analysis file found. Would you like to load it?") msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.exec() if msg_box.result() == QMessageBox.Yes: load_analysis_worker = LoadAnalysisWorker( main_window=self, file_path=file_path[0][:-2] + "smsa" ) load_analysis_worker.signals.status_message.connect(self.status_message) load_analysis_worker.signals.start_progress.connect(self.start_progress) load_analysis_worker.signals.end_progress.connect(self.end_progress) load_analysis_worker.signals.error.connect(self.error_handler) load_analysis_worker.signals.openfile_finished.connect( self.open_file_thread_complete ) load_analysis_worker.signals.save_file_version_outdated.connect( self.open_save_file_version_outdated ) load_analysis_worker.signals.show_residual_widget.connect( self.lifetime_controller.show_residuals_widget ) self.threadpool.start(load_analysis_worker) loading_analysis = True did_open = True if file_path != ("", "") and not loading_analysis: self.status_message(message="Opening file...") # logger.info("About to create ProcessThread object") of_process_thread = ProcessThread(num_processes=1) # logger.info("About to connect signals") of_process_thread.worker_signals.add_datasetindex.connect(self.add_dataset) of_process_thread.worker_signals.add_particlenode.connect(self.add_node) of_process_thread.worker_signals.add_all_particlenodes.connect( self.add_all_nodes ) of_process_thread.worker_signals.bin_size.connect(self.set_bin_size) of_process_thread.worker_signals.data_loaded.connect(self.set_data_loaded) of_process_thread.signals.status_update.connect(self.status_message) of_process_thread.signals.start_progress.connect(self.start_progress) of_process_thread.signals.set_progress.connect(self.set_progress) of_process_thread.signals.step_progress.connect(self.update_progress) of_process_thread.signals.add_progress.connect(self.update_progress) of_process_thread.signals.end_progress.connect(self.end_progress) of_process_thread.signals.error.connect(self.error_handler) of_process_thread.signals.finished.connect(self.open_file_thread_complete) # logger.info("About to create OpenFile object") of_obj = OpenFile( file_path=file_path ) # , progress_tracker=of_progress_tracker) of_process_thread.add_tasks_from_methods(of_obj, "open_h5") # logger.info("About to start Process Thread") self.threadpool.start(of_process_thread) # logger.info("Started Process Thread") self.active_threads.append(of_process_thread) did_open = True if did_open: with open(last_opened_file, "w") as file: file.write(os.path.split(file_path[0])[0])
[docs] def act_save_selected(self): """ " Saves selected particles into a new HDF5 file.""" msg = QMessageBox(self) msg.setWindowTitle("Still in development") msg.setText("This functionality is still in development") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() return selected_nums = self.get_checked_nums() if not len(selected_nums): msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setWindowTitle("Save Error") msg.setText("No particles selected.") msg.setStandardButtons(QMessageBox.Ok) msg.exec() return fname, _ = QFileDialog.getSaveFileName( self, "New or Existing HDF5 file", "", "HDF5 files (*.h5)", options=QFileDialog.DontConfirmOverwrite, ) if os.path.exists(fname[0]): msg = QMessageBox(self) msg.setIcon(QMessageBox.Question) msg.setWindowTitle("Add To Existing File") msg.setText("Do you want to add selected particles to existing file?") msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) if msg.exec() == QMessageBox.Cancel: return if self.current_dataset.name == fname: msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setWindowTitle("Save Error") msg.setText("Can" "t add particles to currently opened file.") msg.setStandardButtons(QMessageBox.Ok) msg.exec() return self.current_dataset.save_particles(fname, selected_nums)
[docs] def act_save_analysis(self): if self.current_dataset is not None: save_analysis_worker = SaveAnalysisWorker( main_window=self, dataset=self.current_dataset ) save_analysis_worker.signals.status_message.connect(self.status_message) save_analysis_worker.signals.start_progress.connect(self.start_progress) save_analysis_worker.signals.end_progress.connect(self.end_progress) save_analysis_worker.signals.error.connect(self.error_handler) self.threadpool.start(save_analysis_worker)
# save_analysis.save_analysis(self, self.current_dataset)
[docs] def act_trim(self): """Used to trim the 'dead' part of a trace as defined by two parameters.""" print("act_trim")
[docs] def act_switch_all(self): self.switching_frequency(all_selected="all")
[docs] def act_switch_selected(self): self.switching_frequency(all_selected="selected")
[docs] def act_set_startpoint(self): start, ok = QInputDialog.getInt(self, "Input Dialog", "Enter startpoint:") self.set_startpoint(start)
[docs] def set_startpoint(self, irf_data=None, start=None): if start is None: start = self.lifetime_controller.startpoint try: # self.tree2dataset().makehistograms(remove_zeros=False, startpoint=start, channel=True) dataset = self.current_dataset dataset.makehistograms(remove_zeros=False, startpoint=start, channel=True) except Exception as exc: print(exc) if self.lifetime_controller.irf_loaded and irf_data: self.lifetime_controller.change_irf_start(start, irf_data) if self.lifetime_controller.startpoint is None: self.lifetime_controller.startpoint = start self.display_data() logger.info("Set startpoint")
"""####################################### ############ Internal Methods ############ #######################################"""
[docs] def add_dataset(self, dataset_node): self.dataset_node = dataset_node self.dataset_index = self.treemodel.addChild(dataset_node) self.current_dataset = dataset_node.dataobj
[docs] def add_node(self, particle_node, num): index = self.treemodel.addChild( particle_node, self.dataset_index ) # , progress_sig) if num == 0: self.treeViewParticles.expand(self.dataset_index) self.treeViewParticles.setCurrentIndex(index) self.current_particle = particle_node.dataobj self.part_nodes.append(particle_node) self.part_index.append(index)
[docs] def add_all_nodes(self, all_nodes): for node, num in all_nodes: if num == -1: assert ( type(node.dataobj) is smsh5.H5dataset ), "First node must be for H5Dataset" self.add_dataset(node) self.treeViewParticles.expand(self.dataset_index) # index = self.treemodel.addChild(node, self.datasetindex) # , progress_sig) else: assert type(node.dataobj) is smsh5.Particle, "Node must be for Particle" self.add_node(node, num)
[docs] def tree_view_clicked(self, model_index): if type(self.treemodel.data(model_index, Qt.UserRole)) is smsh5.Particle: self.set_export_options() self.grouping_controller.check_rois_and_set_label() self.lifetime_controller.update_apply_roi_button_colors() if self.treemodel.data(model_index, Qt.UserRole) is self.dataset_node.dataobj: root_node_checked = self.dataset_node.checked() if all([node.checked() for node in self.part_nodes]) != root_node_checked: for part_node in self.part_nodes: part_node.setChecked(root_node_checked) self._root_was_checked = root_node_checked if root_node_checked: self.lblNum_Selected.setText(str(len(self.part_nodes))) else: self.lblNum_Selected.setText("0") self.treeViewParticles.viewport().repaint() else: checked_list = [node.checked() for node in self.part_nodes] all_checked = all(checked_list) self.dataset_node.setChecked(all_checked) num_checked = sum(checked_list) self.lblNum_Selected.setText(str(num_checked)) self.treeViewParticles.viewport().repaint()
[docs] def tree_view_key_press(self, event): pass
[docs] def act_select_all(self, *args, **kwargs): if self.data_loaded: for node in self.part_nodes: node.setChecked(True) self.lblNum_Selected.setText(str(len(self.part_nodes)))
[docs] def act_invert_selection(self, *args, **kwargs): if self.data_loaded: for node in self.part_nodes: node.setChecked(not node.checked()) num_checked = sum([node.checked() for node in self.part_nodes]) self.lblNum_Selected.setText(str(num_checked)) self.treeViewParticles.viewport().repaint()
[docs] def act_deselect_all(self, *args, **kwargs): if self.data_loaded: for node in self.part_nodes: node.setChecked(False) self.lblNum_Selected.setText("0")
[docs] def tab_change(self, active_tab_index: int): if self.data_loaded and hasattr(self, "current_particle"): tabs_to_display = [ "tabIntensity", "tabLifetime", "tabGrouping", "tabAntibunching", "tabSpectra", "tabRaster_Scan", ] if self.tabWidget.currentWidget().objectName() in tabs_to_display: self.display_data() elif self.tabWidget.currentWidget().objectName() == "tabFiltering": self.filtering_controller.set_levels_to_use()
[docs] def update_int_gui(self): cur_part = self.current_particle if cur_part.has_levels: self.chbInt_Disp_Resolved.show() else: self.chbInt_Disp_Resolved.hide() if cur_part.has_burst: self.chbInt_Disp_Photon_Bursts.show() if cur_part.cpts.bursts_deleted is not None: self.chbInt_Disp_Photon_Bursts.setChecked(True) else: self.chbInt_Disp_Photon_Bursts.setChecked(False) else: self.chbInt_Disp_Photon_Bursts.hide() if cur_part.has_groups: self.chbInt_Disp_Grouped.show() self.chbInt_Show_Groups.setEnabled(True) else: self.chbInt_Disp_Grouped.hide() self.chbInt_Show_Groups.setEnabled(False) self.chbInt_Show_Global_Groups.setEnabled(cur_part.has_global_grouping) if cur_part.using_group_levels: self.chbInt_Disp_Using_Groups.show() else: self.chbInt_Disp_Using_Groups.hide()
[docs] def card_selected(self) -> None: self.display_data(combocard=True)
[docs] def selection_changed(self) -> None: self.display_data()
[docs] def display_data( self, current=None, prev=None, combocard=False, is_global_group=False ) -> None: """Displays the intensity trace and the histogram of the current particle. Directly called by the tree signal currentChanged, thus the two arguments. Arguments --------- current : QtCore.QModelIndex The index of the current selected particle as defined by QtCore.QModelIndex. prev : QtCore.QModelIndex The index of the previous selected particle as defined by QtCore.QModelIndex. combocard : bool True if called due to selecting other TCSPC card. """ # self.current_level = None # self.current_ind = current # self.pre_ind = prev self.treeViewParticles.viewport().repaint() if current is not None: if hasattr(self, "current_particle"): self.current_particle = self.treemodel.get_particle(current) # self.current_level = None # Reset current level when particle changes. if ( hasattr(self, "current_particle") and type(self.current_particle) is smsh5.Particle ): # Select primary or secondary particle based on selected tcspc card if ( self.comboSelectCard.currentIndex() == 1 and self.current_particle.sec_part is not None ): assert not self.current_particle.is_secondary_part self.current_particle = self.current_particle.sec_part elif ( self.comboSelectCard.currentIndex() == 0 and self.current_particle.is_secondary_part ): self.current_particle = self.current_particle.prim_part cur_tab_name = self.tabWidget.currentWidget().objectName() self.txtDescription.setText(self.current_particle.description) # If not called due to a change in selected card, update the card selector with available choices if not combocard: if not self.current_particle.is_secondary_part: card1 = self.current_particle.tcspc_card if self.current_particle.sec_part is not None: card2 = self.current_particle.sec_part.tcspc_card else: card2 = None else: card1 = self.current_particle.prim_part.tcspc_card card2 = self.current_particle.tcspc_card if self.comboSelectCard.count() == 0: self.comboSelectCard.insertItem(0, card1) self.comboSelectCard.insertItem(1, card2) else: self.comboSelectCard.setItemText(0, card1) if self.comboSelectCard.count() == 1: self.comboSelectCard.insertItem(1, card2) else: self.comboSelectCard.setItemText(1, card2) assert self.comboSelectCard.count() <= 2 if cur_tab_name in ["tabIntensity", "tabGrouping", "tabLifetime"]: if cur_tab_name == "tabIntensity": self.update_int_gui() self.intensity_controller.set_bin(self.current_particle.bin_size) self.intensity_controller.plot_trace() if self.current_particle.has_levels: self.intensity_controller.plot_levels() self.intensity_controller.update_level_info() if cur_tab_name != "tabLifetime": self.intensity_controller.plot_hist() else: use_selected = ( False if self.current_particle.level_or_group_selected is None else True ) self.lifetime_controller.plot_decay( use_selected=use_selected, remove_empty=False ) self.lifetime_controller.plot_convd(use_selected=use_selected) self.lifetime_controller.plot_residuals(use_selected=use_selected) self.lifetime_controller.update_results(use_selected=use_selected) self.lifetime_controller.update_apply_roi_button_colors() if ( self.current_particle.has_groups or self.current_particle.has_global_grouping or is_global_group ): # if not is_global_group: self.intensity_controller.plot_group_bounds() if cur_tab_name == "tabGrouping": self.grouping_controller.plot_group_bic( is_global_group=is_global_group ) else: self.grouping_controller.clear_bic() elif cur_tab_name == "tabSpectra" and self.current_particle.has_spectra: self.spectra_controller.plot_spectra() elif ( cur_tab_name == "tabRaster_Scan" and self.current_particle.has_raster_scan ): self.raster_scan_controller.plot_raster_scan() elif cur_tab_name == "tabAntibunching": self.antibunch_controller.plot_corr() elif cur_tab_name == "tabExport": self.set_export_options() # Set Enables # set_apply_groups = False # if self.current_particle.has_levels: # self.int_controller.plot_levels() # set_group = True # if self.current_particle.has_groups: # set_apply_groups = True # else: # set_apply_groups = False # else: # set_group = False # self.btnGroupCurrent.setEnabled(set_group) # self.btnGroupSelected.setEnabled(set_group) # self.btnGroupAll.setEnabled(set_group) # self.btnApplyGroupsCurrent.setEnabled(set_apply_groups) # self.btnApplyGroupsSelected.setEnabled(set_apply_groups) # self.btnApplyGroupsAll.setEnabled(set_apply_groups) logger.info(f"{self.current_particle.name} data displayed")
[docs] def status_message(self, message: str) -> None: """ Updates the status bar with the provided message argument. Parameters ---------- message : str The message that is to be displayed in the status bar. """ if message != "": self.statusBar().showMessage(message) # self.statusBar().show() else: self.statusBar().clearMessage()
[docs] def start_progress(self, max_num: int = None) -> None: """ Sets the maximum value of the progress bar before use. reset parameter can be optionally set to False to prevent the setting of the progress bar value to 0. Parameters ---------- max_num : int The number of iterations or steps that the complete process is made up of. """ if max_num: assert ( type(max_num) is int ), "MainWindow:\tThe type of the 'max_num' parameter is not int." self.progress.setMaximum(max_num) # print(max_num) self.progress.setValue(0) self.progress.setVisible(True) self.current_progress = 0 self.progress.repaint() self.statusBar().repaint() self.repaint()
[docs] def set_progress(self, progress_value: int) -> None: """ Sets the maximum value of the progress bar before use. reset parameter can be optionally set to False to prevent the setting of the progress bar value to 0. Parameters ---------- progress_value : int The number of iterations or steps that the complete process is made up of. """ assert ( type(progress_value) is int ), "MainWindow:\tThe type of the 'max_num' parameter is not int." self.progress.setValue(progress_value) self.progress.repaint() self.statusBar().repaint() self.repaint()
[docs] def update_progress(self, value: Union[int, float] = None) -> None: """Used to update the progress bar by an increment of one. If at maximum sets progress bars visibility to False""" if not value: value = 1.0 if self.progress.isVisible(): self.current_progress += value new_show_value = int(self.current_progress // 1) self.progress.setValue(new_show_value) # print(self.current_progress) if self.current_progress >= self.progress.maximum(): self.end_progress() self.progress.repaint() self.statusBar().repaint() self.repaint()
[docs] def end_progress(self): self.current_progress = 0 self.progress.setValue(0) self.progress.setMaximum(0) self.progress.setVisible(False) self.progress.repaint() self.statusBar().repaint() self.repaint()
[docs] def tree2particle(self, identifier): """Returns the particle dataset for the identifier given. The identifier could be the number of the particle of the datasetnode value. Parameters ---------- identifier The integer number or a datasetnode object of the particle in question. Returns ------- """ if type(identifier) is int: return self.dataset_index.child(identifier, 0).data(Qt.UserRole) if type(identifier) is DatasetTreeNode: return identifier.dataobj
[docs] def tree2dataset(self) -> smsh5.H5dataset: """Returns the H5dataset object of the file loaded. Returns ------- smsh5.H5dataset """ # return self.treemodel.data(self.treemodel.index(0, 0), Qt.UserRole) return self.dataset_index.data(Qt.UserRole)
[docs] def set_data_loaded(self): self.data_loaded = True
[docs] def open_save_file_version_outdated(self): msg_box = QMessageBox(parent=self) msg_box.setWindowTitle("Save File Outdated") msg_box.setText("The save file is outdated. Please reload *.h5 file instead.") msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec()
[docs] def open_file_thread_complete( self, thread: ProcessThread = None, irf=False ) -> None: """Is called as soon as all of the threads have finished.""" if self.data_loaded and not irf: self.current_dataset = self.tree2dataset() self.treeViewParticles.expandAll() print(self.part_index) self.treeViewParticles.setCurrentIndex(self.part_index[0]) self.current_particle = self.tree2particle(0) any_spectra = any( [part.has_spectra for part in self.current_dataset.particles] ) if any_spectra: self.current_dataset.has_spectra = True if not any([p.has_levels for p in self.current_dataset.particles]) and self.settings.auto_resolve_levels: msgbx = TimedMessageBox(30, parent=self) msgbx.setIcon(QMessageBox.Question) msgbx.setText("Would you like to resolve levels now?") msgbx.set_timeout_text( message_pretime="(Resolving levels in ", message_posttime=" seconds)", ) msgbx.setWindowTitle("Resolve Levels?") msgbx.setStandardButtons(QMessageBox.No | QMessageBox.Yes) msgbx.setDefaultButton(QMessageBox.Yes) msgbx_result, timed_out = msgbx.exec() if msgbx_result == QMessageBox.Yes: confidences = ("0.99", "0.95", "0.90", "0.69") index = None if timed_out: index = 0 else: item, ok = QInputDialog.getItem( self, "Choose Confidence", "Select confidence interval to use.", confidences, 0, False, ) if ok: index = list(self.confidence_index.values()).index( int(float(item) * 100) ) if index is not None: self.cmbConfIndex.setCurrentIndex(index) self.intensity_controller.start_resolve_thread("all") if self.data_loaded: self.actionSave_Analysis.setEnabled(True) self.actionSelect_All.setEnabled(True) self.actionInvert_Selection.setEnabled(True) self.actionDeselect_All.setEnabled(True) self.actionRange_Selection.setEnabled(True) self.menuIntensity.setEnabled(True) self.menuLifetime.setEnabled(True) self.chbEx_Use_ROI.setEnabled(True) self.chbInt_Show_ROI.setEnabled(True) self.chbGroup_Use_ROI.setEnabled(True) self.chbEx_Trace.setEnabled(True) self.chbEx_Hist.setEnabled(True) self.chbEx_Plot_Intensity.setEnabled(True) self.rdbInt_Only.setEnabled(True) self.chbEx_Plot_Lifetimes.setEnabled(True) self.rdbHist_Only.setEnabled(True) self.actionRange_Selection.setEnabled(True) self.set_export_options() self.reset_gui() self.chbInt_Show_ROI.setCheckState(1) self.display_data() logger.info("File opened")
[docs] def set_export_options(self): particles = self.get_checked_particles() # particles.append(self.current_particle) if len(particles) == 0: particles = [self.current_particle] all_have_levels = all([p.has_levels for p in particles]) all_have_any_groups = all( [p.has_groups or p.has_global_grouping for p in particles] ) all_have_individual_groups = all([p.has_groups for p in particles]) all_have_global_groups = all([p.has_global_grouping for p in particles]) all_have_lifetimes = all([p.has_fit_a_lifetime for p in particles]) all_have_raster_scans = all([p.has_raster_scan for p in particles]) all_have_spectra = all([p.has_spectra for p in particles]) all_have_corr = all([p.has_corr for p in particles]) self.chbEx_Levels.setEnabled(all_have_levels) self.chbEx_DF_Levels.setEnabled(all_have_levels) if self.chbEx_DF_Levels.isChecked() and all_have_lifetimes: self.chbEx_DF_Levels_Lifetimes.setEnabled(True) else: self.chbEx_DF_Levels_Lifetimes.setEnabled(False) self.chbEx_Grouped_Levels.setEnabled(all_have_individual_groups) self.chbEx_Global_Grouped_Levels.setEnabled(all_have_global_groups) self.chbEx_DF_Grouped_Levels.setEnabled(all_have_individual_groups) if self.chbEx_DF_Grouped_Levels.isChecked() and all_have_individual_groups: self.chbEx_DF_Grouped_Levels_Lifetimes.setEnabled(True) else: self.chbEx_DF_Grouped_Levels_Lifetimes.setEnabled(False) self.chbEx_DF_Global_Grouped_Levels.setEnabled(all_have_global_groups) self.chbEx_Grouping_Info.setEnabled(all_have_individual_groups) self.chbEx_Grouping_Results.setEnabled(all_have_individual_groups) self.chbEx_DF_Grouping_Info.setEnabled(all_have_individual_groups) # Hists always enabled self.chbEx_Lifetimes.setEnabled(all_have_lifetimes) self.chbEx_Spectra_2D.setEnabled(all_have_spectra) # self.chbEx_Spectra_Fitting.setEnabled(False) # Add when spectra analysis added # self.chbEx_Spectra_Traces.setEnabled(False) # Add when spectra analysis added # Int plot always enalbed self.rdbInt_Only.setEnabled(True) if not (all_have_individual_groups or all_have_levels): self.rdbInt_Only.setChecked(True) self.rdbWith_Levels.setEnabled(all_have_levels) self.rdbAnd_Groups.setEnabled(all_have_individual_groups) # Always able to export traces self.chbEx_DF_Traces.setEnabled(True) # self.chbEx_Hist.setEnabled(all_have_lifetimes) # Shouldn't this be true always? self.chbEx_Plot_Group_BIC.setEnabled(all_have_any_groups) self.chbEx_Plot_Lifetimes.setEnabled(all_have_lifetimes) self.rdbWith_Fit.setEnabled(all_have_lifetimes) self.rdbAnd_Residuals.setEnabled(all_have_lifetimes) self.chbEx_Plot_Lifetimes_Only_Groups.setEnabled(all_have_lifetimes) self.chbEx_Plot_Spectra.setEnabled(all_have_spectra) self.chbEx_Raster_Scan_2D.setEnabled(all_have_raster_scans) self.chbEx_Plot_Raster_Scans.setEnabled(all_have_raster_scans) self.chbEx_Corr.setEnabled(all_have_corr) self.chbEx_Plot_Corr.setEnabled(all_have_corr)
[docs] def select_all_export_options(self): self.chbEx_Trace.setChecked(self.chbEx_Trace.isEnabled()) self.chbEx_Levels.setChecked(self.chbEx_Levels.isEnabled()) self.chbEx_Grouped_Levels.setChecked(self.chbEx_Grouped_Levels.isEnabled()) self.chbEx_Grouping_Info.setChecked(self.chbEx_Grouping_Info.isEnabled()) self.chbEx_Grouping_Results.setChecked(self.chbEx_Grouping_Results.isEnabled()) self.chbEx_DF_Grouping_Info.setChecked(self.chbEx_DF_Grouping_Info.isEnabled()) self.chbEx_Hist.setChecked(self.chbEx_Hist.isEnabled()) self.chbEx_Lifetimes.setChecked(self.chbEx_Lifetimes.isEnabled()) self.chbEx_Spectra_2D.setChecked(self.chbEx_Spectra_2D.isEnabled()) # self.chbEx_Spectra_Fitting.setChecked(self.chbEx_Spectra_Fitting.isEnabled()) # self.chbEx_Sptecra_Traces.setChecked(self.chbEx_Sptecra_Traces.isEnabled()) self.chbEx_Plot_Intensity.setChecked(self.chbEx_Plot_Intensity.isEnabled()) self.rdbInt_Only.setChecked(self.rdbInt_Only.isEnabled()) self.rdbWith_Levels.setChecked(self.rdbWith_Levels.isEnabled()) self.rdbAnd_Groups.setChecked(self.rdbAnd_Groups.isEnabled()) self.chbEx_Plot_Group_BIC.setChecked(self.chbEx_Plot_Group_BIC.isEnabled()) self.chbEx_Plot_Lifetimes.setChecked(self.chbEx_Plot_Lifetimes.isEnabled()) self.rdbHist_Only.setChecked(self.rdbHist_Only.isEnabled()) self.rdbWith_Fit.setChecked(self.rdbWith_Fit.isEnabled()) self.rdbAnd_Residuals.setChecked(self.rdbAnd_Residuals.isEnabled()) self.chbEx_Plot_Spectra.setChecked(self.chbEx_Plot_Spectra.isEnabled()) self.chbEx_Raster_Scan_2D.setChecked(self.chbEx_Raster_Scan_2D.isEnabled()) self.chbEx_Plot_Raster_Scans.setChecked( self.chbEx_Plot_Raster_Scans.isEnabled() ) # Not sure if there is only duplication of below # self.chbEx_DF_Levels.setChecked(self.chbEx_DF_Levels.isEnabled()) # self.chbEx_DF_Levels_Lifetimes.setChecked(self.chbEx_DF_Levels_Lifetimes.isEnabled()) # self.chbEx_DF_Grouped_Levels.setChecked(self.chbEx_DF_Grouped_Levels.isEnabled()) # self.chbEx_DF_Grouped_Levels_Lifetimes.setChecked( # self.chbEx_DF_Grouped_Levels_Lifetimes.isEnabled()) # self.chbEx_DF_Grouping_Info.setChecked(self.chbEx_DF_Grouping_Info.isEnabled()) self.chbEx_DF_Traces.setChecked(self.chbEx_DF_Traces.isEnabled()) self.chbEx_DF_Levels.setChecked(self.chbEx_DF_Levels.isEnabled()) self.chbEx_DF_Levels_Lifetimes.setChecked( self.chbEx_DF_Levels_Lifetimes.isEnabled() ) self.chbEx_DF_Grouped_Levels.setChecked( self.chbEx_DF_Grouped_Levels.isEnabled() ) self.chbEx_DF_Grouped_Levels_Lifetimes.setChecked( self.chbEx_DF_Grouped_Levels_Lifetimes.isEnabled() ) self.chbEx_DF_Grouping_Info.setChecked(self.chbEx_DF_Grouping_Info.isEnabled())
[docs] def select_all_plots_export_options(self): self.chbEx_Plot_Intensity.setChecked(self.chbEx_Plot_Intensity.isEnabled()) self.rdbInt_Only.setChecked(self.rdbInt_Only.isEnabled()) self.rdbWith_Levels.setChecked(self.rdbWith_Levels.isEnabled()) self.rdbAnd_Groups.setChecked(self.rdbAnd_Groups.isEnabled()) self.chbEx_Plot_Group_BIC.setChecked(self.chbEx_Plot_Group_BIC.isEnabled()) self.chbEx_Plot_Lifetimes.setChecked(self.chbEx_Plot_Lifetimes.isEnabled()) self.rdbHist_Only.setChecked(self.rdbHist_Only.isEnabled()) self.rdbWith_Fit.setChecked(self.rdbWith_Fit.isEnabled()) self.rdbAnd_Residuals.setChecked(self.rdbAnd_Residuals.isEnabled()) self.chbEx_Plot_Spectra.setChecked(self.chbEx_Plot_Spectra.isEnabled()) self.chbEx_Plot_Raster_Scans.setChecked( self.chbEx_Plot_Raster_Scans.isEnabled() )
[docs] def select_all_dataframes_export_options(self): self.chbEx_DF_Traces.setChecked(self.chbEx_DF_Traces.isEnabled()) self.chbEx_DF_Levels.setChecked(self.chbEx_DF_Levels.isEnabled()) self.chbEx_DF_Levels_Lifetimes.setChecked( self.chbEx_DF_Levels_Lifetimes.isEnabled() ) self.chbEx_DF_Grouped_Levels.setChecked( self.chbEx_DF_Grouped_Levels.isEnabled() ) self.chbEx_DF_Grouped_Levels_Lifetimes.setChecked( self.chbEx_DF_Grouped_Levels_Lifetimes.isEnabled() ) self.chbEx_DF_Grouping_Info.setChecked(self.chbEx_DF_Grouping_Info.isEnabled())
@pyqtSlot(Exception)
[docs] def open_file_error(self, err: Exception): # logger.error(err) pass
[docs] def detect_remove_bursts(self, mode: str = None) -> None: if mode == "current": particles = [self.current_particle] elif mode == "selected": particles = self.get_checked_particles() else: particles = self.current_dataset.particles for part in particles: part.cpts.check_burst() self.remove_bursts(mode=mode)
[docs] def remove_bursts(self, mode: str = None, confirm: bool = True) -> None: if mode == "current": particles = [self.current_particle] elif mode == "selected": particles = self.get_checked_particles() else: particles = self.current_dataset.particles has_burst = [particle.has_burst for particle in particles] if sum(has_burst): if confirm: msgbx = TimedMessageBox(30, parent=self) msgbx.setIcon(QMessageBox.Question) msgbx.setText("Would you like to remove the photon bursts?") msgbx.set_timeout_text( message_pretime="(Removing photon bursts in ", message_posttime=" seconds)", ) msgbx.setWindowTitle("Photon bursts detected") msgbx.setStandardButtons(QMessageBox.No | QMessageBox.Yes) msgbx.setDefaultButton(QMessageBox.Yes) msgbx.show() msgbx_result, _ = msgbx.exec() if not confirm or msgbx_result == QMessageBox.Yes: for particle in particles: if particle.has_burst: particle.cpts.remove_bursts() particle.makelevelhists() if particle.has_groups: particle.remove_and_reset_grouping() self.display_data()
[docs] def restore_bursts(self, mode: str = None) -> None: if mode == "current": particles = [self.current_particle] elif mode == "selected": particles = self.get_checked_particles() else: particles = self.current_dataset.particles for part in particles: if part.cpts.bursts_deleted is not None: part.cpts.restore_bursts() part.makelevelhists() if part.has_groups: part.remove_and_reset_grouping() self.display_data()
[docs] def run_parallel_cpa(self, particle): particle.cpts.run_cpa(confidence=self.conf_parallel, run_levels=True)
[docs] def switching_frequency(self, all_selected: str = None): """ Calculates and exports the accumulated switching frequency of either all the particles, or only the selected. Parameters ---------- all_selected : {'all', 'selected'} Possible values are 'all' (default) or 'selected'. """ try: if all_selected is None: all_selected = "all" assert all_selected.lower() in [ "all", "selected", ], "mode parameter must be either 'all' or 'selected'." if all_selected == "all": data = self.treemodel.data(self.treemodel.index(0, 0), Qt.UserRole) # assert data. except Exception as exc: logger.info("Switching frequency analysis failed: ") else: pass
[docs] def get_checked(self): checked = list() for ind in range(self.treemodel.rowCount(self.dataset_index)): if self.part_nodes[ind].checked(): checked.append((ind, self.part_nodes[ind])) # checked_nums.append(ind) # checked_particles.append(self.part_nodes[ind]) return checked
[docs] def get_checked_nums(self): checked_nums = list() for ind in range(self.treemodel.rowCount(self.dataset_index)): if self.part_nodes[ind].checked(): checked_nums.append(ind + 1) return checked_nums
[docs] def get_checked_particles(self): checked_particles = list() for ind in range(self.treemodel.rowCount(self.dataset_index)): if self.part_nodes[ind].checked(): particle = self.tree2particle(ind) if type(particle) is smsh5.Particle: checked_particles.append(self.tree2particle(ind)) return checked_particles
[docs] def set_particle_check_state(self, particle_number: int, set_checked: bool): self.part_nodes[particle_number].setChecked(set_checked)
[docs] def set_level_resolved(self): self.current_dataset.level_resolved = True
# print(self.level_resolved)
[docs] def gui_plot_intensity_clicked(self, new_value): self.frmPlot_Int_Selection.setEnabled(new_value)
[docs] def gui_plot_lifetime_clicked(self, new_value): self.frmPlot_Lifetime_Selection.setEnabled(new_value)
[docs] def gui_export(self, mode: str = None): self.lock = Lock() f_dir = QFileDialog.getExistingDirectory(self) export_worker = ExportWorker( main_window=self, mode=mode, lock=self.lock, f_dir=f_dir ) sigs = export_worker.signals sigs.start_progress.connect(self.start_progress) sigs.progress.connect(self.update_progress) sigs.end_progress.connect(self.end_progress) sigs.status_message.connect(self.status_message) sigs.error.connect(self.error_handler) sigs.plot_trace_lock.connect(self.intensity_controller.plot_trace) sigs.plot_trace_export_lock.connect(self.intensity_controller.plot_trace) sigs.plot_levels_lock.connect(self.intensity_controller.plot_levels) sigs.plot_levels_export_lock.connect(self.intensity_controller.plot_levels) sigs.plot_group_bounds_export_lock.connect( self.intensity_controller.plot_group_bounds ) sigs.plot_grouping_bic_export_lock.connect( self.grouping_controller.plot_group_bic ) sigs.plot_decay_lock.connect(self.lifetime_controller.plot_decay) sigs.plot_decay_export_lock.connect(self.lifetime_controller.plot_decay) sigs.plot_convd_lock.connect(self.lifetime_controller.plot_convd) sigs.plot_convd_export_lock.connect(self.lifetime_controller.plot_convd) sigs.plot_decay_convd_export_lock.connect( self.lifetime_controller.plot_decay_and_convd ) sigs.plot_decay_convd_residuals_export_lock.connect( self.lifetime_controller.plot_decay_convd_and_hist ) sigs.show_residual_widget_lock.connect( self.lifetime_controller.show_residuals_widget ) sigs.plot_residuals_export_lock.connect(self.lifetime_controller.plot_residuals) sigs.plot_spectra_export_lock.connect(self.spectra_controller.plot_spectra) sigs.plot_raster_scan_export_lock.connect( self.raster_scan_controller.plot_raster_scan ) sigs.plot_corr_lock.connect(self.antibunch_controller.plot_corr) sigs.plot_corr_export_lock.connect(self.antibunch_controller.plot_corr) self.threadpool.start(export_worker)
[docs] def convert_file_dialog(self): convert_file = ConvertFileDialog(mainwindow=self) convert_file.exec()
[docs] def range_selection(self): range_selection_dialog = RangeSelectionDialog(main_window=self) if range_selection_dialog.exec_(): selection_indexes = range_selection_dialog.get_selection( max_range=len(self.part_nodes) ) mode_only, mode_add, mode_remove, _ = range_selection_dialog.get_mode() if max(selection_indexes) > len(self.part_nodes): msg = QMessageBox(self) msg.setWindowTitle("Range Selection") msg.setText("Selection out of bounds!") msg.setIcon(QMessageBox.Warning) msg.setStandardButtons(QMessageBox.Ok) msg.exec() else: for i, node in enumerate(self.part_nodes): is_checked = node.checked() if mode_only: if i + 1 in selection_indexes: is_checked = True else: is_checked = False elif mode_add: if i + 1 in selection_indexes: is_checked = True elif mode_remove: if i + 1 in selection_indexes: is_checked = False else: if i + 1 not in selection_indexes: is_checked = True else: is_checked = False if is_checked != node.checked(): node.setChecked(is_checked) num_checked = sum([node.checked() for node in self.part_nodes]) self.lblNum_Selected.setText(str(num_checked))
[docs] def reset_gui(self): """Sets the GUI elements to enabled if it should be accessible.""" logger.info("Reset GUI") new_state = self.data_loaded # Intensity self.tabIntensity.setEnabled(new_state) self.btnApplyBin.setEnabled(new_state) self.btnApplyBinAll.setEnabled(new_state) self.btnResolve.setEnabled(new_state) self.btnResolve_Selected.setEnabled(new_state) self.btnResolveAll.setEnabled(new_state) self.cmbConfIndex.setEnabled(new_state) self.spbBinSize.setEnabled(new_state) self.actionReset_Analysis.setEnabled(new_state) self.actionSave_Selected.setEnabled(new_state) enable_levels = False if new_state: enable_levels = self.current_dataset.has_levels # self.actionTrim_Dead_Traces.setEnabled(enable_levels) # self.chbGroup_Auto_Apply.setEnabled(enable_levels) # self.menuRegion_of_Interest.setEnabled(enable_levels) # Lifetime self.tabLifetime.setEnabled(new_state) self.btnFitParameters.setEnabled(new_state) self.btnLoadIRF.setEnabled(new_state) if new_state: enable_fitting = self.lifetime_controller.irf_loaded else: enable_fitting = new_state self.chbHasIRF.setChecked(self.lifetime_controller.irf_loaded) self.btnFitCurrent.setEnabled(enable_fitting) self.btnFit.setEnabled(enable_fitting) self.btnFitAll.setEnabled(enable_fitting) self.btnFitSelected.setEnabled(enable_fitting) self.btnNextLevel.setEnabled(enable_levels) self.btnPrevLevel.setEnabled(enable_levels) # print(enable_levels) # Grouping if self.current_dataset is not None: has_levels = any( [particle.has_levels for particle in self.current_dataset.particles] ) self.tabGrouping.setEnabled(has_levels) self.btnGroupCurrent.setEnabled(has_levels) self.btnGroupSelected.setEnabled(has_levels) self.btnGroupAll.setEnabled(has_levels) self.btnGroupGlobal.setEnabled(has_levels) if has_levels: has_groups = any( [ particle.has_groups for particle in self.current_dataset.particles if not particle.is_secondary_part ] ) self.btnApplyGroupsCurrent.setEnabled(has_groups) self.btnApplyGroupsSelected.setEnabled(has_groups) self.btnApplyGroupsAll.setEnabled(has_groups) has_global_grouping = any( [ particle.has_global_grouping for particle in self.current_dataset.particles ] ) self.chbInt_Show_Global_Groups.setEnabled(has_global_grouping) # Spectral if self.current_dataset and self.current_dataset.has_spectra: self.tabSpectra.setEnabled(True) self.btnSubBackground.setEnabled(new_state) self.btnRotateROI.setEnabled(new_state) else: self.tabSpectra.setEnabled(False)
[docs] def close_file(self): if self.current_dataset is not None: self.current_particle = None self.current_dataset.file.close() self.current_dataset = None self.treemodel = DatasetTreeModel() self.treeViewParticles.setModel(self.treemodel) self.data_loaded = False
[docs] def error_handler(self, e: Exception): raise e
[docs] def display_on(): ctypes.windll.kernel32.SetThreadExecutionState(0x80000002) logger.info("Execution State set to Always On")
[docs] def display_reset(): ctypes.windll.kernel32.SetThreadExecutionState(0x80000000) logger.info("Execution State Reset") sys.exit(0)
[docs] def main(): """ Creates QApplication and runs MainWindow(). """ # convert_convert_ui() app = QApplication([]) print("Currently used style:", app.style().metaObject().className()) print("Available styles:", QStyleFactory.keys()) logger.info("App created") main_window = MainWindow() logger.info("Main Window created") main_window.show() should_calc = main_window.sums_file_check() if should_calc: app.processEvents() main_window.calc_store_sums() app.processEvents() # main_window.tabSpectra.repaint() logger.info("Main Window shown") if system() == "Windows": display_on() app.instance().exec_() if system() == "Windows": display_reset() logger.info("App excuted")
if __name__ == "__main__": # Create version file for distribution. Or use the command bellow: # create-version-file version.yml --outfile versionfile.txt --version SMS_VERSION if "--dev" in sys.argv: try: # noinspection PyUnresolvedReferences import pyinstaller_versionfile pyinstaller_versionfile.create_versionfile_from_input_file( output_file="versionfile.txt", input_file="version.yml", version=SMS_VERSION, ) except ImportError as e: pass if "--debug" not in sys.argv: freeze_support() Process(target=main).start() else: main()