Esempio n. 1
0
    def __init__(
        self,
        icon: QStyle.StandardPixmap,
        labelText: str,
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        An icon button with a text label.
        """
        super(LabelledIconButton, self).__init__(parent=parent,
                                                 *args,
                                                 **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

        verticalLayout = QVBoxLayout(self)
        verticalLayout.setContentsMargins(0, 0, 0, 0)

        button = QToolButton(self)
        button.setIcon(self.style().standardIcon(icon), )

        # connect the button to the onButtonPressed signal
        self.onButtonPressed = button.clicked

        label = QLabel(labelText, self)

        verticalLayout.addWidget(button)
        verticalLayout.addWidget(label)
        verticalLayout.setAlignment(label, Qt.Alignment.AlignHCenter)
        verticalLayout.setAlignment(button, Qt.Alignment.AlignHCenter)

        self.setLayout(verticalLayout)
    def __init__(self):
        super(MainWidget, self).__init__()
        self.resize(500, 600)
        self.setWindowTitle("喜马拉雅下载 by[Zero] " + __VERSION__)
        self.mainlayout = QVBoxLayout()
        self.setLayout(self.mainlayout)
        self.groupbox = QGroupBox("选择类型")
        self.groupbox.setFixedHeight(50)
        hlayout = QHBoxLayout(self.groupbox)
        self.signal_m4a = QRadioButton("单个下载")
        self.mut_m4a = QRadioButton("专辑下载")
        self.vip_signal_m4a = QRadioButton("VIP单个下载")
        self.vip_m4a = QRadioButton("VIP专辑下载")

        hlayout.addWidget(self.signal_m4a)
        hlayout.addWidget(self.mut_m4a)
        hlayout.addWidget(self.vip_signal_m4a)
        hlayout.addWidget(self.vip_m4a)
        self.mainlayout.addWidget(self.groupbox)

        frame01 = QFrame(self)
        child_layout = QVBoxLayout()
        print(self.width())
        label01 = QLabel("链   接", self)
        label02 = QLabel("下载目录", self)
        self.url_lineedit = QLineEdit(self)
        self.dir_lineedit = QLineEdit(self)
        hlayout01 = QHBoxLayout()
        hlayout01.addWidget(label01, 1)
        hlayout01.addWidget(self.url_lineedit, 9)
        hlayout02 = QHBoxLayout()
        hlayout02.addWidget(label02, 1)
        hlayout02.addWidget(self.dir_lineedit, 9)
        child_layout.addLayout(hlayout01)
        child_layout.addLayout(hlayout02)
        child_layout.setContentsMargins(
            5, 0, 5, 0)  #(int left, int top, int right, int bottom)
        frame01.setLayout(child_layout)
        self.download_progressbar = QProgressBar()
        self.download_progressbar.setAlignment(
            QtCore.Qt.Alignment.AlignCenter)  #文字居中
        self.download_progressbar.setValue(88)
        self.download_btn = QPushButton("开始下载")
        self.show_plaintextedit = QPlainTextEdit()
        self.show_plaintextedit.setMinimumHeight(400)
        self.mainlayout.addWidget(frame01)
        self.mainlayout.addWidget(self.download_progressbar)
        self.mainlayout.addWidget(self.download_btn)
        self.mainlayout.addWidget(self.show_plaintextedit)
        self.mainlayout.addStretch()
        ### 设置stylesheet
        self.download_btn.setStyleSheet(
            'QPushButton:pressed{ text-align: center;background-color:red;}')
Esempio n. 3
0
class MainWindowContentSection(QWidget):
    def __init__(self, main_window_content, sub_header_text):
        super(MainWindowContentSection, self).__init__()
        self.main_window_content = main_window_content

        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        self.layout.setSpacing(10)

        self.sub_header = QLabel(sub_header_text)
        self.sub_header.setFont(SUBHEADER_FONT)
        self.layout.addWidget(self.sub_header)
    def __init__(self):
        """ Dialog to manage the settings of the application """
        super().__init__()
        self.setWindowTitle("Settings")
        self.setFixedSize(310, 250)

        # Create the general settings widgets for managing the text color,
        # text alignment, and author of the app's content
        # NOTE: Altering the default CSS attributes, such as the color, of a
        # widget can change its appearance. Hence, the button may appear
        # rectangular depending upon your platform
        self.text_color_button = QPushButton()
        self.text_color_button.setStyleSheet(
            "background-color: #000000")  # Black
        self.text_color_button.clicked.connect(self.selectTextColor)

        self.align_left = QRadioButton(text="Left")  # Default
        self.align_left.setChecked(True)
        self.align_center = QRadioButton(text="Center")
        self.align_center.setChecked(False)
        self.align_right = QRadioButton(text="Right")
        self.align_right.setChecked(False)

        # Layout and container for alignment radio buttons
        align_v_box = QVBoxLayout()
        align_v_box.setContentsMargins(0, 5, 0, 0)
        align_v_box.addWidget(self.align_left)
        align_v_box.addWidget(self.align_center)
        align_v_box.addWidget(self.align_right)

        align_frame = QFrame()
        align_frame.setFrameShape(QFrame.Shape.NoFrame)
        align_frame.setLayout(align_v_box)

        self.author_name = QLineEdit()
        self.author_name.setMinimumWidth(160)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.StandardButtons.Ok
            | QDialogButtonBox.StandardButtons.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        dialog_layout = QFormLayout()
        dialog_layout.addRow("<b>Text Color:</b>", self.text_color_button)
        dialog_layout.addRow(HorizontalSeparator())
        dialog_layout.addRow("<b>Text Alignment:</b>", align_frame)
        dialog_layout.addRow(HorizontalSeparator())
        dialog_layout.addRow("<b>Author:</b>", self.author_name)
        dialog_layout.addWidget(self.button_box)
        self.setLayout(dialog_layout)
class NuggetListWidget(QWidget):
    def __init__(self, interactive_matching_widget):
        super(NuggetListWidget, self).__init__(interactive_matching_widget)
        self.interactive_matching_widget = interactive_matching_widget

        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(10)

        # top widget
        self.top_widget = QWidget()
        self.top_layout = QHBoxLayout(self.top_widget)
        self.top_layout.setContentsMargins(0, 0, 0, 0)
        self.top_layout.setSpacing(10)
        self.layout.addWidget(self.top_widget)

        self.description = QLabel(
            "Below you see a list of guessed matches for you to confirm or correct."
        )
        self.description.setFont(LABEL_FONT)
        self.top_layout.addWidget(self.description)

        self.stop_button = QPushButton("Continue With Next Attribute")
        self.stop_button.setFont(BUTTON_FONT)
        self.stop_button.clicked.connect(self._stop_button_clicked)
        self.stop_button.setMaximumWidth(240)
        self.top_layout.addWidget(self.stop_button)

        # nugget list
        self.nugget_list = CustomScrollableList(self, NuggetListItemWidget)
        self.layout.addWidget(self.nugget_list)

    def update_nuggets(self, nuggets):
        max_start_chars = max([
            nugget[CachedContextSentenceSignal]["start_char"]
            for nugget in nuggets
        ])
        self.nugget_list.update_item_list(nuggets, max_start_chars)

    def _stop_button_clicked(self):
        self.interactive_matching_widget.main_window.give_feedback_task(
            {"message": "stop-interactive-matching"})

    def enable_input(self):
        self.stop_button.setEnabled(True)
        self.nugget_list.enable_input()

    def disable_input(self):
        self.stop_button.setDisabled(True)
        self.nugget_list.disable_input()
    def __init__(
        self,
        onPlayButtonPressed: Callable[[], None],
        onPauseButtonPressed: Callable[[], None],
        onStepButtonPressed: Callable[[], None],
        onRestartButtonPressed: Callable[[], None],
        onSpeedControlValueChanged: Callable[[int], None],
        onOpenLogButtonPressed: Callable[[], None],
        onAgentVarsButtonPressed: Callable[[], None],
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        The "Solver Controls…" dropdown view of the solver controls for the active solver
        """
        super(SolverControlsView, self).__init__(parent=parent,
                                                 *args,
                                                 **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

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

        self.__stateControlBoxView = SolverControlButtonsView(
            onPlayButtonPressed=onPlayButtonPressed,
            onPauseButtonPressed=onPauseButtonPressed,
            onStepButtonPressed=onStepButtonPressed,
            onRestartButtonPressed=onRestartButtonPressed,
            parent=self,
        )

        self.__speedControl = MazeSolverSpeedControlView(
            onValueChanged=onSpeedControlValueChanged,
            parent=self,
        )

        self.__solverWindowsButtons = SolverWindowsButtonsView(
            onOpenLogButtonPressed=onOpenLogButtonPressed,
            onAgentVarsButtonPressed=onAgentVarsButtonPressed,
            parent=self,
        )

        layout.addWidget(self.__stateControlBoxView)
        layout.addWidget(self.__speedControl)
        layout.addWidget(self.__solverWindowsButtons)

        self.setLayout(layout)
    def __init__(
        self,
        onValueChanged: Callable[[int], None],
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        Slow/fast slider for maze solver agents speed
        """
        super(MazeSolverSpeedControlView, self).__init__(parent=parent,
                                                         *args,
                                                         **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

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

        slider = QSlider(Qt.Orientations.Horizontal)
        # minimum of 1 op/s (0 would result in a divide by zero op and thus crash)
        slider.setMinimum(1)
        # maximum of 50 op/s
        slider.setMaximum(70)
        # initial value in the middle
        slider.setValue(35)

        # connect the onValueChange event to the method passed to this widget
        slider.valueChanged.connect(onValueChanged)  # type: ignore

        # slow/fast horizontal labels view
        slowFastLabelsLayout = QHBoxLayout()
        slowFastLabelsLayout.setContentsMargins(0, 0, 0, 0)

        slowLabel = QLabel("Slow")
        fastLabel = QLabel("Fast")

        slowFastLabelsLayout.addWidget(slowLabel)
        slowFastLabelsLayout.addStretch()
        slowFastLabelsLayout.addWidget(fastLabel)

        layout.addWidget(slider)
        layout.addLayout(slowFastLabelsLayout)

        self.setLayout(layout)
Esempio n. 8
0
File: page.py Progetto: swavan/oneui
    def _dialog_handler(self, _widget: QWidget):
        _dialog = QDialog(self)

        _widget.saved.connect(_dialog.close)
        _widget.canceled.connect(_dialog.close)

        _layout = QVBoxLayout()
        _layout.setContentsMargins(0, 0, 0, 0)
        _layout.addWidget(_widget)

        _dialog.setWindowModality(Qt.WindowModality.WindowModal)
        _dialog.setLayout(_layout)

        _parent_size = self.sizeHint()
        _modal_size = QSize(_parent_size.width() - 100,
                            _parent_size.height() - 100)

        _dialog.setFixedSize(_modal_size)
        _dialog.show()
Esempio n. 9
0
class MainTabWidget(QWidget):
    def __init__(self, parent):
        super().__init__()
        self.layout = QVBoxLayout(self)
        self.classifyExercises = parent.classifyExercises
        self.infoLabel = parent.informationLabel

        self.tabs = QTabWidget()
        self.tabs.setContentsMargins(10, 10, 10, 10)
        self.tabs.setStyleSheet(CustomQStyles.tabStyle)
        self.tab1 = QWidget()
        self.tab2 = QWidget()
        self.tab3 = QWidget()
        self.tabs.resize(300, 200)
        self.tabs.addTab(self.tab1, "Train")
        self.tabs.addTab(self.tab2, "Test")
        self.tabs.addTab(self.tab3, "Keys")
        self.tabs.setFocusPolicy(Qt.FocusPolicy.NoFocus)

        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.setTabWidgets()

    def setTabWidgets(self):
        # Tab1 widgets:
        layout = QHBoxLayout()
        layout.addWidget(TrainWidget(self))
        self.tab1.layout = layout
        self.tab1.setLayout(self.tab1.layout)

        self.tab2.layout = TestWidget(self)
        self.tab2.setLayout(self.tab2.layout)

        layout2 = QHBoxLayout()
        layout2.addWidget(KeysWidget(self))

        self.tab3.layout = layout2
        self.tab3.setLayout(self.tab3.layout)

    def on_click_select_tab2(self):
        self.tabs.setCurrentIndex(1)
    def __init__(
        self,
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        Grouped controls box for generating mazes
        """
        super(GenerateMazeGroupView, self).__init__(parent=parent,
                                                    *args,
                                                    **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        groupbox = QGroupBox("Generate Maze")
        layout.addWidget(groupbox)

        vbox = QFormLayout()
        groupbox.setLayout(vbox)

        self.__mazeSizePicker = XYPicker(
            minimum=XY(2, 2),
            maximum=XY(250, 250),
            initialValue=XY(2, 2),
            parent=self,
        )

        self.__simplyConnectedCheckbox = QCheckBox()
        self.__simplyConnectedCheckbox.setChecked(True)

        generateButton = QPushButton("Generate")
        generateButton.clicked.connect(
            self.__onGenerateButtonPressed)  # type: ignore

        vbox.addRow("Size", self.__mazeSizePicker)
        vbox.addRow("Simply Connected", self.__simplyConnectedCheckbox)
        vbox.addRow(generateButton)

        self.setLayout(layout)
Esempio n. 11
0
    def __init__(
        self,
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        The 'Log' window view. Shows the current state of the solver at top, and for each history item:
            Left: Human description of command
            Right: Result success or not
        """
        # initialize the solver state history list to a new list
        super(AgentLogView, self).__init__(parent=parent, *args, **kwargs)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        verticalGroupLayout = QVBoxLayout()
        # self.__currentStateBox = CurrentStateBox("Not set…")
        # verticalGroupLayout.addWidget(self.__currentStateBox)

        # define the vertical scroll area of the log
        logLayoutScrollArea = QScrollArea()
        # enable smart vertical scroll
        logLayoutScrollArea.setVerticalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAsNeeded)
        # disable horizontal scroll
        logLayoutScrollArea.setHorizontalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

        # define our log layout (name on left, result on right) and add to layout
        self.__verticalLogLayout = VerticalLogLayout()
        logLayoutScrollArea.setWidget(self.__verticalLogLayout)

        # add the log layout to the vertical layout
        verticalGroupLayout.addWidget(logLayoutScrollArea)

        layout.addLayout(verticalGroupLayout)

        self.setLayout(layout)
        self.setWindowTitle("Agent Log")
Esempio n. 12
0
class MainWindowContent(QWidget):
    def __init__(self, main_window, header_text):
        super(MainWindowContent, self).__init__()
        self.main_window = main_window

        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(10, 5, 10, 5)
        self.layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        self.layout.setSpacing(20)

        self.header = QLabel(header_text)
        self.header.setFont(HEADER_FONT)
        self.layout.addWidget(self.header)

    @abc.abstractmethod
    def enable_input(self):
        raise NotImplementedError

    @abc.abstractmethod
    def disable_input(self):
        raise NotImplementedError
Esempio n. 13
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.")
Esempio n. 14
0
    def __init__(
        self,
        onPlayButtonPressed: Callable[[], None],
        onPauseButtonPressed: Callable[[], None],
        onStepButtonPressed: Callable[[], None],
        onRestartButtonPressed: Callable[[], None],
        onSpeedControlValueChanged: Callable[[int], None],
        onOpenLogButtonPressed: Callable[[], None],
        onAgentVarsButtonPressed: Callable[[], None],
        onSolveButtonPressed: Callable[[MazeSolverSpecification], None],
        mazeSize: XY,
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        Grouped controls box for controls for solving mazes
        """
        self.__onSolveButtonPressed = onSolveButtonPressed

        self.__mazeSize = mazeSize
        self.__maximumXY = XY(
            self.__mazeSize.x - 1,
            self.__mazeSize.y - 1,
        )

        super().__init__(parent=parent, *args, **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        groupbox = QGroupBox("Solve Maze")
        layout.addWidget(groupbox)

        vbox = QFormLayout()
        groupbox.setLayout(vbox)

        self.__startPosition = XYPicker(
            minimum=XY(0, 0),
            maximum=self.__maximumXY,
            initialValue=XY(0, 0),
            parent=self,
            label="•",
        )

        self.__endPosition = XYPicker(
            minimum=XY(0, 0),
            maximum=self.__maximumXY,
            initialValue=self.__maximumXY,
            parent=self,
            label="•",
        )

        self.__solverTypePicker = QComboBox(self)
        self.__solverTypePicker.addItem("Wall Follower", WallFollower)
        self.__solverTypePicker.addItem("Pledge Solver", PledgeSolver)
        self.__solverTypePicker.addItem("Random Mouse", RandomMouse)

        solveButton = QPushButton("Solve")
        solveButton.clicked.connect(  # type: ignore
            lambda: self.__onSolveButtonPressed(
                MazeSolverSpecification(
                    startPosition=self.__startPosition.getValues(),
                    endPosition=self.__endPosition.getValues(),
                    solverType=self.__solverTypePicker.currentData(),
                ),
            )
        )

        self.__solverControlsDropdown = SolverControlsView(
            onPlayButtonPressed=onPlayButtonPressed,
            onPauseButtonPressed=onPauseButtonPressed,
            onStepButtonPressed=onStepButtonPressed,
            onRestartButtonPressed=onRestartButtonPressed,
            onSpeedControlValueChanged=onSpeedControlValueChanged,
            onOpenLogButtonPressed=onOpenLogButtonPressed,
            onAgentVarsButtonPressed=onAgentVarsButtonPressed,
            parent=self,
        )
        #  connect enable/disable signal to child view
        self.setMazeSolverControlsEnabled.connect(
            self.__solverControlsDropdown.setMazeSolverControlsEnabled
        )

        vbox.addRow("Start Position", self.__startPosition)
        vbox.addRow("End Position", self.__endPosition)
        vbox.addRow("Solver Type", self.__solverTypePicker)
        vbox.addRow(solveButton)
        vbox.addRow(self.__solverControlsDropdown)

        self.setLayout(layout)
Esempio n. 15
0
    def __init__(
        self,
        onPlayButtonPressed: Callable[[], None],
        onPauseButtonPressed: Callable[[], None],
        onStepButtonPressed: Callable[[], None],
        onRestartButtonPressed: Callable[[], None],
        onSpeedControlValueChanged: Callable[[int], None],
        onOpenLogButtonPressed: Callable[[], None],
        onAgentVarsButtonPressed: Callable[[], None],
        onGenerateMazeButtonPressed: Callable[[MazeGenerationSpecification],
                                              None],
        onSolveButtonPressed: Callable[[MazeSolverSpecification], None],
        mazeSize: XY,
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """A vertical view of maze generator and solver agent controls views.

        Parameters
        ----------
        onPlayButtonPressed : Callable[[], None]
        onPauseButtonPressed : Callable[[], None]
        onStepButtonPressed : Callable[[], None]
        onRestartButtonPressed : Callable[[], None]
        onSpeedControlValueChanged : Callable[[], None]
        onOpenLogButtonPressed : Callable[[], None]
        onAgentVarsButtonPressed : Callable[[], None]
        onGenerateMazeButtonPressed : Callable[[MazeGenerationSpecification], None]
        onSolveButtonPressed: Callable[[MazeSolverSpecification], None]
        parent : Optional[QWidget], optional
            Parent widget, by default None
        """
        super().__init__(parent=parent, *args, **kwargs)
        self.setMaximumWidth(400)
        self.setContentsMargins(0, 0, 0, 0)

        generateMazeGroupView = GenerateMazeGroupView(parent=self)
        generateMazeGroupView.onMazeSpecChosen.connect(
            onGenerateMazeButtonPressed)
        # connect the enable/disable generateMazeGroupView signal to a lambda that enables/disables the generateMazeGroupView
        self.setMazeGeneratorControlsEnabled.connect(
            generateMazeGroupView.setEnabled)

        solveMazeGroupView = SolveMazeGroupView(
            onPlayButtonPressed=onPlayButtonPressed,
            onPauseButtonPressed=onPauseButtonPressed,
            onStepButtonPressed=onStepButtonPressed,
            onRestartButtonPressed=onRestartButtonPressed,
            onSpeedControlValueChanged=onSpeedControlValueChanged,
            onOpenLogButtonPressed=onOpenLogButtonPressed,
            onAgentVarsButtonPressed=onAgentVarsButtonPressed,
            onSolveButtonPressed=onSolveButtonPressed,
            mazeSize=mazeSize,
            parent=self,
        )
        # connect the enable/disable solver controls signal to a lambda that enables/disables the solver controls
        self.setMazeSolverControlsEnabled.connect(
            solveMazeGroupView.setMazeSolverControlsEnabled)

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

        layout.addWidget(generateMazeGroupView)
        layout.addWidget(solveMazeGroupView)

        # add a vertical stretch to the end so all widgets are at the top
        layout.insertStretch(-1)
        self.setLayout(layout)
Esempio n. 16
0
class CustomScrollableList(QWidget):
    def __init__(self, parent, item_type, floating_widget=None):
        super(CustomScrollableList, self).__init__()
        self.parent = parent
        self.item_type = item_type
        self.floating_widget = floating_widget

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

        self.list_widget = QWidget()
        self.list_layout = QVBoxLayout(self.list_widget)
        self.list_layout.setContentsMargins(0, 0, 0, 0)
        self.list_layout.setSpacing(10)
        self.list_layout.setAlignment(Qt.AlignmentFlag.AlignTop)

        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setFrameStyle(0)
        self.scroll_area.setWidget(self.list_widget)
        self.layout.addWidget(self.scroll_area)

        if self.floating_widget is not None:
            self.list_layout.addWidget(self.floating_widget)

        self.item_widgets = []
        self.num_visible_item_widgets = 0

    def last_item_widget(self):
        return self.item_widgets[self.num_visible_item_widgets - 1]

    def update_item_list(self, item_list, params=None):

        if self.floating_widget is not None:
            self.list_layout.removeWidget(self.floating_widget)

        # make sure that there are enough item widgets
        while len(item_list) > len(self.item_widgets):
            self.item_widgets.append(self.item_type(self.parent))

        # make sure that the correct number of item widgets is shown
        while len(item_list) > self.num_visible_item_widgets:
            widget = self.item_widgets[self.num_visible_item_widgets]
            self.list_layout.addWidget(widget)
            widget.show()
            self.num_visible_item_widgets += 1

        while len(item_list) < self.num_visible_item_widgets:
            widget = self.item_widgets[self.num_visible_item_widgets - 1]
            widget.hide()
            self.list_layout.removeWidget(widget)
            self.num_visible_item_widgets -= 1

        if self.floating_widget is not None:
            self.list_layout.addWidget(self.floating_widget)

        # update item widgets
        for item, item_widget in zip(item_list,
                                     self.item_widgets[:len(item_list)]):
            item_widget.update_item(item, params)

    def enable_input(self):
        for item_widget in self.item_widgets:
            item_widget.enable_input()

    def disable_input(self):
        for item_widget in self.item_widgets:
            item_widget.disable_input()
class DocumentWidget(QWidget):
    def __init__(self, interactive_matching_widget):
        super(DocumentWidget, self).__init__(interactive_matching_widget)
        self.interactive_matching_widget = interactive_matching_widget

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(10)
        self.setLayout(self.layout)

        self.document = None
        self.current_nugget = None
        self.base_formatted_text = ""
        self.idx_mapper = {}
        self.nuggets_in_order = []

        self.text_edit = QTextEdit()
        self.layout.addWidget(self.text_edit)
        self.text_edit.setReadOnly(True)
        self.text_edit.setFrameStyle(0)
        self.text_edit.setFont(CODE_FONT)
        self.text_edit.setHorizontalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.text_edit.setText("")

        self.buttons_widget = QWidget()
        self.buttons_widget_layout = QHBoxLayout(self.buttons_widget)
        self.layout.addWidget(self.buttons_widget)

        self.left_button = QPushButton("Skip Left")
        self.left_button.setFont(BUTTON_FONT)
        self.left_button.clicked.connect(self._left_button_clicked)
        self.buttons_widget_layout.addWidget(self.left_button)

        self.right_button = QPushButton("Skip Right")
        self.right_button.setFont(BUTTON_FONT)
        self.right_button.clicked.connect(self._right_button_clicked)
        self.buttons_widget_layout.addWidget(self.right_button)

        self.match_button = QPushButton("Confirm Match")
        self.match_button.setFont(BUTTON_FONT)
        self.match_button.clicked.connect(self._match_button_clicked)
        self.buttons_widget_layout.addWidget(self.match_button)

        self.no_match_button = QPushButton("There Is No Match")
        self.no_match_button.setFont(BUTTON_FONT)
        self.no_match_button.clicked.connect(self._no_match_button_clicked)
        self.buttons_widget_layout.addWidget(self.no_match_button)

    def _left_button_clicked(self):
        idx = self.nuggets_in_order.index(self.current_nugget)
        if idx > 0:
            self.current_nugget = self.nuggets_in_order[idx - 1]
            self._highlight_current_nugget()

    def _right_button_clicked(self):
        idx = self.nuggets_in_order.index(self.current_nugget)
        if idx < len(self.nuggets_in_order) - 1:
            self.current_nugget = self.nuggets_in_order[idx + 1]
            self._highlight_current_nugget()

    def _match_button_clicked(self):
        self.interactive_matching_widget.main_window.give_feedback_task({
            "message":
            "is-match",
            "nugget":
            self.current_nugget
        })

    def _no_match_button_clicked(self):
        self.interactive_matching_widget.main_window.give_feedback_task({
            "message":
            "no-match-in-document",
            "nugget":
            self.current_nugget
        })

    def _highlight_current_nugget(self):
        mapped_start_char = self.idx_mapper[self.current_nugget.start_char]
        mapped_end_char = self.idx_mapper[self.current_nugget.end_char]
        formatted_text = (
            f"{self.base_formatted_text[:mapped_start_char]}"
            f"<span style='background-color: #FFFF00'><b>"
            f"{self.base_formatted_text[mapped_start_char:mapped_end_char]}</span></b>"
            f"{self.base_formatted_text[mapped_end_char:]}")
        self.text_edit.setText("")
        self.text_edit.textCursor().insertHtml(formatted_text)

    def update_document(self, nugget):
        self.document = nugget.document
        self.current_nugget = nugget
        self.nuggets_in_order = list(
            sorted(self.document.nuggets, key=lambda x: x.start_char))

        if self.nuggets_in_order != []:
            self.idx_mapper = {}
            char_list = []
            end_chars = []
            next_start_char = 0
            next_nugget_idx = 0
            for idx, char in enumerate(list(self.document.text)):
                if idx == next_start_char:
                    if end_chars == []:
                        char_list += list("<b>")
                    end_chars.append(
                        self.nuggets_in_order[next_nugget_idx].end_char)
                    next_nugget_idx += 1
                    if next_nugget_idx < len(self.nuggets_in_order):
                        next_start_char = self.nuggets_in_order[
                            next_nugget_idx].start_char
                    else:
                        next_start_char = -1
                while idx in end_chars:
                    end_chars.remove(idx)
                if end_chars == []:
                    char_list += list("</b>")
                self.idx_mapper[idx] = len(char_list)
                char_list.append(char)
            self.base_formatted_text = "".join(char_list)
        else:
            self.idx_mapper = {}
            for idx in range(len(self.document.text)):
                self.idx_mapper[idx] = idx
            self.base_formatted_text = ""

        self._highlight_current_nugget()

        scroll_cursor = QTextCursor(self.text_edit.document())
        scroll_cursor.setPosition(nugget.start_char)
        self.text_edit.setTextCursor(scroll_cursor)
        self.text_edit.ensureCursorVisible()

    def enable_input(self):
        self.left_button.setEnabled(True)
        self.right_button.setEnabled(True)
        self.match_button.setEnabled(True)
        self.no_match_button.setEnabled(True)

    def disable_input(self):
        self.left_button.setDisabled(True)
        self.right_button.setDisabled(True)
        self.match_button.setDisabled(True)
        self.no_match_button.setDisabled(True)
Esempio n. 18
0
    def __init__(self):
        super().__init__()
        # setup some flags
        self.isFetching = False
        self.isDownloading = False

        # default output path
        self.outputPath = f'{QDir.homePath()}/videos'

        # setup some window specific things
        self.setWindowTitle('YouTube Downloader')
        self.setWindowIcon(QIcon('assets/yt-icon.ico'))
        self.setFixedSize(705, 343)

        # parent layout
        layout = QVBoxLayout()
        layout.setContentsMargins(15, 15, 15, 10)
        self.setLayout(layout)

        # top bar layout
        topBar = QHBoxLayout()

        # detail section
        detailSec = QHBoxLayout()
        metaSec = QVBoxLayout()

        # download section
        downloadSec = QHBoxLayout()
        downloadBtn = QVBoxLayout()

        # output path link button
        self.outputBtn = QPushButton('📂  Output Path')
        self.outputBtn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.outputBtn.setToolTip(self.outputPath)
        self.outputBtn.clicked.connect(self.setOutputPath)

        # status bar
        self.statusBar = QStatusBar()

        # message box
        self.message = QMessageBox()

        # setting up widgets
        self.urlBox = QLineEdit()
        self.urlBox.setFocusPolicy(Qt.FocusPolicy.ClickFocus or Qt.FocusPolicy.NoFocus)
        self.urlBox.setPlaceholderText('🔍 Enter or paste video URL...')
        self.button = QPushButton('Get')
        self.button.setDefault(True)
        self.button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.button.clicked.connect(self.getDetails)

        # thumbnail
        pixmap = QPixmap('assets\placeholder.jpg')
        self.thumb = QLabel()
        self.thumb.setFixedSize(250, 141)
        self.thumb.setScaledContents(True)
        self.thumb.setPixmap(pixmap)

        # detail widgets
        self.title = QLabel('Title: ')
        self.author = QLabel('Author: ')
        self.length = QLabel('Duration: ')
        self.publish_date = QLabel('Published: ')

        # progress bar
        self.progress_bar = QProgressBar()
        
        # download options
        self.download = QComboBox()
        self.download.setPlaceholderText('Download Video')
        self.download.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download.activated.connect(lambda: self.getContent(0))
        self.download.setEnabled(False)

        # download audio button
        self.download_audio = QPushButton('Download Audio')
        self.download_audio.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download_audio.clicked.connect(lambda: self.getContent(1))
        self.download_audio.setEnabled(False)

        # add widgets and layouts
        topBar.addWidget(self.urlBox)
        topBar.addWidget(self.button)

        # detail section
        metaSec.addWidget(self.title)
        metaSec.addWidget(self.author)
        metaSec.addWidget(self.length)
        metaSec.addWidget(self.publish_date)
        detailSec.addWidget(self.thumb)
        detailSec.addSpacing(20)
        detailSec.addLayout(metaSec)

        # download section
        downloadBtn.addWidget(self.download)
        downloadBtn.addWidget(self.download_audio)
        downloadSec.addWidget(self.progress_bar)
        downloadSec.addSpacing(10)
        downloadSec.addLayout(downloadBtn)

        # status bar
        self.statusBar.setSizeGripEnabled(False)
        self.statusBar.addPermanentWidget(self.outputBtn)

        # add content to parent layout
        layout.addLayout(topBar)
        layout.addSpacing(20)
        layout.addLayout(detailSec)
        layout.addSpacing(5)
        layout.addLayout(downloadSec)
        layout.addWidget(self.statusBar)

        # setup a connection thread to keep checking internet connectivity
        self.connection = ConnectionThread()
        self.connection.start()

        # catch the connection response signal
        self.connection.con_response.connect(self.connection_slot)
Esempio n. 19
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupTrayicon()
        self.setupVariables()
        self.setupUi()
        self.setupConnections()
        self.show()

    def setupVariables(self):
        settings = QSettings()
        self.workEndTime = QTime(
            int(settings.value(workHoursKey, 0)),
            int(settings.value(workMinutesKey, 25)),
            int(settings.value(workSecondsKey, 0)),
        )
        self.restEndTime = QTime(
            int(settings.value(restHoursKey, 0)),
            int(settings.value(restMinutesKey, 5)),
            int(settings.value(restSecondsKey, 0)),
        )
        self.timeFormat = "hh:mm:ss"
        self.time = QTime(0, 0, 0, 0)
        self.workTime = QTime(0, 0, 0, 0)
        self.restTime = QTime(0, 0, 0, 0)
        self.totalTime = QTime(0, 0, 0, 0)
        self.currentMode = Mode.work
        self.maxRepetitions = -1
        self.currentRepetitions = 0

    def setupConnections(self):
        """ Create button connections """
        self.startButton.clicked.connect(self.startTimer)
        self.startButton.clicked.connect(
            lambda: self.startButton.setDisabled(True))
        self.startButton.clicked.connect(
            lambda: self.pauseButton.setDisabled(False))
        self.startButton.clicked.connect(
            lambda: self.resetButton.setDisabled(False))
        self.pauseButton.clicked.connect(self.pauseTimer)
        self.pauseButton.clicked.connect(
            lambda: self.startButton.setDisabled(False))
        self.pauseButton.clicked.connect(
            lambda: self.pauseButton.setDisabled(True))
        self.pauseButton.clicked.connect(
            lambda: self.resetButton.setDisabled(False))
        self.pauseButton.clicked.connect(
            lambda: self.startButton.setText("continue"))
        self.resetButton.clicked.connect(self.resetTimer)
        self.resetButton.clicked.connect(
            lambda: self.startButton.setDisabled(False))
        self.resetButton.clicked.connect(
            lambda: self.pauseButton.setDisabled(True))
        self.resetButton.clicked.connect(
            lambda: self.resetButton.setDisabled(True))
        self.resetButton.clicked.connect(
            lambda: self.startButton.setText("start"))
        self.acceptTaskButton.pressed.connect(self.insertTask)
        self.deleteTaskButton.pressed.connect(self.deleteTask)
        """ Create spinbox  connections """
        self.workHoursSpinBox.valueChanged.connect(self.updateWorkEndTime)
        self.workMinutesSpinBox.valueChanged.connect(self.updateWorkEndTime)
        self.workSecondsSpinBox.valueChanged.connect(self.updateWorkEndTime)
        self.restHoursSpinBox.valueChanged.connect(self.updateRestEndTime)
        self.restMinutesSpinBox.valueChanged.connect(self.updateRestEndTime)
        self.restSecondsSpinBox.valueChanged.connect(self.updateRestEndTime)
        self.repetitionsSpinBox.valueChanged.connect(self.updateMaxRepetitions)
        """ Create combobox connections """
        self.modeComboBox.currentTextChanged.connect(self.updateCurrentMode)
        """ Create tablewidget connections """
        self.tasksTableWidget.cellDoubleClicked.connect(
            self.markTaskAsFinished)

    def setupUi(self):
        self.size_policy = QSizePolicy(QSizePolicy.Policy.Expanding,
                                       QSizePolicy.Policy.Expanding)
        """ Create tabwidget """
        self.tabWidget = QTabWidget()
        """ Create tab widgets """
        timerWidget = self.setupTimerTab()
        tasksWidget = self.setupTasksTab()
        statisticsWidget = self.setupStatisticsTab()
        """ add tab widgets to tabwidget"""
        self.timerTab = self.tabWidget.addTab(timerWidget, makeIcon("timer"),
                                              "Timer")
        self.tasksTab = self.tabWidget.addTab(tasksWidget, makeIcon("tasks"),
                                              "Tasks")
        self.statisticsTab = self.tabWidget.addTab(statisticsWidget,
                                                   makeIcon("statistics"),
                                                   "Statistics")
        """ Set mainwindows central widget """
        self.setCentralWidget(self.tabWidget)

    def setupTimerTab(self):
        settings = QSettings()
        self.timerContainer = QWidget(self)
        self.timerContainerLayout = QVBoxLayout(self.timerContainer)
        self.timerContainer.setLayout(self.timerContainerLayout)
        """ Create work groupbox"""
        self.workGroupBox = QGroupBox("Work")
        self.workGroupBoxLayout = QHBoxLayout(self.workGroupBox)
        self.workGroupBox.setLayout(self.workGroupBoxLayout)
        self.workHoursSpinBox = QSpinBox(
            minimum=0,
            maximum=24,
            value=int(settings.value(workHoursKey, 0)),
            suffix="h",
            sizePolicy=self.size_policy,
        )
        self.workMinutesSpinBox = QSpinBox(
            minimum=0,
            maximum=60,
            value=int(settings.value(workMinutesKey, 25)),
            suffix="m",
            sizePolicy=self.size_policy,
        )
        self.workSecondsSpinBox = QSpinBox(
            minimum=0,
            maximum=60,
            value=int(settings.value(workSecondsKey, 0)),
            suffix="s",
            sizePolicy=self.size_policy,
        )
        """ Create rest groupbox"""
        self.restGroupBox = QGroupBox("Rest")
        self.restGroupBoxLayout = QHBoxLayout(self.restGroupBox)
        self.restGroupBox.setLayout(self.restGroupBoxLayout)
        self.restHoursSpinBox = QSpinBox(
            minimum=0,
            maximum=24,
            value=int(settings.value(restHoursKey, 0)),
            suffix="h",
            sizePolicy=self.size_policy,
        )
        self.restMinutesSpinBox = QSpinBox(
            minimum=0,
            maximum=60,
            value=int(settings.value(restMinutesKey, 5)),
            suffix="m",
            sizePolicy=self.size_policy,
        )
        self.restSecondsSpinBox = QSpinBox(
            minimum=0,
            maximum=60,
            value=int(settings.value(restSecondsKey, 0)),
            suffix="s",
            sizePolicy=self.size_policy,
        )
        self.restGroupBoxLayout.addWidget(self.restHoursSpinBox)
        self.restGroupBoxLayout.addWidget(self.restMinutesSpinBox)
        self.restGroupBoxLayout.addWidget(self.restSecondsSpinBox)
        """ Create other groupbox"""
        self.otherGroupBox = QGroupBox("Other")
        self.otherGroupBoxLayout = QHBoxLayout(self.otherGroupBox)
        self.otherGroupBox.setLayout(self.otherGroupBoxLayout)
        self.repetitionsLabel = QLabel("Repetitions")
        self.repetitionsSpinBox = QSpinBox(
            minimum=0,
            maximum=10000,
            value=0,
            sizePolicy=self.size_policy,
            specialValueText="∞",
        )
        self.modeLabel = QLabel("Mode")
        self.modeComboBox = QComboBox(sizePolicy=self.size_policy)
        self.modeComboBox.addItems(["work", "rest"])
        self.otherGroupBoxLayout.addWidget(self.repetitionsLabel)
        self.otherGroupBoxLayout.addWidget(self.repetitionsSpinBox)
        self.otherGroupBoxLayout.addWidget(self.modeLabel)
        self.otherGroupBoxLayout.addWidget(self.modeComboBox)
        """ Create timer groupbox"""
        self.lcdDisplayGroupBox = QGroupBox("Time")
        self.lcdDisplayGroupBoxLayout = QHBoxLayout(self.lcdDisplayGroupBox)
        self.lcdDisplayGroupBox.setLayout(self.lcdDisplayGroupBoxLayout)
        self.timeDisplay = QLCDNumber(8, sizePolicy=self.size_policy)
        self.timeDisplay.setFixedHeight(100)
        self.timeDisplay.display("00:00:00")
        self.lcdDisplayGroupBoxLayout.addWidget(self.timeDisplay)
        """ Create pause, start and reset buttons"""
        self.buttonContainer = QWidget()
        self.buttonContainerLayout = QHBoxLayout(self.buttonContainer)
        self.buttonContainer.setLayout(self.buttonContainerLayout)
        self.startButton = self.makeButton("start", disabled=False)
        self.resetButton = self.makeButton("reset")
        self.pauseButton = self.makeButton("pause")
        """ Add widgets to container """
        self.workGroupBoxLayout.addWidget(self.workHoursSpinBox)
        self.workGroupBoxLayout.addWidget(self.workMinutesSpinBox)
        self.workGroupBoxLayout.addWidget(self.workSecondsSpinBox)
        self.timerContainerLayout.addWidget(self.workGroupBox)
        self.timerContainerLayout.addWidget(self.restGroupBox)
        self.timerContainerLayout.addWidget(self.otherGroupBox)
        self.timerContainerLayout.addWidget(self.lcdDisplayGroupBox)
        self.buttonContainerLayout.addWidget(self.pauseButton)
        self.buttonContainerLayout.addWidget(self.startButton)
        self.buttonContainerLayout.addWidget(self.resetButton)
        self.timerContainerLayout.addWidget(self.buttonContainer)
        return self.timerContainer

    def setupTasksTab(self):
        settings = QSettings()
        """ Create vertical tasks container """
        self.tasksWidget = QWidget(self.tabWidget)
        self.tasksWidgetLayout = QVBoxLayout(self.tasksWidget)
        self.tasksWidget.setLayout(self.tasksWidgetLayout)
        """ Create horizontal input container """
        self.inputContainer = QWidget()
        self.inputContainer.setFixedHeight(50)
        self.inputContainerLayout = QHBoxLayout(self.inputContainer)
        self.inputContainerLayout.setContentsMargins(0, 0, 0, 0)
        self.inputContainer.setLayout(self.inputContainerLayout)
        """ Create text edit """
        self.taskTextEdit = QTextEdit(
            placeholderText="Describe your task briefly.",
            undoRedoEnabled=True)
        """ Create vertical buttons container """
        self.inputButtonContainer = QWidget()
        self.inputButtonContainerLayout = QVBoxLayout(
            self.inputButtonContainer)
        self.inputButtonContainerLayout.setContentsMargins(0, 0, 0, 0)
        self.inputButtonContainer.setLayout(self.inputButtonContainerLayout)
        """ Create buttons """
        self.acceptTaskButton = QToolButton(icon=makeIcon("check"))
        self.deleteTaskButton = QToolButton(icon=makeIcon("trash"))
        """ Create tasks tablewidget """
        self.tasksTableWidget = QTableWidget(0, 1)
        self.tasksTableWidget.setHorizontalHeaderLabels(["Tasks"])
        self.tasksTableWidget.horizontalHeader().setStretchLastSection(True)
        self.tasksTableWidget.verticalHeader().setVisible(False)
        self.tasksTableWidget.setWordWrap(True)
        self.tasksTableWidget.setTextElideMode(Qt.TextElideMode.ElideNone)
        self.tasksTableWidget.setEditTriggers(
            QAbstractItemView.EditTriggers.NoEditTriggers)
        self.tasksTableWidget.setSelectionMode(
            QAbstractItemView.SelectionMode.SingleSelection)
        self.insertTasks(*settings.value(tasksKey, []))
        """ Add widgets to container widgets """
        self.inputButtonContainerLayout.addWidget(self.acceptTaskButton)
        self.inputButtonContainerLayout.addWidget(self.deleteTaskButton)
        self.inputContainerLayout.addWidget(self.taskTextEdit)
        self.inputContainerLayout.addWidget(self.inputButtonContainer)
        self.tasksWidgetLayout.addWidget(self.inputContainer)
        self.tasksWidgetLayout.addWidget(self.tasksTableWidget)
        return self.tasksWidget

    def setupStatisticsTab(self):
        """ Create statistics container """
        self.statisticsContainer = QWidget()
        self.statisticsContainerLayout = QVBoxLayout(self.statisticsContainer)
        self.statisticsContainer.setLayout(self.statisticsContainerLayout)
        """ Create work time groupbox """
        self.statisticsWorkTimeGroupBox = QGroupBox("Work Time")
        self.statisticsWorkTimeGroupBoxLayout = QHBoxLayout()
        self.statisticsWorkTimeGroupBox.setLayout(
            self.statisticsWorkTimeGroupBoxLayout)
        self.statisticsWorkTimeDisplay = QLCDNumber(8)
        self.statisticsWorkTimeDisplay.display("00:00:00")
        self.statisticsWorkTimeGroupBoxLayout.addWidget(
            self.statisticsWorkTimeDisplay)
        """ Create rest time groupbox """
        self.statisticsRestTimeGroupBox = QGroupBox("Rest Time")
        self.statisticsRestTimeGroupBoxLayout = QHBoxLayout()
        self.statisticsRestTimeGroupBox.setLayout(
            self.statisticsRestTimeGroupBoxLayout)
        self.statisticsRestTimeDisplay = QLCDNumber(8)
        self.statisticsRestTimeDisplay.display("00:00:00")
        self.statisticsRestTimeGroupBoxLayout.addWidget(
            self.statisticsRestTimeDisplay)
        """ Create total time groupbox """
        self.statisticsTotalTimeGroupBox = QGroupBox("Total Time")
        self.statisticsTotalTimeGroupBoxLayout = QHBoxLayout()
        self.statisticsTotalTimeGroupBox.setLayout(
            self.statisticsTotalTimeGroupBoxLayout)
        self.statisticsTotalTimeDisplay = QLCDNumber(8)
        self.statisticsTotalTimeDisplay.display("00:00:00")
        self.statisticsTotalTimeGroupBoxLayout.addWidget(
            self.statisticsTotalTimeDisplay)
        """ Add widgets to container """
        self.statisticsContainerLayout.addWidget(
            self.statisticsTotalTimeGroupBox)
        self.statisticsContainerLayout.addWidget(
            self.statisticsWorkTimeGroupBox)
        self.statisticsContainerLayout.addWidget(
            self.statisticsRestTimeGroupBox)
        return self.statisticsContainer

    def setupTrayicon(self):
        self.trayIcon = QSystemTrayIcon(makeIcon("tomato"))
        self.trayIcon.setContextMenu(QMenu())
        self.quitAction = self.trayIcon.contextMenu().addAction(
            makeIcon("exit"), "Quit", self.exit)
        self.quitAction.triggered.connect(self.exit)
        self.trayIcon.activated.connect(self.onActivate)
        self.trayIcon.show()
        self.trayIcon.setToolTip("Pomodoro")
        self.toast = ToastNotifier()

    def leaveEvent(self, event):
        super(MainWindow, self).leaveEvent(event)
        self.tasksTableWidget.clearSelection()

    def closeEvent(self, event):
        super(MainWindow, self).closeEvent(event)
        settings = QSettings()
        settings.setValue(workHoursKey, self.workHoursSpinBox.value())
        settings.setValue(
            workMinutesKey,
            self.workMinutesSpinBox.value(),
        )
        settings.setValue(
            workSecondsKey,
            self.workSecondsSpinBox.value(),
        )
        settings.setValue(restHoursKey, self.restHoursSpinBox.value())
        settings.setValue(
            restMinutesKey,
            self.restMinutesSpinBox.value(),
        )
        settings.setValue(
            restSecondsKey,
            self.restSecondsSpinBox.value(),
        )

        tasks = []
        for i in range(self.tasksTableWidget.rowCount()):
            item = self.tasksTableWidget.item(i, 0)
            if not item.font().strikeOut():
                tasks.append(item.text())
        settings.setValue(tasksKey, tasks)

    def startTimer(self):
        try:
            if not self.timer.isActive():
                self.createTimer()
        except:
            self.createTimer()

    def createTimer(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateTime)
        self.timer.timeout.connect(self.maybeChangeMode)
        self.timer.setInterval(1000)
        self.timer.setSingleShot(False)
        self.timer.start()

    def pauseTimer(self):
        try:
            self.timer.stop()
            self.timer.disconnect()
        except:
            pass

    def resetTimer(self):
        try:
            self.pauseTimer()
            self.time = QTime(0, 0, 0, 0)
            self.displayTime()
        except:
            pass

    def maybeStartTimer(self):
        if self.currentRepetitions != self.maxRepetitions:
            self.startTimer()
            started = True
        else:
            self.currentRepetitions = 0
            started = False
        return started

    def updateWorkEndTime(self):
        self.workEndTime = QTime(
            self.workHoursSpinBox.value(),
            self.workMinutesSpinBox.value(),
            self.workSecondsSpinBox.value(),
        )

    def updateRestEndTime(self):
        self.restEndTime = QTime(
            self.restHoursSpinBox.value(),
            self.restMinutesSpinBox.value(),
            self.restSecondsSpinBox.value(),
        )

    def updateCurrentMode(self, mode: str):
        self.currentMode = Mode.work if mode == "work" else Mode.rest

    def updateTime(self):
        self.time = self.time.addSecs(1)
        self.totalTime = self.totalTime.addSecs(1)
        if self.modeComboBox.currentText() == "work":
            self.workTime = self.workTime.addSecs(1)
        else:
            self.restTime = self.restTime.addSecs(1)
        self.displayTime()

    def updateMaxRepetitions(self, value):
        if value == 0:
            self.currentRepetitions = 0
            self.maxRepetitions = -1
        else:
            self.maxRepetitions = 2 * value

    def maybeChangeMode(self):
        if self.currentMode is Mode.work and self.time >= self.workEndTime:
            self.resetTimer()
            self.modeComboBox.setCurrentIndex(1)
            self.incrementCurrentRepetitions()
            started = self.maybeStartTimer()
            self.showWindowMessage(
                Status.workFinished if started else Status.repetitionsReached)
            if not started:
                self.resetButton.click()
        elif self.currentMode is Mode.rest and self.time >= self.restEndTime:
            self.resetTimer()
            self.modeComboBox.setCurrentIndex(0)
            self.incrementCurrentRepetitions()
            started = self.maybeStartTimer()
            self.showWindowMessage(
                Status.restFinished if started else Status.repetitionsReached)
            if not started:
                self.resetButton.click()

    def incrementCurrentRepetitions(self):
        if self.maxRepetitions > 0:
            self.currentRepetitions += 1

    def insertTask(self):
        task = self.taskTextEdit.toPlainText()
        self.insertTasks(task)

    def insertTasks(self, *tasks):
        for task in tasks:
            if task:
                rowCount = self.tasksTableWidget.rowCount()
                self.tasksTableWidget.setRowCount(rowCount + 1)
                self.tasksTableWidget.setItem(rowCount, 0,
                                              QTableWidgetItem(task))
                self.tasksTableWidget.resizeRowsToContents()
                self.taskTextEdit.clear()

    def deleteTask(self):
        selectedIndexes = self.tasksTableWidget.selectedIndexes()
        if selectedIndexes:
            self.tasksTableWidget.removeRow(selectedIndexes[0].row())

    def markTaskAsFinished(self, row, col):
        item = self.tasksTableWidget.item(row, col)
        font = self.tasksTableWidget.item(row, col).font()
        font.setStrikeOut(False if item.font().strikeOut() else True)
        item.setFont(font)

    def displayTime(self):
        self.timeDisplay.display(self.time.toString(self.timeFormat))
        self.statisticsRestTimeDisplay.display(
            self.restTime.toString(self.timeFormat))
        self.statisticsWorkTimeDisplay.display(
            self.workTime.toString(self.timeFormat))
        self.statisticsTotalTimeDisplay.display(
            self.totalTime.toString(self.timeFormat))

    def showWindowMessage(self, status):
        if status is Status.workFinished:
            title, text = "Break", choice(work_finished_phrases)
        elif status is Status.restFinished:
            title, text = "Work", choice(rest_finished_phrases)
        else:
            title, text = "Finished", choice(work_finished_phrases)
        self.trayIcon.showMessage(title, text, makeIcon("tomato"))
        self.toast.show_toast(title,
                              text,
                              icon_path="pomodoro/data/icons/tomato.ico",
                              duration=10,
                              threaded=True)

    def makeButton(self, text, iconName=None, disabled=True):
        button = QPushButton(text, sizePolicy=self.size_policy)
        if iconName:
            button.setIcon(makeIcon(iconName))
        button.setDisabled(disabled)
        return button

    def exit(self):
        self.close()
        app = QApplication.instance()
        if app:
            app.quit()

    def onActivate(self, reason):
        if reason == QSystemTrayIcon.ActivationReason.Trigger:
            self.show()
Esempio n. 20
0
    def __init__(self):
        super(B23Download, self).__init__()
        # setup some flags
        self.is_fetching = False
        self.is_downloading = False

        # default output path
        basepath = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(basepath, "videos")
        self.output_path = path

        # setup some window specific things
        self.setWindowTitle("Bilibili Favorite Downloader")
        self.setWindowIcon(QIcon("images/icon_bilibili.ico"))
        self.setFixedSize(705, 343)

        # parent layout
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(15, 15, 15, 10)
        self.setLayout(main_layout)

        # top bar layout
        top_layout = QHBoxLayout()

        # detail section
        mid_main_layout = QHBoxLayout()
        mid_right_layout = QVBoxLayout()

        # download section
        bottom_main_layout = QHBoxLayout()
        bottom_right_layout = QVBoxLayout()

        # output path link button
        self.output_btn = QPushButton("📂  Output Path")
        self.output_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.output_btn.setToolTip(self.output_path)
        self.output_btn.clicked.connect(self.set_output_path)

        # status bar
        self.status_bar = QStatusBar()

        # message box
        self.message_box = QMessageBox()

        # setting up widgets
        self.url_edit = QLineEdit()
        self.url_edit.setPlaceholderText("🔍 Enter or paste favorite URL...")
        self.get_btn = QPushButton("Get")
        self.get_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.get_btn.clicked.connect(self.get_details)

        # thumbnail
        pixmap = QPixmap("images/placeholder.png")
        self.thumb = QLabel()
        self.thumb.setFixedSize(250, 141)
        self.thumb.setScaledContents(True)
        self.thumb.setPixmap(pixmap)

        # detail widgets
        self.title = QLabel("Title: ")
        self.author = QLabel("Author: ")
        self.length = QLabel("Videos: ")
        self.publish_date = QLabel("Published: ")

        # progress bar
        self.progress_bar = QProgressBar()

        # download options
        self.download_btn = QPushButton(" Download Videos ")
        self.download_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download_btn.clicked.connect(self.get_content)
        self.download_btn.setEnabled(False)
        self.download_btn.setShortcut("Ctrl+Return")
        self.download_btn.setMinimumWidth(200)

        # add widgets and layouts
        top_layout.addWidget(self.url_edit)
        top_layout.addWidget(self.get_btn)

        # detail section
        mid_right_layout.addWidget(self.title)
        mid_right_layout.addWidget(self.author)
        mid_right_layout.addWidget(self.length)
        mid_right_layout.addWidget(self.publish_date)
        mid_main_layout.addWidget(self.thumb)
        mid_main_layout.addSpacing(20)
        mid_main_layout.addLayout(mid_right_layout)

        # download section
        bottom_right_layout.addWidget(self.download_btn)
        bottom_main_layout.addWidget(self.progress_bar)
        bottom_main_layout.addSpacing(10)
        bottom_main_layout.addLayout(bottom_right_layout)

        # status bar
        self.status_bar.setSizeGripEnabled(False)
        self.status_bar.addPermanentWidget(self.output_btn)

        # add content to parent layout
        main_layout.addLayout(top_layout)
        main_layout.addSpacing(20)
        main_layout.addLayout(mid_main_layout)
        main_layout.addSpacing(5)
        main_layout.addLayout(bottom_main_layout)
        main_layout.addWidget(self.status_bar)
Esempio n. 21
0
class View(QWidget):
    def __init__(self, buffer, view_info):
        super(View, self).__init__()

        self.buffer = buffer

        # Init widget attributes.
        if get_emacs_func_cache_result("eaf-emacs-running-in-wayland-native",
                                       []):
            self.setWindowFlags(Qt.WindowType.FramelessWindowHint
                                | Qt.WindowType.BypassWindowManagerHint)
        elif get_emacs_func_cache_result(
                "eaf-emacs-not-use-reparent-technology", []):
            self.setWindowFlags(Qt.WindowType.FramelessWindowHint
                                | Qt.WindowType.WindowStaysOnTopHint
                                | Qt.WindowType.NoDropShadowWindowHint)
        else:
            self.setWindowFlags(Qt.WindowType.FramelessWindowHint)

        self.setAttribute(Qt.WidgetAttribute.WA_X11DoNotAcceptFocus, True)
        self.setContentsMargins(0, 0, 0, 0)
        self.installEventFilter(self)

        # Init attributes.
        self.view_info = view_info
        (self.buffer_id, self.emacs_xid, self.x, self.y, self.width,
         self.height) = view_info.split(":")
        self.x = int(self.x)
        self.y = int(self.y)
        self.width = int(self.width)
        self.height = int(self.height)

        # Build QGraphicsView.
        self.layout = QVBoxLayout(self)
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.graphics_view = QGraphicsView(buffer, self)

        # Remove border from QGraphicsView.
        self.graphics_view.setHorizontalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.graphics_view.setVerticalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.graphics_view.setRenderHints(
            QPainter.RenderHint.Antialiasing
            | QPainter.RenderHint.SmoothPixmapTransform
            | QPainter.RenderHint.TextAntialiasing)
        self.graphics_view.setFrameStyle(QFrame.Shape.NoFrame)

        # Fill background color.
        self.graphics_view.setBackgroundBrush(QBrush(buffer.background_color))

        # Add graphics view.
        self.layout.addWidget(self.graphics_view)

        # NOTE: show function must start before resize to trigger *first* resizeEvent after show.
        self.show()

        # Resize after show to trigger fit view operation.
        self.resize(self.width, self.height)

        self.buffer.aspect_ratio_change.connect(self.adjust_aspect_ratio)

    def resizeEvent(self, event):
        # Fit content to view rect just when buffer fit_to_view option is enable.
        if self.buffer.fit_to_view:
            if event.oldSize().isValid():
                self.graphics_view.fitInView(
                    self.graphics_view.scene().sceneRect(),
                    Qt.AspectRatioMode.KeepAspectRatio)
                QWidget.resizeEvent(self, event)

    def adjust_aspect_ratio(self):
        widget_width = self.width
        widget_height = self.height

        if self.buffer.aspect_ratio == 0:
            self.buffer.buffer_widget.resize(self.width, self.height)

            self.layout.setContentsMargins(0, 0, 0, 0)
        else:
            view_height = widget_height * (
                1 - 2 * self.buffer.vertical_padding_ratio)
            view_width = view_height * self.buffer.aspect_ratio
            horizontal_padding = (widget_width - view_width) / 2
            vertical_padding = self.buffer.vertical_padding_ratio * widget_height

            self.buffer.buffer_widget.resize(int(view_width), int(view_height))

            self.layout.setContentsMargins(int(horizontal_padding),
                                           int(vertical_padding),
                                           int(horizontal_padding),
                                           int(vertical_padding))

    def eventFilter(self, obj, event):
        # import time
        # print(time.time(), event.type())

        import platform

        if event.type() in [QEvent.Type.ShortcutOverride]:
            eval_in_emacs('eaf-activate-emacs-window', [self.buffer_id])

        # Focus emacs buffer when user click view.
        event_type = [
            QEvent.Type.MouseButtonPress, QEvent.Type.MouseButtonRelease,
            QEvent.Type.MouseButtonDblClick
        ]
        if platform.system() != "Darwin":
            event_type += [QEvent.Type.Wheel]

        if event.type() in event_type:
            focus_emacs_buffer(self.buffer_id)
            # Stop mouse event.
            return True

        return False

    def showEvent(self, event):
        # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed.
        import platform

        self.reparent()

        if platform.system() in ["Windows", "Darwin"]:
            eval_in_emacs('eaf-activate-emacs-window', [])

        # Make graphics view at left-top corner after show.
        self.graphics_view.verticalScrollBar().setValue(0)
        self.graphics_view.horizontalScrollBar().setValue(0)

    def reparent(self):
        # print("Reparent: ", self.buffer.url)
        qwindow = self.windowHandle()

        if not get_emacs_func_cache_result(
                "eaf-emacs-not-use-reparent-technology", []):
            qwindow.setParent(QWindow.fromWinId(int(self.emacs_xid)))

        qwindow.setPosition(QPoint(self.x, self.y))

    def try_show_top_view(self):
        if get_emacs_func_cache_result("eaf-emacs-not-use-reparent-technology",
                                       []):
            self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True)
            self.show()

    def try_hide_top_view(self):
        if get_emacs_func_cache_result("eaf-emacs-not-use-reparent-technology",
                                       []):
            self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, False)
            self.hide()

    def destroy_view(self):
        # print("Destroy: ", self.buffer.url)
        self.destroy()