コード例 #1
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        vbox = QVBoxLayout()

        btn = QPushButton('Dialog', self)
        btn.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
        btn.move(20, 20)

        vbox.addWidget(btn)

        btn.clicked.connect(self.showDialog)

        self.lbl = QLabel('Knowledge only matters', self)
        self.lbl.move(130, 20)

        vbox.addWidget(self.lbl)
        self.setLayout(vbox)

        self.setGeometry(300, 300, 450, 350)
        self.setWindowTitle('Font dialog')
        self.show()

    def showDialog(self):

        font, ok = QFontDialog.getFont()

        if ok:
            self.lbl.setFont(font)
コード例 #2
0
    def setUpWindow(self):
        """Set up the dialog's widgets and layout."""
        header_label = QLabel("""<p style='color:#65888C'>
            Welcome to Database Manager</p>""")
        header_label.setFont(QFont("Arial", 24))
        header_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        self.info_label = QLabel("""<p style='color:#65888C'>
            Sign into your account.</p>""")
        self.info_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)

        username_label = QLabel("Username:"******"Password:"******"Log In", QDialogButtonBox.ButtonRole.AcceptRole)
        button_box.accepted.connect(self.clickedLogInButton)

        log_in_grid = QGridLayout()
        log_in_grid.addWidget(header_label, 0, 0, 1, 3,
                              Qt.AlignmentFlag.AlignCenter)
        log_in_grid.addWidget(self.info_label, 1, 0, 1, 3,
                              Qt.AlignmentFlag.AlignCenter)
        log_in_grid.addWidget(username_label, 2, 0)
        log_in_grid.addWidget(self.username_line, 2, 1)
        log_in_grid.addWidget(password_label, 3, 0)
        log_in_grid.addWidget(self.password_line, 3, 1)
        log_in_grid.addWidget(button_box, 4, 1)
        self.setLayout(log_in_grid)
コード例 #3
0
class MainWindowContentSection(QWidget):
    def __init__(self, main_window_content, sub_header_text):
        super(MainWindowContentSection, self).__init__()
        self.main_window_content = main_window_content

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

        self.sub_header = QLabel(sub_header_text)
        self.sub_header.setFont(SUBHEADER_FONT)
        self.layout.addWidget(self.sub_header)
コード例 #4
0
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()
コード例 #5
0
class Clean_FW_File(QWidget):
    def __init__(self):
        super().__init__()
        self.setFixedWidth(400)
        self.setFixedHeight(300)
        self.setWindowTitle('Clean Freezerworks Export File')

        font = QFont()
        font.setFamily('Consolas')
        font.setPointSize(16)

        self.label = QLabel(self)
        self.label.setFont(font)
        self.label.setText(
            'This is a test to see if the font style works or not!')
コード例 #6
0
    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon(self.iconName))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        vbox = QVBoxLayout()
        label = QLabel("This is PyQt6 Label")
        vbox.addWidget(label)

        label2 = QLabel("This is PyQt6 GUI Application Development")
        label2.setFont(QtGui.QFont("Sanserif ", 20))
        label2.setStyleSheet('color:red')
        vbox.addWidget(label2)

        self.setLayout(vbox)

        self.show()
コード例 #7
0
def makeTitle():
    titleLayout = QHBoxLayout()
    # titleLayout.setSpacing(42)
    titleFont = QtGui.QFont("Times", 32, QtGui.QFont.Weight.Bold)

    title = QLabel(opts.timeclockOpts["title"])
    title.setFixedWidth(820)
    title.setFont(titleFont)

    logo = QLabel()
    logoImage = QtGui.QPixmap("../data/assets/" + opts.timeclockOpts["logo"])
    logoImage = logoImage.scaled(QtCore.QSize(100, 100))
    logo.setPixmap(logoImage)
    if opts.timeclockOpts["darkTheme"]:
        logo.setStyleSheet("QLabel {background:white}")

    titleLayout.addWidget(title)
    titleLayout.addStretch()
    titleLayout.addWidget(logo)
    return titleLayout
コード例 #8
0
ファイル: Dictgui.py プロジェクト: nanza404/lazyDictionary
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('LazyDictionary')
        self.setWindowIcon(QIcon('book.png'))
        self.setFixedHeight(100)
        self.setFixedWidth(300)
        self.label = QLabel('word count: 0', self)
        self.label.setFont(QFont('san serif', 15))
        self.label.move(90, 0)
        self.button()
        self.oneClick = True

    def button(self):
        self.btn0 = QPushButton('Start', self)
        self.btn0.setGeometry(0, 30, 150, 70)
        self.btn0.setStyleSheet('background-color:green')
        self.btn0.setFont(QFont('san serif', 15))
        self.btn1 = QPushButton('Stop', self)
        self.btn1.setGeometry(150, 30, 150, 70)
        self.btn1.setStyleSheet('background-color:grey')
        self.btn1.setFont(QFont('san serif', 15))
        self.btn0.clicked.connect(self.clickButton0)
        self.btn1.clicked.connect(self.clickButton1)

    def clickButton0(self):
        self.btn0.setStyleSheet('background-color:grey')
        self.btn1.setStyleSheet('background-color:red')
        if self.oneClick:
            main.start()
        self.oneClick = False

    def clickButton1(self):
        self.btn1.setStyleSheet('background-color:grey')
        self.btn0.setStyleSheet('background-color:green')
        main.end()
        self.oneClick = True
        
    def checkCount(self):
        text = 'word count: ' + str(htmlparse.wordCount)
        self.label.setText(text)
