class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) #self.verticalLayout_2 = QVBoxLayout() self.tableHeaders = ['VT', 'I:E', 'RR', 'FIO2'] self.widget = uic.loadUi(_UI, self) window_title = "Rhythm" self.json = JsonSettings("settings.json") self.settings_dict = self.json.dict pprint.pprint(self.settings_dict) self.table = QTableWidget(self) self.table.setRowCount(2) self.table.setColumnCount(5) self.table.setHorizontalHeaderLabels(self.tableHeaders) self.table.setItem(0,0, QTableWidgetItem(self.settings_dict[r"vt"])) self.table.setItem(0,1, QTableWidgetItem(self.settings_dict[r"ie"])) self.table.setItem(0,2, QTableWidgetItem(self.settings_dict[r"rr"])) self.table.setItem(0,3, QTableWidgetItem(self.settings_dict[r"fio2"])) #self.table.itemChanged.connect(self.SaveSettings) self.vt = int(self.settings_dict[r"vt"]) self.rr = int(self.settings_dict[r"rr"]) self.ie = int(self.settings_dict[r"ie"]) self.fio2 = int(self.settings_dict[r"fio2"]) self.verticalLayout_2.addWidget(self.table) self.generator = GcodeGenerator(self.vt, self.rr, self.ie, self.fio2) self.motion_table = QTableWidget(self) self.motion_table_headers = ['variables', 'values'] self.motion_table.setColumnCount(2) self.motion_table.setRowCount(10) self.motion_table.setHorizontalHeaderLabels(self.motion_table_headers) self.motion_table.hide() self.dat = deque() self.datpeak = deque() self.lungpressure_line_pen = pg.mkPen(200, 100, 0) self.plotter = PlotWidget() self.plotter.showGrid(x=True, y=True, alpha=None) self.curve1 = self.plotter.plot(0,0,"lungpressure", 'b') self.curve2 = self.plotter.plot(0,0,"peakpressure", pen = self.lungpressure_line_pen) #self.motion_table.setSizeAdjustPolicy(QtWidget.QAbstractScrollArea.AdjustToContents) self.CalculateSettings() self.verticalLayout_2.addWidget(self.motion_table) self.verticalLayout_2.addWidget(self.plotter) self.motion_table.hide() self.gcodetable = QTableWidget(self) self.gcodetable.setRowCount(1) self.gcodetable.setColumnCount(1) #self.verticalLayout_2.addWidget(self.gcodetable) self.hbox = QHBoxLayout() #self.verticalLayout_2.addChildLayout(self.hbox) self.verticalLayout_2.addLayout(self.hbox) self.hbox.addWidget(self.gcodetable) self.txrxtable = QTableWidget() self.txrxtable.setRowCount(1) self.txrxtable.setColumnCount(1) self.hbox.addWidget(self.txrxtable) #self.hbox.addLayout self.peepdial.valueChanged.connect(self.peepDialChanged) self.peeplcd.display(self.peepdial.value()) self.peakdial.valueChanged.connect(self.peakDialChanged) self.peaklcd.display(self.peakdial.value()) self.ipapdial.valueChanged.connect(self.ipapDialChanged) self.ipaplcd.display(self.ipapdial.value()) self.vtdial.valueChanged.connect(self.vtDialChanged) self.vtlcd.display(self.vtdial.value()) self.iedial.valueChanged.connect(self.ieDialChanged) self.ielcd.display(self.iedial.value()) self.rrdial.valueChanged.connect(self.rrDialChanged) self.rrlcd.display(self.rrdial.value()) self.fiodial.valueChanged.connect(self.fioDialChanged) self.fiolcd.display(self.fiodial.value()) self.s = "" self.s2 = "" self.ports = list(port_list.comports()) self.primaryThreadCreated = False self.workerThreadCreated = False self.sensorThreadCreated = False self.serialPortOpen = False self.serialSensorOpen = False def update_param_table(self): self.table.setItem(0,0, QTableWidgetItem(self.settings_dict[r"vt"])) self.table.setItem(0,1, QTableWidgetItem(self.settings_dict[r"ie"])) self.table.setItem(0,2, QTableWidgetItem(self.settings_dict[r"rr"])) self.table.setItem(0,3, QTableWidgetItem(self.settings_dict[r"fio2"])) def peepDialChanged(self): self.peeplcd.display(self.peepdial.value()) def peakDialChanged(self): self.peaklcd.display(self.peakdial.value()) def ipapDialChanged(self): self.ipaplcd.display(self.ipapdial.value()) def vtDialChanged(self): self.vtlcd.display(self.vtdial.value()) self.vt = self.vtdial.value() self.settings_dict[r"vt"] = str(self.vt) self.update_param_table() self.SaveSettings() def ieDialChanged(self): self.table.setItem(0,1, QTableWidgetItem(self.settings_dict[r"ie"])) self.ielcd.display(self.iedial.value()) self.ie = self.iedial.value() self.settings_dict[r"ie"] = str(self.ie) self.update_param_table() self.SaveSettings() def rrDialChanged(self): self.rrlcd.display(self.rrdial.value()) self.rr = self.rrdial.value() self.settings_dict[r"rr"] = str(self.rr) self.update_param_table() self.SaveSettings() def fioDialChanged(self): self.fiolcd.display(self.fiodial.value()) self.fio2 = self.fiodial.value() self.settings_dict[r"fio2"] = str(self.fio2) self.update_param_table() self.SaveSettings() def ShowGcodeTable(self): codelist = self.generator.gcodestr.splitlines() rowcount = len(codelist) self.gcodetable.setRowCount(rowcount) self.gcodetable.setColumnCount(1) for i in range(rowcount): self.gcodetable.setItem(i, 0, QTableWidgetItem(codelist[i])) def CalculateSettings(self): del self.generator ###self.json = JsonSettings("settings.json") ###self.settings_dict = self.json.dict #self.vt = int(self.settings_dict[r"vt"]) #self.rr = int(self.settings_dict[r"rr"]) #self.ie = int(self.settings_dict[r"ie"]) #self.fio2 = int(self.settings_dict[r"fio2"]) self.generator = GcodeGenerator(self.vt, self.rr, self.ie, self.fio2) self.motion_table.setItem(0,0, QTableWidgetItem('Dp')) self.motion_table.setItem(0,1, QTableWidgetItem(str(self.generator.Dp))) self.motion_table.setItem(1,0, QTableWidgetItem('Dr')) self.motion_table.setItem(1,1, QTableWidgetItem(str(self.generator.Dr))) self.motion_table.setItem(2,0, QTableWidgetItem('Dt')) self.motion_table.setItem(2,1, QTableWidgetItem(str(self.generator.Dt))) self.motion_table.setItem(3,0, QTableWidgetItem('Ti')) self.motion_table.setItem(3,1, QTableWidgetItem(str(self.generator.Ti))) self.motion_table.setItem(4,0, QTableWidgetItem('Th')) self.motion_table.setItem(4,1, QTableWidgetItem(str(self.generator.Th))) self.motion_table.setItem(5,0, QTableWidgetItem('Vi')) self.motion_table.setItem(5,1, QTableWidgetItem(str(self.generator.Vi))) self.motion_table.setItem(6,0, QTableWidgetItem('Vh')) self.motion_table.setItem(6,1, QTableWidgetItem(str(self.generator.Vh))) def sensorData(self, data_stream): #print(data_stream.split(',')) lst = data_stream.split(",") self.maxLen = 400 # max number of data points to show on graph if(len(lst) > 1): if len(self.dat) > self.maxLen: self.dat.popleft() # remove oldest if len(self.datpeak) > self.maxLen: self.datpeak.popleft() self.datpeak.append(float(self.peakdial.value())) self.dat.append(float(lst[1])) self.curve1.setData(self.dat) self.curve2.setData(self.datpeak) def write_info(self, data_stream): rcount = self.txrxtable.rowCount() self.txrxtable.insertRow(rcount) self.txrxtable.setItem(rcount,0, QTableWidgetItem(data_stream)) self.txrxtable.scrollToBottom() self.txrxtable.resizeColumnsToContents() self.txrxtable.resizeRowsToContents() if data_stream == "Stopped": if self.primaryThreadCreated: self.primaryThread.exit() self.primaryThread.wait() self.primaryThreadCreated = False del self.primaryThread #elif data_stream == "Loop": # self.primaryThread.exit() ### # self.worker = WorkerThread(self.s, self.generator) # self.workerThread = QThread() # self.workerThread.started.connect(self.worker.run) # self.worker.signal.connect(self.write_info) # self.worker.moveToThread(self.workerThread) # self.workerThread.start() # self.workerThreadCreated = True # print("Starting Worker Thread") #if data_stream == "Stopped": # self.worker = WorkerThread(self.s) # self.workerThread = QThread() # self.workerThread.started.connect(self.worker.run) # self.worker.signal.connect(self.write_info) # self.worker.moveToThread(self.workerThread) # self.workerThread.start() # print("Starting Thread") @Slot() def on_gengcode_clicked(self): self.CalculateSettings() self.generator.Generate() self.ShowGcodeTable() @Slot() def on_scanPorts_clicked(self): self.ports = list(port_list.comports()) self.widget.portsList.clear() self.widget.monitoringPort.clear() print(len(self.ports)) for p in self.ports: self.widget.portsList.addItem(p[0]) self.widget.monitoringPort.addItem(p[0]) @Slot() def on_btninit_clicked(self): if not self.workerThreadCreated: if not self.primaryThreadCreated: self.primary = PrimaryThread(self.s, self.generator) self.primaryThread = QThread() self.primaryThread.started.connect(self.primary.run) self.primary.signal.connect(self.write_info) self.primary.moveToThread(self.primaryThread) self.primaryThread.start() self.primaryThreadCreated = True print("Starting Primary Thread") if self.serialSensorOpen: if not self.sensorThreadCreated: self.sensor = SensorThread(self.s2) self.sensorThread = QThread() self.sensorThread.started.connect(self.sensor.run) self.sensor.signal.connect(self.sensorData) self.sensor.moveToThread(self.sensorThread) self.sensorThread.start() self.sensorThreadCreated = True print("Starting Sensor Thread ...") @Slot() def on_runloop_clicked(self): if not self.primaryThreadCreated: if not self.workerThreadCreated: self.worker = WorkerThread(self.s, self.generator) self.workerThread = QThread() self.workerThread.started.connect(self.worker.run) self.worker.signal.connect(self.write_info) self.worker.moveToThread(self.workerThread) self.workerThread.start() self.workerThreadCreated = True print("Starting Worker Thread") @Slot() def on_disconnect_clicked(self): if self.serialPortOpen: if self.workerThreadCreated: self.worker.Stop() self.workerThread.exit() self.workerThread.wait() self.workerThreadCreated = False del self.workerThread if self.primaryThreadCreated: self.primaryThread.exit() self.primaryThread.wait() self.primaryThreadCreated = False del self.primaryThread if self.sensorThreadCreated: self.sensor.Stop() self.sensorThread.exit() self.sensorThread.wait() self.sensorThreadCreated = False self.s.close() if self.serialSensorOpen: self.s2.close() self.serialPortOpen = False self.serialSensorOpen = False @Slot() def on_connect_clicked(self): try: if not self.serialPortOpen: print("Serial Port Name : " + self.portsList.currentText()) self.s = serial.Serial(self.portsList.currentText(), baudrate=115200, timeout=1) #self.s.open() time.sleep(1) self.serialPortOpen = True #self.s.write("\r\n\r\n") # Hit enter a few times to wake the Printrbot #time.sleep(2) # Wait for Printrbot to initialize while self.s.in_waiting: self.s.readline() #print(self.s.readline().decode("ascii")) #self.s.flushInput() # Flush startup text in serial input #monitoringPort if not self.serialSensorOpen: self.s2 = serial.Serial(self.monitoringPort.currentText(), baudrate=115200, timeout=1) self.serialSensorOpen = True except serial.SerialException as ex: self.serialPortOpen = False print(ex.strerror) print("Error Opening Serial Port..........................................") @Slot() def on_btnCPAP_clicked(self): if self.btnCPAP.isChecked(): #self.btnCPAP.toggle() self.btnCPAP.setStyleSheet('QPushButton {background-color: #7F7F7F}') else: self.btnCPAP.setStyleSheet('QPushButton {background-color: #404040}') def SaveSettings(self): ###self.json = JsonSettings("settings.json") ###self.settings_dict = self.json.dict self.json.dict[r'vt'] = str(self.vt) self.json.dict[r'ie'] = str(self.ie) self.json.dict[r'rr'] = str(self.rr) self.json.dict[r'fio2'] = str(self.fio2) self.generator = GcodeGenerator(self.vt, self.rr, self.ie, self.fio2) self.generator.Generate() if self.workerThreadCreated: self.worker.updateGcode(self.generator) pprint.pprint(self.generator.gcodestr) #self.json.dumptojson() ###self.vt = int(self.settings_dict[r"vt"]) ###self.rr = int(self.settings_dict[r"rr"]) ###self.ie = int(self.settings_dict[r"ie"]) ###self.fio2 = int(self.settings_dict[r"fio2"]) self.CalculateSettings()
class DAQ_Move(Ui_Form, QObject): """ | DAQ_Move object is a module used to control one motor from a specified list. | | Preset is an optional list of dicts used to managers programatically settings such as the name of the controller from the list of possible controllers, COM address... | | Init is a boolean to tell the programm to initialize the controller at the start of the programm given the managers options ========================= ================================================= **Attributes** **Type** *command_stage* instance of Signal *move_done_signal* instance of Signal *update_settings_signal* instance of Signal *status_signal* instance of Signal *bounds_signal* instance of Signal *params* dictionnary list *ui* instance of UI_Form *parent* QObject *title* string *wait_time* int *initialized_state* boolean *Move_done* boolean *controller* instance of the specific controller object *stage* instance of the stage (axis or wathever) object *current_position* float *target_position* float *wait_position_flag* boolean *stage_types* string list ========================= ================================================= See Also -------- set_enabled_move_buttons, set_setting_tree, stage_changed, quit_fun, ini_stage_fun, move_Abs, move_Rel, move_Home, get_position, stop_Motion, show_settings, show_fine_tuning References ---------- QLocale, QObject, Signal, QStatusBar, ParameterTree """ init_signal = Signal(bool) command_stage = Signal(ThreadCommand) command_tcpip = Signal(ThreadCommand) move_done_signal = Signal( str, float ) # to be used in external program to make sure the move has been done, export the current position. str refer to the unique title given to the module update_settings_signal = Signal(edict) status_signal = Signal(str) bounds_signal = Signal(bool) params = daq_move_params def __init__(self, parent, title="pymodaq Move", init=False): """DAQ_Move object is a module used to control one motor from a specified list. managers is an optional list of dicts used to managers programatically settings such as the name of the controller from the list of possible controllers, COM address... init is a boolean to tell the programm to initialize the controller at the start of the programm given the managers options To differenciate various instance of this class """ self.logger = utils.set_logger(f'{logger.name}.{title}') self.logger.info(f'Initializing DAQ_Move: {title}') super().__init__() here = Path(__file__).parent splash = QtGui.QPixmap(str(here.parent.joinpath('splash.png'))) self.splash_sc = QtWidgets.QSplashScreen(splash, Qt.WindowStaysOnTopHint) self.ui = Ui_Form() self.ui.setupUi(parent) self.ui.Moveto_pb_bis_2.setVisible(False) self.parent = parent self.ui.title_label.setText(title) self.title = title self.ui.statusbar = QtWidgets.QStatusBar(parent) self.ui.StatusBarLayout.addWidget(self.ui.statusbar) self.ui.statusbar.setMaximumHeight(20) self.send_to_tcpip = False self.tcpclient_thread = None self.wait_time = 1000 self.ui.Ini_state_LED self.ui.Ini_state_LED.clickable = False self.ui.Ini_state_LED.set_as_false() self.ui.Move_Done_LED.clickable = False self.ui.Move_Done_LED.set_as_false() self.initialized_state = False self.ui.Current_position_sb.setReadOnly(False) self.move_done_bool = True # ###########IMPORTANT############################ self.controller = None # the hardware controller/set after initialization and to be used by other modules # ################################################ self.current_position = 0 self.target_position = 0 self.wait_position_flag = True self.ui.Current_position_sb.setValue(self.current_position) self.set_enabled_move_buttons(enable=False) self.ui.groupBox.hide() self.parent.resize(150, 200) # #Setting stages types self.stage_types = [mov['name'] for mov in DAQ_Move_Stage_type] self.ui.Stage_type_combo.clear() self.ui.Stage_type_combo.addItems(self.stage_types) # create main parameter tree self.ui.settings_tree = ParameterTree() self.ui.verticalLayout_2.addWidget(self.ui.settings_tree) self.ui.settings_tree.setMinimumWidth(300) self.settings = Parameter.create(name='Settings', type='group', children=self.params) self.ui.settings_tree.setParameters(self.settings, showTop=False) # connecting from tree self.settings.sigTreeStateChanged.connect( self.parameter_tree_changed ) # any changes on the settings will update accordingly the detector self.ui.settings_tree.setVisible(False) self.set_setting_tree() QtWidgets.QApplication.processEvents() # #Connecting buttons: self.ui.Stage_type_combo.currentIndexChanged.connect( self.set_setting_tree) self.ui.Stage_type_combo.currentIndexChanged.connect( self.stage_changed) self.ui.Quit_pb.clicked.connect(self.quit_fun) self.ui.IniStage_pb.clicked.connect(self.ini_stage_fun) self.update_status("Ready", wait_time=self.wait_time) self.ui.Move_Abs_pb.clicked.connect( lambda: self.move_Abs(self.ui.Abs_position_sb.value())) self.ui.Move_Rel_plus_pb.clicked.connect( lambda: self.move_Rel(self.ui.Rel_position_sb.value())) self.ui.Move_Rel_minus_pb.clicked.connect( lambda: self.move_Rel(-self.ui.Rel_position_sb.value())) self.ui.Find_Home_pb.clicked.connect(self.move_Home) self.ui.Get_position_pb.clicked.connect(self.get_position) self.ui.Stop_pb.clicked.connect(self.stop_Motion) self.ui.parameters_pb.clicked.connect(self.show_settings) self.ui.fine_tuning_pb.clicked.connect(self.show_fine_tuning) self.ui.Abs_position_sb.valueChanged.connect( self.ui.Abs_position_sb_bis.setValue) self.ui.Abs_position_sb_bis.valueChanged.connect( self.ui.Abs_position_sb.setValue) self.ui.Moveto_pb_bis.clicked.connect( lambda: self.move_Abs(self.ui.Abs_position_sb_bis.value())) # initialize the controller if init=True if init: self.ui.IniStage_pb.click() @property def actuator(self): return self.ui.Stage_type_combo.currentText() @actuator.setter def actuator(self, actuator): self.ui.Stage_type_combo.setCurrentText(actuator) if self.actuator != actuator: raise ActuatorError( f'{actuator} is not a valid installed actuator: {self.stage_types}' ) def init(self): self.ui.IniStage_pb.click() def ini_stage_fun(self): """ Init : * a DAQ_move_stage instance if not exists * a linked thread connected by signal to the DAQ_move_main instance See Also -------- set_enabled_move_buttons, DAQ_utils.ThreadCommand, DAQ_Move_stage, DAQ_Move_stage.queue_command, thread_status, DAQ_Move_stage.update_settings, update_status """ try: if not self.ui.IniStage_pb.isChecked(): try: self.set_enabled_move_buttons(enable=False) self.ui.Stage_type_combo.setEnabled(True) self.ui.Ini_state_LED.set_as_false() self.command_stage.emit(ThreadCommand(command="close")) except Exception as e: self.logger.exception(str(e)) else: self.stage_name = self.ui.Stage_type_combo.currentText() stage = DAQ_Move_stage(self.stage_name, self.current_position, self.title) self.stage_thread = QThread() stage.moveToThread(self.stage_thread) self.command_stage[ThreadCommand].connect(stage.queue_command) stage.status_sig[ThreadCommand].connect(self.thread_status) self.update_settings_signal[edict].connect( stage.update_settings) self.stage_thread.stage = stage self.stage_thread.start() self.ui.Stage_type_combo.setEnabled(False) self.command_stage.emit( ThreadCommand(command="ini_stage", attributes=[ self.settings.child( ('move_settings')).saveState(), self.controller ])) except Exception as e: self.logger.exception(str(e)) self.set_enabled_move_buttons(enable=False) def get_position(self): """ Get the current position from the launched thread via the "check_position" Thread Command. See Also -------- update_status, DAQ_utils.ThreadCommand """ try: self.command_stage.emit(ThreadCommand(command="check_position")) except Exception as e: self.logger.exception(str(e)) def move(self, move_command: MoveCommand): """Public method to trigger the correct action on the actuator. Should be used by external applications""" if move_command.move_type == 'abs': self.move_Abs(move_command.value) elif move_command.move_type == 'rel': self.move_Rel(move_command.value) elif move_command.move_type == 'home': self.move_Home(move_command.value) def move_Abs(self, position, send_to_tcpip=False): """ | Make the move from an absolute position. | | The move is made if target is in bounds, sending the thread command "Reset_Stop_Motion" and "move_Abs". =============== ========== =========================================== **Parameters** **Type** **Description** *position* float The absolute target position of the move =============== ========== =========================================== See Also -------- update_status, check_out_bounds, DAQ_utils.ThreadCommand """ try: self.send_to_tcpip = send_to_tcpip if not (position == self.current_position and self.stage_name == "Thorlabs_Flipper"): self.ui.Move_Done_LED.set_as_false() self.move_done_bool = False self.target_position = position self.update_status("Moving", wait_time=self.wait_time) # self.check_out_bounds(position) self.command_stage.emit( ThreadCommand(command="Reset_Stop_Motion")) self.command_stage.emit( ThreadCommand(command="move_Abs", attributes=[position])) except Exception as e: self.logger.exception(str(e)) def move_Home(self, send_to_tcpip=False): """ Send the thread commands "Reset_Stop_Motion" and "move_Home" and update the status. See Also -------- update_status, DAQ_utils.ThreadCommand """ self.send_to_tcpip = send_to_tcpip try: self.ui.Move_Done_LED.set_as_false() self.move_done_bool = False self.update_status("Moving", wait_time=self.wait_time) self.command_stage.emit(ThreadCommand(command="Reset_Stop_Motion")) self.command_stage.emit(ThreadCommand(command="move_Home")) except Exception as e: self.logger.exception(str(e)) def move_Rel_p(self): self.ui.Move_Rel_plus_pb.click() def move_Rel_m(self, send_to_tcpip=False): self.ui.Move_Rel_minus_pb.click() def move_Rel(self, rel_position, send_to_tcpip=False): """ | Make a move from the given relative psition and the current one. | | The move is done if (current position + relative position) is in bounds sending Threads Commands "Reset_Stop_Motion" and "move_done" =============== ========== =================================================== **Parameters** **Type** **Description** *position* float The relative target position from the current one =============== ========== =================================================== See Also -------- update_status, check_out_bounds, DAQ_utils.ThreadCommand """ try: self.send_to_tcpip = send_to_tcpip self.ui.Move_Done_LED.set_as_false() self.move_done_bool = False self.target_position = self.current_position + rel_position self.update_status("Moving", wait_time=self.wait_time) # self.check_out_bounds(self.target_position) self.command_stage.emit(ThreadCommand(command="Reset_Stop_Motion")) self.command_stage.emit( ThreadCommand(command="move_Rel", attributes=[rel_position])) except Exception as e: self.logger.exception(str(e)) def parameter_tree_changed(self, param, changes): """ | Check eventual changes in the changes list parameter. | | In case of changed values, emit the signal containing the current path and parameter via update_settings_signal to the connected hardware. =============== ==================================== ================================================== **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter The parameter to be checked *changes* (parameter,change,infos) tuple list The (parameter,change,infos) list to be treated =============== ==================================== ================================================== """ 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': if 'main_settings' not in path: self.update_settings_signal.emit( edict(path=path, param=data[0].saveState(), change=change)) elif change == 'value': if param.name() == 'connect_server': if param.value(): self.connect_tcp_ip() else: self.command_tcpip.emit(ThreadCommand('quit')) elif param.name() == 'ip_address' or param.name == 'port': self.command_tcpip.emit( ThreadCommand( 'update_connection', dict(ipaddress=self.settings.child( 'main_settings', 'tcpip', 'ip_address').value(), port=self.settings.child( 'main_settings', 'tcpip', 'port').value()))) if path is not None: if 'main_settings' not in path: self.update_settings_signal.emit( edict(path=path, param=param, change=change)) if self.settings.child('main_settings', 'tcpip', 'tcp_connected').value(): self.command_tcpip.emit( ThreadCommand('send_info', dict(path=path, param=param))) elif change == 'parent': if param.name() not in putils.iter_children( self.settings.child('main_settings'), []): self.update_settings_signal.emit( edict(path=['move_settings'], param=param, change=change)) def connect_tcp_ip(self): if self.settings.child('main_settings', 'tcpip', 'connect_server').value(): self.tcpclient_thread = QThread() tcpclient = TCPClient(self.settings.child('main_settings', 'tcpip', 'ip_address').value(), self.settings.child('main_settings', 'tcpip', 'port').value(), self.settings.child(('move_settings')), client_type="ACTUATOR") tcpclient.moveToThread(self.tcpclient_thread) self.tcpclient_thread.tcpclient = tcpclient tcpclient.cmd_signal.connect(self.process_tcpip_cmds) self.command_tcpip[ThreadCommand].connect(tcpclient.queue_command) self.tcpclient_thread.start() tcpclient.init_connection() @Slot(ThreadCommand) def process_tcpip_cmds(self, status): if 'move_abs' in status.command: self.move_Abs(status.attributes[0], send_to_tcpip=True) elif 'move_rel' in status.command: self.move_Rel(status.attributes[0], send_to_tcpip=True) elif 'move_home' in status.command: self.move_Home(send_to_tcpip=True) elif 'check_position' in status.command: self.send_to_tcpip = True self.command_stage.emit(ThreadCommand('check_position')) elif status.command == 'connected': self.settings.child('main_settings', 'tcpip', 'tcp_connected').setValue(True) elif status.command == 'disconnected': self.settings.child('main_settings', 'tcpip', 'tcp_connected').setValue(False) elif status.command == 'Update_Status': self.thread_status(status) elif status.command == 'set_info': param_dict = ioxml.XML_string_to_parameter(status.attributes[1])[0] param_tmp = Parameter.create(**param_dict) param = self.settings.child('move_settings', *status.attributes[0][1:]) param.restoreState(param_tmp.saveState()) def quit_fun(self): """ Leave the current instance of DAQ_Move_Main closing the parent widget. """ # insert anything that needs to be closed before leaving try: if self.initialized_state: self.ui.IniStage_pb.click() self.parent.close() # close the parent widget try: self.parent.parent().parent().close( ) # the dock parent (if any) except Exception as e: self.logger.info('No dock parent to close') except Exception as e: icon = QtGui.QIcon() icon.addPixmap( QtGui.QPixmap(":/Labview_icons/Icon_Library/close2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) msgBox = QtWidgets.QMessageBox(parent=None) msgBox.addButton(QtWidgets.QMessageBox.Yes) msgBox.addButton(QtWidgets.QMessageBox.No) msgBox.setWindowTitle("Error") msgBox.setText( str(e) + " error happened when uninitializing the stage.\nDo you still want to quit?" ) msgBox.setDefaultButton(QtWidgets.QMessageBox.Yes) ret = msgBox.exec() if ret == QtWidgets.QMessageBox.Yes: self.parent.close() @Slot() def raise_timeout(self): """ Update status with "Timeout occured" statement. See Also -------- update_status """ self.update_status("Timeout occured", wait_time=self.wait_time) self.wait_position_flag = False def set_enabled_move_buttons(self, enable=False): """ Set the move buttons enabled (or not) in User Interface from the gridLayout_buttons course. =============== ========== ================================================ **Parameters** **Type** **Description** *enable* boolean The parameter making enable or not the buttons =============== ========== ================================================ """ Nchildren = self.ui.gridLayout_buttons.count() for ind in range(Nchildren): widget = self.ui.gridLayout_buttons.itemAt(ind).widget() if widget is not None: widget.setEnabled(enable) self.ui.Moveto_pb_bis.setEnabled(enable) self.ui.Abs_position_sb_bis.setEnabled(enable) self.ui.Current_position_sb.setEnabled(enable) @Slot(int) def set_setting_tree(self, index=0): """ Set the move settings parameters tree, clearing the current tree and setting the 'move_settings' node. See Also -------- update_status """ self.stage_name = self.ui.Stage_type_combo.currentText() self.settings.child('main_settings', 'move_type').setValue(self.stage_name) try: for child in self.settings.child(('move_settings')).children(): child.remove() parent_module = utils.find_dict_in_list_from_key_val( DAQ_Move_Stage_type, 'name', self.stage_name) class_ = getattr( getattr(parent_module['module'], 'daq_move_' + self.stage_name), 'DAQ_Move_' + self.stage_name) params = getattr(class_, 'params') move_params = Parameter.create(name='move_settings', type='group', children=params) self.settings.child( ('move_settings')).addChildren(move_params.children()) except Exception as e: self.logger.exception(str(e)) def show_fine_tuning(self): """ Make GroupBox visible if User Interface corresponding attribute is checked to show fine tuning in. """ if self.ui.fine_tuning_pb.isChecked(): self.ui.groupBox.show() else: self.ui.groupBox.hide() def show_settings(self): """ Make settings tree visible if User Interface corresponding attribute is checked to show the settings tree in. """ if self.ui.parameters_pb.isChecked(): self.ui.settings_tree.setVisible(True) else: self.ui.settings_tree.setVisible(False) @Slot(int) def stage_changed(self, index=0): """ See Also -------- move_Abs """ pass def stop_Motion(self): """ stop any motion via the launched thread with the "stop_Motion" Thread Command. See Also -------- update_status, DAQ_utils.ThreadCommand """ try: self.command_stage.emit(ThreadCommand(command="stop_Motion")) except Exception as e: self.logger.exception(str(e)) @Slot(ThreadCommand) 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 main0 | Interpret a command from the command given by the ThreadCommand status : * In case of **'Update_status'** command, call the update_status method with status attributes as parameters * In case of **'ini_stage'** command, initialise a Stage from status attributes * In case of **'close'** command, close the launched stage thread * In case of **'check_position'** command, set the Current_position value from status attributes * In case of **'move_done'** command, set the Current_position value, make profile of move_done and send the move done signal with status attributes * In case of **'Move_Not_Done'** command, set the current position value from the status attributes, make profile of Not_Move_Done and send the Thread Command "Move_abs" * In case of **'update_settings'** command, create child "Move Settings" from status attributes (if possible) ================ ================= ====================================================== **Parameters** **Type** **Description** *status* ThreadCommand() instance of ThreadCommand containing two attributes : * *command* str * *attributes* list ================ ================= ====================================================== See Also -------- update_status, set_enabled_move_buttons, get_position, DAQ_utils.ThreadCommand, parameter_tree_changed, raise_timeout """ if status.command == "Update_Status": if len(status.attributes) > 2: self.update_status(status.attributes[0], wait_time=self.wait_time, log_type=status.attributes[1]) else: self.update_status(status.attributes[0], wait_time=self.wait_time) elif status.command == "ini_stage": # status.attributes[0]=edict(initialized=bool,info="", controller=) self.update_status("Stage initialized: {:} info: {:}".format( status.attributes[0]['initialized'], status.attributes[0]['info']), wait_time=self.wait_time) if status.attributes[0]['initialized']: self.controller = status.attributes[0]['controller'] self.set_enabled_move_buttons(enable=True) self.ui.Ini_state_LED.set_as_true() self.initialized_state = True else: self.initialized_state = False if self.initialized_state: self.get_position() self.init_signal.emit(self.initialized_state) elif status.command == "close": try: self.update_status(status.attributes[0], wait_time=self.wait_time) self.stage_thread.exit() self.stage_thread.wait() finished = self.stage_thread.isFinished() if finished: pass delattr(self, 'stage_thread') else: self.update_status('thread is locked?!', self.wait_time, 'log') except Exception as e: self.logger.exception(str(e)) self.initialized_state = False self.init_signal.emit(self.initialized_state) elif status.command == "check_position": self.ui.Current_position_sb.setValue(status.attributes[0]) self.current_position = status.attributes[0] if self.settings.child( 'main_settings', 'tcpip', 'tcp_connected').value() and self.send_to_tcpip: self.command_tcpip.emit( ThreadCommand('position_is', status.attributes)) elif status.command == "move_done": self.ui.Current_position_sb.setValue(status.attributes[0]) self.current_position = status.attributes[0] self.move_done_bool = True self.ui.Move_Done_LED.set_as_true() self.move_done_signal.emit(self.title, status.attributes[0]) if self.settings.child( 'main_settings', 'tcpip', 'tcp_connected').value() and self.send_to_tcpip: self.command_tcpip.emit( ThreadCommand('move_done', status.attributes)) elif status.command == "Move_Not_Done": self.ui.Current_position_sb.setValue(status.attributes[0]) self.current_position = status.attributes[0] self.move_done_bool = False self.ui.Move_Done_LED.set_as_false() self.command_stage.emit( ThreadCommand(command="move_Abs", attributes=[self.target_position])) elif status.command == 'update_main_settings': # this is a way for the plugins to update main settings of the ui (solely values, limits and options) try: if status.attributes[2] == 'value': self.settings.child('main_settings', *status.attributes[0]).setValue( status.attributes[1]) elif status.attributes[2] == 'limits': self.settings.child('main_settings', *status.attributes[0]).setLimits( status.attributes[1]) elif status.attributes[2] == 'options': self.settings.child( 'main_settings', *status.attributes[0]).setOpts(**status.attributes[1]) except Exception as e: self.logger.exception(str(e)) elif status.command == 'update_settings': # ThreadCommand(command='update_settings',attributes=[path,data,change])) try: self.settings.sigTreeStateChanged.disconnect( self.parameter_tree_changed ) # any changes on the settings will update accordingly the detector except Exception: pass try: if status.attributes[2] == 'value': self.settings.child('move_settings', *status.attributes[0]).setValue( status.attributes[1]) elif status.attributes[2] == 'limits': self.settings.child('move_settings', *status.attributes[0]).setLimits( status.attributes[1]) elif status.attributes[2] == 'options': self.settings.child( 'move_settings', *status.attributes[0]).setOpts(**status.attributes[1]) elif status.attributes[2] == 'childAdded': child = Parameter.create(name='tmp') child.restoreState(status.attributes[1][0]) self.settings.child('move_settings', *status.attributes[0]).addChild( status.attributes[1][0]) except Exception as e: self.logger.exception(str(e)) self.settings.sigTreeStateChanged.connect( self.parameter_tree_changed ) # any changes on the settings will update accordingly the detector elif status.command == 'raise_timeout': self.raise_timeout() elif status.command == 'outofbounds': self.bounds_signal.emit(True) elif status.command == 'show_splash': self.ui.settings_tree.setEnabled(False) self.splash_sc.show() self.splash_sc.raise_() self.splash_sc.showMessage(status.attributes[0], color=Qt.white) elif status.command == 'close_splash': self.splash_sc.close() self.ui.settings_tree.setEnabled(True) elif status.command == 'set_allowed_values': if 'decimals' in status.attributes: self.ui.Current_position_sb.setDecimals( status.attributes['decimals']) self.ui.Abs_position_sb.setDecimals( status.attributes['decimals']) self.ui.Abs_position_sb_bis.setDecimals( status.attributes['decimals']) if 'minimum' in status.attributes: self.ui.Current_position_sb.setMinimum( status.attributes['minimum']) self.ui.Abs_position_sb.setMinimum( status.attributes['minimum']) self.ui.Abs_position_sb_bis.setMinimum( status.attributes['minimum']) if 'maximum' in status.attributes: self.ui.Current_position_sb.setMaximum( status.attributes['maximum']) self.ui.Abs_position_sb.setMaximum( status.attributes['maximum']) self.ui.Abs_position_sb_bis.setMaximum( status.attributes['maximum']) if 'step' in status.attributes: self.ui.Current_position_sb.setSingleStep( status.attributes['step']) self.ui.Abs_position_sb.setSingleStep( status.attributes['step']) self.ui.Abs_position_sb_bis.setSingleStep( status.attributes['step']) def update_status(self, txt, wait_time=0): """ Show the given txt message in the status bar with a delay of wait_time ms if specified (0 by default). ================ ========== ================================= **Parameters** **Type** **Description** *txt* string The message to show *wait_time* int The delay time of showing ================ ========== ================================= """ self.ui.statusbar.showMessage(txt, wait_time) self.status_signal.emit(txt) self.logger.info(txt)
class DAQ_PID(QObject): """ """ command_pid = Signal(ThreadCommand) curr_points_signal = Signal(dict) setpoints_signal = Signal(dict) emit_curr_points_sig = Signal() models = get_models() params = [ {'title': 'Models', 'name': 'models', 'type': 'group', 'expanded': True, 'visible': True, 'children': [ {'title': 'Models class:', 'name': 'model_class', 'type': 'list', 'limits': [d['name'] for d in models]}, {'title': 'Model params:', 'name': 'model_params', 'type': 'group', 'children': []}, ]}, {'title': 'Move settings:', 'name': 'move_settings', 'expanded': True, 'type': 'group', 'visible': False, 'children': [ {'title': 'Units:', 'name': 'units', 'type': 'str', 'value': ''}]}, # here only to be compatible with DAQ_Scan, the model could update it {'title': 'Main Settings:', 'name': 'main_settings', 'expanded': True, 'type': 'group', 'children': [ {'title': 'Acquisition Timeout (ms):', 'name': 'timeout', 'type': 'int', 'value': 10000}, {'title': 'epsilon', 'name': 'epsilon', 'type': 'float', 'value': 0.01, 'tooltip': 'Precision at which move is considered as done'}, {'title': 'PID controls:', 'name': 'pid_controls', 'type': 'group', 'children': [ {'title': 'Sample time (ms):', 'name': 'sample_time', 'type': 'int', 'value': 10}, {'title': 'Refresh plot time (ms):', 'name': 'refresh_plot_time', 'type': 'int', 'value': 200}, {'title': 'Output limits:', 'name': 'output_limits', 'expanded': True, 'type': 'group', 'children': [ {'title': 'Output limit (min):', 'name': 'output_limit_min_enabled', 'type': 'bool', 'value': False}, {'title': 'Output limit (min):', 'name': 'output_limit_min', 'type': 'float', 'value': 0}, {'title': 'Output limit (max):', 'name': 'output_limit_max_enabled', 'type': 'bool', 'value': False}, {'title': 'Output limit (max:', 'name': 'output_limit_max', 'type': 'float', 'value': 100}, ]}, {'title': 'Auto mode:', 'name': 'auto_mode', 'type': 'bool', 'value': False, 'readonly': True}, {'title': 'Prop. on measurement:', 'name': 'proportional_on_measurement', 'type': 'bool', 'value': False}, {'title': 'PID constants:', 'name': 'pid_constants', 'type': 'group', 'children': [ {'title': 'Kp:', 'name': 'kp', 'type': 'float', 'value': 0.1, 'min': 0}, {'title': 'Ki:', 'name': 'ki', 'type': 'float', 'value': 0.01, 'min': 0}, {'title': 'Kd:', 'name': 'kd', 'type': 'float', 'value': 0.001, 'min': 0}, ]}, ]}, ]}, ] def __init__(self, dockarea): super().__init__() self.settings = Parameter.create(title='PID settings', name='pid_settings', type='group', children=self.params) self.title = 'PyMoDAQ PID' self.Initialized_state = False self.model_class = None self._curr_points = dict([]) self._setpoints = dict([]) self.modules_manager = None self.dock_area = dockarea self.check_moving = False self.setupUI() self.enable_controls_pid(False) self.enable_controls_pid_run(False) self.emit_curr_points_sig.connect(self.emit_curr_points) def set_module_manager(self, detector_modules, actuator_modules): self.modules_manager = ModulesManager(detector_modules, actuator_modules) 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, self.modules_manager, setpoints=self.setpoints, params=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(), sample_time=self.settings.child('main_settings', 'pid_controls', 'sample_time').value() / 1000, output_limits=output_limits, auto_mode=False), ) 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 Exception: pass self.pid_led.set_as_false() self.enable_controls_pid_run(False) self.Initialized_state = True 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.curr_points = datas['input'] 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) widget = QtWidgets.QWidget() widget_toolbar = QtWidgets.QWidget() verlayout = QtWidgets.QVBoxLayout() widget.setLayout(verlayout) self.toolbar_layout = QtWidgets.QGridLayout() widget_toolbar.setLayout(self.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') self.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') self.toolbar_layout.addWidget(self.ini_model_action, 2, 0) self.ini_model_action.clicked.connect(self.ini_model) self.model_led = QLED() self.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') self.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() self.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) self.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) self.toolbar_layout.addWidget(self.pause_action, 0, 3) self.pause_action.setChecked(True) self.pause_action.clicked.connect(self.pause_PID) lab = QtWidgets.QLabel('Target Value:') self.toolbar_layout.addWidget(lab, 3, 0, 1, 2) lab1 = QtWidgets.QLabel('Current Value:') self.toolbar_layout.addWidget(lab1, 4, 0, 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_pid) 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]['name']) # 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_name): self.settings.child('models', 'model_params').clearChildren() models = get_models() if len(models) > 0: model_class = find_dict_in_list_from_key_val(models, 'name', model_name)['class'] 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', [])) 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): for setp in self.setpoints_sb: setp.setEnabled(not self.pause_action.isChecked()) self.command_pid.emit(ThreadCommand('pause_PID', [self.pause_action.isChecked()])) 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.modules_manager.actuators: mod.stop_Motion() def set_model(self): model_name = self.settings.child('models', 'model_class').value() self.model_class = find_dict_in_list_from_key_val(self.models, 'name', model_name)['class'](self) self.set_setpoints_buttons() self.model_class.ini_model() self.settings.child('main_settings', 'epsilon').setValue(self.model_class.epsilon) def ini_model(self): try: if self.model_class is None: self.set_model() self.modules_manager.selected_actuators_name = self.model_class.actuators_name self.modules_manager.selected_detectors_name = self.model_class.detectors_name self.enable_controls_pid(True) self.model_led.set_as_true() self.ini_model_action.setEnabled(False) except Exception as e: logger.exception(str(e)) @property def setpoints(self): return [sp.value() for sp in self.setpoints_sb] @setpoints.setter def setpoints(self, values): for ind, sp in enumerate(self.setpoints_sb): sp.setValue(values[ind]) def setpoints_external(self, values_dict): for key in values_dict: index = self.model_class.setpoints_names.index(key) self.setpoints_sb[index].setValue(values_dict[key]) @property def curr_points(self): return [sp.value() for sp in self.currpoints_sb] @curr_points.setter def curr_points(self, values): for ind, sp in enumerate(self.currpoints_sb): sp.setValue(values[ind]) def emit_curr_points(self): if self.model_class is not None: self.curr_points_signal.emit(dict(zip(self.model_class.setpoints_names, self.curr_points))) def set_setpoints_buttons(self): self.setpoints_sb = [] self.currpoints_sb = [] for ind_set in range(self.model_class.Nsetpoints): self.setpoints_sb.append(SpinBox()) self.setpoints_sb[-1].setMinimumHeight(40) font = self.setpoints_sb[-1].font() font.setPointSizeF(20) self.setpoints_sb[-1].setFont(font) self.setpoints_sb[-1].setDecimals(6) self.toolbar_layout.addWidget(self.setpoints_sb[-1], 3, 2+ind_set, 1, 1) self.setpoints_sb[-1].valueChanged.connect(self.update_runner_setpoints) self.currpoints_sb.append(SpinBox()) self.currpoints_sb[-1].setMinimumHeight(40) self.currpoints_sb[-1].setReadOnly(True) self.currpoints_sb[-1].setDecimals(6) self.currpoints_sb[-1].setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) font = self.currpoints_sb[-1].font() font.setPointSizeF(20) self.currpoints_sb[-1].setFont(font) self.toolbar_layout.addWidget(self.currpoints_sb[-1], 4, 2+ind_set, 1, 1) self.setpoints_signal.connect(self.setpoints_external) def quit_fun(self): """ """ try: try: self.PIDThread.exit() 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 =============== =================================== ================================================================ """ 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() == 'refresh_plot_time' or param.name() == 'timeout': self.command_pid.emit(ThreadCommand('update_timer', [param.name(), param.value()])) elif param.name() == 'sample_time': self.command_pid.emit(ThreadCommand('update_options', dict(sample_time=param.value()))) elif param.name() in putils.iter_children( self.settings.child('main_settings', 'pid_controls', 'output_limits'), []): output_limits = convert_output_limits( self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_min').value(), self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_min_enabled').value(), self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_max').value(), self.settings.child('main_settings', 'pid_controls', 'output_limits', 'output_limit_max_enabled').value()) self.command_pid.emit(ThreadCommand('update_options', dict(output_limits=output_limits))) elif param.name() in putils.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 putils.iter_children(self.settings.child('models', 'model_params'), []): if self.model_class is not None: self.model_class.update_settings(param) elif param.name() == 'detector_modules': self.model_class.update_detector_names() elif change == 'parent': pass def update_runner_setpoints(self): self.command_pid.emit(ThreadCommand('update_setpoints', self.setpoints)) @Slot(list) def thread_status(self, status): # general function to get datas/infos from all threads back to the main """ """ pass
class SpectralOperationHandler(QDialog): """ Widget to handle user interactions with operations that are communicated from the SpecViz viewer. This is built to work with :func:`~spectral_cube.SpectralCube.apply_function` method by passing in a callable :class:`specviz.analysis.operations.FunctionalOperation` object. Parameters ---------- data : :class:`~glue.core.data.Data` Glue data object on which the spectral operation will be performed. function : :class:`specviz.analysis.operations.FunctionalOperation` Python class instance whose `call` function will be performed on the :class:`~spectral_cube.SpectralCube` object. """ def __init__(self, data, component_id, layout, function=None, func_proxy=None, stack=None, operation_name=None, ui_settings=None, *args, **kwargs): super(SpectralOperationHandler, self).__init__(*args, **kwargs) self._data = data self._stack = stack self._func_proxy = func_proxy self._function = function or (stack[0] if stack is not None else None) self._operation_name = operation_name or (self._function.function.__name__ if self._function is not None else "None") self._component_id = component_id self._operation_thread = None self._layout = layout self._ui_settings = ui_settings self._op_thread = None self.setup_ui() self.setup_connections() def setup_ui(self): """Setup the PyQt UI for this dialog.""" # Load the ui dialog loadUi(os.path.join(os.path.dirname(__file__), "operation_dialog.ui"), self) if self._ui_settings is not None: self.setWindowTitle(self._ui_settings.get("title")) self.operation_group_box.setTitle(self._ui_settings.get("group_box_title")) self.description_label.setText(self._ui_settings.get("description")) component_ids = [str(i) for i in self._data.component_ids()] cur_ind = self._data.component_ids().index(self._component_id) if self._stack is not None: operation_stack = [] for oper in self._stack: func_params = [] for arg in oper.args: func_params.append("{}".format(arg)) for k, v in oper.kwargs.items(): func_params.append("{}={}".format(k, str(v)[:10] + "... " + str( v)[-5:] if len( str(v)) > 15 else str( v))) operation_stack.append("{}({})".format(oper.function.__name__, ", ".join(func_params))) self.operation_combo_box.addItems(operation_stack) else: self.operation_combo_box.addItem(self._operation_name) # Populate combo box self.data_component_combo_box.addItems(component_ids) self.data_component_combo_box.setCurrentIndex(cur_ind) # Disable the button box if there are no available operations if self._function is None: self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) def setup_connections(self): """Setup signal/slot connections for this dialog.""" # When an operation is selected, update the function reference self.operation_combo_box.currentIndexChanged.connect( self.on_operation_index_changed) # When a data component is selected, update the data object reference self.data_component_combo_box.currentIndexChanged.connect( self.on_data_component_index_changed) # If the abort button is clicked, attempted to stop execution self.abort_button.clicked.connect(self.on_aborted) def _compose_cube(self): """ Create a :class:`~spectral_cube.SpectralCube` from a Glue data component. """ if issubclass(self._data.__class__, Subset): wcs = self._data.data.coords.wcs data = self._data.data mask = self._data.to_mask() else: wcs = self._data.coords.wcs data = self._data mask = np.ones(self._data.shape).astype(bool) mask = BooleanArrayMask(mask=mask, wcs=wcs) return SpectralCube(data[self._component_id], wcs=wcs, mask=mask, meta={'unit':self._data.get_component(self._component_id).units}) def on_operation_index_changed(self, index): """Called when the index of the operation combo box has changed.""" self._function = self._stack[index] def on_data_component_index_changed(self, index): """Called when the index of the component combo box has changed.""" self._component_id = self._data.component_ids()[index] def accept(self): """Called when the user clicks the "Okay" button of the dialog.""" # Show the progress bar and abort button self.progress_bar.setEnabled(True) self.abort_button.setEnabled(True) self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False) if self._func_proxy is not None: op_func = lambda *args, **kwargs: self._func_proxy(self._function, *args, **kwargs) else: op_func = self._function self._op_thread = QThread() self._op_worker = OperationWorker(self._compose_cube(), op_func) self._op_worker.moveToThread(self._op_thread) self._op_worker.result.connect(self.on_finished) self._op_worker.status.connect(self.on_status_updated) self._op_thread.started.connect(self._op_worker.run) self._op_thread.start() # data, unit = op_func(self._compose_cube(), None) # self.on_finished(data, unit) def on_aborted(self): """Called when the user aborts the operation.""" self._op_thread.terminate() self.progress_bar.reset() # Hide the progress bar and abort button self.abort_button.setEnabled(False) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True) def on_status_updated(self, value): """ Called when the status of the operation has been updated. This can be optionally be passed a value to use as the new progress bar value. Parameters ---------- value : float The value passed to the :class:`~qtpy.QtWidgets.QProgressBar` instance. """ self.progress_bar.setValue(value * 100) def on_finished(self, data, unit=None): """ Called when the `QThread` has finished performing the operation on the `SpectralCube` object. Parameters ---------- data : ndarray The result of the operation performed on the `SpectralCube` object. """ component_name = "{} {}".format(self._component_id, self._operation_name) comp_count = len([x for x in self._data.component_ids() if component_name in str(x)]) if comp_count > 0: component_name = "{} {}".format(component_name, comp_count) if data.ndim == 2: coords = WCSCoordinates(wcs=self._data.coords.wcs.celestial) self._data.container_2d = Data(label=self._data.label + " [2d]", coords=coords) self._data.container_2d.add_component(data, component_name) # self._layout.session.data_collection.append(self._data.container_2d) self._layout.add_overlay(data, component_name, display_now=True) else: component = Component(data, units=unit) self._data.add_component(component, component_name) self._op_thread.exit() super(SpectralOperationHandler, self).accept()