class Window(QMainWindow): def __init__(self): super(Window, self).__init__(flags=Qt.Window) self.index_page = 0 self.valid_players = player_store.players self.setWindowTitle("Business FUT") self.setWindowIcon(QIcon("img/fut_icon.ico")) self.tab_widgets = QTabWidget() self.tab_widgets.setStyleSheet(QTabWidget_stylesheet) self.tab_widgets.blockSignals(True) self.tab_widgets.currentChanged.connect(self.on_tab_changed) self.buy_tab = BuyTab() self.favorite_tab = FavoriteTab() self.sell_tab = SellTab() self.tab_widgets.addTab(self.buy_tab, "Achats") self.tab_widgets.addTab(self.favorite_tab, "Favoris") self.tab_widgets.addTab(self.sell_tab, "Ventes") self.setCentralWidget(self.tab_widgets) self.tab_widgets.blockSignals(False) def on_tab_changed(self): self.tab_widgets.currentWidget().update_table() def keyPressEvent(self, e): if type(e) == QKeyEvent and e.key() == Qt.Key_F1: c.ech += 1 if c.ech > 3: c.ech = 1 from model.database import Database db = Database() db.update_constant(c.ech) self.close()
class Ui_main_window(object): trigger = pyqtSignal() def __init__(self, window): super().__init__() self.config_setter = None self.main_window = window self.main_grid = QtWidgets.QWidget(self.main_window) self.process = QtCore.QProcess() self._pid = -1 self.process_finished = False self.std_err_file = "" self.std_out_file = "" self.new_process_starting_date = "" self.old_process_starting_date = "" self.timer = QTimer() self.resources_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "resources") self.log_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "log") self.statusbar = QtWidgets.QStatusBar(self.main_window) self.menubar = QtWidgets.QMenuBar(self.main_window) self.header_label = QtWidgets.QLabel(self.main_grid) self.line = QtWidgets.QFrame(self.main_grid) self.base_grid = QtWidgets.QGridLayout(self.main_grid) # File selection grid self.file_selector_grid = QtWidgets.QGridLayout() self.snake_label = QtWidgets.QLabel(self.main_grid) self.wd_label = QtWidgets.QLabel(self.main_grid) self.custom_snakefile_full_path = "" self.wd_folder_full_path = "" self.snake_button = QtWidgets.QPushButton(self.main_grid) self.snake_button.clicked.connect( lambda: self.set_snake_file(self.select_snakefile_label)) self.wd_button = QtWidgets.QPushButton(self.main_grid) self.wd_button.clicked.connect( lambda: self.set_wd(self.select_wdfolder_label)) self.select_snakefile_label = QtWidgets.QLabel(self.main_grid) self.select_wdfolder_label = QtWidgets.QLabel(self.main_grid) # Attribute grid self.attribute_settings_grid = QtWidgets.QGridLayout() self.set_configs_label = QtWidgets.QLabel(self.main_grid) self.set_configs_button = QtWidgets.QPushButton(self.main_grid) self.set_configs_button.clicked.connect(self.open_config_setter) self.config_setter_window = QtWidgets.QMainWindow() self.overwrite_label = QtWidgets.QLabel(self.main_grid) self.overwrite_cb = QtWidgets.QCheckBox(self.main_grid) self.overwrite_cb.stateChanged.connect(self.check_runnable) self.cores_label = QtWidgets.QLabel(self.main_grid) self.cores_line = QtWidgets.QLineEdit(self.main_grid) self.cores_line.setFixedWidth(120) self.cores_line.setText("2") self.cores_line.setValidator(QIntValidator()) self.asim_label = QtWidgets.QLabel(self.main_grid) self.asim_cb = QtWidgets.QCheckBox(self.main_grid) self.mt_label = QtWidgets.QLabel(self.main_grid) self.mt_list = QtWidgets.QListWidget(self.main_grid) self.mt_list.setFixedHeight(130) self.mt_list.setSelectionMode(QAbstractItemView.MultiSelection) self.ast_label = QtWidgets.QLabel(self.main_grid) self.ast_list = QtWidgets.QListWidget(self.main_grid) self.ast_list.setFixedHeight(130) self.ast_list.setSelectionMode(QAbstractItemView.MultiSelection) for tool in MAPPING_TOOLS: self.mt_list.addItem(tool) for tool in AS_TOOLS: self.ast_list.addItem(tool) self.current_run_label = QtWidgets.QLabel(self.main_grid) self.refresh_status_button = QtWidgets.QPushButton(self.main_grid) self.refresh_status_button.clicked.connect(self.check_running) self.cleanup_button = QtWidgets.QPushButton(self.main_grid) self.cleanup_button.clicked.connect(self.cleanup) self.snake_out_text_edit = QPlainTextEdit() self.error_text_edit = QPlainTextEdit() self.snake_out_text_edit.setReadOnly(True) self.error_text_edit.setReadOnly(True) self.tabs = QTabWidget() self.tabs.setMinimumHeight(200) self.tabs.blockSignals(True) self.tabs.currentChanged.connect(self.refresh_log_display) self.snakemake_out_tab = QWidget() self.error_out_tab = QWidget() self.tabs.addTab(self.snakemake_out_tab, "DICAST Output") self.tabs.addTab(self.error_out_tab, "Snakemake Output") # Bottom button grid self.horizontal_layout = QtWidgets.QHBoxLayout() self.okcancel_buttonbox = QtWidgets.QDialogButtonBox(self.main_grid) self.okcancel_buttonbox.accepted.connect(self.on_button_ok_clicked) def setupUi(self, window): window.setObjectName("main_window") size_policy_f_f = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) size_policy_f_f.setHorizontalStretch(0) size_policy_f_f.setVerticalStretch(0) size_policy_e_f = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) size_policy_e_p = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) size_policy_e_p.setHorizontalStretch(0) size_policy_e_p.setVerticalStretch(0) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(window.sizePolicy().hasHeightForWidth()) window.setSizePolicy(sizePolicy) icon = QtGui.QIcon() pix_map_path = os.path.join(self.resources_path, "Dicast.svg") icon.addPixmap(QtGui.QPixmap(pix_map_path), QtGui.QIcon.Normal, QtGui.QIcon.Off) window.setWindowIcon(icon) self.main_grid.setEnabled(True) self.main_grid.setObjectName("main_grid") self.base_grid.setObjectName("gridLayout_2") horizontal_spacer = QtWidgets.QSpacerItem( 20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.base_grid.addItem(horizontal_spacer, 6, 0, 1, 1) self.file_selector_grid.setSizeConstraint( QtWidgets.QLayout.SetDefaultConstraint) self.file_selector_grid.setObjectName("file_selector_grid") vertical_spacer = QtWidgets.QSpacerItem( 0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) size_policy_e_p.setHeightForWidth( self.wd_label.sizePolicy().hasHeightForWidth()) self.wd_label.setSizePolicy(size_policy_e_p) self.wd_label.setObjectName("wd_label") self.file_selector_grid.addWidget(self.wd_label, 0, 0, 1, 2, QtCore.Qt.AlignLeft) size_policy_f_f.setHeightForWidth( self.wd_button.sizePolicy().hasHeightForWidth()) self.wd_button.setSizePolicy(size_policy_f_f) self.wd_button.setObjectName("wd_button") self.file_selector_grid.addWidget( self.wd_button, 1, 0, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.file_selector_grid.addWidget( self.select_wdfolder_label, 1, 1, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignHCenter) self.file_selector_grid.addItem(vertical_spacer, 1, 3, 1, 1) size_policy_e_p.setHeightForWidth( self.snake_label.sizePolicy().hasHeightForWidth()) self.snake_label.setSizePolicy(size_policy_e_p) self.snake_label.setObjectName("snake_label") self.file_selector_grid.addWidget(self.snake_label, 0, 2, 1, 2, QtCore.Qt.AlignLeft) size_policy_f_f.setHeightForWidth( self.snake_button.sizePolicy().hasHeightForWidth()) self.snake_button.setSizePolicy(size_policy_f_f) self.snake_button.setObjectName("snake_button") self.snake_button.setToolTip( "If you have a customized Snakefile, you can select it here." "\nOr leave it blank to use the one provided by DICAST." "\nIf you do use your own, the configuration file created with this" "\ntool will be saved to the same directory as your Snakefile.") self.file_selector_grid.addWidget( self.snake_button, 1, 2, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignHCenter) self.file_selector_grid.addWidget( self.select_snakefile_label, 1, 3, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignHCenter) self.file_selector_grid.addItem(vertical_spacer, 1, 1, 1, 1) size_policy_e_p.setHeightForWidth( self.set_configs_label.sizePolicy().hasHeightForWidth()) self.set_configs_label.setSizePolicy(size_policy_e_p) self.set_configs_label.setObjectName("config_label") self.file_selector_grid.addWidget(self.set_configs_label, 0, 4, 1, 2, QtCore.Qt.AlignLeft) size_policy_f_f.setHeightForWidth( self.set_configs_button.sizePolicy().hasHeightForWidth()) self.set_configs_button.setSizePolicy(size_policy_f_f) self.set_configs_button.setObjectName("config_button") self.file_selector_grid.addWidget( self.set_configs_button, 1, 4, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.file_selector_grid.addItem(vertical_spacer, 1, 5, 1, 1) self.base_grid.addLayout(self.file_selector_grid, 2, 0, 1, 1) # Attributes # self.set_configs_label.setObjectName("set_configs_label") # self.attribute_settings_grid.addWidget(self.set_configs_label, 0, 0, 1, 1) # size_policy_f_f.setHeightForWidth(self.set_configs_button.sizePolicy().hasHeightForWidth()) # self.set_configs_button.setSizePolicy(size_policy_f_f) # self.set_configs_button.setObjectName("set_configs_button") # self.attribute_settings_grid.addWidget(self.set_configs_button, 0, 1, 1, 1) self.overwrite_label.setObjectName("overwrite_label") self.attribute_settings_grid.addWidget(self.overwrite_label, 0, 0, 1, 1) self.overwrite_cb.setObjectName("overwrite_cb") self.attribute_settings_grid.addWidget(self.overwrite_cb, 0, 1, 1, 1, QtCore.Qt.AlignLeft) self.cores_label.setObjectName("cores_label") self.attribute_settings_grid.addWidget(self.cores_label, 1, 0, 1, 1) size_policy_e_f.setHeightForWidth( self.cores_line.sizePolicy().hasHeightForWidth()) self.cores_line.setSizePolicy(size_policy_e_f) self.cores_line.setObjectName("cores_line") self.attribute_settings_grid.addWidget(self.cores_line, 1, 1, 1, 2) self.asim_label.setObjectName("asim_label") self.attribute_settings_grid.addWidget(self.asim_label, 2, 0, 1, 1) self.asim_cb.setObjectName("asim_cb") self.attribute_settings_grid.addWidget(self.asim_cb, 2, 1, 1, 1, QtCore.Qt.AlignLeft) self.attribute_settings_grid.setObjectName("attribute_settings_grid") self.mt_label.setObjectName("mt_label") self.attribute_settings_grid.addWidget(self.mt_label, 3, 0, 1, 1) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) self.mt_list.setMinimumHeight(60) self.mt_list.setSizePolicy(sizePolicy) self.mt_list.setObjectName("mt_list") self.attribute_settings_grid.addWidget(self.mt_list, 4, 0, 1, 1, QtCore.Qt.AlignLeft) self.ast_label.setObjectName("ast_label") self.attribute_settings_grid.addWidget(self.ast_label, 3, 1, 1, 1) self.ast_list.setMinimumHeight(60) self.ast_list.setSizePolicy(sizePolicy) self.ast_list.setObjectName("ast_list") self.attribute_settings_grid.addWidget(self.ast_list, 4, 1, 1, 1, QtCore.Qt.AlignLeft) self.current_run_label.setObjectName("current_run_label") self.current_run_label.setWordWrap(True) self.base_grid.addWidget(self.current_run_label, 5, 0, 1, 1) self.base_grid.addWidget(self.tabs, 6, 0, 1, 1) self.snake_out_text_edit.setObjectName("snake_out_text_edit") self.snakemake_out_tab.layout = QVBoxLayout() self.snakemake_out_tab.layout.addWidget(self.snake_out_text_edit, 0) self.snakemake_out_tab.setLayout(self.snakemake_out_tab.layout) self.error_text_edit.setObjectName("error_text_edit") self.error_out_tab.layout = QVBoxLayout() self.error_out_tab.layout.addWidget(self.error_text_edit, 0) self.error_out_tab.setLayout(self.error_out_tab.layout) self.horizontal_layout.setObjectName("horizontal_layout") size_policy_f_f.setHeightForWidth( self.refresh_status_button.sizePolicy().hasHeightForWidth()) self.refresh_status_button.setSizePolicy(size_policy_f_f) self.refresh_status_button.setObjectName("refresh_status_button") self.horizontal_layout.addWidget(self.refresh_status_button) size_policy_f_f.setHeightForWidth( self.cleanup_button.sizePolicy().hasHeightForWidth()) self.cleanup_button.setSizePolicy(size_policy_f_f) self.cleanup_button.setObjectName("refresh_status_button") self.cleanup_button.setToolTip( "Should a run fail or be aborted, it may be necessary to remove\n" "unfinished log files and the lock on the working directory.") self.horizontal_layout.addWidget(self.cleanup_button) self.horizontal_layout.addItem(vertical_spacer) self.base_grid.addLayout(self.horizontal_layout, 7, 0, 1, 1) self.okcancel_buttonbox.setStandardButtons( QtWidgets.QDialogButtonBox.Close | QtWidgets.QDialogButtonBox.Abort | QtWidgets.QDialogButtonBox.Ok) self.submit_button = self.okcancel_buttonbox.button( QtWidgets.QDialogButtonBox.Ok) self.abort_button = self.okcancel_buttonbox.button( QtWidgets.QDialogButtonBox.Abort) palette = self.abort_button.palette() palette.setColor(self.abort_button.backgroundRole(), QColor(252, 53, 53)) self.abort_button.setAutoFillBackground(True) self.abort_button.setPalette(palette) self.cancel_button = self.okcancel_buttonbox.button( QtWidgets.QDialogButtonBox.Close) self.abort_button.clicked.connect(self.on_button_abort_clicked) self.cancel_button.clicked.connect(self.main_window.close) self.okcancel_buttonbox.setObjectName("okcancel_buttonbox") self.base_grid.addWidget(self.okcancel_buttonbox, 8, 0, 1, 1) self.base_grid.addLayout(self.attribute_settings_grid, 3, 0, 1, 1) self.header_label.setObjectName("header_label") pix_map_path = os.path.join(self.resources_path, "Dicast-text.png") pixmap = QPixmap(pix_map_path) pixmap = pixmap.scaled(250, 250, QtCore.Qt.KeepAspectRatio) self.header_label.setPixmap(pixmap) self.base_grid.addWidget(self.header_label, 0, 0, 1, 1) self.line.setFrameShape(QtWidgets.QFrame.HLine) self.line.setFrameShadow(QtWidgets.QFrame.Sunken) self.line.setObjectName("line") self.base_grid.addWidget(self.line, 1, 0, 1, 1) window.setCentralWidget(self.main_grid) self.menubar.setGeometry(QtCore.QRect(0, 0, 692, 20)) self.menubar.setObjectName("menubar") window.setMenuBar(self.menubar) self.statusbar.setObjectName("statusbar") window.setStatusBar(self.statusbar) self.retranslateUi(window) QtCore.QMetaObject.connectSlotsByName(window) self.tabs.blockSignals(False) self.check_running() self.timer.timeout.connect(self.check_running) self.timer.setInterval(1000) def retranslateUi(self, window): _translate = QtCore.QCoreApplication.translate window.setWindowTitle(_translate("main_window", "DICAST")) self.wd_button.setText(_translate("main_window", "Browse")) self.snake_button.setText(_translate("main_window", "Browse")) self.snake_label.setText( _translate("main_window", "Select custom Snakefile")) self.select_snakefile_label.setText( _translate("main_window", "Select a file...")) self.select_wdfolder_label.setText( _translate("main_window", "Select a folder...")) self.wd_label.setText( _translate("main_window", "Select working directory")) self.mt_label.setText( _translate("main_window", "Which mapping tools: ")) self.set_configs_label.setText( _translate("main_window", "Customize configuration file")) self.set_configs_button.setText(_translate("main_window", "Customize")) self.overwrite_label.setText( _translate("main_window", "Possible overwrite acknowledgment")) self.overwrite_cb.setText(_translate("main_window", "Acknowledged")) self.cores_label.setText( _translate("main_window", "Number of cores available to Snakemake")) self.ast_label.setText(_translate("main_window", "Which AS tools: ")) self.asim_label.setText( _translate("main_window", "Do you want to run ASimulatoR?")) self.asim_cb.setText(_translate("main_window", "Yes")) self.refresh_status_button.setText( _translate("main_window", "Refresh status")) self.cleanup_button.setText(_translate("main_window", "Cleanup")) def set_wd(self, label): path = on_button_get_folder_path_clicked(label, "/") if path != "": self.wd_folder_full_path = path wd_button_palette = self.wd_button.style().standardPalette() if self.wd_folder_full_path != "": self.config_setter = ConfigSetter(self.config_setter_window, self.wd_folder_full_path) if not self.config_setter.parse_successful: self.wd_folder_full_path = "" self.select_wdfolder_label.setText("Select a folder...") self.config_setter = None wd_button_palette.setColor(self.wd_button.backgroundRole(), QColor(252, 53, 53)) else: self.config_setter.setup_ui(self.config_setter_window) self.wd_button.setPalette(wd_button_palette) def set_snake_file(self, label): if self.wd_folder_full_path != "": self.custom_snakefile_full_path = on_button_get_file_path_clicked( label, self.wd_folder_full_path) else: show_popup( "Error - Missing working directory", "Please set the working directory before selecting the snakefile." ) def on_button_ok_clicked(self): if self.check_runnable(True): self.submit() self.overwrite_cb.setChecked(False) self.check_running() def submit(self): self.start_process() self.process.started.connect( lambda: self.submit_button.setDisabled(True)) self.process.finished.connect( lambda: self.submit_button.setEnabled(True)) self.process.started.connect( lambda: self.cleanup_button.setDisabled(True)) self.process.finished.connect( lambda: self.cleanup_button.setEnabled(True)) def check_runnable(self, submit_state=False): overwrite_cb_palette = self.overwrite_cb.palette() if submit_state: if not self.overwrite_cb.isChecked(): overwrite_cb_palette.setColor( self.overwrite_cb.backgroundRole(), QColor(252, 128, 121)) self.overwrite_cb.setAutoFillBackground(True) else: overwrite_cb_palette = self.overwrite_cb.style( ).standardPalette() wd_button_palette = self.wd_button.palette() if self.wd_folder_full_path == "": wd_button_palette.setColor(self.wd_button.backgroundRole(), QColor(252, 53, 53)) else: wd_button_palette = self.wd_button.style().standardPalette() self.wd_button.setPalette(wd_button_palette) self.overwrite_cb.setPalette(overwrite_cb_palette) return self.overwrite_cb.isChecked() def check_running(self): def check_process(): log_file = os.path.join(self.log_path, "_pid.txt") try: with open(log_file, "r") as f: process_identification = f.read() _pid = int(process_identification.split("-")[0]) self.old_process_starting_date = process_identification.split( "-")[1] try: if _pid != "" and _pid != -1: self._pid = _pid process = psutil.Process(_pid) # Check if the process with the PID parsed from the _pid.txt file is actually the DICAST process by comparing it's creation time # with the datetime parsed from the _pid.txt file. If the difference is smaller than 5 seconds, we assume its the same process # 5 seconds because there can be a time delay between setting the datetime for the log files and the creation of the process old_datetime = datetime.strptime( self.old_process_starting_date, '%Y_%m_%d_%H_%M_%S') time_difference = abs( (old_datetime - datetime.fromtimestamp( process.create_time())).total_seconds()) if time_difference < 5: p_status = psutil.Process(_pid).status() self.submit_button.setEnabled(False) else: raise psutil.NoSuchProcess else: return "Current status: Empty _pid.txt" except psutil.NoSuchProcess: self.submit_button.setEnabled(True) if self.timer.isActive(): self.timer.stop() return "Current status: No instance running, previous instance finished. " \ "Tabs below will show log files from the previous run." else: if not self.timer.isActive(): self.timer.start() return f"Current status: Instance {_pid} has status: {p_status}" except FileNotFoundError: return f"Current status: Missing pid file. No instance running or wrong folder: {log_file}" self.current_run_label.setText(check_process()) self.run_all_refresh_log_display() def run_all_refresh_log_display(self): self.refresh_log_display(0) self.refresh_log_display(1) def build_snakemake_config(self): config_dict = { "Possible_overwrite_acknowledge": self.overwrite_cb.isChecked(), "ASimulatoR": self.asim_cb.isChecked(), "Mapping_tools": " ".join([item.text() for item in self.mt_list.selectedItems()]), "Alternative_splicing_detection_tools": " ".join([item.text() for item in self.ast_list.selectedItems()]) } if self.custom_snakefile_full_path != "": config_json_path = os.path.dirname(self.custom_snakefile_full_path) config_json_path = os.path.join(config_json_path, 'snakemake_config.json') else: config_json_path = os.path.join(self.wd_folder_full_path, 'scripts', 'snakemake', 'snakemake_config.json') if Path(config_json_path).is_file(): date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S') backup_config_file = os.path.join( os.path.dirname(config_json_path), f"backup_{date}_{os.path.basename(config_json_path)}") os.rename(config_json_path, backup_config_file) backup_path = os.path.join(os.path.dirname(config_json_path), 'backup') if not os.path.exists(backup_path): os.makedirs(backup_path) move(backup_config_file, backup_path) with open(config_json_path, 'w') as f: json.dump(config_dict, f) def create_command_line_args(self): if self.custom_snakefile_full_path != "": snakefile_path = self.custom_snakefile_full_path snakefile_config_path = os.path.join( os.path.dirname(self.custom_snakefile_full_path), 'snakemake_config.json') else: snakefile_path = os.path.join(self.wd_folder_full_path, 'scripts', 'snakemake', 'Snakefile') snakefile_config_path = os.path.join(self.wd_folder_full_path, 'scripts', 'snakemake', 'snakemake_config.json') if not Path(snakefile_path).is_file(): show_popup( "Error - File not found", f"Couldn't find Snakefile {snakefile_path}." f"\nPlease make sure you have selected the correct working directory and the file is in the correct folder." ) return False if not Path(snakefile_config_path).is_file(): show_popup( "Error - File not found", f"Couldn't find Snakefile config {snakefile_config_path}." f"\nSomething must have gone wrong in the creation of the config file. Please make sure the working directory is correct." f"\nIf you used a custom Snakefile, make sure you have write access to the folder." ) return False args = [ "-j", f"{self.cores_line.text()}", "-s", f"{snakefile_path}", "-d", f"{self.wd_folder_full_path}", "--configfile", f"{snakefile_config_path}" ] return args def start_process(self): self.build_snakemake_config() run_str = "snakemake" args = self.create_command_line_args() if not args: return self.process.setProgram(run_str) self.process.setArguments(args) if not os.path.isdir(self.log_path): os.makedirs(self.log_path) self.new_process_starting_date = datetime.today().strftime( '%Y_%m_%d_%H_%M_%S') self.std_err_file = os.path.join( self.log_path, f"snakemake_log_{self.new_process_starting_date}.txt") self.std_out_file = os.path.join( self.log_path, f"dicast_log_{self.new_process_starting_date}.txt") self.process.setStandardOutputFile(self.std_out_file) self.process.setStandardErrorFile(self.std_err_file) ok, pid = self.process.startDetached() if ok: self._pid = pid with open(os.path.join(self.log_path, "_pid.txt"), "w") as f: process_identification = f"{str(self._pid)}-{self.new_process_starting_date}" f.write(process_identification) else: show_popup( "Error - Failed to start.", "An error occurred and DICAST was unable to run the Snakemake pipeline.\n" "Make sure you have activated the correct conda environment and you can run snakemake.\n" "See the documentation for more information.") def on_button_abort_clicked(self): if not self.process_finished: self.stop_process() self.check_running() def process_finished(self): self.submit_button.setEnabled(True) self.process_finished = True def stop_process(self): if self._pid > 0: try: p = psutil.Process(self._pid) except psutil.NoSuchProcess: pass else: for child in p.children(recursive=True): child.kill() p.kill() self._pid = -1 def cleanup(self): if self.wd_folder_full_path == "": return locks_folder = os.path.join(self.wd_folder_full_path, ".snakemake", "locks") snake_output_folder = os.path.join(self.wd_folder_full_path, "output", "snakemake") if os.path.exists(locks_folder): rmtree(locks_folder) if os.path.exists(snake_output_folder): rmtree(snake_output_folder) def refresh_log_display(self, i): if i == 0: try: with open(self.std_out_file, 'r') as dicast_log_file: data = dicast_log_file.read() self.snake_out_text_edit.setPlainText(data) # Scroll to the bottom self.snake_out_text_edit.verticalScrollBar().setValue( self.snake_out_text_edit.verticalScrollBar().maximum()) except FileNotFoundError: try: log_file = os.path.join( self.log_path, "dicast_log_" + self.old_process_starting_date + ".txt") with open(log_file, 'r') as out_dicast_log_file: data = out_dicast_log_file.read() self.snake_out_text_edit.setPlainText(data) self.snake_out_text_edit.verticalScrollBar().setValue( self.snake_out_text_edit.verticalScrollBar( ).maximum()) except FileNotFoundError: self.snake_out_text_edit.setPlainText( "Couldn't find DICAST log file from previous run.") elif i == 1: try: with open(self.std_err_file, 'r') as out_snakemake_log_file: data = out_snakemake_log_file.read() self.error_text_edit.setPlainText(data) self.error_text_edit.verticalScrollBar().setValue( self.error_text_edit.verticalScrollBar().maximum()) except FileNotFoundError: try: snakemake_log_file = os.path.join( self.log_path, "snakemake_log_" + self.old_process_starting_date + ".txt") with open(snakemake_log_file, 'r') as out_snakemake_log_file: data = out_snakemake_log_file.read() self.error_text_edit.setPlainText(data) self.error_text_edit.verticalScrollBar().setValue( self.error_text_edit.verticalScrollBar().maximum()) except FileNotFoundError: self.error_text_edit.setPlainText( "Couldn't find Snakemake log file from previous run.") def open_config_setter(self): if self.config_setter is not None and self.config_setter.parse_successful: self.config_setter_window.show() else: show_popup( "Error - No config file found", "No config file found. Make sure the correct working directory is set." )
class MultiView(QWidget): LEFT=0.15 RIGHT=0.98 BOTTOM=0.15 TOP=0.98 WSPACE=0.25 HSPACE=0.35 DPI = 300 def __init__(self, pot=PlotOrganizationType.Vertical, nplots=1, ncols=1, parent=None): #QDialog.__init__(self) #super().__init__(self) #QWidget.__init__() super().__init__() self.parent_application = parent self.pot = pot self.nplots = nplots self.ncols = ncols self.setupUi() mpl.rcParams['savefig.dpi'] = self.DPI def setupUi(self): # Remove seaborn dependency dark_gray = ".15" light_gray = ".8" style_dict = { "figure.facecolor": "white", "text.color": dark_gray, "axes.labelcolor": dark_gray, "axes.facecolor": "white", "axes.edgecolor": dark_gray, "axes.linewidth": 1.25, "grid.color": light_gray, "legend.frameon": False, "legend.numpoints": 1, "legend.scatterpoints": 1, "xtick.direction": "out", "ytick.direction": "out", "xtick.color": dark_gray, "ytick.color": dark_gray, "xtick.major.size": 6, "ytick.major.size": 6, "xtick.minor.size": 3, "ytick.minor.size": 3, "axes.grid": False, "axes.axisbelow": True, "image.cmap": "rocket", "font.family": ["sans-serif"], "font.sans-serif": ["Arial", "DejaVu Sans", "Liberation Sans", "Bitstream Vera Sans", "sans-serif"], "grid.linestyle": "-", "lines.solid_capstyle": "round", } mpl.rcParams.update(style_dict) self.setObjectName("self") self.horizontalLayout = QHBoxLayout(self) self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setSpacing(0) self.horizontalLayout.setObjectName("horizontalLayout") self.plotselecttabWidget = QTabWidget(self) self.plotselecttabWidget.setMaximumSize(QSize(22, 1000)) self.plotselecttabWidget.setTabPosition(QTabWidget.West) self.plotselecttabWidget.setTabShape(QTabWidget.Triangular) self.plotselecttabWidget.setUsesScrollButtons(False) self.plotselecttabWidget.setDocumentMode(False) self.plotselecttabWidget.setTabsClosable(False) self.plotselecttabWidget.setObjectName("plotselecttabWidget") self.plotselecttabWidget.setStyleSheet("QTabBar::tab { color:black; height: 40px; }") # Create a tab for all plots if (self.nplots>1): self.tab = QWidget() self.tab.setMaximumSize(QSize(0, 0)) self.plotselecttabWidget.addTab(self.tab, "All") else: self.plotselecttabWidget.setVisible(False) # Create a tab for each plot for i in range(self.nplots): self.tab = QWidget() self.tab.setMaximumSize(QSize(0, 0)) self.plotselecttabWidget.addTab(self.tab, "%d"%(i+1)) self.horizontalLayout.addWidget(self.plotselecttabWidget) self.plotselecttabWidget.setCurrentIndex(0) self.plotcontainer = QVBoxLayout() self.plotcontainer.setObjectName("plotcontainer") self.horizontalLayout.addLayout(self.plotcontainer) # Create the multiplot figure gs = self.organizeplots(self.pot, self.nplots, self.ncols) self.axarr = [] self.figure = plt.figure() for i in range(self.nplots): self.axarr.append(self.figure.add_subplot(gs[i])) self.set_bbox() self.canvas = FigureCanvasQTAgg(self.figure) self.canvas.setFocusPolicy( Qt.ClickFocus ) self.canvas.setFocus() self.plotcontainer.addWidget(self.canvas) self.init_plot(0) connection_id = self.plotselecttabWidget.currentChanged.connect(self.handle_plottabChanged) axes = plt.gcf().axes for ax_i in axes: for side in ["top", "right"]: ax_i.spines[side].set_visible(False) ax_i.xaxis.tick_bottom() ax_i.yaxis.tick_left() self.hidden_tab = [] def set_bbox(self): self.bbox = [] x0min = y0min = 1e9 x1max = y1max = -1e9 for i in range(self.nplots): bboxnow = self.axarr[i].get_position() self.bbox.append(bboxnow) x0min = min(x0min, bboxnow.x0) y0min = min(y0min, bboxnow.y0) x1max = max(x1max, bboxnow.x1) y1max = max(y1max, bboxnow.y1) self.bboxmax = [x0min, y0min, x1max-x0min, y1max-y0min] def reorg_fig(self, nplots): """Reorganise the views to show nplots""" if CmdBase.mode == CmdMode.GUI: self.parent_application.sp_nviews.blockSignals(True) self.parent_application.sp_nviews.setValue(nplots) self.parent_application.sp_nviews.blockSignals(False) self.plotselecttabWidget.blockSignals(True) nplot_old = self.nplots self.nplots = nplots gs = self.organizeplots(self.pot, self.nplots, self.ncols) # hide tabs if only one figure self.plotselecttabWidget.setVisible(nplots > 1) for i in range(nplots): self.axarr[i].set_position(gs[i].get_position(self.figure)) self.axarr[i].set_subplotspec(gs[i]) for tab in self.hidden_tab: tab_id = tab[0] widget = tab[1] tab_text = tab[2] self.plotselecttabWidget.insertTab(tab_id, widget, tab_text) self.hidden_tab = [] for i in range(nplots, len(self.axarr)): self.axarr[i].set_visible(False) tab_id = i + 1 widget = self.plotselecttabWidget.widget(nplots + 1) tab_text = self.plotselecttabWidget.tabText(nplots + 1) self.hidden_tab.append([tab_id, widget, tab_text]) self.plotselecttabWidget.removeTab(nplots + 1) self.set_bbox() for i in range(self.nplots): self.axarr[i].set_position(self.bbox[i]) # add new axes to plt for i in range(nplot_old, self.nplots): try: plt.subplot(self.axarr[i]) except: pass # remove axes from plt for i in range(self.nplots, nplot_old): try: plt.delaxes(self.axarr[i]) except: pass for ds in self.parent_application.datasets.values(): ds.nplots = nplots self.parent_application.update_all_ds_plots() self.handle_plottabChanged(0) # switch to all plot tab self.plotselecttabWidget.blockSignals(False) def init_plot(self, index): if index == 0: #multiplots for i in range(self.nplots): self.axarr[i].set_position(self.bbox[i]) else: #single plot max-size tab_to_maxi = index - 1 for i in range(self.nplots): if i == tab_to_maxi: #hide other plots self.axarr[i].set_visible(True) self.axarr[i].set_position(self.bboxmax) else: self.axarr[i].set_visible(False) self.parent_application.current_viewtab = index self.canvas.draw() def handle_plottabChanged(self, index): self.parent_application.current_viewtab = index if index == 0: #multiplots view_name = self.parent_application.multiviews[0].name if CmdBase.mode == CmdMode.GUI: ind = self.parent_application.viewComboBox.findText(view_name, Qt.MatchExactly) self.parent_application.viewComboBox.blockSignals(True) self.parent_application.viewComboBox.setCurrentIndex(ind) #set the view combobox according to current view self.parent_application.viewComboBox.blockSignals(False) for i in range(self.nplots): self.axarr[i].set_position(self.bbox[i]) self.axarr[i].set_visible(True) try: plt.subplot(self.axarr[i]) except: pass else: #single plot max-size tab_to_maxi = index - 1 # in 0 1 2 view_name = self.parent_application.multiviews[tab_to_maxi].name if CmdBase.mode == CmdMode.GUI: ind = self.parent_application.viewComboBox.findText(view_name) self.parent_application.viewComboBox.blockSignals(True) self.parent_application.viewComboBox.setCurrentIndex(ind) #set the view combobox according to current view self.parent_application.viewComboBox.blockSignals(False) for i in range(self.nplots): if i == tab_to_maxi: #hide other plots self.axarr[i].set_visible(True) self.axarr[i].set_position(self.bboxmax) try: plt.subplot(self.axarr[i]) except: pass else: self.axarr[i].set_visible(False) try: plt.delaxes(self.axarr[i]) except: pass self.parent_application.update_datacursor_artists() self.canvas.draw() self.parent_application.set_view_tools(view_name) def organizeHorizontal(self, nplots): gs = gridspec.GridSpec(1, self.nplots,left=self.LEFT,right=self.RIGHT, bottom=self.BOTTOM,top=self.TOP, wspace=self.WSPACE,hspace=self.HSPACE) return gs def organizeVertical(self, nplots): gs = gridspec.GridSpec(self.nplots, 1,left=self.LEFT,right=self.RIGHT, bottom=self.BOTTOM,top=self.TOP, wspace=self.WSPACE,hspace=self.HSPACE) return gs def organizeOptimalRow(self, nplots, ncols): row = math.ceil(nplots / ncols) gstmp = gridspec.GridSpec(row, ncols,left=self.LEFT,right=self.RIGHT, bottom=self.BOTTOM,top=self.TOP, wspace=self.WSPACE,hspace=self.HSPACE) gs=[] # First row might be different gs.append(gstmp[0,0:row*ncols-nplots+1]) for j in range(row*ncols-nplots+1,ncols): gs.append(gstmp[0,j]) for i in range(1,row): for j in range(ncols): gs.append(gstmp[i,j]) return gs def organizeOptimalColumn(self, nplots, ncols): row = math.ceil(nplots/ncols) gstmp = gridspec.GridSpec(row, ncols,left=self.LEFT,right=self.RIGHT, bottom=self.BOTTOM,top=self.TOP, wspace=self.WSPACE,hspace=self.HSPACE) gs = [] # First column might be different gs.append(gstmp[0:row*ncols-nplots+1,0]) for j in range(row*ncols-nplots+1,row): gs.append(gstmp[j,0]) for i in range(1,ncols): for j in range(row): gs.append(gstmp[j, i]) return gs def organizeplots(self, organizationtype, nplots=1, ncols=1, gs=None): if organizationtype == PlotOrganizationType.Vertical: return self.organizeVertical(nplots) elif organizationtype == PlotOrganizationType.Horizontal: return self.organizeHorizontal(nplots) elif organizationtype == PlotOrganizationType.OptimalRow: return self.organizeOptimalRow(nplots, ncols) elif organizationtype == PlotOrganizationType.OptimalColumn: return self.organizeOptimalColumn(nplots, ncols) elif organizationtype == PlotOrganizationType.Specified: pass elif organizationtype == PlotOrganizationType.DefaultOrganization: pass
class CamTabsWidget(QWidget): frames_ready = pyqtSignal(QImage, QImage, QImage) def __init__(self, parent, path): super(QWidget, self).__init__(parent) self.__path = path self.__counter = 0 self.layout = QVBoxLayout(self) # initialize tab screen self.__tabs = QTabWidget() self.__tabs.blockSignals(True) # just for not showing the initial message self.__tabs.currentChanged.connect(self.on_change) self.__tab3D = QWidget() self.__tab2D = QWidget() # add tabs self.__tabs.addTab(self.__tab3D, "3D") self.__tabs.addTab(self.__tab2D, "2D") # create the content and layout of the tab with 3D cameras self.__set_tab3d_layout() # create the content and layout of the tab with 2D cameras self.__set_tab2d_layout() # set all objects that support the pipeline to create 3D images self.__set_3d_supporting_objects() # capture the cameras' frames to create a movie self.layout.addWidget(self.__tabs) self.__run_movie() self.__tabs.blockSignals(False) def __set_3d_supporting_objects(self): self.pd_collection = [] self.mapper_collection = [] self.actor_collection = [] self.np_array = [] self.cells_npy = [] self.timer_count = 0 self._n_coordinates = 0 self.align = rs.align(rs.stream.color) self.__iren = self.__vtkWidget.GetRenderWindow().GetInteractor() self.__iren.GetInteractorStyle().SetCurrentStyleToTrackballActor() self.__iren.GetInteractorStyle().SetCurrentStyleToTrackballCamera() self._timer = QTimer(self) self._timer.timeout.connect(self.timer_event) self.view_coordinates = [[0., .5, .5, 1.], [.5, .5, 1., 1.], [0., 0., .5, .5], [.5, 0., 1., .5]] def __set_tab3d_layout(self): frame = QFrame() # add a vtk-based window for interaction self.__vtkWidget = QVTKRenderWindowInteractor(frame) # add a label and a text box for input of the object id self.__lbl_3d_idx = QLabel('ID:', self) self.__txt_box_3d_idx = QLineEdit(self) # add a button to save 3D images self.__btn_save_3d_img = QPushButton('Save Image', self) self.__btn_save_3d_img.setToolTip('Save 3D image to the file.') self.__btn_save_3d_img.clicked.connect(self.save_3d_image) # set the layout of the tab which holds the 3D cameras self.__tab3D.setLayout(self.__create_3d_cams_grid_layout()) def __set_tab2d_layout(self): self.__lbl_2d_cam_1 = QLabel() self.__lbl_2d_cam_2 = QLabel() self.__lbl_2d_cam_3 = QLabel() self.__lbl_2d_cam_4 = QLabel() # add a label and a text box for input of the object id self.__lbl_2d_idx = QLabel('ID:', self) self.__txt_box_2d_idx = QLineEdit(self) # add a button to save 3D images self.__btn_save_2d_img = QPushButton('Save Image', self) self.__btn_save_2d_img.setToolTip('Save 2D image to the file.') self.__btn_save_2d_img.clicked.connect(self.save_2d_image) # set the layout of the tab which holds the 2D cameras self.__tab2D.setLayout(self.__create_2d_cams_grid_layout()) def get_txt_box_2d_idx(self): return self.__txt_box_2d_idx def get_txt_box_3d_idx(self): return self.__txt_box_3d_idx def __run_movie(self): cam_counter = 0 for pipe in pipelines: frame_set = pipe.wait_for_frames() # Wait for a coherent color frame # frames = None # real_sense_cam.get_pipeline().wait_for_frames() # Align the depth frame to color frame aligned_frames = self.align.process(frame_set) depth_frame = aligned_frames.get_depth_frame() color_frame = aligned_frames.get_color_frame() color_image = np.asanyarray(color_frame.get_data()) color_image = color_image.reshape((color_image.shape[0] * color_image.shape[1], 3)) # self._colors.SetNumberOfTuples(color_image.shape[0]) colors = vtk.vtkUnsignedCharArray() colors.SetNumberOfComponents(3) # colors.SetName("Colors") current_pd = vtk.vtkPolyData() self.pd_collection.append(current_pd) colors.SetArray(vtk_np.numpy_to_vtk(color_image), color_image.shape[0] * color_image.shape[1], 1) current_pd.GetPointData().SetScalars(colors) pc = rs.pointcloud() point_cloud = pc.calculate(depth_frame) pc.map_to(color_frame) v, t = point_cloud.get_vertices(), point_cloud.get_texture_coordinates() vertices = np.asanyarray(v).view(np.float32).reshape(-1, 3) # xyz self._n_coordinates = vertices.shape[0] points = vtk.vtkPoints() cells = vtk.vtkCellArray() points.SetData(vtk_np.numpy_to_vtk(vertices)) cells_npy = np.vstack([np.ones(self._n_coordinates, dtype=np.int64), np.arange(self._n_coordinates, dtype=np.int64)]).T.flatten() cells.SetCells(self._n_coordinates, vtk_np.numpy_to_vtkIdTypeArray(cells_npy)) self.pd_collection[cam_counter].SetPoints(points) self.pd_collection[cam_counter].SetVerts(cells) mapper = vtk.vtkPolyDataMapper() self.mapper_collection.append(mapper) self.mapper_collection[cam_counter].SetInputData(self.pd_collection[cam_counter]) transform = vtk.vtkTransform() transform.SetMatrix(flip_transform) actor = vtk.vtkActor() self.actor_collection.append(actor) self.actor_collection[cam_counter].SetMapper(self.mapper_collection[cam_counter]) self.actor_collection[cam_counter].GetProperty().SetRepresentationToPoints() self.actor_collection[cam_counter].SetUserTransform(transform) current_ren = vtk.vtkRenderer() current_ren.GetActiveCamera() # set viewports if the number of cams ara greater than one if len(pipelines) > 1: current_ren.SetViewport(self.view_coordinates[cam_counter]) current_ren.AddActor(self.actor_collection[cam_counter]) self.__vtkWidget.GetRenderWindow().AddRenderer(current_ren) cam_counter += 1 self.__iren.AddObserver('TimerEvent', self.update_poly_data) dt = 30 # ms self.__iren.CreateRepeatingTimer(dt) def update_poly_data(self, obj=None, event=None): cam_counter = 0 rgb_cam_images = [] for pipe in pipelines: frame_set = pipe.wait_for_frames() # Wait for a coherent color frame # frames = real_sense_cam.get_pipeline().wait_for_frames() # Align the depth frame to color frame aligned_frames = self.align.process(frame_set) depth_frame = aligned_frames.get_depth_frame() color_frame = aligned_frames.get_color_frame() pc = rs.pointcloud() point_cloud = pc.calculate(depth_frame) pc.map_to(color_frame) v, t = point_cloud.get_vertices(), point_cloud.get_texture_coordinates() vertices = np.asanyarray(v).view(np.float32).reshape(-1, 3) # xyz color_image = np.asanyarray(color_frame.get_data()) rgb_cam_images.append(np_color_img_to_q_image(color_image)) color_image = color_image.reshape((color_image.shape[0] * color_image.shape[1], 3)) colors = vtk.vtkUnsignedCharArray() colors.SetNumberOfComponents(3) colors.SetArray(vtk_np.numpy_to_vtk(color_image), color_image.shape[0] * color_image.shape[1], 1) self.pd_collection[cam_counter].GetPointData().SetScalars(colors) points = vtk.vtkPoints() cells = vtk.vtkCellArray() points.SetData(vtk_np.numpy_to_vtk(vertices)) cells_npy = np.vstack([np.ones(self._n_coordinates, dtype=np.int64), np.arange(self._n_coordinates, dtype=np.int64)]).T.flatten() cells.SetCells(self._n_coordinates, vtk_np.numpy_to_vtkIdTypeArray(cells_npy)) self.pd_collection[cam_counter].SetPoints(points) self.pd_collection[cam_counter].SetVerts(cells) self.pd_collection[cam_counter].Modified() cam_counter += 1 self.frames_ready.emit(rgb_cam_images[0], rgb_cam_images[1], rgb_cam_images[2]) self.__iren.GetRenderWindow().Render() # print(self.timer_count) self.timer_count += 1 def __create_2d_cams_grid_layout(self): layout = QGridLayout() layout_idx = QHBoxLayout() layout_idx.addWidget(self.__lbl_2d_idx) layout_idx.addWidget(self.__txt_box_2d_idx) # add all widgets to the layout # add four labels of images, one for each camera layout.addWidget(self.__lbl_2d_cam_1, 0, 0, 1, 1) layout.addWidget(self.__lbl_2d_cam_2, 0, 1, 1, 1) layout.addWidget(self.__lbl_2d_cam_3, 1, 0, 1, 1) layout.addWidget(self.__lbl_2d_cam_4, 1, 1, 1, 1) # add a label and a text box to input the object id # layout.addWidget(self.__lbl_2d_idx, 2, 0, 1, 1) # layout.addWidget(self.__txt_box_2d_idx, 2, 1, 1, 1) layout.addLayout(layout_idx, 2, 0, 1, 2) # add a save button layout.addWidget(self.__btn_save_2d_img, 3, 0, 1, 2) # could we create a shared button to save all images at once? return layout def __create_3d_cams_grid_layout(self): layout = QGridLayout() layout.addWidget(self.__vtkWidget, 0, 0, 1, 2) # add a label and a text box to input the object id layout.addWidget(self.__lbl_3d_idx, 1, 0, 1, 1) layout.addWidget(self.__txt_box_3d_idx, 1, 1, 1, 1) # add a save button layout.addWidget(self.__btn_save_3d_img, 2, 0, 1, 2) return layout def timer_event(self): self.__iren.TimerEvent() def create_timer(self, obj, evt): self._timer.start(0) def destroy_timer(self, obj, evt): self._timer.stop() return 1 def get_iren(self): return self.__iren @QtCore.pyqtSlot(QImage, QImage, QImage) def receive_frame(self, rgb_1, rgb_2, rgb_3): ratio = 1. p_cam1 = QPixmap.fromImage(rgb_1) p_cam2 = QPixmap.fromImage(rgb_2) p_cam3 = QPixmap.fromImage(rgb_3) # p_cam4 = QPixmap.fromImage(rgb_4) w_cam1, h_cam1 = p_cam1.width() * ratio, p_cam1.height() * ratio w_cam2, h_cam2 = p_cam1.width() * ratio, p_cam1.height() * ratio w_cam3, h_cam3 = p_cam1.width() * ratio, p_cam1.height() * ratio # w_cam4 = self.__lbl_2d_cam_1.width() # h_cam4 = self.__lbl_2d_cam_1.height() self.__lbl_2d_cam_1.setPixmap(p_cam1.scaled(h_cam1, w_cam1, PyQt5.QtCore.Qt.KeepAspectRatio)) self.__lbl_2d_cam_2.setPixmap(p_cam2.scaled(h_cam1, w_cam1, PyQt5.QtCore.Qt.KeepAspectRatio)) self.__lbl_2d_cam_3.setPixmap(p_cam3.scaled(h_cam1, w_cam1, PyQt5.QtCore.Qt.KeepAspectRatio)) # self.__lbl_2d_cam_4.setPixmap(QPixmap.fromImage(rgb_4)) def on_change(self, i): if i == 0: # 3d tab self.__txt_box_3d_idx.setText(self.__txt_box_2d_idx.text()) else: self.__txt_box_2d_idx.setText(self.__txt_box_3d_idx.text()) # QMessageBox.information(self, # "Tab Index Changed!", # "Current Tab Index: %d" % i) # changed! def save_2d_image(self): cam_1_pixmap = self.__lbl_2d_cam_1.pixmap() cam_2_pixmap = self.__lbl_2d_cam_2.pixmap() cam_3_pixmap = self.__lbl_2d_cam_3.pixmap() # cam_4_pixmap = self.__lbl_2d_cam_4.pixmap() # set the correct path to save the images files cam_1_pixmap.save("") cam_2_pixmap.save("") cam_3_pixmap.save("") # cam_4_pixmap.save("") def save_3d_image(self): for i in range(nr_devices): writer = vtkXMLPolyDataWriter() path_cam = self.__path + serial_numbers[i] + "/3D/" path_exists = os.path.isdir(path_cam) today_str = datetime.now().strftime("%H:%M:%S.%f") if not path_exists: os.makedirs(path_cam) f_name = path_cam + self.__txt_box_3d_idx.text() + "_img_" + str(self.__counter) + "_" + today_str + ".vtp" writer.SetFileName(f_name) writer.SetInputData(self.pd_collection[i]) writer.Write() self.__counter += 1
class WidgetGallery(QDialog): def __init__(self, parent=None): super(WidgetGallery, self).__init__(parent) self.originalPalette = QApplication.palette() self.selectedString = "selected files:" #String used to generate list of args for scripts self.resize(600, 500) #Application default resolution # Initialize tab screen self.tabs = QTabWidget() self.tabs.blockSignals(True) self.createT1() self.createT2() self.createT3() self.tabs.addTab(self.T1, "HTTP GET") self.tabs.addTab(self.T2, "Tests") self.tabs.addTab(self.T3, "Utils") # Currently contains nothing, but it is the tof of the layout above tabs topLayout = QHBoxLayout() topLayout.addStretch(1) # Layout of the main program. Basically just the tabs. mainLayout = QVBoxLayout() mainLayout.addLayout(topLayout) mainLayout.addWidget(self.tabs) mainLayout.addStretch(1) # Window layout and header self.setLayout(mainLayout) self.tabs.blockSignals( False) #This is used to prevent an error during startup self.setWindowTitle("Deep Learning SCA Tool") # Input argument handling if len(sys.argv) > 2: if sys.argv[1] == "-t": self.tabs.setCurrentIndex(1) temp = self.selectedString for i in sys.argv[2:]: temp = temp + "\n" + i self.updateSelected(temp) elif sys.argv[1] == "-u": self.tabs.setCurrentIndex(2) temp = self.selectedString for i in sys.argv[2:]: temp = temp + "\n" + i self.updateSelected(temp) if len(sys.argv) > 1: if sys.argv[1] not in ['-u', '-t']: self.tabs.setCurrentIndex(1) temp = self.selectedString for i in sys.argv[1:]: temp = temp + "\n" + i self.updateSelected(temp) #Naming convention atm: T = tab, #X = index starting from 1, L = left side #So if something is createT1L it means create the left side of tab1... def createT1L(self): self.tab1 = QWidget() tab1Layout = QVBoxLayout(self) self.createBL() self.createBR() tab1Layout.addWidget(self.BL) tab1Layout.addWidget(self.BR) self.tab1.setLayout(tab1Layout) #Not sure if there's a better way to do this since I want elements to be aligned. #The block separation in this code represents GUI modules. #Naming convention: t = tab, #X = index starting from 1, l = left side #Naming convention: b = button, s = script, h = help #Each widget has its own layout variable where if possible it has the same prefix def createT2L(self): self.tab2 = QWidget() t2lLayout = QVBoxLayout(self) t2b1Widget = QWidget() t2b1Layout = QHBoxLayout() tab2Button1 = QPushButton("Files") tab2Button1h = QPushButton("?") t2b1Layout.addWidget(tab2Button1, 40) t2b1Layout.addWidget(tab2Button1h, 1) t2b1Widget.setLayout(t2b1Layout) t2lLayout.addWidget(t2b1Widget) self.tab2TextWidget = QWidget() tab2TextLayout = QHBoxLayout() self.tab2.textEdit = QTextEdit() self.tab2.textEdit.setPlainText(self.selectedString) tab2Texth = QPushButton("?") tab2TextLayout.addWidget(self.tab2.textEdit, 40) tab2TextLayout.addWidget(tab2Texth, 1) self.tab2TextWidget.setLayout(tab2TextLayout) t2lLayout.addWidget(self.tab2TextWidget) t2dropdownWidget = QWidget() t2dropdownLayout = QHBoxLayout() t2dropdown = QComboBox() t2dropdown.addItem("Average Rank Test") t2dropdown.addItem("option 2 tbd") t2dropdown.addItem("option 3 tbd") t2dropdownh = QPushButton("?") t2dropdownLayout.addWidget(t2dropdown, 40) t2dropdownLayout.addWidget(t2dropdownh, 1) t2dropdownWidget.setLayout(t2dropdownLayout) t2lLayout.addWidget(t2dropdownWidget) #SELECTION GROUP 1 t2Group1 = QWidget() t2Group1Layout = QVBoxLayout() t2interval = QWidget() t2intervalLayout = QHBoxLayout() traceInterval = QLineEdit('57:153') traceIntervalh = QPushButton("?") t2intervalLayout.addWidget(traceInterval, 40) t2intervalLayout.addWidget(traceIntervalh, 1) t2interval.setLayout(t2intervalLayout) t2Group1Layout.addWidget(t2interval) t2keybyte = QWidget() t2keybyteLayout = QHBoxLayout() keybytePos = QComboBox() # keybytePos.addItem("Select Keybyte Position") keybytePos.addItem("0") keybytePos.addItem("1") keybytePos.addItem("2") keybytePos.addItem("3") keybytePos.addItem("4") keybytePos.addItem("5") keybytePos.addItem("6") keybytePos.addItem("7") keybytePos.addItem("8") keybytePos.addItem("9") keybytePos.addItem("A") keybytePos.addItem("B") keybytePos.addItem("C") keybytePos.addItem("D") keybytePos.addItem("E") keybytePos.addItem("F") keybytePosh = QPushButton("?") t2keybyteLayout.addWidget(keybytePos, 40) t2keybyteLayout.addWidget(keybytePosh, 1) t2keybyte.setLayout(t2keybyteLayout) t2Group1Layout.addWidget(t2keybyte) t2iter = QWidget() t2iterLayout = QHBoxLayout() numtraces = QLineEdit('50') numiter = QLineEdit('100') numiterh = QPushButton("?") t2iterLayout.addWidget(numtraces, 40) t2iterLayout.addWidget(numiter, 40) t2iterLayout.addWidget(numiterh, 1) t2iter.setLayout(t2iterLayout) t2Group1Layout.addWidget(t2iter) t2art = QWidget() t2artLayout = QHBoxLayout() art = QPushButton("run average rank test") arth = QPushButton("?") t2artLayout.addWidget(art, 40) t2artLayout.addWidget(arth, 1) t2art.setLayout(t2artLayout) t2Group1Layout.addWidget(t2art) t2Group1.setLayout(t2Group1Layout) t2lLayout.addWidget(t2Group1) self.tab2.setLayout(t2lLayout) #SELECTION GROUP 2 t2Group2 = QWidget() t2ConfirmLayout = QHBoxLayout() tab2Confirm = QPushButton("Confirm") tab2Confirmh = QPushButton("?") t2ConfirmLayout.addWidget(tab2Confirm, 40) t2ConfirmLayout.addWidget(tab2Confirmh, 1) t2Group2.setLayout(t2ConfirmLayout) t2Group2.hide() t2lLayout.addWidget(t2Group2) #SELECTION GROUP 3 t2Group3 = QWidget() t2HideLayout = QHBoxLayout() tab2Hide = QPushButton("Hide") tab2Hideh = QPushButton("?") t2HideLayout.addWidget(tab2Hide, 40) t2HideLayout.addWidget(tab2Hideh, 1) t2Group3.setLayout(t2HideLayout) t2Group3.hide() t2lLayout.addWidget(t2Group3) #A lot of local functions will be used as well as some "global" (bound to super) functions. #The following is a list of functions that will be used on buttons presses or text changes #Naming convention: info = update right hand side info column. def filebrowserinfo(): info = "Browse for files to add to the list in the text editor. Trained models are of type .h5 while history files and raw results data are .npy." self.updateInfo(info, self.tabs.currentIndex()) def selectedinfo(): info = "This is an editable list of files currently selected. You can edit it by just deleting text. Never remove the first line and never enter non-file non-empty strings on a line." self.updateInfo(info, self.tabs.currentIndex()) def dropdowninfo(): info = "Use the dropdown menu to select which script to run on the selected files. It is up to you as the user to not make a mistake..." self.updateInfo(info, self.tabs.currentIndex()) def artinfo(): info = "Average rank test for selected models. More info to come." self.updateInfo(info, self.tabs.currentIndex()) def confirminfo(): info = "Placeholder." self.updateInfo(info, self.tabs.currentIndex()) def intervalinfo(): info = "Set the interval for the first keybyte position to correspond to your model's input size. Subsequent keybytes will be calculated from this." self.updateInfo(info, self.tabs.currentIndex()) def keybyteinfo(): info = "select which keybyte position to attack using selected models. Best success is typically attacking the same position as trained on." self.updateInfo(info, self.tabs.currentIndex()) def iterinfo(): info = "set the number of traces to plot and the number of test iterations to run for your average results. I recommend leaving them on default while trying to improve models." self.updateInfo(info, self.tabs.currentIndex()) def textchanget2(): temp = self.tab2.textEdit.toPlainText() self.updateSelected(temp, ignore=1, current=self.tabs.currentIndex()) def clickedt2b1(): self.openFileNamesDialog() def clickedConfirm(): print(self.selectedString) def avgrank(): traces = numtraces.text() iterations = numiter.text() interval = traceInterval.text() tracestart = re.search('(\d+):(\d+)', interval).group(1) traceend = re.search('(\d+):(\d+)', interval).group(2) keybytepos = keybytePos.currentIndex() alert = QMessageBox() alert.setText("choose trace file") alert.exec_() tracefile = self.openTracesDialog() if not tracefile: returng alert.setText("choose plaintext file") alert.exec_() ptfile = self.openTracesDialog() if not ptfile: return alert.setText( "currently the key is hardcoded to be [ 26, 206, 149, 113, 251, 46, 52, 156, 5, 162, 215, 87, 29, 47, 187, 236]. This may be changed to loading a key file instead in the future" ) alert.exec_() models = self.splitter(self.selectedString) subprocess.call([ 'python', 'scripts/average_rank_test.py', str(traces), str(iterations), str(tracestart), str(traceend), str(keybytepos), tracefile, ptfile ] + models[1:]) def selection(i): if i == 0: t2Group1.show() t2Group2.hide() t2Group3.hide() if i == 1: t2Group1.hide() t2Group2.show() t2Group3.hide() if i == 2: t2Group1.hide() t2Group2.hide() t2Group3.show() #Actually bind the scripts to the buttons and textedits. This needs to be defined after the #functions they will be used which is why they are hidden down here tab2Button1.clicked.connect(clickedt2b1) tab2Button1h.clicked.connect(filebrowserinfo) tab2Texth.clicked.connect(selectedinfo) t2dropdownh.clicked.connect(dropdowninfo) tab2Confirm.clicked.connect(clickedConfirm) tab2Confirmh.clicked.connect(confirminfo) traceIntervalh.clicked.connect(intervalinfo) keybytePosh.clicked.connect(keybyteinfo) numiterh.clicked.connect(iterinfo) art.clicked.connect(avgrank) arth.clicked.connect(artinfo) self.tab2.textEdit.textChanged.connect(textchanget2) t2dropdown.currentIndexChanged.connect(selection) def createT3L(self): self.tab3 = QWidget() t3lLayout = QVBoxLayout(self) t3b1Widget = QWidget() t3b1Layout = QHBoxLayout() tab3Button1 = QPushButton("Files") tab3Button1h = QPushButton("?") t3b1Layout.addWidget(tab3Button1, 40) t3b1Layout.addWidget(tab3Button1h, 1) t3b1Widget.setLayout(t3b1Layout) t3lLayout.addWidget(t3b1Widget) self.tab3TextWidget = QWidget() tab3TextLayout = QHBoxLayout() self.tab3.textEdit = QTextEdit() self.tab3.textEdit.setPlainText(self.selectedString) tab3Texth = QPushButton("?") tab3TextLayout.addWidget(self.tab3.textEdit, 40) tab3TextLayout.addWidget(tab3Texth, 1) self.tab3TextWidget.setLayout(tab3TextLayout) t3lLayout.addWidget(self.tab3TextWidget) t3dropdownWidget = QWidget() t3dropdownLayout = QHBoxLayout() t3dropdown = QComboBox() t3dropdown.addItem("Unzip .tar.zip traces") t3dropdown.addItem("Model Input Shape") t3dropdown.addItem("Model Summary") t3dropdown.addItem("Plot History Files") t3dropdown.addItem("Plot a trace") t3dropdownh = QPushButton("?") t3dropdownLayout.addWidget(t3dropdown, 40) t3dropdownLayout.addWidget(t3dropdownh, 1) t3dropdownWidget.setLayout(t3dropdownLayout) t3lLayout.addWidget(t3dropdownWidget) #SELECTION GROUP 1 t3Group1 = QWidget() unzipperLayout = QHBoxLayout() unzipper = QPushButton("Unzip") unzipperh = QPushButton("?") unzipperLayout.addWidget(unzipper, 40) unzipperLayout.addWidget(unzipperh, 1) t3Group1.setLayout(unzipperLayout) t3lLayout.addWidget(t3Group1) #SELECTION GROUP 2 t3Group2 = QWidget() inputShapeLayout = QHBoxLayout() inShape = QPushButton("Print Model Input Shapes") inShapeh = QPushButton("?") inputShapeLayout.addWidget(inShape, 40) inputShapeLayout.addWidget(inShapeh, 1) t3Group2.setLayout(inputShapeLayout) t3Group2.hide() t3lLayout.addWidget(t3Group2) #SELECTION GROUP 3 t3Group3 = QWidget() summaryLayout = QHBoxLayout() modelSummary = QPushButton("Print Model Summary") modelSummaryh = QPushButton("?") summaryLayout.addWidget(modelSummary, 40) summaryLayout.addWidget(modelSummaryh, 1) t3Group3.setLayout(summaryLayout) t3Group3.hide() t3lLayout.addWidget(t3Group3) #SELECTION GROUP 4 t3Group4 = QWidget() historyLayout = QHBoxLayout() plotHistory = QPushButton("Plot History Files") plotHistoryh = QPushButton("?") historyLayout.addWidget(plotHistory, 40) historyLayout.addWidget(plotHistoryh, 1) t3Group4.setLayout(historyLayout) t3Group4.hide() t3lLayout.addWidget(t3Group4) #SELECTION GROUP 5 t3Group5 = QWidget() traceLayout = QHBoxLayout() plotTrace = QPushButton("Plot Trace") plotTraceh = QPushButton("?") traceLayout.addWidget(plotTrace, 40) traceLayout.addWidget(plotTraceh, 1) t3Group5.setLayout(traceLayout) t3Group5.hide() t3lLayout.addWidget(t3Group5) self.tab3.setLayout(t3lLayout) def textchanget3(): temp = self.tab3.textEdit.toPlainText() self.updateSelected(temp, ignore=2, current=self.tabs.currentIndex()) def clickedt3b1(): self.openFileNamesDialog() def filebrowserinfo(): info = "Browse for files to add to the list in the text editor. Trained models are of type .h5 while history files and raw results data are .npy." self.updateInfo(info, self.tabs.currentIndex()) def selectedinfo(): info = "This is an editable list of files currently selected. You can edit it by just deleting text. Never remove the first line and never enter non-file non-empty strings on a line." self.updateInfo(info, self.tabs.currentIndex()) def dropdowninfo(): info = "Use the dropdown menu to select which script to run on the selected files. It is up to you as the user to not make a mistake..." self.updateInfo(info, self.tabs.currentIndex()) def unzipperinfo(): info = "SEBASTIAN WRITE YOUR INFOR HERE!!!" self.updateInfo(info, self.tabs.currentIndex()) def inshapeinfo(): info = "A script for printing the input shape of all models selected." self.updateInfo(info, self.tabs.currentIndex()) def summaryinfo(): info = "A script for printing the model summary of all models selected." self.updateInfo(info, self.tabs.currentIndex()) def historyinfo(): info = "A script for plotting all selected history files from trained models. These should be .npz files." self.updateInfo(info, self.tabs.currentIndex()) def traceinfo(): info = "A script for plotting a randomly selected trace from each selected trace file. Make sure to only select trace files for this." self.updateInfo(info, self.tabs.currentIndex()) def clickedConfirm(): print(self.selectedString) def summary(): args = self.splitter(self.selectedString) subprocess.call(['python', 'scripts/model_summary.py'] + args[1:]) def inputshape(): args = self.splitter(self.selectedString) subprocess.call(['python', 'scripts/input_shape.py'] + args[1:]) def historyplotter(): args = self.splitter(self.selectedString) subprocess.call(['python', 'scripts/plot_history.py'] + args[1:]) def traceplot(): args = self.splitter(self.selectedString) subprocess.call(['python', 'scripts/trace_plotter.py'] + args[1:]) def selection(i): if i == 0: t3Group1.show() t3Group2.hide() t3Group3.hide() t3Group4.hide() t3Group5.hide() if i == 1: t3Group1.hide() t3Group2.show() t3Group3.hide() t3Group4.hide() t3Group5.hide() if i == 2: t3Group1.hide() t3Group2.hide() t3Group3.show() t3Group4.hide() t3Group5.hide() if i == 3: t3Group1.hide() t3Group2.hide() t3Group3.hide() t3Group4.show() t3Group5.hide() if i == 4: t3Group1.hide() t3Group2.hide() t3Group3.hide() t3Group4.hide() t3Group5.show() tab3Button1.clicked.connect(clickedt3b1) tab3Button1h.clicked.connect(filebrowserinfo) plotHistory.clicked.connect(historyplotter) t3dropdownh.clicked.connect(dropdowninfo) unzipper.clicked.connect(clickedConfirm) unzipperh.clicked.connect(unzipperinfo) inShape.clicked.connect(inputshape) inShapeh.clicked.connect(inshapeinfo) modelSummary.clicked.connect(summary) modelSummaryh.clicked.connect(summaryinfo) plotHistoryh.clicked.connect(historyinfo) plotTrace.clicked.connect(traceplot) plotTraceh.clicked.connect(traceinfo) tab3Texth.clicked.connect(selectedinfo) self.tab3.textEdit.textChanged.connect(textchanget3) t3dropdown.currentIndexChanged.connect(selection) #The following three definitions are for right hand side of tabs, used for displaying info. def createT1R(self): self.T1R = QWidget() t1rLayout = QVBoxLayout(self) self.tab1info = QLabel() self.tab1info.setWordWrap(True) self.tab1info.setText("press the ? buttons to get more info") t1rLayout.addWidget(self.tab1info) self.T1R.setLayout(t1rLayout) def createT2R(self): self.T2R = QWidget() t2rLayout = QVBoxLayout(self) self.tab2info = QLabel() self.tab2info.setWordWrap(True) self.tab2info.setText("press the ? buttons to get more info") t2rLayout.addWidget(self.tab2info) self.T2R.setLayout(t2rLayout) def createT3R(self): self.T3R = QWidget() t3rLayout = QVBoxLayout(self) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) self.T3R.setSizePolicy(sizePolicy) self.tab3info = QLabel() self.tab3info.setWordWrap(True) self.tab3info.setText("press the ? buttons to get more info") t3rLayout.addWidget(self.tab3info) self.T3R.setLayout(t3rLayout) #The actual tabs. Naming convention will have to be changed to be consistent later. #These are basically just containers holding the two halves of each tab. def createT1(self): self.T1 = QGroupBox() t1Layout = QHBoxLayout() self.createT1L() self.createT1R() t1Layout.addWidget(self.tab1) t1Layout.addWidget(self.T1R) self.T1.setLayout(t1Layout) def createT2(self): self.T2 = QGroupBox() t2Layout = QHBoxLayout() self.createT2L() self.createT2R() self.T2R.setMaximumWidth(100) t2Layout.addWidget(self.tab2) t2Layout.addWidget(self.T2R) self.T2.setLayout(t2Layout) def createT3(self): self.T3 = QGroupBox() t3Layout = QHBoxLayout() self.createT3L() self.createT3R() self.T3R.setMaximumWidth(100) t3Layout.addWidget(self.tab3) t3Layout.addWidget(self.T3R) self.T3.setLayout(t3Layout) #This will be changed later. The first draft of this program used quadrants for its design. #This was the old Bottom Right quadrant. Will be changed to use same design as t2 and t3. def createBL(self): self.BL = QGroupBox("Results") self.BL.textEdit = QTextEdit() self.BL.textEdit.setPlainText("waiting for results...") mylayout = QGridLayout() mylayout.addWidget(self.BL.textEdit, 0, 0, 1, 2) mylayout.setRowStretch(5, 1) self.BL.setLayout(mylayout) def createBR(self): self.BR = QGroupBox("IP address/URL") self.BR.lineEdit = QLineEdit('0.0.0.0') confirmIPButton = QPushButton("Confirm") def on_button_clicked(): globalIP = self.BR.lineEdit.text() r = requests.get(globalIP) self.BL.textEdit.setPlainText('HEADER:\n\n' + str(r.headers) + '\n\nCONTENT:\n\n' + str(r.content)) alert = QMessageBox() alert.setText(str(r.status_code)) alert.exec_() confirmIPButton.clicked.connect(on_button_clicked) layout = QGridLayout() layout.addWidget(self.BR.lineEdit, 0, 0, 1, 2) layout.addWidget(confirmIPButton, 1, 0, 1, 2) layout.setRowStretch(5, 1) self.BR.setLayout(layout) #File browser used to load files if you didn't use the terminal for that. def openFileNamesDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog files, _ = QFileDialog.getOpenFileNames( self, "QFileDialog.getOpenFileNames()", "", "All Files (*);;Model Files (*.h5);;Numpy Arrays (*.npy);;Numpy Zipfiles (*.npz)", options=options) if files: temp = self.selectedString for i in files: temp = temp + "\n" + i self.updateSelected(temp) def openTracesDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "Numpy Arrays (*.npy)", options=options) if fileName: return fileName else: return False #Updates and synchronizes the loaded file lists on t2 and t3. #The conditionals prevent infinite recursion. def updateSelected(self, newSelected, ignore=0, current=0): self.selectedString = newSelected if ignore is not 1: if current is not 1: self.tab2.textEdit.setPlainText(self.selectedString) if ignore is not 2: if current is not 2: self.tab3.textEdit.setPlainText(self.selectedString) #Update the right hand side info bar with whatever query the user clicked. def updateInfo(self, info, tabnumber): if tabnumber == 0: self.tab1info.setText(info) if tabnumber == 1: self.tab2info.setText(info) if tabnumber == 2: self.tab3info.setText(info) #I don't think this is used anymore? Probably old debugging thing. #Will be removed if it turns out it is useless. def tabSwitch(self): print(str(self.tabs.currentIndex())) if self.tabs.currentIndex() is not 0: if self.tabs.currentIndex() == 1: temp = self.tab2.textEdit.toPlainText() self.updateSelected(temp) if self.tabs.currentIndex() == 2: temp = self.tab3.textEdit.toPlainText() self.updateSelected(temp) self.updateSelected(self.selectedString) #Script for splitting the string stored in the text edit into a list strings #to send as args for the linked scripts def splitter(self, s): return [line.strip() for line in s.splitlines() if line.strip()]
class TabDocker(QWidget): """ Tab widget for the settingswidgets. Inherits: QWidget """ sig_start_plot = pyqtSignal() latest_active = [None] def __init__(self, parent=None, **kwargs): """ Initialise layout for TabDocker Arguments: parent - Parent widget (default None) Return: None """ super(TabDocker, self).__init__(parent) self.parent = parent try: self.layout = kwargs['layout'] except KeyError: self.layout = None try: self.name = kwargs['name'] except KeyError: self.name = None self.widgets = [] layout_tmp = QVBoxLayout(self) self.parent_widget = QWidget(self) layout_tmp.addWidget(self.parent_widget) layout_tmp.setContentsMargins(0, 0, 0, 0) layout = QVBoxLayout(self.parent_widget) layout.setContentsMargins(0, 0, 0, 0) self.tab_widget = QTabWidget(self) if self.layout in ('TAB1', 'Settings'): tab_bar = MyTabBar(self.tab_widget) tab_bar.setObjectName('vertical') self.tab_widget.setObjectName('vertical') self.tab_widget.setTabBar(tab_bar) self.tab_widget.setTabPosition(QTabWidget.West) layout.addWidget(self.tab_widget) self.tab_widget.currentChanged.connect(self.assign_latest) @pyqtSlot(int) def assign_latest(self, idx): current_name = self.tab_widget.tabText(idx) try: parent_content = self.parent.content[self.layout].name except AttributeError: # Exception for the Default settings dialog parent_content = False except TypeError: # Exception for the Default settings dialog parent_content = False except KeyError: # Exception for the main window dialog parent_content = False check_list = (parent_content, self.name, current_name) latest_active = self for list_idx, entry in enumerate(check_list): if entry == 'Visualisation': cur_tab_widget = self.tab_widget.widget(idx) try: for i in range(list_idx): idx = cur_tab_widget.currentIndex() cur_tab_widget = cur_tab_widget.widget(idx) latest_active = cur_tab_widget if cur_tab_widget is not None else self except: latest_active = self break if self.latest_active[0] != latest_active: self.latest_active[0] = latest_active latest_active.sig_start_plot.emit() def setCurrentIndex(self, idx): """ Set the current Index of the tab_widget. Arguments: idx - Index to set Returns: Current index of self.tab_widget """ return self.tab_widget.setCurrentIndex(idx) def setCurrentWidget(self, widget): """ Set the current widget of the tab_widget. Arguments: idx - Widget to set Returns: Current index of self.tab_widget """ return self.tab_widget.setCurrentWidget(widget) def currentIndex(self): """ Get the current Index of the tab_widget. Returns: Current index of self.tab_widget """ return self.tab_widget.currentIndex() def add_tab(self, widget, name, add_widgets=True): """ Add a new tab to the TabDocker Arguments: widget - Widget to add name - Name of the widget Return: None """ if isinstance(widget, TabDocker): widget.parent_widget.setObjectName('tab') else: pass current_state = self.tab_widget.blockSignals(True) index = self.tab_widget.addTab(widget, name) if add_widgets: self.widgets.append(widget) self.tab_widget.blockSignals(current_state) self.tab_widget.setTabToolTip(index, name) def count(self): """ Return the number of tabs. Arguments: None Returns: Number of tabs """ return self.tab_widget.count() def widget(self, idx): """ Return the widget that belongs to the idx of tabs. Arguments: idx - Tab index Returns: Widget """ return self.tab_widget.widget(idx) def setMovable(self, status): """ Set the movable status for the tab widgets Arguments: status - Boolean variable for the status Returns: None """ return self.tab_widget.setMovable(status) def tabText(self, idx): """ Return the text of the tab at idx Arguments: idx - Index of the tab Returns: Text of the tab at position isx """ return self.tab_widget.tabText(idx) def setTabText(self, idx, text): """ Set the text for the tab at idx Arguments: idx - Index of the tab text - Text of the tab Returns: None """ return self.tab_widget.setTabText(idx, text) def removeTab(self, idx): """ Remove the widget located at tab idx Arguments: idx - Idx of the widget Returns: None """ current_state = self.tab_widget.blockSignals(True) idx = self.tab_widget.removeTab(idx) self.tab_widget.blockSignals(current_state) return idx def indexOf(self, widget): """ Get the index of the widget. Arguments: widget - Adress of the widget Returns: Index of the widget """ return self.tab_widget.indexOf(widget) def setTabPosition(self, position): """ Set the tab position of the Tab bar Arguments: position - Tab position as string ['North', 'East', 'West', 'South'] Returns: None """ tab_position_dict = { 'North': QTabWidget.North, 'South': QTabWidget.South, 'West': QTabWidget.West, 'East': QTabWidget.East, } self.tab_widget.setTabPosition(tab_position_dict[position]) def setTabEnabled(self, index, state): """ Set the tab position index to the enable state. Arguments: index - Tab position index state - State (True or False) Returns: None """ self.tab_widget.setTabEnabled(index, state) def order_tabs(self): current_state = self.tab_widget.blockSignals(True) widget_tuple = tuple([(self.widget(idx).name, self.widget(idx), self.tab_widget.isTabEnabled(idx)) for idx in range(self.count())]) for idx in reversed(range(self.count())): self.removeTab(idx) for idx, (name, widget, state) in enumerate(sorted(widget_tuple)): self.add_tab(widget, name, add_widgets=False) self.setTabEnabled(idx, state) if state: self.setCurrentIndex(idx) self.tab_widget.blockSignals(current_state) def enable_tab(self, visible): """ Enable or disable the tab. Arguments: visible - Enable if True, Disable if False name - Name of the tab to disable. Returns: None """ index = self.parent.content[self.layout].indexOf(self) if not visible: self.parent.content[self.layout].removeTab(index) else: self.parent.content[self.layout].add_tab(self, self.name) self.parent.content[self.layout].order_tabs()
class TfrmBase(QMainWindow, TScreenStates): recordCount = 0 def __init__(self, parent=None): super(TfrmBase, self).__init__() self.FOnStateChange = self.onStateChange self.activeState = self.ssInactive self._defaultSettings() self._createWidgets() self._setEvents() def _defaultSettings(self): self.setObjectName("frmBase") self.resize(640, 480) self.setMinimumSize(QSize(640, 480)) def _createWidgets(self): self._createLayout() self._createMenus() self._createToolBar() self._createStatusBar() self._createPages() self._setLayouts() def _createLayout(self): self.clientArea = QWidget() self.clientArea.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.clientArea.setMinimumSize(QSize(640, 400)) self.clientArea.setBaseSize(QSize(640, 400)) self.clientArea.setLayoutDirection(Qt.LeftToRight) self.clientArea.setObjectName("clientArea") self.gridLayout = QGridLayout(self.clientArea) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setSpacing(0) self.gridLayout.setObjectName("gridLayout") def _createMenus(self): # Create a Menu Bar self.mnMenu = self.menuBar() self.mnMenu.setObjectName("mnMenu") # Create all Top Menus self.mnApp = QMenu('&Aplicação') self.mnApp.setObjectName('mnApp') self.mnOpe = QMenu('&Operação') self.mnOpe.setObjectName("mnOperations") self.mnNav = QMenu('&Navegação') self.mnNav.setObjectName("mnNav") # Set Menus to MenuBar self.mnMenu.addMenu(self.mnNav) self.mnMenu.addMenu(self.mnOpe) self.mnMenu.addMenu(self.mnApp) # Crealte all Actions to Application Menu self._createAppActions() self._createOpeActions() self._setMenuActions() self.mnMenu.addAction(self.mnApp.menuAction()) self.mnMenu.addAction(self.mnOpe.menuAction()) self.mnMenu.addAction(self.mnNav.menuAction()) self._settingActionsEvents() def _createAppActions(self): # Exit Program Action self.acExit = QAction( self.getIcon("./resources/exit.ico", QSize(32, 32)), '&Sair') self.acExit.setObjectName("acExit") self.acExit.setShortcut('Ctrl+Q') self.acExit.setStatusTip('Finalizar o Programa') self.acExit.triggered.connect(self.closeApp) def _createOpeActions(self): # Search Action self.acSearch = QAction( self.getIcon("./resources/Search.ico", QSize(32, 32)), '&Pesquisar') self.acSearch.setObjectName("acSearch") self.acSearch.setShortcut('F5,Ctrl+P') self.acSearch.setStatusTip( 'Preenche o Filtro para Selecionar Registros') # List Action self.acList = QAction( self.getIcon("./resources/list.ico", QSize(32, 32)), '&Listar') self.acList.setShortcut('Ctrl+L') self.acList.setStatusTip('Listar todos os Registros') self.acList.setObjectName("acList") # Insert Action self.acInsert = QAction( self.getIcon("./resources/db_add.ico", QSize(32, 32)), '&Inserir') self.acInsert.setShortcut('F2,Ins') self.acInsert.setStatusTip('Incluir Novo Registros') self.acInsert.setObjectName("acInsert") # Update Action self.acUpdate = QAction( self.getIcon("./resources/db_update.ico", QSize(32, 32)), '&Editar') self.acUpdate.setShortcut('Ctrl+U') self.acUpdate.setStatusTip('Editar o Registro Atual') self.acUpdate.setObjectName("acUpdate") # Delete Action self.acDelete = QAction( self.getIcon("./resources/db_remove.ico", QSize(32, 32)), '&Excluir') self.acDelete.setShortcut('Ctrl+Del') self.acDelete.setStatusTip('Exclui o Registro Atual') self.acDelete.setObjectName("acDelete") # Save Action self.acSave = QAction( self.getIcon("./resources/db_commit.ico", QSize(32, 32)), '&Salvar') self.acSave.setShortcut('F10,Ctrl+S') self.acSave.setStatusTip('Salva as Alterações do Registro') self.acSave.setObjectName("acSave") # Cancel Action self.acCancel = QAction( self.getIcon("./resources/cancel.ico", QSize(32, 32)), '&Cancelar') self.acCancel.setShortcut('Esc') self.acCancel.setStatusTip('Cancela as Alterações do Registro') self.acCancel.setObjectName("acCancel") # First Action self.acFirst = QAction( self.getIcon("./resources/start.ico", QSize(32, 32)), '&Início') self.acFirst.setShortcut('Ctrl+Left') self.acFirst.setStatusTip('Vai para o Primeiro Registro') self.acFirst.setObjectName("acFirst") # Prior Action self.acPrior = QAction( self.getIcon("./resources/left.ico", QSize(32, 32)), '&Anterior') self.acPrior.setShortcut('Left') self.acPrior.setStatusTip('Vai para o Registro Anterior') self.acPrior.setObjectName("acPrior") # Next Action self.acNext = QAction( self.getIcon("./resources/right.ico", QSize(32, 32)), '&Próximo') self.acNext.setShortcut('Right') self.acNext.setStatusTip('Vai para o Próximo Registro') self.acNext.setObjectName("acNext") # Last Action self.acLast = QAction( self.getIcon("./resources/end.ico", QSize(32, 32)), '&Último') self.acLast.setShortcut('Ctrl+Right') self.acLast.setStatusTip('Vai para o Último Registro') self.acLast.setObjectName("acLast") # Form Title Action self.dcTitle = QAction() font = QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.dcTitle.setFont(font) self.dcTitle.setObjectName("dcTitle") def getIcon(self, res: str, size: QSize) -> QIcon: icon = QIcon() icon.addPixmap( QPixmap(res).scaled(size.width(), size.height(), Qt.KeepAspectRatio), QIcon.Active, QIcon.On) return icon def _setMenuActions(self): # Set Menu Application Actions self.mnApp.addAction(self.acExit) # Set Menu Operations Actions self.mnOpe.addAction(self.acSearch) self.mnOpe.addAction(self.acList) self.mnOpe.addSeparator() self.mnOpe.addAction(self.acInsert) self.mnOpe.addAction(self.acUpdate) self.mnOpe.addAction(self.acDelete) self.mnOpe.addSeparator() self.mnOpe.addAction(self.acSave) self.mnOpe.addAction(self.acCancel) # Set Menu Navigation Actions self.mnNav.addAction(self.acFirst) self.mnNav.addAction(self.acPrior) self.mnNav.addAction(self.acNext) self.mnNav.addAction(self.acLast) def _settingActionsEvents(self): # Set Menu Operations Trigger onClick self.acSearch.triggered.connect( lambda: self.setFormStatus(self.ssSearch)) self.acList.triggered.connect( lambda: self.setFormStatus(self.ssSearchAll)) self.acInsert.triggered.connect( lambda: self.setFormStatus(self.ssInsert)) self.acUpdate.triggered.connect( lambda: self.setFormStatus(self.ssUpdate)) self.acDelete.triggered.connect( lambda: self.setFormStatus(self.ssDelete)) self.acSave.triggered.connect(lambda: self.setFormStatus(self.ssPost)) self.acCancel.triggered.connect( lambda: self.setFormStatus(self.ssCancel)) # Set Menu Navigation Trigger onClick self.acFirst.triggered.connect( lambda: self.setFormStatus(self.ssFirst)) self.acPrior.triggered.connect( lambda: self.setFormStatus(self.ssPrior)) self.acNext.triggered.connect(lambda: self.setFormStatus(self.ssNext)) self.acLast.triggered.connect(lambda: self.setFormStatus(self.ssLast)) def _createToolBar(self): # Create a tbActions ToolBar self.tbActions = QToolBar() self.tbActions.setMinimumSize(QSize(300, 34)) self.tbActions.setMaximumSize(QSize(16777215, 34)) self.tbActions.setBaseSize(QSize(300, 34)) self.tbActions.setAcceptDrops(False) self.tbActions.setToolTipDuration(3) self.tbActions.setAllowedAreas(Qt.TopToolBarArea) self.tbActions.setObjectName("tbActions") self.addToolBar(Qt.TopToolBarArea, self.tbActions) # Create a tbTitle ToolBar self.tbTitle = QToolBar() self.tbTitle.setMinimumSize(QSize(340, 34)) self.tbTitle.setMaximumSize(QSize(16777215, 34)) self.tbTitle.setBaseSize(QSize(341, 34)) self.tbTitle.setAllowedAreas(Qt.TopToolBarArea) self.tbTitle.setToolButtonStyle(Qt.ToolButtonTextOnly) self.tbTitle.setFloatable(False) self.tbTitle.setObjectName("tbTitle") # self.tbTitle.setLabelAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.addToolBar(Qt.TopToolBarArea, self.tbTitle) # Call Add Actions to ToolBar self._setToolBarActions() def _setToolBarActions(self): # Set ToolBar Actions self.tbActions.addAction(self.acSearch) self.tbActions.addAction(self.acInsert) self.tbActions.addAction(self.acUpdate) self.tbActions.addAction(self.acDelete) self.tbActions.addSeparator() self.tbActions.addAction(self.acSave) self.tbActions.addAction(self.acExit) self.tbTitle.addAction(self.dcTitle) def _createStatusBar(self): self.sbStatus = QStatusBar() self.sbStatus.setMaximumHeight(24) self.sbStatus.setObjectName("sbStatus") self.sbStatus.setStyleSheet(""" .QLabel { background-color: #FFFFFF; color: #000000; } """) self.lbStatus = QLabel(self.sbStatus) self.lbStatus.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.lbStatus.setText('Inactive') self.lbStatus.setMinimumSize(QSize(130, 15)) self.lbStatus.setFrameShape(QFrame.Panel) self.lbStatus.setFrameShadow(QFrame.Sunken) self.sbStatus.addPermanentWidget(self.lbStatus) self.setStatusBar(self.sbStatus) def _createPages(self): self.tabMain = QTabWidget(self.clientArea) self.tabMain.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.tabMain.setTabPosition(QTabWidget.South) self.tabMain.setObjectName("tabMain") self.pgList = QWidget(self.tabMain) self.pgList.setObjectName("pgList") self.pgDetail = QWidget(self.tabMain) self.pgDetail.setObjectName("pgDetail") self.tabMain.addTab(self.pgList, "") self.tabMain.addTab(self.pgDetail, "") self._createPageListContent() def _createPageListContent(self): self.treeWidget = QTreeWidget(self.pgList) self.treeWidget.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.treeWidget.setFrameShape(QFrame.NoFrame) self.treeWidget.setFrameShadow(QFrame.Plain) self.treeWidget.setColumnCount(3) self.treeWidget.setObjectName("treeWidget") self.treeWidget.headerItem().setText(0, "Campo") self.treeWidget.headerItem().setText(1, "Campo") self.treeWidget.headerItem().setText(2, "Campo") self.treeWidget.setGeometry(QRect(0, 0, 640, 370)) self.treeWidget.setMinimumSize(QSize(640, 370)) self.tabMain.setCurrentIndex(0) def _setLayouts(self): self.gridLayout.addWidget(self.tabMain, 0, Qt.AlignBottom | Qt.AlignRight, 1, 1) self.setCentralWidget(self.clientArea) def translateForm(self): self._translate = QCoreApplication.translate self.setWindowTitle( self._translate("TfrmBase", "Tela de Básica de Cadastros")) self.mnApp.setTitle(self._translate("TfrmBase", "Aplicação")) self.mnOpe.setTitle(self._translate("TfrmBase", "Operações")) self.mnNav.setTitle(self._translate("TfrmBase", "Navegação")) self.sbStatus.setToolTip(self._translate("TfrmBase", "Barra de Status")) self.tbActions.setWindowTitle( self._translate("TfrmBase", "Ferramentas")) self.tbActions.setToolTip( self._translate("TfrmBase", "Barra de Ferramentas")) self.tbTitle.setWindowTitle(self._translate("TfrmBase", "Descrição")) self.acExit.setText(self._translate("TfrmBase", "&Sair")) self.acExit.setToolTip(self._translate("TfrmBase", "Sair do Programa")) self.acSearch.setText(self._translate("TfrmBase", "&Pesquisar")) self.acSearch.setStatusTip( self._translate("TfrmBase", "Procurar Por um Registro")) self.acList.setText(self._translate("TfrmBase", "&Listar Todos")) self.acList.setStatusTip( self._translate("TfrmBase", "Lista todos os Registros")) self.acInsert.setText(self._translate("TfrmBase", "&Inserir")) self.acInsert.setStatusTip( self._translate("TfrmBase", "Adicionar Registro")) self.acUpdate.setText(self._translate("TfrmBase", "&Editar")) self.acUpdate.setStatusTip( self._translate("TfrmBase", "Editar Registro")) self.acDelete.setText(self._translate("TfrmBase", "E&xcluir")) self.acDelete.setStatusTip( self._translate("TfrmBase", "Excluir Registro")) self.acSave.setText(self._translate("TfrmBase", "&Salvar")) self.acSave.setToolTip(self._translate("TfrmBase", "Salvar Registro")) self.acCancel.setText(self._translate("TfrmBase", "&Cancelar")) self.acCancel.setToolTip( self._translate("TfrmBase", "Cencelar Alterações")) self.dcTitle.setText( self._translate("TfrmBase", "Título da Tela de Cadastros")) self.dcTitle.setToolTip( self._translate("TfrmBase", "Título da Tela de Cadastros")) self.tabMain.setTabText( self.tabMain.indexOf(self.pgList), self._translate("TfrmBase", "Lista dos Registros")) self.tabMain.setTabToolTip( self.tabMain.indexOf(self.pgList), self._translate("TfrmBase", "Listagem das Ferramentas")) self.tabMain.setTabText( self.tabMain.indexOf(self.pgDetail), self._translate("TfrmBase", "Detalhes do Registro Selecionando")) @property def activeState(self): return self._activeValue @property def activeStateColor(self): return self.activeValue['FG'] @property def activeStateBackgroud(self): return self.activeValue['BG'] @activeState.setter # Seta a Propriedade _activeState def activeState(self, value: int): self.workValue = value self._activeState = value def setScreenState(self, stt: int): self.acExit.setEnabled(self.inBrowse(stt)) # Set Menu Operations Actions self.acSearch.setEnabled((self.inBrowse(stt) or (self.recordCount == 0))) self.acList.setEnabled((self.inBrowse(stt) or (self.recordCount == 0))) self.acInsert.setEnabled(self.inBrowse(stt)) self.acUpdate.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acDelete.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acSave.setEnabled(self.inUpdate(stt)) self.acCancel.setEnabled(self.inUpdate(stt)) # Set Menu Navigation Actions self.acFirst.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acPrior.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acNext.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acLast.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) # Set tab Main if state in Browse enabled self.tabMain.setEnabled(self.inBrowse(stt)) def _layoutWidgets(self): return (self.frmLayout.itemAt(i) for i in range(self.frmLayout.count())) def _getAllFields(self): arrFields = [] for w in self._layoutWidgets(): if (not (isinstance(w, QLabel))): arrFields.append(w) return arrFields def setEnableFields(self, enable: bool = True): # Enable All Fields for controls in self._layoutWidgets(): QWidget(controls).setEnabled(enable) def clearFields(self): # cliar content of all fileds for controls in self._getAllFields(): QWidget(controls).setText('') def setColorFields(self): # cliar content of all fileds style = ".QWidget { backgroud-color: " + self.activeStateBackgroud + "; }" for controls in self._getAllFields(): QWidget(controls).setStyle(style) def showDataDetails(self): # move data of selected record to fileds if (self.tabMain.currentIndex() == 0): self.tabMain.setCurrentIndex(1) def filterRecord(self): raise NotImplementedError(500) def getFirstRecord(self): raise NotImplementedError(500) def getPriorRecord(self): raise NotImplementedError(500) def getNextRecord(self): raise NotImplementedError(500) def getLastRecord(self): raise NotImplementedError(500) def insertRecord(self): raise NotImplementedError(500) def deleteRecord(self): raise NotImplementedError(500) def updateRecord(self): raise NotImplementedError(500) def postRecord(self): raise NotImplementedError(500) def execOpertations(self, state: int): if ((state == self.ssFilter) or (state == self.ssSearchAll)): self.filterRecord() elif (state == self.ssFirst): self.getFirstRecord() elif (state == self.ssPrior): self.getPriorRecord() elif (state == self.ssNext): self.getNextRecord() elif (state == self.ssLast): self.getLastRecord() elif (state == self.ssInsert): self.insertRecord() elif (state == self.ssDelete): self.deleteRecord() elif (state == self.ssUpdate): self.updateRecord() elif (state == self.ssPost): self.postRecord() else: raise NotImplementedError(401, 'Operação não suportada') @pyqtSlot(int) def setFormStatus(self, state: int): if ((state == self.ssSearch) and (self.activeState != state)): self.clearFields() self.setColorFields() self.showDataDetails() if (self.activeState != state): self.activeState = state if (state == self.ssCancel): self.activeState = self.ssBrowse @pyqtSlot(int, int, dict, str) def onStateChange(self, NewState: int, OldState: int, Result: dict = {}, Err: str = ''): try: # show screen state on status bar state = self.getStateProperties(NewState) style = '.QLabel { background-color: ' + state[ 'BG'] + '; color: ' + state['FG'] + '; }' self.sbStatus.setStyleSheet(style) self.lbStatus.setText(state['Descr']) # call operation into child screen self.execOpertations(NewState) # change buttons states self.setScreenState(NewState) # set result status code and result satatus Message self.setResultStatusCode = 200 self.setResultStatusMessage = '' except Exception as e: self.ResultStatusCode = 200 self.ResultStatusMessage = str(e) QMessageBox.critical(self, self.windowTitle(), self.ResultStatusMessage) return self.result @pyqtSlot() def tabMainChanged(self): self.sbStatus.showMessage('TabMain change tabIndex to (' + str(self.tabMain.currentIndex()) + ')!') if (self.tabMain.currentIndex() == 1): self.showDataDetails() @pyqtSlot() def InsertData(self): # self.sbStatus.showMessage('Prepare to insert data....') pass def _setEvents(self): self.tabMain.blockSignals( True) # just for not showing the initial message self.tabMain.currentChanged.connect(self.tabMainChanged) # changed! self.tabMain.blockSignals(False) # wait signals now @pyqtSlot() def closeApp(self): self.close()