コード例 #9
0
class MainWindowContent(QWidget):
    def __init__(self, main_window, header_text):
        super(MainWindowContent, self).__init__()
        self.main_window = main_window

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

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

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

    @abc.abstractmethod
    def disable_input(self):
        raise NotImplementedError
コード例 #10
0
ファイル: MenuBar.py プロジェクト: alexpdev/blackjack
class About(QDialog):
    """Dialog with information about the Program."""
    def __init__(self, parent=None, window=None):
        """Construct Dialog Box.

        kwargs:
            parent (QWidget, optional) Parent widget object. Defaults to None.
            window (QWidget, optional) Program's MainWindow. Defaults to None.
        """
        super().__init__(parent=parent)
        self.window = window
        font = QFont()
        font2 = QFont()
        font.setPointSize(11)
        font2.setPointSize(20)
        fixed = QSizePolicy.Policy.Fixed
        sizePolicy = QSizePolicy(fixed, fixed)
        self.resize(365, 229)
        self.setWindowTitle("About")
        self.setObjectName("aboutBox")
        self.setGeometry(QRect(180, 190, 171, 32))
        self.setSizePolicy(sizePolicy)
        self.label = QLabel(self)
        self.label_2 = QLabel(self)
        self.label_3 = QLabel(self)
        self.label_4 = QLabel(self)
        self.label_5 = QLabel(self)
        self.label_6 = QLabel(self)
        self.label.setGeometry(QRect(20, 10, 161, 51))
        self.label_2.setGeometry(QRect(150, 30, 49, 16))
        self.label_3.setGeometry(QRect(20, 80, 211, 16))
        self.label_4.setGeometry(QRect(20, 160, 201, 20))
        self.label_5.setGeometry(QRect(20, 120, 341, 41))
        self.label_6.setGeometry(QRect(20, 110, 121, 16))
        self.label.setFont(font2)
        self.label_3.setFont(font)
        self.label_5.setFont(font)
        self.label_6.setFont(font)
        self.label.setText("BlackJack")
        self.label_2.setText("v 0.3")
        self.label_3.setText("Copyright 2021 AlexPdev Inc.")
        self.label_4.setText("https://fsf.org/")
        self.label_5.setText("License GNU LESSER GENERAL PUBLIC LICENSE")
        self.label_6.setText("Creator AlexPdev")
        self.button = QPushButton("Ok", parent=self)
        self.button.pressed.connect(self.okbutton)

    def okbutton(self):
        """Close Window."""
        self.done(self.accept)
コード例 #11
0
class UIComicInfoWidget(QWidget):
    load_chapter_list_signa = QtCore.pyqtSignal(ChapterInfo)
    load_download_task_signa = QtCore.pyqtSignal(DownloadTask)

    def __init__(self, comic_info: ComicInfo, down_v_box_layout: QVBoxLayout):
        super().__init__()
        self.comic_info = comic_info
        self.down_v_box_layout = down_v_box_layout
        self.img_label = QLabel(self)
        self.img_label.setScaledContents(True)
        img = QImage.fromData(comic_info.cover)
        w, h = image_resize(comic_info.cover, width=200)
        self.img_label.resize(QtCore.QSize(w, h))
        self.img_label.setGeometry(10, 10, w, h)
        self.img_label.setPixmap(QPixmap.fromImage(img))
        # self.img_label.setPixmap(QtGui.QPixmap("/Users/bo/my/tmp/老夫子2/第1卷/1.jpg"))

        self.title = QLabel(self)
        self.title.setGeometry(220, 10, 100, 40)
        title_font = QtGui.QFont()
        title_font.setPointSize(16)
        title_font.setBold(True)
        title_font.setUnderline(True)
        self.title.setFont(title_font)
        self.title.setText(comic_info.title)
        self.title.setWordWrap(True)

        info_font = QtGui.QFont()
        info_font.setPointSize(14)
        # 作者
        self.author = QLabel(self)
        self.author.setText("作者 : " + comic_info.author)
        self.author.setGeometry(220, 50, 150, 40)
        self.author.setWordWrap(True)
        self.author.setFont(info_font)
        # 状态
        self.status = QLabel(self)
        self.status.setText("更新状态 : " + comic_info.status)
        self.status.setGeometry(220, 90, 150, 40)
        self.status.setFont(info_font)

        # 热度
        self.heat = QLabel(self)
        self.heat.setText("热度 : " + str(comic_info.heat))
        self.heat.setGeometry(220, 130, 150, 40)
        self.heat.setFont(info_font)

        # 类型
        self.tip = QLabel(self)
        self.tip.setText("类型 : " + comic_info.tip)
        self.tip.setGeometry(220, 170, 150, 40)
        self.tip.setWordWrap(True)
        self.tip.setFont(info_font)

        # web
        self.domain = QLabel(self)
        self.domain.setText(f"查看原网页 : {comic_info.domain}")
        self.domain.setText(f'查看原网页 : <a href="{comic_info.url}">{comic_info.domain}</a>')
        self.domain.setGeometry(220, 210, 150, 40)
        self.domain.setOpenExternalLinks(True)
        self.domain.setFont(info_font)

        # 描述
        self.describe = QLabel(self)
        self.describe.setText("  " + comic_info.describe)
        self.describe.setGeometry(10, 320, 350, 330)
        self.describe.setWordWrap(True)
        # 对齐方式
        self.describe.setAlignment(
            QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop)

        # 章节列表,创建一个区域

        self.searchHBoxLayout = QHBoxLayout()
        # self.searchHBoxLayout.addSpacing()
        self.searchGroupBox = QGroupBox()
        self.searchGroupBox.setLayout(self.searchHBoxLayout)

        self.searchScroll = QScrollArea(self)
        self.searchScroll.setGeometry(370, 10, 574, 590)
        self.searchScroll.setWidget(self.searchGroupBox)
        self.searchScroll.setWidgetResizable(True)

        # 全选
        self.check_all = QCheckBox(self)
        self.check_all.setText("全选")
        self.check_all.setGeometry(700, 610, 100, 20)
        self.check_all.stateChanged.connect(self.check_all_fun)

        # 下载
        self.down_button = QPushButton(self)
        self.down_button.setText("下载")
        self.down_button.setGeometry(780, 605, 50, 30)

        self.down_button.clicked.connect(self.download_button_click)

        self.load_chapter_list_signa.connect(self.load_chapter)
        self.load_download_task_signa.connect(self.download_callback)

        # 调用对应的service的接口,获取章节列表
        constant.SERVICE.chapter(comic_info, self.load_chapter_list_signa.emit)

    i = 0
    searchVBoxLayout: QVBoxLayout
    check_box_list: List[QCheckBox] = []

    def check_all_fun(self):
        for check_box in self.check_box_list:
            check_box.setChecked(self.check_all.isChecked())

    def download_callback(self, task: DownloadTask):
        widget = DownLoadTaskWidget(task)
        self.down_v_box_layout.addWidget(widget)

    def download_button_click(self):
        flag = False
        for check_box in self.check_box_list:
            if check_box.isChecked():
                constant.SERVICE.parse_image(self.comic_info, check_box.property("chapter_info"),
                                             self.load_download_task_signa.emit)
                if not flag:
                    QMessageBox.information(self, "下载通知", "正在解析选中章节", QMessageBox.StandardButton.Yes)
                    flag = True

    def load_chapter(self, chapter_info: ChapterInfo):
        if self.i % 26 == 0:
            self.searchVBoxLayout = QVBoxLayout()
            self.searchVBoxLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)  # 对齐方式,研究了3个小时 o(╥﹏╥)o
            self.searchHBoxLayout.addLayout(self.searchVBoxLayout)

        check_box = QCheckBox()
        self.check_box_list.append(check_box)
        check_box.setText(chapter_info.title)
        check_box.setProperty("chapter_info", chapter_info)
        task = constant.downloaded_task_map.get(chapter_info.url)
        if task and task.status == -1:
            check_box.setStyleSheet('color:red')
            check_box.setChecked(True)

        self.searchVBoxLayout.addWidget(check_box)
        self.i += 1
