class CueGeneralSettings(CueSettingsPage):
    Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Cue')

    def __init__(self, cue_class, **kwargs):
        super().__init__(cue_class, **kwargs)
        self.setLayout(QVBoxLayout())

        self.tabWidget = QTabWidget(self)
        self.layout().addWidget(self.tabWidget)

        # TAB 1 (Behaviours)
        self.tab_1 = QWidget(self.tabWidget)
        self.tab_1.setLayout(QVBoxLayout())
        self.tabWidget.addTab(self.tab_1, '1')

        # Start-Action
        self.startActionGroup = QGroupBox(self.tab_1)
        self.startActionGroup.setLayout(QHBoxLayout())
        self.tab_1.layout().addWidget(self.startActionGroup)

        self.startActionCombo = QComboBox(self.startActionGroup)
        for action in [CueAction.Start, CueAction.FadeInStart]:
            if action in cue_class.CueActions:
                self.startActionCombo.addItem(
                    translate('CueAction', action.name), action.value)
        self.startActionCombo.setEnabled(self.startActionCombo.count() > 1)
        self.startActionGroup.layout().addWidget(self.startActionCombo)

        self.startActionLabel = QLabel(self.startActionGroup)
        self.startActionLabel.setAlignment(Qt.AlignCenter)
        self.startActionGroup.layout().addWidget(self.startActionLabel)

        # Stop-Action
        self.stopActionGroup = QGroupBox(self.tab_1)
        self.stopActionGroup.setLayout(QHBoxLayout())
        self.tab_1.layout().addWidget(self.stopActionGroup)

        self.stopActionCombo = QComboBox(self.stopActionGroup)
        for action in [
                CueAction.Stop, CueAction.Pause, CueAction.FadeOutStop,
                CueAction.FadeOutPause
        ]:
            if action in cue_class.CueActions:
                self.stopActionCombo.addItem(
                    translate('CueAction', action.name), action.value)
        self.stopActionCombo.setEnabled(self.stopActionCombo.count() > 1)
        self.stopActionGroup.layout().addWidget(self.stopActionCombo)

        self.stopActionLabel = QLabel(self.stopActionGroup)
        self.stopActionLabel.setAlignment(Qt.AlignCenter)
        self.stopActionGroup.layout().addWidget(self.stopActionLabel)

        self.tab_1.layout().addSpacing(150)
        self.tab_1.setEnabled(self.stopActionCombo.isEnabled()
                              and self.startActionCombo.isEnabled())

        # TAB 2 (Pre/Post Wait)
        self.tab_2 = QWidget(self.tabWidget)
        self.tab_2.setLayout(QVBoxLayout())
        self.tabWidget.addTab(self.tab_2, '2')

        # Pre wait
        self.preWaitGroup = QGroupBox(self.tab_2)
        self.preWaitGroup.setLayout(QHBoxLayout())
        self.tab_2.layout().addWidget(self.preWaitGroup)

        self.preWaitSpin = QDoubleSpinBox(self.preWaitGroup)
        self.preWaitSpin.setMaximum(3600 * 24)
        self.preWaitGroup.layout().addWidget(self.preWaitSpin)

        self.preWaitLabel = QLabel(self.preWaitGroup)
        self.preWaitLabel.setAlignment(Qt.AlignCenter)
        self.preWaitGroup.layout().addWidget(self.preWaitLabel)

        # Post wait
        self.postWaitGroup = QGroupBox(self.tab_2)
        self.postWaitGroup.setLayout(QHBoxLayout())
        self.tab_2.layout().addWidget(self.postWaitGroup)

        self.postWaitSpin = QDoubleSpinBox(self.postWaitGroup)
        self.postWaitSpin.setMaximum(3600 * 24)
        self.postWaitGroup.layout().addWidget(self.postWaitSpin)

        self.postWaitLabel = QLabel(self.postWaitGroup)
        self.postWaitLabel.setAlignment(Qt.AlignCenter)
        self.postWaitGroup.layout().addWidget(self.postWaitLabel)

        # Next action
        self.nextActionGroup = QGroupBox(self.tab_2)
        self.nextActionGroup.setLayout(QHBoxLayout())
        self.tab_2.layout().addWidget(self.nextActionGroup)

        self.nextActionCombo = QComboBox(self.nextActionGroup)
        for action in CueNextAction:
            self.nextActionCombo.addItem(
                translate('CueNextAction', action.name), action.value)
        self.nextActionGroup.layout().addWidget(self.nextActionCombo)

        # TAB 3 (Fade In/Out)
        self.tab_3 = QWidget(self.tabWidget)
        self.tab_3.setLayout(QVBoxLayout())
        self.tabWidget.addTab(self.tab_3, '3')

        # FadeIn
        self.fadeInGroup = QGroupBox(self.tab_3)
        self.fadeInGroup.setEnabled(
            CueAction.FadeInStart in cue_class.CueActions)
        self.fadeInGroup.setLayout(QHBoxLayout())
        self.tab_3.layout().addWidget(self.fadeInGroup)

        self.fadeInEdit = FadeEdit(self.fadeInGroup,
                                   mode=FadeComboBox.Mode.FadeIn)
        self.fadeInGroup.layout().addWidget(self.fadeInEdit)

        # FadeOut
        self.fadeOutGroup = QGroupBox(self.tab_3)
        self.fadeOutGroup.setEnabled(
            CueAction.FadeOutPause in cue_class.CueActions
            or CueAction.FadeOutStop in cue_class.CueActions)
        self.fadeOutGroup.setLayout(QHBoxLayout())
        self.tab_3.layout().addWidget(self.fadeOutGroup)

        self.fadeOutEdit = FadeEdit(self.fadeOutGroup,
                                    mode=FadeComboBox.Mode.FadeOut)
        self.fadeOutGroup.layout().addWidget(self.fadeOutEdit)

        self.retranslateUi()

    def retranslateUi(self):
        # Tabs
        self.tabWidget.setTabText(0, translate('CueSettings', 'Behaviours'))
        self.tabWidget.setTabText(1, translate('CueSettings', 'Pre/Post Wait'))
        self.tabWidget.setTabText(2, translate('CueSettings', 'Fade In/Out'))

        # Start-Action
        self.startActionGroup.setTitle(translate('CueSettings',
                                                 'Start action'))
        self.startActionLabel.setText(
            translate('CueSettings', 'Default action to start the cue'))
        # Stop-Action
        self.stopActionGroup.setTitle(translate('CueSettings', 'Stop action'))
        self.stopActionLabel.setText(
            translate('CueSettings', 'Default action to stop the cue'))

        # PreWait
        self.preWaitGroup.setTitle(translate('CueSettings', 'Pre wait'))
        self.preWaitLabel.setText(
            translate('CueSettings', 'Wait before cue execution'))
        # PostWait
        self.postWaitGroup.setTitle(translate('CueSettings', 'Post wait'))
        self.postWaitLabel.setText(
            translate('CueSettings', 'Wait after cue execution'))
        # NextAction
        self.nextActionGroup.setTitle(translate('CueSettings', 'Next action'))

        # FadeIn/Out
        self.fadeInGroup.setTitle(translate('FadeSettings', 'Fade In'))
        self.fadeOutGroup.setTitle(translate('FadeSettings', 'Fade Out'))

    def load_settings(self, settings):
        self.startActionCombo.setCurrentText(
            translate('CueAction', settings.get('default_start_action', '')))
        self.stopActionCombo.setCurrentText(
            translate('CueAction', settings.get('default_stop_action', '')))

        self.preWaitSpin.setValue(settings.get('pre_wait', 0))
        self.postWaitSpin.setValue(settings.get('post_wait', 0))
        self.nextActionCombo.setCurrentText(
            translate('CueNextAction', settings.get('next_action', '')))

        self.fadeInEdit.setFadeType(settings.get('fadein_type', ''))
        self.fadeInEdit.setDuration(settings.get('fadein_duration', 0))
        self.fadeOutEdit.setFadeType(settings.get('fadeout_type', ''))
        self.fadeOutEdit.setDuration(settings.get('fadeout_duration', 0))

    def enable_check(self, enable):
        self.startActionGroup.setCheckable(enable)
        self.startActionGroup.setChecked(False)
        self.stopActionGroup.setCheckable(enable)
        self.stopActionGroup.setChecked(False)

        self.preWaitGroup.setCheckable(enable)
        self.preWaitGroup.setChecked(False)
        self.postWaitGroup.setCheckable(enable)
        self.postWaitGroup.setChecked(False)
        self.nextActionGroup.setCheckable(enable)
        self.nextActionGroup.setChecked(False)

        self.fadeInGroup.setCheckable(enable)
        self.fadeInGroup.setChecked(False)
        self.fadeOutGroup.setCheckable(enable)
        self.fadeOutGroup.setChecked(False)

    def get_settings(self):
        conf = {}
        checkable = self.preWaitGroup.isCheckable()

        if not (checkable and not self.startActionGroup.isChecked()):
            if self.startActionCombo.isEnabled():
                conf[
                    'default_start_action'] = self.startActionCombo.currentData(
                    )
        if not (checkable and not self.stopActionGroup.isChecked()):
            if self.stopActionCombo.isEnabled():
                conf['default_stop_action'] = self.stopActionCombo.currentData(
                )

        if not (checkable and not self.preWaitGroup.isChecked()):
            conf['pre_wait'] = self.preWaitSpin.value()
        if not (checkable and not self.postWaitGroup.isChecked()):
            conf['post_wait'] = self.postWaitSpin.value()
        if not (checkable and not self.nextActionGroup.isChecked()):
            conf['next_action'] = self.nextActionCombo.currentData()

        if not (checkable and not self.fadeInGroup.isChecked()):
            conf['fadein_type'] = self.fadeInEdit.fadeType()
            conf['fadein_duration'] = self.fadeInEdit.duration()
        if not (checkable and not self.fadeInGroup.isChecked()):
            conf['fadeout_type'] = self.fadeOutEdit.fadeType()
            conf['fadeout_duration'] = self.fadeOutEdit.duration()

        return conf
