class CueGeneralSettings(CueSettingsPage): Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Cue') def __init__(self, cue_class, **kwargs): super().__init__(cue_class, **kwargs) self.setLayout(QVBoxLayout()) self.tabWidget = QTabWidget(self) self.layout().addWidget(self.tabWidget) # TAB 1 (Behaviours) self.tab_1 = QWidget(self.tabWidget) self.tab_1.setLayout(QVBoxLayout()) self.tabWidget.addTab(self.tab_1, '1') # Start-Action self.startActionGroup = QGroupBox(self.tab_1) self.startActionGroup.setLayout(QHBoxLayout()) self.tab_1.layout().addWidget(self.startActionGroup) self.startActionCombo = QComboBox(self.startActionGroup) for action in [CueAction.Start, CueAction.FadeInStart]: if action in cue_class.CueActions: self.startActionCombo.addItem( translate('CueAction', action.name), action.value) self.startActionCombo.setEnabled(self.startActionCombo.count() > 1) self.startActionGroup.layout().addWidget(self.startActionCombo) self.startActionLabel = QLabel(self.startActionGroup) self.startActionLabel.setAlignment(Qt.AlignCenter) self.startActionGroup.layout().addWidget(self.startActionLabel) # Stop-Action self.stopActionGroup = QGroupBox(self.tab_1) self.stopActionGroup.setLayout(QHBoxLayout()) self.tab_1.layout().addWidget(self.stopActionGroup) self.stopActionCombo = QComboBox(self.stopActionGroup) for action in [ CueAction.Stop, CueAction.Pause, CueAction.FadeOutStop, CueAction.FadeOutPause ]: if action in cue_class.CueActions: self.stopActionCombo.addItem( translate('CueAction', action.name), action.value) self.stopActionCombo.setEnabled(self.stopActionCombo.count() > 1) self.stopActionGroup.layout().addWidget(self.stopActionCombo) self.stopActionLabel = QLabel(self.stopActionGroup) self.stopActionLabel.setAlignment(Qt.AlignCenter) self.stopActionGroup.layout().addWidget(self.stopActionLabel) self.tab_1.layout().addSpacing(150) self.tab_1.setEnabled(self.stopActionCombo.isEnabled() and self.startActionCombo.isEnabled()) # TAB 2 (Pre/Post Wait) self.tab_2 = QWidget(self.tabWidget) self.tab_2.setLayout(QVBoxLayout()) self.tabWidget.addTab(self.tab_2, '2') # Pre wait self.preWaitGroup = QGroupBox(self.tab_2) self.preWaitGroup.setLayout(QHBoxLayout()) self.tab_2.layout().addWidget(self.preWaitGroup) self.preWaitSpin = QDoubleSpinBox(self.preWaitGroup) self.preWaitSpin.setMaximum(3600 * 24) self.preWaitGroup.layout().addWidget(self.preWaitSpin) self.preWaitLabel = QLabel(self.preWaitGroup) self.preWaitLabel.setAlignment(Qt.AlignCenter) self.preWaitGroup.layout().addWidget(self.preWaitLabel) # Post wait self.postWaitGroup = QGroupBox(self.tab_2) self.postWaitGroup.setLayout(QHBoxLayout()) self.tab_2.layout().addWidget(self.postWaitGroup) self.postWaitSpin = QDoubleSpinBox(self.postWaitGroup) self.postWaitSpin.setMaximum(3600 * 24) self.postWaitGroup.layout().addWidget(self.postWaitSpin) self.postWaitLabel = QLabel(self.postWaitGroup) self.postWaitLabel.setAlignment(Qt.AlignCenter) self.postWaitGroup.layout().addWidget(self.postWaitLabel) # Next action self.nextActionGroup = QGroupBox(self.tab_2) self.nextActionGroup.setLayout(QHBoxLayout()) self.tab_2.layout().addWidget(self.nextActionGroup) self.nextActionCombo = QComboBox(self.nextActionGroup) for action in CueNextAction: self.nextActionCombo.addItem( translate('CueNextAction', action.name), action.value) self.nextActionGroup.layout().addWidget(self.nextActionCombo) # TAB 3 (Fade In/Out) self.tab_3 = QWidget(self.tabWidget) self.tab_3.setLayout(QVBoxLayout()) self.tabWidget.addTab(self.tab_3, '3') # FadeIn self.fadeInGroup = QGroupBox(self.tab_3) self.fadeInGroup.setEnabled( CueAction.FadeInStart in cue_class.CueActions) self.fadeInGroup.setLayout(QHBoxLayout()) self.tab_3.layout().addWidget(self.fadeInGroup) self.fadeInEdit = FadeEdit(self.fadeInGroup, mode=FadeComboBox.Mode.FadeIn) self.fadeInGroup.layout().addWidget(self.fadeInEdit) # FadeOut self.fadeOutGroup = QGroupBox(self.tab_3) self.fadeOutGroup.setEnabled( CueAction.FadeOutPause in cue_class.CueActions or CueAction.FadeOutStop in cue_class.CueActions) self.fadeOutGroup.setLayout(QHBoxLayout()) self.tab_3.layout().addWidget(self.fadeOutGroup) self.fadeOutEdit = FadeEdit(self.fadeOutGroup, mode=FadeComboBox.Mode.FadeOut) self.fadeOutGroup.layout().addWidget(self.fadeOutEdit) self.retranslateUi() def retranslateUi(self): # Tabs self.tabWidget.setTabText(0, translate('CueSettings', 'Behaviours')) self.tabWidget.setTabText(1, translate('CueSettings', 'Pre/Post Wait')) self.tabWidget.setTabText(2, translate('CueSettings', 'Fade In/Out')) # Start-Action self.startActionGroup.setTitle(translate('CueSettings', 'Start action')) self.startActionLabel.setText( translate('CueSettings', 'Default action to start the cue')) # Stop-Action self.stopActionGroup.setTitle(translate('CueSettings', 'Stop action')) self.stopActionLabel.setText( translate('CueSettings', 'Default action to stop the cue')) # PreWait self.preWaitGroup.setTitle(translate('CueSettings', 'Pre wait')) self.preWaitLabel.setText( translate('CueSettings', 'Wait before cue execution')) # PostWait self.postWaitGroup.setTitle(translate('CueSettings', 'Post wait')) self.postWaitLabel.setText( translate('CueSettings', 'Wait after cue execution')) # NextAction self.nextActionGroup.setTitle(translate('CueSettings', 'Next action')) # FadeIn/Out self.fadeInGroup.setTitle(translate('FadeSettings', 'Fade In')) self.fadeOutGroup.setTitle(translate('FadeSettings', 'Fade Out')) def load_settings(self, settings): self.startActionCombo.setCurrentText( translate('CueAction', settings.get('default_start_action', ''))) self.stopActionCombo.setCurrentText( translate('CueAction', settings.get('default_stop_action', ''))) self.preWaitSpin.setValue(settings.get('pre_wait', 0)) self.postWaitSpin.setValue(settings.get('post_wait', 0)) self.nextActionCombo.setCurrentText( translate('CueNextAction', settings.get('next_action', ''))) self.fadeInEdit.setFadeType(settings.get('fadein_type', '')) self.fadeInEdit.setDuration(settings.get('fadein_duration', 0)) self.fadeOutEdit.setFadeType(settings.get('fadeout_type', '')) self.fadeOutEdit.setDuration(settings.get('fadeout_duration', 0)) def enable_check(self, enable): self.startActionGroup.setCheckable(enable) self.startActionGroup.setChecked(False) self.stopActionGroup.setCheckable(enable) self.stopActionGroup.setChecked(False) self.preWaitGroup.setCheckable(enable) self.preWaitGroup.setChecked(False) self.postWaitGroup.setCheckable(enable) self.postWaitGroup.setChecked(False) self.nextActionGroup.setCheckable(enable) self.nextActionGroup.setChecked(False) self.fadeInGroup.setCheckable(enable) self.fadeInGroup.setChecked(False) self.fadeOutGroup.setCheckable(enable) self.fadeOutGroup.setChecked(False) def get_settings(self): conf = {} checkable = self.preWaitGroup.isCheckable() if not (checkable and not self.startActionGroup.isChecked()): if self.startActionCombo.isEnabled(): conf[ 'default_start_action'] = self.startActionCombo.currentData( ) if not (checkable and not self.stopActionGroup.isChecked()): if self.stopActionCombo.isEnabled(): conf['default_stop_action'] = self.stopActionCombo.currentData( ) if not (checkable and not self.preWaitGroup.isChecked()): conf['pre_wait'] = self.preWaitSpin.value() if not (checkable and not self.postWaitGroup.isChecked()): conf['post_wait'] = self.postWaitSpin.value() if not (checkable and not self.nextActionGroup.isChecked()): conf['next_action'] = self.nextActionCombo.currentData() if not (checkable and not self.fadeInGroup.isChecked()): conf['fadein_type'] = self.fadeInEdit.fadeType() conf['fadein_duration'] = self.fadeInEdit.duration() if not (checkable and not self.fadeInGroup.isChecked()): conf['fadeout_type'] = self.fadeOutEdit.fadeType() conf['fadeout_duration'] = self.fadeOutEdit.duration() return conf
class PostProcessGUI(QDialog): def __init__(self, parent_window=None, _is_dialog=False): super().__init__(parent=parent_window) self._is_dialog = _is_dialog self.initUI() def initUI(self): input_v_layout = QVBoxLayout() input_v_layout.setAlignment(Qt.AlignTop) input_v_layout.setContentsMargins(0, 0, 0, 0) # TODO add a set of parameters there for the post process self.groupBox_post_process = QGroupBox( 'Refine segmentation/Create a binary mask', objectName='groupBox_post_process') self.groupBox_post_process.setCheckable(True) self.groupBox_post_process.setChecked(True) # self.groupBox_post_process.setEnabled(True) group_box_post_process_parameters_layout = QGridLayout() group_box_post_process_parameters_layout.setAlignment(Qt.AlignTop) group_box_post_process_parameters_layout.setHorizontalSpacing(3) group_box_post_process_parameters_layout.setVerticalSpacing(3) # do a radio dialog with all the stuff needed... # test all post_process_method_selection_label = QLabel('Post process method') # (or bond score for pretrained model) post_process_method_selection_label.setStyleSheet("QLabel { color : red; }") self.post_process_method_selection = QComboBox(objectName='post_process_method_selection') self.post_process_method_selection.addItem('Default (Slow/robust) (EPySeg pre-trained model only!)') self.post_process_method_selection.addItem('Fast (May contain more errors) (EPySeg pre-trained model only!)') self.post_process_method_selection.addItem('Old method (Sometimes better than default) (EPySeg pre-trained model only!)') self.post_process_method_selection.addItem('Simply binarize output using threshold') self.post_process_method_selection.addItem('Keep first channel only') self.post_process_method_selection.addItem('None (Raw model output)') self.post_process_method_selection.currentTextChanged.connect(self._post_process_method_changed) group_box_post_process_parameters_layout.addWidget(post_process_method_selection_label, 0, 0, 1, 1) group_box_post_process_parameters_layout.addWidget(self.post_process_method_selection, 0, 1, 1, 3) # TODO --> always make this relative threshold_label = QLabel( 'Threshold: (in case of over/under segmentation, please increase/decrease, respectively)') # (or bond score for pretrained model) threshold_label.setStyleSheet("QLabel { color : red; }") self.threshold_bond_or_binarisation = QDoubleSpinBox(objectName='threshold_bond_or_binarisation') self.threshold_bond_or_binarisation.setSingleStep(0.01) self.threshold_bond_or_binarisation.setRange(0.01, 1) # 100_000 makes no sense (oom) but anyway self.threshold_bond_or_binarisation.setValue(0.42) # probably should be 1 to 3 depending on the tissue self.threshold_bond_or_binarisation.setEnabled(False) # threshold_hint = QLabel() # (or bond score for pretrained model) self.autothreshold = QCheckBox("Auto",objectName='autothreshold') self.autothreshold.setChecked(True) self.autothreshold.stateChanged.connect(self._threshold_changed) group_box_post_process_parameters_layout.addWidget(threshold_label, 1, 0, 1, 2) group_box_post_process_parameters_layout.addWidget(self.threshold_bond_or_binarisation, 1, 2) group_box_post_process_parameters_layout.addWidget(self.autothreshold, 1, 3) # groupBox_post_process_parameters_layout.addWidget(threshold_hint, 0, 3) filter_by_size_label = QLabel('Further filter segmentation by size:') self.filter_by_cell_size_combo = QComboBox(objectName='filter_by_cell_size_combo') self.filter_by_cell_size_combo.addItem('None (quite often the best choice)') self.filter_by_cell_size_combo.addItem('Local median (slow/very good) divided by') self.filter_by_cell_size_combo.addItem('Cells below Average area (global) divided by') self.filter_by_cell_size_combo.addItem('Global median divided by') self.filter_by_cell_size_combo.addItem('Cells below size (in px)') # add a listener to model Architecture self.filter_by_cell_size_combo.currentTextChanged.connect(self._filter_changed) group_box_post_process_parameters_layout.addWidget(filter_by_size_label, 2, 0) group_box_post_process_parameters_layout.addWidget(self.filter_by_cell_size_combo, 2, 1, 1, 2) self.avg_area_division_or_size_spinbox = QSpinBox(objectName='avg_area_division_or_size_spinbox') self.avg_area_division_or_size_spinbox.setSingleStep(1) self.avg_area_division_or_size_spinbox.setRange(1, 10000000) # 100_000 makes no sense (oom) but anyway self.avg_area_division_or_size_spinbox.setValue(2) # probably should be 1 to 3 depending on the tissue self.avg_area_division_or_size_spinbox.setEnabled(False) group_box_post_process_parameters_layout.addWidget(self.avg_area_division_or_size_spinbox, 2, 3) self.prevent_exclusion_of_too_many_cells_together = QCheckBox('Do not exclude groups bigger than', objectName='prevent_exclusion_of_too_many_cells_together') self.prevent_exclusion_of_too_many_cells_together.setChecked(False) self.prevent_exclusion_of_too_many_cells_together.setEnabled(False) # max_nb_of_cells_to_be_excluded_together_label = QLabel('Group size') self.max_nb_of_cells_to_be_excluded_together_spinbox = QSpinBox(objectName='max_nb_of_cells_to_be_excluded_together_spinbox') self.max_nb_of_cells_to_be_excluded_together_spinbox.setSingleStep(1) self.max_nb_of_cells_to_be_excluded_together_spinbox.setRange(1, 10000000) # max makes no sense self.max_nb_of_cells_to_be_excluded_together_spinbox.setValue( 3) # default should be 2 or 3 because seg is quite good so above makes no sense self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(False) cells_text_labels = QLabel('cells') self.restore_secure_cells = QCheckBox('Restore most likely cells',objectName='restore_secure_cells') self.restore_secure_cells.setChecked(False) self.restore_secure_cells.setEnabled(False) # help for post process # help_ico = QIcon.fromTheme('help-contents') self.help_button_postproc = QPushButton('?', None) bt_width = self.help_button_postproc.fontMetrics().boundingRect(self.help_button_postproc.text()).width() + 7 self.help_button_postproc.setMaximumWidth(bt_width * 2) self.help_button_postproc.clicked.connect(self.show_tip) group_box_post_process_parameters_layout.addWidget(self.restore_secure_cells, 3, 0) group_box_post_process_parameters_layout.addWidget(self.prevent_exclusion_of_too_many_cells_together, 3, 1) group_box_post_process_parameters_layout.addWidget(self.max_nb_of_cells_to_be_excluded_together_spinbox, 3, 2) group_box_post_process_parameters_layout.addWidget(cells_text_labels, 3, 3) # TODO --> improve layout to make help button smaller group_box_post_process_parameters_layout.addWidget(self.help_button_postproc, 0, 5, 3, 1) self.groupBox_post_process.setLayout(group_box_post_process_parameters_layout) input_v_layout.addWidget(self.groupBox_post_process) self.setLayout(input_v_layout) if self._is_dialog: # OK and Cancel buttons self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.layout().addWidget(self.buttons) def _threshold_changed(self): self.threshold_bond_or_binarisation.setEnabled(not self.autothreshold.isChecked()) # self.post_process_method_selection.addItem('Default (Slow/robust)') # self.post_process_method_selection.addItem('Fast (May contain more errors)') # self.post_process_method_selection.addItem('Old method (Less constant than default but sometimes better)') # self.post_process_method_selection.addItem('Simply binarize output using threshold') # self.post_process_method_selection.addItem('None (Raw model output)') def _post_process_method_changed(self): text = self.post_process_method_selection.currentText().lower() if 'none' in text or 'first' in text: self.set_threshold_enabled(False) self.set_safety_parameters(False) self.set_filter_by_size_enabled(False) elif 'simply' in text: self.set_threshold_enabled(True) self.set_safety_parameters(False) self.set_filter_by_size_enabled(False) elif 'old' in text: self.set_threshold_enabled(False) self.set_safety_parameters(True) self.set_filter_by_size_enabled(True) else: self.set_threshold_enabled(True) self.set_safety_parameters(False) self.set_filter_by_size_enabled(True) def set_filter_by_size_enabled(self, bool): if bool is False: self.filter_by_cell_size_combo.setEnabled(False) self.avg_area_division_or_size_spinbox.setEnabled(False) else: self.filter_by_cell_size_combo.setEnabled(True) self.avg_area_division_or_size_spinbox.setEnabled(True) def set_threshold_enabled(self, bool): if bool is False: self.autothreshold.setEnabled(False) self.threshold_bond_or_binarisation.setEnabled(False) else: self.autothreshold.setEnabled(True) self._threshold_changed() def set_safety_parameters(self, bool): self._filter_changed() def show_tip(self): QToolTip.showText(self.sender().mapToGlobal(QPoint(30, 30)), markdown_file_to_html('refine_segmentation.md')) def isChecked(self): return self.groupBox_post_process.isChecked() def setChecked(self, bool): return self.groupBox_post_process.setChecked(bool) def _filter_changed(self): current_filter = self.filter_by_cell_size_combo.currentText().lower() current_mode = self.post_process_method_selection.currentText().lower() if 'one' in current_filter: self.avg_area_division_or_size_spinbox.setEnabled(False) self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(False) self.prevent_exclusion_of_too_many_cells_together.setEnabled(False) self.restore_secure_cells.setEnabled(False) else: self.avg_area_division_or_size_spinbox.setEnabled(True) self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(True) self.prevent_exclusion_of_too_many_cells_together.setEnabled(True) self.restore_secure_cells.setEnabled(True) if 'divided' in current_filter: self.avg_area_division_or_size_spinbox.setValue(2) else: self.avg_area_division_or_size_spinbox.setValue(300) if not 'old' in current_mode: self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(False) self.prevent_exclusion_of_too_many_cells_together.setEnabled(False) self.restore_secure_cells.setEnabled(False) def _get_post_process_filter(self): current_filter = self.filter_by_cell_size_combo.currentText().lower() if 'one' in current_filter or not self.filter_by_cell_size_combo.isEnabled(): return None if 'size' in current_filter: return self.avg_area_division_or_size_spinbox.value() if 'verage' in current_filter: return 'avg' if 'local' in current_filter: return 'local' if 'global' in current_filter: return 'global median' def get_parameters_directly(self): '''Get the parameters for model training Returns ------- dict containing post processing parameters ''' self.post_process_parameters = {} post_proc_method = self.post_process_method_selection.currentText().lower() if 'none' in post_proc_method: self.post_process_parameters['post_process_algorithm'] = None else: self.post_process_parameters['post_process_algorithm'] = post_proc_method self.post_process_parameters['filter'] = self._get_post_process_filter() if self.threshold_bond_or_binarisation.isEnabled(): self.post_process_parameters['threshold'] = self.threshold_bond_or_binarisation.value() if self.autothreshold.isEnabled() and self.autothreshold.isChecked(): self.post_process_parameters[ 'threshold'] = None # None means autothrehsold # maybe add more options some day if self.avg_area_division_or_size_spinbox.isEnabled(): self.post_process_parameters['correction_factor'] = self.avg_area_division_or_size_spinbox.value() if self.restore_secure_cells.isEnabled(): self.post_process_parameters['restore_safe_cells'] = self.restore_secure_cells.isChecked() if self.max_nb_of_cells_to_be_excluded_together_spinbox.isEnabled(): self.post_process_parameters[ 'cutoff_cell_fusion'] = self.max_nb_of_cells_to_be_excluded_together_spinbox.value() if self.prevent_exclusion_of_too_many_cells_together.isChecked() else None if 'old' in self.post_process_method_selection.currentText().lower(): # just for max use that --> maybe do this as an option some day self.post_process_parameters['hq_predictions'] = 'max' return self.post_process_parameters def get_parameters(self): return (self.get_parameters_directly()) @staticmethod def getDataAndParameters(parent_window=None, _is_dialog=False): # get all the params for augmentation dialog = PostProcessGUI(parent_window=parent_window, _is_dialog=_is_dialog) result = dialog.exec_() parameters = dialog.get_parameters() return (parameters, result == QDialog.Accepted)
class PictureEditor(QFrame): picture_data: Picture pic_path: str='./' def __init__(self, parent, picture_parameter: dict): QFrame.__init__(self, parent, flags=Qt.FramelessWindowHint) self.buffer = parent.buffer self.picture_parameter = picture_parameter self.picture_label = QLabel() self.resize_multiple = 1 scroll = QScrollArea(self) scroll.setWidget(self.picture_label) scroll.setObjectName('picture') self.name_combo = QComboBox(self) self.index_combo = QComboBox(self) self.palette_combo = QComboBox(self) self.resize_combo = QComboBox(self) self.name_combo.setFixedWidth(120) self.index_combo.setFixedWidth(120) self.palette_combo.setFixedWidth(120) self.resize_combo.setFixedWidth(120) self.name_combo.setItemDelegate(QStyledItemDelegate()) self.index_combo.setItemDelegate(QStyledItemDelegate()) self.palette_combo.setItemDelegate(QStyledItemDelegate()) self.resize_combo.setItemDelegate(QStyledItemDelegate()) self.resize_combo.addItems([f' × {i + 1}' for i in range(4)]) self.name_combo.currentTextChanged[str].connect(self.name_change) self.index_combo.currentIndexChanged.connect(self.refresh_data) self.palette_combo.currentIndexChanged.connect(self.refresh_data) self.resize_combo.currentIndexChanged.connect(self.refresh_data) self.name_combo.addItems(picture_parameter) output_button = QPushButton('導出圖片') input_button = QPushButton('導入圖片') output_button.clicked.connect(self.output_picture) input_button.clicked.connect(self.input_picture) control_layout = QVBoxLayout() control_layout.addWidget(QLabel('選擇圖片'), alignment=Qt.AlignLeft) control_layout.addWidget(self.name_combo, alignment=Qt.AlignRight) control_layout.addWidget(QLabel('選擇編號'), alignment=Qt.AlignLeft) control_layout.addWidget(self.index_combo, alignment=Qt.AlignRight) control_layout.addWidget(QLabel('選擇色板'), alignment=Qt.AlignLeft) control_layout.addWidget(self.palette_combo, alignment=Qt.AlignRight) control_layout.addWidget(QLabel('缩放比例'), alignment=Qt.AlignLeft) control_layout.addWidget(self.resize_combo, alignment=Qt.AlignRight) control_layout.addWidget(output_button, alignment=Qt.AlignRight) control_layout.addWidget(input_button, alignment=Qt.AlignRight) control_layout.addStretch() layout = QHBoxLayout() layout.addLayout(control_layout) layout.addWidget(scroll) self.setLayout(layout) def name_change(self, name: str): picture_setting = DATA_PARAMETER.get('圖片_' + name) self.index_combo.disconnect() self.index_combo.clear() self.index_combo.addItems([f'{i+1:>13d}' for i in range(picture_setting['quantity']['normal_quantity'])]) self.index_combo.currentIndexChanged.connect(self.refresh_data) if self.picture_parameter.get(name): palette_setting = PALETTE_PARAMETER.get('色板_' + self.picture_parameter.get(name)) else: palette_setting = () self.palette_combo.disconnect() self.palette_combo.clear() if palette_setting: self.palette_combo.addItems([f'0x{address:08X}' for address in palette_setting]) self.palette_combo.setEnabled(True) else: self.palette_combo.setEnabled(False) self.palette_combo.currentIndexChanged.connect(self.refresh_data) self.refresh_data() def refresh_data(self): picture_setting = DATA_PARAMETER.get('圖片_' + self.name_combo.currentText()) self.picture_data = Picture(self, **picture_setting) if self.palette_combo.isEnabled(): self.picture_data.palette.set_normal_offset(int(self.palette_combo.currentText(), 16)) self.resize_multiple = self.resize_combo.currentIndex() + 1 self.set_picture() def set_picture(self): picture = self.picture_data.get_data(self.index_combo.currentIndex()) width = self.picture_data.width * self.resize_multiple height = self.picture_data.height * self.resize_multiple self.picture_label.setFixedSize(width, height) self.picture_label.setPixmap(picture.resize((width, height)).toqpixmap()) def output_picture(self): filename = QFileDialog.getSaveFileName(self, '导出图片', self.pic_path, 'BMP图像(*.bmp)')[0] if filename: self.pic_path = filename[0: filename.rfind('/') + 1] if self.index_combo.isEnabled(): picture = self.picture_data.get_data(self.index_combo.currentIndex()) else: picture = self.picture_data.get_data(0) picture.save(filename) def input_picture(self): filename = QFileDialog.getOpenFileName(self, '导入图片', self.pic_path, '*.bmp;;*.png;;*.gif;;*.tif')[0] if filename: self.pic_path = filename[0: filename.rfind('/') + 1] width = self.picture_data.width * self.resize_multiple height = self.picture_data.height * self.resize_multiple picture = Image.open(filename).resize((width, height)) self.picture_data.set_data(self.index_combo.currentIndex(), picture) self.set_picture()
class finance_edit(QWidget): def __init__(self, home_button: QPushButton, parent=None): super().__init__(parent) self.presetting = False intValidator = QIntValidator() self.setStyleSheet(QSS) self.current_pending_months = [] self.current_advance_months = [] self.home_button = home_button self.save_button = None # -- LEVEL TWO GRID self.grid = QGridLayout() self.grid.setSpacing(20) self.setLayout(self.grid) # -- CELL ZERO self.receipt_label = QLabel("RECEIPT ID :") self.receipt_label.setWordWrap(True) self.receipt_id = QLineEdit() self.receipt_id.setStyleSheet("font: bold") self.receipt_id.setAlignment(Qt.AlignCenter) self.receipt_id.setToolTip("Enter a valid Receipt-ID") self.print_button = QPushButton("PRINT") self.print_button.setFixedWidth(122) self.print_button.setToolTip("Get Receipt PDF") self.resend_button = QPushButton("RESEND") self.resend_button.setToolTip("Send a copy via mail to the member") self.edit_button = QPushButton("EDIT") self.edit_button.setToolTip("Edit the details of this record") self.buttons_layout = QHBoxLayout() self.buttons_layout.addWidget(self.resend_button) self.buttons_layout.addWidget(self.edit_button) self.buttons_layout.setSpacing(5) # --- self.flat_label = QLabel("FLAT NO. :") self.flat_combo = QComboBox() model = self.flat_combo.model() for flat in flats: model.appendRow(QStandardItem(flat)) self.flat_combo.setStyleSheet( 'text-color: black; selection-background-color: rgb(215,215,215)') self.flat_combo.currentIndexChanged['QString'].connect(self.set_name) # --- self.name_label = QLabel("NAME :") self.name_value = QLabel() self.name_value.setFixedWidth(200) self.name_value.setText("Mr D. S. Patil") # --- self.finance_entry_layout0 = QFormLayout() self.finance_entry_layout0.addRow(self.receipt_label, self.receipt_id) self.finance_entry_layout0.addRow(self.print_button, self.buttons_layout) self.finance_entry_layout0.addRow(self.flat_label, self.flat_combo) self.finance_entry_layout0.addRow(self.name_label, self.name_value) self.finance_entry_layout0.setVerticalSpacing(90) # -- CELL ONE self.date_label = QLabel("DATE OF TRANSACTION :") self.date_label.setWordWrap(True) self.date_line = QDateEdit() self.date_line.setCalendarPopup(True) self.date_line.setDate(QDate.currentDate()) self.date_line.setDisplayFormat("dd MMMM, yyyy") self.single_radio = QRadioButton("Single Month") self.multiple_radio = QRadioButton("Multiple Months") self.single_radio.setChecked(True) self.single_radio.toggled.connect( lambda: self.months(button=self.single_radio)) self.multiple_radio.toggled.connect( lambda: self.months(button=self.multiple_radio)) self.finance_entry_layout1_h1 = QHBoxLayout() self.finance_entry_layout1_h1.addWidget(self.date_line) self.finance_entry_layout1_h1.addWidget(self.single_radio) self.finance_entry_layout1_h1.addWidget(self.multiple_radio) self.finance_entry_layout1_h1.setSpacing(90) # --- self.month_label = QLabel("FEES FOR :") self.month_combo = QComboBox() self.set_pending_months() self.month_combo.setStyleSheet( 'text-color: black; selection-background-color: rgb(215,215,215)') # --- self.month_till_label = QLabel("FEES TILL :") self.month_till_label.setAlignment(Qt.AlignCenter) self.month_till_combo = QComboBox() self.set_advance_months() self.month_till_combo.setStyleSheet( 'text-color: black; selection-background-color: rgb(215,215,215)') self.finance_entry_layout1_h2 = QHBoxLayout() self.finance_entry_layout1_h2.addWidget(self.month_combo) self.finance_entry_layout1_h2.addWidget(self.month_till_label) self.finance_entry_layout1_h2.addWidget(self.month_till_combo) self.finance_entry_layout1_h2.setSpacing(90) self.month_till_label.setEnabled(False) self.month_till_combo.setEnabled(False) # --- self.amount_label = QLabel("AMOUNT :") self.amount_label.setAlignment(Qt.AlignCenter) self.amount_line = QLineEdit() self.amount_line.setText("1500") self.amount_line.setValidator(intValidator) # --- self.fine_label = QLabel("FINE :") self.fine_label.setAlignment(Qt.AlignCenter) self.fine_line = QLineEdit() self.fine_line.setText("0") self.fine_line.setValidator(intValidator) self.fine_line.setStyleSheet("border: 1px solid red; color: red") self.finance_entry_layout1_h3 = QHBoxLayout() self.finance_entry_layout1_h3.addWidget(self.amount_line) self.finance_entry_layout1_h3.addWidget(self.fine_label) self.finance_entry_layout1_h3.addWidget(self.fine_line) self.finance_entry_layout1_h3.setSpacing(90) # --- self.finance_entry_layout1 = QFormLayout() self.finance_entry_layout1.addRow(self.date_label, self.finance_entry_layout1_h1) self.finance_entry_layout1.addRow(self.month_label, self.finance_entry_layout1_h2) self.finance_entry_layout1.addRow(self.amount_label, self.finance_entry_layout1_h3) self.finance_entry_layout1.setVerticalSpacing(90) # -- CELL TWO self.mode_label = QLabel("PAYMENT MODE :") self.mode_label.setWordWrap(True) # --- self.mode_combo = QComboBox() self.mode_combo.addItem("Cash") self.mode_combo.addItem("Online Funds Transfer") self.mode_combo.addItem("Cheque") self.mode_combo.setStyleSheet( 'text-color: black; selection-background-color: rgb(215,215,215)') self.mode_combo.currentIndexChanged['QString'].connect( self.mode_selection) # --- self.ref_label = QLabel("REFERENCE ID :") self.ref_label.setWordWrap(True) self.ref_line = QLineEdit() self.ref_label.setDisabled(True) self.ref_line.setDisabled(True) self.total_label = QLabel( f"TOTAL PAYABLE AMOUNT : {int(self.amount_line.text()) + int(self.fine_line.text())}" ) self.total_label.setWordWrap(True) self.total_label.setAlignment(Qt.AlignCenter) self.save_button = QPushButton("UPDATE AND RESEND RECEIPT") self.save_button.setStyleSheet("font: bold") self.save_button.clicked.connect(lambda: self.check_form()) self.status = QLabel("Ready") self.status.setStyleSheet("font: 8pt") self.status.setAlignment(Qt.AlignCenter) self.bar = QProgressBar(self) self.bar.setValue(0) self.bar.setMaximum(100) self.bar.setToolTip("Please wait until the process is completed.") # --- self.finance_entry_layout2 = QFormLayout() self.finance_entry_layout2.addRow(self.mode_label, self.mode_combo) self.finance_entry_layout2.addRow(self.ref_label, self.ref_line) self.finance_entry_layout2.addItem(create_spacer_item(w=5, h=30)) self.finance_entry_layout2.addRow(self.total_label) self.finance_entry_layout2.addRow(self.save_button) self.finance_entry_layout2.addRow(self.status) self.finance_entry_layout2.addRow(self.bar) self.finance_entry_layout2.setVerticalSpacing(50) self.finance_entry_group0 = QGroupBox() self.finance_entry_group0.setLayout(self.finance_entry_layout0) self.finance_entry_group1 = QGroupBox() self.finance_entry_group1.setLayout(self.finance_entry_layout1) self.finance_entry_group2 = QGroupBox() self.finance_entry_group2.setLayout(self.finance_entry_layout2) self.finance_entry_group2.setFixedWidth(550) # -- FUNCTIONALITY: self.date_line.dateChanged.connect(lambda: self.set_pending_months( date=str(self.date_line.date().toPyDate()))) self.month_combo.currentIndexChanged['QString'].connect( self.set_advance_months) self.month_combo.currentIndexChanged['QString'].connect( lambda ind: self.calculate_fine('from', ind)) self.month_till_combo.currentIndexChanged['QString'].connect( lambda ind: self.calculate_fine('till', ind)) self.month_combo.currentTextChanged.connect(self.calculate_amount) self.month_till_combo.currentTextChanged.connect(self.calculate_amount) self.amount_line.textChanged.connect(self.set_total) self.fine_line.textChanged.connect(self.set_total) self.edit_button.clicked.connect(self.enable_edit) self.print_button.clicked.connect(self.print_receipt) self.resend_button.clicked.connect(self.resend_receipt) self.receipt_id.textChanged['QString'].connect( lambda receipt_id: self.get_receipt(receipt_id)) # -- FINANCE ENTRY GRID self.grid.addWidget(self.finance_entry_group0, 0, 0, 2, 1) self.grid.addWidget(self.finance_entry_group1, 0, 1, 2, 1) self.grid.addWidget(self.finance_entry_group2, 0, 2, 2, 1) self.save_button.setEnabled(False) self.print_button.setEnabled(False) self.resend_button.setEnabled(False) self.edit_button.setEnabled(False) self.flat_label.setEnabled(False) self.flat_combo.setEnabled(False) self.name_label.setEnabled(False) self.name_value.setEnabled(False) self.finance_entry_group1.setEnabled(False) self.finance_entry_group2.setEnabled(False) def get_receipt(self, receipt_id: str): data = db_tools.get_from_db(table='records', attribute='*', key='receipt_id', value=receipt_id) if len(data) > 0: self.presetting = True data = data[0] receipt_date = data[1].split("/") flat = data[2] fee_month = data[3] fee_till = data[4] amount = data[5] fine = data[6] mode = data[7] ref = data[8] date_to_set = QDate(int(receipt_date[2]), int(receipt_date[1]), int(receipt_date[0])) fee_month = fee_month.split("/") fee_month_to_set = f"{fee_month[0]} '{fee_month[1]}" self.date_line.setDate(date_to_set) self.flat_combo.setCurrentText(flat) self.month_combo.setCurrentText(fee_month_to_set) self.amount_line.setText(f"{amount}") self.fine_line.setText(f"{fine}") self.mode_combo.setCurrentText(mode) self.ref_line.setText(ref) self.total_label.setText(f"TOTAL PAYABLE AMOUNT : {amount + fine}") if fee_till != "-": fee_till = fee_till.split("/") fee_till_to_set = f"{fee_till[0]} '{fee_till[1]}" self.multiple_radio.setChecked(True) self.single_radio.setChecked(False) self.month_till_combo.setCurrentText(fee_till_to_set) else: self.multiple_radio.setChecked(False) self.single_radio.setChecked(True) if mode == "Cash": self.ref_label.setEnabled(False) self.ref_line.setEnabled(False) self.presetting = False self.print_button.setEnabled(True) self.resend_button.setEnabled(True) self.edit_button.setEnabled(True) else: self.print_button.setEnabled(False) self.resend_button.setEnabled(False) self.edit_button.setEnabled(False) self.flat_label.setEnabled(False) self.flat_combo.setEnabled(False) self.name_label.setEnabled(False) self.name_value.setEnabled(False) self.finance_entry_group1.setEnabled(False) self.finance_entry_group2.setEnabled(False) def enable_edit(self): self.flat_label.setEnabled(True) self.flat_combo.setEnabled(True) self.name_label.setEnabled(True) self.name_value.setEnabled(True) self.finance_entry_group1.setEnabled(True) self.finance_entry_group2.setEnabled(True) def months(self, button): if button.isChecked(): if button.text() == "Single Month": self.month_till_label.setEnabled(False) self.month_till_combo.setEnabled(False) elif button.text() == "Multiple Months": self.month_till_label.setEnabled(True) self.month_till_combo.setEnabled(True) self.calculate_amount() def mode_selection(self, selection): if selection == "Cash": self.ref_label.setText("REFERENCE ID :") self.ref_label.setDisabled(True) self.ref_line.setDisabled(True) elif selection == "Cheque": self.ref_label.setText("CHEQUE NO. :") self.ref_label.setDisabled(False) self.ref_line.setDisabled(False) elif selection == "Online Funds Transfer": self.ref_label.setText("REFERENCE ID :") self.ref_label.setDisabled(False) self.ref_line.setDisabled(False) if self.save_button is not None: self.save_button.setEnabled(True) def check_form(self): reply = QMessageBox() if len(self.amount_line.text()) == 0: reply.setIcon(QMessageBox.Warning) reply.setText("AMOUNTS field cannot be left empty.") reply.setStandardButtons(QMessageBox.Retry) reply.setWindowTitle("INVALID ENTRY") reply.exec_() elif len(self.fine_line.text()) == 0: reply.setIcon(QMessageBox.Warning) reply.setText("FINE field cannot be left empty.") reply.setStandardButtons(QMessageBox.Retry) reply.setWindowTitle("INVALID ENTRY") reply.exec_() elif self.ref_line.isEnabled() and len(self.ref_line.text()) == 0: reply.setIcon(QMessageBox.Warning) reply.setText( "Please enter the REFERENCE INFORMATION for the transaction.") reply.setStandardButtons(QMessageBox.Retry) reply.setWindowTitle("INVALID ENTRY") reply.exec_() else: code, ok = QInputDialog().getText( self, "Security", "Enter the authentication code:") if verify_code(code) and ok: print("Verified") if self.month_till_combo.isEnabled(): fee_till = f" - {self.month_till_combo.currentText()}" else: fee_till = '' if self.ref_line.isEnabled(): ref = f" - ({self.ref_line.text()})" else: ref = '' detailed_text = f"Date : {fix_date(str(self.date_line.date().toPyDate()))}\n" \ f"Fee for : {str(self.month_combo.currentText())}{fee_till}\n" \ f"Flat No : {str(self.flat_combo.currentText())}\n" \ f"Amount : {float(self.amount_line.text())}\n" \ f"Fine : {float(self.fine_line.text())}\n" \ f" -> TOTAL : {float(self.amount_line.text()) + float(self.fine_line.text())} <-\n" \ f"Payment Mode : {str(self.mode_combo.currentText())}{ref}" reply.setWindowTitle("SUCCESSFUL ENTRY") reply.setIcon(QMessageBox.Information) reply.setText("ENTRY HAS BEEN RECORDED.\n") reply.setInformativeText("Please confirm the details below.") reply.setDetailedText(detailed_text) confirm_button = reply.addButton('Confirm', QMessageBox.AcceptRole) edit_button = reply.addButton('Edit', QMessageBox.RejectRole) confirm_button.clicked.connect( lambda: self.final_clicked(button=confirm_button)) edit_button.clicked.connect( lambda: self.final_clicked(button=edit_button)) reply.exec_() else: reply.setIcon(QMessageBox.Critical) reply.setText( "Transaction cannot be edited without the valid code.") reply.setWindowTitle("INVALID Code") reply.exec_() def set_name(self, flat): name = get_name(flat) self.name_value.setText(str(name)) if self.save_button is not None: self.save_button.setEnabled(True) def update_entry(self): receipt_id = self.receipt_id.text() date = str(self.date_line.date().toPyDate()) fee_month = str(self.month_combo.currentText()) if self.month_till_combo.isEnabled(): fee_till = self.month_till_combo.currentText() month = f"{fee_month}-{fee_till}" else: fee_till = '' month = fee_month flat = str(self.flat_combo.currentText()) amount = float(self.amount_line.text()) fine = float(self.fine_line.text()) mode = str(self.mode_combo.currentText()) if self.ref_line.isEnabled(): ref = self.ref_line.text() else: ref = '' new_receipt = receipt.receipt(date=fix_date(date), flat=flat, month=fee_month, month_till=fee_till, amount=amount, fine=fine, mode=mode, ref=ref) self.disable() self.total_label.setText( "Confirming.. PLEASE DO NOT PERFORM ANY OTHER OPERATIONS.") new_receipt.update_db(receipt_id=self.receipt_id.text()) self.status.setText("Adding to databse & Generating receipt") self.bar.setValue(40) design_receipt(receipt_id=f"{receipt_id}") self.status.setText( "Sending the receipt to the member. (Depends on your internet connection)" ) self.bar.setValue(75) email_status = send_receipt(flat=flat, month=month, updated=True) self.enable() self.set_total() if email_status: self.bar.setValue(100) else: reply = QMessageBox() reply.setIcon(QMessageBox.Warning) reply.setText( "The system cannot access the internet. Make sure you have an active connection, or any firewall" "feature blocking the access.") reply.setStandardButtons(QMessageBox.Retry) reply.setWindowTitle("INTERNET") reply.exec_() def final_clicked(self, button): if button.text() == "Confirm": print("in confirm") self.update_entry() self.receipt_id.setText("") self.receipt_id.setFocus() self.flat_combo.setCurrentIndex(0) self.name_value.setText("") self.amount_line.setText('0') self.fine_line.setText('0') self.bar.setValue(0) self.status.setText("Done !") self.ref_line.clear() def set_pending_months(self, date: str = None): if not self.presetting: if date is None: date = str(self.date_line.date().toPyDate()) months = calculate_months(month=date, pending=True, advance=False) self.current_pending_months = months self.month_combo.clear() model = self.month_combo.model() for month in months: model.appendRow(QStandardItem(month)) if self.save_button is not None: self.save_button.setEnabled(True) def set_advance_months(self, date: str = None): if not self.presetting: if date is None or date == '': date = fix_date_back(self.current_pending_months[0]) else: date = fix_date_back(date) months = calculate_months(month=date, pending=False, advance=True) self.current_advance_months = months self.month_till_combo.clear() model = self.month_till_combo.model() for month in months: model.appendRow(QStandardItem(month)) if self.save_button is not None: self.save_button.setEnabled(True) def calculate_amount(self): if not self.presetting: if self.month_combo.count() == 0 or self.month_till_combo.count( ) == 0: self.amount_line.setText('0') return else: all_possible_months = self.current_advance_months.copy() all_possible_months = all_possible_months[::-1] all_possible_months.extend([ x for x in self.current_pending_months if x not in self.current_advance_months ]) if self.month_till_combo.isEnabled(): from_index = all_possible_months.index( self.month_combo.currentText()) till_index = all_possible_months.index( self.month_till_combo.currentText()) amount = (from_index - till_index + 1) * 1500 else: amount = 1500 self.amount_line.setText(str(amount)) self.amount_line.setToolTip(f"Total months : {amount // 1500}") if self.save_button is not None: self.save_button.setEnabled(True) def calculate_fine(self, from_where: str, month): if not self.presetting: if month == '' and self.month_combo.count( ) == 0 or self.month_till_combo.count() == 0: self.fine_line.setText('0') return else: if self.month_till_combo.isEnabled(): try: till_index = self.current_pending_months.index( str(self.month_till_combo.currentText())) except ValueError: till_index = 0 else: try: till_index = self.current_pending_months.index( str(self.month_combo.currentText())) except ValueError: self.fine_line.setText('0') return try: from_index = self.current_pending_months.index( str(self.month_combo.currentText())) except ValueError: self.fine_line.setText('0') return all_fine_months = [] for month in self.current_pending_months[ till_index:from_index + 1]: all_fine_months.append([month]) transact_date = str(self.date_line.date().toPyDate()) total_fine = 0 for month in all_fine_months: fine = calculate_fine( month=month[0], transact_date=fix_date(transact_date)) month = month.append(fine) total_fine += fine * 50 self.fine_line.setText(str(total_fine)) self.set_fine_tip(all_fine_months=all_fine_months) if self.save_button is not None: self.save_button.setEnabled(True) def set_fine_tip(self, all_fine_months: list): tool_line = '' for month in all_fine_months: tool_line += f"{month[0]} x {month[1]}\n" self.fine_line.setToolTip(tool_line) def set_total(self): if not self.presetting: if len(self.amount_line.text()) > 0: amount = float(self.amount_line.text()) else: amount = 0 if len(self.fine_line.text()) > 0: fine = int(self.fine_line.text()) else: fine = 0 self.total_label.setText(f"TOTAL PAYABLE AMOUNT : {amount + fine}") def print_receipt(self): receipt_id = self.receipt_id.text() design_receipt(receipt_id=f"{receipt_id}", send=False) user = os.environ['USERPROFILE'] path = user + '\\Desktop\\SocietyERP\\Receipts' subprocess.Popen(rf'explorer /select,{path}') self.receipt_id.setText("") self.receipt_id.setFocus() self.print_button.setEnabled(False) self.edit_button.setEnabled(False) self.resend_button.setEnabled(False) def resend_receipt(self): receipt_id = self.receipt_id.text() fee_month = str(self.month_combo.currentText()) if self.month_till_combo.isEnabled(): fee_till = self.month_till_combo.currentText() month = f"{fee_month}-{fee_till}" else: month = fee_month flat = str(self.flat_combo.currentText()) self.status.setText("Generating receipt") self.bar.setValue(40) design_receipt(receipt_id=f"{receipt_id}") self.status.setText( "Sending the receipt to the member. (Depends on your internet connection)" ) self.bar.setValue(75) email_status = send_receipt(flat=flat, month=month, updated=True) if email_status: self.bar.setValue(0) self.status.setText("Done !") self.receipt_id.setText("") self.flat_combo.setCurrentIndex(0) self.name_value.setText("") self.amount_line.setText('0') self.fine_line.setText('0') self.receipt_id.setFocus() else: reply = QMessageBox() reply.setIcon(QMessageBox.Warning) reply.setText( "The system cannot access the internet. Make sure you have an active connection, or any firewall" "feature blocking the access.") reply.setStandardButtons(QMessageBox.Retry) reply.setWindowTitle("INTERNET") reply.exec_() def disable(self): self.finance_entry_group0.setEnabled(False) self.finance_entry_group1.setEnabled(False) self.home_button.setEnabled(False) self.home_button.setEnabled(False) self.mode_label.setEnabled(False) self.mode_combo.setEnabled(False) self.ref_label.setEnabled(False) self.ref_line.setEnabled(False) self.save_button.setEnabled(False) def enable(self): self.finance_entry_group0.setEnabled(True) self.finance_entry_group1.setEnabled(True) self.home_button.setEnabled(True) self.home_button.setEnabled(True) self.mode_label.setEnabled(True) self.mode_combo.setEnabled(True) self.ref_label.setEnabled(True) self.ref_line.setEnabled(True) self.save_button.setEnabled(True)
class SettingsWidget(QWidget): """ Widget for setting entrys Inherits: QWidget Signals: sig_index_changed - Emitted, if the index of a combo box changes (Combo box name|str) """ sig_index_changed = pyqtSignal(str) def __init__(self, name, content, content_others, mount_directory, global_dict=None, input_file_names=None, parent=None): """ Initialise the layout. Arguments: content - Content for the widget. parent - Parent widget (default None) Returns: None """ super(SettingsWidget, self).__init__(parent=parent) self.action = QAction(self) self.action.setShortcut( QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Return)) self.action.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.action.triggered.connect(self.enlarge) self.addAction(self.action) # Layout layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # Global content self.parent = parent self.default = content[0] self.key_name = name self.typ = content[1]['typ'] self.values = content[1]['values'] self.name = content[1]['name'] self.tooltip = content[1]['tooltip'] self.dtype = content[1]['dtype'] self.name_global = content[1]['name_global'] self.global_value = 'ON-THE-FLY' self.widget_auto = None self.mount_directory = mount_directory try: dependency_name, dependency_value = content[1]['group'].split(':') except ValueError: dependency_name = None dependency_value = None if self.name == 'Project name': pattern = None pattern_example = None for entry in content_others: for widget in entry: for key in widget: if key == 'Project name pattern': pattern = widget[key][0] elif key == 'Project name pattern example': pattern_example = widget[key][0] add_to_tooltip = [self.tooltip] if pattern: self.default = pattern add_to_tooltip.extend([ 'Needs to follow:', pattern, ]) if pattern_example: add_to_tooltip.extend(['For example', pattern_example]) self.tooltip = '\n'.join(add_to_tooltip) if self.tooltip: final_tooltip = [] for line in self.tooltip.splitlines(): final_tooltip.append('\n'.join( [line[i:i + 80] for i in range(0, len(line), 80)])) self.tooltip = '\n'.join(final_tooltip) else: self.tooltip = self.name self.tooltip = '{0}\nDefault: \'{1}\''.format(self.tooltip, self.default) if dependency_name is not None: self.tooltip += '\n\nDependency: \'{0}\' is \'{1}\''.format( dependency_name, dependency_value) additional_widget = [] self.add_widgets = [] if self.typ not in ('COMBO', 'COMBOX'): additional_widget.append( (self.enlarge, QStyle.SP_TitleBarNormalButton, 'Open the text field in "Enlarged mode".')) if self.typ == 'PLAIN' or self.typ == 'PASSWORD': self.edit = QLineEdit(self.name, self) self.edit.setToolTip(self.tooltip) self.edit.textChanged.connect(self.change_tooltip) self.edit.setText(self.default) if self.typ == 'PASSWORD': self.edit.setEchoMode(self.edit.Password) self.tooltip = '{0}\n\nShortcuts:\nCtrl + Shift + Return -> Open enlarged view'.format( self.tooltip) elif self.typ in ('FILE', 'FILE/SEARCH'): input_file_names.append(self.name) self.edit = QLineEdit(self.name, self) self.edit.textChanged.connect(self.change_tooltip) self.edit.setText(self.default) self.edit.setPlaceholderText('Press shift+return') self.edit.returnPressed.connect(self._find_file) self.tooltip = '{0}\n\nShortcuts:\nCtrl + Shift + Return -> Open file dialog\nCtrl + Return -> Open enlarged view'.format( self.tooltip) additional_widget.append( (self._find_file, QStyle.SP_DialogOpenButton, 'Open the "Find file" dialog.')) elif self.typ in ('DIR', 'DIR/SEARCH'): self.edit = QLineEdit(self.name, self) self.edit.textChanged.connect(self.change_tooltip) self.edit.setText(self.default) self.edit.setPlaceholderText('Press shift+return') self.edit.returnPressed.connect(self._find_dir) self.tooltip = '{0}\n\nShortcuts:\nCtrl + Shift + Return -> Open directory dialog\nCtrl + Return -> Open enlarged view'.format( self.tooltip) additional_widget.append( (self._find_dir, QStyle.SP_DialogOpenButton, 'Open the "Find directory" dialog.')) elif self.typ in ('COMBO', 'COMBOX'): self.edit = QComboBox(self) self.edit.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.edit.currentTextChanged.connect(self.change_tooltip) self.edit.currentIndexChanged.connect( lambda: self.sig_index_changed.emit(self.name)) self.edit.addItems(self.values) self.edit.setCurrentIndex(self.edit.findText(self.default)) self.change_color_if_true() if self.typ == 'COMBOX': self.edit.setEditable(True) self.edit.setInsertPolicy(QComboBox.NoInsert) self.edit.editTextChanged.connect(self.change_tooltip) self.edit.editTextChanged.connect( lambda: self.sig_index_changed.emit(self.name)) else: print('SettingsWidget:', self.typ, 'Not known! Stopping here!') sys.exit() self.label = QLabel(self.name, self) large_list = ['Path'] if name in large_list: self.label.setObjectName('setting_large') self.edit.setObjectName('setting_large') else: self.label.setObjectName('setting') self.edit.setObjectName('setting') self.label.setToolTip(self.name) try: self.edit.textEdited.connect( lambda: self.edit.setStyleSheet(tu.get_style(typ='unchanged'))) except AttributeError: pass try: self.edit.currentIndexChanged.connect(self.change_color_if_true) except AttributeError: pass layout.addWidget(self.label) layout_h = QHBoxLayout() layout_h.setContentsMargins(0, 0, 0, 0) layout_h.setSpacing(0) layout_h.addWidget(self.edit, stretch=1) try: self.pre_global = self.edit.text() except AttributeError: self.pre_global = self.edit.currentText() for func, icon, tooltip in additional_widget: pb = QPushButton(self) pb.setObjectName('global') pb.setStyleSheet( 'QPushButton {color: rgba(0, 0, 0 ,0); background-color: rgba(0, 0, 0, 0)}' ) icon = pb.style().standardIcon(icon) pb.setIcon(icon) pb.setToolTip(tooltip) pb.clicked.connect(func) self.add_widgets.append(pb) if self.name_global is not None: self.edit.setObjectName('settinger') self.tooltip = '{0}\n\nGlobal value: {{global_value}}'.format( self.tooltip) self.widget_auto = QPushButton('G', self) self.widget_auto.setObjectName('global') self.widget_auto.setToolTip( 'Use the global value specified in the "Global" settings tab') state = True self.widget_auto.setCheckable(state) self.widget_auto.toggled.connect(self._toggle_change) self.widget_auto.setChecked(state) layout_h.addWidget(self.widget_auto, stretch=0) for global_name in self.name_global: if global_dict is not None: global_dict.setdefault(global_name, []).append(self) for pb in self.add_widgets: layout_h.addWidget(pb, stretch=0) layout_h.addStretch(1) layout.addLayout(layout_h) self.tooltip = '{0}\n\nCurrent Text: \'{{current_text}}\''.format( self.tooltip) try: self.edit.textChanged.emit(self.edit.text()) except AttributeError: self.edit.currentTextChanged.emit(self.edit.currentText()) @pyqtSlot(bool) def _toggle_change(self, state): pre_pre_global = self.pre_global if state and self.edit.isEnabled() == True: try: self.pre_global = self.edit.text() except AttributeError: self.pre_global = self.edit.currentText() if self.key_name == 'Global' and state: try: current_global = self.get_current_global() if not current_global: self.sender().setChecked(not state) self.pre_global = pre_pre_global return except Exception: self.pre_global = pre_pre_global self.sender().setChecked(not state) return elif state: current_global = self.global_value self.edit.setEnabled(not state) for entry in self.add_widgets: entry.setEnabled(not state) self.action.setEnabled(not state) if not state: try: self.edit.setText(self.pre_global) self.edit.setStyleSheet(tu.get_style('unchanged')) except AttributeError: self.edit.setCurrentText(self.pre_global) self.change_color_if_true() else: try: self.edit.setText(current_global) except AttributeError: self.edit.setCurrentText(current_global) self.edit.setStyleSheet(tu.get_style('global')) def get_current_global(self): current_global = self.pre_global if self.name == 'GPU': try: nvidia_output = subprocess.check_output(['nvidia-smi', '-L']) gpu_devices = re.findall('^GPU \d+: (.+) \(UUID: GPU-.*\)$', nvidia_output.decode('utf-8'), re.MULTILINE) except subprocess.CalledProcessError: gpu_devices = [] except Exception as e: print(e) gpu_devices = [] if len(set(gpu_devices)) != 1: tu.message( 'The computer does have different types of GPUs available or no GPUs available or the GPU\'s crashed! In order to not make any mistakes, please specify the GPU IDs manually' ) return False else: current_global = ' '.join( [str(entry) for entry in range(len(gpu_devices))]) elif self.name in ('Memory usage', 'Memory usage large'): try: current_global = str( float(self.pre_global) / max(int(self.global_value), 1)) except Exception: pass elif self.name == 'Pixel size bin': try: pixel_size_raw = float( self.parent.content['Pixel size'].get_settings() ['Pixel size']) current_bin = int( self.parent.content['Bin X times'].get_settings() ['Bin X times']) current_global = str(pixel_size_raw * current_bin) except Exception: pass elif self.name == 'Box size': try: current_radius = float( self.parent.content['Protein radius'].get_settings() ['Protein radius']) good_box_size = [ 16, 24, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 84, 96, 100, 104, 112, 120, 128, 132, 140, 168, 180, 192, 196, 208, 216, 220, 224, 240, 256, 260, 288, 300, 320, 352, 360, 384, 416, 440, 448, 480, 512, 540, 560, 576, 588, 600, 630, 640, 648, 672, 686, 700, 720, 750, 756, 768, 784, 800, 810, 840, 864, 882, 896, 900, 960, 972, 980, 1000, 1008, 1024 ] box_size = int(current_radius * 3) good_box_size.append(box_size) good_box_size = sorted(good_box_size) try: current_global = str( good_box_size[good_box_size.index(box_size) + 1]) except IndexError: current_global = str(box_size) except Exception: pass return current_global @pyqtSlot(str) def change_tooltip(self, text): edit = self.sender() if self.typ != 'PASSWORD': tooltip = self.tooltip.format(current_text=text, global_value=self.global_value) edit.setToolTip(tooltip) @pyqtSlot() def enlarge(self): if self.typ in ('COMBO', 'COMBOX'): return else: input_box = inputbox.InputBox( is_password=bool(self.typ == 'PASSWORD'), parent=self) input_box.setText('Enlarged view', self.name) input_box.setDefault(self.edit.text()) input_box.set_type(self.typ) result = input_box.exec_() if result: self.edit.setText(input_box.getText()) @pyqtSlot() def change_color_if_true(self): """ Change the color, if the types are all true. Arguments: None Returns: None """ if self.widget_auto is not None: if self.widget_auto.isChecked(): self.edit.setStyleSheet(tu.get_style(typ='global')) return if self.edit.currentText() == 'False': self.edit.setStyleSheet(tu.get_style(typ='False')) else: self.edit.setStyleSheet(tu.get_style(typ='True')) @pyqtSlot() def _find_file(self): """ Find file with an open file dialog. Arguments: None Returns: None """ if '/SEARCH' in self.typ: current_dir = self.mount_directory else: current_dir = os.getcwd() in_file = QFileDialog.getOpenFileName( caption='Find file: {0}'.format(self.name), directory=current_dir, options=QFileDialog.DontUseNativeDialog) in_file = in_file[0] if in_file != '': self.edit.setText(in_file) @pyqtSlot() def _find_dir(self): """ Find directory with an open directory dialog Arguments: None Returns: None """ if '/SEARCH' in self.typ: current_dir = self.mount_directory else: current_dir = os.getcwd() in_dir = QFileDialog.getExistingDirectory( caption='Find directory: {0}'.format(self.name), directory=current_dir, options=QFileDialog.DontUseNativeDialog) if in_dir != '': if self.name == 'Project name': in_dir = os.path.basename(in_dir) self.edit.setText(in_dir) if '/SEARCH' in self.typ: self.parent.search_for_projects(in_dir) def get_settings(self, quiet=False): """ Get the settings as dict. Arguments: quiet - True, if prints should not be shown. Returns: None, if an error occured. Settings as dictionary. """ settings = {} if isinstance(self.edit, QComboBox): value = self.edit.currentText() elif isinstance(self.edit, QLineEdit): value = self.edit.text() else: message = 'Unreachable code! Please contact the TranSPHIRE authors!' tu.message(message) return None value = value.strip() if ' ' in value and (self.typ in ('FILE', 'DIR', 'FILE/SEARCH') or self.name in ('Rename prefix', 'Rename suffix', 'Project name')): self.edit.setStyleSheet(tu.get_style(typ='error')) message = '{0}: {1} needs to be {2}. To avoid problems later, file paths are not allowed to contain whitespaces. If this is the case, please rename the respective folders and files'.format( self.label.text(), value, self.dtype) if not quiet: tu.message(message) return None elif value: if tu.check_instance(value=value, typ=self.dtype): pass elif value == 'ON-THE-FLY' and self.widget_auto is not None: pass else: self.edit.setStyleSheet(tu.get_style(typ='error')) message = '{0}: {1} needs to be {2}.'.format( self.label.text(), value, self.dtype) if not quiet: tu.message(message) return None else: pass settings[self.name] = value if self.widget_auto is not None: is_auto = self.widget_auto.isChecked() else: is_auto = None global_name = None if self.name_global is None else self.name_global[0] settings['{0}_global'.format(self.name)] = [global_name, is_auto] return settings def get_combo_entries(self): """ Get the entries of the combo boxes. Arguments: None Returns: List containing entries. """ entries_list = [] for idx in range(self.edit.count()): entries_list.append(self.edit.itemText(idx)) return entries_list def set_settings(self, text, is_checked): """ Set settings text - Text to set to the widget. Returns: None """ if is_checked is not None: is_checked_type = is_checked.split(', ')[1][:-1] if is_checked_type == 'None': is_checked = None elif is_checked_type == 'True': is_checked = True elif is_checked_type == 'False': is_checked = False else: assert False, is_checked_type if is_checked is not None: try: self.widget_auto.setChecked(is_checked) except AttributeError: pass widget_auto_checked = self.widget_auto.isChecked( ) if self.widget_auto is not None else False if is_checked or widget_auto_checked: self.pre_global = text else: if self.typ in ('COMBO', 'COMBOX'): index = self.edit.findText(text) if index == -1: index = 0 else: pass self.edit.setCurrentIndex(index - 1) self.edit.setCurrentIndex(index) else: self.edit.setText(text)
class MIDISettings(SettingsPage): NAME = 'MIDI settings' BACKENDS = {'RtMidi': 'mido.backends.rtmidi', 'PortMidi': 'mido.backends.portmidi'} def __init__(self, **kwargs): super().__init__(**kwargs) self.setLayout(QVBoxLayout()) self.layout().setAlignment(Qt.AlignTop) # MIDI Input self.inputGroup = QGroupBox(self) self.inputGroup.setTitle('MIDI input device') self.inputGroup.setLayout(QVBoxLayout()) self.layout().addWidget(self.inputGroup) self.backendCombo = QComboBox(self.inputGroup) self.backendCombo.addItems(self.BACKENDS) self.backendCombo.currentTextChanged.connect(self._change_backend) self.inputGroup.layout().addWidget(self.backendCombo) self.deviceCombo = QComboBox(self.inputGroup) self.inputGroup.layout().addWidget(self.deviceCombo) self._load_devices() def get_settings(self): conf = {} if self.backendCombo.isEnabled(): conf['backend'] = self.BACKENDS[self.backendCombo.currentText()] if self.deviceCombo.isEnabled(): conf['inputdevice'] = self.deviceCombo.currentText() MIDIInputHandler().change_port(conf['inputdevice']) return {'MIDI': conf} def load_settings(self, settings): if 'backend' in settings['MIDI']: for backend in self.BACKENDS: if settings['MIDI']['backend'] == self.BACKENDS[backend]: self.backendCombo.setCurrentText(backend) break if 'inputdevice' in settings['MIDI']: self.deviceCombo.setCurrentText('default') # If the device is not found remains 'default' self.deviceCombo.setCurrentText(settings['MIDI']['inputdevice']) def _load_devices(self): if check_module('Midi'): self.deviceCombo.clear() self.deviceCombo.addItem('default') self.deviceCombo.addItems(MIDIInputHandler().get_input_names()) else: self.deviceCombo.setEnabled(False) def _change_backend(self, current): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setEnabled(False) try: MIDIInputHandler().change_backend(self.BACKENDS[current]) self._load_devices() except RuntimeError as e: logging.exception('Failed to load the backend', e, dialog=True) finally: QApplication.restoreOverrideCursor() self.setEnabled(True)
class MIDISettings(SettingsSection): NAME = 'MIDI preferences' BACKENDS = {'RtMidi': 'mido.backends.rtmidi', 'PortMidi': 'mido.backends.portmidi'} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # MIDI Input self.inputGroup = QGroupBox(self) self.inputGroup.setTitle('Input MIDI device') self.inputGroup.setLayout(QVBoxLayout()) self.inputGroup.setGeometry(0, 0, self.width(), 120) self.backendCombo = QComboBox(self.inputGroup) self.backendCombo.addItems(self.BACKENDS) self.backendCombo.currentTextChanged.connect(self._change_backend) self.inputGroup.layout().addWidget(self.backendCombo) self.deviceCombo = QComboBox(self.inputGroup) self.inputGroup.layout().addWidget(self.deviceCombo) self._load_devices() def get_configuration(self): conf = {} if self.backendCombo.isEnabled(): conf['backend'] = self.BACKENDS[self.backendCombo.currentText()] if self.deviceCombo.isEnabled(): conf['inputdevice'] = self.deviceCombo.currentText() InputMidiHandler().change_port(conf['inputdevice']) return {'MIDI': conf} def set_configuration(self, conf): if 'backend' in conf['MIDI']: for backend in self.BACKENDS: if conf['MIDI']['backend'] == self.BACKENDS[backend]: self.backendCombo.setCurrentText(backend) break if 'inputdevice' in conf['MIDI']: self.deviceCombo.setCurrentText('default') # If the device is not found remains 'default' self.deviceCombo.setCurrentText(conf['MIDI']['inputdevice']) def _load_devices(self): if check_module('midi'): self.deviceCombo.clear() self.deviceCombo.addItem('default') self.deviceCombo.addItems(InputMidiHandler().get_input_names()) else: self.deviceCombo.setEnabled(False) def _change_backend(self, current): self.setEnabled(False) try: InputMidiHandler().change_backend(self.BACKENDS[current]) self._load_devices() except RuntimeError as e: QMessageBox.critical(self, 'Error', str(e)) finally: self.setEnabled(True)