コード例 #12
0
ファイル: main_window.py プロジェクト: DataManagementLab/ASET
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.")
コード例 #13
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)
コード例 #14
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()
コード例 #15
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)
コード例 #16
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)
コード例 #17
0
class DataGUI(QWidget):
    def __init__(self):
        super().__init__()

        self.experiment_file_name = None
        self.experiment_file_directory = None
        self.data_directory = None
        self.max_rois = 50
        self.roi_type = 'freehand'
        self.roi_radius = None
        self.existing_roi_set_paths = {}

        self.current_roi_index = 0
        self.current_z_slice = 0
        self.current_channel = 1  # index
        self.image_series_name = ''
        self.series_number = None
        self.roi_response = []
        self.roi_mask = []
        self.roi_path = []
        self.roi_image = None
        self.roi_path_list = []

        self.blank_image = np.zeros((1, 1))

        self.colors = [
            mcolors.to_rgb(x) for x in list(mcolors.TABLEAU_COLORS)[:20]
        ]

        self.initUI()

    def initUI(self):
        self.grid = QGridLayout(self)

        self.file_control_grid = QGridLayout()
        self.file_control_grid.setSpacing(3)
        self.grid.addLayout(self.file_control_grid, 0, 0)

        self.file_tree_grid = QGridLayout()
        self.file_tree_grid.setSpacing(3)
        self.grid.addLayout(self.file_tree_grid, 1, 0)

        self.group_control_grid = QGridLayout()
        self.group_control_grid.setSpacing(3)
        self.grid.addLayout(self.group_control_grid, 0, 1)

        self.attribute_grid = QGridLayout()
        self.attribute_grid.setSpacing(3)
        self.grid.addLayout(self.attribute_grid, 1, 1)

        self.roi_control_grid = QGridLayout()
        self.roi_control_grid.setSpacing(3)
        self.grid.addLayout(self.roi_control_grid, 0, 2)

        self.plot_grid = QGridLayout()
        self.plot_grid.setSpacing(3)
        self.grid.addLayout(self.plot_grid, 1, 2)

        # # # # File control browser: # # # # # # # # (0,0)
        loadButton = QPushButton("Load expt. file", self)
        loadButton.clicked.connect(self.selectDataFile)
        # Label with current expt file
        self.currentExperimentLabel = QLabel('')
        self.file_control_grid.addWidget(loadButton, 0, 0)
        self.file_control_grid.addWidget(self.currentExperimentLabel, 1, 0)

        directoryButton = QPushButton("Select data directory", self)
        directoryButton.clicked.connect(self.selectDataDirectory)
        self.file_control_grid.addWidget(directoryButton, 0, 1)
        self.data_directory_display = QLabel('')
        self.data_directory_display.setFont(QtGui.QFont('SansSerif', 8))
        self.file_control_grid.addWidget(self.data_directory_display, 1, 1)

        # Attach metadata to file
        attachDatabutton = QPushButton("Attach metadata to file", self)
        attachDatabutton.clicked.connect(self.attachData)
        self.file_control_grid.addWidget(attachDatabutton, 2, 0, 1, 2)

        # Select image data file
        selectImageDataFileButton = QPushButton("Select image data file", self)
        selectImageDataFileButton.clicked.connect(self.selectImageDataFile)
        self.file_control_grid.addWidget(selectImageDataFileButton, 3, 0, 1, 2)

        # # # # File tree: # # # # # # # #  (1,0)
        self.groupTree = QTreeWidget(self)
        self.groupTree.setHeaderHidden(True)
        self.groupTree.itemClicked.connect(self.onTreeItemClicked)
        self.file_tree_grid.addWidget(self.groupTree, 3, 0, 2, 7)

        # # # # Group control: # # # # # # # # (0, 1)
        deleteGroupButton = QPushButton("Delete selected group", self)
        deleteGroupButton.clicked.connect(self.deleteSelectedGroup)
        self.group_control_grid.addWidget(deleteGroupButton, 0, 0, 1, 2)

        # File name display
        self.currentImageFileNameLabel = QLabel('')
        self.group_control_grid.addWidget(self.currentImageFileNameLabel, 1, 0)

        # Channel drop down
        ch_label = QLabel('Channel:')
        self.ChannelComboBox = QComboBox(self)
        self.ChannelComboBox.addItem("1")
        self.ChannelComboBox.addItem("0")
        self.ChannelComboBox.activated.connect(self.selectChannel)
        self.group_control_grid.addWidget(ch_label, 2, 0)
        self.group_control_grid.addWidget(self.ChannelComboBox, 2, 1)

        # # # # Attribute table: # # # # # # # # (1, 1)
        self.tableAttributes = QTableWidget()
        self.tableAttributes.setStyleSheet("")
        self.tableAttributes.setColumnCount(2)
        self.tableAttributes.setObjectName("tableAttributes")
        self.tableAttributes.setRowCount(0)
        item = QTableWidgetItem()
        font = QtGui.QFont()
        font.setPointSize(10)
        item.setFont(font)
        item.setBackground(QtGui.QColor(121, 121, 121))
        brush = QtGui.QBrush(QtGui.QColor(91, 91, 91))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        item.setForeground(brush)
        self.tableAttributes.setHorizontalHeaderItem(0, item)
        item = QTableWidgetItem()
        item.setBackground(QtGui.QColor(123, 123, 123))
        brush = QtGui.QBrush(QtGui.QColor(91, 91, 91))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        item.setForeground(brush)
        self.tableAttributes.setHorizontalHeaderItem(1, item)
        self.tableAttributes.horizontalHeader().setCascadingSectionResizes(
            True)
        self.tableAttributes.horizontalHeader().setHighlightSections(False)
        self.tableAttributes.horizontalHeader().setSortIndicatorShown(True)
        self.tableAttributes.horizontalHeader().setStretchLastSection(True)
        self.tableAttributes.verticalHeader().setVisible(False)
        self.tableAttributes.verticalHeader().setHighlightSections(False)
        item = self.tableAttributes.horizontalHeaderItem(0)
        item.setText("Attribute")
        item = self.tableAttributes.horizontalHeaderItem(1)
        item.setText("Value")

        self.tableAttributes.itemChanged.connect(self.update_attrs_to_file)
        self.attribute_grid.addWidget(self.tableAttributes, 3, 0, 1, 8)

        # # # # Roi control # # # # # # # # (0, 2)
        # ROI type drop-down
        self.RoiTypeComboBox = QComboBox(self)
        self.RoiTypeComboBox.addItem("freehand")
        radii = [1, 2, 3, 4, 6, 8]
        for radius in radii:
            self.RoiTypeComboBox.addItem("circle:" + str(radius))
        self.RoiTypeComboBox.activated.connect(self.selectRoiType)
        self.roi_control_grid.addWidget(self.RoiTypeComboBox, 0, 0)

        # Clear all ROIs button
        self.clearROIsButton = QPushButton("Clear ROIs", self)
        self.clearROIsButton.clicked.connect(self.clearRois)
        self.roi_control_grid.addWidget(self.clearROIsButton, 0, 2)

        # Response display type dropdown
        self.RoiResponseTypeComboBox = QComboBox(self)

        self.RoiResponseTypeComboBox.addItem("RawTrace")
        self.RoiResponseTypeComboBox.addItem("TrialAverage")
        self.RoiResponseTypeComboBox.addItem("TrialResponses")
        self.RoiResponseTypeComboBox.addItem("TrialAverageDFF")
        self.roi_control_grid.addWidget(self.RoiResponseTypeComboBox, 2, 2)

        # ROIset file name line edit box
        self.defaultRoiSetName = "roi_set_name"
        self.le_roiSetName = QLineEdit(self.defaultRoiSetName)
        self.roi_control_grid.addWidget(self.le_roiSetName, 1, 1)

        # Save ROIs button
        self.saveROIsButton = QPushButton("Save ROIs", self)
        self.saveROIsButton.clicked.connect(self.saveRois)
        self.roi_control_grid.addWidget(self.saveROIsButton, 1, 0)

        # Load ROI set combobox
        self.loadROIsComboBox = QComboBox(self)
        self.loadROIsComboBox.addItem("(load existing ROI set)")
        self.loadROIsComboBox.activated.connect(self.selectedExistingRoiSet)
        self.roi_control_grid.addWidget(self.loadROIsComboBox, 1, 2)
        self.updateExistingRoiSetList()

        # Delete current roi button
        self.deleteROIButton = QPushButton("Delete ROI", self)
        self.deleteROIButton.clicked.connect(self.deleteRoi)
        self.roi_control_grid.addWidget(self.deleteROIButton, 2, 0)

        # Current roi slider
        self.roiSlider = QSlider(QtCore.Qt.Orientation.Horizontal, self)
        self.roiSlider.setMinimum(0)
        self.roiSlider.setMaximum(self.max_rois)
        self.roiSlider.valueChanged.connect(self.sliderUpdated)
        self.roi_control_grid.addWidget(self.roiSlider, 2, 1, 1, 1)

        ctx = plt.rc_context({
            'xtick.major.size': 1,
            'axes.spines.top': False,
            'axes.spines.right': False,
            'xtick.labelsize': 'xx-small',
            'ytick.labelsize': 'xx-small',
            'xtick.major.size': 1.0,
            'ytick.major.size': 1.0,
            'xtick.major.pad': 1.0,
            'ytick.major.pad': 1.0
        })
        with ctx:
            self.responseFig = plt.figure(frameon=False, layout='constrained')
            self.responsePlot = self.responseFig.add_subplot(111)
            self.responseCanvas = FigureCanvas(self.responseFig)
        self.responseCanvas.draw_idle()
        self.plot_grid.addWidget(self.responseCanvas, 0, 0)

        # # # # Image canvas # # # # # # # # (1, 2)
        self.roi_fig = plt.figure()
        self.roi_ax = self.roi_fig.add_subplot(111)
        self.roi_canvas = FigureCanvas(self.roi_fig)
        self.toolbar = NavigationToolbar(self.roi_canvas, self)
        self.roi_ax.set_aspect('equal')
        self.roi_ax.set_axis_off()
        self.plot_grid.addWidget(self.toolbar, 1, 0)
        self.plot_grid.addWidget(self.roi_canvas, 2, 0)
        self.plot_grid.setRowStretch(0, 1)
        self.plot_grid.setRowStretch(1, 3)
        self.plot_grid.setRowStretch(2, 3)

        # Current z slice slider
        self.zSlider = QSlider(QtCore.Qt.Orientation.Horizontal, self)
        self.zSlider.setMinimum(0)
        self.zSlider.setMaximum(50)
        self.zSlider.setValue(0)
        self.zSlider.valueChanged.connect(self.zSliderUpdated)
        self.plot_grid.addWidget(self.zSlider, 3, 0)

        self.roi_fig.tight_layout()

        self.setWindowTitle('Visanalysis')
        self.setGeometry(200, 200, 1200, 600)
        self.show()

    def _populateTree(self, widget, dict):
        widget.clear()
        self.fill_item(widget.invisibleRootItem(), dict)

    def fill_item(self, item, value):
        item.setExpanded(True)
        if type(value) is dict:
            for key, val in sorted(value.items()):
                child = QTreeWidgetItem()
                child.setText(0, key)
                item.addChild(child)
                self.fill_item(child, val)
        elif type(value) is list:
            for val in value:
                child = QTreeWidgetItem()
                item.addChild(child)
                if type(val) is dict:
                    child.setText(0, '[dict]')
                    self.fill_item(child, val)
                elif type(val) is list:
                    child.setText(0, '[list]')
                    self.fill_item(child, val)
                else:
                    child.setText(0, val)
                child.setExpanded(True)
        else:
            child = QTreeWidgetItem()
            child.setText(0, value)
            item.addChild(child)

    def onTreeItemClicked(self, item, column):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        group_path = h5io.getPathFromTreeItem(
            self.groupTree.selectedItems()[0])
        self.clearRois()
        self.series_number = None
        if 'series_' in group_path:
            self.series_number = int(
                group_path.split('series_')[-1].split('/')[0])
            if self.plugin.dataIsAttached(file_path, self.series_number):
                self.plugin.updateImagingDataObject(
                    self.experiment_file_directory, self.experiment_file_name,
                    self.series_number)
            # look for image_file_name or ask user to select it
            if self.data_directory is not None:
                image_file_name = h5io.readImageFileName(
                    file_path, self.series_number)
                if image_file_name is None or image_file_name == '':
                    image_file_path, _ = QFileDialog.getOpenFileName(
                        self, "Select image file")
                    print('User selected image file at {}'.format(
                        image_file_path))
                    image_file_name = os.path.split(image_file_path)[-1]
                    self.data_directory = os.path.split(
                        image_file_path)[:-1][0]
                    h5io.attachImageFileName(file_path, self.series_number,
                                             image_file_name)
                    print('Attached image_file_name {} to series {}'.format(
                        image_file_name, self.series_number))
                    print('Data directory is {}'.format(self.data_directory))

                self.image_file_name = image_file_name
                self.currentImageFileNameLabel.setText(self.image_file_name)

        else:  # clicked part of the tree upstream of any series
            self.series_number = None

        if item.parent() is not None:
            if item.parent().text(
                    column) == 'rois':  # selected existing roi group
                roi_set_name = item.text(column)
                # print('Selected roi set {} from series {}'.format(roi_set_name, self.series_number))
                self.le_roiSetName.setText(roi_set_name)
                roi_set_path = h5io.getPathFromTreeItem(
                    self.groupTree.selectedItems()[0])
                self.loadRois(roi_set_path)
                self.redrawRoiTraces()

        if group_path != '':
            attr_dict = h5io.getAttributesFromGroup(file_path, group_path)
            editable_values = True  # user can edit metadata
            self.populate_attrs(attr_dict=attr_dict,
                                editable_values=editable_values)

        # show roi image
        if self.series_number is not None:  # Clicked on node of the tree associated with a single series
            if self.data_directory is not None:  # user has selected a raw data directory
                if self.plugin.dataIsAttached(file_path, self.series_number):
                    self.plugin.updateImageSeries(
                        data_directory=self.data_directory,
                        image_file_name=self.image_file_name,
                        series_number=self.series_number,
                        channel=self.current_channel)
                    self.roi_image = self.plugin.mean_brain
                    self.zSlider.setValue(0)
                    self.zSlider.setMaximum(self.roi_image.shape[2] - 1)
                    self.redrawRoiTraces()
                else:
                    print('Attach metadata to file before drawing rois')

            else:
                print('Select a data directory before drawing rois')

        # # # TEST # # #
        memory_usage = psutil.Process(os.getpid()).memory_info().rss * 10**-9
        print('Current Memory Usage: {:.2f}GB'.format(memory_usage))
        sys.stdout.flush()
        # # # TEST # # #

    def updateExistingRoiSetList(self):
        if self.experiment_file_name is not None:
            file_path = os.path.join(self.experiment_file_directory,
                                     self.experiment_file_name + '.hdf5')
            self.existing_roi_set_paths = self.plugin.getRoiSetPaths(
                file_path)  # dictionary of name: full path
            self.loadROIsComboBox.clear()
            for r_path in self.existing_roi_set_paths:
                self.loadROIsComboBox.addItem(r_path)

            self.show()

    def selectedExistingRoiSet(self):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        roi_set_key = self.loadROIsComboBox.currentText()
        roi_set_path = self.existing_roi_set_paths[roi_set_key]

        _, _, self.roi_path, self.roi_mask = self.plugin.loadRoiSet(
            file_path, roi_set_path)

        if self.series_number is not None:
            self.roi_response = []
            for new_path in self.roi_path:
                new_roi_resp = self.plugin.getRoiDataFromPath(
                    roi_path=new_path)
                self.roi_response.append(new_roi_resp)

            # update slider to show most recently drawn roi response
            self.current_roi_index = len(self.roi_response) - 1
            self.roiSlider.setValue(self.current_roi_index)

            # Update figures
            self.redrawRoiTraces()

    def selectDataFile(self):
        filePath, _ = QFileDialog.getOpenFileName(
            self, "Open experiment (hdf5) file")
        self.experiment_file_name = os.path.split(filePath)[1].split('.')[0]
        self.experiment_file_directory = os.path.split(filePath)[0]

        if self.experiment_file_name != '':
            self.currentExperimentLabel.setText(self.experiment_file_name)
            self.initializeDataAnalysis()
            self.populateGroups()
            self.updateExistingRoiSetList()

    def selectDataDirectory(self):
        filePath = str(
            QFileDialog.getExistingDirectory(self, "Select data directory"))
        self.data_directory = filePath
        self.data_directory_display.setText('..' + self.data_directory[-24:])

    def initializeDataAnalysis(self):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        data_type = h5io.getDataType(file_path)
        # Load plugin based on Rig name in hdf5 file
        if data_type == 'Bruker':
            from visanalysis.plugin import bruker
            self.plugin = bruker.BrukerPlugin()
        elif data_type == 'AODscope':
            from visanalysis.plugin import aodscope
            self.plugin = aodscope.AodScopePlugin()
        else:
            self.plugin = h5io.BasePlugin()

        self.plugin.parent_gui = self

        # # # TEST # # #
        memory_usage = psutil.Process(os.getpid()).memory_info().rss * 10**-9
        print('Current memory usage: {:.2f}GB'.format(memory_usage))
        sys.stdout.flush()
        # # # TEST # # #

    def attachData(self):
        if self.data_directory is not None:
            file_path = os.path.join(self.experiment_file_directory,
                                     self.experiment_file_name + '.hdf5')
            self.plugin.attachData(self.experiment_file_name, file_path,
                                   self.data_directory)
            print('Data attached')
        else:
            print('Select a data directory before attaching new data')

    def selectImageDataFile(self):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')

        image_file_path, _ = QFileDialog.getOpenFileName(
            self, "Select image file")
        print('User selected image file at {}'.format(image_file_path))
        self.image_file_name = os.path.split(image_file_path)[-1]
        self.data_directory = os.path.split(image_file_path)[:-1][0]
        h5io.attachImageFileName(file_path, self.series_number,
                                 self.image_file_name)
        print('Attached image_file_name {} to series {}'.format(
            self.image_file_name, self.series_number))
        print('Data directory is {}'.format(self.data_directory))

        self.currentImageFileNameLabel.setText(self.image_file_name)

        # show roi image
        if self.series_number is not None:
            if self.data_directory is not None:  # user has selected a raw data directory
                self.plugin.updateImageSeries(
                    data_directory=self.data_directory,
                    image_file_name=self.image_file_name,
                    series_number=self.series_number,
                    channel=self.current_channel)
                self.roi_image = self.plugin.mean_brain
                self.zSlider.setValue(0)
                self.zSlider.setMaximum(self.roi_image.shape[2] - 1)
                self.redrawRoiTraces()
            else:
                print('Select a data directory before drawing rois')

    def deleteSelectedGroup(self):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        group_path = h5io.getPathFromTreeItem(
            self.groupTree.selectedItems()[0])
        group_name = group_path.split('/')[-1]

        buttonReply = QMessageBox.question(
            self, 'Delete series',
            "Are you sure you want to delete group {}?".format(group_name),
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No)
        if buttonReply == QMessageBox.StandardButton.Yes:
            h5io.deleteGroup(file_path=file_path, group_path=group_path)
            print('Deleted group {}'.format(group_name))
            self.updateExistingRoiSetList()
            self.populateGroups()
        else:
            print('Delete aborted')

    def populateGroups(self):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        self.group_dset_dict = h5io.getHierarchy(file_path)
        self._populateTree(self.groupTree, self.group_dset_dict)

    def populate_attrs(self, attr_dict=None, editable_values=False):
        """Populate attribute for currently selected group."""
        self.tableAttributes.blockSignals(
            True)  # block udpate signals for auto-filled forms
        self.tableAttributes.setRowCount(0)
        self.tableAttributes.setColumnCount(2)
        self.tableAttributes.setSortingEnabled(False)

        if attr_dict:
            for num, key in enumerate(attr_dict):
                self.tableAttributes.insertRow(self.tableAttributes.rowCount())
                key_item = QTableWidgetItem(key)
                key_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable
                                  | QtCore.Qt.ItemFlag.ItemIsEnabled)
                self.tableAttributes.setItem(num, 0, key_item)

                val_item = QTableWidgetItem(str(attr_dict[key]))
                if editable_values:
                    val_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable
                                      | QtCore.Qt.ItemFlag.ItemIsEditable
                                      | QtCore.Qt.ItemFlag.ItemIsEnabled)
                else:
                    val_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable
                                      | QtCore.Qt.ItemFlag.ItemIsEnabled)
                self.tableAttributes.setItem(num, 1, val_item)

        self.tableAttributes.blockSignals(False)

    def update_attrs_to_file(self, item):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        group_path = h5io.getPathFromTreeItem(
            self.groupTree.selectedItems()[0])

        attr_key = self.tableAttributes.item(item.row(), 0).text()
        attr_val = item.text()

        # update attr in file
        h5io.changeAttribute(file_path=file_path,
                             group_path=group_path,
                             attr_key=attr_key,
                             attr_val=attr_val)
        print('Changed attr {} to = {}'.format(attr_key, attr_val))

