class DAQ_PID(QObject): """ """ log_signal = pyqtSignal(str) #look for eventual model files command_pid = pyqtSignal(ThreadCommand) command_stage = pyqtSignal(ThreadCommand) move_done_signal = pyqtSignal(str, float) models = [] try: model_mod = importlib.import_module('pymodaq_pid_models') for ind_file, entry in enumerate(os.scandir(os.path.join(model_mod.__path__[0], 'models'))): if not entry.is_dir() and entry.name != '__init__.py': try: file, ext = os.path.splitext(entry.name) importlib.import_module('.'+file, model_mod.__name__+'.models') models.append(file) except Exception as e: print(e) if 'PIDModelMock' in models: mods = models mods.pop(models.index('PIDModelMock')) models = ['PIDModelMock'] models.extend(mods) except Exception as e: print(e) if len(models) == 0: logger.warning('No valid installed models') def __init__(self,area, detector_modules = [], actuator_modules =[]): QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super(DAQ_PID,self).__init__() self.settings = Parameter.create(title='PID settings', name='pid_settings', type='group', children=params) self.title = 'PyMoDAQ PID' self.Initialized_state = False self.model_class = None self.detector_modules = detector_modules self.actuator_modules = actuator_modules self.dock_area = area self.overshoot = None self.check_moving = False self.preset_manager = PresetManager() self.setupUI() self.command_stage.connect(self.move_Abs) #to be compatible with actuator modules within daq scan self.enable_controls_pid(False) self.enable_controls_pid_run(False) def ini_PID(self): if self.ini_PID_action.isChecked(): output_limits =[None,None] if self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_min_enabled').value(): output_limits[0] = self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_min').value() if self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_max_enabled').value(): output_limits[1] = self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_max').value() self.PIDThread = QThread() pid_runner = PIDRunner(self.model_class, [mod.move_done_signal for mod in self.actuator_modules], [mod.grab_done_signal for mod in self.detector_modules], [mod.command_stage for mod in self.actuator_modules], [mod.command_detector for mod in self.detector_modules], dict(Kp=self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'kp').value(), Ki=self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'ki').value(), Kd=self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'kd').value(), setpoint=self.settings.child('main_settings', 'pid_controls', 'set_point').value(), sample_time=self.settings.child('main_settings', 'pid_controls', 'sample_time').value()/1000, output_limits=output_limits, auto_mode=False), filter=dict(enable=self.settings.child('main_settings', 'pid_controls', 'filter', 'filter_enable').value(), value=self.settings.child('main_settings', 'pid_controls', 'filter', 'filter_step').value()), det_averaging=[mod.settings.child('main_settings', 'Naverage').value() for mod in self.detector_modules], ) self.PIDThread.pid_runner = pid_runner pid_runner.pid_output_signal.connect(self.process_output) pid_runner.status_sig.connect(self.thread_status) self.command_pid.connect(pid_runner.queue_command) pid_runner.moveToThread(self.PIDThread) self.PIDThread.start() self.pid_led.set_as_true() self.enable_controls_pid_run(True) else: if hasattr(self,'PIDThread'): if self.PIDThread.isRunning(): try: self.PIDThread.quit() except: pass self.pid_led.set_as_false() self.enable_controls_pid_run(False) self.Initialized_state = True pyqtSlot(dict) def process_output(self, datas): self.output_viewer.show_data([[dat] for dat in datas['output']]) self.input_viewer.show_data([[dat] for dat in datas['input']]) self.currpoint_sb.setValue(np.mean(datas['input'])) if self.check_moving: if np.abs(np.mean(datas['input'])-self.settings.child('main_settings', 'pid_controls', 'set_point').value()) < \ self.settings.child('main_settings', 'epsilon').value(): self.move_done_signal.emit(self.title, np.mean(datas['input'])) self.check_moving = False print('Move from {:s} is done: {:f}'.format('PID', np.mean(datas['input']))) @pyqtSlot(ThreadCommand) def move_Abs(self, command=ThreadCommand()): """ """ if command.command == "move_Abs": self.check_moving = True self.setpoint_sb.setValue(command.attributes[0]) QtWidgets.QApplication.processEvents() def enable_controls_pid(self,enable = False): self.ini_PID_action.setEnabled(enable) self.setpoint_sb.setOpts(enabled = enable) def enable_controls_pid_run(self,enable = False): self.run_action.setEnabled(enable) self.pause_action.setEnabled(enable) def setupUI(self): self.dock_pid = Dock('PID controller', self.dock_area) self.dock_area.addDock(self.dock_pid) #%% create logger dock self.logger_dock=Dock("Logger") self.logger_list=QtWidgets.QListWidget() self.logger_list.setMinimumWidth(300) self.logger_dock.addWidget(self.logger_list) self.dock_area.addDock(self.logger_dock,'right') self.logger_dock.setVisible(True) widget = QtWidgets.QWidget() widget_toolbar = QtWidgets.QWidget() verlayout = QtWidgets.QVBoxLayout() widget.setLayout(verlayout) toolbar_layout = QtWidgets.QGridLayout() widget_toolbar.setLayout(toolbar_layout) iconquit = QtGui.QIcon() iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.quit_action = QtWidgets.QPushButton(iconquit, "Quit") self.quit_action.setToolTip('Quit the application') toolbar_layout.addWidget(self.quit_action,0,0,1,2) self.quit_action.clicked.connect(self.quit_fun) iconini = QtGui.QIcon() iconini.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/ini.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ini_model_action = QtWidgets.QPushButton(iconini, "Init Model") self.ini_model_action.setToolTip('Initialize the chosen model') toolbar_layout.addWidget(self.ini_model_action,2,0) self.ini_model_action.clicked.connect(self.ini_model) self.model_led = QLED() toolbar_layout.addWidget(self.model_led, 2,1) self.ini_PID_action = QtWidgets.QPushButton(iconini, "Init PID") self.ini_PID_action.setToolTip('Initialize the PID loop') toolbar_layout.addWidget(self.ini_PID_action,2,2) self.ini_PID_action.setCheckable(True) self.ini_PID_action.clicked.connect(self.ini_PID) self.pid_led = QLED() toolbar_layout.addWidget(self.pid_led, 2,3) self.iconrun = QtGui.QIcon() self.iconrun.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.icon_stop = QtGui.QIcon() self.icon_stop.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/stop.png")) self.run_action = QtWidgets.QPushButton(self.iconrun, "", None) self.run_action.setToolTip('Start PID loop') self.run_action.setCheckable(True) toolbar_layout.addWidget(self.run_action,0,2) self.run_action.clicked.connect(self.run_PID) iconpause = QtGui.QIcon() iconpause.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/pause.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.pause_action = QtWidgets.QPushButton(iconpause, "", None) self.pause_action.setToolTip('Pause PID') self.pause_action.setCheckable(True) toolbar_layout.addWidget(self.pause_action,0,3) self.pause_action.setChecked(True) self.pause_action.clicked.connect(self.pause_PID) lab = QtWidgets.QLabel('Set Point:') toolbar_layout.addWidget(lab, 3,0,1,2) self.setpoint_sb = custom_tree.SpinBoxCustom() self.setpoint_sb.setMinimumHeight(40) font = self.setpoint_sb.font() font.setPointSizeF(20) self.setpoint_sb.setFont(font) self.setpoint_sb.setDecimals(6) toolbar_layout.addWidget(self.setpoint_sb,3,2,1,2) self.setpoint_sb.valueChanged.connect(self.settings.child('main_settings', 'pid_controls', 'set_point').setValue) lab1 = QtWidgets.QLabel('Current Point:') toolbar_layout.addWidget(lab1, 4,0,1,2) self.currpoint_sb = custom_tree.SpinBoxCustom() self.currpoint_sb.setMinimumHeight(40) self.currpoint_sb.setReadOnly(True) self.currpoint_sb.setDecimals(6) self.currpoint_sb.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) font = self.currpoint_sb.font() font.setPointSizeF(20) self.currpoint_sb.setFont(font) toolbar_layout.addWidget(self.currpoint_sb,4,2,1,2) #create main parameter tree self.settings_tree = ParameterTree() self.settings_tree.setParameters(self.settings, showTop=False) verlayout.addWidget(widget_toolbar) verlayout.addWidget(self.settings_tree) self.dock_output = Dock('PID output') widget_output = QtWidgets.QWidget() self.output_viewer = Viewer0D(widget_output) self.dock_output.addWidget(widget_output) self.dock_area.addDock(self.dock_output, 'right') self.dock_input = Dock('PID input') widget_input = QtWidgets.QWidget() self.input_viewer = Viewer0D(widget_input) self.dock_input.addWidget(widget_input) self.dock_area.addDock(self.dock_input, 'bottom',self.dock_output) if len(self.models) != 0: self.get_set_model_params(self.models[0]) #connecting from tree self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed)#any changes on the settings will update accordingly the detector self.dock_pid.addWidget(widget) def get_set_model_params(self, model_file): self.settings.child('models', 'model_params').clearChildren() model = importlib.import_module('.' + model_file, self.model_mod.__name__+'.models') model_class = getattr(model, model_file) params = getattr(model_class, 'params') self.settings.child('models', 'model_params').addChildren(params) def run_PID(self): if self.run_action.isChecked(): self.run_action.setIcon(self.icon_stop) self.command_pid.emit(ThreadCommand('start_PID', [self.model_class.curr_input])) QtWidgets.QApplication.processEvents() QtWidgets.QApplication.processEvents() self.command_pid.emit(ThreadCommand('run_PID', [self.model_class.curr_output])) else: self.run_action.setIcon(self.iconrun) self.command_pid.emit(ThreadCommand('stop_PID')) QtWidgets.QApplication.processEvents() def pause_PID(self): self.command_pid.emit(ThreadCommand('pause_PID', [self.pause_action.isChecked()])) def update_status(self,txt,log_type=None): """ Show the txt message in the status bar with a delay of wait_time ms. =============== =========== ======================= **Parameters** **Type** **Description** *txt* string The message to show *wait_time* int the delay of showing *log_type* string the type of the log =============== =========== ======================= """ try: if log_type is not None: self.log_signal.emit(txt) logging.info(txt) except Exception as e: pass @pyqtSlot(str) def add_log(self,txt): """ Add the QListWisgetItem initialized with txt informations to the User Interface logger_list and to the save_parameters.logger array. =============== =========== ====================== **Parameters** **Type** **Description** *txt* string the log info to add. =============== =========== ====================== """ try: now=datetime.datetime.now() new_item=QtWidgets.QListWidgetItem(now.strftime('%Y/%m/%d %H:%M:%S')+": "+txt) self.logger_list.addItem(new_item) except: pass def set_file_preset(self,model): """ Set a file managers from the converted xml file given by the filename parameter. =============== =========== =================================================== **Parameters** **Type** **Description** *filename* string the name of the xml file to be converted/treated =============== =========== =================================================== Returns ------- (Object list, Object list) tuple The updated (Move modules list, Detector modules list). See Also -------- custom_tree.XML_file_to_parameter, set_param_from_param, stop_moves, update_status,DAQ_Move_main.daq_move, DAQ_viewer_main.daq_viewer """ filename = os.path.join(get_set_pid_path(), model + '.xml') self.preset_file = filename self.preset_manager.set_file_preset(filename, show=False) self.move_docks = [] self.det_docks_settings = [] self.det_docks_viewer = [] move_forms = [] actuator_modules = [] detector_modules = [] move_types = [] ################################################################# ###### sort plugins by IDs and within the same IDs by Master and Slave status plugins=[{'type': 'move', 'value': child} for child in self.preset_manager.preset_params.child(('Moves')).children()]+[{'type': 'det', 'value': child} for child in self.preset_manager.preset_params.child(('Detectors')).children()] for plug in plugins: plug['ID']=plug['value'].child('params','main_settings','controller_ID').value() if plug["type"]=='det': plug['status']=plug['value'].child('params','detector_settings','controller_status').value() else: plug['status']=plug['value'].child('params','move_settings', 'multiaxes', 'multi_status').value() IDs=list(set([plug['ID'] for plug in plugins])) #%% plugins_sorted=[] for id in IDs: plug_Ids=[] for plug in plugins: if plug['ID']==id: plug_Ids.append(plug) plug_Ids.sort(key=lambda status: status['status']) plugins_sorted.append(plug_Ids) ################################################################# ####################### ind_move=-1 ind_det=-1 for plug_IDs in plugins_sorted: for ind_plugin, plugin in enumerate(plug_IDs): plug_name=plugin['value'].child(('name')).value() plug_init=plugin['value'].child(('init')).value() plug_settings=plugin['value'].child(('params')) if plugin['type'] == 'move': ind_move+=1 plug_type=plug_settings.child('main_settings','move_type').value() self.move_docks.append(Dock(plug_name, size=(150,250))) if ind_move==0: self.dock_area.addDock(self.move_docks[-1], 'top',self.logger_dock) else: self.dock_area.addDock(self.move_docks[-1], 'above',self.move_docks[-2]) move_forms.append(QtWidgets.QWidget()) mov_mod_tmp=DAQ_Move(move_forms[-1],plug_name) mov_mod_tmp.ui.Stage_type_combo.setCurrentText(plug_type) mov_mod_tmp.ui.Quit_pb.setEnabled(False) QtWidgets.QApplication.processEvents() set_param_from_param(mov_mod_tmp.settings,plug_settings) QtWidgets.QApplication.processEvents() mov_mod_tmp.bounds_signal[bool].connect(self.stop_moves) self.move_docks[-1].addWidget(move_forms[-1]) actuator_modules.append(mov_mod_tmp) try: if ind_plugin==0: #should be a master type plugin if plugin['status']!="Master": raise Exception('error in the master/slave type for plugin {}'.format(plug_name)) if plug_init: actuator_modules[-1].ui.IniStage_pb.click() QtWidgets.QApplication.processEvents() if 'Mock' in plug_type: QThread.msleep(500) else: QThread.msleep(4000) # to let enough time for real hardware to init properly QtWidgets.QApplication.processEvents() master_controller=actuator_modules[-1].controller else: if plugin['status']!="Slave": raise Exception('error in the master/slave type for plugin {}'.format(plug_name)) if plug_init: actuator_modules[-1].controller=master_controller actuator_modules[-1].ui.IniStage_pb.click() QtWidgets.QApplication.processEvents() if 'Mock' in plug_type: QThread.msleep(500) else: QThread.msleep(4000) # to let enough time for real hardware to init properly QtWidgets.QApplication.processEvents() except Exception as e: self.update_status(getLineInfo()+ str(e),'log') else: ind_det+=1 plug_type=plug_settings.child('main_settings','DAQ_type').value() plug_subtype=plug_settings.child('main_settings','detector_type').value() self.det_docks_settings.append(Dock(plug_name+" settings", size=(150,250))) self.det_docks_viewer.append(Dock(plug_name+" viewer", size=(350,350))) if ind_det==0: self.logger_dock.area.addDock(self.det_docks_settings[-1], 'bottom', self.dock_input) #dock_area of the logger dock else: self.dock_area.addDock(self.det_docks_settings[-1], 'bottom',self.det_docks_settings[-2]) self.dock_area.addDock(self.det_docks_viewer[-1],'right',self.det_docks_settings[-1]) det_mod_tmp=DAQ_Viewer(self.dock_area,dock_settings=self.det_docks_settings[-1], dock_viewer=self.det_docks_viewer[-1],title=plug_name, DAQ_type=plug_type, parent_scan=self) detector_modules.append(det_mod_tmp) detector_modules[-1].ui.Detector_type_combo.setCurrentText(plug_subtype) detector_modules[-1].ui.Quit_pb.setEnabled(False) set_param_from_param(det_mod_tmp.settings,plug_settings) QtWidgets.QApplication.processEvents() try: if ind_plugin==0: #should be a master type plugin if plugin['status']!="Master": raise Exception('error in the master/slave type for plugin {}'.format(plug_name)) if plug_init: detector_modules[-1].ui.IniDet_pb.click() QtWidgets.QApplication.processEvents() if 'Mock' in plug_subtype: QThread.msleep(500) else: QThread.msleep(4000) # to let enough time for real hardware to init properly QtWidgets.QApplication.processEvents() master_controller=detector_modules[-1].controller else: if plugin['status']!="Slave": raise Exception('error in the master/slave type for plugin {}'.format(plug_name)) if plug_init: detector_modules[-1].controller=master_controller detector_modules[-1].ui.IniDet_pb.click() QtWidgets.QApplication.processEvents() if 'Mock' in plug_subtype: QThread.msleep(500) else: QThread.msleep(4000) # to let enough time for real hardware to init properly QtWidgets.QApplication.processEvents() except Exception as e: self.update_status(getLineInfo()+ str(e),'log') detector_modules[-1].settings.child('main_settings','overshoot').show() detector_modules[-1].overshoot_signal[bool].connect(self.stop_moves) QtWidgets.QApplication.processEvents() return actuator_modules,detector_modules pyqtSlot(bool) def stop_moves(self,overshoot): """ Foreach module of the move module object list, stop motion. See Also -------- stop_scan, DAQ_Move_main.daq_move.stop_Motion """ self.overshoot = overshoot for mod in self.actuator_modules: mod.stop_Motion() def set_default_preset(self): actuators = self.model_class.actuators actuator_names = self.model_class.actuators_name detectors_type = self.model_class.detectors_type detectors = self.model_class.detectors detectors_name = self.model_class.detectors_name detector_modules = [] for ind_det, det in enumerate(detectors): detector_modules.append(DAQ_Viewer(area, title=detectors_name[ind_det], DAQ_type=detectors_type[ind_det])) #self.detector_modules[-1].ui.IniDet_pb.click() QtWidgets.QApplication.processEvents() detector_modules[-1].ui.Detector_type_combo.setCurrentText(detectors[ind_det]) detector_modules[-1].ui.Quit_pb.setEnabled(False) self.dock_area.addDock(self.dock_output, 'bottom') self.dock_area.moveDock(self.dock_input, 'bottom', self.dock_output) self.dock_area.addDock(self.dock_pid, 'left') dock_moves = [] actuator_modules = [] for ind_act, act in enumerate(actuators): form = QtWidgets.QWidget() dock_moves.append(Dock(actuator_names[ind_act])) area.addDock(dock_moves[-1], 'bottom', self.dock_pid) dock_moves[-1].addWidget(form) actuator_modules.append(DAQ_Move(form)) QtWidgets.QApplication.processEvents() actuator_modules[-1].ui.Stage_type_combo.setCurrentText(actuators[ind_act]) actuator_modules[-1].ui.Quit_pb.setEnabled(False) #self.actuator_modules[-1].ui.IniStage_pb.click() #QThread.msleep(1000) QtWidgets.QApplication.processEvents() return actuator_modules, detector_modules def ini_model(self): try: model_name = self.settings.child('models', 'model_class').value() model = importlib.import_module('.' +model_name, self.model_mod.__name__+'.models') self.model_class = getattr(model, model_name)(self) #try to get corresponding managers file filename = os.path.join(get_set_pid_path(), model_name + '.xml') if os.path.isfile(filename): self.actuator_modules, self.detector_modules = self.set_file_preset(model_name) else: self.actuator_modules, self.detector_modules = self.set_default_preset() # # connecting to logger # for mov in self.actuator_modules: # mov.log_signal[str].connect(self.add_log) # for det in self.detector_modules: # det.log_signal[str].connect(self.add_log) # self.log_signal[str].connect(self.add_log) self.model_class.ini_model() self.enable_controls_pid(True) self.model_led.set_as_true() self.ini_model_action.setEnabled(False) except Exception as e: self.update_status(getLineInfo() + str(e), log_type='log') def quit_fun(self): """ """ try: try: self.PIDThread.exit() except Exception as e: print(e) for module in self.actuator_modules: try: module.quit_fun() QtWidgets.QApplication.processEvents() QThread.msleep(1000) QtWidgets.QApplication.processEvents() except Exception as e: print(e) for module in self.detector_modules: try: module.stop_all() QtWidgets.QApplication.processEvents() module.quit_fun() QtWidgets.QApplication.processEvents() QThread.msleep(1000) QtWidgets.QApplication.processEvents() except Exception as e: print(e) areas=self.dock_area.tempAreas[:] for area in areas: area.win.close() QtWidgets.QApplication.processEvents() QThread.msleep(1000) QtWidgets.QApplication.processEvents() self.dock_area.parent().close() except Exception as e: print(e) def parameter_tree_changed(self,param,changes): """ Foreach value changed, update : * Viewer in case of **DAQ_type** parameter name * visibility of button in case of **show_averaging** parameter name * visibility of naverage in case of **live_averaging** parameter name * scale of axis **else** (in 2D pymodaq type) Once done emit the update settings signal to link the commit. =============== =================================== ================================================================ **Parameters** **Type** **Description** *param* instance of ppyqtgraph parameter the parameter to be checked *changes* tuple list Contain the (param,changes,info) list listing the changes made =============== =================================== ================================================================ See Also -------- change_viewer, daq_utils.custom_parameter_tree.iter_children """ for param, change, data in changes: path = self.settings.childPath(param) if change == 'childAdded': pass elif change == 'value': if param.name() == 'model_class': self.get_set_model_params(param.value()) elif param.name() == 'module_settings': if param.value(): self.settings.sigTreeStateChanged.disconnect( self.parameter_tree_changed) param.setValue(False) self.settings.sigTreeStateChanged.connect( self.parameter_tree_changed) self.preset_manager.set_PID_preset(self.settings.child('models','model_class').value()) elif param.name() == 'refresh_plot_time' or param.name() == 'timeout': self.command_pid.emit(ThreadCommand('update_timer', [param.name(),param.value()])) elif param.name() == 'set_point': if self.pid_led.state: self.command_pid.emit(ThreadCommand('update_options', dict(setpoint=param.value()))) else: output = self.model_class.convert_output(param.value(),0, stab=False) for ind_act, act in enumerate(self.actuator_modules): act.move_Abs(output[ind_act]) elif param.name() == 'sample_time': self.command_pid.emit(ThreadCommand('update_options', dict(sample_time=param.value()))) elif param.name() in custom_tree.iter_children(self.settings.child('main_settings', 'pid_controls', 'output_limits'), []): output_limits = [None, None] if self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_min_enabled').value(): output_limits[0] = self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_min').value() if self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_max_enabled').value(): output_limits[1] = self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_max').value() self.command_pid.emit(ThreadCommand('update_options', dict(output_limits=output_limits))) elif param.name() in custom_tree.iter_children(self.settings.child('main_settings', 'pid_controls', 'filter'), []): self.command_pid.emit(ThreadCommand('update_filter', [dict(enable=self.settings.child('main_settings', 'pid_controls', 'filter', 'filter_enable').value(), value=self.settings.child('main_settings', 'pid_controls', 'filter', 'filter_step').value())])) elif param.name() in custom_tree.iter_children(self.settings.child('main_settings', 'pid_controls', 'pid_constants'), []): Kp = self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'kp').value() Ki = self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'ki').value() Kd = self.settings.child('main_settings', 'pid_controls', 'pid_constants', 'kd').value() self.command_pid.emit(ThreadCommand('update_options', dict(tunings= (Kp, Ki, Kd)))) elif param.name() in custom_tree.iter_children(self.settings.child('models', 'model_params'),[]): self.model_class.update_settings(param) elif param.name() == 'detector_modules': self.model_class.update_detector_names() elif change == 'parent': pass @pyqtSlot(list) def thread_status(self,status): # general function to get datas/infos from all threads back to the main """ | General function to get datas/infos from all threads back to the main. | Switch the status with : * *"Update status"* : Update the status bar with the status attribute txt message """ if status[0]=="Update_Status": self.update_status(status[1],log_type=status[2])
class Spectrometer(QObject): """ Defines a Spectrometer object, unified interface for many spectrometers Parameters that could be set in the selected detector plugin (should be defined there): 'laser_wl' : value of the configured laser (could eventually be changed, case of Xplora, Labram...) 'spectro_center_freq': value of the configured grating center wavelength (could eventually be changed, case of Shamrock, Xplora...) """ #custom signal that will be fired sometimes. Could be connected to an external object method or an internal method log_signal = Signal(str) #list of dicts enabling the settings tree on the user interface params = [ { 'title': 'Configuration settings:', 'name': 'config_settings', 'type': 'group', 'children': [ { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl', 'type': 'float', 'value': 515. }, { 'title': 'Laser wavelength (nm):', 'name': 'laser_wl_list', 'type': 'list', 'limits': [''] }, { 'title': 'Current Detector:', 'name': 'curr_det', 'type': 'str', 'value': '' }, { 'title': 'Show detector:', 'name': 'show_det', 'type': 'bool', 'value': False }, ], }, { 'title': 'Calibration settings:', 'name': 'calib_settings', 'type': 'group', 'children': [ { 'title': 'Use calibration:', 'name': 'use_calib', 'type': 'bool', 'value': False }, { 'title': 'Save calibration', 'name': 'save_calib', 'type': 'bool_push', 'value': False }, { 'title': 'Load calibration', 'name': 'load_calib', 'type': 'bool_push', 'value': False }, { 'title': 'Calibration coeffs:', 'name': 'calib_coeffs', 'type': 'group', 'children': [ { 'title': 'Center wavelength (nm):', 'name': 'center_calib', 'type': 'float', 'value': 515. }, { 'title': 'Slope (nm/pxl):', 'name': 'slope_calib', 'type': 'float', 'value': 1. }, { 'title': 'Second order :', 'name': 'second_calib', 'type': 'float', 'value': 0 }, { 'title': 'third:', 'name': 'third_calib', 'type': 'float', 'value': 0 }, ] }, { 'title': 'Perform calibration:', 'name': 'do_calib', 'type': 'bool', 'value': False }, ] }, { 'title': 'Acquisition settings:', 'name': 'acq_settings', 'type': 'group', 'children': [ { 'title': 'Spectro. Center:', 'name': 'spectro_center_freq', 'type': 'float', 'value': 800, }, { 'title': 'Spectro. Center:', 'name': 'spectro_center_freq_txt', 'type': 'str', 'value': '????', 'readonly': True }, { 'title': 'Units:', 'name': 'units', 'type': 'list', 'value': 'nm', 'limits': ['nm', 'cm-1', 'eV'] }, { 'title': 'Exposure (ms):', 'name': 'exposure_ms', 'type': 'float', 'value': 100, }, ] }, ] def __init__(self, parent): QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates)) super().__init__() if not isinstance(parent, DockArea): raise Exception('no valid parent container, expected a DockArea') self.wait_time = 2000 #ms self.offline = True self.dockarea = parent self.mainwindow = parent.parent() self.spectro_widget = QtWidgets.QWidget() self.data_dict = None """ List of the possible plugins that could be used with Spectrometer module type : dimensionality of the detector name: name of the plugin calib = True means there is a builtin calibration of the frequency axis movable : tells if the dispersion can be set (for instance by moving a grating) unit: valid only if calib is True. Unit of the calibration axis (x_axis of the detector), most often in nanometers. Possible values are 'nm', 'radfs' (rad/femtosecond), 'eV' laser: if False, laser cannot be changed by the program, do it manually laser_list: if laser is True, laser_list gives a list of selectable lasers """ self.current_det = None # will be after initialization self.laser_set_manual = True #init the object parameters self.detector = None self.save_file_pathname = None self._spectro_wl = 550 # center wavelngth of the spectrum self.viewer_freq_axis = utils.Axis(data=None, label='Photon energy', units='') self.raw_data = [] #init the user interface self.dashboard = self.set_dashboard() self.dashboard.preset_loaded_signal.connect( lambda: self.show_detector(False)) self.dashboard.preset_loaded_signal.connect(self.set_detector) self.dashboard.preset_loaded_signal.connect(self.initialized) self.set_GUI() self.dashboard.new_preset_created.connect( lambda: self.create_menu(self.menubar)) self.show_detector(False) self.dockarea.setEnabled(False) def set_dashboard(self): params = [ { 'title': 'Spectro Settings:', 'name': 'spectro_settings', 'type': 'group', 'children': [ { 'title': 'Is calibrated?', 'name': 'iscalibrated', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has internal frequency calibration or not.' }, { 'title': 'Movable?', 'name': 'ismovable', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has a functionality to change its central frequency: as a movable grating' ' for instance.' }, { 'title': 'Laser selectable?', 'name': 'laser_selectable', 'type': 'bool', 'value': False, 'tooltip': 'Whether the selected plugin has a functionality to change its excitation ray' }, { 'title': 'Laser ray:', 'name': 'laser_ray', 'type': 'list', 'value': '', 'show_pb': True, 'tooltip': 'List of settable laser rays (not manual ones)' }, ] }, ] dashboard = DashBoard(self.dockarea.addTempArea()) dashboard.set_preset_path(spectro_path) options = [ dict(path='saving_options', options_dict=dict(visible=False)), dict(path='use_pid', options_dict=dict(visible=False)), dict(path='Moves', options_dict=dict(visible=False)) ] dashboard.set_extra_preset_params(params, options) dashboard.dockarea.window().setVisible(False) return dashboard def set_GUI(self): ########################################### ########################################### #init the docks containing the main widgets ####################################################################################################################### #create a dock containing a viewer object, displaying the data for the spectrometer self.dock_viewer = Dock('Viewer dock', size=(350, 350)) self.dockarea.addDock(self.dock_viewer, 'left') target_widget = QtWidgets.QWidget() self.viewer = Viewer1D(target_widget) self.dock_viewer.addWidget(target_widget) ################################################################ #create a logger dock where to store info senf from the programm self.dock_logger = Dock("Logger") self.logger_list = QtWidgets.QListWidget() self.logger_list.setMinimumWidth(300) self.dock_logger.addWidget(self.logger_list) self.dockarea.addDock(self.dock_logger, 'right') self.log_signal[str].connect(self.add_log) ############################################ # creating a menubar self.menubar = self.mainwindow.menuBar() self.create_menu(self.menubar) #creating a toolbar self.toolbar = QtWidgets.QToolBar() self.create_toolbar() self.mainwindow.addToolBar(self.toolbar) #creating a status bar self.statusbar = QtWidgets.QStatusBar() self.statusbar.setMaximumHeight(25) self.status_laser = QtWidgets.QLabel('????') self.status_laser.setAlignment(Qt.AlignCenter) #self.status_laser.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) #self.status_laser.setReadOnly(True) self.status_laser.setMaximumWidth(80) self.status_laser.setMinimumWidth(80) self.status_laser.setToolTip('Current laser wavelength') self.status_laser.setStyleSheet("background-color: red") self.status_center = QtWidgets.QLabel('????') self.status_center.setAlignment(Qt.AlignCenter) #self.status_center.setReadOnly(True) #self.status_center.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) self.status_center.setMaximumWidth(80) self.status_center.setMinimumWidth(80) self.status_center.setToolTip( 'center frequency of the spectrum, either in nm or cm-1') self.status_center.setStyleSheet("background-color: red") self.status_init = QLED() self.status_init.setToolTip('Initialization state of the detector') self.status_init.set_as_false() self.status_init.clickable = False self.statusbar.addPermanentWidget(self.status_laser) self.statusbar.addPermanentWidget(self.status_center) self.statusbar.addPermanentWidget(self.status_init) self.dockarea.window().setStatusBar(self.statusbar) ############################################# self.settings = Parameter.create(name='settings', type='group', children=self.params) self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) dock_config_settings = Dock('Configuration', size=(300, 350)) self.dockarea.addDock(dock_config_settings, 'above', self.dock_logger) # create main parameter tree self.config_settings_tree = ParameterTree() dock_config_settings.addWidget(self.config_settings_tree, 10) self.config_settings_tree.setMinimumWidth(300) self.config_settings_tree.setParameters(self.settings.child( ('config_settings')), showTop=False) #any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied dock_calib_settings = Dock('Calibration', size=(300, 350)) self.dockarea.addDock(dock_calib_settings, 'above', self.dock_logger) # create main parameter tree self.calib_settings_tree = ParameterTree() dock_calib_settings.addWidget(self.calib_settings_tree, 10) self.calib_settings_tree.setMinimumWidth(300) self.calib_settings_tree.setParameters(self.settings.child( ('calib_settings')), showTop=False) #any change to the tree on the user interface will call the parameter_tree_changed method where all actions will be applied #this one for the custom application settings dock_acq_settings = Dock('Acquisition', size=(300, 350)) self.dockarea.addDock(dock_acq_settings, 'above', dock_config_settings) # create main parameter tree self.acq_settings_tree = ParameterTree() dock_acq_settings.addWidget(self.acq_settings_tree, 10) self.acq_settings_tree.setMinimumWidth(300) self.acq_settings_tree.setParameters(self.settings.child( ('acq_settings')), showTop=False) @Slot(ThreadCommand) def cmd_from_det(self, status): try: if status.command == 'spectro_wl': self.status_center.setStyleSheet("background-color: green") self.spectro_wl_is(status.attributes[0]) elif status.command == 'laser_wl': #self.laser_set_manual = False self.settings.child('config_settings', 'laser_wl_list').setValue( status.attributes[0]) self.status_laser.setText('{:}nm'.format(status.attributes[0])) self.status_laser.setStyleSheet("background-color: green") self.update_center_frequency(self.spectro_wl) elif status.command == 'exposure_ms': self.settings.child('acq_settings', 'exposure_ms').setValue( status.attributes[0]) elif status.command == "x_axis": x_axis = status.attributes[0] if np.any(x_axis['data'] != self.viewer_freq_axis['data'] ) and self.current_det['calib']: self.viewer_freq_axis.update(x_axis) self.update_axis() except Exception as e: logger.exception(str(e)) def update_status(self, txt, wait_time=1000, log_type=None): """ """ self.statusbar.showMessage(txt, wait_time) if log_type is not None: self.log_signal.emit(txt) def set_detector(self): self.detector = self.dashboard.detector_modules[0] self.settings.child('config_settings', 'curr_det').setValue( f"{self.detector.settings.child('main_settings','DAQ_type').value()} / " f"{self.detector.settings.child('main_settings','detector_type').value()} / {self.detector.title}" ) self.detector.custom_sig[ThreadCommand].connect(self.cmd_from_det) self.current_det = \ dict(laser=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'laser_selectable').value(), laser_list=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'laser_ray').opts['limits'], movable=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'ismovable').value(), calib=self.dashboard.preset_manager.preset_params.child('spectro_settings', 'iscalibrated').value(), ) self.detector.grab_done_signal.connect(self.show_data) self.settings.sigTreeStateChanged.disconnect( self.parameter_tree_changed) if self.current_det['laser']: self.settings.child('config_settings', 'laser_wl_list').show() self.settings.child('config_settings', 'laser_wl').hide() self.settings.child( 'config_settings', 'laser_wl_list').setOpts(limits=self.current_det['laser_list']) else: self.settings.child('config_settings', 'laser_wl').show() self.settings.child('config_settings', 'laser_wl_list').hide() self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed) #apply current detector particularities #self.settings.child('acq_settings', 'spectro_center_freq').setOpts(readonly=not self.current_det['movable']) self.get_spectro_wl() QtWidgets.QApplication.processEvents() self.get_laser_wl() QtWidgets.QApplication.processEvents() self.get_exposure_ms() QtWidgets.QApplication.processEvents() def get_exposure_ms(self): self.detector.command_detector.emit(ThreadCommand('get_exposure_ms')) def set_exposure_ms(self, data): self.detector.command_detector.emit( ThreadCommand('set_exposure_ms', [data])) @Slot(bool) def initialized(self, state, offline=False): self.offline = offline self.grab_action.setEnabled(state) self.snap_action.setEnabled(state) if state or offline: self.status_init.set_as_true() self.dockarea.setEnabled(True) else: self.status_init.set_as_false() def update_center_frequency(self, spectro_wl): self._spectro_wl = spectro_wl if self.settings.child('acq_settings', 'units').value() == 'nm': self.settings.child('acq_settings', 'spectro_center_freq').setValue(spectro_wl) elif self.settings.child('acq_settings', 'units').value() == 'cm-1': self.settings.child('acq_settings', 'spectro_center_freq').setValue( Enm2cmrel( spectro_wl, self.settings.child( 'config_settings', 'laser_wl').value())) elif self.settings.child('acq_settings', 'units').value() == 'eV': self.settings.child('acq_settings', 'spectro_center_freq').setValue( nm2eV(spectro_wl)) self.set_status_center( self.settings.child('acq_settings', 'spectro_center_freq').value(), self.settings.child('acq_settings', 'units').value()) def set_status_center(self, val, unit, precision=3): self.status_center.setText(f'{val:.{precision}f} {unit}') def spectro_wl_is(self, spectro_wl): """ this slot receives a signal from the detector telling it what's the current spectro_wl Parameters ---------- spectro_wl """ self._spectro_wl = spectro_wl self.update_center_frequency(spectro_wl) def set_spectro_wl(self, spectro_wl): try: if self.current_det['movable']: self.detector.command_detector.emit( ThreadCommand('set_spectro_wl', [spectro_wl])) except Exception as e: logger.exception(str(e)) def get_spectro_wl(self): if self.current_det['calib']: self.settings.child('acq_settings', 'spectro_center_freq').show() self.settings.child('acq_settings', 'spectro_center_freq_txt').hide() self.detector.command_detector.emit( ThreadCommand('get_spectro_wl')) self.detector.command_detector.emit(ThreadCommand('get_axis')) else: self.settings.child('acq_settings', 'spectro_center_freq').hide() self.settings.child('acq_settings', 'spectro_center_freq_txt').show() self.viewer_freq_axis['units'] = 'Pxls' def get_laser_wl(self): if self.current_det['laser']: self.detector.command_detector.emit(ThreadCommand('get_laser_wl')) else: self.settings.child('config_settings', 'laser_wl').setValue(0) @property def spectro_wl(self): # try to get the param value from detector (if it has been added in the plugin) return self._spectro_wl @spectro_wl.setter def spectro_wl(self, spec_wl): # try to get the param value from detector (if it has been added in the plugin) self.set_spectro_wl(spec_wl) def show_detector(self, show=True): self.dashboard.mainwindow.setVisible(show) for area in self.dashboard.dockarea.tempAreas: area.window().setVisible(show) def parameter_tree_changed(self, param, changes): for param, change, data in changes: path = self.settings.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() if change == 'childAdded': pass elif change == 'value': if param.name() == 'show_det': self.show_detector(data) elif param.name() == 'spectro_center_freq': unit = self.settings.child('acq_settings', 'units').value() if unit == 'nm': center_wavelength = data elif unit == 'cm-1': center_wavelength = Ecmrel2Enm( data, self.settings.child('config_settings', 'laser_wl').value()) elif unit == 'eV': center_wavelength = eV2nm(data) if int(self.spectro_wl * 100) != int( 100 * center_wavelength): #comprison at 1e-2 self.spectro_wl = center_wavelength self.update_axis() elif param.name() == 'units': if self.settings.child( 'acq_settings', 'spectro_center_freq').value() > 0.000000001: if data == 'nm': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( self._spectro_wl) elif data == 'cm-1': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( Enm2cmrel( self._spectro_wl, self.settings.child( 'config_settings', 'laser_wl').value())) elif data == 'eV': self.settings.child( 'acq_settings', 'spectro_center_freq').setValue( nm2eV(self._spectro_wl)) self.set_status_center( self.settings.child('acq_settings', 'spectro_center_freq').value(), self.settings.child('acq_settings', 'units').value()) elif param.name() == 'laser_wl_list': if data is not None: self.move_laser_wavelength(data) elif param.name() == 'laser_wl': if data is not None: self.move_laser_wavelength(data) if int(data) == 0: self.settings.child('acq_settings', 'units').setValue('nm') self.settings.child('acq_settings', 'units').setOpts(readonly=True) else: self.settings.child( 'acq_settings', 'units').setOpts(readonly=False) if data != 0: self.set_manual_laser_wl(data) elif param.name() == 'exposure_ms': self.set_exposure_ms(data) elif param.name() == 'do_calib': if len(self.raw_data) != 0: if data: self.calib_dock = Dock('Calibration module') self.dockarea.addDock(self.calib_dock) self.calibration = Calibration(self.dockarea) self.calib_dock.addWidget(self.calibration) self.calibration.coeffs_calib.connect( self.update_calibration) else: self.calib_dock.close() elif param.name() == 'save_calib': filename = select_file(start_path=self.save_file_pathname, save=True, ext='xml') if filename != '': custom_tree.parameter_to_xml_file( self.settings.child('calib_settings', 'calib_coeffs'), filename) elif param.name() == 'load_calib': filename = select_file(start_path=self.save_file_pathname, save=False, ext='xml') if filename != '': children = custom_tree.XML_file_to_parameter(filename) self.settings.child( 'calib_settings', 'calib_coeffs').restoreState( Parameter.create( title='Calibration coeffs:', name='calib_coeffs', type='group', children=children).saveState()) elif param.name() in custom_tree.iter_children(self.settings.child('calib_settings', 'calib_coeffs')) \ or param.name() == 'use_calib': if self.settings.child('calib_settings', 'use_calib').value(): calib_coeffs = [ self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'slope_calib').value(), self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').value() ] self.update_center_frequency( self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').value()) self.settings.child('acq_settings', 'spectro_center_freq').show() self.settings.child( 'acq_settings', 'spectro_center_freq').setOpts(readonly=True) self.status_center.setStyleSheet( "background-color: green") self.settings.child('acq_settings', 'spectro_center_freq_txt').hide() x_axis_pxls = np.linspace(0, self.raw_data[0].size - 1, self.raw_data[0].size) self.viewer_freq_axis['data'] = np.polyval( calib_coeffs, x_axis_pxls - np.max(x_axis_pxls) / 2) self.update_axis() else: self.settings.child('acq_settings', 'spectro_center_freq').hide() self.settings.child('acq_settings', 'spectro_center_freq_txt').show() self.status_center.setStyleSheet( "background-color: red") elif change == 'parent': pass @Slot(list) def update_calibration(self, coeffs): self.settings.child('calib_settings', 'calib_coeffs', 'center_calib').setValue(coeffs[0]) self.settings.child('calib_settings', 'calib_coeffs', 'slope_calib').setValue(coeffs[1]) if len(coeffs) > 2: self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').setValue(coeffs[2]) else: self.settings.child('calib_settings', 'calib_coeffs', 'second_calib').setValue(0) if len(coeffs) > 3: self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').setValue(coeffs[3]) else: self.settings.child('calib_settings', 'calib_coeffs', 'third_calib').setValue(0) def set_manual_laser_wl(self, laser_wl): messg = QtWidgets.QMessageBox() messg.setText( 'You manually changed the laser wavelength to {:}nm!'.format( laser_wl)) messg.setInformativeText("Is that correct?") messg.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) ret = messg.exec() if ret == QtWidgets.QMessageBox.Yes: self.status_laser.setText('{:}nm'.format(laser_wl)) self.status_laser.setStyleSheet("background-color: green") self.settings.child('acq_settings', 'units').setOpts(readonly=False) def move_laser_wavelength(self, laser_wavelength): #do hardware stuff if possible (Mock, labspec...) try: if self.current_det['laser']: self.detector.command_detector.emit( ThreadCommand('set_laser_wl', [laser_wavelength])) except Exception as e: logger.exception(str(e)) @Slot(OrderedDict) def show_data(self, data): """ do stuff with data from the detector if its grab_done_signal has been connected Parameters ---------- data: (OrderedDict) #OrderedDict(name=self.title,x_axis=None,y_axis=None,z_axis=None,data0D=None,data1D=None,data2D=None) """ self.data_dict = data if 'data1D' in data: self.raw_data = [] for key in data['data1D']: self.raw_data.append(data['data1D'][key]['data']) if 'x_axis' in data['data1D'][key]: x_axis = data['data1D'][key]['x_axis'] else: x_axis = utils.Axis(data=np.linspace( 0, len(data['data1D'][key]['data']) - 1, len(data['data1D'][key]['data'])), units='pxls', label='') if self.viewer_freq_axis['data'] is None: self.viewer_freq_axis.update(x_axis) elif np.any(x_axis['data'] != self.viewer_freq_axis['data'] ) and self.current_det['calib']: self.viewer_freq_axis.update(x_axis) self.viewer.show_data(self.raw_data) self.update_axis() def update_axis(self): axis = utils.Axis() unit = self.settings.child('acq_settings', 'units').value() if unit == 'nm': axis['data'] = self.viewer_freq_axis['data'] elif unit == 'cm-1': axis['data'] = Enm2cmrel( self.viewer_freq_axis['data'], self.settings.child('config_settings', 'laser_wl').value()) elif unit == 'eV': axis['data'] = nm2eV(self.viewer_freq_axis['data']) axis['units'] = unit axis['label'] = 'Photon energy' self.viewer.x_axis = axis def create_menu(self, menubar): """ """ menubar.clear() # %% create file menu file_menu = menubar.addMenu('File') load_action = file_menu.addAction('Load file') load_action.triggered.connect(self.load_file) save_action = file_menu.addAction('Save file') save_action.triggered.connect(self.save_data) export_action = file_menu.addAction('Export as ascii') export_action.triggered.connect(lambda: self.save_data(export=True)) file_menu.addSeparator() file_menu.addAction('Show log file', self.show_log) file_menu.addSeparator() quit_action = file_menu.addAction('Quit') quit_action.triggered.connect(self.quit_function) settings_menu = menubar.addMenu('Settings') settings_menu.addAction('Show Units Converter', self.show_units_converter) docked_menu = settings_menu.addMenu('Docked windows') docked_menu.addAction('Load Layout', self.load_layout_state) docked_menu.addAction('Save Layout', self.save_layout_state) self.preset_menu = menubar.addMenu(self.dashboard.preset_menu) self.preset_menu.menu().addSeparator() self.preset_menu.menu().addAction( 'Offline Mode', lambda: self.initialized(state=False, offline=True)) def load_layout_state(self, file=None): """ Load and restore a layout state from the select_file obtained pathname file. See Also -------- utils.select_file """ try: if file is None: file = select_file(save=False, ext='dock') if file is not None: with open(str(file), 'rb') as f: dockstate = pickle.load(f) self.dockarea.restoreState(dockstate) file = file.name self.settings.child('loaded_files', 'layout_file').setValue(file) except Exception as e: logger.exception(str(e)) def save_layout_state(self, file=None): """ Save the current layout state in the select_file obtained pathname file. Once done dump the pickle. See Also -------- utils.select_file """ try: dockstate = self.dockarea.saveState() if 'float' in dockstate: dockstate['float'] = [] if file is None: file = select_file(start_path=None, save=True, ext='dock') if file is not None: with open(str(file), 'wb') as f: pickle.dump(dockstate, f, pickle.HIGHEST_PROTOCOL) except Exception as e: logger.exception(str(e)) def show_log(self): import webbrowser webbrowser.open(logging.getLogger('pymodaq').handlers[0].baseFilename) def show_units_converter(self): self.units_converter = UnitsConverter() dock_converter = Dock('Units Converter', size=(300, 350)) self.dockarea.addDock(dock_converter, 'bottom', self.dock_logger) dock_converter.addWidget(self.units_converter.parent) def load_file(self): data, fname, node_path = browse_data(ret_all=True) if data is not None: h5utils = H5BrowserUtil() h5utils.open_file(fname) data, axes, nav_axes, is_spread = h5utils.get_h5_data(node_path) data_node = h5utils.get_node(node_path) if data_node.attrs['type'] == 'data': if data_node.attrs['data_dimension'] == '1D': data_dict = OrderedDict(data1D=dict( raw=dict(data=data, x_axis=axes['x_axis']))) self.show_data(data_dict) h5utils.close_file() def quit_function(self): #close all stuff that need to be if self.detector is not None: self.detector.quit_fun() QtWidgets.QApplication.processEvents() self.mainwindow.close() def create_toolbar(self): self.toolbar.addWidget(QtWidgets.QLabel('Acquisition:')) iconquit = QtGui.QIcon() iconquit.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.quit_action = QtWidgets.QAction(iconquit, "Quit program", None) self.toolbar.addAction(self.quit_action) self.quit_action.triggered.connect(self.quit_function) iconload = QtGui.QIcon() iconload.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/Open.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.loadaction = QtWidgets.QAction( iconload, "Load target file (.h5, .png, .jpg) or data from camera", None) self.toolbar.addAction(self.loadaction) self.loadaction.triggered.connect(self.load_file) iconsave = QtGui.QIcon() iconsave.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/SaveAs.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.saveaction = QtWidgets.QAction(iconsave, "Save current data", None) self.toolbar.addAction(self.saveaction) self.saveaction.triggered.connect(self.save_data) iconrun = QtGui.QIcon() iconrun.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.grab_action = QtWidgets.QAction(iconrun, 'Grab', None) self.grab_action.setCheckable(True) self.toolbar.addAction(self.grab_action) self.grab_action.triggered.connect(self.grab_detector) iconsnap = QtGui.QIcon() iconsnap.addPixmap(QtGui.QPixmap(":/icons/Icon_Library/snap.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.snap_action = QtWidgets.QAction(iconsnap, 'Snap', None) self.snap_action.triggered.connect(self.snap_detector) self.toolbar.addAction(self.snap_action) self.grab_action.setEnabled(False) self.snap_action.setEnabled(False) def grab_detector(self): self.detector.ui.grab_pb.click() def snap_detector(self): self.detector.ui.single_pb.click() def save_data(self, export=False): try: if export: ext = 'dat' else: ext = 'h5' path = select_file(start_path=self.save_file_pathname, save=True, ext=ext) if not (not (path)): if not export: h5saver = H5Saver(save_type='detector') h5saver.init_file(update_h5=True, custom_naming=False, addhoc_file_path=path) settings_str = b'<All_settings>' + custom_tree.parameter_to_xml_string( self.settings) if self.detector is not None: settings_str += custom_tree.parameter_to_xml_string( self.detector.settings) if hasattr(self.detector.ui.viewers[0], 'roi_manager'): settings_str += custom_tree.parameter_to_xml_string( self.detector.ui.viewers[0].roi_manager. settings) settings_str += custom_tree.parameter_to_xml_string( h5saver.settings) settings_str += b'</All_settings>' det_group = h5saver.add_det_group(h5saver.raw_group, "Data", settings_str) try: self.channel_arrays = OrderedDict([]) data_dim = 'data1D' if not h5saver.is_node_in_group(det_group, data_dim): self.channel_arrays['data1D'] = OrderedDict([]) data_group = h5saver.add_data_group( det_group, data_dim) for ind_channel, data in enumerate( self.raw_data): # list of numpy arrays channel = f'CH{ind_channel:03d}' channel_group = h5saver.add_CH_group( data_group, title=channel) self.channel_arrays[data_dim][ 'parent'] = channel_group self.channel_arrays[data_dim][ channel] = h5saver.add_data( channel_group, dict(data=data, x_axis=self.viewer_freq_axis), scan_type='', enlargeable=False) h5saver.close_file() except Exception as e: logger.exception(str(e)) else: data_to_save = [self.viewer_freq_axis['data']] data_to_save.extend([dat for dat in self.raw_data]) np.savetxt(path, data_to_save, delimiter='\t') except Exception as e: logger.exception(str(e)) @Slot(str) def add_log(self, txt): """ Add a log to the logger list from the given text log and the current time ================ ========= ====================== **Parameters** **Type** **Description** *txt* string the log to be added ================ ========= ====================== """ now = datetime.datetime.now() new_item = QtWidgets.QListWidgetItem(str(now) + ": " + txt) self.logger_list.addItem(new_item) ##to do ##self.save_parameters.logger_array.append(str(now)+": "+txt) @Slot(str) def emit_log(self, txt): """ Emit a log-signal from the given log index =============== ======== ======================= **Parameters** **Type** **Description** *txt* string the log to be emitted =============== ======== ======================= """ self.log_signal.emit(txt)