예제 #1
0
class AttributeCreatorWidget(CustomScrollableListItem):
    def __init__(self, document_base_creator_widget) -> None:
        super(AttributeCreatorWidget,
              self).__init__(document_base_creator_widget)
        self.document_base_creator_widget = document_base_creator_widget

        self.setFixedHeight(40)
        self.setStyleSheet("background-color: white")

        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(20, 0, 20, 0)
        self.layout.setSpacing(10)

        self.name = QLineEdit()
        self.name.setFont(CODE_FONT_BOLD)
        self.name.setStyleSheet("border: none")
        self.layout.addWidget(self.name)

        self.delete_button = QPushButton()
        self.delete_button.setIcon(QIcon("aset_ui/resources/trash.svg"))
        self.delete_button.setFlat(True)
        self.delete_button.clicked.connect(self._delete_button_clicked)
        self.layout.addWidget(self.delete_button)

    def update_item(self, item, params=None):
        self.name.setText(item)

    def _delete_button_clicked(self):
        self.document_base_creator_widget.delete_attribute(self.name.text())

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

    def disable_input(self):
        self.delete_button.setEnabled(False)
예제 #2
0
    def __init__(self, ID=0):
        super(NodeWidget, self).__init__()
        laytout = QHBoxLayout()
        self.input_id = QLineEdit(str(ID))
        self.input_id.setFixedWidth(30)
        self.input_text = QLineEdit("")
        self.input_text.setFixedHeight(20)
        self.input_text.setFixedWidth(400)
        self.input_type = QComboBox()
        self.input_type.addItem('开始/结束')
        self.input_type.addItem('流程')
        self.input_type.addItem('判定')
        self.input_type.addItem('None')
        self.input_type.setFixedWidth(80)
        self.input_x = QLineEdit("0")
        self.input_x.setFixedWidth(30)
        self.input_y = QLineEdit("0")
        self.input_y.setFixedWidth(30)
        self.input_link1 = QLineEdit("")
        self.input_link1.setFixedWidth(30)
        self.input_link2 = QLineEdit("")
        self.input_link2.setFixedWidth(30)

        laytout.addWidget(self.input_id)
        laytout.addWidget(self.input_text)
        laytout.addWidget(self.input_type)
        laytout.addWidget(self.input_x)
        laytout.addWidget(self.input_y)
        laytout.addWidget(self.input_link1)
        laytout.addWidget(self.input_link2)
        laytout.addStretch()
        laytout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(laytout)
        self.data = []
    def __init__(
        self,
        onOpenLogButtonPressed: Callable[[], None],
        onAgentVarsButtonPressed: Callable[[], None],
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        The "Open Log" and "Agent Variables" buttons view used for opening those windows.
        """
        super(SolverWindowsButtonsView, self).__init__(parent=parent, *args, **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

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

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

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

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

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

        self.setLayout(layout)
예제 #4
0
파일: ServicesUI.py 프로젝트: wbeebe/pyqt
 def __addInputAndRadio__(self, input, radio):
     hbox = QHBoxLayout()
     hbox.setContentsMargins(0, 0, 0, 0)
     hbox.addWidget(input)
     hbox.addWidget(radio)
     widget = QWidget(self)
     widget.setLayout(hbox)
     self.layout.addWidget(widget, self.row, 0, 1, -1)
     self.row += 1
예제 #5
0
 def __addInputAndSelect__(self, input, top):
     hbox = QHBoxLayout()
     hbox.setContentsMargins(0, 0, 0, 0)
     hbox.addWidget(input)
     browseButton = BrowseButton(self, input, top)
     browseButton.adjustSize()
     hbox.addWidget(browseButton)
     widget = QWidget(self)
     widget.setLayout(hbox)
     self.layout.addWidget(widget, self.row, 0, 1, -1)
     self.row += 1
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()
예제 #7
0
    def __init__(
        self,
        minimum: XY,
        maximum: XY,
        initialValue: XY,
        parent: Optional[QWidget] = None,
        label: str = "by",
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        A horizontal dual spin box widget designed for the XY size of a maze.
        """
        valid = True
        if (initialValue.x < minimum.x) or (initialValue.y > maximum.y):
            valid = False
        elif (initialValue.x < minimum.x) or (initialValue.y > maximum.y):
            valid = False

        if not valid:
            raise ValueError(
                f"Initial value for XYPicker must be between {minimum} and {maximum}."
            )

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

        mazeSizePickerLayout = QHBoxLayout()
        mazeSizePickerLayout.setContentsMargins(0, 0, 0, 0)

        self.__xSpinBox = QSpinBox()
        self.__ySpinBox = QSpinBox()

        self.__xSpinBox.setMinimum(minimum.x)
        self.__ySpinBox.setMinimum(minimum.y)

        self.__xSpinBox.setMaximum(maximum.x)
        self.__ySpinBox.setMaximum(maximum.y)

        self.__xSpinBox.setValue(initialValue.x)
        self.__ySpinBox.setValue(initialValue.y)

        mazeSizePickerLayout.addWidget(self.__xSpinBox)
        mazeSizePickerLayout.addWidget(QLabel(label))
        mazeSizePickerLayout.addWidget(self.__ySpinBox)

        self.setLayout(mazeSizePickerLayout)
    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)
    def __init__(self, table_view):
        """ A simple class that inherits QWidget and acts as a container 
        for items added to QTableView cells """
        super().__init__()
        self.table_view = table_view
        self.setAutoFillBackground(True)

        view_button = QPushButton(QIcon("icons/view.png"), None)
        view_button.clicked.connect(self.displayItemValues)
        delete_button = QPushButton(QIcon("icons/trash.png"), None)
        delete_button.clicked.connect(self.deleteTableRows)

        # Create the layout for the buttons
        cell_layout = QHBoxLayout()
        cell_layout.setContentsMargins(0, 0, 0, 0)
        cell_layout.setSpacing(0)
        cell_layout.addWidget(view_button)
        cell_layout.addWidget(delete_button)
        self.setLayout(cell_layout)