# %% # # # # # # # # ROI SELECTOR WIDGET # # # # # # # # # # # # # # # # # # #

    def refreshLassoWidget(self, keep_paths=False):
        self.roi_ax.clear()
        init_lasso = False
        if self.roi_image is not None:
            if len(self.roi_mask) > 0:
                newImage = plot_tools.overlayImage(
                    self.roi_image[:, :, self.current_z_slice],
                    self.roi_mask,
                    0.5,
                    self.colors,
                    z=self.current_z_slice)
            else:
                newImage = self.roi_image[:, :, self.current_z_slice]
            self.roi_ax.imshow(newImage, cmap=cm.gray)
            init_lasso = True
        else:
            self.roi_ax.imshow(self.blank_image)
        self.roi_ax.set_axis_off()

        self.roi_canvas.draw()

        if not keep_paths:
            self.roi_path_list = []

        if init_lasso:
            if self.roi_type == 'circle':
                self.lasso_1 = EllipseSelector(self.roi_ax,
                                               onselect=self.newEllipse,
                                               button=1)
            elif self.roi_type == 'freehand':
                self.lasso_1 = LassoSelector(self.roi_ax,
                                             onselect=self.newFreehand,
                                             button=1)
                self.lasso_2 = LassoSelector(self.roi_ax,
                                             onselect=self.appendFreehand,
                                             button=3)
            else:
                print(
                    'Warning ROI type not recognized. Choose circle or freehand'
                )

    def newFreehand(self, verts):
        new_roi_path = path.Path(verts)
        new_roi_path.z_level = self.zSlider.value()
        new_roi_path.channel = self.current_channel
        self.updateRoiSelection([new_roi_path])

    def appendFreehand(self, verts):
        print('Appending rois, hit Enter/Return to finish')
        new_roi_path = path.Path(verts)
        new_roi_path.z_level = self.zSlider.value()
        new_roi_path.channel = self.current_channel
        self.roi_path_list.append(new_roi_path)

    def keyPressEvent(self, event):
        if type(event) == QtGui.QKeyEvent:
            if np.any([
                    event.key() == QtCore.Qt.Key.Key_Return,
                    event.key() == QtCore.Qt.Key.Key_Enter
            ]):
                if len(self.roi_path_list) > 0:
                    event.accept()
                    self.updateRoiSelection(self.roi_path_list)
                else:
                    event.ignore()
            else:
                event.ignore()
        else:
            event.ignore()

    def newEllipse(self, pos1, pos2, definedRadius=None):
        x1 = np.round(pos1.xdata)
        x2 = np.round(pos2.xdata)
        y1 = np.round(pos1.ydata)
        y2 = np.round(pos2.ydata)

        radiusX = np.sqrt((x1 - x2)**2) / 2
        radiusY = np.sqrt((y1 - y2)**2) / 2
        if self.roi_radius is not None:
            radiusX = self.roi_radius

        center = (np.round((x1 + x2) / 2), np.round((y1 + y2) / 2))
        new_roi_path = path.Path.circle(center=center, radius=radiusX)
        new_roi_path.z_level = self.zSlider.value()
        new_roi_path.channel = self.current_channel
        self.updateRoiSelection([new_roi_path])

    def updateRoiSelection(self, new_roi_path):
        mask = self.plugin.getRoiMaskFromPath(new_roi_path)
        new_roi_resp = self.plugin.getRoiDataFromPath(roi_path=new_roi_path)
        if mask.sum() == 0:
            print('No pixels in the roi you just drew')
            return
        # update list of roi data
        self.roi_mask.append(mask)
        self.roi_path.append(new_roi_path)  # list of lists of paths
        self.roi_response.append(new_roi_resp)
        # update slider to show most recently drawn roi response
        self.current_roi_index = len(self.roi_response) - 1
        self.roiSlider.setValue(self.current_roi_index)

        # Update figures
        self.redrawRoiTraces()

    def sliderUpdated(self):
        self.current_roi_index = self.roiSlider.value()
        self.redrawRoiTraces()

    def zSliderUpdated(self):
        self.current_z_slice = self.zSlider.value()
        if self.roi_image is not None:
            self.refreshLassoWidget(keep_paths=True)

    def redrawRoiTraces(self):
        self.clearRoiArtists()
        if self.current_roi_index < len(self.roi_response):
            current_raw_trace = np.squeeze(
                self.roi_response[self.current_roi_index])
            fxn_name = self.RoiResponseTypeComboBox.currentText()
            display_trace = getattr(self.plugin,
                                    'getRoiResponse_{}'.format(fxn_name))(
                                        [current_raw_trace])
            self.responsePlot.plot(display_trace,
                                   color=self.colors[self.current_roi_index],
                                   linewidth=1,
                                   alpha=0.5)
            self.responsePlot.set_xlim([0, len(display_trace)])
            y_min = np.nanmin(display_trace)
            y_max = np.nanmax(display_trace)
            self.responsePlot.set_ylim([y_min, y_max])
        self.responseCanvas.draw()

        self.refreshLassoWidget(keep_paths=False)