Beispiel #2
0
class PostProcessGUI(QDialog):

    def __init__(self, parent_window=None, _is_dialog=False):
        super().__init__(parent=parent_window)
        self._is_dialog = _is_dialog
        self.initUI()

    def initUI(self):
        input_v_layout = QVBoxLayout()
        input_v_layout.setAlignment(Qt.AlignTop)
        input_v_layout.setContentsMargins(0, 0, 0, 0)
        # TODO add a set of parameters there for the post process
        self.groupBox_post_process = QGroupBox(
            'Refine segmentation/Create a binary mask', objectName='groupBox_post_process')
        self.groupBox_post_process.setCheckable(True)
        self.groupBox_post_process.setChecked(True)
        # self.groupBox_post_process.setEnabled(True)

        group_box_post_process_parameters_layout = QGridLayout()
        group_box_post_process_parameters_layout.setAlignment(Qt.AlignTop)
        group_box_post_process_parameters_layout.setHorizontalSpacing(3)
        group_box_post_process_parameters_layout.setVerticalSpacing(3)

        # do a radio dialog with all the stuff needed...
        # test all

        post_process_method_selection_label = QLabel('Post process method')  # (or bond score for pretrained model)
        post_process_method_selection_label.setStyleSheet("QLabel { color : red; }")

        self.post_process_method_selection = QComboBox(objectName='post_process_method_selection')
        self.post_process_method_selection.addItem('Default (Slow/robust) (EPySeg pre-trained model only!)')
        self.post_process_method_selection.addItem('Fast (May contain more errors) (EPySeg pre-trained model only!)')
        self.post_process_method_selection.addItem('Old method (Sometimes better than default) (EPySeg pre-trained model only!)')
        self.post_process_method_selection.addItem('Simply binarize output using threshold')
        self.post_process_method_selection.addItem('Keep first channel only')
        self.post_process_method_selection.addItem('None (Raw model output)')
        self.post_process_method_selection.currentTextChanged.connect(self._post_process_method_changed)

        group_box_post_process_parameters_layout.addWidget(post_process_method_selection_label, 0, 0, 1, 1)
        group_box_post_process_parameters_layout.addWidget(self.post_process_method_selection, 0, 1, 1, 3)

        # TODO --> always make this relative
        threshold_label = QLabel(
            'Threshold: (in case of over/under segmentation, please increase/decrease, respectively)')  # (or bond score for pretrained model)
        threshold_label.setStyleSheet("QLabel { color : red; }")
        self.threshold_bond_or_binarisation = QDoubleSpinBox(objectName='threshold_bond_or_binarisation')
        self.threshold_bond_or_binarisation.setSingleStep(0.01)
        self.threshold_bond_or_binarisation.setRange(0.01, 1)  # 100_000 makes no sense (oom) but anyway
        self.threshold_bond_or_binarisation.setValue(0.42)  # probably should be 1 to 3 depending on the tissue
        self.threshold_bond_or_binarisation.setEnabled(False)
        # threshold_hint = QLabel()  # (or bond score for pretrained model)

        self.autothreshold = QCheckBox("Auto",objectName='autothreshold')
        self.autothreshold.setChecked(True)
        self.autothreshold.stateChanged.connect(self._threshold_changed)

        group_box_post_process_parameters_layout.addWidget(threshold_label, 1, 0, 1, 2)
        group_box_post_process_parameters_layout.addWidget(self.threshold_bond_or_binarisation, 1, 2)
        group_box_post_process_parameters_layout.addWidget(self.autothreshold, 1, 3)
        # groupBox_post_process_parameters_layout.addWidget(threshold_hint, 0, 3)

        filter_by_size_label = QLabel('Further filter segmentation by size:')
        self.filter_by_cell_size_combo = QComboBox(objectName='filter_by_cell_size_combo')
        self.filter_by_cell_size_combo.addItem('None (quite often the best choice)')
        self.filter_by_cell_size_combo.addItem('Local median (slow/very good) divided by')
        self.filter_by_cell_size_combo.addItem('Cells below Average area (global) divided by')
        self.filter_by_cell_size_combo.addItem('Global median divided by')
        self.filter_by_cell_size_combo.addItem('Cells below size (in px)')

        # add a listener to model Architecture
        self.filter_by_cell_size_combo.currentTextChanged.connect(self._filter_changed)

        group_box_post_process_parameters_layout.addWidget(filter_by_size_label, 2, 0)
        group_box_post_process_parameters_layout.addWidget(self.filter_by_cell_size_combo, 2, 1, 1, 2)

        self.avg_area_division_or_size_spinbox = QSpinBox(objectName='avg_area_division_or_size_spinbox')
        self.avg_area_division_or_size_spinbox.setSingleStep(1)
        self.avg_area_division_or_size_spinbox.setRange(1, 10000000)  # 100_000 makes no sense (oom) but anyway
        self.avg_area_division_or_size_spinbox.setValue(2)  # probably should be 1 to 3 depending on the tissue
        self.avg_area_division_or_size_spinbox.setEnabled(False)
        group_box_post_process_parameters_layout.addWidget(self.avg_area_division_or_size_spinbox, 2, 3)

        self.prevent_exclusion_of_too_many_cells_together = QCheckBox('Do not exclude groups bigger than', objectName='prevent_exclusion_of_too_many_cells_together')
        self.prevent_exclusion_of_too_many_cells_together.setChecked(False)
        self.prevent_exclusion_of_too_many_cells_together.setEnabled(False)

        # max_nb_of_cells_to_be_excluded_together_label = QLabel('Group size')
        self.max_nb_of_cells_to_be_excluded_together_spinbox = QSpinBox(objectName='max_nb_of_cells_to_be_excluded_together_spinbox')
        self.max_nb_of_cells_to_be_excluded_together_spinbox.setSingleStep(1)
        self.max_nb_of_cells_to_be_excluded_together_spinbox.setRange(1, 10000000)  # max makes no sense
        self.max_nb_of_cells_to_be_excluded_together_spinbox.setValue(
            3)  # default should be 2 or 3 because seg is quite good so above makes no sense
        self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(False)
        cells_text_labels = QLabel('cells')

        self.restore_secure_cells = QCheckBox('Restore most likely cells',objectName='restore_secure_cells')
        self.restore_secure_cells.setChecked(False)
        self.restore_secure_cells.setEnabled(False)

        # help for post process
        # help_ico = QIcon.fromTheme('help-contents')
        self.help_button_postproc = QPushButton('?', None)
        bt_width = self.help_button_postproc.fontMetrics().boundingRect(self.help_button_postproc.text()).width() + 7
        self.help_button_postproc.setMaximumWidth(bt_width * 2)
        self.help_button_postproc.clicked.connect(self.show_tip)

        group_box_post_process_parameters_layout.addWidget(self.restore_secure_cells, 3, 0)
        group_box_post_process_parameters_layout.addWidget(self.prevent_exclusion_of_too_many_cells_together, 3, 1)
        group_box_post_process_parameters_layout.addWidget(self.max_nb_of_cells_to_be_excluded_together_spinbox, 3, 2)
        group_box_post_process_parameters_layout.addWidget(cells_text_labels, 3, 3)

        # TODO --> improve layout to make help button smaller
        group_box_post_process_parameters_layout.addWidget(self.help_button_postproc, 0, 5, 3, 1)

        self.groupBox_post_process.setLayout(group_box_post_process_parameters_layout)
        input_v_layout.addWidget(self.groupBox_post_process)
        self.setLayout(input_v_layout)

        if self._is_dialog:
            # OK and Cancel buttons
            self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
                                            QtCore.Qt.Horizontal, self)
            self.buttons.accepted.connect(self.accept)
            self.buttons.rejected.connect(self.reject)
            self.layout().addWidget(self.buttons)

    def _threshold_changed(self):
        self.threshold_bond_or_binarisation.setEnabled(not self.autothreshold.isChecked())

    # self.post_process_method_selection.addItem('Default (Slow/robust)')
    # self.post_process_method_selection.addItem('Fast (May contain more errors)')
    # self.post_process_method_selection.addItem('Old method (Less constant than default but sometimes better)')
    # self.post_process_method_selection.addItem('Simply binarize output using threshold')
    # self.post_process_method_selection.addItem('None (Raw model output)')

    def _post_process_method_changed(self):
        text = self.post_process_method_selection.currentText().lower()
        if 'none' in text or 'first' in text:
            self.set_threshold_enabled(False)
            self.set_safety_parameters(False)
            self.set_filter_by_size_enabled(False)
        elif 'simply' in text:
            self.set_threshold_enabled(True)
            self.set_safety_parameters(False)
            self.set_filter_by_size_enabled(False)
        elif 'old' in text:
            self.set_threshold_enabled(False)
            self.set_safety_parameters(True)
            self.set_filter_by_size_enabled(True)
        else:
            self.set_threshold_enabled(True)
            self.set_safety_parameters(False)
            self.set_filter_by_size_enabled(True)

    def set_filter_by_size_enabled(self, bool):
        if bool is False:
            self.filter_by_cell_size_combo.setEnabled(False)
            self.avg_area_division_or_size_spinbox.setEnabled(False)
        else:
            self.filter_by_cell_size_combo.setEnabled(True)
            self.avg_area_division_or_size_spinbox.setEnabled(True)

    def set_threshold_enabled(self, bool):
        if bool is False:
            self.autothreshold.setEnabled(False)
            self.threshold_bond_or_binarisation.setEnabled(False)
        else:
            self.autothreshold.setEnabled(True)
            self._threshold_changed()

    def set_safety_parameters(self, bool):
        self._filter_changed()

    def show_tip(self):
        QToolTip.showText(self.sender().mapToGlobal(QPoint(30, 30)), markdown_file_to_html('refine_segmentation.md'))

    def isChecked(self):
        return self.groupBox_post_process.isChecked()

    def setChecked(self, bool):
        return self.groupBox_post_process.setChecked(bool)

    def _filter_changed(self):
        current_filter = self.filter_by_cell_size_combo.currentText().lower()
        current_mode = self.post_process_method_selection.currentText().lower()
        if 'one' in current_filter:
            self.avg_area_division_or_size_spinbox.setEnabled(False)
            self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(False)
            self.prevent_exclusion_of_too_many_cells_together.setEnabled(False)
            self.restore_secure_cells.setEnabled(False)
        else:
            self.avg_area_division_or_size_spinbox.setEnabled(True)
            self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(True)
            self.prevent_exclusion_of_too_many_cells_together.setEnabled(True)
            self.restore_secure_cells.setEnabled(True)
            if 'divided' in current_filter:
                self.avg_area_division_or_size_spinbox.setValue(2)
            else:
                self.avg_area_division_or_size_spinbox.setValue(300)
            if not 'old' in current_mode:
                self.max_nb_of_cells_to_be_excluded_together_spinbox.setEnabled(False)
                self.prevent_exclusion_of_too_many_cells_together.setEnabled(False)
                self.restore_secure_cells.setEnabled(False)

    def _get_post_process_filter(self):
        current_filter = self.filter_by_cell_size_combo.currentText().lower()
        if 'one' in current_filter or not self.filter_by_cell_size_combo.isEnabled():
            return None
        if 'size' in current_filter:
            return self.avg_area_division_or_size_spinbox.value()
        if 'verage' in current_filter:
            return 'avg'
        if 'local' in current_filter:
            return 'local'
        if 'global' in current_filter:
            return 'global median'

    def get_parameters_directly(self):
        '''Get the parameters for model training

            Returns
            -------
            dict
                containing post processing parameters

            '''

        self.post_process_parameters = {}
        post_proc_method = self.post_process_method_selection.currentText().lower()
        if 'none' in post_proc_method:
            self.post_process_parameters['post_process_algorithm'] = None
        else:
            self.post_process_parameters['post_process_algorithm'] = post_proc_method
        self.post_process_parameters['filter'] = self._get_post_process_filter()
        if self.threshold_bond_or_binarisation.isEnabled():
            self.post_process_parameters['threshold'] = self.threshold_bond_or_binarisation.value()
        if self.autothreshold.isEnabled() and self.autothreshold.isChecked():
            self.post_process_parameters[
                'threshold'] = None  # None means autothrehsold # maybe add more options some day
        if self.avg_area_division_or_size_spinbox.isEnabled():
            self.post_process_parameters['correction_factor'] = self.avg_area_division_or_size_spinbox.value()
        if self.restore_secure_cells.isEnabled():
            self.post_process_parameters['restore_safe_cells'] = self.restore_secure_cells.isChecked()
        if self.max_nb_of_cells_to_be_excluded_together_spinbox.isEnabled():
            self.post_process_parameters[
                'cutoff_cell_fusion'] = self.max_nb_of_cells_to_be_excluded_together_spinbox.value() if self.prevent_exclusion_of_too_many_cells_together.isChecked() else None

        if 'old' in self.post_process_method_selection.currentText().lower():
            # just for max use that --> maybe do this as an option some day
            self.post_process_parameters['hq_predictions'] = 'max'

        return self.post_process_parameters

    def get_parameters(self):
        return (self.get_parameters_directly())

    @staticmethod
    def getDataAndParameters(parent_window=None, _is_dialog=False):
        # get all the params for augmentation
        dialog = PostProcessGUI(parent_window=parent_window, _is_dialog=_is_dialog)
        result = dialog.exec_()
        parameters = dialog.get_parameters()
        return (parameters, result == QDialog.Accepted)
