def setRadioButton(self, radioButton: QRadioButton, mode): radioButton.setAutoExclusive(False) if mode == 0: radioButton.setChecked(False) if mode == 1: radioButton.setChecked(True) if mode == 2: radioButton.setChecked(not radioButton.isChecked()) radioButton.setAutoExclusive(True) QApplication.processEvents()
def initUI(self): btn_1 = QRadioButton(self) btn_1.setText('버튼1') btn_1.move(60, 50) btn_2 = QRadioButton('&Button2', self) # Alt + B 를 입력하면 단축키가 된다. btn_2.setText('버튼2') btn_2.setChecked(True) btn_2.move(60, 80) btn_3 = QRadioButton('버튼3', self) btn_3.move(60, 110) btn_3.setAutoExclusive(False) self.setGeometry(300, 300, 300, 150) self.setWindowTitle('QRadioButton') self.show()
def createRadioButton(parent: QWidget, objectName="RadioButton", text="RadioButton", toolTip=None, isEnable=True, autoExclusive=True, isChecked=False, geometry: QRect = None, sizePolicy: QSizePolicy = None, onToggled=None): """ 创建一个单选按钮 :param parent: 父QWidget :param objectName: objectName :param text: text :param toolTip: toolTip :param isEnable: enable :param autoExclusive: autoExclusive False 独立到,True 同一个父widget到为一组 :param isChecked: isChecked 默认是否选中,true选中 :param geometry: geometry :param sizePolicy: 缩放策略 :param onToggled: toggled checked状态切换回调 :return: 单选按钮 """ widget = QRadioButton(parent) widgetSetAttrs(widget, objectName, toolTip=toolTip, geometry=geometry, isEnable=isEnable, sizePolicy=sizePolicy) widget.setText(_translate(contextName, text)) widget.setAutoExclusive(autoExclusive) widget.setChecked(isChecked) if onToggled: widget.toggled.connect(onToggled) return widget
class ServerPlugin(Plugin): __icon__ = "fa.qrcode" __pname__ = "server" __views__ = ["slice_viewer"] __tab__ = "server" def __init__(self, parent=None): super().__init__(parent=parent) run_config = { "server_ip": "127.0.0.1", "server_port": "8134", "workspace_name": "test_hunt_d4b", "use_ssh": False, "ssh_host": "ws168.diamond.ac.uk", "ssh_port": "22", } workspace_config = { "dataset_name": "data", "datasets_dir": "/path/to/my/data/dir", "vol_fname": "myfile.h5", "workspace_name": "my_survos_workspace", "downsample_by": "1", } from survos2.server.config import cfg pipeline_config = dict(cfg) self.run_config = run_config self.workspace_config = workspace_config self.pipeline_config = pipeline_config self.server_process = None self.client_process = None self.layout = QVBoxLayout() tabwidget = QTabWidget() tab1 = QWidget() tab2 = QWidget() tabwidget.addTab(tab1, "Setup and Start Survos") self.create_workspace_button = QPushButton("Create workspace") tab1.layout = QVBoxLayout() tab1.setLayout(tab1.layout) chroot_fields = self.get_chroot_fields() tab1.layout.addWidget(chroot_fields) workspace_fields = self.get_workspace_fields() tab1.layout.addWidget(workspace_fields) self.setup_adv_run_fields() self.adv_run_fields.hide() run_fields = self.get_run_fields() tab1.layout.addWidget(run_fields) output_config_button = QPushButton("Save config") self.create_workspace_button.clicked.connect( self.create_workspace_clicked) output_config_button.clicked.connect(self.output_config_clicked) self.layout.addWidget(tabwidget) self.setGeometry(300, 300, 600, 400) self.setWindowTitle("SuRVoS Settings Editor") current_fpth = os.path.dirname(os.path.abspath(__file__)) self.setWindowIcon( QIcon(os.path.join(current_fpth, "resources", "logo.png"))) self.setLayout(self.layout) self.show() def get_chroot_fields(self): chroot_fields = QGroupBox("Set Main Directory for Storing Workspaces:") chroot_fields.setMaximumHeight(130) chroot_layout = QGridLayout() self.given_chroot_linedt = QLineEdit(CHROOT) chroot_layout.addWidget(self.given_chroot_linedt, 1, 0, 1, 2) set_chroot_button = QPushButton("Set Workspaces Root") chroot_layout.addWidget(set_chroot_button, 1, 2) chroot_fields.setLayout(chroot_layout) set_chroot_button.clicked.connect(self.set_chroot) return chroot_fields def get_workspace_fields(self): """Gets the QGroupBox that contains all the fields for setting up the workspace. Returns: PyQt5.QWidgets.GroupBox: GroupBox with workspace fields. """ select_data_button = QPushButton("Select") workspace_fields = QGroupBox("Create New Workspace:") wf_layout = QGridLayout() wf_layout.addWidget(QLabel("Data File Path:"), 0, 0) current_data_path = Path(self.workspace_config["datasets_dir"], self.workspace_config["vol_fname"]) self.data_filepth_linedt = QLineEdit(str(current_data_path)) wf_layout.addWidget(self.data_filepth_linedt, 1, 0, 1, 2) wf_layout.addWidget(select_data_button, 1, 2) wf_layout.addWidget(QLabel("HDF5 Internal Data Path:"), 2, 0, 1, 1) ws_dataset_name = self.workspace_config["dataset_name"] internal_h5_path = (ws_dataset_name if str(ws_dataset_name).startswith("/") else "/" + ws_dataset_name) self.h5_intpth_linedt = QLineEdit(internal_h5_path) wf_layout.addWidget(self.h5_intpth_linedt, 2, 1, 1, 1) wf_layout.addWidget(QLabel("Workspace Name:"), 3, 0) self.ws_name_linedt_1 = QLineEdit( self.workspace_config["workspace_name"]) wf_layout.addWidget(self.ws_name_linedt_1, 3, 1) wf_layout.addWidget(QLabel("Downsample Factor:"), 4, 0) self.downsample_spinner = QSpinBox() self.downsample_spinner.setRange(1, 10) self.downsample_spinner.setSpecialValueText("None") self.downsample_spinner.setMaximumWidth(60) self.downsample_spinner.setValue( int(self.workspace_config["downsample_by"])) wf_layout.addWidget(self.downsample_spinner, 4, 1, 1, 1) # ROI self.setup_roi_fields() wf_layout.addWidget(self.roi_fields, 4, 2, 1, 2) self.roi_fields.hide() wf_layout.addWidget(self.create_workspace_button, 5, 0, 1, 3) workspace_fields.setLayout(wf_layout) select_data_button.clicked.connect(self.launch_data_loader) return workspace_fields def setup_roi_fields(self): """Sets up the QGroupBox that displays the ROI dimensions, if selected.""" self.roi_fields = QGroupBox("ROI:") roi_fields_layout = QHBoxLayout() # z roi_fields_layout.addWidget(QLabel("z:"), 0) self.zstart_roi_val = QLabel("0") roi_fields_layout.addWidget(self.zstart_roi_val, 1) roi_fields_layout.addWidget(QLabel("-"), 2) self.zend_roi_val = QLabel("0") roi_fields_layout.addWidget(self.zend_roi_val, 3) # y roi_fields_layout.addWidget(QLabel("y:"), 4) self.ystart_roi_val = QLabel("0") roi_fields_layout.addWidget(self.ystart_roi_val, 5) roi_fields_layout.addWidget(QLabel("-"), 6) self.yend_roi_val = QLabel("0") roi_fields_layout.addWidget(self.yend_roi_val, 7) # x roi_fields_layout.addWidget(QLabel("x:"), 8) self.xstart_roi_val = QLabel("0") roi_fields_layout.addWidget(self.xstart_roi_val, 9) roi_fields_layout.addWidget(QLabel("-"), 10) self.xend_roi_val = QLabel("0") roi_fields_layout.addWidget(self.xend_roi_val, 11) self.roi_fields.setLayout(roi_fields_layout) def setup_adv_run_fields(self): """Sets up the QGroupBox that displays the advanced optiona for starting SuRVoS2.""" self.adv_run_fields = QGroupBox("Advanced Run Settings:") adv_run_layout = QGridLayout() adv_run_layout.addWidget(QLabel("Server IP Address:"), 0, 0) self.server_ip_linedt = QLineEdit(self.run_config["server_ip"]) adv_run_layout.addWidget(self.server_ip_linedt, 0, 1) adv_run_layout.addWidget(QLabel("Server Port:"), 1, 0) self.server_port_linedt = QLineEdit(self.run_config["server_port"]) adv_run_layout.addWidget(self.server_port_linedt, 1, 1) # SSH Info self.ssh_button = QRadioButton("Use SSH") self.ssh_button.setAutoExclusive(False) adv_run_layout.addWidget(self.ssh_button, 0, 2) ssh_flag = self.run_config.get("use_ssh", False) if ssh_flag: self.ssh_button.setChecked(True) self.ssh_button.toggled.connect(self.toggle_ssh) self.adv_ssh_fields = QGroupBox("SSH Settings:") adv_ssh_layout = QGridLayout() adv_ssh_layout.setColumnStretch(2, 2) ssh_host_label = QLabel("Host") self.ssh_host_linedt = QLineEdit(self.run_config.get("ssh_host", "")) adv_ssh_layout.addWidget(ssh_host_label, 0, 0) adv_ssh_layout.addWidget(self.ssh_host_linedt, 0, 1, 1, 2) ssh_user_label = QLabel("Username") self.ssh_username_linedt = QLineEdit(self.get_login_username()) adv_ssh_layout.addWidget(ssh_user_label, 1, 0) adv_ssh_layout.addWidget(self.ssh_username_linedt, 1, 1, 1, 2) ssh_port_label = QLabel("Port") self.ssh_port_linedt = QLineEdit(self.run_config.get("ssh_port", "")) adv_ssh_layout.addWidget(ssh_port_label, 2, 0) adv_ssh_layout.addWidget(self.ssh_port_linedt, 2, 1, 1, 2) self.adv_ssh_fields.setLayout(adv_ssh_layout) #adv_run_layout.addWidget(self.adv_ssh_fields, 1, 2, 2, 5) self.adv_run_fields.setLayout(adv_run_layout) def get_run_fields(self): """Gets the QGroupBox that contains the fields for starting SuRVoS. Returns: PyQt5.QWidgets.GroupBox: GroupBox with run fields. """ self.run_button = QPushButton("Start Server") self.stop_button = QPushButton("Stop Server") self.existing_button = QPushButton("Use Existing Server") advanced_button = QRadioButton("Advanced") run_fields = QGroupBox("Run SuRVoS:") run_layout = QGridLayout() workspaces = os.listdir(CHROOT) self.workspaces_list = ComboBox() for s in workspaces: self.workspaces_list.addItem(key=s) run_layout.addWidget(QLabel("Workspace Name:"), 0, 0) self.ws_name_linedt_2 = QLineEdit( self.workspace_config["workspace_name"]) self.ws_name_linedt_2.setAlignment(Qt.AlignLeft) self.workspaces_list.setLineEdit(self.ws_name_linedt_2) # run_layout.addWidget(self.ws_name_linedt_2, 0, 1) run_layout.addWidget(self.workspaces_list, 0, 1) run_layout.addWidget(advanced_button, 1, 0) run_layout.addWidget(self.adv_run_fields, 2, 1) run_layout.addWidget(self.run_button, 3, 0, 1, 3) run_layout.addWidget(self.stop_button, 4, 0, 1, 3) run_layout.addWidget(self.existing_button, 5, 0, 1, 3) run_fields.setLayout(run_layout) advanced_button.toggled.connect(self.toggle_advanced) self.run_button.clicked.connect(self.run_clicked) self.stop_button.clicked.connect(self.stop_clicked) self.existing_button.clicked.connect(self.existing_clicked) return run_fields def get_login_username(self): try: user = getpass.getuser() except Exception: user = "" return user def refresh_chroot(self): workspaces = os.listdir(DataModel.g.CHROOT) self.workspaces_list.clear() for s in workspaces: self.workspaces_list.addItem(key=s) @pyqtSlot() def set_chroot(self): CHROOT = self.given_chroot_linedt.text() Config.update({"model": {"chroot": CHROOT}}) logger.debug(f"Setting CHROOT to {CHROOT}") DataModel.g.CHROOT = CHROOT self.refresh_chroot() @pyqtSlot() def launch_data_loader(self): """Load the dialog box widget to select data with data preview window and ROI selection.""" path = None int_h5_pth = None dialog = LoadDataDialog(self) result = dialog.exec_() self.roi_limits = None if result == QDialog.Accepted: path = dialog.winput.path.text() int_h5_pth = dialog.int_h5_pth.text() down_factor = dialog.downsample_spinner.value() if path and int_h5_pth: self.data_filepth_linedt.setText(path) self.h5_intpth_linedt.setText(int_h5_pth) self.downsample_spinner.setValue(down_factor) if dialog.roi_changed: self.roi_limits = tuple(map(str, dialog.get_roi_limits())) self.roi_fields.show() self.update_roi_fields_from_dialog() else: self.roi_fields.hide() def update_roi_fields_from_dialog(self): """Updates the ROI fields in the main window.""" x_start, x_end, y_start, y_end, z_start, z_end = self.roi_limits self.xstart_roi_val.setText(x_start) self.xend_roi_val.setText(x_end) self.ystart_roi_val.setText(y_start) self.yend_roi_val.setText(y_end) self.zstart_roi_val.setText(z_start) self.zend_roi_val.setText(z_end) @pyqtSlot() def toggle_advanced(self): """Controls displaying/hiding the advanced run fields on radio button toggle.""" rbutton = self.sender() if rbutton.isChecked(): self.adv_run_fields.show() else: self.adv_run_fields.hide() @pyqtSlot() def toggle_ssh(self): """Controls displaying/hiding the SSH fields on radio button toggle.""" rbutton = self.sender() if rbutton.isChecked(): self.adv_ssh_fields.show() else: self.adv_ssh_fields.hide() @pyqtSlot() def create_workspace_clicked(self): """Performs checks and coordinates workspace creation on button press.""" logger.debug("Creating workspace: ") # Set the path to the data file vol_path = Path(self.data_filepth_linedt.text()) if not vol_path.is_file(): err_str = f"No data file exists at {vol_path}!" logger.error(err_str) self.button_feedback_response(err_str, self.create_workspace_button, "maroon") else: self.workspace_config["datasets_dir"] = str(vol_path.parent) self.workspace_config["vol_fname"] = str(vol_path.name) dataset_name = self.h5_intpth_linedt.text() self.workspace_config["dataset_name"] = str(dataset_name).strip( "/") # Set the workspace name ws_name = self.ws_name_linedt_1.text() self.workspace_config["workspace_name"] = ws_name # Set the downsample factor ds_factor = self.downsample_spinner.value() self.workspace_config["downsample_by"] = ds_factor # Set the ROI limits if they exist if self.roi_limits: self.workspace_config["roi_limits"] = self.roi_limits try: response = init_ws(self.workspace_config) _, error = response if not error: self.button_feedback_response( "Workspace created sucessfully", self.create_workspace_button, "green", ) # Update the workspace name in the 'Run' section self.ws_name_linedt_2.setText(self.ws_name_linedt_1.text()) except WorkspaceException as e: logger.exception(e) self.button_feedback_response(str(e), self.create_workspace_button, "maroon") self.refresh_chroot() def button_feedback_response(self, message, button, colour_str, timeout=2): """Changes button colour and displays feedback message for a limited time period. Args: message (str): Message to display in button. button (PyQt5.QWidgets.QBushButton): The button to manipulate. colour_str (str): The standard CSS colour string or hex code describing the colour to change the button to. """ timeout *= 1000 msg_old = button.text() col_old = button.palette().button().color txt_col_old = button.palette().buttonText().color button.setText(message) button.setStyleSheet(f"background-color: {colour_str}; color: white") timer = QTimer() timer.singleShot( timeout, lambda: self.reset_button(button, msg_old, col_old, txt_col_old)) @pyqtSlot() def reset_button(self, button, msg_old, col_old, txt_col_old): """Sets a button back to its original display settings. Args: button (PyQt5.QWidgets.QBushButton): The button to manipulate. msg_old (str): Message to display in button. col_old (str): The standard CSS colour string or hex code describing the colour to change the button to. txt_col_old (str): The standard CSS colour string or hex code describing the colour to change the button text to. """ button.setStyleSheet(f"background-color: {col_old().name()}") button.setStyleSheet(f"color: {txt_col_old().name()}") button.setText(msg_old) button.update() @pyqtSlot() def output_config_clicked(self): """Outputs pipeline config YAML file on button click.""" out_fname = "pipeline_cfg.yml" logger.debug(f"Outputting pipeline config: {out_fname}") with open(out_fname, "w") as outfile: yaml.dump(self.pipeline_config, outfile, default_flow_style=False, sort_keys=False) def get_ssh_params(self): ssh_host = self.ssh_host_linedt.text() ssh_user = self.ssh_username_linedt.text() ssh_port = int(self.ssh_port_linedt.text()) return ssh_host, ssh_user, ssh_port def start_server_over_ssh(self): params = self.get_ssh_params() if not all(params): logger.error( "Not all SSH parameters given! Not connecting to SSH.") pass ssh_host, ssh_user, ssh_port = params # Pop up dialog to ask for password text, ok = QInputDialog.getText(None, "Login", f"Password for {ssh_user}@{ssh_host}", QLineEdit.Password) if ok and text: self.ssh_worker = SSHWorker(params, text, self.run_config) self.ssh_thread = QThread(self) self.ssh_worker.moveToThread(self.ssh_thread) self.ssh_worker.button_message_signal.connect( self.send_msg_to_run_button) self.ssh_worker.error_signal.connect(self.on_ssh_error) self.ssh_worker.finished.connect(self.start_client) self.ssh_worker.update_ip_linedt_signal.connect( self.update_ip_linedt) self.ssh_thread.started.connect( self.ssh_worker.start_server_over_ssh) self.ssh_thread.start() def closeEvent(self, event): reply = QMessageBox.question( self, "Quit", "Are you sure you want to quit? " "The server will be stopped.", QMessageBox.Yes | QMessageBox.No, QMessageBox.No, ) if reply == QMessageBox.Yes: event.accept() else: event.ignore() @pyqtSlot() def on_ssh_error(self): self.ssh_error = True @pyqtSlot(str) def update_ip_linedt(self, ip): self.server_ip_linedt.setText(ip) @pyqtSlot(list) def send_msg_to_run_button(self, param_list): self.button_feedback_response(param_list[0], self.run_button, param_list[1], param_list[2]) @pyqtSlot() def stop_clicked(self): logger.debug("Stopping server") if self.server_process is not None: self.server_process.kill() @pyqtSlot() def run_clicked(self): """Starts SuRVoS2 server and client as subprocesses when 'Run' button pressed. Raises: Exception: If survos.py not found. """ with progress(total=3) as pbar: pbar.set_description("Starting server...") pbar.update(1) self.ssh_error = ( False # Flag which will be set to True if there is an SSH error ) command_dir = os.path.abspath(os.path.dirname(__file__)) # os.getcwd() # Set current dir to survos root from pathlib import Path command_dir = Path( command_dir).absolute().parent.parent.parent.resolve() os.chdir(command_dir) self.script_fullname = os.path.join(command_dir, "survos.py") if not os.path.isfile(self.script_fullname): raise Exception("{}: Script not found".format( self.script_fullname)) # Retrieve the parameters from the fields TODO: Put some error checking in self.run_config["workspace_name"] = self.ws_name_linedt_2.text() self.run_config["server_port"] = self.server_port_linedt.text() # Temporary measure to check whether the workspace exists or not full_ws_path = os.path.join(Config["model.chroot"], self.run_config["workspace_name"]) if not os.path.isdir(full_ws_path): logger.error( f"No workspace can be found at {full_ws_path}, Not starting SuRVoS." ) self.button_feedback_response( f"Workspace {self.run_config['workspace_name']} does not appear to exist!", self.run_button, "maroon", ) return pbar.update(1) # Try some fancy SSH stuff here if self.ssh_button.isChecked(): self.start_server_over_ssh() else: self.server_process = subprocess.Popen([ "python", self.script_fullname, "start_server", self.run_config["workspace_name"], self.run_config["server_port"], DataModel.g.CHROOT, ]) try: outs, errs = self.server_process.communicate(timeout=10) print(f"OUTS: {outs, errs}") except subprocess.TimeoutExpired: pass # self.start_client() logger.info(f"setting remote: {self.server_port_linedt.text()}") remote_ip_port = "127.0.0.1:" + self.server_port_linedt.text() logger.info(f"setting remote: {remote_ip_port}") resp = Launcher.g.set_remote(remote_ip_port) logger.info(f"Response from server to setting remote: {resp}") cfg.ppw.clientEvent.emit({ "source": "server_tab", "data": "set_workspace", "workspace": self.ws_name_linedt_2.text(), }) cfg.ppw.clientEvent.emit({ "source": "panel_gui", "data": "refresh", "value": None }) #cfg.ppw.clientEvent.emit({'data' : 'view_feature', 'feature_id' : '001_raw'}) pbar.update(1) @pyqtSlot() def existing_clicked(self): ssh_ip = self.server_ip_linedt.text() remote_ip_port = ssh_ip + ":" + self.server_port_linedt.text() logger.info(f"setting remote: {remote_ip_port}") resp = Launcher.g.set_remote(remote_ip_port) logger.info(f"Response from server to setting remote: {resp}") cfg.ppw.clientEvent.emit({ "source": "server_tab", "data": "set_workspace", "workspace": self.ws_name_linedt_2.text(), }) cfg.ppw.clientEvent.emit({ "source": "panel_gui", "data": "refresh", "value": None }) def start_client(self): if not self.ssh_error: self.button_feedback_response("Starting Client.", self.run_button, "green", 7) self.run_config["server_ip"] = self.server_ip_linedt.text() self.client_process = subprocess.Popen([ "python", self.script_fullname, "nu_gui", self.run_config["workspace_name"], str(self.run_config["server_ip"]) + ":" + str(self.run_config["server_port"]), ])
class MainWindow(QMainWindow): """[summary] Main window class containing the main window and its associated methods. Args: QMainWindow (QObject): See qt documentation for more info. """ # Send logging parameters to worker method logging_requested = QtCore.pyqtSignal(str, str, bool, str, object) def __init__(self, *args, **kwargs): """[summary] Function to initialise the Main Window, which will hold all the subsequent widgets to be created. """ super(MainWindow, self).__init__(*args, **kwargs) self._tdc1_dev = None # tdc1 device object self._dev_mode = '' # 0 = 'singles', 1 = 'pairs', 3 = 'timestamp' self.device_path = '' # Device path, eg. 'COM4' self.integration_time = 1 self._logfile_name = '' # Track the logfile(csv) being used by GUI self._ch_start = 1 # Start channel for pairs self._ch_stop = 3 # Stop channel for pairs self.plotSamples = 501 # Number of data points to plot self.log_flag = False # Flag to track if data is being logged to csv file self.acq_flag = False # Track if data is being acquired self._radio_flags = [ 0, 0, 0, 0 ] # Tracking which radio buttons are selected. All 0s by default self.logger = None # Variable that will hold the logWorker object self.logger_thread = None # Variable that will hold the QThread object self.initUI() # UI is initialised afer the class variables are defined self._plot_tab = self.tabs.currentIndex( ) # Counts graph = 0, Coincidences graph = 1 self.idx = min(len(self.y1), self.plotSamples) # Index for plotting def initUI(self): """[summary] Contains all the UI elements and associated functionalities. """ defaultFont = QtGui.QFont("Helvetica", 14) #---------Buttons---------# self.scanForDevice_Button = QtWidgets.QPushButton( "Scan for Device", self) self.scanForDevice_Button.clicked.connect(self.updateDevList) #self.scanForDevice_Button.setFixedSize(QSize(115, 35)) self.liveStart_Button = QtWidgets.QPushButton("Live Start", self) self.liveStart_Button.clicked.connect(self.liveStart) #self.liveStart_Button.setFixedSize(QSize(115, 35)) self.selectLogfile_Button = QtWidgets.QPushButton( "Select Logfile", self) self.selectLogfile_Button.clicked.connect(self.selectLogfile) #self.selectLogfile_Button.setFixedSize(QSize(115, 35)) # setAutoExclusive method is used to toggle the radio buttons independently. self.radio1_Button = QRadioButton("Channel 1", self) self.radio1_Button.setStyleSheet('color: red; font-size: 14px') self.radio1_Button.setAutoExclusive(False) self.radio1_Button.toggled.connect( lambda: self.displayPlot1(self.radio1_Button)) self.radio2_Button = QRadioButton("Channel 2", self) self.radio2_Button.setStyleSheet('color: green; font-size: 14px') self.radio2_Button.setAutoExclusive(False) self.radio2_Button.toggled.connect( lambda: self.displayPlot2(self.radio2_Button)) self.radio3_Button = QRadioButton("Channel 3", self) self.radio3_Button.setStyleSheet('color: blue; font-size: 14px') self.radio3_Button.setAutoExclusive(False) self.radio3_Button.toggled.connect( lambda: self.displayPlot3(self.radio3_Button)) self.radio4_Button = QRadioButton("Channel 4", self) self.radio4_Button.setStyleSheet('color: black; font-size: 14px') self.radio4_Button.setAutoExclusive(False) self.radio4_Button.toggled.connect( lambda: self.displayPlot4(self.radio4_Button)) #---------Buttons---------# #---------Labels---------# #labelFontSize = "font-size: 18px" self.deviceLabel = QtWidgets.QLabel("Device:", self) self.deviceModeLabel = QtWidgets.QLabel("GUI Mode:", self) self.logfileLabel = QtWidgets.QLabel('', self) self.samplesLabel = QtWidgets.QLabel('Plot Samples:', self) self.integrationLabel = QtWidgets.QLabel("Integration time (ms):", self) self.Ch1CountsLabel = QtWidgets.QLabel("0", self) self.Ch1CountsLabel.setStyleSheet("color: red; font-size: 128px") self.Ch1CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.Ch2CountsLabel = QtWidgets.QLabel("0", self) self.Ch2CountsLabel.setStyleSheet("color: green; font-size: 128px") self.Ch2CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.Ch3CountsLabel = QtWidgets.QLabel("0", self) self.Ch3CountsLabel.setStyleSheet("color: blue; font-size: 128px") self.Ch3CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.Ch4CountsLabel = QtWidgets.QLabel("0", self) self.Ch4CountsLabel.setStyleSheet("color: black; font-size: 128px") self.Ch4CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.startChannelLabel = QtWidgets.QLabel("Start Channel:", self) self.stopChannelLabel = QtWidgets.QLabel("Stop Channel:", self) self.centerLabel = QtWidgets.QLabel("Center:", self) self.pairsRateLabel = QtWidgets.QLabel("Pairs/sec: <br>" + "0") self.pairsRateLabel.setStyleSheet("font-size: 64px") self.pairsRateLabel.setAlignment(QtCore.Qt.AlignCenter) self.resolutionTextLabel = QtWidgets.QLabel("Bin Width:", self) #---------Labels---------# #---------Interactive Fields---------# self.integrationSpinBox = QSpinBox(self) self.integrationSpinBox.setRange( 0, 65535) # Max integration time based on tdc1 specs self.integrationSpinBox.setValue(1000) # Default 1000ms = 1s self.integrationSpinBox.setKeyboardTracking( False ) # Makes sure valueChanged signal only fires when you want it to self.integrationSpinBox.valueChanged.connect(self.update_intTime) dev_list = serial_connection.search_for_serial_devices( tdc1.TimeStampTDC1.DEVICE_IDENTIFIER) self.devCombobox = QComboBox(self) self.devCombobox.addItem('Select your device') self.devCombobox.addItems(dev_list) self.devCombobox.currentTextChanged.connect(self.selectDevice) _dev_modes = ['singles', 'pairs'] self.modesCombobox = QComboBox(self) self.modesCombobox.addItem('Select mode') self.modesCombobox.addItems(_dev_modes) self.modesCombobox.currentTextChanged.connect(self.selectDeviceMode) _channels = ['1', '2', '3', '4'] self.channelsCombobox1 = QComboBox(self) self.channelsCombobox1.addItem('Select') self.channelsCombobox1.addItems(_channels) self.channelsCombobox1.setCurrentIndex(1) self.channelsCombobox1.currentTextChanged.connect(self.updateStart) self.channelsCombobox2 = QComboBox(self) self.channelsCombobox2.addItem('Select') self.channelsCombobox2.addItems(_channels) self.channelsCombobox2.setCurrentIndex(3) self.channelsCombobox2.currentTextChanged.connect(self.updateStop) self.samplesSpinbox = QSpinBox(self) self.samplesSpinbox.setRange(0, 501) self.samplesSpinbox.setValue(501) # Default plot 501 data points self.samplesSpinbox.setKeyboardTracking(False) self.samplesSpinbox.valueChanged.connect(self.updatePlotSamples) self.centerSpinbox = QSpinBox(self) self.centerSpinbox.setRange(0, 1000) self.centerSpinbox.setKeyboardTracking(False) self.resolutionSpinbox = QSpinBox(self) self.resolutionSpinbox.setRange(0, 1000) self.resolutionSpinbox.setKeyboardTracking(False) self.resolutionSpinbox.valueChanged.connect(self.updateBins) #---------Interactive Fields---------# #---------PLOTS---------# # Initiating plot data variables # Plot 1 - Four channel counts plot self.x = [] self.y1 = [] self.y2 = [] self.y3 = [] self.y4 = [] self.xnew = [] self.y1new = [] self.y2new = [] self.y3new = [] self.y4new = [] self.y_data = [self.y1new, self.y2new, self.y3new, self.y4new] # Plot 2 - Time difference histogram (Channel cross-correlation) self.bins = 501 self.binsize = 2 # milliseconds self.x0 = np.arange(0, self.bins * self.binsize, self.bins) self.y0 = np.zeros_like(self.x0) self.x0new = [] self.y0new = [] font = QtGui.QFont("Arial", 24) labelStyle = '<span style=\"color:black;font-size:25px\">' # Setting up plot window 1 (Plot Widget) self.tdcPlot = pg.PlotWidget(title="Counts Graph") self.tdcPlot.setBackground('w') self.tdcPlot.setLabel('left', labelStyle + 'Counts') self.tdcPlot.setLabel('bottom', labelStyle + 'Sample Number') self.tdcPlot.getAxis('left').tickFont = font self.tdcPlot.getAxis('bottom').tickFont = font self.tdcPlot.getAxis('bottom').setPen(color='k') self.tdcPlot.getAxis('left').setPen(color='k') self.tdcPlot.showGrid(y=True) # Setting up plot window 2 (Plot Widget) self.tdcPlot2 = pg.PlotWidget(title="Coincidences Histogram") self.tdcPlot2.setBackground('w') self.tdcPlot2.setLabel('left', labelStyle + 'Coincidences') self.tdcPlot2.setLabel('bottom', labelStyle + 'Time Delay') self.tdcPlot2.getAxis('left').tickFont = font self.tdcPlot2.getAxis('bottom').tickFont = font self.tdcPlot2.getAxis('bottom').setPen(color='k') self.tdcPlot2.getAxis('left').setPen(color='k') self.tdcPlot2.showGrid(y=True) # Setting up data plots (Plot data item) self.lineStyle1 = pg.mkPen(width=2, color='r') # Red self.lineStyle2 = pg.mkPen(width=2, color='g') # Green self.lineStyle3 = pg.mkPen(width=2, color='b') # Blue self.lineStyle4 = pg.mkPen(width=2, color='k') # Black self.lineStyle0 = pg.mkPen(width=1, color='r') # Plotting the graph - https://pyqtgraph.readthedocs.io/en/latest/plotting.html for organisation of plotting classes # Take note: multiple plotDataItems can sit on one plotWidget self.linePlot1 = self.tdcPlot.plot(self.x, self.y1, pen=self.lineStyle1) self.linePlot2 = self.tdcPlot.plot(self.x, self.y2, pen=self.lineStyle2) self.linePlot3 = self.tdcPlot.plot(self.x, self.y3, pen=self.lineStyle3) self.linePlot4 = self.tdcPlot.plot(self.x, self.y4, pen=self.lineStyle4) self.histogramPlot = self.tdcPlot2.plot(self.x0, self.y0, pen=self.lineStyle0, symbol='x', symbolPen='b', symbolBrush=0.2) self.linePlots = [ self.linePlot1, self.linePlot2, self.linePlot3, self.linePlot4 ] #---------PLOTS---------# #---------Main Window---------# self.setWindowTitle("TDC-1") #---------Main Window---------# #---------Tabs---------# self.tabs = QTabWidget() self.tab1 = QWidget() self.layout = QGridLayout() self.layout.addWidget(self.tdcPlot, 0, 0, 5, 5) self.layout.addWidget(self.Ch1CountsLabel, 0, 5) self.layout.addWidget(self.Ch2CountsLabel, 1, 5) self.layout.addWidget(self.Ch3CountsLabel, 2, 5) self.layout.addWidget(self.Ch4CountsLabel, 3, 5) self.tab1.setLayout(self.layout) self.tabs.addTab(self.tab1, "Counts") self.tab2 = QWidget() self.layout2 = QGridLayout() self.layout2.addWidget(self.tdcPlot2, 0, 0, 5, 5) self.layout2.addWidget(self.pairsRateLabel, 0, 5) self.tab2.setLayout(self.layout2) self.tabs.addTab(self.tab2, "Coincidences") self.tabs.currentChanged.connect(self.update_plot_tab) #---------Tabs---------# #Layout self.grid = QGridLayout() self.grid.setSpacing(20) self.grid.addWidget(self.deviceLabel, 0, 0) self.grid.addWidget(self.devCombobox, 0, 1) self.grid.addWidget(self.deviceModeLabel, 0, 2) self.grid.addWidget(self.modesCombobox, 0, 3, 1, 1) self.grid.addWidget(self.integrationLabel, 1, 0) self.grid.addWidget(self.integrationSpinBox, 1, 1) self.grid.addWidget(self.samplesLabel, 1, 2) self.grid.addWidget(self.samplesSpinbox, 1, 3, 1, 1) self.grid.addWidget(self.liveStart_Button, 2, 0) self.grid.addWidget(self.scanForDevice_Button, 2, 1) self.grid.addWidget(self.selectLogfile_Button, 2, 2) self.grid.addWidget(self.logfileLabel, 2, 3) self.grid.addWidget(self.tabs, 4, 0, 5, 4) self.singlesGroupbox = QGroupBox('Singles') self.singlesLayout = QHBoxLayout() self.singlesLayout.addWidget(self.radio1_Button) self.singlesLayout.addWidget(self.radio2_Button) self.singlesLayout.addWidget(self.radio3_Button) self.singlesLayout.addWidget(self.radio4_Button) self.singlesGroupbox.setLayout(self.singlesLayout) self.grid.addWidget(self.singlesGroupbox, 3, 0, 1, 2) self.pairsGroupbox = QGroupBox('Pairs') self.pairsSpinLayout = QHBoxLayout() self.pairsSpinLayout.addWidget(self.startChannelLabel) self.pairsSpinLayout.addWidget(self.channelsCombobox1) self.pairsSpinLayout.addWidget(self.stopChannelLabel) self.pairsSpinLayout.addWidget(self.channelsCombobox2) #self.pairsLabelLayout = QHBoxLayout() self.pairsCenterLayout = QHBoxLayout() self.pairsCenterLayout.addWidget(self.centerLabel) self.pairsCenterLayout.addWidget(self.centerSpinbox) self.pairsCenterLayout.addWidget(self.resolutionTextLabel) self.pairsCenterLayout.addWidget(self.resolutionSpinbox) self.pairsLayout = QVBoxLayout() #self.pairsLayout.addLayout(self.pairsLabelLayout) self.pairsLayout.addLayout(self.pairsSpinLayout) self.pairsLayout.addLayout(self.pairsCenterLayout) self.pairsGroupbox.setLayout(self.pairsLayout) self.grid.addWidget(self.pairsGroupbox, 3, 2, 1, 2) #Main Widget (on which the grid is to be implanted) self.mainwidget = QWidget() self.mainwidget.layout = self.grid self.mainwidget.setLayout(self.mainwidget.layout) self.mainwidget.setFont(defaultFont) self.setCentralWidget(self.mainwidget) # Connected to devComboBox.currentTextChanged @QtCore.pyqtSlot(str) def selectDevice(self, devPath: str): # Only allow resetting + changing device if not currently collecting data # Add msg box to allow user confirmation if self.acq_flag == False: self.StrongResetInternalVariables() self.resetDataAndPlots() self.resetGUIelements() if devPath != 'Select your device': self._tdc1_dev = tdc1.TimeStampTDC1(devPath) self.device_path = devPath @QtCore.pyqtSlot() def updateDevList(self): self.devCombobox.clear() self.devCombobox.addItem('Select your device') devices = serial_connection.search_for_serial_devices( tdc1.TimeStampTDC1.DEVICE_IDENTIFIER) self.devCombobox.addItems(devices) # Connected to modesCombobox.currentTextChanged @QtCore.pyqtSlot(str) def selectDeviceMode(self, newMode: str): # Only allow resetting + device mode change if not currently collecting data # Add msg box to allow user confirmation if self.acq_flag == False: self.WeakResetInternalVariables() self.resetDataAndPlots() self.resetGUIelements() if newMode != 'Select mode': if self._tdc1_dev == None: self._tdc1_dev = tdc1.TimeStampTDC1(self.device_path) self._tdc1_dev.mode = newMode # Setting tdc1 mode with @setter self._dev_mode = newMode # Update plot index on plot tab change @QtCore.pyqtSlot() def update_plot_tab(self): self._plot_tab = self.tabs.currentIndex() # Update integration time on spinbox value change @QtCore.pyqtSlot(int) def update_intTime(self, int_time: int): # Convert to seconds self.integration_time = int_time * 1e-3 if self.logger: self.logger.int_time = int_time * 1e-3 @QtCore.pyqtSlot(int) def updatePlotSamples(self, samples: int): self.plotSamples = samples # Click Live Start button to get started! @QtCore.pyqtSlot() # Connected to self.liveStart_button.clicked def liveStart(self): #If currently live plotting if self.acq_flag is True and self.liveStart_Button.text( ) == "Live Stop": self.acq_flag = False self.logger.active_flag = False # To stop logger from looping self.selectLogfile_Button.setEnabled(True) self.liveStart_Button.setText("Live Start") time.sleep(1) # Pause to let all loops end self._tdc1_dev = None # Destroy tdc1_dev object self.logger = None # Destroy logger self.logger_thread = None # and thread...? #If not currently live plotting elif self.acq_flag is False and self.liveStart_Button.text( ) == "Live Start": if self._tdc1_dev == None: self._tdc1_dev = tdc1.TimeStampTDC1( self.devCombobox.currentText()) self.acq_flag = True self.resetDataAndPlots() self.selectLogfile_Button.setEnabled(False) self.modesCombobox.setEnabled(True) self.liveStart_Button.setText("Live Stop") self.startLogging() # Logging def startLogging(self): """[summary] Creation process of worker object and QThread. """ # Create worker instance and a thread self.logger = logWorker() self.logger_thread = QtCore.QThread( self) # QThread is not a thread, but a thread MANAGER # Assign worker to the thread and start the thread self.logger.moveToThread(self.logger_thread) self.logger_thread.start( ) # This is where the thread is actually created, I think # Connect signals and slots AFTER moving the object to the thread self.logging_requested.connect(self.logger.log_which_data) self.logger.data_is_logged.connect(self.update_data_from_thread) self.logger.histogram_logged.connect(self.updateHistogram) self.logger.int_time = int( self.integrationSpinBox.text()) * 1e-3 # Convert to seconds #self.log_flag = True self.logging_requested.emit(self._logfile_name, self.device_path, self.log_flag, self._dev_mode, \ self._tdc1_dev) @QtCore.pyqtSlot() def selectLogfile(self): if self.acq_flag == False: if self.selectLogfile_Button.text() == 'Select Logfile': default_filetype = 'csv' start = datetime.now().strftime( "%Y%m%d_%Hh%Mm%Ss ") + "_TDC1." + default_filetype self._logfile_name = QtGui.QFileDialog.getSaveFileName( self, "Save to log file", start)[0] self.logfileLabel.setText(self._logfile_name) if self._logfile_name != '': #self.startLogging_Button.setEnabled(True) self.log_flag = True self.selectLogfile_Button.setText('Unselect Logfile') elif self.selectLogfile_Button.text() == 'Unselect Logfile': self.logfileLabel.setText('') self.selectLogfile_Button.setText('Select Logfile') # Updating data # Connected to data_is_logged signal @QtCore.pyqtSlot(tuple, str, list) def update_data_from_thread(self, data: tuple, dev_mode: str, radio_flags: list): if len(self.x) == PLT_SAMPLES: self.x = self.x[1:] self.x.append(self.x[-1] + 1) self.y1 = self.y1[1:] self.y2 = self.y2[1:] self.y3 = self.y3[1:] self.y4 = self.y4[1:] else: self.x.append(len(self.x) + 1) # Takes care of empty list case as well self.y1.append(data[0]) self.y2.append(data[1]) self.y3.append(data[2]) self.y4.append(data[3]) self.idx = min(len(self.y1), self.plotSamples) self.y1new = self.y1[-self.idx:] self.y2new = self.y2[-self.idx:] self.y3new = self.y3[-self.idx:] self.y4new = self.y4[-self.idx:] self.y_data = [self.y1new, self.y2new, self.y3new, self.y4new] self._radio_flags = radio_flags self.Ch1CountsLabel.setText(str(data[0])) self.Ch2CountsLabel.setText(str(data[1])) self.Ch3CountsLabel.setText(str(data[2])) self.Ch4CountsLabel.setText(str(data[3])) self.updatePlots(self._radio_flags) # Updating plots 1-4 def updatePlots(self, radio_flags: list): for i in range(len(radio_flags)): if radio_flags[i] == 1: self.linePlots[i].setData(self.x[-self.idx:], self.y_data[i][-self.idx:]) # Radio button slots (functions) @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot1(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked() == True: # Possible to clear self.x and self.y1 without disrupting the worker loop? self.updatePlots(self._radio_flags) self.linePlot1.setPen(self.lineStyle1) self.logger.radio_flags[0] = 1 self._radio_flags[0] = 1 elif b.isChecked() == False: self.linePlot1.setPen(None) self.logger.radio_flags[0] = 0 self._radio_flags[0] = 0 @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot2(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked() == True: self.updatePlots(self._radio_flags) self.linePlot2.setPen(self.lineStyle2) self.logger.radio_flags[1] = 1 self._radio_flags[1] = 1 elif b.isChecked() == False: self.linePlot2.setPen(None) self.logger.radio_flags[1] = 0 self._radio_flags[1] = 0 @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot3(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked() == True: self.updatePlots(self._radio_flags) self.linePlot3.setPen(self.lineStyle3) self.logger.radio_flags[2] = 1 self._radio_flags[2] = 1 elif b.isChecked() == False: self.linePlot3.setPen(None) self.logger.radio_flags[2] = 0 self._radio_flags[2] = 0 @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot4(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked(): self.updatePlots(self._radio_flags) self.linePlot4.setPen(self.lineStyle4) self.logger.radio_flags[3] = 1 self._radio_flags[3] = 1 elif b.isChecked() == False: self.linePlot4.setPen(None) self.logger.radio_flags[3] = 0 self._radio_flags[3] = 0 @QtCore.pyqtSlot(str) def updateStart(self, channel: str): cs = int(channel) if self.acq_flag == True and self.modesCombobox.currentText( ) == "pairs": self._ch_start = cs if self.logger: self.logger.ch_start = cs @QtCore.pyqtSlot(str) def updateStop(self, channel: str): cs = int(channel) if self.acq_flag == True and self.modesCombobox.currentText( ) == "pairs": self._ch_stop = cs if self.logger: self.logger.ch_stop = cs # Histogram # Connected to histogram_logged signal def updateHistogram(self, g2_data: dict): # {int - ch_start counts, int- ch_stop counts, int - actual acq time, float - time bins, float - histogram values} # time bins and histogram vals are both np arrays incremental_y = g2_data['histogram'] incremental_y_int = incremental_y.astype(np.int32) self.y0 += incremental_y_int center = self.centerSpinbox.currentValue() idx = int(-(-center // self.binsize)) # Upside-down floor div aka ceiling div startidx = idx - idx_width stopidx = idx + idx_width try: self.x0new = self.x0[startidx:stopidx] self.y0new = self.y0[startidx:stopidx] # If startidx is negative except IndexError: try: self.x0new = self.x0[:stopidx] self.y0new = self.y0[:stopidx] except IndexError: try: self.x0new = self.x0[startidx:] self.y0new = self.y0[startidx:] except: pass self.histogramPlot.setData(self.x0new, self.y0new) totalpairs = np.sum(self.y0, dtype=np.int32) self.pairsRateLabel.setText("<font size=24>Pairs/sec: </font><br>" + "<font size=96>" + totalpairs + "</font>") # Reserve in case above func fails # def updateHistogram(self, g2_data: dict): # # {int - ch_start counts, int- ch_stop counts, int - actual acq time, float - time bins, float - histogram values} # # time bins and histogram vals are both np arrays # incremental_y = g2_data['histogram'] # incremental_y_int = incremental_y.astype(np.int32) # self.y0 += incremental_y_int # self.idx = min(len(self.y0), self.plotSamples) # self.x0new = self.x0[:(self.idx)] # self.y0new = self.y0[:(self.idx)] # self.histogramPlot.setData(self.x0new, self.y0new) # totalpairs = np.sum(self.y0, dtype=np.int32) # self.pairsRateLabel.setText("<font size=24>Pairs/sec: </font><br>" + "<font size=96>" + totalpairs + "</font>") @QtCore.pyqtSlot(int) def updateBins(self, bin_width): self.binsize = bin_width self.x0 = np.arange(0, self.bins * self.binsize, self.binsize) # For future use def StrongResetInternalVariables(self): self.integration_time = 1 self._logfile_name = '' # Track the logfile(csv) being used by GUI self.resetWorkerAndThread() self._tdc1_dev = None # tdc1 device object self._dev_mode = '' # 0 = 'singles', 1 = 'pairs', 3 = 'timestamp' self.device_path = '' # Device path, eg. 'COM4' def WeakResetInternalVariables(self): # Excludes resetting variables relating to the device object (device, mode, path) self.integration_time = 1 self._logfile_name = '' # Track the logfile(csv) being used by GUI self.resetWorkerAndThread() def resetWorkerAndThread(self): time.sleep(2) # To allow threads to end self.logger = None self.logger_thread = None # For future use def resetGUIelements(self): self.liveStart_Button.setEnabled(True) self.selectLogfile_Button.setEnabled(True) self.logfileLabel.setText('') self.radio1_Button.setChecked(False) self.radio2_Button.setChecked(False) self.radio3_Button.setChecked(False) self.radio4_Button.setChecked(False) self.integrationSpinBox.setValue(1000) self.samplesSpinbox.setValue(501) def resetDataAndPlots(self): self.x0 = np.arange(0, self.bins * self.binsize, self.binsize) self.y0 = np.zeros_like(self.x0) self.x0new = [] self.y0new = [] self.x = [] self.y1 = [] self.y2 = [] self.y3 = [] self.y4 = [] self.xnew = [] self.y1new = [] self.y2new = [] self.y3new = [] self.y4new = [] self.linePlot1.setData(self.x, self.y1) self.linePlot2.setData(self.x, self.y2) self.linePlot3.setData(self.x, self.y3) self.linePlot4.setData(self.x, self.y4) self.histogramPlot.setData(self.x0, self.y0) self.radio1_Button.setChecked(False) self.radio2_Button.setChecked(False) self.radio3_Button.setChecked(False) self.radio4_Button.setChecked(False) self._radio_flags = [0, 0, 0, 0]
class PreviewArea(QWidget): def __init__(self): super().__init__() self.bundle = None self.manual_change = True size_policy = QSizePolicy() size_policy.setHorizontalPolicy(QSizePolicy.Expanding) size_policy.setVerticalPolicy(QSizePolicy.Expanding) self.setSizePolicy(size_policy) layout = QVBoxLayout() layout.setContentsMargins(0, 4, 0, 0) this_row = QHBoxLayout() this_row.addSpacing(4) selection_label = QLabel() selection_label.setText("Dieses Bild: ") this_row.addWidget(selection_label) self.keep_button = QRadioButton() self.keep_button.setText("behalten") self.keep_button.setMaximumHeight(14) self.keep_button.toggled.connect(self.mark_bundle) this_row.addWidget(self.keep_button) self.discard_button = QRadioButton() self.discard_button.setText("löschen") self.discard_button.setMaximumHeight(14) this_row.addWidget(self.discard_button) this_row.addStretch(1) layout.addLayout(this_row) img_scroll_area = QScrollArea() img_scroll_area.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.img_widget = ImageWidget(None) img_scroll_area.setWidget(self.img_widget) layout.addWidget(img_scroll_area, stretch=1) layout.addStretch() self.setLayout(layout) def set_image(self, img_d): self.manual_change = False self.bundle = img_d self.bundle.data_changed.connect(self.bundle_changed) self.img_widget.set_img(img_d.get_image()) self.bundle_changed() self.update() self.manual_change = True def mark_bundle(self, keep=False): if self.manual_change: self.manual_change = False self.bundle.set_manual(keep) self.manual_change = True def bundle_changed(self): if self.bundle.keep is None: self.discard_button.setAutoExclusive(False) self.keep_button.setAutoExclusive(False) self.discard_button.setChecked(False) self.keep_button.setChecked(False) self.discard_button.setAutoExclusive(True) self.keep_button.setAutoExclusive(True) elif not self.bundle.keep: self.discard_button.setChecked(True) else: self.keep_button.setChecked(True)
def createCardSelector(self, card, parent): btn = QRadioButton(None, parent) btn.setChecked(False) btn.setAutoExclusive(True) return btn
def __init__(self, *args, **kwargs): super(CompanyTab, self).__init__(*args, **kwargs) self.setWidgetResizable(True) scroll_content = QWidget(self) main_layout = QVBoxLayout(scroll_content) scroll_content.setLayout(main_layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) ga = QGroupBox() ga.setTitle("Genetical Algorithm") main_layout.addWidget(ga) self.layout_ga = QVBoxLayout() ga_fl = QHBoxLayout() population_label = QLabel("Population: ") population_edit = QSpinBox() population_edit.setMaximum(999) population_edit.setMinimum(2) population_edit.setValue(100) ga_fl.addStretch(1) ga_fl.addWidget(population_label) ga_fl.addWidget(population_edit) ga_fl.addStretch(1) generations_label = QLabel("Generations: ") generations_edit = QSpinBox() generations_edit.setMaximum(999) generations_edit.setMinimum(10) generations_edit.setValue(100) ga_fl.addWidget(generations_label) ga_fl.addWidget(generations_edit) ga_fl.addStretch(1) self.layout_ga.addLayout(ga_fl) t = CollapsibleBox(title="Advanced Options") genetical_layout = QVBoxLayout() one = QHBoxLayout() parameters = QGroupBox() parameters_layout = QHBoxLayout() parameters.setTitle("Parameters") mu_label = QLabel("μ: ") mu_edit = QDoubleSpinBox() mu_edit.setMaximum(1) mu_edit.setValue(1) mu_edit.setSingleStep(0.05) parameters_layout.addStretch(1) parameters_layout.addWidget(mu_label) parameters_layout.addWidget(mu_edit) parameters_layout.addStretch(1) lambda_label = QLabel("λ: ") lambda_edit = QDoubleSpinBox() lambda_edit.setValue(3) parameters_layout.addWidget(lambda_label) parameters_layout.addWidget(lambda_edit) parameters_layout.addStretch(1) parameters.setLayout(parameters_layout) one.addWidget(parameters) crossover = QGroupBox() crossover_layout = QHBoxLayout() crossover.setTitle("Crossover") cx_eta_label = QLabel("Eta: ") cx_eta_label.setToolTip( "High eta will produce children resembling to their parents, while a small eta will produce solutions much more different." ) cx_eta_edit = QSpinBox() cx_eta_edit.setToolTip( "High eta will produce children resembling to their parents, while a small eta will produce solutions much more different." ) cx_eta_edit.setValue(5) crossover_layout.addStretch(1) crossover_layout.addWidget(cx_eta_label) crossover_layout.addWidget(cx_eta_edit) crossover_layout.addStretch(1) cx_pb_label = QLabel("Probability: ") cx_pb_edit = QDoubleSpinBox() cx_pb_edit.setValue(0.5) cx_pb_edit.setMaximum(1) cx_pb_edit.setSingleStep(0.05) crossover_layout.addWidget(cx_pb_label) crossover_layout.addWidget(cx_pb_edit) crossover_layout.addStretch(1) crossover.setLayout(crossover_layout) one.addWidget(crossover) genetical_layout.addLayout(one) two = QHBoxLayout() mutations = QGroupBox() mutations_layout = QHBoxLayout() mutations.setTitle("Mutations") mut_eta_label = QLabel("Eta: ") mut_eta_label.setToolTip( "High eta will produce children resembling to their parents, while a small eta will produce solutions much more different." ) mut_eta_edit = QSpinBox() mut_eta_edit.setToolTip( "High eta will produce children resembling to their parents, while a small eta will produce solutions much more different." ) mut_eta_edit.setValue(5) mutations_layout.addStretch(1) mutations_layout.addWidget(mut_eta_label) mutations_layout.addWidget(mut_eta_edit) mutations_layout.addStretch(1) mut_pb_label = QLabel("Probability: ") mut_pb_edit = QDoubleSpinBox() mut_pb_edit.setMaximum(1) mut_pb_edit.setSingleStep(0.05) mut_pb_edit.setValue(0.5) mutations_layout.addWidget(mut_pb_label) mutations_layout.addWidget(mut_pb_edit) mutations_layout.addStretch(1) mut_indpb_label = QLabel("Indp probability: ") mut_indpb_edit = QDoubleSpinBox() mut_indpb_edit.setValue(1.0) mut_indpb_edit.setMaximum(1) mut_indpb_edit.setSingleStep(0.05) mutations_layout.addWidget(mut_indpb_label) mutations_layout.addWidget(mut_indpb_edit) mutations_layout.addStretch(1) mutations.setLayout(mutations_layout) two.addWidget(mutations) genetical_layout.addLayout(two) t.setContentLayout(genetical_layout) self.layout_ga.addWidget(t) ga.setLayout(self.layout_ga) # similarity = QGroupBox() similarity.setTitle("Similarity") similarity_layout = QHBoxLayout() molecules = QVBoxLayout() label = QLabel("Molecule(s) for the comparison") ligand = QRadioButton("Ligand") ligand.setAutoExclusive(False) protein = QRadioButton("Protein") protein.setAutoExclusive(False) metal = QRadioButton("Metal") metal.setAutoExclusive(False) molecules.addWidget(label) molecules.addWidget(ligand) molecules.addWidget(protein) molecules.addWidget(metal) similarity_layout.addLayout(molecules) threshold_layout = QHBoxLayout() threshold_label = QLabel("Threshold: ") threshold = QDoubleSpinBox() threshold.setSingleStep(0.05) threshold_layout.addStretch(1) threshold_layout.addWidget(threshold_label) threshold_layout.addWidget(threshold) threshold_layout.addStretch(1) similarity_layout.addLayout(threshold_layout) similarity.setLayout(similarity_layout) main_layout.addWidget(similarity) # output = QGroupBox() output.setTitle("Output") output_layout = QVBoxLayout() name = QHBoxLayout() name_label = QPushButton("Save as...") self.name_edit = QLineEdit() self.name_edit.setText(os.getcwd()) name_label.clicked.connect(self.save_path) name.addWidget(name_label) name.addWidget(self.name_edit) # output_layout.addLayout(name) precision_label = QLabel("Precision: ") precision_edit = QSpinBox() precision_edit.setMinimum(-3) precision_edit.setMaximum(6) precision_edit.setValue(3) name.addWidget(precision_label) name.addWidget(precision_edit) output_layout.addLayout(name) one = QHBoxLayout() compress = QCheckBox("Compress") compress.setChecked(True) verbose = QCheckBox("Verbose") verbose.setChecked(True) history = QCheckBox("History") check = QCheckBox("Check every") self.check_edit = QSpinBox() self.check_edit.setValue(10) self.check_edit.setEnabled(False) check.clicked.connect(self.check_edit_connect) pareto = QCheckBox("Pareto") prompt = QCheckBox("Prompt of exception") one.addWidget(compress) one.addStretch(1) one.addWidget(verbose) one.addStretch(1) one.addWidget(history) one.addStretch(1) one.addWidget(check) one.addWidget(self.check_edit) one.addStretch(1) one.addWidget(pareto) one.addStretch(1) one.addWidget(prompt) output_layout.addLayout(one) output.setLayout(output_layout) main_layout.addWidget(output) # main_layout.addWidget(QFrame()) main_layout.addWidget(QFrame()) main_layout.addWidget(QFrame()) main_layout.addWidget(QFrame()) self.setWidget(scroll_content)
class StartRabbitMQUtil(QtWidgets.QMainWindow): def __init__(self, parser): super(StartRabbitMQUtil, self).__init__() self.setWindowTitle("RabbitMQ Utility Launcher") # Set the window title #------------------------------ #self.app = app self.parser = parser self.frame = None self.env = "" #------------------------------ self.qapp = QApplication([]) #V = self.qapp.desktop().screenGeometry() #h = V.height() - 600 #w = V.width() - 600 h = 200 w = 250 self.resize(w, h) self.widget = QWidget() self.rmqmw = None self.rmqMenu = None self.mainexit = False self.statusBar = QStatusBar() self.setStatusBar(self.statusBar) self.envBox = QGroupBox("Environment") hbox = QHBoxLayout() vbrb = QVBoxLayout() #hbox1 = QHBoxLayout() self.envDev = QRadioButton("Development") self.envQa = QRadioButton("Qa") self.envProd = QRadioButton("Production") self.envDev.toggled.connect(lambda:self.btnstate(self.envDev)) self.envQa.toggled.connect(lambda:self.btnstate(self.envQa)) self.envProd.toggled.connect(lambda:self.btnstate(self.envProd)) vbrb.addWidget(self.envDev) vbrb.addWidget(self.envQa) vbrb.addWidget(self.envProd) #hbox1.addLayout(vbrb) self.envBox.setLayout(vbrb) hbox.addWidget(self.envBox) hbox2 = QHBoxLayout() self.launchButton = QPushButton("Launch") self.launchButton.setEnabled(False) self.launchButton.clicked.connect(self.launch) self.closeButton = QPushButton("Reset") self.closeButton.setEnabled(False) self.closeButton.clicked.connect(self.closermq) exitButton = QPushButton("Exit") exitButton.clicked.connect(self.closewin) hbox2.addWidget(self.launchButton) hbox2.addWidget(self.closeButton) hbox2.addWidget(exitButton) vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addLayout(hbox2) self.widget.setLayout(vbox) self.setCentralWidget(self.widget) def btnstate(self,b): radioButton = b.text() if len(radioButton) != 0 and b.isChecked() == True: self.launchButton.setEnabled(True) if radioButton == "Development" and b.isChecked() == True: self.env = "dev" elif radioButton == "Qa" and b.isChecked() == True: self.env = "qa" else: self.env = "prod" def closermq(self): self.envDev.setEnabled(True) self.envQa.setEnabled(True) self.envProd.setEnabled(True) self.envDev.setAutoExclusive(False) self.envDev.setChecked(False) self.envDev.setAutoExclusive(True) self.envQa.setAutoExclusive(False) self.envQa.setChecked(False) self.envQa.setAutoExclusive(True) self.envProd.setAutoExclusive(False) self.envProd.setChecked(False) self.envProd.setAutoExclusive(True) if self.rmqmw is not None: self.rmqmw.close() if self.rmqMenu is not None: self.rmqMenu.close() self.closeButton.setEnabled(False) def launch(self): self.closeButton.setEnabled(True) iniFile = "./resources/properties/{env}/{env}_rabbitmq.ini".format(env=self.env) os.environ['RMQ_INI_FILE'] = iniFile self.parser.read(iniFile) self.rmqMenu = RMQUMenu(self.parser) self.rmqmw = windows.ModernWindow(window=self.rmqMenu, parser=self.parser) self.rmqMenu.setFrame(self.rmqmw) self.rmqMenu._mainExited.connect(self.mainexited) self.rmqmw.show() self.rmqMenu.show() self.envDev.setEnabled(False) self.envQa.setEnabled(False) self.envProd.setEnabled(False) self.launchButton.setEnabled(False) def mainexited(self, exited): self.mainexit = exited def setFrame(self, frame): self.frame = frame def closewin(self): if not self.mainexit: if self.rmqmw is not None: self.rmqmw.close() if self.rmqMenu is not None: self.rmqMenu.close() #self.close() #self.frame.close() self.qapp.quit() def closeEvent(self, event): if not self.mainexit: if self.rmqmw is not None: self.rmqmw.close() if self.rmqMenu is not None: self.rmqMenu.close() #self.close() #self.frame.close() self.qapp.quit()
class FrontEndRunner(QWidget): """Main FrontEnd Runner window for creating workspace and starting SuRVoS2.""" def __init__(self, run_config, workspace_config, pipeline_config, *args, **kwargs): super().__init__() self.run_config = run_config self.workspace_config = workspace_config self.pipeline_config = pipeline_config self.server_process = None self.client_process = None pipeline_config_ptree = self.init_ptree(self.pipeline_config, name="Pipeline") self.layout = QVBoxLayout() tabwidget = QTabWidget() tab1 = QWidget() tab2 = QWidget() tabwidget.addTab(tab1, "Setup and Start Survos") tabwidget.addTab(tab2, "Pipeline") self.create_workspace_button = QPushButton("Create workspace") tab1.layout = QVBoxLayout() tab1.setLayout(tab1.layout) chroot_fields = self.get_chroot_fields() tab1.layout.addWidget(chroot_fields) self.setup_adv_run_fields() self.adv_run_fields.hide() run_fields = self.get_run_fields() tab1.layout.addWidget(run_fields) output_config_button = QPushButton("Save config") tab2.layout = QVBoxLayout() tab2.setLayout(tab2.layout) tab2.layout.addWidget(pipeline_config_ptree) tab2.layout.addWidget(output_config_button) self.create_workspace_button.clicked.connect(self.create_workspace_clicked) output_config_button.clicked.connect(self.output_config_clicked) self.layout.addWidget(tabwidget) self.setGeometry(300, 300, 600, 400) self.setWindowTitle("SuRVoS Settings Editor") current_fpth = os.path.dirname(os.path.abspath(__file__)) self.setWindowIcon(QIcon(os.path.join(current_fpth, "resources", "logo.png"))) self.setLayout(self.layout) self.show() def get_workspace_fields(self): """Gets the QGroupBox that contains all the fields for setting up the workspace. Returns: PyQt5.QWidgets.GroupBox: GroupBox with workspace fields. """ select_data_button = QPushButton("Select") workspace_fields = QGroupBox("Create Workspace:") wf_layout = QGridLayout() wf_layout.addWidget(QLabel("Data File Path:"), 0, 0) current_data_path = Path( self.workspace_config["datasets_dir"], self.workspace_config["vol_fname"] ) self.data_filepth_linedt = QLineEdit(str(current_data_path)) wf_layout.addWidget(self.data_filepth_linedt, 1, 0, 1, 2) wf_layout.addWidget(select_data_button, 1, 2) wf_layout.addWidget(QLabel("HDF5 Internal Data Path:"), 2, 0, 1, 1) ws_dataset_name = self.workspace_config["dataset_name"] internal_h5_path = ( ws_dataset_name if str(ws_dataset_name).startswith("/") else "/" + ws_dataset_name ) self.h5_intpth_linedt = QLineEdit(internal_h5_path) wf_layout.addWidget(self.h5_intpth_linedt, 2, 1, 1, 1) wf_layout.addWidget(QLabel("Workspace Name:"), 3, 0) self.ws_name_linedt_1 = QLineEdit(self.workspace_config["workspace_name"]) wf_layout.addWidget(self.ws_name_linedt_1, 3, 1) wf_layout.addWidget(QLabel("Downsample Factor:"), 4, 0) self.downsample_spinner = QSpinBox() self.downsample_spinner.setRange(1, 10) self.downsample_spinner.setSpecialValueText("None") self.downsample_spinner.setMaximumWidth(60) self.downsample_spinner.setValue(int(self.workspace_config["downsample_by"])) wf_layout.addWidget(self.downsample_spinner, 4, 1, 1, 1) # ROI self.setup_roi_fields() wf_layout.addWidget(self.roi_fields, 4, 2, 1, 2) self.roi_fields.hide() wf_layout.addWidget(self.create_workspace_button, 5, 0, 1, 3) workspace_fields.setLayout(wf_layout) select_data_button.clicked.connect(self.launch_data_loader) return workspace_fields def setup_roi_fields(self): """Sets up the QGroupBox that displays the ROI dimensions, if selected.""" self.roi_fields = QGroupBox("ROI:") roi_fields_layout = QHBoxLayout() # z roi_fields_layout.addWidget(QLabel("z:"), 0) self.zstart_roi_val = QLabel("0") roi_fields_layout.addWidget(self.zstart_roi_val, 1) roi_fields_layout.addWidget(QLabel("-"), 2) self.zend_roi_val = QLabel("0") roi_fields_layout.addWidget(self.zend_roi_val, 3) # y roi_fields_layout.addWidget(QLabel("y:"), 4) self.ystart_roi_val = QLabel("0") roi_fields_layout.addWidget(self.ystart_roi_val, 5) roi_fields_layout.addWidget(QLabel("-"), 6) self.yend_roi_val = QLabel("0") roi_fields_layout.addWidget(self.yend_roi_val, 7) # x roi_fields_layout.addWidget(QLabel("x:"), 8) self.xstart_roi_val = QLabel("0") roi_fields_layout.addWidget(self.xstart_roi_val, 9) roi_fields_layout.addWidget(QLabel("-"), 10) self.xend_roi_val = QLabel("0") roi_fields_layout.addWidget(self.xend_roi_val, 11) self.roi_fields.setLayout(roi_fields_layout) def setup_adv_run_fields(self): """Sets up the QGroupBox that displays the advanced optiona for starting SuRVoS2.""" self.adv_run_fields = QGroupBox("Advanced Run Settings:") adv_run_layout = QGridLayout() adv_run_layout.addWidget(QLabel("Server IP Address:"), 0, 0) self.server_ip_linedt = QLineEdit(self.run_config["server_ip"]) adv_run_layout.addWidget(self.server_ip_linedt, 0, 1) adv_run_layout.addWidget(QLabel("Server Port:"), 1, 0) self.server_port_linedt = QLineEdit(self.run_config["server_port"]) adv_run_layout.addWidget(self.server_port_linedt, 1, 1) # SSH Info self.ssh_button = QRadioButton("Use SSH") self.ssh_button.setAutoExclusive(False) adv_run_layout.addWidget(self.ssh_button, 0, 2) ssh_flag = self.run_config.get("use_ssh", False) if ssh_flag: self.ssh_button.setChecked(True) self.ssh_button.toggled.connect(self.toggle_ssh) self.adv_ssh_fields = QGroupBox("SSH Settings:") adv_ssh_layout = QGridLayout() adv_ssh_layout.setColumnStretch(2, 2) ssh_host_label = QLabel("Host") self.ssh_host_linedt = QLineEdit(self.run_config.get("ssh_host", "")) adv_ssh_layout.addWidget(ssh_host_label, 0, 0) adv_ssh_layout.addWidget(self.ssh_host_linedt, 0, 1, 1, 2) ssh_user_label = QLabel("Username") self.ssh_username_linedt = QLineEdit(self.get_login_username()) adv_ssh_layout.addWidget(ssh_user_label, 1, 0) adv_ssh_layout.addWidget(self.ssh_username_linedt, 1, 1, 1, 2) ssh_port_label = QLabel("Port") self.ssh_port_linedt = QLineEdit(self.run_config.get("ssh_port", "")) adv_ssh_layout.addWidget(ssh_port_label, 2, 0) adv_ssh_layout.addWidget(self.ssh_port_linedt, 2, 1, 1, 2) self.adv_ssh_fields.setLayout(adv_ssh_layout) adv_run_layout.addWidget(self.adv_ssh_fields, 1, 2, 2, 5) self.adv_run_fields.setLayout(adv_run_layout) def get_run_fields(self): """Gets the QGroupBox that contains the fields for starting SuRVoS. Returns: PyQt5.QWidgets.GroupBox: GroupBox with run fields. """ self.run_button = QPushButton("Run SuRVoS") advanced_button = QRadioButton("Advanced") run_fields = QGroupBox("Run SuRVoS:") run_layout = QGridLayout() run_layout.addWidget(QLabel("Workspace Name:"), 0, 0) workspaces = os.listdir(CHROOT) self.workspaces_list = ComboBox() for s in workspaces: self.workspaces_list.addItem(key=s) run_layout.addWidget(self.workspaces_list, 0, 0) self.ws_name_linedt_2 = QLineEdit(self.workspace_config["workspace_name"]) self.ws_name_linedt_2.setAlignment(Qt.AlignLeft) run_layout.addWidget(self.ws_name_linedt_2, 0, 2) run_layout.addWidget(advanced_button, 1, 0) run_layout.addWidget(self.adv_run_fields, 2, 1) run_layout.addWidget(self.run_button, 3, 0, 1, 3) run_fields.setLayout(run_layout) advanced_button.toggled.connect(self.toggle_advanced) self.run_button.clicked.connect(self.run_clicked) return run_fields def get_login_username(self): try: user = getpass.getuser() except Exception: user = "" return user @pyqtSlot() def launch_data_loader(self): """Load the dialog box widget to select data with data preview window and ROI selection.""" path = None int_h5_pth = None dialog = LoadDataDialog(self) result = dialog.exec_() self.roi_limits = None if result == QDialog.Accepted: path = dialog.winput.path.text() int_h5_pth = dialog.int_h5_pth.text() down_factor = dialog.downsample_spinner.value() if path and int_h5_pth: self.data_filepth_linedt.setText(path) self.h5_intpth_linedt.setText(int_h5_pth) self.downsample_spinner.setValue(down_factor) if dialog.roi_changed: self.roi_limits = tuple(map(str, dialog.get_roi_limits())) self.roi_fields.show() self.update_roi_fields_from_dialog() else: self.roi_fields.hide() def update_roi_fields_from_dialog(self): """Updates the ROI fields in the main window.""" x_start, x_end, y_start, y_end, z_start, z_end = self.roi_limits self.xstart_roi_val.setText(x_start) self.xend_roi_val.setText(x_end) self.ystart_roi_val.setText(y_start) self.yend_roi_val.setText(y_end) self.zstart_roi_val.setText(z_start) self.zend_roi_val.setText(z_end) @pyqtSlot() def toggle_advanced(self): """Controls displaying/hiding the advanced run fields on radio button toggle.""" rbutton = self.sender() if rbutton.isChecked(): self.adv_run_fields.show() else: self.adv_run_fields.hide() @pyqtSlot() def toggle_ssh(self): """Controls displaying/hiding the SSH fields on radio button toggle.""" rbutton = self.sender() if rbutton.isChecked(): self.adv_ssh_fields.show() else: self.adv_ssh_fields.hide() def setup_ptree_params(self, p, config_dict): def parameter_tree_change(param, changes): for param, change, data in changes: path = p.childPath(param) if path is not None: childName = ".".join(path) else: childName = param.name() sibs = param.parent().children() config_dict[path[-1]] = data p.sigTreeStateChanged.connect(parameter_tree_change) def valueChanging(param, value): pass for child in p.children(): child.sigValueChanging.connect(valueChanging) for ch2 in child.children(): ch2.sigValueChanging.connect(valueChanging) return p def dict_to_params(self, param_dict, name="Group"): ptree_param_dicts = [] ctr = 0 for key in param_dict.keys(): entry = param_dict[key] if type(entry) == str: d = {"name": key, "type": "str", "value": entry} elif type(entry) == int: d = {"name": key, "type": "int", "value": entry} elif type(entry) == list: d = {"name": key, "type": "list", "values": entry} elif type(entry) == float: d = {"name": key, "type": "float", "value": entry} elif type(entry) == bool: d = {"name": key, "type": "bool", "value": entry} elif type(entry) == dict: d = self.dict_to_params(entry, name="Subgroup")[0] d["name"] = key + "_" + str(ctr) ctr += 1 else: print(f"Can't handle type {type(entry)}") ptree_param_dicts.append(d) ptree_init = [{"name": name, "type": "group", "children": ptree_param_dicts}] return ptree_init def init_ptree(self, config_dict, name="Group"): ptree_init = self.dict_to_params(config_dict, name) parameters = Parameter.create( name="ptree_init", type="group", children=ptree_init ) params = self.setup_ptree_params(parameters, config_dict) ptree = ParameterTree() ptree.setParameters(params, showTop=False) return ptree @pyqtSlot() def create_workspace_clicked(self): """Performs checks and coordinates workspace creation on button press.""" logger.debug("Creating workspace: ") # Set the path to the data file vol_path = Path(self.data_filepth_linedt.text()) if not vol_path.is_file(): err_str = f"No data file exists at {vol_path}!" logger.error(err_str) self.button_feedback_response( err_str, self.create_workspace_button, "maroon" ) else: self.workspace_config["datasets_dir"] = str(vol_path.parent) self.workspace_config["vol_fname"] = str(vol_path.name) dataset_name = self.h5_intpth_linedt.text() self.workspace_config["dataset_name"] = str(dataset_name).strip("/") # Set the workspace name ws_name = self.ws_name_linedt_1.text() self.workspace_config["workspace_name"] = ws_name # Set the downsample factor ds_factor = self.downsample_spinner.value() self.workspace_config["downsample_by"] = ds_factor # Set the ROI limits if they exist if self.roi_limits: self.workspace_config["roi_limits"] = self.roi_limits try: response = init_ws(self.workspace_config) _, error = response if not error: self.button_feedback_response( "Workspace created sucessfully", self.create_workspace_button, "green", ) # Update the workspace name in the 'Run' section self.ws_name_linedt_2.setText(self.ws_name_linedt_1.text()) except WorkspaceException as e: logger.exception(e) self.button_feedback_response( str(e), self.create_workspace_button, "maroon" ) def button_feedback_response(self, message, button, colour_str, timeout=2): """Changes button colour and displays feedback message for a limited time period. Args: message (str): Message to display in button. button (PyQt5.QWidgets.QBushButton): The button to manipulate. colour_str (str): The standard CSS colour string or hex code describing the colour to change the button to. """ timeout *= 1000 msg_old = button.text() col_old = button.palette().button().color txt_col_old = button.palette().buttonText().color button.setText(message) button.setStyleSheet(f"background-color: {colour_str}; color: white") timer = QTimer() timer.singleShot( timeout, lambda: self.reset_button(button, msg_old, col_old, txt_col_old) ) @pyqtSlot() def reset_button(self, button, msg_old, col_old, txt_col_old): """Sets a button back to its original display settings. Args: button (PyQt5.QWidgets.QBushButton): The button to manipulate. msg_old (str): Message to display in button. col_old (str): The standard CSS colour string or hex code describing the colour to change the button to. txt_col_old (str): The standard CSS colour string or hex code describing the colour to change the button text to. """ button.setStyleSheet(f"background-color: {col_old().name()}") button.setStyleSheet(f"color: {txt_col_old().name()}") button.setText(msg_old) button.update() @pyqtSlot() def output_config_clicked(self): """Outputs pipeline config YAML file on button click.""" out_fname = "pipeline_cfg.yml" logger.debug(f"Outputting pipeline config: {out_fname}") with open(out_fname, "w") as outfile: yaml.dump( self.pipeline_config, outfile, default_flow_style=False, sort_keys=False ) def get_ssh_params(self): ssh_host = self.ssh_host_linedt.text() ssh_user = self.ssh_username_linedt.text() ssh_port = int(self.ssh_port_linedt.text()) return ssh_host, ssh_user, ssh_port def start_server_over_ssh(self): params = self.get_ssh_params() if not all(params): logger.error("Not all SSH parameters given! Not connecting to SSH.") ssh_host, ssh_user, ssh_port = params # Pop up dialog to ask for password text, ok = QInputDialog.getText( None, "Login", f"Password for {ssh_user}@{ssh_host}", QLineEdit.Password ) if ok and text: self.ssh_worker = SSHWorker(params, text, self.run_config) self.ssh_thread = QThread(self) self.ssh_worker.moveToThread(self.ssh_thread) self.ssh_worker.button_message_signal.connect(self.send_msg_to_run_button) self.ssh_worker.error_signal.connect(self.on_ssh_error) self.ssh_worker.finished.connect(self.start_client) self.ssh_worker.update_ip_linedt_signal.connect(self.update_ip_linedt) self.ssh_thread.started.connect(self.ssh_worker.start_server_over_ssh) self.ssh_thread.start() def closeEvent(self, event): reply = QMessageBox.question( self, "Quit", "Are you sure you want to quit? " "The server will be stopped.", QMessageBox.Yes | QMessageBox.No, QMessageBox.No, ) if reply == QMessageBox.Yes: event.accept() else: event.ignore() @pyqtSlot() def on_ssh_error(self): self.ssh_error = True @pyqtSlot(str) def update_ip_linedt(self, ip): self.server_ip_linedt.setText(ip) @pyqtSlot(list) def send_msg_to_run_button(self, param_list): self.button_feedback_response( param_list[0], self.run_button, param_list[1], param_list[2] ) @pyqtSlot() def run_clicked(self): """Starts SuRVoS2 server and client as subprocesses when 'Run' button pressed. Raises: Exception: If survos.py not found. """ self.ssh_error = ( False # Flag which will be set to True if there is an SSH error ) command_dir = os.getcwd() self.script_fullname = os.path.join(command_dir, "survos.py") if not os.path.isfile(self.script_fullname): raise Exception("{}: Script not found".format(self.script_fullname)) # Retrieve the parameters from the fields TODO: Put some error checking in self.run_config["workspace_name"] = self.ws_name_linedt_2.text() self.run_config["server_port"] = self.server_port_linedt.text() # Temporary measure to check whether the workspace exists or not full_ws_path = os.path.join( Config["model.chroot"], self.run_config["workspace_name"] ) if not os.path.isdir(full_ws_path): logger.error( f"No workspace can be found at {full_ws_path}, Not starting SuRVoS." ) self.button_feedback_response( f"Workspace {self.run_config['workspace_name']} does not appear to exist!", self.run_button, "maroon", ) return # Try some fancy SSH stuff here if self.ssh_button.isChecked(): self.start_server_over_ssh() else: self.server_process = subprocess.Popen( [ "python", self.script_fullname, "start_server", self.run_config["workspace_name"], self.run_config["server_port"], ] ) try: outs, errs = self.server_process.communicate(timeout=10) print(f"OUTS: {outs, errs}") except subprocess.TimeoutExpired: pass self.start_client() def start_client(self): if not self.ssh_error: self.button_feedback_response( "Starting Client.", self.run_button, "green", 7 ) self.run_config["server_ip"] = self.server_ip_linedt.text() self.client_process = subprocess.Popen( [ "python", self.script_fullname, "nu_gui", self.run_config["workspace_name"], str(self.run_config["server_ip"]) + ":" + str(self.run_config["server_port"]), ] )