# %% # # # # # # # # LOADING / SAVING / COMPUTING ROIS # # # # # # # # # # # # # # # # # # #

    def loadRois(self, roi_set_path):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        self.roi_response, self.roi_image, self.roi_path, self.roi_mask = self.plugin.loadRoiSet(
            file_path, roi_set_path)
        self.zSlider.setValue(0)
        self.zSlider.setMaximum(self.roi_image.shape[2] - 1)

    def saveRois(self):
        file_path = os.path.join(self.experiment_file_directory,
                                 self.experiment_file_name + '.hdf5')
        roi_set_name = self.le_roiSetName.text()
        if roi_set_name in h5io.getAvailableRoiSetNames(
                file_path, self.series_number):
            buttonReply = QMessageBox.question(
                self, 'Overwrite roi set',
                "Are you sure you want to overwrite roi set: {}?".format(
                    roi_set_name),
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                QMessageBox.StandardButton.No)
            if buttonReply == QMessageBox.StandardButton.Yes:
                self.plugin.saveRoiSet(file_path,
                                       series_number=self.series_number,
                                       roi_set_name=roi_set_name,
                                       roi_mask=self.roi_mask,
                                       roi_response=self.roi_response,
                                       roi_image=self.roi_image,
                                       roi_path=self.roi_path)
                print('Saved roi set {} to series {}'.format(
                    roi_set_name, self.series_number))
                self.populateGroups()
                self.updateExistingRoiSetList()
            else:
                print('Overwrite aborted - pick a unique roi set name')
        else:
            self.plugin.saveRoiSet(file_path,
                                   series_number=self.series_number,
                                   roi_set_name=roi_set_name,
                                   roi_mask=self.roi_mask,
                                   roi_response=self.roi_response,
                                   roi_image=self.roi_image,
                                   roi_path=self.roi_path)
            print('Saved roi set {} to series {}'.format(
                roi_set_name, self.series_number))
            self.populateGroups()
            self.updateExistingRoiSetList()

    def deleteRoi(self):
        if self.current_roi_index < len(self.roi_response):
            self.roi_mask.pop(self.current_roi_index)
            self.roi_response.pop(self.current_roi_index)
            self.roi_path.pop(self.current_roi_index)
            self.roiSlider.setValue(self.current_roi_index - 1)
            self.redrawRoiTraces()

    def clearRois(self):
        self.roi_mask = []
        self.roi_response = []
        self.roi_path = []
        self.roi_image = None
        self.clearRoiArtists()
        self.redrawRoiTraces()
        self.roi_ax.clear()

    def clearRoiArtists(self):
        for artist in self.responsePlot.lines + self.responsePlot.collections:
            artist.remove()

    def selectRoiType(self):
        self.roi_type = self.RoiTypeComboBox.currentText().split(':')[0]
        if 'circle' in self.RoiTypeComboBox.currentText():
            self.roi_radius = int(
                self.RoiTypeComboBox.currentText().split(':')[1])
        else:
            self.roi_radius = None
        self.redrawRoiTraces()

    def selectChannel(self):
        self.current_channel = int(self.ChannelComboBox.currentText())

        # show roi image
        if self.series_number is not None:
            if self.data_directory is not None:  # user has selected a raw data directory
                self.plugin.updateImageSeries(
                    data_directory=self.data_directory,
                    image_file_name=self.image_file_name,
                    series_number=self.series_number,
                    channel=self.current_channel)
                self.roi_image = self.plugin.mean_brain
                self.zSlider.setValue(0)
                self.zSlider.setMaximum(self.roi_image.shape[2] - 1)
                self.redrawRoiTraces()
            else:
                print('Select a data directory before drawing rois')