Beispiel #3
0
class PictureEditor(QFrame):
    picture_data: Picture
    pic_path: str='./'

    def __init__(self, parent, picture_parameter: dict):
        QFrame.__init__(self, parent, flags=Qt.FramelessWindowHint)
        self.buffer = parent.buffer
        self.picture_parameter = picture_parameter

        self.picture_label = QLabel()
        self.resize_multiple = 1

        scroll = QScrollArea(self)
        scroll.setWidget(self.picture_label)
        scroll.setObjectName('picture')

        self.name_combo = QComboBox(self)
        self.index_combo = QComboBox(self)
        self.palette_combo = QComboBox(self)
        self.resize_combo = QComboBox(self)

        self.name_combo.setFixedWidth(120)
        self.index_combo.setFixedWidth(120)
        self.palette_combo.setFixedWidth(120)
        self.resize_combo.setFixedWidth(120)

        self.name_combo.setItemDelegate(QStyledItemDelegate())
        self.index_combo.setItemDelegate(QStyledItemDelegate())
        self.palette_combo.setItemDelegate(QStyledItemDelegate())
        self.resize_combo.setItemDelegate(QStyledItemDelegate())

        self.resize_combo.addItems([f'          × {i + 1}' for i in range(4)])

        self.name_combo.currentTextChanged[str].connect(self.name_change)
        self.index_combo.currentIndexChanged.connect(self.refresh_data)
        self.palette_combo.currentIndexChanged.connect(self.refresh_data)
        self.resize_combo.currentIndexChanged.connect(self.refresh_data)

        self.name_combo.addItems(picture_parameter)

        output_button = QPushButton('導出圖片')
        input_button = QPushButton('導入圖片')
        output_button.clicked.connect(self.output_picture)
        input_button.clicked.connect(self.input_picture)

        control_layout = QVBoxLayout()
        control_layout.addWidget(QLabel('選擇圖片'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.name_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(QLabel('選擇編號'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.index_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(QLabel('選擇色板'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.palette_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(QLabel('缩放比例'), alignment=Qt.AlignLeft)
        control_layout.addWidget(self.resize_combo, alignment=Qt.AlignRight)
        control_layout.addWidget(output_button, alignment=Qt.AlignRight)
        control_layout.addWidget(input_button, alignment=Qt.AlignRight)

        control_layout.addStretch()

        layout = QHBoxLayout()
        layout.addLayout(control_layout)
        layout.addWidget(scroll)

        self.setLayout(layout)

    def name_change(self, name: str):
        picture_setting = DATA_PARAMETER.get('圖片_' + name)
        self.index_combo.disconnect()
        self.index_combo.clear()
        self.index_combo.addItems([f'{i+1:>13d}' for i in range(picture_setting['quantity']['normal_quantity'])])
        self.index_combo.currentIndexChanged.connect(self.refresh_data)

        if self.picture_parameter.get(name):
            palette_setting = PALETTE_PARAMETER.get('色板_' + self.picture_parameter.get(name))
        else:
            palette_setting = ()
        self.palette_combo.disconnect()
        self.palette_combo.clear()
        if palette_setting:
            self.palette_combo.addItems([f'0x{address:08X}' for address in palette_setting])
            self.palette_combo.setEnabled(True)
        else:
            self.palette_combo.setEnabled(False)
        self.palette_combo.currentIndexChanged.connect(self.refresh_data)
        self.refresh_data()

    def refresh_data(self):
        picture_setting = DATA_PARAMETER.get('圖片_' + self.name_combo.currentText())
        self.picture_data = Picture(self, **picture_setting)
        if self.palette_combo.isEnabled():
            self.picture_data.palette.set_normal_offset(int(self.palette_combo.currentText(), 16))
        self.resize_multiple = self.resize_combo.currentIndex() + 1
        self.set_picture()

    def set_picture(self):
        picture = self.picture_data.get_data(self.index_combo.currentIndex())
        width = self.picture_data.width * self.resize_multiple
        height = self.picture_data.height * self.resize_multiple
        self.picture_label.setFixedSize(width, height)
        self.picture_label.setPixmap(picture.resize((width, height)).toqpixmap())

    def output_picture(self):
        filename = QFileDialog.getSaveFileName(self, '导出图片', self.pic_path, 'BMP图像(*.bmp)')[0]
        if filename:
            self.pic_path = filename[0: filename.rfind('/') + 1]
            if self.index_combo.isEnabled():
                picture = self.picture_data.get_data(self.index_combo.currentIndex())
            else:
                picture = self.picture_data.get_data(0)
            picture.save(filename)

    def input_picture(self):
        filename = QFileDialog.getOpenFileName(self, '导入图片', self.pic_path, '*.bmp;;*.png;;*.gif;;*.tif')[0]
        if filename:
            self.pic_path = filename[0: filename.rfind('/') + 1]
            width = self.picture_data.width * self.resize_multiple
            height = self.picture_data.height * self.resize_multiple
            picture = Image.open(filename).resize((width, height))
            self.picture_data.set_data(self.index_combo.currentIndex(), picture)
            self.set_picture()
class finance_edit(QWidget):
    def __init__(self, home_button: QPushButton, parent=None):
        super().__init__(parent)

        self.presetting = False

        intValidator = QIntValidator()
        self.setStyleSheet(QSS)
        self.current_pending_months = []
        self.current_advance_months = []

        self.home_button = home_button
        self.save_button = None

        # -- LEVEL TWO GRID
        self.grid = QGridLayout()
        self.grid.setSpacing(20)
        self.setLayout(self.grid)

        # -- CELL ZERO
        self.receipt_label = QLabel("RECEIPT ID :")
        self.receipt_label.setWordWrap(True)

        self.receipt_id = QLineEdit()
        self.receipt_id.setStyleSheet("font: bold")
        self.receipt_id.setAlignment(Qt.AlignCenter)
        self.receipt_id.setToolTip("Enter a valid Receipt-ID")

        self.print_button = QPushButton("PRINT")
        self.print_button.setFixedWidth(122)
        self.print_button.setToolTip("Get Receipt PDF")

        self.resend_button = QPushButton("RESEND")
        self.resend_button.setToolTip("Send a copy via mail to the member")

        self.edit_button = QPushButton("EDIT")
        self.edit_button.setToolTip("Edit the details of this record")

        self.buttons_layout = QHBoxLayout()
        self.buttons_layout.addWidget(self.resend_button)
        self.buttons_layout.addWidget(self.edit_button)
        self.buttons_layout.setSpacing(5)

        # ---
        self.flat_label = QLabel("FLAT NO. :")
        self.flat_combo = QComboBox()
        model = self.flat_combo.model()

        for flat in flats:
            model.appendRow(QStandardItem(flat))

        self.flat_combo.setStyleSheet(
            'text-color: black; selection-background-color: rgb(215,215,215)')

        self.flat_combo.currentIndexChanged['QString'].connect(self.set_name)

        # ---
        self.name_label = QLabel("NAME :")
        self.name_value = QLabel()
        self.name_value.setFixedWidth(200)
        self.name_value.setText("Mr D. S. Patil")

        # ---
        self.finance_entry_layout0 = QFormLayout()
        self.finance_entry_layout0.addRow(self.receipt_label, self.receipt_id)
        self.finance_entry_layout0.addRow(self.print_button,
                                          self.buttons_layout)
        self.finance_entry_layout0.addRow(self.flat_label, self.flat_combo)
        self.finance_entry_layout0.addRow(self.name_label, self.name_value)
        self.finance_entry_layout0.setVerticalSpacing(90)

        # -- CELL ONE
        self.date_label = QLabel("DATE OF TRANSACTION :")
        self.date_label.setWordWrap(True)

        self.date_line = QDateEdit()
        self.date_line.setCalendarPopup(True)
        self.date_line.setDate(QDate.currentDate())
        self.date_line.setDisplayFormat("dd MMMM, yyyy")

        self.single_radio = QRadioButton("Single Month")
        self.multiple_radio = QRadioButton("Multiple Months")
        self.single_radio.setChecked(True)

        self.single_radio.toggled.connect(
            lambda: self.months(button=self.single_radio))
        self.multiple_radio.toggled.connect(
            lambda: self.months(button=self.multiple_radio))

        self.finance_entry_layout1_h1 = QHBoxLayout()
        self.finance_entry_layout1_h1.addWidget(self.date_line)
        self.finance_entry_layout1_h1.addWidget(self.single_radio)
        self.finance_entry_layout1_h1.addWidget(self.multiple_radio)
        self.finance_entry_layout1_h1.setSpacing(90)

        # ---
        self.month_label = QLabel("FEES FOR :")
        self.month_combo = QComboBox()
        self.set_pending_months()

        self.month_combo.setStyleSheet(
            'text-color: black; selection-background-color: rgb(215,215,215)')

        # ---
        self.month_till_label = QLabel("FEES TILL :")
        self.month_till_label.setAlignment(Qt.AlignCenter)
        self.month_till_combo = QComboBox()

        self.set_advance_months()

        self.month_till_combo.setStyleSheet(
            'text-color: black; selection-background-color: rgb(215,215,215)')

        self.finance_entry_layout1_h2 = QHBoxLayout()
        self.finance_entry_layout1_h2.addWidget(self.month_combo)
        self.finance_entry_layout1_h2.addWidget(self.month_till_label)
        self.finance_entry_layout1_h2.addWidget(self.month_till_combo)
        self.finance_entry_layout1_h2.setSpacing(90)

        self.month_till_label.setEnabled(False)
        self.month_till_combo.setEnabled(False)

        # ---
        self.amount_label = QLabel("AMOUNT :")
        self.amount_label.setAlignment(Qt.AlignCenter)
        self.amount_line = QLineEdit()
        self.amount_line.setText("1500")

        self.amount_line.setValidator(intValidator)

        # ---
        self.fine_label = QLabel("FINE :")
        self.fine_label.setAlignment(Qt.AlignCenter)
        self.fine_line = QLineEdit()
        self.fine_line.setText("0")

        self.fine_line.setValidator(intValidator)
        self.fine_line.setStyleSheet("border: 1px solid red; color: red")

        self.finance_entry_layout1_h3 = QHBoxLayout()
        self.finance_entry_layout1_h3.addWidget(self.amount_line)
        self.finance_entry_layout1_h3.addWidget(self.fine_label)
        self.finance_entry_layout1_h3.addWidget(self.fine_line)
        self.finance_entry_layout1_h3.setSpacing(90)

        # ---
        self.finance_entry_layout1 = QFormLayout()
        self.finance_entry_layout1.addRow(self.date_label,
                                          self.finance_entry_layout1_h1)
        self.finance_entry_layout1.addRow(self.month_label,
                                          self.finance_entry_layout1_h2)
        self.finance_entry_layout1.addRow(self.amount_label,
                                          self.finance_entry_layout1_h3)
        self.finance_entry_layout1.setVerticalSpacing(90)

        # -- CELL TWO
        self.mode_label = QLabel("PAYMENT MODE :")
        self.mode_label.setWordWrap(True)

        # ---
        self.mode_combo = QComboBox()
        self.mode_combo.addItem("Cash")
        self.mode_combo.addItem("Online Funds Transfer")
        self.mode_combo.addItem("Cheque")

        self.mode_combo.setStyleSheet(
            'text-color: black; selection-background-color: rgb(215,215,215)')
        self.mode_combo.currentIndexChanged['QString'].connect(
            self.mode_selection)

        # ---
        self.ref_label = QLabel("REFERENCE ID :")
        self.ref_label.setWordWrap(True)
        self.ref_line = QLineEdit()

        self.ref_label.setDisabled(True)
        self.ref_line.setDisabled(True)

        self.total_label = QLabel(
            f"TOTAL PAYABLE AMOUNT : {int(self.amount_line.text()) + int(self.fine_line.text())}"
        )
        self.total_label.setWordWrap(True)
        self.total_label.setAlignment(Qt.AlignCenter)

        self.save_button = QPushButton("UPDATE AND RESEND RECEIPT")
        self.save_button.setStyleSheet("font: bold")
        self.save_button.clicked.connect(lambda: self.check_form())

        self.status = QLabel("Ready")
        self.status.setStyleSheet("font: 8pt")
        self.status.setAlignment(Qt.AlignCenter)

        self.bar = QProgressBar(self)
        self.bar.setValue(0)
        self.bar.setMaximum(100)
        self.bar.setToolTip("Please wait until the process is completed.")

        # ---
        self.finance_entry_layout2 = QFormLayout()
        self.finance_entry_layout2.addRow(self.mode_label, self.mode_combo)
        self.finance_entry_layout2.addRow(self.ref_label, self.ref_line)
        self.finance_entry_layout2.addItem(create_spacer_item(w=5, h=30))
        self.finance_entry_layout2.addRow(self.total_label)
        self.finance_entry_layout2.addRow(self.save_button)
        self.finance_entry_layout2.addRow(self.status)
        self.finance_entry_layout2.addRow(self.bar)
        self.finance_entry_layout2.setVerticalSpacing(50)

        self.finance_entry_group0 = QGroupBox()
        self.finance_entry_group0.setLayout(self.finance_entry_layout0)

        self.finance_entry_group1 = QGroupBox()
        self.finance_entry_group1.setLayout(self.finance_entry_layout1)

        self.finance_entry_group2 = QGroupBox()
        self.finance_entry_group2.setLayout(self.finance_entry_layout2)
        self.finance_entry_group2.setFixedWidth(550)

        # -- FUNCTIONALITY:
        self.date_line.dateChanged.connect(lambda: self.set_pending_months(
            date=str(self.date_line.date().toPyDate())))

        self.month_combo.currentIndexChanged['QString'].connect(
            self.set_advance_months)

        self.month_combo.currentIndexChanged['QString'].connect(
            lambda ind: self.calculate_fine('from', ind))
        self.month_till_combo.currentIndexChanged['QString'].connect(
            lambda ind: self.calculate_fine('till', ind))

        self.month_combo.currentTextChanged.connect(self.calculate_amount)
        self.month_till_combo.currentTextChanged.connect(self.calculate_amount)

        self.amount_line.textChanged.connect(self.set_total)
        self.fine_line.textChanged.connect(self.set_total)

        self.edit_button.clicked.connect(self.enable_edit)
        self.print_button.clicked.connect(self.print_receipt)
        self.resend_button.clicked.connect(self.resend_receipt)

        self.receipt_id.textChanged['QString'].connect(
            lambda receipt_id: self.get_receipt(receipt_id))

        # -- FINANCE ENTRY GRID
        self.grid.addWidget(self.finance_entry_group0, 0, 0, 2, 1)
        self.grid.addWidget(self.finance_entry_group1, 0, 1, 2, 1)
        self.grid.addWidget(self.finance_entry_group2, 0, 2, 2, 1)

        self.save_button.setEnabled(False)
        self.print_button.setEnabled(False)
        self.resend_button.setEnabled(False)
        self.edit_button.setEnabled(False)
        self.flat_label.setEnabled(False)
        self.flat_combo.setEnabled(False)
        self.name_label.setEnabled(False)
        self.name_value.setEnabled(False)
        self.finance_entry_group1.setEnabled(False)
        self.finance_entry_group2.setEnabled(False)

    def get_receipt(self, receipt_id: str):
        data = db_tools.get_from_db(table='records',
                                    attribute='*',
                                    key='receipt_id',
                                    value=receipt_id)

        if len(data) > 0:

            self.presetting = True

            data = data[0]

            receipt_date = data[1].split("/")
            flat = data[2]
            fee_month = data[3]
            fee_till = data[4]
            amount = data[5]
            fine = data[6]
            mode = data[7]
            ref = data[8]

            date_to_set = QDate(int(receipt_date[2]), int(receipt_date[1]),
                                int(receipt_date[0]))

            fee_month = fee_month.split("/")
            fee_month_to_set = f"{fee_month[0]} '{fee_month[1]}"

            self.date_line.setDate(date_to_set)

            self.flat_combo.setCurrentText(flat)

            self.month_combo.setCurrentText(fee_month_to_set)

            self.amount_line.setText(f"{amount}")

            self.fine_line.setText(f"{fine}")

            self.mode_combo.setCurrentText(mode)

            self.ref_line.setText(ref)

            self.total_label.setText(f"TOTAL PAYABLE AMOUNT : {amount + fine}")

            if fee_till != "-":
                fee_till = fee_till.split("/")
                fee_till_to_set = f"{fee_till[0]} '{fee_till[1]}"

                self.multiple_radio.setChecked(True)
                self.single_radio.setChecked(False)

                self.month_till_combo.setCurrentText(fee_till_to_set)

            else:
                self.multiple_radio.setChecked(False)
                self.single_radio.setChecked(True)

            if mode == "Cash":
                self.ref_label.setEnabled(False)
                self.ref_line.setEnabled(False)

            self.presetting = False

            self.print_button.setEnabled(True)
            self.resend_button.setEnabled(True)
            self.edit_button.setEnabled(True)

        else:
            self.print_button.setEnabled(False)
            self.resend_button.setEnabled(False)
            self.edit_button.setEnabled(False)

            self.flat_label.setEnabled(False)
            self.flat_combo.setEnabled(False)
            self.name_label.setEnabled(False)
            self.name_value.setEnabled(False)
            self.finance_entry_group1.setEnabled(False)
            self.finance_entry_group2.setEnabled(False)

    def enable_edit(self):
        self.flat_label.setEnabled(True)
        self.flat_combo.setEnabled(True)
        self.name_label.setEnabled(True)
        self.name_value.setEnabled(True)
        self.finance_entry_group1.setEnabled(True)
        self.finance_entry_group2.setEnabled(True)

    def months(self, button):
        if button.isChecked():
            if button.text() == "Single Month":
                self.month_till_label.setEnabled(False)
                self.month_till_combo.setEnabled(False)

            elif button.text() == "Multiple Months":
                self.month_till_label.setEnabled(True)
                self.month_till_combo.setEnabled(True)

        self.calculate_amount()

    def mode_selection(self, selection):
        if selection == "Cash":
            self.ref_label.setText("REFERENCE ID :")
            self.ref_label.setDisabled(True)
            self.ref_line.setDisabled(True)

        elif selection == "Cheque":
            self.ref_label.setText("CHEQUE NO. :")
            self.ref_label.setDisabled(False)
            self.ref_line.setDisabled(False)

        elif selection == "Online Funds Transfer":
            self.ref_label.setText("REFERENCE ID :")
            self.ref_label.setDisabled(False)
            self.ref_line.setDisabled(False)

        if self.save_button is not None:
            self.save_button.setEnabled(True)

    def check_form(self):
        reply = QMessageBox()

        if len(self.amount_line.text()) == 0:
            reply.setIcon(QMessageBox.Warning)
            reply.setText("AMOUNTS field cannot be left empty.")
            reply.setStandardButtons(QMessageBox.Retry)
            reply.setWindowTitle("INVALID ENTRY")
            reply.exec_()

        elif len(self.fine_line.text()) == 0:
            reply.setIcon(QMessageBox.Warning)
            reply.setText("FINE field cannot be left empty.")
            reply.setStandardButtons(QMessageBox.Retry)
            reply.setWindowTitle("INVALID ENTRY")
            reply.exec_()

        elif self.ref_line.isEnabled() and len(self.ref_line.text()) == 0:
            reply.setIcon(QMessageBox.Warning)
            reply.setText(
                "Please enter the REFERENCE INFORMATION for the transaction.")
            reply.setStandardButtons(QMessageBox.Retry)
            reply.setWindowTitle("INVALID ENTRY")
            reply.exec_()

        else:
            code, ok = QInputDialog().getText(
                self, "Security", "Enter the authentication code:")

            if verify_code(code) and ok:
                print("Verified")

                if self.month_till_combo.isEnabled():
                    fee_till = f" - {self.month_till_combo.currentText()}"
                else:
                    fee_till = ''

                if self.ref_line.isEnabled():
                    ref = f" - ({self.ref_line.text()})"
                else:
                    ref = ''

                detailed_text = f"Date : {fix_date(str(self.date_line.date().toPyDate()))}\n" \
                                f"Fee for : {str(self.month_combo.currentText())}{fee_till}\n" \
                                f"Flat No : {str(self.flat_combo.currentText())}\n" \
                                f"Amount : {float(self.amount_line.text())}\n" \
                                f"Fine : {float(self.fine_line.text())}\n" \
                                f"    -> TOTAL : {float(self.amount_line.text()) + float(self.fine_line.text())} <-\n" \
                                f"Payment Mode : {str(self.mode_combo.currentText())}{ref}"

                reply.setWindowTitle("SUCCESSFUL ENTRY")
                reply.setIcon(QMessageBox.Information)
                reply.setText("ENTRY HAS BEEN RECORDED.\n")
                reply.setInformativeText("Please confirm the details below.")

                reply.setDetailedText(detailed_text)
                confirm_button = reply.addButton('Confirm',
                                                 QMessageBox.AcceptRole)
                edit_button = reply.addButton('Edit', QMessageBox.RejectRole)

                confirm_button.clicked.connect(
                    lambda: self.final_clicked(button=confirm_button))
                edit_button.clicked.connect(
                    lambda: self.final_clicked(button=edit_button))
                reply.exec_()

            else:
                reply.setIcon(QMessageBox.Critical)
                reply.setText(
                    "Transaction cannot be edited without the valid code.")
                reply.setWindowTitle("INVALID Code")
                reply.exec_()

    def set_name(self, flat):
        name = get_name(flat)
        self.name_value.setText(str(name))

        if self.save_button is not None:
            self.save_button.setEnabled(True)

    def update_entry(self):
        receipt_id = self.receipt_id.text()
        date = str(self.date_line.date().toPyDate())
        fee_month = str(self.month_combo.currentText())

        if self.month_till_combo.isEnabled():
            fee_till = self.month_till_combo.currentText()
            month = f"{fee_month}-{fee_till}"

        else:
            fee_till = ''
            month = fee_month

        flat = str(self.flat_combo.currentText())
        amount = float(self.amount_line.text())
        fine = float(self.fine_line.text())
        mode = str(self.mode_combo.currentText())

        if self.ref_line.isEnabled():
            ref = self.ref_line.text()
        else:
            ref = ''

        new_receipt = receipt.receipt(date=fix_date(date),
                                      flat=flat,
                                      month=fee_month,
                                      month_till=fee_till,
                                      amount=amount,
                                      fine=fine,
                                      mode=mode,
                                      ref=ref)
        self.disable()
        self.total_label.setText(
            "Confirming.. PLEASE DO NOT PERFORM ANY OTHER OPERATIONS.")

        new_receipt.update_db(receipt_id=self.receipt_id.text())

        self.status.setText("Adding to databse & Generating receipt")
        self.bar.setValue(40)
        design_receipt(receipt_id=f"{receipt_id}")

        self.status.setText(
            "Sending the receipt to the member. (Depends on your internet connection)"
        )
        self.bar.setValue(75)
        email_status = send_receipt(flat=flat, month=month, updated=True)
        self.enable()
        self.set_total()

        if email_status:
            self.bar.setValue(100)

        else:
            reply = QMessageBox()
            reply.setIcon(QMessageBox.Warning)
            reply.setText(
                "The system cannot access the internet. Make sure you have an active connection, or any firewall"
                "feature blocking the access.")
            reply.setStandardButtons(QMessageBox.Retry)
            reply.setWindowTitle("INTERNET")
            reply.exec_()

    def final_clicked(self, button):
        if button.text() == "Confirm":
            print("in confirm")
            self.update_entry()

            self.receipt_id.setText("")
            self.receipt_id.setFocus()
            self.flat_combo.setCurrentIndex(0)
            self.name_value.setText("")
            self.amount_line.setText('0')
            self.fine_line.setText('0')

            self.bar.setValue(0)
            self.status.setText("Done !")
            self.ref_line.clear()

    def set_pending_months(self, date: str = None):
        if not self.presetting:
            if date is None:
                date = str(self.date_line.date().toPyDate())

            months = calculate_months(month=date, pending=True, advance=False)

            self.current_pending_months = months

            self.month_combo.clear()

            model = self.month_combo.model()
            for month in months:
                model.appendRow(QStandardItem(month))

            if self.save_button is not None:
                self.save_button.setEnabled(True)

    def set_advance_months(self, date: str = None):
        if not self.presetting:

            if date is None or date == '':
                date = fix_date_back(self.current_pending_months[0])
            else:
                date = fix_date_back(date)

            months = calculate_months(month=date, pending=False, advance=True)

            self.current_advance_months = months

            self.month_till_combo.clear()
            model = self.month_till_combo.model()

            for month in months:
                model.appendRow(QStandardItem(month))

            if self.save_button is not None:
                self.save_button.setEnabled(True)

    def calculate_amount(self):
        if not self.presetting:

            if self.month_combo.count() == 0 or self.month_till_combo.count(
            ) == 0:
                self.amount_line.setText('0')
                return

            else:
                all_possible_months = self.current_advance_months.copy()
                all_possible_months = all_possible_months[::-1]

                all_possible_months.extend([
                    x for x in self.current_pending_months
                    if x not in self.current_advance_months
                ])

                if self.month_till_combo.isEnabled():
                    from_index = all_possible_months.index(
                        self.month_combo.currentText())
                    till_index = all_possible_months.index(
                        self.month_till_combo.currentText())

                    amount = (from_index - till_index + 1) * 1500

                else:
                    amount = 1500

                self.amount_line.setText(str(amount))
                self.amount_line.setToolTip(f"Total months : {amount // 1500}")

            if self.save_button is not None:
                self.save_button.setEnabled(True)

    def calculate_fine(self, from_where: str, month):
        if not self.presetting:

            if month == '' and self.month_combo.count(
            ) == 0 or self.month_till_combo.count() == 0:
                self.fine_line.setText('0')
                return

            else:
                if self.month_till_combo.isEnabled():
                    try:
                        till_index = self.current_pending_months.index(
                            str(self.month_till_combo.currentText()))
                    except ValueError:
                        till_index = 0
                else:
                    try:
                        till_index = self.current_pending_months.index(
                            str(self.month_combo.currentText()))
                    except ValueError:
                        self.fine_line.setText('0')
                        return

                try:
                    from_index = self.current_pending_months.index(
                        str(self.month_combo.currentText()))
                except ValueError:
                    self.fine_line.setText('0')
                    return

                all_fine_months = []

                for month in self.current_pending_months[
                        till_index:from_index + 1]:
                    all_fine_months.append([month])

                transact_date = str(self.date_line.date().toPyDate())

                total_fine = 0
                for month in all_fine_months:
                    fine = calculate_fine(
                        month=month[0], transact_date=fix_date(transact_date))
                    month = month.append(fine)

                    total_fine += fine * 50

                self.fine_line.setText(str(total_fine))
                self.set_fine_tip(all_fine_months=all_fine_months)

            if self.save_button is not None:
                self.save_button.setEnabled(True)

    def set_fine_tip(self, all_fine_months: list):
        tool_line = ''

        for month in all_fine_months:
            tool_line += f"{month[0]} x {month[1]}\n"

        self.fine_line.setToolTip(tool_line)

    def set_total(self):
        if not self.presetting:

            if len(self.amount_line.text()) > 0:
                amount = float(self.amount_line.text())
            else:
                amount = 0

            if len(self.fine_line.text()) > 0:
                fine = int(self.fine_line.text())
            else:
                fine = 0

            self.total_label.setText(f"TOTAL PAYABLE AMOUNT : {amount + fine}")

    def print_receipt(self):
        receipt_id = self.receipt_id.text()
        design_receipt(receipt_id=f"{receipt_id}", send=False)

        user = os.environ['USERPROFILE']
        path = user + '\\Desktop\\SocietyERP\\Receipts'

        subprocess.Popen(rf'explorer /select,{path}')

        self.receipt_id.setText("")
        self.receipt_id.setFocus()

        self.print_button.setEnabled(False)
        self.edit_button.setEnabled(False)
        self.resend_button.setEnabled(False)

    def resend_receipt(self):
        receipt_id = self.receipt_id.text()

        fee_month = str(self.month_combo.currentText())

        if self.month_till_combo.isEnabled():
            fee_till = self.month_till_combo.currentText()
            month = f"{fee_month}-{fee_till}"

        else:
            month = fee_month

        flat = str(self.flat_combo.currentText())

        self.status.setText("Generating receipt")
        self.bar.setValue(40)
        design_receipt(receipt_id=f"{receipt_id}")

        self.status.setText(
            "Sending the receipt to the member. (Depends on your internet connection)"
        )
        self.bar.setValue(75)
        email_status = send_receipt(flat=flat, month=month, updated=True)

        if email_status:
            self.bar.setValue(0)
            self.status.setText("Done !")

            self.receipt_id.setText("")
            self.flat_combo.setCurrentIndex(0)
            self.name_value.setText("")
            self.amount_line.setText('0')
            self.fine_line.setText('0')
            self.receipt_id.setFocus()

        else:
            reply = QMessageBox()
            reply.setIcon(QMessageBox.Warning)
            reply.setText(
                "The system cannot access the internet. Make sure you have an active connection, or any firewall"
                "feature blocking the access.")
            reply.setStandardButtons(QMessageBox.Retry)
            reply.setWindowTitle("INTERNET")
            reply.exec_()

    def disable(self):
        self.finance_entry_group0.setEnabled(False)
        self.finance_entry_group1.setEnabled(False)
        self.home_button.setEnabled(False)
        self.home_button.setEnabled(False)
        self.mode_label.setEnabled(False)
        self.mode_combo.setEnabled(False)
        self.ref_label.setEnabled(False)
        self.ref_line.setEnabled(False)
        self.save_button.setEnabled(False)

    def enable(self):
        self.finance_entry_group0.setEnabled(True)
        self.finance_entry_group1.setEnabled(True)
        self.home_button.setEnabled(True)
        self.home_button.setEnabled(True)
        self.mode_label.setEnabled(True)
        self.mode_combo.setEnabled(True)
        self.ref_label.setEnabled(True)
        self.ref_line.setEnabled(True)
        self.save_button.setEnabled(True)
class SettingsWidget(QWidget):
    """
    Widget for setting entrys

    Inherits:
    QWidget

    Signals:
    sig_index_changed - Emitted, if the index of a combo box changes (Combo box name|str)
    """
    sig_index_changed = pyqtSignal(str)

    def __init__(self,
                 name,
                 content,
                 content_others,
                 mount_directory,
                 global_dict=None,
                 input_file_names=None,
                 parent=None):
        """
        Initialise the layout.

        Arguments:
        content - Content for the widget.
        parent - Parent widget (default None)

        Returns:
        None
        """
        super(SettingsWidget, self).__init__(parent=parent)

        self.action = QAction(self)
        self.action.setShortcut(
            QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Return))
        self.action.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.action.triggered.connect(self.enlarge)
        self.addAction(self.action)

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

        # Global content
        self.parent = parent
        self.default = content[0]
        self.key_name = name
        self.typ = content[1]['typ']
        self.values = content[1]['values']
        self.name = content[1]['name']
        self.tooltip = content[1]['tooltip']
        self.dtype = content[1]['dtype']
        self.name_global = content[1]['name_global']
        self.global_value = 'ON-THE-FLY'
        self.widget_auto = None
        self.mount_directory = mount_directory
        try:
            dependency_name, dependency_value = content[1]['group'].split(':')
        except ValueError:
            dependency_name = None
            dependency_value = None

        if self.name == 'Project name':
            pattern = None
            pattern_example = None
            for entry in content_others:
                for widget in entry:
                    for key in widget:
                        if key == 'Project name pattern':
                            pattern = widget[key][0]
                        elif key == 'Project name pattern example':
                            pattern_example = widget[key][0]
            add_to_tooltip = [self.tooltip]
            if pattern:
                self.default = pattern
                add_to_tooltip.extend([
                    'Needs to follow:',
                    pattern,
                ])
            if pattern_example:
                add_to_tooltip.extend(['For example', pattern_example])
            self.tooltip = '\n'.join(add_to_tooltip)

        if self.tooltip:
            final_tooltip = []
            for line in self.tooltip.splitlines():
                final_tooltip.append('\n'.join(
                    [line[i:i + 80] for i in range(0, len(line), 80)]))
            self.tooltip = '\n'.join(final_tooltip)
        else:
            self.tooltip = self.name

        self.tooltip = '{0}\nDefault: \'{1}\''.format(self.tooltip,
                                                      self.default)
        if dependency_name is not None:
            self.tooltip += '\n\nDependency: \'{0}\' is \'{1}\''.format(
                dependency_name, dependency_value)

        additional_widget = []
        self.add_widgets = []
        if self.typ not in ('COMBO', 'COMBOX'):
            additional_widget.append(
                (self.enlarge, QStyle.SP_TitleBarNormalButton,
                 'Open the text field in "Enlarged mode".'))

        if self.typ == 'PLAIN' or self.typ == 'PASSWORD':
            self.edit = QLineEdit(self.name, self)
            self.edit.setToolTip(self.tooltip)
            self.edit.textChanged.connect(self.change_tooltip)
            self.edit.setText(self.default)

            if self.typ == 'PASSWORD':
                self.edit.setEchoMode(self.edit.Password)

            self.tooltip = '{0}\n\nShortcuts:\nCtrl + Shift + Return -> Open enlarged view'.format(
                self.tooltip)

        elif self.typ in ('FILE', 'FILE/SEARCH'):
            input_file_names.append(self.name)
            self.edit = QLineEdit(self.name, self)
            self.edit.textChanged.connect(self.change_tooltip)
            self.edit.setText(self.default)
            self.edit.setPlaceholderText('Press shift+return')
            self.edit.returnPressed.connect(self._find_file)
            self.tooltip = '{0}\n\nShortcuts:\nCtrl + Shift + Return -> Open file dialog\nCtrl + Return -> Open enlarged view'.format(
                self.tooltip)

            additional_widget.append(
                (self._find_file, QStyle.SP_DialogOpenButton,
                 'Open the "Find file" dialog.'))

        elif self.typ in ('DIR', 'DIR/SEARCH'):
            self.edit = QLineEdit(self.name, self)
            self.edit.textChanged.connect(self.change_tooltip)
            self.edit.setText(self.default)
            self.edit.setPlaceholderText('Press shift+return')
            self.edit.returnPressed.connect(self._find_dir)
            self.tooltip = '{0}\n\nShortcuts:\nCtrl + Shift + Return -> Open directory dialog\nCtrl + Return -> Open enlarged view'.format(
                self.tooltip)

            additional_widget.append(
                (self._find_dir, QStyle.SP_DialogOpenButton,
                 'Open the "Find directory" dialog.'))

        elif self.typ in ('COMBO', 'COMBOX'):
            self.edit = QComboBox(self)
            self.edit.setSizeAdjustPolicy(QComboBox.AdjustToContents)
            self.edit.currentTextChanged.connect(self.change_tooltip)
            self.edit.currentIndexChanged.connect(
                lambda: self.sig_index_changed.emit(self.name))
            self.edit.addItems(self.values)
            self.edit.setCurrentIndex(self.edit.findText(self.default))
            self.change_color_if_true()
            if self.typ == 'COMBOX':
                self.edit.setEditable(True)
                self.edit.setInsertPolicy(QComboBox.NoInsert)
                self.edit.editTextChanged.connect(self.change_tooltip)
                self.edit.editTextChanged.connect(
                    lambda: self.sig_index_changed.emit(self.name))

        else:
            print('SettingsWidget:', self.typ, 'Not known! Stopping here!')
            sys.exit()

        self.label = QLabel(self.name, self)
        large_list = ['Path']
        if name in large_list:
            self.label.setObjectName('setting_large')
            self.edit.setObjectName('setting_large')
        else:
            self.label.setObjectName('setting')
            self.edit.setObjectName('setting')

        self.label.setToolTip(self.name)
        try:
            self.edit.textEdited.connect(
                lambda: self.edit.setStyleSheet(tu.get_style(typ='unchanged')))
        except AttributeError:
            pass
        try:
            self.edit.currentIndexChanged.connect(self.change_color_if_true)
        except AttributeError:
            pass
        layout.addWidget(self.label)

        layout_h = QHBoxLayout()
        layout_h.setContentsMargins(0, 0, 0, 0)
        layout_h.setSpacing(0)
        layout_h.addWidget(self.edit, stretch=1)
        try:
            self.pre_global = self.edit.text()
        except AttributeError:
            self.pre_global = self.edit.currentText()

        for func, icon, tooltip in additional_widget:
            pb = QPushButton(self)
            pb.setObjectName('global')
            pb.setStyleSheet(
                'QPushButton {color: rgba(0, 0, 0 ,0); background-color: rgba(0, 0, 0, 0)}'
            )
            icon = pb.style().standardIcon(icon)
            pb.setIcon(icon)
            pb.setToolTip(tooltip)
            pb.clicked.connect(func)
            self.add_widgets.append(pb)

        if self.name_global is not None:
            self.edit.setObjectName('settinger')
            self.tooltip = '{0}\n\nGlobal value: {{global_value}}'.format(
                self.tooltip)

            self.widget_auto = QPushButton('G', self)
            self.widget_auto.setObjectName('global')
            self.widget_auto.setToolTip(
                'Use the global value specified in the "Global" settings tab')
            state = True
            self.widget_auto.setCheckable(state)
            self.widget_auto.toggled.connect(self._toggle_change)
            self.widget_auto.setChecked(state)
            layout_h.addWidget(self.widget_auto, stretch=0)

            for global_name in self.name_global:
                if global_dict is not None:
                    global_dict.setdefault(global_name, []).append(self)

        for pb in self.add_widgets:
            layout_h.addWidget(pb, stretch=0)

        layout_h.addStretch(1)
        layout.addLayout(layout_h)

        self.tooltip = '{0}\n\nCurrent Text: \'{{current_text}}\''.format(
            self.tooltip)
        try:
            self.edit.textChanged.emit(self.edit.text())
        except AttributeError:
            self.edit.currentTextChanged.emit(self.edit.currentText())

    @pyqtSlot(bool)
    def _toggle_change(self, state):
        pre_pre_global = self.pre_global
        if state and self.edit.isEnabled() == True:
            try:
                self.pre_global = self.edit.text()
            except AttributeError:
                self.pre_global = self.edit.currentText()
        if self.key_name == 'Global' and state:
            try:
                current_global = self.get_current_global()
                if not current_global:
                    self.sender().setChecked(not state)
                    self.pre_global = pre_pre_global
                    return
            except Exception:
                self.pre_global = pre_pre_global
                self.sender().setChecked(not state)
                return
        elif state:
            current_global = self.global_value

        self.edit.setEnabled(not state)
        for entry in self.add_widgets:
            entry.setEnabled(not state)
        self.action.setEnabled(not state)

        if not state:
            try:
                self.edit.setText(self.pre_global)
                self.edit.setStyleSheet(tu.get_style('unchanged'))
            except AttributeError:
                self.edit.setCurrentText(self.pre_global)
                self.change_color_if_true()

        else:
            try:
                self.edit.setText(current_global)
            except AttributeError:
                self.edit.setCurrentText(current_global)
            self.edit.setStyleSheet(tu.get_style('global'))

    def get_current_global(self):
        current_global = self.pre_global
        if self.name == 'GPU':
            try:
                nvidia_output = subprocess.check_output(['nvidia-smi', '-L'])
                gpu_devices = re.findall('^GPU \d+: (.+) \(UUID: GPU-.*\)$',
                                         nvidia_output.decode('utf-8'),
                                         re.MULTILINE)
            except subprocess.CalledProcessError:
                gpu_devices = []
            except Exception as e:
                print(e)
                gpu_devices = []
            if len(set(gpu_devices)) != 1:
                tu.message(
                    'The computer does have different types of GPUs available or no GPUs available or the GPU\'s crashed! In order to not make any mistakes, please specify the GPU IDs manually'
                )
                return False
            else:
                current_global = ' '.join(
                    [str(entry) for entry in range(len(gpu_devices))])

        elif self.name in ('Memory usage', 'Memory usage large'):
            try:
                current_global = str(
                    float(self.pre_global) / max(int(self.global_value), 1))
            except Exception:
                pass

        elif self.name == 'Pixel size bin':
            try:
                pixel_size_raw = float(
                    self.parent.content['Pixel size'].get_settings()
                    ['Pixel size'])
                current_bin = int(
                    self.parent.content['Bin X times'].get_settings()
                    ['Bin X times'])
                current_global = str(pixel_size_raw * current_bin)
            except Exception:
                pass

        elif self.name == 'Box size':
            try:
                current_radius = float(
                    self.parent.content['Protein radius'].get_settings()
                    ['Protein radius'])
                good_box_size = [
                    16, 24, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 84, 96,
                    100, 104, 112, 120, 128, 132, 140, 168, 180, 192, 196, 208,
                    216, 220, 224, 240, 256, 260, 288, 300, 320, 352, 360, 384,
                    416, 440, 448, 480, 512, 540, 560, 576, 588, 600, 630, 640,
                    648, 672, 686, 700, 720, 750, 756, 768, 784, 800, 810, 840,
                    864, 882, 896, 900, 960, 972, 980, 1000, 1008, 1024
                ]
                box_size = int(current_radius * 3)
                good_box_size.append(box_size)
                good_box_size = sorted(good_box_size)

                try:
                    current_global = str(
                        good_box_size[good_box_size.index(box_size) + 1])
                except IndexError:
                    current_global = str(box_size)
            except Exception:
                pass

        return current_global

    @pyqtSlot(str)
    def change_tooltip(self, text):
        edit = self.sender()
        if self.typ != 'PASSWORD':
            tooltip = self.tooltip.format(current_text=text,
                                          global_value=self.global_value)
            edit.setToolTip(tooltip)

    @pyqtSlot()
    def enlarge(self):
        if self.typ in ('COMBO', 'COMBOX'):
            return
        else:
            input_box = inputbox.InputBox(
                is_password=bool(self.typ == 'PASSWORD'), parent=self)
            input_box.setText('Enlarged view', self.name)
            input_box.setDefault(self.edit.text())
            input_box.set_type(self.typ)
            result = input_box.exec_()

            if result:
                self.edit.setText(input_box.getText())

    @pyqtSlot()
    def change_color_if_true(self):
        """
        Change the color, if the types are all true.

        Arguments:
        None

        Returns:
        None
        """
        if self.widget_auto is not None:
            if self.widget_auto.isChecked():
                self.edit.setStyleSheet(tu.get_style(typ='global'))
                return

        if self.edit.currentText() == 'False':
            self.edit.setStyleSheet(tu.get_style(typ='False'))
        else:
            self.edit.setStyleSheet(tu.get_style(typ='True'))

    @pyqtSlot()
    def _find_file(self):
        """
        Find file with an open file dialog.

        Arguments:
        None

        Returns:
        None
        """
        if '/SEARCH' in self.typ:
            current_dir = self.mount_directory
        else:
            current_dir = os.getcwd()

        in_file = QFileDialog.getOpenFileName(
            caption='Find file: {0}'.format(self.name),
            directory=current_dir,
            options=QFileDialog.DontUseNativeDialog)

        in_file = in_file[0]

        if in_file != '':
            self.edit.setText(in_file)

    @pyqtSlot()
    def _find_dir(self):
        """
        Find directory with an open directory dialog

        Arguments:
        None

        Returns:
        None
        """
        if '/SEARCH' in self.typ:
            current_dir = self.mount_directory
        else:
            current_dir = os.getcwd()

        in_dir = QFileDialog.getExistingDirectory(
            caption='Find directory: {0}'.format(self.name),
            directory=current_dir,
            options=QFileDialog.DontUseNativeDialog)
        if in_dir != '':
            if self.name == 'Project name':
                in_dir = os.path.basename(in_dir)
            self.edit.setText(in_dir)

            if '/SEARCH' in self.typ:
                self.parent.search_for_projects(in_dir)

    def get_settings(self, quiet=False):
        """
        Get the settings as dict.

        Arguments:
        quiet - True, if prints should not be shown.

        Returns:
        None, if an error occured.
        Settings as dictionary.
        """

        settings = {}
        if isinstance(self.edit, QComboBox):
            value = self.edit.currentText()

        elif isinstance(self.edit, QLineEdit):
            value = self.edit.text()

        else:
            message = 'Unreachable code! Please contact the TranSPHIRE authors!'
            tu.message(message)
            return None
        value = value.strip()

        if ' ' in value and (self.typ in ('FILE', 'DIR', 'FILE/SEARCH')
                             or self.name in ('Rename prefix', 'Rename suffix',
                                              'Project name')):
            self.edit.setStyleSheet(tu.get_style(typ='error'))
            message = '{0}: {1} needs to be {2}. To avoid problems later, file paths are not allowed to contain whitespaces. If this is the case, please rename the respective folders and files'.format(
                self.label.text(), value, self.dtype)

            if not quiet:
                tu.message(message)

            return None

        elif value:

            if tu.check_instance(value=value, typ=self.dtype):
                pass

            elif value == 'ON-THE-FLY' and self.widget_auto is not None:
                pass

            else:
                self.edit.setStyleSheet(tu.get_style(typ='error'))
                message = '{0}: {1} needs to be {2}.'.format(
                    self.label.text(), value, self.dtype)

                if not quiet:
                    tu.message(message)

                return None

        else:
            pass

        settings[self.name] = value
        if self.widget_auto is not None:
            is_auto = self.widget_auto.isChecked()
        else:
            is_auto = None
        global_name = None if self.name_global is None else self.name_global[0]
        settings['{0}_global'.format(self.name)] = [global_name, is_auto]

        return settings

    def get_combo_entries(self):
        """
        Get the entries of the combo boxes.

        Arguments:
        None

        Returns:
        List containing entries.
        """
        entries_list = []
        for idx in range(self.edit.count()):
            entries_list.append(self.edit.itemText(idx))
        return entries_list

    def set_settings(self, text, is_checked):
        """
        Set settings

        text - Text to set to the widget.

        Returns:
        None
        """
        if is_checked is not None:
            is_checked_type = is_checked.split(', ')[1][:-1]
            if is_checked_type == 'None':
                is_checked = None
            elif is_checked_type == 'True':
                is_checked = True
            elif is_checked_type == 'False':
                is_checked = False
            else:
                assert False, is_checked_type

            if is_checked is not None:
                try:
                    self.widget_auto.setChecked(is_checked)
                except AttributeError:
                    pass

        widget_auto_checked = self.widget_auto.isChecked(
        ) if self.widget_auto is not None else False

        if is_checked or widget_auto_checked:
            self.pre_global = text
        else:
            if self.typ in ('COMBO', 'COMBOX'):
                index = self.edit.findText(text)
                if index == -1:
                    index = 0
                else:
                    pass
                self.edit.setCurrentIndex(index - 1)
                self.edit.setCurrentIndex(index)
            else:
                self.edit.setText(text)
class MIDISettings(SettingsPage):

    NAME = 'MIDI settings'
    BACKENDS = {'RtMidi': 'mido.backends.rtmidi',
                'PortMidi': 'mido.backends.portmidi'}

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.setLayout(QVBoxLayout())
        self.layout().setAlignment(Qt.AlignTop)

        # MIDI Input
        self.inputGroup = QGroupBox(self)
        self.inputGroup.setTitle('MIDI input device')
        self.inputGroup.setLayout(QVBoxLayout())
        self.layout().addWidget(self.inputGroup)

        self.backendCombo = QComboBox(self.inputGroup)
        self.backendCombo.addItems(self.BACKENDS)
        self.backendCombo.currentTextChanged.connect(self._change_backend)
        self.inputGroup.layout().addWidget(self.backendCombo)

        self.deviceCombo = QComboBox(self.inputGroup)
        self.inputGroup.layout().addWidget(self.deviceCombo)

        self._load_devices()

    def get_settings(self):
        conf = {}

        if self.backendCombo.isEnabled():
            conf['backend'] = self.BACKENDS[self.backendCombo.currentText()]
        if self.deviceCombo.isEnabled():
            conf['inputdevice'] = self.deviceCombo.currentText()
            MIDIInputHandler().change_port(conf['inputdevice'])

        return {'MIDI': conf}

    def load_settings(self, settings):
        if 'backend' in settings['MIDI']:
            for backend in self.BACKENDS:
                if settings['MIDI']['backend'] == self.BACKENDS[backend]:
                    self.backendCombo.setCurrentText(backend)
                    break
        if 'inputdevice' in settings['MIDI']:
            self.deviceCombo.setCurrentText('default')
            # If the device is not found remains 'default'
            self.deviceCombo.setCurrentText(settings['MIDI']['inputdevice'])

    def _load_devices(self):
        if check_module('Midi'):
            self.deviceCombo.clear()
            self.deviceCombo.addItem('default')
            self.deviceCombo.addItems(MIDIInputHandler().get_input_names())
        else:
            self.deviceCombo.setEnabled(False)

    def _change_backend(self, current):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        self.setEnabled(False)

        try:
            MIDIInputHandler().change_backend(self.BACKENDS[current])
            self._load_devices()
        except RuntimeError as e:
            logging.exception('Failed to load the backend', e, dialog=True)
        finally:
            QApplication.restoreOverrideCursor()
            self.setEnabled(True)
class MIDISettings(SettingsSection):

    NAME = 'MIDI preferences'
    BACKENDS = {'RtMidi': 'mido.backends.rtmidi',
                'PortMidi': 'mido.backends.portmidi'}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # MIDI Input
        self.inputGroup = QGroupBox(self)
        self.inputGroup.setTitle('Input MIDI device')
        self.inputGroup.setLayout(QVBoxLayout())
        self.inputGroup.setGeometry(0, 0, self.width(), 120)

        self.backendCombo = QComboBox(self.inputGroup)
        self.backendCombo.addItems(self.BACKENDS)
        self.backendCombo.currentTextChanged.connect(self._change_backend)
        self.inputGroup.layout().addWidget(self.backendCombo)

        self.deviceCombo = QComboBox(self.inputGroup)
        self.inputGroup.layout().addWidget(self.deviceCombo)

        self._load_devices()

    def get_configuration(self):
        conf = {}

        if self.backendCombo.isEnabled():
            conf['backend'] = self.BACKENDS[self.backendCombo.currentText()]
        if self.deviceCombo.isEnabled():
            conf['inputdevice'] = self.deviceCombo.currentText()
            InputMidiHandler().change_port(conf['inputdevice'])

        return {'MIDI': conf}

    def set_configuration(self, conf):
        if 'backend' in conf['MIDI']:
            for backend in self.BACKENDS:
                if conf['MIDI']['backend'] == self.BACKENDS[backend]:
                    self.backendCombo.setCurrentText(backend)
                    break
        if 'inputdevice' in conf['MIDI']:
            self.deviceCombo.setCurrentText('default')
            # If the device is not found remains 'default'
            self.deviceCombo.setCurrentText(conf['MIDI']['inputdevice'])

    def _load_devices(self):
        if check_module('midi'):
            self.deviceCombo.clear()
            self.deviceCombo.addItem('default')
            self.deviceCombo.addItems(InputMidiHandler().get_input_names())
        else:
            self.deviceCombo.setEnabled(False)

    def _change_backend(self, current):
        self.setEnabled(False)
        try:
            InputMidiHandler().change_backend(self.BACKENDS[current])
            self._load_devices()
        except RuntimeError as e:
            QMessageBox.critical(self, 'Error', str(e))
        finally:
            self.setEnabled(True)