예제 #10
0
    def __init__(self):
        super(MyWidget, self).__init__()
        self.setFixedSize(620, 600)
        self.setWindowTitle("自动画流程图工具 version:1.0.0 ")
        self.laytout = QVBoxLayout(self)
        headwidget = QWidget(self)
        headlayout = QHBoxLayout()
        label01 = QLabel(" ID ")
        label02 = QLabel("流程图内容")
        label03 = QLabel("节点类型")
        label04 = QLabel("X坐标")
        label05 = QLabel("Y坐标")
        label06 = QLabel("link1")
        label07 = QLabel('link2')
        headlayout.addWidget(label01, 3)
        headlayout.addWidget(label02, 40)
        headlayout.addWidget(label03, 8)
        headlayout.addWidget(label04, 3)
        headlayout.addWidget(label05, 3)
        headlayout.addWidget(label06, 3)
        headlayout.addWidget(label07, 3)
        headlayout.addStretch()
        headlayout.setContentsMargins(0, 0, 0, 0)
        headwidget.setLayout(headlayout)
        btnwidget = QWidget()
        btnlayout = QHBoxLayout()
        self.add_btn = QPushButton("添加节点")
        self.start_btn = QPushButton("开始画图")
        btnlayout.addWidget(self.add_btn)
        btnlayout.addWidget(self.start_btn)
        btnwidget.setLayout(btnlayout)
        btnlayout.setContentsMargins(0, 0, 0, 0)

        self.laytout.addStretch()
        self.laytout.addWidget(btnwidget)
        self.laytout.addWidget(headwidget)
        #self.laytout.addStretch()

        self.add_btn.clicked.connect(self.add_node)
        self.start_btn.clicked.connect(self.start_draw_visiflow)

        self.nodes = []
        self.id = 0
예제 #11
0
class Ribbon(QWidget):
     
    selection_mode = pyqtSignal(bool)

    def __init__(self, parent):
         
        super(Ribbon, self).__init__(parent)
        self.configure_gui()
        self.create_widgets()
        
    def configure_gui(self):
         
        self.undo = ['']
        self.redo = []
        
        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 5, 0)

    def create_widgets(self): pass    
예제 #12
0
    def exerciseLayout(self, index):
        hLayout = QHBoxLayout()
        setSpinBox = QSpinBox()
        setSpinBox.setRange(1, 100)
        setSpinBox.setValue(5)
        setSpinBox.setMaximumWidth(80)

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

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

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

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

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

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

        hLayout.setContentsMargins(0, 10, 0, 10)
        self.exerciseLayouts.append(hLayout)
        return hLayout
