Example #1
0
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]
Example #2
0
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]
Example #3
0
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)
Example #4
0
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())
Example #5
0
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)