def __init__(
        self,
        onOpenLogButtonPressed: Callable[[], None],
        onAgentVarsButtonPressed: Callable[[], None],
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        The "Open Log" and "Agent Variables" buttons view used for opening those windows.
        """
        super(SolverWindowsButtonsView, self).__init__(parent=parent, *args, **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)

        # define the buttons
        openLogButton = QPushButton("Open Log")
        agentVarsButton = QPushButton("Agent Variables")

        # connect them to their respective methods
        openLogButton.pressed.connect(onOpenLogButtonPressed)  # type: ignore
        agentVarsButton.pressed.connect(onAgentVarsButtonPressed)  # type: ignore

        layout.addWidget(openLogButton)
        layout.addWidget(agentVarsButton)

        layout.setAlignment(openLogButton, Qt.Alignment.AlignHCenter)
        layout.setAlignment(agentVarsButton, Qt.Alignment.AlignHCenter)

        self.setLayout(layout)
示例#2
0
    def __init__(self, parent, top):
        super(QWidget, self).__init__(parent)
        self.top = top
        hlayout = QHBoxLayout()
        self.layout = QGridLayout()
        hlayout.addLayout(self.layout)
        hlayout.setAlignment(hlayout, Qt.Alignment.AlignTop)
        self.setLayout(hlayout)
        self.row = 0

        self.__addLabel__("Federate Name")
        self.federateName = QLineEdit('REMOTE_WORKSTATION')
        self.__addInput__(self.federateName)

        self.__addLabel__("Message Directory Cache")
        self.messageDirectoryCache = QLineEdit(self)
        self.__addInputAndSelect__(self.messageDirectoryCache, self.top)

        self.__addLabel__("Map Data Cache")
        self.mapDataCache = QLineEdit(self)
        self.__addInputAndSelect__(self.mapDataCache, self.top)

        self.__addLabel__("Raster Map Cache")
        self.rasterMapCache = QLineEdit(self)
        self.__addInputAndSelect__(self.rasterMapCache, self.top)

        self.__addLabel__("Remote Control Location")
        self.remoteControlLocation = QLineEdit(self)
        self.__addInputAndSelect__(self.remoteControlLocation, self.top)
示例#3
0
文件: ServicesUI.py 项目: wbeebe/pyqt
    def __init__(self, parent, top):
        super(QWidget, self).__init__(parent)
        self.top = top
        hlayout = QHBoxLayout()
        self.layout = QGridLayout()
        hlayout.addLayout(self.layout)
        hlayout.setAlignment(hlayout, Qt.Alignment.AlignTop)
        self.setLayout(hlayout)
        self.row = 0

        self.__addLabel__("Gateway Services")
        self.__addLabel__("Gateway Host Name/IP Address")
        self.gatewayHostName = QLineEdit(self)
        self.__addInput__(self.gatewayHostName)

        self.__addLabel__("Exercise Data Server Host Name/IP Address")
        self.productionEDS = QLineEdit(self)
        self.prodEnable = QRadioButton("Production")
        self.prodEnable.setChecked(True)
        self.prodEnable.toggled.connect(self.radioProdClicked)
        self.prodEnable.setStyleSheet("QRadioButton{ width: 100; }")
        self.__addInputAndRadio__(self.productionEDS, self.prodEnable)
        self.testEDS = QLineEdit(self)
        self.testEnable = QRadioButton("Test")
        self.testEnable.toggled.connect(self.radioTestClicked)
        self.testEnable.setStyleSheet("QRadioButton{ width: 100; }")
        self.__addInputAndRadio__(self.testEDS, self.testEnable)

        self.__addLabel__("Messaging Port")
        self.messagePort = QLineEdit("61616")
        self.__addInput__(self.messagePort)
示例#4
0
 def createRow(self, exercise, index):
     item = QHBoxLayout()
     label = QLabel(exercise.name)
     item.addWidget(label)
     item.setAlignment(label, Qt.Alignment.AlignCenter)
     button = QPushButton(exercise.assigned_key[0])
     # button.installEventFilter(self)
     button.setStyleSheet(""" QPushButton
         {
             border: 1px solid grey;
             background-color: white;
         }
         """)
     button.clicked.connect(partial(self.openDialog, button))
     self.buttons.append(button)
     item.addWidget(button)
     self.mainLayout.addLayout(item, index, 0)
     print(index, exercise.name, exercise.assigned_key)
示例#5
0
class DocumentBaseViewerWidget(MainWindowContent):
    def __init__(self, main_window):
        super(DocumentBaseViewerWidget, self).__init__(main_window,
                                                       "Document Base")

        # controls
        self.controls = QWidget()
        self.controls_layout = QHBoxLayout(self.controls)
        self.controls_layout.setContentsMargins(0, 0, 0, 0)
        self.controls_layout.setSpacing(10)
        self.controls_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.layout.addWidget(self.controls)

        self.create_document_base_button = QPushButton(
            "Create a new Document Base")
        self.create_document_base_button.setFont(BUTTON_FONT)
        self.create_document_base_button.clicked.connect(
            self.main_window.show_document_base_creator_widget_task)
        self.controls_layout.addWidget(self.create_document_base_button)

        self.load_and_run_default_preprocessing_phase_button = QPushButton(
            "Preprocess the Document Base")
        self.load_and_run_default_preprocessing_phase_button.setFont(
            BUTTON_FONT)
        self.load_and_run_default_preprocessing_phase_button.clicked.connect(
            self.main_window.load_and_run_default_preprocessing_phase_task)
        self.controls_layout.addWidget(
            self.load_and_run_default_preprocessing_phase_button)

        self.load_and_run_default_matching_phase_button = QPushButton(
            "Match the Nuggets to the Attributes")
        self.load_and_run_default_matching_phase_button.setFont(BUTTON_FONT)
        self.load_and_run_default_matching_phase_button.clicked.connect(
            self.main_window.load_and_run_default_matching_phase_task)
        self.controls_layout.addWidget(
            self.load_and_run_default_matching_phase_button)

        self.save_table_button = QPushButton("Export the Table to CSV")
        self.save_table_button.setFont(BUTTON_FONT)
        self.save_table_button.clicked.connect(
            self.main_window.save_table_to_csv_task)
        self.controls_layout.addWidget(self.save_table_button)

        # documents
        self.documents = MainWindowContentSection(self, "Documents:")
        self.layout.addWidget(self.documents)

        self.num_documents = QLabel("number of documents: -")
        self.num_documents.setFont(LABEL_FONT)
        self.documents.layout.addWidget(self.num_documents)

        self.num_nuggets = QLabel("number of nuggets: -")
        self.num_nuggets.setFont(LABEL_FONT)
        self.documents.layout.addWidget(self.num_nuggets)

        # attributes
        self.attributes = MainWindowContentSection(self, "Attributes:")
        self.layout.addWidget(self.attributes)

        self.add_attribute_button = QPushButton("Add Attribute")
        self.add_attribute_button.setFont(BUTTON_FONT)
        self.add_attribute_button.clicked.connect(
            self.main_window.add_attribute_task)

        self.attributes_list = CustomScrollableList(self, AttributeWidget,
                                                    self.add_attribute_button)
        self.attributes.layout.addWidget(self.attributes_list)

    def update_document_base(self, document_base):
        # update documents
        self.num_documents.setText(
            f"number of documents: {len(document_base.documents)}")
        self.num_nuggets.setText(
            f"number of nuggets: {len(document_base.nuggets)}")

        # update attributes
        self.attributes_list.update_item_list(document_base.attributes,
                                              document_base)

    def enable_input(self):
        self.create_document_base_button.setEnabled(True)

        if self.main_window.document_base is not None:
            self.load_and_run_default_preprocessing_phase_button.setEnabled(
                True)
            self.load_and_run_default_matching_phase_button.setEnabled(True)
            self.save_table_button.setEnabled(True)
            self.add_attribute_button.setEnabled(True)
            self.attributes_list.enable_input()

    def disable_input(self):
        self.create_document_base_button.setDisabled(True)
        self.load_and_run_default_preprocessing_phase_button.setDisabled(True)
        self.load_and_run_default_matching_phase_button.setDisabled(True)
        self.save_table_button.setDisabled(True)
        self.add_attribute_button.setDisabled(True)
        self.attributes_list.disable_input()
示例#6
0
class DocumentBaseCreatorWidget(MainWindowContent):
    def __init__(self, main_window) -> None:
        super(DocumentBaseCreatorWidget,
              self).__init__(main_window, "Create Document Base")

        self.documents = MainWindowContentSection(self, "Documents:")
        self.layout.addWidget(self.documents)

        self.documents_explanation = QLabel(
            "Enter the path of the directory that contains the documents as .txt files."
        )
        self.documents_explanation.setFont(LABEL_FONT)
        self.documents.layout.addWidget(self.documents_explanation)

        self.path_widget = QFrame()
        self.path_layout = QHBoxLayout(self.path_widget)
        self.path_layout.setContentsMargins(20, 0, 20, 0)
        self.path_layout.setSpacing(10)
        self.path_widget.setStyleSheet("background-color: white")
        self.path_widget.setFixedHeight(40)
        self.documents.layout.addWidget(self.path_widget)

        self.path = QLineEdit()
        self.path.setFont(CODE_FONT_BOLD)
        self.path.setStyleSheet("border: none")
        self.path_layout.addWidget(self.path)

        self.edit_path_button = QPushButton()
        self.edit_path_button.setIcon(QIcon("aset_ui/resources/folder.svg"))
        self.edit_path_button.setFlat(True)
        self.edit_path_button.clicked.connect(self._edit_path_button_clicked)
        self.path_layout.addWidget(self.edit_path_button)

        self.attributes = MainWindowContentSection(self, "Attributes:")
        self.layout.addWidget(self.attributes)

        self.labels_explanation = QLabel("Enter the attribute names.")
        self.labels_explanation.setFont(LABEL_FONT)
        self.attributes.layout.addWidget(self.labels_explanation)

        self.create_attribute_button = QPushButton("New Attribute")
        self.create_attribute_button.setFont(BUTTON_FONT)
        self.create_attribute_button.clicked.connect(
            self._create_attribute_button_clicked)

        self.attribute_names = []
        self.attributes_list = CustomScrollableList(
            self, AttributeCreatorWidget, self.create_attribute_button)
        self.attributes.layout.addWidget(self.attributes_list)

        self.buttons_widget = QWidget()
        self.buttons_layout = QHBoxLayout(self.buttons_widget)
        self.buttons_layout.setContentsMargins(0, 0, 0, 0)
        self.buttons_layout.setSpacing(10)
        self.buttons_layout.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.attributes.layout.addWidget(self.buttons_widget)

        self.cancel_button = QPushButton("Cancel")
        self.cancel_button.setFont(BUTTON_FONT)
        self.cancel_button.clicked.connect(self._cancel_button_clicked)
        self.buttons_layout.addWidget(self.cancel_button)

        self.create_document_base_button = QPushButton("Create Document Base")
        self.create_document_base_button.setFont(BUTTON_FONT)
        self.create_document_base_button.clicked.connect(
            self._create_document_base_button_clicked)
        self.buttons_layout.addWidget(self.create_document_base_button)

    def enable_input(self):
        self.edit_path_button.setEnabled(True)
        self.create_attribute_button.setEnabled(True)
        self.cancel_button.setEnabled(True)
        self.create_document_base_button.setEnabled(True)
        self.attributes_list.enable_input()

    def disable_input(self):
        self.edit_path_button.setDisabled(True)
        self.create_attribute_button.setDisabled(True)
        self.cancel_button.setDisabled(True)
        self.create_document_base_button.setDisabled(True)
        self.attributes_list.disable_input()

    def initialize_for_new_document_base(self):
        self.path.setText("")
        self.attribute_names = []
        self.attributes_list.update_item_list([])

    def delete_attribute(self, attribute_name):
        self.attribute_names = []
        for attribute_widget in self.attributes_list.item_widgets[:self.
                                                                  attributes_list
                                                                  .
                                                                  num_visible_item_widgets]:
            self.attribute_names.append(attribute_widget.name.text())
        self.attribute_names.remove(attribute_name)
        self.attributes_list.update_item_list(self.attribute_names)
        self.attributes_list.last_item_widget().name.setFocus()

    def _edit_path_button_clicked(self):
        path = str(
            QFileDialog.getExistingDirectory(
                self, "Choose a directory of text files."))
        if path != "":
            path = f"{path}/*.txt"
            self.path.setText(path)

    def _create_attribute_button_clicked(self):
        self.attribute_names = []
        for attribute_widget in self.attributes_list.item_widgets[:self.
                                                                  attributes_list
                                                                  .
                                                                  num_visible_item_widgets]:
            self.attribute_names.append(attribute_widget.name.text())
        self.attribute_names.append("")
        self.attributes_list.update_item_list(self.attribute_names)
        self.attributes_list.last_item_widget().name.setFocus()

    def _cancel_button_clicked(self):
        self.main_window.show_start_menu_widget()
        self.main_window.enable_global_input()

    def _create_document_base_button_clicked(self):
        self.attribute_names = []
        for attribute_widget in self.attributes_list.item_widgets[:self.
                                                                  attributes_list
                                                                  .
                                                                  num_visible_item_widgets]:
            self.attribute_names.append(attribute_widget.name.text())
        self.main_window.create_document_base_task(self.path.text(),
                                                   self.attribute_names)
示例#7
0
    def __init__(self, parent):
        super(QWidget, self).__init__(parent)
        hlayout = QHBoxLayout()
        self.layout = QGridLayout()
        hlayout.addLayout(self.layout)
        hlayout.setAlignment(hlayout, Qt.Alignment.AlignTop)
        self.setLayout(hlayout)
        self.row = 0

        self.__addLine__(
            "Python Version:",
            "{}.{}.{} {}".format(sys.version_info.major,
                                 sys.version_info.minor,
                                 sys.version_info.micro,
                                 sys.version_info.releaselevel))

        self.__addLine__("Qt Version:", PYQT_VERSION_STR)

        if path.isfile('/etc/system-release'):
            with open('/etc/system-release', 'r') as reader:
                dist = reader.readline()
                self.__addLine__("Distribution:", dist.strip())
        elif path.isfile('/etc/lsb-release'):
            with open('/etc/lsb-release') as reader:
                lsb = reader.readlines()
                line = str(lsb[-1])
                dist = line.strip().split("=")[-1].strip('"')
                self.__addLine__("Distribution:", dist)

        self.__addLine__("Operating System:", platform.uname().system)

        self.__addLine__("Kernel Release:", platform.uname().release)

        self.__addLine__(
            "Total Physical Memory:",
            "{:.1f} GiB".format(psutil.virtual_memory().total / (1024**3)))

        self.__addLine__(
            "Root Disk Space as Total/Used/Free:",
            "{:.1f} GiB/{:.1f} GiB/{:.1f} GiB".format(
                psutil.disk_usage('/').total / (1024**3),
                psutil.disk_usage('/').used / (1024**3),
                psutil.disk_usage('/').free / (1024**3)))

        self.__addLine__(
            "Swap Space as Total/Used/Free:",
            "{:.1f} GiB/{:.2f} GiB/{:.1f} GiB".format(
                psutil.swap_memory().total / (1024**3),
                psutil.swap_memory().used / (1024**3),
                psutil.swap_memory().free / (1024**3)))

        if hasattr(psutil, 'cpu-thermal') and callable(
                getattr(psutil, 'cpu-thermal')):
            self.__addLine__(
                "CPU Temperature:", "{:.1f}\xb0 C".format(
                    psutil.sensors_temperatures()['cpu-thermal'][0].current))
        elif hasattr(psutil, 'thermal-fan-est') and callable(
                getattr(psutil, 'thermal-fan-est')):
            self.__addLine__(
                "CPU Temperature:",
                "{:.1f}\xb0 C".format(psutil.sensors_temperatures()
                                      ['thermal-fan-est'][0].current))
示例#8
0
class MainWindow(QMainWindow):
    ################################
    # signals (aset ui --> aset api)
    ################################
    create_document_base = pyqtSignal(str, list)
    add_attribute = pyqtSignal(str, ASETDocumentBase)
    remove_attribute = pyqtSignal(str, ASETDocumentBase)
    forget_matches_for_attribute = pyqtSignal(str, ASETDocumentBase)
    load_document_base_from_bson = pyqtSignal(str)
    save_document_base_to_bson = pyqtSignal(str, ASETDocumentBase)
    save_table_to_csv = pyqtSignal(str, ASETDocumentBase)
    forget_matches = pyqtSignal(ASETDocumentBase)
    load_preprocessing_phase_from_config = pyqtSignal(str)
    save_preprocessing_phase_to_config = pyqtSignal(str, PreprocessingPhase)
    load_matching_phase_from_config = pyqtSignal(str)
    save_matching_phase_to_config = pyqtSignal(str, BaseMatchingPhase)
    run_preprocessing_phase = pyqtSignal(ASETDocumentBase, PreprocessingPhase, Statistics)
    run_matching_phase = pyqtSignal(ASETDocumentBase, BaseMatchingPhase, Statistics)
    save_statistics_to_json = pyqtSignal(str, Statistics)
    load_and_run_default_preprocessing_phase = pyqtSignal(ASETDocumentBase, Statistics)
    load_and_run_default_matching_phase = pyqtSignal(ASETDocumentBase, Statistics)

    ##############################
    # slots (aset api --> aset ui)
    ##############################
    @pyqtSlot(str, float)
    def status(self, message, progress):
        logger.debug("Called slot 'status'.")

        self.status_widget_message.setText(message)
        if progress == -1:
            self.status_widget_progress.setRange(0, 0)
        else:
            self.status_widget_progress.setRange(0, 100)
            self.status_widget_progress.setValue(int(progress * 100))

    @pyqtSlot(str)
    def finished(self, message):
        logger.debug("Called slot 'finished'.")

        self.status_widget_message.setText(message)
        self.status_widget_progress.setRange(0, 100)
        self.status_widget_progress.setValue(100)

        self.show_document_base_viewer_widget()
        self.enable_global_input()

    @pyqtSlot(str)
    def error(self, message):
        logger.debug("Called slot 'error'.")

        self.status_widget_message.setText(message)
        self.status_widget_progress.setRange(0, 100)
        self.status_widget_progress.setValue(0)

        self.show_document_base_viewer_widget()
        self.enable_global_input()

    @pyqtSlot(ASETDocumentBase)
    def document_base_to_ui(self, document_base):
        logger.debug("Called slot 'document_base_to_ui'.")

        self.document_base = document_base
        self.document_base_viewer_widget.update_document_base(self.document_base)

        self._was_enabled.append(self.save_document_base_to_bson_action)
        self._was_enabled.append(self.save_table_to_csv_action)
        self._was_enabled.append(self.add_attribute_action)
        self._was_enabled.append(self.remove_attribute_action)
        self._was_enabled.append(self.forget_matches_for_attribute_action)
        self._was_enabled.append(self.forget_matches_action)
        self._was_enabled.append(self.load_and_run_default_preprocessing_phase_action)
        self._was_enabled.append(self.load_and_run_default_matching_phase_action)
        if self.preprocessing_phase is not None:
            self._was_enabled.append(self.run_preprocessing_phase_action)
        if self.matching_phase is not None:
            self._was_enabled.append(self.run_matching_phase_action)

    @pyqtSlot(PreprocessingPhase)
    def preprocessing_phase_to_ui(self, preprocessing_phase):
        logger.debug("Called slot 'preprocessing_phase_to_ui'.")

        self.preprocessing_phase = preprocessing_phase

        self._was_enabled.append(self.save_preprocessing_phase_to_config_action)
        if self.document_base is not None:
            self._was_enabled.append(self.run_preprocessing_phase_action)

    @pyqtSlot(BaseMatchingPhase)
    def matching_phase_to_ui(self, matching_phase):
        logger.debug("Called slot 'matching_phase_to_ui'.")

        self.matching_phase = matching_phase

        self._was_enabled.append(self.save_matching_phase_to_config_action)
        if self.document_base is not None:
            self._was_enabled.append(self.run_preprocessing_phase_action)

    @pyqtSlot(Statistics)
    def statistics_to_ui(self, statistics):
        logger.debug("Called slot 'statistics_to_ui'.")

        self.statistics = statistics

    @pyqtSlot(dict)
    def feedback_request_to_ui(self, feedback_request):
        logger.debug("Called slot 'feedback_request_to_ui'.")

        self.interactive_matching_widget.handle_feedback_request(feedback_request)

    # noinspection PyUnresolvedReferences
    def _connect_slots_and_signals(self):
        self.create_document_base.connect(self.api.create_document_base)
        self.add_attribute.connect(self.api.add_attribute)
        self.remove_attribute.connect(self.api.remove_attribute)
        self.forget_matches_for_attribute.connect(self.api.forget_matches_for_attribute)
        self.load_document_base_from_bson.connect(self.api.load_document_base_from_bson)
        self.save_document_base_to_bson.connect(self.api.save_document_base_to_bson)
        self.save_table_to_csv.connect(self.api.save_table_to_csv)
        self.forget_matches.connect(self.api.forget_matches)
        self.load_preprocessing_phase_from_config.connect(self.api.load_preprocessing_phase_from_config)
        self.save_preprocessing_phase_to_config.connect(self.api.save_preprocessing_phase_to_config)
        self.load_matching_phase_from_config.connect(self.api.load_matching_phase_from_config)
        self.save_matching_phase_to_config.connect(self.api.save_matching_phase_to_config)
        self.run_preprocessing_phase.connect(self.api.run_preprocessing_phase)
        self.run_matching_phase.connect(self.api.run_matching_phase)
        self.save_statistics_to_json.connect(self.api.save_statistics_to_json)
        self.load_and_run_default_preprocessing_phase.connect(self.api.load_and_run_default_preprocessing_phase)
        self.load_and_run_default_matching_phase.connect(self.api.load_and_run_default_matching_phase)

        self.api.status.connect(self.status)
        self.api.finished.connect(self.finished)
        self.api.error.connect(self.error)
        self.api.document_base_to_ui.connect(self.document_base_to_ui)
        self.api.preprocessing_phase_to_ui.connect(self.preprocessing_phase_to_ui)
        self.api.matching_phase_to_ui.connect(self.matching_phase_to_ui)
        self.api.statistics_to_ui.connect(self.statistics_to_ui)
        self.api.feedback_request_to_ui.connect(self.feedback_request_to_ui)

    #######
    # tasks
    #######
    def load_document_base_from_bson_task(self):
        logger.info("Execute task 'load_document_base_from_bson_task'.")

        path, ok = QFileDialog.getOpenFileName(self, "Choose a document collection .bson file!")
        if ok:
            self.disable_global_input()
            # noinspection PyUnresolvedReferences
            self.load_document_base_from_bson.emit(str(path))

    def save_document_base_to_bson_task(self):
        logger.info("Execute task 'save_document_base_to_bson_task'.")

        if self.document_base is not None:
            path, ok = QFileDialog.getSaveFileName(self, "Choose where to save the document collection .bson file!")
            if ok:
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.save_document_base_to_bson.emit(str(path), self.document_base)

    def add_attribute_task(self):
        logger.info("Execute task 'add_attribute_task'.")

        if self.document_base is not None:
            name, ok = QInputDialog.getText(self, "Create Attribute", "Attribute name:")
            if ok:
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.add_attribute.emit(str(name), self.document_base)

    def remove_attribute_task(self):
        logger.info("Execute task 'remove_attribute_task'.")

        if self.document_base is not None:
            name, ok = QInputDialog.getText(self, "Remove Attribute", "Attribute name:")
            if ok:
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.remove_attribute.emit(str(name), self.document_base)

    def remove_attribute_with_given_name_task(self, attribute_name):
        logger.info("Execute task 'remove_attribute_with_given_name_task'.")

        if self.document_base is not None:
            self.disable_global_input()
            # noinspection PyUnresolvedReferences
            self.remove_attribute.emit(str(attribute_name), self.document_base)

    def forget_matches_for_attribute_task(self):
        logger.info("Execute task 'forget_matches_for_attribute_task'.")

        if self.document_base is not None:
            name, ok = QInputDialog.getText(self, "Forget Matches for Attribute", "Attribute name:")
            if ok:
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.forget_matches_for_attribute.emit(str(name), self.document_base)

    def forget_matches_for_attribute_with_given_name_task(self, attribute_name):
        logger.info("Execute task 'forget_matches_for_attribute_with_given_name_task'.")

        if self.document_base is not None:
            self.disable_global_input()
            # noinspection PyUnresolvedReferences
            self.forget_matches_for_attribute.emit(attribute_name, self.document_base)

    def forget_matches_task(self):
        logger.info("Execute task 'forget_matches_task'.")

        if self.document_base is not None:
            self.disable_global_input()
            # noinspection PyUnresolvedReferences
            self.forget_matches.emit(self.document_base)

    def enable_collect_statistics_task(self):
        logger.info("Execute task 'task_enable_collect_statistics'.")

        self.collect_statistics = True
        self.enable_collect_statistics_action.setEnabled(False)
        self.disable_collect_statistics_action.setEnabled(True)

    def disable_collect_statistics_task(self):
        logger.info("Execute task 'disable_collect_statistics_task'.")

        self.collect_statistics = False
        self.disable_collect_statistics_action.setEnabled(False)
        self.enable_collect_statistics_action.setEnabled(True)

    def save_statistics_to_json_task(self):
        logger.info("Execute task 'save_statistics_to_json_task'.")

        if self.statistics is not None:
            path = str(QFileDialog.getSaveFileName(self, "Choose where to save the statistics .json file!")[0])
            if path != "":
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.save_statistics_to_json.emit(path, self.statistics)

    def show_document_base_creator_widget_task(self):
        logger.info("Execute task 'show_document_base_creator_widget_task'.")

        self.disable_global_input()
        self.document_base_creator_widget.enable_input()
        self.document_base_creator_widget.initialize_for_new_document_base()
        self.show_document_base_creator_widget()
        self.document_base_creator_widget.path.setFocus()

    def create_document_base_task(self, path, attribute_names):
        logger.info("Execute task 'create_document_base_task'.")

        self.disable_global_input()
        # noinspection PyUnresolvedReferences
        self.create_document_base.emit(path, attribute_names)

    def save_table_to_csv_task(self):
        logger.info("Execute task 'save_table_to_csv_task'.")

        if self.document_base is not None:
            path = str(QFileDialog.getSaveFileName(self, "Choose where to save the table .csv file!")[0])
            if path != "":
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.save_table_to_csv.emit(path, self.document_base)

    def load_preprocessing_phase_from_config_task(self):
        logger.info("Execute task 'load_preprocessing_phase_from_config_task'.")

        path = str(QFileDialog.getOpenFileName(self, "Choose a configuration .json file!")[0])
        if path != "":
            self.disable_global_input()
            # noinspection PyUnresolvedReferences
            self.load_preprocessing_phase_from_config.emit(path)

    def save_preprocessing_phase_to_config_task(self):
        logger.info("Execute task 'save_preprocessing_phase_to_config_task'.")

        if self.preprocessing_phase is not None:
            path = str(QFileDialog.getSaveFileName(self, "Choose where to save the configuration .json file!")[0])
            if path != "":
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.save_preprocessing_phase_to_config.emit(path, self.preprocessing_phase)

    def load_matching_phase_from_config_task(self):
        logger.info("Execute task 'load_matching_phase_from_config_task'.")

        path = str(QFileDialog.getOpenFileName(self, "Choose a configuration .json file!")[0])
        if path != "":
            self.disable_global_input()
            # noinspection PyUnresolvedReferences
            self.load_matching_phase_from_config.emit(path)

    def save_matching_phase_to_config_task(self):
        logger.info("Execute task 'save_matching_phase_to_config_task'.")

        if self.matching_phase is not None:
            path = str(QFileDialog.getSaveFileName(self, "Choose where to save the configuration .json file!")[0])
            if path != "":
                self.disable_global_input()
                # noinspection PyUnresolvedReferences
                self.save_matching_phase_to_config.emit(path, self.matching_phase)

    def run_preprocessing_phase_task(self):
        logger.info("Execute task 'run_preprocessing_phase_task'.")

        if self.document_base is not None and self.preprocessing_phase is not None:
            self.statistics = Statistics(self.collect_statistics)
            self.save_statistics_to_json_action.setEnabled(self.collect_statistics)

            self.disable_global_input()

            # noinspection PyUnresolvedReferences
            self.run_preprocessing_phase.emit(self.document_base, self.preprocessing_phase, self.statistics)

    def run_matching_phase_task(self):
        logger.info("Execute task 'run_matching_phase_task'.")

        if self.document_base is not None and self.matching_phase is not None:
            self.statistics = Statistics(self.collect_statistics)
            self.save_statistics_to_json_action.setEnabled(self.collect_statistics)

            self.disable_global_input()
            self.interactive_matching_widget.enable_input()
            self.show_interactive_matching_widget()

            # noinspection PyUnresolvedReferences
            self.run_matching_phase.emit(self.document_base, self.matching_phase, self.statistics)

    def give_feedback_task(self, feedback):
        logger.info("Execute task 'give_feedback_task'.")

        self.api.feedback = feedback
        self.feedback_cond.wakeAll()

    def load_and_run_default_preprocessing_phase_task(self):
        logger.info("Execute task 'load_and_run_default_preprocessing_phase_task'.")

        if self.document_base is not None:
            self.statistics = Statistics(self.collect_statistics)
            self.save_statistics_to_json_action.setEnabled(self.collect_statistics)

            self.disable_global_input()

            # noinspection PyUnresolvedReferences
            self.load_and_run_default_preprocessing_phase.emit(self.document_base, self.statistics)

    def load_and_run_default_matching_phase_task(self):
        logger.info("Execute task 'load_and_run_default_matching_phase_task'.")

        if self.document_base is not None:
            self.statistics = Statistics(self.collect_statistics)
            self.save_statistics_to_json_action.setEnabled(self.collect_statistics)

            self.disable_global_input()
            self.interactive_matching_widget.enable_input()
            self.show_interactive_matching_widget()

            # noinspection PyUnresolvedReferences
            self.load_and_run_default_matching_phase.emit(self.document_base, self.statistics)

    ##################
    # controller logic
    ##################
    def enable_global_input(self):
        for action in self._was_enabled:
            action.setEnabled(True)

        self.document_base_creator_widget.enable_input()
        self.document_base_viewer_widget.enable_input()
        self.interactive_matching_widget.enable_input()
        self._was_enabled = []

    def disable_global_input(self):
        for action in self._all_actions:
            if action.isEnabled():
                self._was_enabled.append(action)
            action.setEnabled(False)

        self.document_base_creator_widget.disable_input()
        self.document_base_viewer_widget.disable_input()
        self.interactive_matching_widget.disable_input()

    def show_document_base_viewer_widget(self):
        if self.document_base_viewer_widget.isHidden():
            self.central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
            self.start_menu_widget.hide()
            self.interactive_matching_widget.hide()
            self.document_base_creator_widget.hide()

            self.central_widget_layout.removeWidget(self.start_menu_widget)
            self.central_widget_layout.removeWidget(self.interactive_matching_widget)
            self.central_widget_layout.removeWidget(self.document_base_creator_widget)
            self.central_widget_layout.addWidget(self.document_base_viewer_widget)
            self.document_base_viewer_widget.show()
            self.central_widget_layout.update()

    def show_interactive_matching_widget(self):
        if self.interactive_matching_widget.isHidden():
            self.central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
            self.start_menu_widget.hide()
            self.document_base_viewer_widget.hide()
            self.document_base_creator_widget.hide()

            self.central_widget_layout.removeWidget(self.start_menu_widget)
            self.central_widget_layout.removeWidget(self.document_base_viewer_widget)
            self.central_widget_layout.removeWidget(self.document_base_creator_widget)
            self.central_widget_layout.addWidget(self.interactive_matching_widget)
            self.interactive_matching_widget.show()
            self.central_widget_layout.update()

    def show_document_base_creator_widget(self):
        if self.document_base_creator_widget.isHidden():
            self.central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
            self.start_menu_widget.hide()
            self.document_base_viewer_widget.hide()
            self.interactive_matching_widget.hide()

            self.central_widget_layout.removeWidget(self.start_menu_widget)
            self.central_widget_layout.removeWidget(self.document_base_viewer_widget)
            self.central_widget_layout.removeWidget(self.interactive_matching_widget)
            self.central_widget_layout.addWidget(self.document_base_creator_widget)
            self.document_base_creator_widget.show()
            self.central_widget_layout.update()

    def show_start_menu_widget(self):
        if self.start_menu_widget.isHidden():
            self.central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
            self.document_base_viewer_widget.hide()
            self.document_base_creator_widget.hide()
            self.interactive_matching_widget.hide()

            self.central_widget_layout.removeWidget(self.document_base_viewer_widget)
            self.central_widget_layout.removeWidget(self.document_base_creator_widget)
            self.central_widget_layout.removeWidget(self.interactive_matching_widget)
            self.central_widget_layout.addWidget(self.start_menu_widget)
            self.start_menu_widget.show()
            self.central_widget_layout.update()

    def __init__(self) -> None:
        super(MainWindow, self).__init__()
        self.setWindowTitle("ASET")

        self.document_base = None
        self.preprocessing_phase = None
        self.matching_phase = None
        self.statistics = Statistics(True)
        self.collect_statistics = True

        # set up the api_thread and api and connect slots and signals
        self.feedback_mutex = QMutex()
        self.feedback_cond = QWaitCondition()
        self.api = ASETAPI(self.feedback_mutex, self.feedback_cond)
        self.api_thread = QThread()
        self.api.moveToThread(self.api_thread)
        self._connect_slots_and_signals()
        self.api_thread.start()

        # set up the status bar
        self.status_bar = self.statusBar()
        self.status_bar.setFont(STATUS_BAR_FONT)

        self.status_widget = QWidget(self.status_bar)
        self.status_widget_layout = QHBoxLayout(self.status_widget)
        self.status_widget_layout.setContentsMargins(0, 0, 0, 0)
        self.status_widget_message = QLabel()
        self.status_widget_message.setFont(STATUS_BAR_FONT)

        self.status_widget_message.setMinimumWidth(10)
        self.status_widget_layout.addWidget(self.status_widget_message)
        self.status_widget_progress = QProgressBar()
        self.status_widget_progress.setMinimumWidth(10)
        self.status_widget_progress.setMaximumWidth(200)
        self.status_widget_progress.setTextVisible(False)
        self.status_widget_progress.setMaximumHeight(20)
        self.status_widget_layout.addWidget(self.status_widget_progress)
        self.status_bar.addPermanentWidget(self.status_widget)

        # set up the actions
        self._all_actions = []
        self._was_enabled = []

        self.exit_action = QAction("&Exit", self)
        self.exit_action.setIcon(QIcon("aset_ui/resources/leave.svg"))
        self.exit_action.setStatusTip("Exit the application.")
        self.exit_action.triggered.connect(QApplication.instance().quit)
        self._all_actions.append(self.exit_action)

        self.show_document_base_creator_widget_action = QAction("&Create new document base", self)
        self.show_document_base_creator_widget_action.setIcon(QIcon("aset_ui/resources/two_documents.svg"))
        self.show_document_base_creator_widget_action.setStatusTip(
            "Create a new document base from a collection of .txt files and a list of attribute names."
        )
        self.show_document_base_creator_widget_action.triggered.connect(self.show_document_base_creator_widget_task)
        self._all_actions.append(self.show_document_base_creator_widget_action)

        self.add_attribute_action = QAction("&Add attribute", self)
        self.add_attribute_action.setIcon(QIcon("aset_ui/resources/plus.svg"))
        self.add_attribute_action.setStatusTip("Add a new attribute to the document base.")
        self.add_attribute_action.triggered.connect(self.add_attribute_task)
        self.add_attribute_action.setEnabled(False)
        self._all_actions.append(self.add_attribute_action)

        self.remove_attribute_action = QAction("&Remove attribute", self)
        self.remove_attribute_action.setIcon(QIcon("aset_ui/resources/trash.svg"))
        self.remove_attribute_action.setStatusTip("Remove an attribute from the document base.")
        self.remove_attribute_action.triggered.connect(self.remove_attribute_task)
        self.remove_attribute_action.setEnabled(False)
        self._all_actions.append(self.remove_attribute_action)

        self.forget_matches_for_attribute_action = QAction("&Forget matches for attribute", self)
        self.forget_matches_for_attribute_action.setIcon(QIcon("aset_ui/resources/redo.svg"))
        self.forget_matches_for_attribute_action.setStatusTip("Forget the matches for a single attribute.")
        self.forget_matches_for_attribute_action.triggered.connect(self.forget_matches_for_attribute_task)
        self.forget_matches_for_attribute_action.setEnabled(False)
        self._all_actions.append(self.forget_matches_for_attribute_action)

        self.load_document_base_from_bson_action = QAction("&Load document base", self)
        self.load_document_base_from_bson_action.setIcon(QIcon("aset_ui/resources/folder.svg"))
        self.load_document_base_from_bson_action.setStatusTip("Load an existing document base from a .bson file.")
        self.load_document_base_from_bson_action.triggered.connect(self.load_document_base_from_bson_task)
        self._all_actions.append(self.load_document_base_from_bson_action)

        self.save_document_base_to_bson_action = QAction("&Save document base", self)
        self.save_document_base_to_bson_action.setIcon(QIcon("aset_ui/resources/save.svg"))
        self.save_document_base_to_bson_action.setStatusTip("Save the document base in a .bson file.")
        self.save_document_base_to_bson_action.triggered.connect(self.save_document_base_to_bson_task)
        self.save_document_base_to_bson_action.setEnabled(False)
        self._all_actions.append(self.save_document_base_to_bson_action)

        self.save_table_to_csv_action = QAction("&Export table to CSV", self)
        self.save_table_to_csv_action.setIcon(QIcon("aset_ui/resources/table.svg"))
        self.save_table_to_csv_action.setStatusTip("Save the table to a .csv file.")
        self.save_table_to_csv_action.triggered.connect(self.save_table_to_csv_task)
        self.save_table_to_csv_action.setEnabled(False)
        self._all_actions.append(self.save_table_to_csv_action)

        self.forget_matches_action = QAction("&Forget all matches", self)
        self.forget_matches_action.setIcon(QIcon("aset_ui/resources/redo.svg"))
        self.forget_matches_action.setStatusTip("Forget the matches for all attributes.")
        self.forget_matches_action.triggered.connect(self.forget_matches_task)
        self.forget_matches_action.setEnabled(False)
        self._all_actions.append(self.forget_matches_action)

        self.load_and_run_default_preprocessing_phase_action = QAction(
            "&Load and run default preprocessing phase", self
        )
        self.load_and_run_default_preprocessing_phase_action.setStatusTip(
            "Load the default preprocessing phase and run it on the document collection."
        )
        self.load_and_run_default_preprocessing_phase_action.setIcon(QIcon("aset_ui/resources/run_run.svg"))
        self.load_and_run_default_preprocessing_phase_action.setDisabled(True)
        self.load_and_run_default_preprocessing_phase_action.triggered.connect(
            self.load_and_run_default_preprocessing_phase_task
        )
        self._all_actions.append(self.load_and_run_default_preprocessing_phase_action)

        self.load_preprocessing_phase_from_config_action = QAction("&Load preprocessing phase", self)
        self.load_preprocessing_phase_from_config_action.setStatusTip(
            "Load a preprocessing phase from a .json configuration file."
        )
        self.load_preprocessing_phase_from_config_action.triggered.connect(
            self.load_preprocessing_phase_from_config_task
        )
        self._all_actions.append(self.load_preprocessing_phase_from_config_action)

        self.save_preprocessing_phase_to_config_action = QAction("&Save preprocessing phase", self)
        self.save_preprocessing_phase_to_config_action.setStatusTip(
            "Save the preprocessing phase in a .json configuration file."
        )
        self.save_preprocessing_phase_to_config_action.triggered.connect(self.save_preprocessing_phase_to_config_task)
        self.save_preprocessing_phase_to_config_action.setEnabled(False)
        self._all_actions.append(self.save_preprocessing_phase_to_config_action)

        self.run_preprocessing_phase_action = QAction("Run preprocessing phase", self)
        self.run_preprocessing_phase_action.setIcon(QIcon("aset_ui/resources/run.svg"))
        self.run_preprocessing_phase_action.setStatusTip("Run the preprocessing phase on the document collection.")
        self.run_preprocessing_phase_action.triggered.connect(self.run_preprocessing_phase_task)
        self.run_preprocessing_phase_action.setEnabled(False)
        self._all_actions.append(self.run_preprocessing_phase_action)

        self.load_and_run_default_matching_phase_action = QAction(
            "&Load and run default matching phase", self
        )
        self.load_and_run_default_matching_phase_action.setStatusTip(
            "Load the default matching phase and run it on the document collection."
        )
        self.load_and_run_default_matching_phase_action.setIcon(QIcon("aset_ui/resources/run_run.svg"))
        self.load_and_run_default_matching_phase_action.setDisabled(True)
        self.load_and_run_default_matching_phase_action.triggered.connect(
            self.load_and_run_default_preprocessing_phase_task
        )
        self._all_actions.append(self.load_and_run_default_matching_phase_action)

        self.load_matching_phase_from_config_action = QAction("&Load matching phase", self)
        self.load_matching_phase_from_config_action.setStatusTip(
            "Load a matching phase from a .json configuration file."
        )
        self.load_matching_phase_from_config_action.triggered.connect(self.load_matching_phase_from_config_task)
        self._all_actions.append(self.load_matching_phase_from_config_action)

        self.save_matching_phase_to_config_action = QAction("&Save matching phase", self)
        self.save_matching_phase_to_config_action.setStatusTip("Save the matching phase in a .json configuration file.")
        self.save_matching_phase_to_config_action.triggered.connect(self.save_matching_phase_to_config_task)
        self.save_matching_phase_to_config_action.setEnabled(False)
        self._all_actions.append(self.save_matching_phase_to_config_action)

        self.run_matching_phase_action = QAction("Run matching phase", self)
        self.run_matching_phase_action.setIcon(QIcon("aset_ui/resources/run.svg"))
        self.run_matching_phase_action.setStatusTip("Run the matching phase on the document collection.")
        self.run_matching_phase_action.triggered.connect(self.run_matching_phase_task)
        self.run_matching_phase_action.setEnabled(False)
        self._all_actions.append(self.run_matching_phase_action)

        self.enable_collect_statistics_action = QAction("&Enable statistics", self)
        self.enable_collect_statistics_action.setIcon(QIcon("aset_ui/resources/statistics.svg"))
        self.enable_collect_statistics_action.setStatusTip("Enable collecting statistics.")
        self.enable_collect_statistics_action.triggered.connect(self.enable_collect_statistics_task)
        self.enable_collect_statistics_action.setEnabled(False)
        self._all_actions.append(self.enable_collect_statistics_action)

        self.disable_collect_statistics_action = QAction("&Disable statistics", self)
        self.disable_collect_statistics_action.setIcon(QIcon("aset_ui/resources/statistics_incorrect.svg"))
        self.disable_collect_statistics_action.setStatusTip("Disable collecting statistics.")
        self.disable_collect_statistics_action.triggered.connect(self.disable_collect_statistics_task)
        self._all_actions.append(self.disable_collect_statistics_action)

        self.save_statistics_to_json_action = QAction("&Save statistics", self)
        self.save_statistics_to_json_action.setIcon(QIcon("aset_ui/resources/statistics_save.svg"))
        self.save_statistics_to_json_action.setStatusTip("Save the statistics to a .json file.")
        self.save_statistics_to_json_action.triggered.connect(self.save_statistics_to_json_task)
        self.save_statistics_to_json_action.setEnabled(False)
        self._all_actions.append(self.save_statistics_to_json_action)

        # set up the menu bar
        self.menubar = self.menuBar()
        self.menubar.setFont(MENU_FONT)

        self.file_menu = self.menubar.addMenu("&File")
        self.file_menu.setFont(MENU_FONT)
        self.file_menu.addAction(self.exit_action)

        self.document_base_menu = self.menubar.addMenu("&Document Base")
        self.document_base_menu.setFont(MENU_FONT)
        self.document_base_menu.addAction(self.show_document_base_creator_widget_action)
        self.document_base_menu.addSeparator()
        self.document_base_menu.addAction(self.load_document_base_from_bson_action)
        self.document_base_menu.addAction(self.save_document_base_to_bson_action)
        self.document_base_menu.addSeparator()
        self.document_base_menu.addAction(self.save_table_to_csv_action)
        self.document_base_menu.addSeparator()
        self.document_base_menu.addAction(self.add_attribute_action)
        self.document_base_menu.addAction(self.remove_attribute_action)
        self.document_base_menu.addSeparator()
        self.document_base_menu.addAction(self.forget_matches_for_attribute_action)
        self.document_base_menu.addAction(self.forget_matches_action)

        self.preprocessing_menu = self.menubar.addMenu("&Preprocessing")
        self.preprocessing_menu.setFont(MENU_FONT)
        self.preprocessing_menu.addAction(self.load_and_run_default_preprocessing_phase_action)
        self.preprocessing_menu.addSeparator()
        self.preprocessing_menu.addAction(self.load_preprocessing_phase_from_config_action)
        self.preprocessing_menu.addAction(self.save_preprocessing_phase_to_config_action)
        self.preprocessing_menu.addSeparator()
        self.preprocessing_menu.addAction(self.run_preprocessing_phase_action)

        self.matching_menu = self.menubar.addMenu("&Matching")
        self.matching_menu.setFont(MENU_FONT)
        self.matching_menu.addAction(self.load_and_run_default_matching_phase_action)
        self.matching_menu.addSeparator()
        self.matching_menu.addAction(self.load_matching_phase_from_config_action)
        self.matching_menu.addAction(self.save_matching_phase_to_config_action)
        self.matching_menu.addSeparator()
        self.matching_menu.addAction(self.run_matching_phase_action)

        self.statistics_menu = self.menubar.addMenu("&Statistics")
        self.statistics_menu.setFont(MENU_FONT)
        self.statistics_menu.addAction(self.enable_collect_statistics_action)
        self.statistics_menu.addAction(self.disable_collect_statistics_action)
        self.statistics_menu.addSeparator()
        self.statistics_menu.addAction(self.save_statistics_to_json_action)

        # start menu
        self.start_menu_widget = QWidget()
        self.start_menu_layout = QVBoxLayout(self.start_menu_widget)
        self.start_menu_layout.setContentsMargins(0, 0, 0, 0)
        self.start_menu_layout.setSpacing(30)
        self.start_menu_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        self.start_menu_widget.setMaximumWidth(400)

        self.start_menu_header = QLabel("Welcome to ASET!")
        self.start_menu_header.setFont(HEADER_FONT)
        self.start_menu_layout.addWidget(self.start_menu_header)

        self.start_menu_create_new_document_base_widget = QWidget()
        self.start_menu_create_new_document_base_layout = QVBoxLayout(self.start_menu_create_new_document_base_widget)
        self.start_menu_create_new_document_base_layout.setContentsMargins(0, 0, 0, 0)
        self.start_menu_create_new_document_base_layout.setSpacing(10)
        self.start_menu_layout.addWidget(self.start_menu_create_new_document_base_widget)

        self.start_menu_create_new_document_base_subheader = QLabel("Create a new document base.")
        self.start_menu_create_new_document_base_subheader.setFont(SUBHEADER_FONT)
        self.start_menu_create_new_document_base_layout.addWidget(self.start_menu_create_new_document_base_subheader)

        self.start_menu_create_new_document_base_wrapper_widget = QWidget()
        self.start_menu_create_new_document_base_wrapper_layout = QHBoxLayout(
            self.start_menu_create_new_document_base_wrapper_widget)
        self.start_menu_create_new_document_base_wrapper_layout.setContentsMargins(0, 0, 0, 0)
        self.start_menu_create_new_document_base_wrapper_layout.setSpacing(20)
        self.start_menu_create_new_document_base_wrapper_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.start_menu_create_new_document_base_layout.addWidget(
            self.start_menu_create_new_document_base_wrapper_widget)

        self.start_menu_create_document_base_button = QPushButton()
        self.start_menu_create_document_base_button.setFixedHeight(45)
        self.start_menu_create_document_base_button.setFixedWidth(45)
        self.start_menu_create_document_base_button.setIcon(QIcon("aset_ui/resources/two_documents.svg"))
        self.start_menu_create_document_base_button.clicked.connect(self.show_document_base_creator_widget_task)
        self.start_menu_create_new_document_base_wrapper_layout.addWidget(self.start_menu_create_document_base_button)

        self.start_menu_create_document_base_label = QLabel(
            "Create a new document base from a directory\nof .txt files and a list of attribute names.")
        self.start_menu_create_document_base_label.setFont(LABEL_FONT)
        self.start_menu_create_new_document_base_wrapper_layout.addWidget(self.start_menu_create_document_base_label)

        self.start_menu_load_document_base_widget = QWidget()
        self.start_menu_load_document_base_layout = QVBoxLayout(self.start_menu_load_document_base_widget)
        self.start_menu_load_document_base_layout.setContentsMargins(0, 0, 0, 0)
        self.start_menu_load_document_base_layout.setSpacing(10)
        self.start_menu_layout.addWidget(self.start_menu_load_document_base_widget)

        self.start_menu_load_document_base_subheader = QLabel("Load an existing document base.")
        self.start_menu_load_document_base_subheader.setFont(SUBHEADER_FONT)
        self.start_menu_load_document_base_layout.addWidget(self.start_menu_load_document_base_subheader)

        self.start_menu_load_document_base_wrapper_widget = QWidget()
        self.start_menu_load_document_base_wrapper_layout = QHBoxLayout(
            self.start_menu_load_document_base_wrapper_widget)
        self.start_menu_load_document_base_wrapper_layout.setContentsMargins(0, 0, 0, 0)
        self.start_menu_load_document_base_wrapper_layout.setSpacing(20)
        self.start_menu_load_document_base_wrapper_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.start_menu_load_document_base_layout.addWidget(self.start_menu_load_document_base_wrapper_widget)

        self.start_menu_load_document_base_button = QPushButton()
        self.start_menu_load_document_base_button.setFixedHeight(45)
        self.start_menu_load_document_base_button.setFixedWidth(45)
        self.start_menu_load_document_base_button.setIcon(QIcon("aset_ui/resources/folder.svg"))
        self.start_menu_load_document_base_button.clicked.connect(self.load_document_base_from_bson_task)
        self.start_menu_load_document_base_wrapper_layout.addWidget(self.start_menu_load_document_base_button)

        self.start_menu_load_document_base_label = QLabel("Load an existing document base\nfrom a .bson file.")
        self.start_menu_load_document_base_label.setFont(LABEL_FONT)
        self.start_menu_load_document_base_wrapper_layout.addWidget(self.start_menu_load_document_base_label)

        # main UI
        self.central_widget = QWidget(self)
        self.central_widget_layout = QHBoxLayout(self.central_widget)
        self.central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setCentralWidget(self.central_widget)

        self.document_base_creator_widget = DocumentBaseCreatorWidget(self)
        self.document_base_viewer_widget = DocumentBaseViewerWidget(self)
        self.interactive_matching_widget = InteractiveMatchingWidget(self)

        self.document_base_creator_widget.hide()
        self.document_base_viewer_widget.hide()
        self.interactive_matching_widget.hide()
        self.central_widget_layout.addWidget(self.start_menu_widget)
        self.central_widget_layout.update()

        self.resize(1400, 800)
        self.show()

        logger.info("Initialized MainWindow.")
示例#9
0
    def __init__(self, main_window: QWidget):
        super().__init__()
        self.search_callback = None
        # 主题空间 子组件都放这个Widget里
        self.centralWidget = QtWidgets.QWidget(main_window)
        self.centralWidget.setObjectName("centralWidget")
        # 搜索框
        self.souInput = QtWidgets.QLineEdit(self.centralWidget)
        self.souInput.setGeometry(QtCore.QRect(40, 30, 800, 30))
        font = QtGui.QFont()
        font.setPointSize(22)
        font.setKerning(True)
        font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault)
        self.souInput.setFont(font)
        self.souInput.setObjectName("souInput")
        self.souInput.setText("龙珠")

        self.modBox = CheckableComboBox(self.centralWidget)
        self.modBox.setGeometry(QtCore.QRect(850, 30, 120, 30))
        for k in constant.mod_dist.keys():
            if k in constant.mod_list:
                self.modBox.addItem(QtCore.Qt.CheckState.Checked, k)
            else:
                self.modBox.addItem(QtCore.Qt.CheckState.Unchecked, k)

        # QTabWidget tab页签
        self.tabWidget = QtWidgets.QTabWidget(self.centralWidget)
        self.tabWidget.setGeometry(QtCore.QRect(40, 70, 944, 668))
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setObjectName("tabWidget")
        # 下载页面
        self.down_tab = QtWidgets.QWidget()
        self.down_tab.setStatusTip("")
        self.down_tab.setAutoFillBackground(False)
        self.down_tab.setObjectName("down_tab")
        self.tabWidget.addTab(self.down_tab, "下载列表")
        # 书架页面
        self.bookshelf_tab = QtWidgets.QWidget()
        self.bookshelf_tab.setObjectName("bookshelf_tab")
        self.tabWidget.addTab(self.bookshelf_tab, "书架")
        # 搜索结果页面
        self.search_tab = QtWidgets.QWidget()
        self.search_tab.setObjectName("search_tab")
        self.tabWidget.addTab(self.search_tab, "搜索结果")
        # None 空按钮,tab签右侧按钮,设置到前面
        tbr = self.tabWidget.tabBar().tabButton(0, QTabBar.ButtonPosition.RightSide)
        self.tabWidget.tabBar().setTabButton(0, QTabBar.ButtonPosition.LeftSide, tbr)
        self.tabWidget.tabBar().setTabButton(1, QTabBar.ButtonPosition.LeftSide, tbr)
        self.tabWidget.tabBar().setTabButton(2, QTabBar.ButtonPosition.LeftSide, tbr)
        # 启用关闭页签的功能
        self.tabWidget.tabCloseRequested.connect(self.tab_close)
        # 默认打开到书架
        self.tabWidget.setCurrentIndex(1)
        # 主体的centralWidget 放到主窗口中
        main_window.setCentralWidget(self.centralWidget)
        # 书架页
        self.bookshelfVBoxLayout = QVBoxLayout()
        self.bookshelfGroupBox = QGroupBox()
        self.bookshelfScroll = QScrollArea()
        self.bookshelfLayout = QVBoxLayout(self.bookshelf_tab)
        self.bookshelfVBoxLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)

        self.bookshelfGroupBox.setLayout(self.bookshelfVBoxLayout)
        self.bookshelfScroll.setWidget(self.bookshelfGroupBox)
        self.bookshelfScroll.setWidgetResizable(True)
        self.bookshelfLayout.addWidget(self.bookshelfScroll)
        # 搜索页
        self.searchVBoxLayout = QVBoxLayout()
        self.searchGroupBox = QGroupBox()
        self.searchScroll = QScrollArea()
        self.searchLayout = QVBoxLayout(self.search_tab)
        self.searchVBoxLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)

        self.searchGroupBox.setLayout(self.searchVBoxLayout)
        self.searchScroll.setWidget(self.searchGroupBox)
        self.searchScroll.setWidgetResizable(True)
        self.searchLayout.addWidget(self.searchScroll)
        # 下载页
        self.downVBoxLayout = QVBoxLayout()
        self.downGroupBox = QGroupBox()
        self.downScroll = QScrollArea()
        self.downLayout = QVBoxLayout(self.down_tab)
        self.downVBoxLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)

        self.downGroupBox.setLayout(self.downVBoxLayout)
        self.downScroll.setWidget(self.downGroupBox)
        self.downScroll.setWidgetResizable(True)
        self.downLayout.addWidget(self.downScroll)
        down_button_layout = QHBoxLayout()
        self.downLayout.addLayout(down_button_layout)
        down_button_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
        all_start = QPushButton()
        all_start.setText("全部开始")
        all_stop = QPushButton()
        all_stop.setText("全部停止")
        clear_done = QPushButton()
        clear_done.setText("清理已完成")

        down_button_layout.addWidget(all_start)
        down_button_layout.addWidget(all_stop)
        down_button_layout.addWidget(clear_done)

        self.souInput.returnPressed.connect(self.input_return_pressed)  # 回车搜索

        self.load_comic_list_signa.connect(self.search_load_comic_list)  # 更新ui的插槽

        self.bookshelf_load_comic_list()
        self.download_callback()
示例#10
0
class CalibrateWizard(QWizard):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.setWizardStyle(QWizard.WizardStyle.ModernStyle)

        # CREATE PAGE 1, LINE EDIT, TITLES
        buttons_layout = [QWizard.WizardButton.NextButton]
        self.page1 = QWizardPage()
        self.page1.setTitle('Select the exercises you wish to do later')
        self.page1.setSubTitle(
            'Below are listed all the available and selected exercises by you.'
        )
        self.listSelection = TwoListSelection()
        # listSelection.addAvailableItems(["item-{}".format(i) for i in range(5)])
        hLayout1 = QHBoxLayout(self.page1)
        hLayout1.addWidget(self.listSelection)

        # CREATE PAGE 2, LABEL, TITLES
        self.page2 = QWizardPage()
        self.page2.setFinalPage(True)
        self.setButtonLayout(buttons_layout)
        self.page2.setTitle('Calibrate every exercise')
        self.page2.setSubTitle(
            'Do every exercise once, record after pressing button.')
        self.contentLayout = QVBoxLayout(self.page2)
        self.hLayout2 = QHBoxLayout()

        # Create progress bar, buttons
        self.actionsLayout = QHBoxLayout()
        self.finishButton = QPushButton('Ready')
        self.finishButton.setStyleSheet(CustomQStyles.buttonStyle)
        self.finishButton.setFixedSize(120, 35)

        self.progress = QProgressBar()
        self.progress.setRange(0, 1)
        self.actionsLayout.addWidget(self.progress)
        self.actionsLayout.setAlignment(self.progress,
                                        Qt.Alignment.AlignBottom)
        self.actionsLayout.addWidget(self.finishButton)
        self.actionsLayout.setAlignment(self.finishButton,
                                        Qt.Alignment.AlignBottom)

        self.contentLayout.addLayout(self.hLayout2)
        self.contentLayout.addLayout(self.actionsLayout)
        self.actionsLayout.setContentsMargins(15, 35, 15, 0)
        itemsTextList = [
            str(self.listSelection.mInput.item(i).text())
            for i in range(self.listSelection.mInput.count())
        ]
        print("items:", itemsTextList)

        self.button(QWizard.WizardButton.NextButton).clicked.connect(
            self.onWizardNextButton)
        self.finishButton.clicked.connect(self.onWizardFinishButton)

        self.addPage(self.page1)
        self.addPage(self.page2)

        # Recording data
        self.buttons = []
        self.images = []
        self.labels = []
        self.exerciseLayouts = []
        self.recordReady = []
        self.recordThread = RecordThread(self.parent.classifyExercises)

        # Training recorded data
        self.trained = False
        self.trainThread = TrainThread(self.parent.classifyExercises)
        self.trainThread.taskFinished.connect(self.onTrainFinished)

    # Send list to next page
    def onWizardNextButton(self):
        self.setPage(1, self.page1)
        self.trained = False
        itemsTextList = [
            str(self.listSelection.mInput.item(i).text())
            for i in range(self.listSelection.mInput.count())
        ]
        # Update list
        if self.parent.classifyExercises is not None:
            self.parent.classifyExercises.UpdateExerciseList(itemsTextList)

        # Set elements on UI
        self.setMinimumWidth(len(itemsTextList) * 200)
        self.deleteItemsOfLayout(self.hLayout2)
        self.images.clear()
        self.labels.clear()
        self.buttons.clear()
        self.recordReady.clear()
        for x, i in zip(itemsTextList, range(len(itemsTextList))):
            self.exerciseLayouts.append(QVBoxLayout())
            self.buttons.append(QPushButton('Record'))
            self.recordReady.append(False)
            image = QLabel()
            image.setPixmap(
                QPixmap(os.getcwd() + "/resources/images/" + itemsTextList[i] +
                        ".png"))
            self.labels.append(QLabel(itemsTextList[i]))
            self.images.append(image)
            self.buttons[i].setFixedSize(100, 35)
            self.buttons[i].clicked.connect(
                functools.partial(self.onRecordExerciseButtonClicked, x, i))
            self.buttons[i].setStyleSheet(CustomQStyles.outlineButtonStyle)
            self.exerciseLayouts[i].addWidget(self.labels[i])
            self.exerciseLayouts[i].addWidget(self.images[i])
            self.exerciseLayouts[i].addWidget(self.buttons[i])
            self.exerciseLayouts[i].setAlignment(self.labels[i],
                                                 Qt.Alignment.AlignCenter)
            self.exerciseLayouts[i].setAlignment(self.images[i],
                                                 Qt.Alignment.AlignCenter)
            self.exerciseLayouts[i].setAlignment(self.buttons[i],
                                                 Qt.Alignment.AlignCenter)
            self.hLayout2.addLayout(self.exerciseLayouts[i])

    def onRecordExerciseButtonClicked(self, exercise, ind):
        print("Recording - ", exercise)
        if self.parent.classifyExercises is not None:
            self.recordThread.exercise = exercise
            self.recordThread.taskFinished.connect(
                functools.partial(self.recordFinished, exercise, ind),
                Qt.ConnectionType.SingleShotConnection)
            self.recordThread.start()
            self.recordReady[ind] = False
            self.buttons[ind].setStyleSheet(CustomQStyles.recordButtonStyle)
            self.images[ind].setPixmap(
                QPixmap(os.getcwd() + "/resources/images/" + exercise +
                        ".png"))

    def recordFinished(self, exercise, index):
        imagePath = os.getcwd() + "/resources/images/" + exercise + ".png"
        if self.recordThread.result == 0:
            imagePath = os.getcwd(
            ) + "/resources/images/" + exercise + "-fail.png"
        elif self.recordThread.result == 1:
            imagePath = os.getcwd(
            ) + "/resources/images/" + exercise + "-success.png"
            self.recordReady[index] = True
        else:
            print("None.")
        self.images[index].setPixmap(QPixmap(imagePath))
        self.buttons[index].setStyleSheet(CustomQStyles.outlineButtonStyle)
        print(self.recordReady)

    def onWizardFinishButton(self):
        if all(x == True for x in self.recordReady):
            print("All recorded!")
            if not self.trained:
                if self.parent.classifyExercises is not None:
                    self.progress.setRange(0, 0)  # indefinite progress bar
                    self.parent.classifyExercises.SaveProcessedData()
                    self.parent.classifyExercises.SavePatientData()
                    self.parent.ui.loadPatientList()
                    self.trainThread.start()

            else:
                self.close()

        else:
            print("Not all recorded!")

    def onTrainFinished(self):
        self.progress.setRange(0, 1)
        self.progress.setValue(1)
        self.trained = True
        CustomMessage.showDialog("Message", "Training model finished!",
                                 QMessageBox.StandardButtons.Ok)
        self.finishButton.setText('Finish')

    def deleteItemsOfLayout(self, layout):
        if layout is not None:
            while layout.count():
                item = layout.takeAt(0)
                widget = item.widget()
                if widget is not None:
                    widget.setParent(None)
                else:
                    self.deleteItemsOfLayout(item.layout())
示例#11
0
class TestWidget(QVBoxLayout):
    def __init__(self, parent):
        super().__init__(parent)
        self.classifyExercises = parent.classifyExercises
        self.columns = [
            'Set', 'Exercise', 'Assigned Key', 'Accuracy', 'Latency', 'Avg.lat'
        ]
        self.exerciseLayouts = []
        self.setButtons = []
        self.initUi()
        print("init tab2")

    def initUi(self):
        # top labels
        self.mainLayout = QVBoxLayout()
        self.labels = QHBoxLayout()
        for x in self.columns:
            label = QLabel(x)
            self.labels.addWidget(label)
            self.labels.setAlignment(label, QtCore.Qt.Alignment.AlignHCenter)

        self.mainLayout.addLayout(self.labels)
        self.mainLayout.setAlignment(self.labels, QtCore.Qt.Alignment.AlignTop)
        if self.classifyExercises is not None:
            for e in range(0, self.classifyExercises.number_of_gestures):
                alignment = self.exerciseLayout(e)
                self.mainLayout.addLayout(alignment)
                self.mainLayout.setAlignment(alignment,
                                             QtCore.Qt.Alignment.AlignTop)
        self.addLayout(self.mainLayout)
        self.setAlignment(self.mainLayout, QtCore.Qt.Alignment.AlignTop)

    def exerciseLayout(self, index):
        hLayout = QHBoxLayout()
        setSpinBox = QSpinBox()
        setSpinBox.setRange(1, 100)
        setSpinBox.setValue(5)
        setSpinBox.setMaximumWidth(80)

        startButton = QPushButton(self.classifyExercises.exercises[index].name)
        startButton.setMaximumWidth(120)

        assignedKey = QPushButton(
            self.classifyExercises.exercises[index].assigned_key[0])
        assignedKey.setStyleSheet('background-color: grey; color: white;')
        assignedKey.setMaximumWidth(50)
        assignedKey.setEnabled(False)

        progress = QProgressBar()
        progress.setMaximumWidth(150)
        progress.setValue(randint(1, 100))
        progress.setAlignment(QtCore.Qt.Alignment.AlignCenter)

        latency = QLabel(str(randint(50, 250)) + 'ms')

        avgLatency = QLabel(str(randint(50, 250)) + 'ms')

        hLayout.addWidget(setSpinBox)
        hLayout.setAlignment(setSpinBox, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(startButton)
        hLayout.setAlignment(startButton, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(assignedKey)
        hLayout.setAlignment(assignedKey, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(progress)
        hLayout.setAlignment(progress, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(latency)
        hLayout.setAlignment(latency, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(avgLatency)
        hLayout.setAlignment(avgLatency, QtCore.Qt.Alignment.AlignHCenter)

        hLayout.setContentsMargins(0, 10, 0, 10)
        self.exerciseLayouts.append(hLayout)
        return hLayout
示例#12
0
    def exerciseLayout(self, index):
        hLayout = QHBoxLayout()
        setSpinBox = QSpinBox()
        setSpinBox.setRange(1, 100)
        setSpinBox.setValue(5)
        setSpinBox.setMaximumWidth(80)

        startButton = QPushButton(self.classifyExercises.exercises[index].name)
        startButton.setMaximumWidth(120)

        assignedKey = QPushButton(
            self.classifyExercises.exercises[index].assigned_key[0])
        assignedKey.setStyleSheet('background-color: grey; color: white;')
        assignedKey.setMaximumWidth(50)
        assignedKey.setEnabled(False)

        progress = QProgressBar()
        progress.setMaximumWidth(150)
        progress.setValue(randint(1, 100))
        progress.setAlignment(QtCore.Qt.Alignment.AlignCenter)

        latency = QLabel(str(randint(50, 250)) + 'ms')

        avgLatency = QLabel(str(randint(50, 250)) + 'ms')

        hLayout.addWidget(setSpinBox)
        hLayout.setAlignment(setSpinBox, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(startButton)
        hLayout.setAlignment(startButton, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(assignedKey)
        hLayout.setAlignment(assignedKey, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(progress)
        hLayout.setAlignment(progress, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(latency)
        hLayout.setAlignment(latency, QtCore.Qt.Alignment.AlignHCenter)
        hLayout.addWidget(avgLatency)
        hLayout.setAlignment(avgLatency, QtCore.Qt.Alignment.AlignHCenter)

        hLayout.setContentsMargins(0, 10, 0, 10)
        self.exerciseLayouts.append(hLayout)
        return hLayout
    def __init__(self):
        super().__init__()
        self.setWindowTitle("📸 Random Image Tools V1.2 🔨")
        self.image_list = []
        # ToDo: Add a "rename?" flag. Like rename image files

        # to store the directory path for later use
        self.directory_path_name = "/"

        # Set up the layouts
        layer_one = QHBoxLayout()  # Select a folder, selected directory

        layer_one_and_a_half = QHBoxLayout()  # Selected directory contents

        layer_two = QHBoxLayout()  # Second line of buttons
        layer_two_vertical_one = QVBoxLayout()  # Store the first column w/checkbox and "Convert"
        layer_two_vertical_two = QVBoxLayout()  # Store the second column w/checkbox and "Open in File Browser"

        layer_three = QHBoxLayout()  # Conversion process state
        vertical_layout_parent = QVBoxLayout()

        # Parent widget
        widget = QWidget()

        # Displays selected directory
        self.directory_label = QLabel()
        self.directory_label.setText("Directory to be worked on will show here            ")
        self.directory_label.show()

        # Displays "Select folder" button
        self.select_a_folder_button = QPushButton()
        self.select_a_folder_button.setText("Select a folder:")
        self.select_a_folder_button.clicked.connect(self.select_folder_prompt)
        self.select_a_folder_button.show()

        # Displays the image contents of the selected folder
        self.image_paths_list_widget = QListWidget()
        self.image_paths_list_widget.show()

        # Displays button to initiate image conversion
        self.convert_to_png_button = QPushButton()
        self.convert_to_png_button.setText("Convert to PNG")
        self.convert_to_png_button.clicked.connect(self.convert_folder_to_png)
        self.convert_to_png_button.show()

        # Check boxes for "Create new folder for PNGs" and "Delete original files after converting"
        self.create_new_folder_checkbox = QCheckBox()
        self.create_new_folder_checkbox.setText("Create new folder to store converted PNG's?")
        self.create_new_folder_checkbox.show()

        self.delete_original_files_checkbox = QCheckBox()
        self.delete_original_files_checkbox.setText("Delete original files after converting them to PNG?")
        self.create_new_folder_checkbox.show()

        # Displays button to open selected directory in the file browser
        self.show_folder_button = QPushButton()
        self.show_folder_button.setText("Open selected folder in file browser")
        self.show_folder_button.clicked.connect(self.open_folder)
        self.show_folder_button.show()

        # Displays label when conversion is finished, and the corresponding progress bar
        self.conversion_finished_or_error_label = QLabel()
        self.conversion_finished_or_error_label.setText("👀 waiting for you to press \"Convert to PNG\" ")

        # Put the find folder button and folder selected button together
        layer_one.addWidget(self.select_a_folder_button)
        layer_one.addWidget(self.directory_label)

        # Image paths of selected folder
        layer_one_and_a_half.addWidget(self.image_paths_list_widget)

        # Put the convert button and open-in-finder button together
        layer_two_vertical_one.addWidget(self.convert_to_png_button)
        layer_two_vertical_one.addWidget(self.delete_original_files_checkbox)
        layer_two.addLayout(layer_two_vertical_one)

        layer_two_vertical_two.addWidget(self.show_folder_button)
        layer_two_vertical_two.addWidget(self.create_new_folder_checkbox)
        layer_two.addLayout(layer_two_vertical_two)

        # Label and progress bar
        layer_three.addWidget(self.conversion_finished_or_error_label)
        layer_three.setAlignment(Qt.AlignmentFlag.AlignHCenter)

        # Put the "convert to png" button beneath
        vertical_layout_parent.addLayout(layer_one)
        vertical_layout_parent.addLayout(layer_one_and_a_half)
        vertical_layout_parent.addLayout(layer_two)
        vertical_layout_parent.addLayout(layer_three)

        widget.setLayout(vertical_layout_parent)
        self.setCentralWidget(widget)
示例#14
0
    def initUI(self):
        self.setWindowTitle("登录蓝奏云")
        self.setWindowIcon(QIcon(SRC_DIR + "login.ico"))
        logo = QLabel()
        logo.setPixmap(QPixmap(SRC_DIR + "logo3.gif"))
        logo.setStyleSheet("background-color:rgb(0,153,255);")
        logo.setAlignment(Qt.AlignmentFlag.AlignCenter)

        self.tabs = QTabWidget()
        self.auto_tab = QWidget()
        self.hand_tab = QWidget()

        # Add tabs
        self.tabs.addTab(self.auto_tab,"自动获取Cookie")
        self.tabs.addTab(self.hand_tab,"手动输入Cookie")
        self.auto_get_cookie_ok = AutoResizingTextEdit("🔶点击👇自动获取浏览器登录信息👇")
        self.auto_get_cookie_ok.setReadOnly(True)
        self.auto_get_cookie_btn = QPushButton("自动读取浏览器登录信息")
        auto_cookie_notice = '支持浏览器:Chrome, Chromium, Opera, Edge, Firefox'
        self.auto_get_cookie_btn.setToolTip(auto_cookie_notice)
        self.auto_get_cookie_btn.clicked.connect(self.call_auto_get_cookie)
        self.auto_get_cookie_btn.setStyleSheet("QPushButton {min-width: 210px;max-width: 210px;}")

        self.name_lb = QLabel("&U 用户")
        self.name_lb.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.name_ed = QLineEdit()
        self.name_lb.setBuddy(self.name_ed)

        self.pwd_lb = QLabel("&P 密码")
        self.pwd_lb.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.pwd_ed = QLineEdit()
        self.pwd_ed.setEchoMode(QLineEdit.EchoMode.Password)
        self.pwd_lb.setBuddy(self.pwd_ed)

        self.cookie_lb = QLabel("&Cookie")
        self.cookie_ed = QTextEdit()
        notice = "由于滑动验证的存在,需要输入cookie,cookie请使用浏览器获取\n" + \
            "cookie会保存在本地,下次使用。其格式如下:\n ylogin=value1; phpdisk_info=value2"
        self.cookie_ed.setPlaceholderText(notice)
        self.cookie_lb.setBuddy(self.cookie_ed)

        self.show_input_cookie_btn = QPushButton("显示Cookie输入框")
        self.show_input_cookie_btn.setToolTip(notice)
        self.show_input_cookie_btn.setStyleSheet("QPushButton {min-width: 110px;max-width: 110px;}")
        self.show_input_cookie_btn.clicked.connect(self.change_show_input_cookie)
        self.ok_btn = QPushButton("登录")
        self.ok_btn.clicked.connect(self.change_ok_btn)
        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.clicked.connect(self.change_cancel_btn)
        lb_line_1 = QLabel()
        lb_line_1.setText('<html><hr />切换用户</html>')
        lb_line_2 = QLabel()
        lb_line_2.setText('<html><hr /></html>')

        self.form = QFormLayout()
        self.form.setLabelAlignment(Qt.AlignmentFlag.AlignRight)
        self.form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)  # 覆盖MacOS的默认样式
        self.form.addRow(self.name_lb, self.name_ed)
        self.form.addRow(self.pwd_lb, self.pwd_ed)
        if is_windows:
            def set_assister_path():
                """设置辅助登录程序路径"""
                assister_path = QFileDialog.getOpenFileName(self, "选择辅助登录程序路径",
                                                            self._cwd, "EXE Files (*.exe)")
                if not assister_path[0]:
                    return None
                assister_path = os.path.normpath(assister_path[0])  # windows backslash
                if assister_path == self._cookie_assister:
                    return None
                self.assister_ed.setText(assister_path)
                self._cookie_assister = assister_path

            self.assister_lb = QLabel("登录辅助程序")
            self.assister_lb.setAlignment(Qt.AlignmentFlag.AlignCenter)
            self.assister_ed = MyLineEdit(self)
            self.assister_ed.setText(self._cookie_assister)
            self.assister_ed.clicked.connect(set_assister_path)
            self.assister_lb.setBuddy(self.assister_ed)
            self.form.addRow(self.assister_lb, self.assister_ed)

        hbox = QHBoxLayout()
        hbox.addWidget(self.show_input_cookie_btn)
        hbox.addStretch(1)
        hbox.addWidget(self.ok_btn)
        hbox.addWidget(self.cancel_btn)

        user_box = QHBoxLayout()
        self.user_num = 0
        self.user_btns = {}
        for user in self._config.users_name:
            user = str(user)  # TODO: 可能需要删掉
            self.user_btns[user] = QDoublePushButton(user)
            self.user_btns[user].setStyleSheet("QPushButton {border:none;}")
            if user == self._config.name:
                self.user_btns[user].setStyleSheet("QPushButton {background-color:rgb(0,153,2);}")
                self.tabs.setCurrentIndex(1)
            self.user_btns[user].setToolTip(f"点击选中,双击切换至用户:{user}")
            self.user_btns[user].doubleClicked.connect(self.choose_user)
            self.user_btns[user].clicked.connect(self.delete_chose_user)
            user_box.addWidget(self.user_btns[user])
            self.user_num += 1
            user_box.addStretch(1)

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(logo)
        vbox = QVBoxLayout()
        if self._config.name:
            vbox.addWidget(lb_line_1)
            user_box.setAlignment(Qt.AlignmentFlag.AlignCenter)
            vbox.addLayout(user_box)
            vbox.addWidget(lb_line_2)
            if self.user_num > 1:
                self.del_user_btn = QPushButton("删除账户")
                self.del_user_btn.setIcon(QIcon(SRC_DIR + "delete.ico"))
                self.del_user_btn.setStyleSheet("QPushButton {min-width: 180px;max-width: 180px;}")
                self.del_user_btn.clicked.connect(self.call_del_chose_user)
                vbox.addWidget(self.del_user_btn)
            else:
                self.del_user_btn = None
            vbox.addStretch(1)

        vbox.addLayout(self.form)
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        vbox.setAlignment(Qt.AlignmentFlag.AlignCenter)

        self.hand_tab.setLayout(vbox)
        auto_cookie_vbox = QVBoxLayout()
        auto_cookie_vbox.addWidget(self.auto_get_cookie_ok)
        auto_cookie_vbox.addWidget(self.auto_get_cookie_btn)
        auto_cookie_vbox.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.auto_tab.setLayout(auto_cookie_vbox)
        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)
        self.update_selection(self._config.name)