예제 #13
0
    def __init__(self, *args):
        QFrame.__init__(self, *args)

        self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Sunken)

        self.edit = QTextEdit()
        self.edit.setFrameStyle(QFrame.Shape.NoFrame)
        self.edit.setAcceptRichText(False)
        self.edit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)

        self.number_bar = self.NumberBar()
        self.number_bar.set_text_edit(self.edit)

        hbox = QHBoxLayout(self)
        hbox.setSpacing(0)
        hbox.setContentsMargins(10,0,0,0)
        hbox.addWidget(self.number_bar)
        hbox.addWidget(self.edit)

        self.edit.installEventFilter(self)
        self.edit.viewport().installEventFilter(self)
예제 #14
0
    def displayTag(self, tagName):
        tagWidget = QWidget()
        tagWidget.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
        tagWidget.enterEvent = lambda e: self.setCursor(
            QCursor(Qt.CursorShape.PointingHandCursor))
        tagWidget.leaveEvent = lambda e: self.setCursor(
            QCursor(Qt.CursorShape.ArrowCursor))
        tagWidget.mouseReleaseEvent = lambda e: self.removeTag(
            self.flowLayout.indexOf(tagWidget), returnTag=False)
        self.renderStyleSheet(tagWidget)

        hBoxTag = QHBoxLayout()

        tagLabel = QLabel()
        tagLabel.setText(tagName)
        tagLabel.setStyleSheet(f'''
            QLabel {{
                background-color: transparent;
                border: none;
            }}
        ''')
        hBoxTag.addWidget(tagLabel)

        crossIcon = QPixmap('MangoUI/TagBox/img/crossresized.png')
        crossIconLabel = QLabel()
        crossIconLabel.setPixmap(crossIcon)
        crossIconLabel.setStyleSheet(f'''
            QLabel {{
                background-color: transparent;
                border: none;
            }}
        ''')
        hBoxTag.addWidget(crossIconLabel)

        hBoxTag.setContentsMargins(10, 6, 6, 6)
        tagWidget.setLayout(hBoxTag)
        self.flowLayout.addWidget(tagWidget)
예제 #15
0
class AttributeWidget(CustomScrollableListItem):
    def __init__(self, document_base_viewer):
        super(AttributeWidget, self).__init__(document_base_viewer)
        self.document_base_viewer = document_base_viewer
        self.attribute = None

        self.setFixedHeight(40)
        self.setStyleSheet("background-color: white")

        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(20, 0, 20, 0)
        self.layout.setSpacing(40)

        self.attribute_name = QLabel()
        self.attribute_name.setFont(CODE_FONT_BOLD)
        self.layout.addWidget(self.attribute_name,
                              alignment=Qt.AlignmentFlag.AlignLeft)

        self.num_matched = QLabel("matches: -")
        self.num_matched.setFont(CODE_FONT)
        self.layout.addWidget(self.num_matched,
                              alignment=Qt.AlignmentFlag.AlignLeft)

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

        self.forget_matches_button = QPushButton()
        self.forget_matches_button.setIcon(QIcon("aset_ui/resources/redo.svg"))
        self.forget_matches_button.setToolTip(
            "Forget matches for this attribute.")
        self.forget_matches_button.setFlat(True)
        self.forget_matches_button.clicked.connect(
            self._forget_matches_button_clicked)
        self.buttons_layout.addWidget(self.forget_matches_button)

        self.remove_button = QPushButton()
        self.remove_button.setIcon(QIcon("aset_ui/resources/trash.svg"))
        self.remove_button.setToolTip("Remove this attribute.")
        self.remove_button.setFlat(True)
        self.remove_button.clicked.connect(self._remove_button_clicked)
        self.buttons_layout.addWidget(self.remove_button)

    def update_item(self, item, params=None):
        self.attribute = item

        if len(params.attributes) == 0:
            max_attribute_name_len = 10
        else:
            max_attribute_name_len = max(
                len(attribute.name) for attribute in params.attributes)
        self.attribute_name.setText(self.attribute.name + (
            " " * (max_attribute_name_len - len(self.attribute.name))))

        mappings_in_some_documents = False
        no_mappings_in_some_documents = False
        num_matches = 0
        for document in params.documents:
            if self.attribute.name in document.attribute_mappings.keys():
                mappings_in_some_documents = True
                if document.attribute_mappings[self.attribute.name] != []:
                    num_matches += 1
            else:
                no_mappings_in_some_documents = True

        if not mappings_in_some_documents and no_mappings_in_some_documents:
            self.num_matched.setText("not matched yet")
        elif mappings_in_some_documents and no_mappings_in_some_documents:
            self.num_matched.setText("only partly matched")
        else:
            self.num_matched.setText(f"matches: {num_matches}")

    def enable_input(self):
        self.forget_matches_button.setEnabled(True)
        self.remove_button.setEnabled(True)

    def disable_input(self):
        self.forget_matches_button.setDisabled(True)
        self.remove_button.setDisabled(True)

    def _forget_matches_button_clicked(self):
        self.document_base_viewer.main_window.forget_matches_for_attribute_with_given_name_task(
            self.attribute.name)

    def _remove_button_clicked(self):
        self.document_base_viewer.main_window.remove_attribute_with_given_name_task(
            self.attribute.name)
