class FormTabWidget(QWidget): update_buttons = Signal() def __init__(self, datalist, comment="", parent=None): QWidget.__init__(self, parent) layout = QVBoxLayout() self.tabwidget = QTabWidget() layout.addWidget(self.tabwidget) self.setLayout(layout) self.widgetlist = [] for data, title, comment in datalist: if len(data[0]) == 3: widget = FormComboWidget(data, comment=comment, parent=self) else: widget = FormWidget(data, comment=comment, parent=self) index = self.tabwidget.addTab(widget, title) self.tabwidget.setTabToolTip(index, comment) self.widgetlist.append(widget) def setup(self): for widget in self.widgetlist: widget.setup() def get(self): return [widget.get() for widget in self.widgetlist]
class FormTabWidget(QWidget): update_buttons = Signal() def __init__(self, datalist, comment="", parent=None): QWidget.__init__(self, parent) layout = QVBoxLayout() self.tabwidget = QTabWidget() layout.addWidget(self.tabwidget) self.setLayout(layout) self.widgetlist = [] for data, title, comment in datalist: if len(data[0])==3: widget = FormComboWidget(data, comment=comment, parent=self) else: widget = FormWidget(data, comment=comment, parent=self) index = self.tabwidget.addTab(widget, title) self.tabwidget.setTabToolTip(index, comment) self.widgetlist.append(widget) def setup(self): for widget in self.widgetlist: widget.setup() def get(self): return [ widget.get() for widget in self.widgetlist]
class MasterControl(QWidget): def __init__(self, parent, initialTab=0, b=config.mainB, c=config.mainC, v=config.mainV, text=config.mainText): super().__init__() self.isRefreshing = True self.parent = parent # set title self.setWindowTitle(config.thisTranslation["controlPanel"]) if config.restrictControlPanelWidth and config.screenWidth > config.masterControlWidth: self.setFixedWidth(config.masterControlWidth) # setup item option lists self.setupResourceLists() # setup interface self.text = text self.setupUI(b, c, v, text, initialTab) self.isRefreshing = False # manage key capture def event(self, event): if event.type() == QEvent.KeyRelease: if event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_B: self.tabs.setCurrentIndex(0) elif event.key() == Qt.Key_L: self.tabs.setCurrentIndex(1) elif event.key() == Qt.Key_F: self.tabs.setCurrentIndex(2) elif event.key() == Qt.Key_Y: self.tabs.setCurrentIndex(3) elif event.key() == Qt.Key_M: self.tabs.setCurrentIndex(4) elif event.key() == Qt.Key_Escape: self.hide() return QWidget.event(self, event) def closeEvent(self, event): # Control panel is designed for frequent use # Hiding it instead of closing may save time from reloading event.ignore() self.hide() def setupResourceLists(self): self.parent.setupResourceLists() # bible versions self.textList = self.parent.textList self.textFullNameList = self.parent.textFullNameList self.strongBibles = self.parent.strongBibles if self.parent.versionCombo is not None and config.menuLayout in ( "classic", "focus", "aleph"): for index, fullName in enumerate(self.textFullNameList): self.parent.versionCombo.setItemData(index, fullName, Qt.ToolTipRole) # commentaries self.commentaryList = self.parent.commentaryList #self.commentaryFullNameList = [Commentary(module).commentaryInfo() for module in self.commentaryList] self.commentaryFullNameList = self.parent.commentaryFullNameList # reference book # menu10_dialog self.referenceBookList = self.parent.referenceBookList # topic # menu5_topics self.topicListAbb = self.parent.topicListAbb self.topicList = self.parent.topicList # lexicon # context1_originalLexicon self.lexiconList = self.parent.lexiconList # dictionary # context1_dict self.dictionaryListAbb = self.parent.dictionaryListAbb self.dictionaryList = self.parent.dictionaryList # encyclopedia # context1_encyclopedia self.encyclopediaListAbb = self.parent.encyclopediaListAbb self.encyclopediaList = self.parent.encyclopediaList # 3rd-party dictionary # menu5_3rdDict self.thirdPartyDictionaryList = self.parent.thirdPartyDictionaryList # def setupItemLists(self): # # bible versions # self.textList = BiblesSqlite().getBibleList() # self.textFullNameList = [Bible(text).bibleInfo() for text in self.textList] # self.strongBibles = [text for text in self.textList if Bible(text).bibleStrong()] # if self.parent.versionCombo is not None and config.menuLayout in ("classic", "focus", "aleph"): # for index, fullName in enumerate(self.textFullNameList): # self.parent.versionCombo.setItemData(index, fullName, Qt.ToolTipRole) # # commentaries # self.commentaryList = Commentary().getCommentaryList() # #self.commentaryFullNameList = [Commentary(module).commentaryInfo() for module in self.commentaryList] # self.commentaryFullNameList = [] # for module in self.commentaryList: # info = Commentary(module).commentaryInfo() # if info == "https://Marvel.Bible Commentary" and module in Commentary.marvelCommentaries: # info = Commentary.marvelCommentaries[module] # self.commentaryFullNameList.append(info) # # reference book # # menu10_dialog # bookData = BookData() # self.referenceBookList = [book for book, *_ in bookData.getBookList()] # # open database # indexes = IndexesSqlite() # # topic # # menu5_topics # topicDictAbb2Name = {abb: name for abb, name in indexes.topicList} # self.topicListAbb = list(topicDictAbb2Name.keys()) # topicDict = {name: abb for abb, name in indexes.topicList} # self.topicList = list(topicDict.keys()) # # lexicon # # context1_originalLexicon # self.lexiconList = LexiconData().lexiconList # # dictionary # # context1_dict # dictionaryDictAbb2Name = {abb: name for abb, name in indexes.dictionaryList} # self.dictionaryListAbb = list(dictionaryDictAbb2Name.keys()) # dictionaryDict = {name: abb for abb, name in indexes.dictionaryList} # self.dictionaryList = list(dictionaryDict.keys()) # # encyclopedia # # context1_encyclopedia # encyclopediaDictAbb2Name = {abb: name for abb, name in indexes.encyclopediaList} # self.encyclopediaListAbb = list(encyclopediaDictAbb2Name.keys()) # encyclopediaDict = {name: abb for abb, name in indexes.encyclopediaList} # self.encyclopediaList = list(encyclopediaDict.keys()) # # 3rd-party dictionary # # menu5_3rdDict # self.thirdPartyDictionaryList = ThirdPartyDictionary(self.parent.textCommandParser.isThridPartyDictionary(config.thirdDictionary)).moduleList def setupUI(self, b, c, v, text, initialTab): mainLayout = QVBoxLayout() mainLayout.addWidget(self.sharedWidget()) mainLayout.addWidget(self.tabWidget(b, c, v, text, initialTab)) self.setLayout(mainLayout) def sharedWidget(self): sharedWidget = QWidget() sharedWidgetLayout = QVBoxLayout() subLayout = QHBoxLayout() subLayout.addWidget(self.commandFieldWidget()) subLayout.addWidget(self.autoCloseCheckBox()) sharedWidgetLayout.addLayout(subLayout) sharedWidget.setLayout(sharedWidgetLayout) return sharedWidget def updateBibleTabText(self, reference): self.tabs.setTabText(0, reference) def tabWidget(self, b, c, v, text, initialTab): self.tabs = QTabWidget() # 0 self.bibleTab = BibleExplorer(self, (b, c, v, text)) self.tabs.addTab(self.bibleTab, config.thisTranslation["cp0"]) self.tabs.setTabToolTip(0, config.thisTranslation["cp0Tip"]) # 1 libraryTab = LibraryLauncher(self) self.tabs.addTab(libraryTab, config.thisTranslation["cp1"]) self.tabs.setTabToolTip(1, config.thisTranslation["cp1Tip"]) # 2 self.toolTab = SearchLauncher(self) self.tabs.addTab(self.toolTab, config.thisTranslation["cp2"]) self.tabs.setTabToolTip(2, config.thisTranslation["cp2Tip"]) # 3 self.historyTab = HistoryLauncher(self) self.tabs.addTab(self.historyTab, config.thisTranslation["cp3"]) self.tabs.setTabToolTip(3, config.thisTranslation["cp3Tip"]) # 4 self.miscellaneousTab = MiscellaneousLauncher(self) self.tabs.addTab(self.miscellaneousTab, config.thisTranslation["cp4"]) self.tabs.setTabToolTip(4, config.thisTranslation["cp4Tip"]) # set action with changing tabs self.tabs.currentChanged.connect(self.tabChanged) # set initial tab self.tabs.setCurrentIndex(initialTab) return self.tabs def commandFieldWidget(self): self.commandField = QLineEdit() self.commandField.setClearButtonEnabled(True) self.commandField.setToolTip( config.thisTranslation["enter_command_here"]) self.commandField.returnPressed.connect(self.commandEntered) return self.commandField def autoCloseCheckBox(self): checkbox = QCheckBox() checkbox.setText(config.thisTranslation["autoClose"]) checkbox.setToolTip(config.thisTranslation["autoCloseToolTip"]) checkbox.setChecked(config.closeControlPanelAfterRunningCommand) checkbox.stateChanged.connect( self.closeControlPanelAfterRunningCommandChanged) return checkbox # Common layout def buttonsWidget(self, buttonElementTupleTuple, r2l=False, translation=True): buttons = QWidget() buttonsLayouts = QVBoxLayout() buttonsLayouts.setSpacing(3) for buttonElementTuple in buttonElementTupleTuple: buttonsLayouts.addLayout( self.buttonsLayout(buttonElementTuple, r2l, translation)) buttons.setLayout(buttonsLayouts) return buttons def buttonsLayout(self, buttonElementTuple, r2l=False, translation=True): buttonsLayout = QBoxLayout( QBoxLayout.RightToLeft if r2l else QBoxLayout.LeftToRight) buttonsLayout.setSpacing(5) for label, action in buttonElementTuple: buttonLabel = config.thisTranslation[ label] if translation else label button = QPushButton(buttonLabel) button.clicked.connect(action) buttonsLayout.addWidget(button) return buttonsLayout def comboFeatureLayout(self, feature, combo, action): # QGridLayout: https://stackoverflow.com/questions/61451279/how-does-setcolumnstretch-and-setrowstretch-works layout = QGridLayout() layout.setSpacing(5) # combo layout.addWidget(combo, 0, 0, 1, 3) # button button = QPushButton(config.thisTranslation[feature]) button.clicked.connect(action) layout.addWidget(button, 0, 3, 1, 1) return layout # Actions def closeControlPanelAfterRunningCommandChanged(self): config.closeControlPanelAfterRunningCommand = not config.closeControlPanelAfterRunningCommand def updateBCVText(self, b, c, v, text): self.bibleTab.updateBCVText(b, c, v, text) def commandEntered(self): command = self.commandField.text() self.runTextCommand(command, False) def runTextCommand(self, command, printCommand=True, reloadMainWindow=False): if printCommand: self.commandField.setText(command) self.parent.textCommandLineEdit.setText(command) self.parent.runTextCommand(command) if reloadMainWindow: self.parent.reloadCurrentRecord() if config.closeControlPanelAfterRunningCommand and not self.isRefreshing: self.hide() def tabChanged(self, index): self.isRefreshing = True # refresh content if index == 3: self.historyTab.refresh() elif index == 4: self.miscellaneousTab.refresh() # set focus if index == 2: self.toolTab.searchField.setFocus() if config.contextItem: self.toolTab.searchField.setText(config.contextItem) config.contextItem = "" elif self.parent.mainView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.mainView.currentWidget().selectedText()) elif self.parent.studyView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.studyView.currentWidget().selectedText()) else: self.commandField.setFocus() self.isRefreshing = False def displayMessage(self, message="", title="UniqueBible"): reply = QMessageBox.information(self, title, message)
class CreatePlan(QWidget): plan_created = Signal() plan_node_changed = Signal() def __init__(self, settings: PartSettings): super().__init__() self.settings = settings self.save_translate_dict: typing.Dict[str, SaveBase] = {x.get_short_name(): x for x in save_dict.values()} self.plan = PlanPreview(self) self.save_plan_btn = QPushButton("Save") self.clean_plan_btn = QPushButton("Remove all") self.remove_btn = QPushButton("Remove") self.update_element_chk = QCheckBox("Update element") self.change_root = EnumComboBox(RootType) self.save_choose = QComboBox() self.save_choose.addItem("<none>") self.save_choose.addItems(list(self.save_translate_dict.keys())) self.save_btn = QPushButton("Save") self.segment_profile = SearchableListWidget() self.pipeline_profile = SearchableListWidget() self.segment_stack = QTabWidget() self.segment_stack.addTab(self.segment_profile, "Profile") self.segment_stack.addTab(self.pipeline_profile, "Pipeline") self.generate_mask_btn = QPushButton("Add mask") self.generate_mask_btn.setToolTip("Mask need to have unique name") self.mask_name = QLineEdit() self.mask_operation = EnumComboBox(MaskOperation) self.chanel_num = QSpinBox() self.choose_channel_for_measurements = QComboBox() self.choose_channel_for_measurements.addItems( ["Same as segmentation"] + [str(x + 1) for x in range(MAX_CHANNEL_NUM)] ) self.units_choose = EnumComboBox(Units) self.units_choose.set_value(self.settings.get("units_value", Units.nm)) self.chanel_num.setRange(0, 10) self.expected_node_type = None self.save_constructor = None self.chose_profile_btn = QPushButton("Add Profile") self.get_big_btn = QPushButton("Leave the biggest") self.get_big_btn.hide() self.get_big_btn.setDisabled(True) self.measurements_list = SearchableListWidget(self) self.measurement_name_prefix = QLineEdit(self) self.add_calculation_btn = QPushButton("Add measurement calculation") self.information = QTextEdit() self.information.setReadOnly(True) self.protect = False self.mask_set = set() self.calculation_plan = CalculationPlan() self.plan.set_plan(self.calculation_plan) self.segmentation_mask = MaskWidget(settings) self.file_mask = FileMask() self.change_root.currentIndexChanged.connect(self.change_root_type) self.save_choose.currentTextChanged.connect(self.save_changed) self.measurements_list.currentTextChanged.connect(self.show_measurement) self.segment_profile.currentTextChanged.connect(self.show_segment) self.measurements_list.currentTextChanged.connect(self.show_measurement_info) self.segment_profile.currentTextChanged.connect(self.show_segment_info) self.pipeline_profile.currentTextChanged.connect(self.show_segment_info) self.pipeline_profile.currentTextChanged.connect(self.show_segment) self.mask_name.textChanged.connect(self.mask_name_changed) self.generate_mask_btn.clicked.connect(self.create_mask) self.clean_plan_btn.clicked.connect(self.clean_plan) self.remove_btn.clicked.connect(self.remove_element) self.mask_name.textChanged.connect(self.mask_text_changed) self.chose_profile_btn.clicked.connect(self.add_segmentation) self.get_big_btn.clicked.connect(self.add_leave_biggest) self.add_calculation_btn.clicked.connect(self.add_measurement) self.save_plan_btn.clicked.connect(self.add_calculation_plan) # self.forgot_mask_btn.clicked.connect(self.forgot_mask) # self.cmap_save_btn.clicked.connect(self.save_to_cmap) self.save_btn.clicked.connect(self.add_save_to_project) self.update_element_chk.stateChanged.connect(self.mask_text_changed) self.update_element_chk.stateChanged.connect(self.show_measurement) self.update_element_chk.stateChanged.connect(self.show_segment) self.update_element_chk.stateChanged.connect(self.update_names) self.segment_stack.currentChanged.connect(self.change_segmentation_table) plan_box = QGroupBox("Prepare workflow:") lay = QVBoxLayout() lay.addWidget(self.plan) bt_lay = QGridLayout() bt_lay.setSpacing(1) bt_lay.addWidget(self.save_plan_btn, 0, 0) bt_lay.addWidget(self.clean_plan_btn, 0, 1) bt_lay.addWidget(self.remove_btn, 1, 0) bt_lay.addWidget(self.update_element_chk, 1, 1) lay.addLayout(bt_lay) plan_box.setLayout(lay) plan_box.setStyleSheet(group_sheet) other_box = QGroupBox("Other operations:") other_box.setContentsMargins(0, 0, 0, 0) bt_lay = QVBoxLayout() bt_lay.setSpacing(0) bt_lay.addWidget(QLabel("Root type:")) bt_lay.addWidget(self.change_root) bt_lay.addStretch(1) bt_lay.addWidget(QLabel("Saving:")) bt_lay.addWidget(self.save_choose) bt_lay.addWidget(self.save_btn) other_box.setLayout(bt_lay) other_box.setStyleSheet(group_sheet) mask_box = QGroupBox("Use mask from:") mask_box.setStyleSheet(group_sheet) self.mask_stack = QTabWidget() self.mask_stack.addTab(stretch_widget(self.file_mask), "File") self.mask_stack.addTab(stretch_widget(self.segmentation_mask), "Current ROI") self.mask_stack.addTab(stretch_widget(self.mask_operation), "Operations on masks") self.mask_stack.setTabToolTip(2, "Allows to create mask which is based on masks previously added to plan.") lay = QGridLayout() lay.setSpacing(0) lay.addWidget(self.mask_stack, 0, 0, 1, 2) label = QLabel("Mask name:") label.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'") self.mask_name.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'") lay.addWidget(label, 1, 0) lay.addWidget(self.mask_name, 1, 1) lay.addWidget(self.generate_mask_btn, 2, 0, 1, 2) mask_box.setLayout(lay) segment_box = QGroupBox("ROI extraction:") segment_box.setStyleSheet(group_sheet) lay = QVBoxLayout() lay.setSpacing(0) lay.addWidget(self.segment_stack) lay.addWidget(self.chose_profile_btn) lay.addWidget(self.get_big_btn) segment_box.setLayout(lay) measurement_box = QGroupBox("Set of measurements:") measurement_box.setStyleSheet(group_sheet) lay = QGridLayout() lay.setSpacing(0) lay.addWidget(self.measurements_list, 0, 0, 1, 2) lab = QLabel("Name prefix:") lab.setToolTip("Prefix added before each column name") lay.addWidget(lab, 1, 0) lay.addWidget(self.measurement_name_prefix, 1, 1) lay.addWidget(QLabel("Channel:"), 2, 0) lay.addWidget(self.choose_channel_for_measurements, 2, 1) lay.addWidget(QLabel("Units:"), 3, 0) lay.addWidget(self.units_choose, 3, 1) lay.addWidget(self.add_calculation_btn, 4, 0, 1, 2) measurement_box.setLayout(lay) info_box = QGroupBox("Information") info_box.setStyleSheet(group_sheet) lay = QVBoxLayout() lay.addWidget(self.information) info_box.setLayout(lay) layout = QGridLayout() fst_col = QVBoxLayout() fst_col.addWidget(plan_box, 1) fst_col.addWidget(mask_box) layout.addWidget(plan_box, 0, 0, 5, 1) # layout.addWidget(plan_box, 0, 0, 3, 1) # layout.addWidget(mask_box, 3, 0, 2, 1) # layout.addWidget(segmentation_mask_box, 1, 1) layout.addWidget(mask_box, 0, 2, 1, 2) layout.addWidget(other_box, 0, 1) layout.addWidget(segment_box, 1, 1, 1, 2) layout.addWidget(measurement_box, 1, 3) layout.addWidget(info_box, 3, 1, 1, 3) self.setLayout(layout) self.generate_mask_btn.setDisabled(True) self.chose_profile_btn.setDisabled(True) self.add_calculation_btn.setDisabled(True) self.mask_allow = False self.segment_allow = False self.file_mask_allow = False self.node_type = NodeType.root self.node_name = "" self.plan_node_changed.connect(self.mask_text_changed) self.plan.changed_node.connect(self.node_type_changed) self.plan_node_changed.connect(self.show_segment) self.plan_node_changed.connect(self.show_measurement) self.plan_node_changed.connect(self.mask_stack_change) self.mask_stack.currentChanged.connect(self.mask_stack_change) self.file_mask.value_changed.connect(self.mask_stack_change) self.mask_name.textChanged.connect(self.mask_stack_change) self.node_type_changed() def change_root_type(self): value: RootType = self.change_root.get_value() self.calculation_plan.set_root_type(value) self.plan.update_view() def change_segmentation_table(self): index = self.segment_stack.currentIndex() text = self.segment_stack.tabText(index) if self.update_element_chk.isChecked(): self.chose_profile_btn.setText("Replace " + text) else: self.chose_profile_btn.setText("Add " + text) self.segment_profile.setCurrentItem(None) self.pipeline_profile.setCurrentItem(None) def save_changed(self, text): text = str(text) if text == "<none>": self.save_btn.setText("Save") self.save_btn.setToolTip("Choose file type") self.expected_node_type = None self.save_constructor = None else: save_class = self.save_translate_dict.get(text, None) if save_class is None: self.save_choose.setCurrentText("<none>") return self.save_btn.setText(f"Save to {save_class.get_short_name()}") self.save_btn.setToolTip("Choose mask create in plan view") if save_class.need_mask(): self.expected_node_type = NodeType.mask elif save_class.need_segmentation(): self.expected_node_type = NodeType.segment else: self.expected_node_type = NodeType.root self.save_constructor = Save self.save_activate() def save_activate(self): self.save_btn.setDisabled(True) if self.node_type == self.expected_node_type: self.save_btn.setEnabled(True) return def segmentation_from_project(self): self.calculation_plan.add_step(Operations.reset_to_base) self.plan.update_view() def update_names(self): if self.update_element_chk.isChecked(): self.chose_profile_btn.setText("Replace Profile") self.add_calculation_btn.setText("Replace set of measurements") self.generate_mask_btn.setText("Replace mask") else: self.chose_profile_btn.setText("Add Profile") self.add_calculation_btn.setText("Add set of measurements") self.generate_mask_btn.setText("Generate mask") def node_type_changed(self): # self.cmap_save_btn.setDisabled(True) self.save_btn.setDisabled(True) self.node_name = "" if self.plan.currentItem() is None: self.mask_allow = False self.file_mask_allow = False self.segment_allow = False self.remove_btn.setDisabled(True) self.plan_node_changed.emit() logging.debug("[node_type_changed] return") return node_type = self.calculation_plan.get_node_type() self.node_type = node_type if node_type in [NodeType.file_mask, NodeType.mask, NodeType.segment, NodeType.measurement, NodeType.save]: self.remove_btn.setEnabled(True) else: self.remove_btn.setEnabled(False) if node_type in (NodeType.mask, NodeType.file_mask): self.mask_allow = False self.segment_allow = True self.file_mask_allow = False self.node_name = self.calculation_plan.get_node().operation.name elif node_type == NodeType.segment: self.mask_allow = True self.segment_allow = False self.file_mask_allow = False self.save_btn.setEnabled(True) # self.cmap_save_btn.setEnabled(True) elif node_type == NodeType.root: self.mask_allow = False self.segment_allow = True self.file_mask_allow = True elif node_type in (NodeType.none, NodeType.measurement, NodeType.save): self.mask_allow = False self.segment_allow = False self.file_mask_allow = False self.save_activate() self.plan_node_changed.emit() def add_save_to_project(self): save_class = self.save_translate_dict.get(self.save_choose.currentText(), None) if save_class is None: QMessageBox.warning(self, "Save problem", "Not found save class") dial = FormDialog( [AlgorithmProperty("suffix", "File suffix", ""), AlgorithmProperty("directory", "Sub directory", "")] + save_class.get_fields() ) if dial.exec(): values = dial.get_values() suffix = values["suffix"] directory = values["directory"] del values["suffix"] del values["directory"] save_elem = Save(suffix, directory, save_class.get_name(), save_class.get_short_name(), values) if self.update_element_chk.isChecked(): self.calculation_plan.replace_step(save_elem) else: self.calculation_plan.add_step(save_elem) self.plan.update_view() def create_mask(self): text = str(self.mask_name.text()).strip() if text != "" and text in self.mask_set: QMessageBox.warning(self, "Already exists", "Mask with this name already exists", QMessageBox.Ok) return if _check_widget(self.mask_stack, EnumComboBox): # existing mask mask_dialog = TwoMaskDialog if self.mask_operation.get_value() == MaskOperation.mask_intersection: # Mask intersection MaskConstruct = MaskIntersection else: MaskConstruct = MaskSum dial = mask_dialog(self.mask_set) if not dial.exec(): return names = dial.get_result() mask_ob = MaskConstruct(text, *names) elif _check_widget(self.mask_stack, MaskWidget): mask_ob = MaskCreate(text, self.segmentation_mask.get_mask_property()) elif _check_widget(self.mask_stack, FileMask): mask_ob = self.file_mask.get_value(text) else: raise ValueError("Unknowsn widget") if self.update_element_chk.isChecked(): node = self.calculation_plan.get_node() name = node.operation.name if name in self.calculation_plan.get_reused_mask() and name != text: QMessageBox.warning( self, "Cannot remove", f"Cannot remove mask '{name}' from plan because it is used in other elements" ) return self.mask_set.remove(name) self.mask_set.add(mask_ob.name) self.calculation_plan.replace_step(mask_ob) else: self.mask_set.add(mask_ob.name) self.calculation_plan.add_step(mask_ob) self.plan.update_view() self.mask_text_changed() def mask_stack_change(self): node_type = self.calculation_plan.get_node_type() if self.update_element_chk.isChecked() and node_type not in [NodeType.mask, NodeType.file_mask]: self.generate_mask_btn.setDisabled(True) text = self.mask_name.text() update = self.update_element_chk.isChecked() if self.node_type == NodeType.none: self.generate_mask_btn.setDisabled(True) return operation = self.calculation_plan.get_node().operation if ( not update and isinstance(operation, (MaskMapper, MaskBase)) and self.calculation_plan.get_node().operation.name == text ): self.generate_mask_btn.setDisabled(True) return if _check_widget(self.mask_stack, EnumComboBox): # reuse mask if len(self.mask_set) > 1 and ( (not update and node_type == NodeType.root) or (update and node_type == NodeType.file_mask) ): self.generate_mask_btn.setEnabled(True) else: self.generate_mask_btn.setEnabled(False) self.generate_mask_btn.setToolTip("Need at least two named mask and root selected") elif _check_widget(self.mask_stack, MaskWidget): # mask from segmentation if (not update and node_type == NodeType.segment) or (update and node_type == NodeType.mask): self.generate_mask_btn.setEnabled(True) else: self.generate_mask_btn.setEnabled(False) self.generate_mask_btn.setToolTip("Select segmentation") else: if (not update and node_type == NodeType.root) or (update and node_type == NodeType.file_mask): self.generate_mask_btn.setEnabled(self.file_mask.is_valid()) else: self.generate_mask_btn.setEnabled(False) self.generate_mask_btn.setToolTip("Need root selected") def mask_name_changed(self, text): if str(text) in self.mask_set: self.generate_mask_btn.setDisabled(True) else: self.generate_mask_btn.setDisabled(False) def add_leave_biggest(self): profile = self.calculation_plan.get_node().operation profile.leave_biggest_swap() self.calculation_plan.replace_step(profile) self.plan.update_view() def add_segmentation(self): if self.segment_stack.currentIndex() == 0: text = str(self.segment_profile.currentItem().text()) if text not in self.settings.segmentation_profiles: self.refresh_all_profiles() return profile = self.settings.segmentation_profiles[text] if self.update_element_chk.isChecked(): self.calculation_plan.replace_step(profile) else: self.calculation_plan.add_step(profile) self.plan.update_view() else: # self.segment_stack.currentIndex() == 1 text = self.pipeline_profile.currentItem().text() segmentation_pipeline = self.settings.segmentation_pipelines[text] pos = self.calculation_plan.current_pos[:] old_pos = self.calculation_plan.current_pos[:] for el in segmentation_pipeline.mask_history: self.calculation_plan.add_step(el.segmentation) self.plan.update_view() node = self.calculation_plan.get_node(pos) pos.append(len(node.children) - 1) self.calculation_plan.set_position(pos) self.calculation_plan.add_step(MaskCreate("", el.mask_property)) self.plan.update_view() pos.append(0) self.calculation_plan.set_position(pos) self.calculation_plan.add_step(segmentation_pipeline.segmentation) self.calculation_plan.set_position(old_pos) self.plan.update_view() def add_measurement(self): text = str(self.measurements_list.currentItem().text()) measurement_copy = deepcopy(self.settings.measurement_profiles[text]) prefix = str(self.measurement_name_prefix.text()).strip() channel = self.choose_channel_for_measurements.currentIndex() - 1 measurement_copy.name_prefix = prefix # noinspection PyTypeChecker measurement_calculate = MeasurementCalculate( channel=channel, measurement_profile=measurement_copy, name_prefix=prefix, units=self.units_choose.get_value(), ) if self.update_element_chk.isChecked(): self.calculation_plan.replace_step(measurement_calculate) else: self.calculation_plan.add_step(measurement_calculate) self.plan.update_view() def remove_element(self): conflict_mask, used_mask = self.calculation_plan.get_file_mask_names() if len(conflict_mask) > 0: logging.info("Mask in use") QMessageBox.warning(self, "In use", "Masks {} are used in other places".format(", ".join(conflict_mask))) return self.mask_set -= used_mask self.calculation_plan.remove_step() self.plan.update_view() def clean_plan(self): self.calculation_plan = CalculationPlan() self.plan.set_plan(self.calculation_plan) self.node_type_changed() self.mask_set = set() def mask_text_changed(self): name = str(self.mask_name.text()).strip() self.generate_mask_btn.setDisabled(True) # load mask from file if not self.update_element_chk.isChecked(): # generate mask from segmentation if self.mask_allow and (name == "" or name not in self.mask_set): self.generate_mask_btn.setEnabled(True) else: if self.node_type != NodeType.file_mask and self.node_type != NodeType.mask: return # generate mask from segmentation if self.node_type == NodeType.mask and (name == "" or name == self.node_name or name not in self.mask_set): self.generate_mask_btn.setEnabled(True) def add_calculation_plan(self, text=None): if text is None or isinstance(text, bool): text, ok = QInputDialog.getText(self, "Plan title", "Set plan title") else: text, ok = QInputDialog.getText( self, "Plan title", f"Set plan title. Previous ({text}) is already in use", text=text ) text = text.strip() if ok: if text == "": QMessageBox.information( self, "Name cannot be empty", "Name cannot be empty, Please set correct name", QMessageBox.Ok ) self.add_calculation_plan() return if text in self.settings.batch_plans: res = QMessageBox.information( self, "Name already in use", "Name already in use. Would like to overwrite?", QMessageBox.Yes | QMessageBox.No, ) if res == QMessageBox.No: self.add_calculation_plan(text) return plan = copy(self.calculation_plan) plan.set_name(text) self.settings.batch_plans[text] = plan self.settings.dump() self.plan_created.emit() @staticmethod def get_index(item: QListWidgetItem, new_values: typing.List[str]) -> int: if item is None: return -1 text = item.text() try: return new_values.index(text) except ValueError: return -1 @staticmethod def refresh_profiles(list_widget: QListWidget, new_values: typing.List[str], index: int): list_widget.clear() list_widget.addItems(new_values) if index != -1: list_widget.setCurrentRow(index) def showEvent(self, _event): self.refresh_all_profiles() def refresh_all_profiles(self): new_measurements = list(sorted(self.settings.measurement_profiles.keys())) new_segment = list(sorted(self.settings.segmentation_profiles.keys())) new_pipelines = list(sorted(self.settings.segmentation_pipelines.keys())) measurement_index = self.get_index(self.measurements_list.currentItem(), new_measurements) segment_index = self.get_index(self.segment_profile.currentItem(), new_segment) pipeline_index = self.get_index(self.pipeline_profile.currentItem(), new_pipelines) self.protect = True self.refresh_profiles(self.measurements_list, new_measurements, measurement_index) self.refresh_profiles(self.segment_profile, new_segment, segment_index) self.refresh_profiles(self.pipeline_profile, new_pipelines, pipeline_index) self.protect = False def show_measurement_info(self, text=None): if self.protect: return if text is None: if self.measurements_list.currentItem() is not None: text = str(self.measurements_list.currentItem().text()) else: return profile = self.settings.measurement_profiles[text] self.information.setText(str(profile)) def show_measurement(self): if self.update_element_chk.isChecked(): if self.node_type == NodeType.measurement: self.add_calculation_btn.setEnabled(True) else: self.add_calculation_btn.setDisabled(True) else: if self.measurements_list.currentItem() is not None: self.add_calculation_btn.setEnabled(self.mask_allow) else: self.add_calculation_btn.setDisabled(True) def show_segment_info(self, text=None): if self.protect: return if text == "": return if self.segment_stack.currentIndex() == 0: if text is None: if self.segment_profile.currentItem() is not None: text = str(self.segment_profile.currentItem().text()) else: return profile = self.settings.segmentation_profiles[text] else: if text is None: if self.pipeline_profile.currentItem() is not None: text = str(self.pipeline_profile.currentItem().text()) else: return profile = self.settings.segmentation_pipelines[text] self.information.setText(profile.pretty_print(analysis_algorithm_dict)) def show_segment(self): if self.update_element_chk.isChecked() and self.segment_stack.currentIndex() == 0: self.get_big_btn.setDisabled(True) if self.node_type == NodeType.segment: self.chose_profile_btn.setEnabled(True) else: self.chose_profile_btn.setDisabled(True) else: if self.node_type == NodeType.segment: self.get_big_btn.setEnabled(True) else: self.get_big_btn.setDisabled(True) if ( self.segment_stack.currentIndex() == 0 and self.segment_profile.currentItem() ) or self.pipeline_profile.currentItem() is not None: self.chose_profile_btn.setEnabled(self.segment_allow) else: self.chose_profile_btn.setDisabled(True) def edit_plan(self): plan = self.sender().plan_to_edit # type: CalculationPlan self.calculation_plan = copy(plan) self.plan.set_plan(self.calculation_plan) self.mask_set.clear() self.calculation_plan.set_position([]) self.mask_set.update(self.calculation_plan.get_mask_names())
class MasterControl(QWidget): def __init__(self, parent, initialTab=0, b=config.mainB, c=config.mainC, v=config.mainV, text=config.mainText): super().__init__() self.isRefreshing = True self.parent = parent # set title self.setWindowTitle(config.thisTranslation["controlPanel"]) if config.restrictControlPanelWidth and config.screenWidth > config.masterControlWidth: self.setFixedWidth(config.masterControlWidth) # setup item option lists self.setupResourceLists() # setup interface self.text = text self.setupUI(b, c, v, text, initialTab) # setup keyboard shortcuts self.setupKeyboardShortcuts() self.isRefreshing = False def setupKeyboardShortcuts(self): for index, shortcut in enumerate( (sc.openControlPanelTab0, sc.openControlPanelTab1, sc.openControlPanelTab2, sc.openControlPanelTab3, sc.openControlPanelTab4, sc.openControlPanelTab5)): shortcut = QShortcut(QKeySequence(shortcut), self) shortcut.activated.connect( lambda index=index: self.tabs.setCurrentIndex(index)) # manage key capture def event(self, event): if event.type() == QEvent.KeyRelease: if event.key() == Qt.Key_Escape: self.hide() if isinstance(event, QEvent): return QWidget.event(self, event) def closeEvent(self, event): # Control panel is designed for frequent use # Hiding it instead of closing may save time from reloading event.ignore() self.hide() def setupResourceLists(self): self.parent.setupResourceLists() # bible versions self.textList = self.parent.textList self.textFullNameList = self.parent.textFullNameList self.strongBibles = self.parent.strongBibles if self.parent.versionCombo is not None and config.menuLayout in ( "classic", "focus", "aleph"): for index, fullName in enumerate(self.textFullNameList): self.parent.versionCombo.setItemData(index, fullName, Qt.ToolTipRole) # commentaries self.commentaryList = self.parent.commentaryList #self.commentaryFullNameList = [Commentary(module).commentaryInfo() for module in self.commentaryList] self.commentaryFullNameList = self.parent.commentaryFullNameList # reference book # menu10_dialog self.referenceBookList = self.parent.referenceBookList # topic # menu5_topics self.topicListAbb = self.parent.topicListAbb self.topicList = self.parent.topicList # lexicon # context1_originalLexicon self.lexiconList = self.parent.lexiconList # dictionary # context1_dict self.dictionaryListAbb = self.parent.dictionaryListAbb self.dictionaryList = self.parent.dictionaryList # encyclopedia # context1_encyclopedia self.encyclopediaListAbb = self.parent.encyclopediaListAbb self.encyclopediaList = self.parent.encyclopediaList # 3rd-party dictionary # menu5_3rdDict self.thirdPartyDictionaryList = self.parent.thirdPartyDictionaryList # pdf list self.pdfList = self.parent.pdfList # docx list self.docxList = self.parent.docxList def setupUI(self, b, c, v, text, initialTab): mainLayout = QVBoxLayout() mainLayout.addWidget(self.sharedWidget()) mainLayout.addWidget(self.tabWidget(b, c, v, text, initialTab)) self.setLayout(mainLayout) def sharedWidget(self): sharedWidget = QWidget() sharedWidgetLayout = QVBoxLayout() subLayout = QHBoxLayout() subLayout.addWidget(self.commandFieldWidget()) subLayout.addWidget(self.autoCloseCheckBox()) sharedWidgetLayout.addLayout(subLayout) sharedWidget.setLayout(sharedWidgetLayout) return sharedWidget def updateBibleTabText(self, reference): self.tabs.setTabText(0, reference) def tabWidget(self, b, c, v, text, initialTab): self.tabs = QTabWidget() # 0 self.bibleTab = BibleExplorer(self, (b, c, v, text)) self.tabs.addTab(self.bibleTab, config.thisTranslation["cp0"]) self.tabs.setTabToolTip(0, sc.openControlPanelTab0) # 1 libraryTab1 = LibraryLauncher(self) self.tabs.addTab(libraryTab1, config.thisTranslation["cp1"]) self.tabs.setTabToolTip(1, sc.openControlPanelTab1) # 2 libraryTab2 = Library2Launcher(self) self.tabs.addTab(libraryTab2, config.thisTranslation["cp2"]) self.tabs.setTabToolTip(2, sc.openControlPanelTab2) # 3 self.toolTab = SearchLauncher(self) self.tabs.addTab(self.toolTab, config.thisTranslation["cp3"]) self.tabs.setTabToolTip(3, sc.openControlPanelTab3) # 4 self.historyTab = HistoryLauncher(self) self.tabs.addTab(self.historyTab, config.thisTranslation["cp4"]) self.tabs.setTabToolTip(4, sc.openControlPanelTab4) # 5 self.miscellaneousTab = MiscellaneousLauncher(self) self.tabs.addTab(self.miscellaneousTab, config.thisTranslation["cp5"]) self.tabs.setTabToolTip(5, sc.openControlPanelTab5) # 6 if config.isVlcInstalled: mediaTab = MediaLauncher(self) self.tabs.addTab(mediaTab, config.thisTranslation["mediaPlayer"]) self.tabs.setTabToolTip(6, sc.openControlPanelTab6) #7 self.morphologyTab = MorphologyLauncher(self) self.tabs.addTab(self.morphologyTab, config.thisTranslation["cp7"]) self.tabs.setTabToolTip(7, sc.openControlPanelTab7) # set action with changing tabs self.tabs.currentChanged.connect(self.tabChanged) # set initial tab self.tabs.setCurrentIndex(initialTab) return self.tabs def commandFieldWidget(self): self.commandField = QLineEdit() self.commandField.setClearButtonEnabled(True) self.commandField.setToolTip( config.thisTranslation["enter_command_here"]) self.commandField.returnPressed.connect(self.commandEntered) return self.commandField def autoCloseCheckBox(self): checkbox = QCheckBox() checkbox.setText(config.thisTranslation["autoClose"]) checkbox.setToolTip(config.thisTranslation["autoCloseToolTip"]) checkbox.setChecked(config.closeControlPanelAfterRunningCommand) checkbox.stateChanged.connect( self.closeControlPanelAfterRunningCommandChanged) return checkbox # Common layout def buttonsWidget(self, buttonElementTupleTuple, r2l=False, translation=True): buttons = QWidget() buttonsLayouts = QVBoxLayout() buttonsLayouts.setSpacing(3) for buttonElementTuple in buttonElementTupleTuple: buttonsLayouts.addLayout( self.buttonsLayout(buttonElementTuple, r2l, translation)) buttons.setLayout(buttonsLayouts) return buttons def buttonsLayout(self, buttonElementTuple, r2l=False, translation=True): buttonsLayout = QBoxLayout( QBoxLayout.RightToLeft if r2l else QBoxLayout.LeftToRight) buttonsLayout.setSpacing(5) for label, action in buttonElementTuple: buttonLabel = config.thisTranslation[ label] if translation else label button = QPushButton(buttonLabel) button.clicked.connect(action) buttonsLayout.addWidget(button) return buttonsLayout def comboFeatureLayout(self, feature, combo, action): # QGridLayout: https://stackoverflow.com/questions/61451279/how-does-setcolumnstretch-and-setrowstretch-works layout = QGridLayout() layout.setSpacing(5) # combo layout.addWidget(combo, 0, 0, 1, 3) # button button = QPushButton(config.thisTranslation[feature]) button.clicked.connect(action) layout.addWidget(button, 0, 3, 1, 1) return layout # Actions def closeControlPanelAfterRunningCommandChanged(self): config.closeControlPanelAfterRunningCommand = not config.closeControlPanelAfterRunningCommand def updateBCVText(self, b, c, v, text): self.bibleTab.updateBCVText(b, c, v, text) def commandEntered(self): command = self.commandField.text() self.runTextCommand(command, False) def runTextCommand(self, command, printCommand=True, reloadMainWindow=False): if printCommand: self.commandField.setText(command) self.parent.textCommandLineEdit.setText(command) self.parent.runTextCommand(command) if reloadMainWindow: self.parent.reloadCurrentRecord() if config.closeControlPanelAfterRunningCommand and not self.isRefreshing: self.hide() def tabChanged(self, index): self.isRefreshing = True # refresh content if index == 4: self.historyTab.refresh() elif index == 5: self.miscellaneousTab.refresh() # set focus if index == 3: self.toolTab.searchField.setFocus() if config.contextItem: self.toolTab.searchField.setText(config.contextItem) config.contextItem = "" elif self.parent.mainView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.mainView.currentWidget().selectedText()) elif self.parent.studyView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.studyView.currentWidget().selectedText()) else: self.commandField.setFocus() self.isRefreshing = False def displayMessage(self, message="", title="UniqueBible"): reply = QMessageBox.information(self, title, message)