예제 #16
0
    def __init__(self, parent=None):
        super(VideoWindow, self).__init__(parent)
        self.setWindowTitle("StudioProject")
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.gScene = QGraphicsScene(self)
        self.gView = GraphicView(self.gScene, self)
        self.gView.viewport().setAttribute(
            Qt.WidgetAttribute.WA_AcceptTouchEvents, False)
        # self.gView.setBackgroundBrush(QBrush(Qt.black))

        self.videoStartDatetime = None
        self.videoCurrentDatetime = None

        self.projectFile = ''
        self.graphicsFile = ''
        self.videoFile = ''

        self.obsTb = ObsToolbox(self)

        # # ===================== Setting video item ==============================
        # self.videoItem = QGraphicsVideoItem()
        # self.videoItem.setAspectRatioMode(Qt.KeepAspectRatio)
        # self.gScene.addItem(self.videoItem)
        # self.videoItem.mouseMoveEvent = self.gView.mouseMoveEvent

        self.mediaPlayer = QMediaPlayer(self)
        # self.mediaPlayer.setVideoOutput(self.videoItem)
        self.mediaPlayer.playbackStateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.errorOccurred.connect(self.handleError)
        # self.mediaPlayer.setMuted(True)
        # self.mediaPlayer.setNotifyInterval(100)

        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setIcon(self.style().standardIcon(
            QStyle.StandardPixmap.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

        self.changePlayRateBtn = QPushButton('1x')
        self.changePlayRateBtn.setFixedWidth(40)
        # self.incrPlayRateBtn.setEnabled(False)
        self.changePlayRateBtn.clicked.connect(self.changePlayRate)

        self.positionSlider = QSlider(Qt.Orientation.Horizontal)
        self.positionSlider.setRange(0, 0)
        self.positionSlider.sliderMoved.connect(self.setPosition)

        self.timerLabel = QLabel()
        self.timerLabel.setText('--:--:--')
        self.timerLabel.setFixedWidth(58)

        self.dateLabel = QLabel()
        self.dateLabel.setText('Video date: --')
        self.statusBar.addPermanentWidget(self.dateLabel)

        # Create open action
        self.openVideoAction = QAction(QIcon('icons/video-file.png'),
                                       'Open video', self)
        self.openVideoAction.setShortcut('Ctrl+O')
        self.openVideoAction.setStatusTip('Open video file')
        self.openVideoAction.triggered.connect(self.openVideoFile)

        # Create observation action
        obsTbAction = QAction(QIcon('icons/checklist.png'),
                              'Observation toolbox', self)
        obsTbAction.setStatusTip('Open observation toolbox')
        obsTbAction.triggered.connect(self.openObsToolbox)

        self.drawPointAction = QAction(QIcon('icons/drawPoint.png'),
                                       'Draw point', self)
        self.drawPointAction.setStatusTip('Draw point over the video')
        self.drawPointAction.setCheckable(True)
        self.drawPointAction.setEnabled(False)
        self.drawPointAction.triggered.connect(self.drawingClick)

        self.drawLineAction = QAction(QIcon('icons/drawLine.png'), 'Draw line',
                                      self)
        self.drawLineAction.setStatusTip('Draw line over the video')
        self.drawLineAction.setCheckable(True)
        self.drawLineAction.setEnabled(False)
        self.drawLineAction.triggered.connect(self.drawingClick)

        self.drawZoneAction = QAction(QIcon('icons/drawZone.png'), 'Draw zone',
                                      self)
        self.drawZoneAction.setStatusTip('Draw zone over the video')
        self.drawZoneAction.setCheckable(True)
        self.drawZoneAction.setEnabled(False)
        self.drawZoneAction.triggered.connect(self.drawingClick)

        self.maskGenAction = QAction(QIcon('icons/mask.png'),
                                     'Generate mask file', self)
        self.maskGenAction.setStatusTip(
            'Generate mask file for TrafficIntelligence')
        self.maskGenAction.setCheckable(True)
        self.maskGenAction.setEnabled(False)
        self.maskGenAction.triggered.connect(self.generateMask)

        actionGroup = QActionGroup(self)
        actionGroup.addAction(self.drawPointAction)
        actionGroup.addAction(self.drawLineAction)
        actionGroup.addAction(self.drawZoneAction)

        openProjectAction = QAction(QIcon('icons/open-project.png'),
                                    'Open project', self)
        openProjectAction.setStatusTip('Open project')
        openProjectAction.triggered.connect(self.openProject)

        self.saveProjectAction = QAction(QIcon('icons/save-project.png'),
                                         'Save project', self)
        self.saveProjectAction.setStatusTip('Save project')
        self.saveProjectAction.setEnabled(False)
        self.saveProjectAction.triggered.connect(self.saveProject)

        self.saveGraphAction = QAction(QIcon('icons/save-graphics.png'),
                                       'Save graphics', self)
        self.saveGraphAction.setStatusTip('Save graphics to database')
        self.saveGraphAction.setEnabled(False)
        self.saveGraphAction.triggered.connect(self.saveGraphics)

        self.loadGraphAction = QAction(QIcon('icons/folders.png'),
                                       'Load graphics', self)
        self.loadGraphAction.setStatusTip('Load graphics from database')
        self.loadGraphAction.setEnabled(False)
        self.loadGraphAction.triggered.connect(self.loadGraphics)

        # Create exit action
        exitAction = QAction(QIcon('icons/close.png'), 'Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.exitCall)  # self.exitCall

        # Create menu bar and add action
        # menuBar = self.menuBar()
        # menuBar.setNativeMenuBar(False)
        # fileMenu = menuBar.addMenu('&File')
        # fileMenu.addAction(openVideoAction)
        # fileMenu.addAction(obsTbAction)
        # fileMenu.addAction(exitAction)

        self.toolbar = self.addToolBar('Tools')
        self.toolbar.setIconSize(QSize(24, 24))
        self.toolbar.addAction(openProjectAction)
        self.toolbar.addAction(self.saveProjectAction)
        self.toolbar.addAction(self.openVideoAction)

        # self.toolbar.insertSeparator(self.loadGraphAction)
        # self.toolbar.addAction(self.loadGraphAction)
        # self.toolbar.addAction(self.saveGraphAction)
        # self.toolbar.addAction(self.drawPointAction)
        # self.toolbar.addAction(self.drawLineAction)
        # self.toolbar.addAction(self.drawZoneAction)
        self.toolbar.addAction(self.maskGenAction)
        # self.toolbar.insertSeparator(self.drawPointAction)

        self.toolbar.insertSeparator(obsTbAction)
        self.toolbar.addAction(obsTbAction)

        self.toolbar.insertSeparator(exitAction)
        self.toolbar.addAction(exitAction)

        # Create a widget for window contents
        wid = QWidget(self)
        self.setCentralWidget(wid)

        # Create layouts to place inside widget
        controlLayout = QHBoxLayout()
        controlLayout.setContentsMargins(0, 0, 0, 0)
        # controlLayout.addWidget(self.decrPlayRateBtn)
        controlLayout.addWidget(self.playButton)
        controlLayout.addWidget(self.changePlayRateBtn)
        controlLayout.addWidget(self.timerLabel)
        controlLayout.addWidget(self.positionSlider)
        # controlLayout.addWidget(self.durationLabel)

        layout = QVBoxLayout()
        layout.addWidget(self.gView)
        layout.addLayout(controlLayout)

        # Set widget to contain window contents
        wid.setLayout(layout)
예제 #17
0
class NuggetListItemWidget(CustomScrollableListItem):
    def __init__(self, nugget_list_widget):
        super(NuggetListItemWidget, self).__init__(nugget_list_widget)
        self.nugget_list_widget = nugget_list_widget
        self.nugget = None

        self.setFixedHeight(40)
        self.setStyleSheet("background-color: white")

        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(20, 0, 20, 0)
        self.layout.setSpacing(10)

        self.info_label = QLabel()
        self.info_label.setFont(CODE_FONT_BOLD)
        self.layout.addWidget(self.info_label)

        self.left_split_label = QLabel("|")
        self.left_split_label.setFont(CODE_FONT_BOLD)
        self.layout.addWidget(self.left_split_label)

        self.text_edit = QTextEdit()
        self.text_edit.setReadOnly(True)
        self.text_edit.setFrameStyle(0)
        self.text_edit.setFont(CODE_FONT)
        self.text_edit.setLineWrapMode(QTextEdit.LineWrapMode.FixedPixelWidth)
        self.text_edit.setLineWrapColumnOrWidth(10000)
        self.text_edit.setHorizontalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.text_edit.setVerticalScrollBarPolicy(
            Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.text_edit.setFixedHeight(30)
        self.text_edit.setText("")
        self.layout.addWidget(self.text_edit)

        self.right_split_label = QLabel("|")
        self.right_split_label.setFont(CODE_FONT_BOLD)
        self.layout.addWidget(self.right_split_label)

        self.match_button = QPushButton()
        self.match_button.setIcon(QIcon("aset_ui/resources/correct.svg"))
        self.match_button.setFlat(True)
        self.match_button.clicked.connect(self._match_button_clicked)
        self.layout.addWidget(self.match_button)

        self.fix_button = QPushButton()
        self.fix_button.setIcon(QIcon("aset_ui/resources/incorrect.svg"))
        self.fix_button.setFlat(True)
        self.fix_button.clicked.connect(self._fix_button_clicked)
        self.layout.addWidget(self.fix_button)

    def update_item(self, item, params=None):
        self.nugget = item

        sentence = self.nugget[CachedContextSentenceSignal]["text"]
        start_char = self.nugget[CachedContextSentenceSignal]["start_char"]
        end_char = self.nugget[CachedContextSentenceSignal]["end_char"]

        self.text_edit.setText("")
        formatted_text = (
            f"{'&#160;' * (params - start_char)}{sentence[:start_char]}"
            f"<span style='background-color: #FFFF00'><b>{sentence[start_char:end_char]}</b></span>"
            f"{sentence[end_char:]}{'&#160;' * 50}")
        self.text_edit.textCursor().insertHtml(formatted_text)

        scroll_cursor = QTextCursor(self.text_edit.document())
        scroll_cursor.setPosition(params + 50)
        self.text_edit.setTextCursor(scroll_cursor)
        self.text_edit.ensureCursorVisible()

        self.info_label.setText(
            f"{str(round(self.nugget[CachedDistanceSignal], 2)).ljust(4)}")

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

    def _fix_button_clicked(self):
        self.nugget_list_widget.interactive_matching_widget.get_document_feedback(
            self.nugget)

    def enable_input(self):
        self.match_button.setEnabled(True)
        self.fix_button.setEnabled(True)

    def disable_input(self):
        self.match_button.setDisabled(True)
        self.fix_button.setDisabled(True)
예제 #18
0
class CalibrateWizard(QWizard):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.setWizardStyle(QWizard.WizardStyle.ModernStyle)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            else:
                self.close()

        else:
            print("Not all recorded!")

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

    def deleteItemsOfLayout(self, layout):
        if layout is not None:
            while layout.count():
                item = layout.takeAt(0)
                widget = item.widget()
                if widget is not None:
                    widget.setParent(None)
                else:
                    self.deleteItemsOfLayout(item.layout())
예제 #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()
예제 #20
0
class DocumentBaseViewerWidget(MainWindowContent):
    def __init__(self, main_window):
        super(DocumentBaseViewerWidget, self).__init__(main_window,
                                                       "Document Base")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def _create_document_base_button_clicked(self):
        self.attribute_names = []
        for attribute_widget in self.attributes_list.item_widgets[:self.
                                                                  attributes_list
                                                                  .
                                                                  num_visible_item_widgets]:
            self.attribute_names.append(attribute_widget.name.text())
        self.main_window.create_document_base_task(self.path.text(),
                                                   self.attribute_names)
    def __init__(
        self,
        onPlayButtonPressed: Callable[[], None],
        onPauseButtonPressed: Callable[[], None],
        onStepButtonPressed: Callable[[], None],
        onRestartButtonPressed: Callable[[], None],
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        The play, pause, step and restart buttons for the solver
        """
        super(SolverControlButtonsView, self).__init__(parent=parent,
                                                       *args,
                                                       **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

        buttonsLayout = QHBoxLayout()
        buttonsLayout.setContentsMargins(0, 0, 0, 0)

        ###
        ### Play, pause, step and restart buttons
        ###

        self.__playButton = LabelledIconButton(
            icon=QStyle.StandardPixmap.SP_MediaPlay,
            labelText="Play",
            parent=self,
        )
        self.__playButton.onButtonPressed.connect(
            onPlayButtonPressed)  # type: ignore

        self.__pauseButton = LabelledIconButton(
            icon=QStyle.StandardPixmap.SP_MediaPause,
            labelText="Pause",
            parent=self,
        )
        self.__pauseButton.onButtonPressed.connect(
            onPauseButtonPressed)  # type: ignore

        self.__stepButton = LabelledIconButton(
            icon=QStyle.StandardPixmap.SP_ArrowForward,
            labelText="Step",
            parent=self,
        )
        self.__stepButton.onButtonPressed.connect(
            onStepButtonPressed)  # type: ignore

        self.__restartButton = LabelledIconButton(
            icon=QStyle.StandardPixmap.SP_BrowserReload,
            labelText="Restart",
            parent=self,
        )
        self.__restartButton.onButtonPressed.connect(
            onRestartButtonPressed)  # type: ignore

        buttonsLayout.addWidget(self.__playButton)
        buttonsLayout.addWidget(self.__pauseButton)
        buttonsLayout.addWidget(self.__stepButton)
        buttonsLayout.addWidget(self.__restartButton)

        self.setLayout(buttonsLayout)
예제 #23
0
class ManageData(QMainWindow):

    populateGallery = pyqtSignal()
    closedWindow = pyqtSignal(object)
    
    def __init__(self):

        super(ManageData, self).__init__()
        self.setWindowTitle('Manage Data')
        self.configure_gui()
        self.create_widgets()
        self.create_menu()
        self.showMaximized()
        self.mysql = Authenticate()

    def configure_gui(self):
        
        self.center = QWidget(self)
        self.layout = QHBoxLayout()

        self.center.setLayout(self.layout)
        self.setCentralWidget(self.center)
        self.layout.setContentsMargins(5, 0, 5, 0)
        self.layout.setSpacing(0)

    def create_widgets(self):

        self.windows = set()

        self.gallery = Gallery(self)
        self.preview = Preview(self, 'white')
        self.layout.addWidget(self.gallery)
        self.layout.addWidget(self.preview)
        
        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)
        self.statusbar.setFixedHeight(25)

    def create_menu(self): pass

    def keyPressEvent(self, event):

        key_press = event.key()
        modifiers = event.modifiers()
        alt = modifiers == Qt.Modifier.ALT

        if alt:
            
            if key_press == Qt.Key_Left: self.ribbon.go_back()
                
            elif key_press == Qt.Key_Right: self.ribbon.go_forward()
            
        if key_press == Qt.Key.Key_F4: self.ribbon.tags.setFocus()
        
        elif key_press == Qt.Key.Key_F5: self.select_records()

        elif key_press == Qt.Key.Key_Delete:
            
            self.delete_records(self.gallery.selectedIndexes())
                        
        elif key_press == Qt.Key.Key_Escape: self.close()
    
    def closeEvent(self, event):
        
        self.mysql.close()
        for window in self.windows: window.close()
        self.closedWindow.emit(self)
예제 #24
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.")