Exemple #1
0
class ButtonGroupWidget(QGroupBox):
    def __init__(self, parent=None):
        super(ButtonGroupWidget, self).__init__(parent)
        self._selectedOption = -1
        self.buttonGroup = QButtonGroup()
        self.layoutGroupBox = QVBoxLayout(self)

    def addRadioButton(self, optionText, optionId):
        print(optionText)
        radioButton = QRadioButton(optionText)
        radioButton.clicked.connect(self.updateOptionSelected)
        self.layoutGroupBox.addWidget(radioButton)
        self.buttonGroup.addButton(radioButton, optionId)

    def updateOptionSelected(self):
        # print(self.buttonGroup.checkedId()) # for test purpose
        # self.selectedOption = self.buttonGroup.checkedId()
        # print(self._selectedOption) # for test purpose
        self.selectedOption = self.buttonGroup.checkedId()
        QApplication.postEvent(
            self, QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier))

    def getSelectedOption(self):
        print("get selectedOption is called")
        return self._selectedOption

    def setSelectedOption(self, selectedOption):
        # print ("selectedOption",selectedOption)
        print("set selectedOption is called")
        self._selectedOption = selectedOption
        self.buttonGroup.button(selectedOption).setChecked(True)

    selectedOption = Property(int, getSelectedOption, setSelectedOption)
Exemple #2
0
class SpriteView(QWidget):
    sprite_edited = Signal()

    def __init__(self, parent):
        super().__init__(parent)
        self._scene = QGraphicsScene(self)
        self._graphics_view = QGraphicsView(self._scene, self)

        layout = QVBoxLayout(self)
        layout.addWidget(self._graphics_view)

        self._pixmap_item = SpriteGraphicsItem(self)
        self._scene.addItem(self._pixmap_item)

        self._image = QImage(SPRITE_SIZE, SPRITE_SIZE, QImage.Format_ARGB32)
        self.sprite = [[0] * SPRITE_SIZE] * SPRITE_SIZE

        button_layout = QHBoxLayout(self)
        layout.addLayout(button_layout)
        self._color_button_group = QButtonGroup(self)
        for color in COLORS:
            button = QPushButton(self)
            button.setCheckable(True)
            button.setText(f"{color}")
            self._color_button_group.addButton(button, color)
            button_layout.addWidget(button)

        self._selected_color = 0
        self._color_button_group.button(self._selected_color).setChecked(True)
        QObject.connect(self._color_button_group, SIGNAL("buttonClicked(int)"),
                        self._color_selected)

        self._update_scaling()

    @property
    def sprite(self):
        return self._sprite

    @sprite.setter
    def sprite(self, sprite):
        if len(sprite) != SPRITE_SIZE:
            raise SpriteFormatException("Invalid Row Count")
        for row in sprite:
            if len(row) != SPRITE_SIZE:
                raise SpriteFormatException("Invalid Column Count")
            for value in row:
                if value not in COLORS:
                    raise SpriteFormatException(f"Invalid Color Value {value}")
        self._sprite = sprite
        self._update_image()

    def _update_image(self):
        for row in range(SPRITE_SIZE):
            for column in range(SPRITE_SIZE):
                self._image.setPixel(column, row,
                                     COLORS[self._sprite[row][column]])
        pixmap = QPixmap.fromImage(self._image)
        self._pixmap_item.setPixmap(pixmap)

    def resizeEvent(self, *args, **kwargs):
        super().resizeEvent(*args, **kwargs)
        self._update_scaling()

    def showEvent(self, *args, **kwargs):
        super().showEvent(*args, **kwargs)
        self._update_scaling()

    def _update_scaling(self):
        self._graphics_view.fitInView(self._pixmap_item, Qt.KeepAspectRatio)

    def _color_selected(self, id):
        self._selected_color = id

    def paint_pixel(self, x, y):
        if self._sprite[y][x] == self._selected_color:
            return
        self._sprite[y][x] = self._selected_color
        self._update_image()
        self.sprite_edited.emit()
class SelectDialog(QDialog):
    def __init__(self, widget, image, imageResult):
        super(SelectDialog, self).__init__()
        self.widget = widget
        self.image = image.copy()
        self.candidateResults = imageResult
        labelW = 200  #控制图片大小
        h, w = self.image.shape[:2]
        if w > labelW:
            ratio = float(labelW) / float(w)
        else:
            labelW = w
            ratio = 1
        labelH = int(h * ratio)
        self.image = cv2.resize(self.image, (labelW, labelH),
                                cv2.INTER_NEAREST)
        for i in range(len(self.candidateResults)):
            self.candidateResults[i] = cv2.resize(self.candidateResults[i],
                                                  (labelW, labelH))
        # self.resize(labelW * 3 + 150, 800)
        #设置图片宽高结束
        # self.selectTrue = False
        self.Vlayout = QVBoxLayout()  #整体布局
        self.Vlayout.setAlignment(Qt.AlignCenter)
        image_label = QLabel()
        image_label.setFixedSize(QSize(labelW, labelH))
        image_label.setPixmap(numpytoPixmap(self.image))
        size = self.image.shape[:2]
        self.firstImageLayout = QHBoxLayout()  #原图的布局
        self.firstImageLayout.addWidget(image_label)
        self.Vlayout.addLayout(self.firstImageLayout)  #插入原图布局
        self.buttonGroup = QButtonGroup()
        id = 0
        self.resultLayout = QHBoxLayout()  #四个结果图的布局
        self.selectAlphas = []
        for i, alpha in enumerate(self.candidateResults):
            bg = config.getBackground(size, 2)
            b, g, r, a = cv2.split(alpha)
            bgr = np.stack([b, g, r], axis=2)
            a = np.stack([a] * 3, axis=2) / 255.0
            alpha = self.changeBackground(bgr, a, bg)
            self.selectAlphas.append([bgr, a])
            final_label = BtnLabel(self, i)  # 自动以Label类
            final_label.setFixedSize(QSize(labelW, labelH))
            final_label.setPixmap(numpytoPixmap(alpha))
            final_label.setCursor(QCursor(Qt.PointingHandCursor))
            radioButton = QRadioButton(str(id))  # 创建单选按钮
            self.buttonGroup.addButton(radioButton, id)
            id += 1
            self.Hlayout = QVBoxLayout()  # 一张图和一个radio的布局
            self.Hlayout.addWidget(final_label)
            self.Hlayout.addWidget(radioButton)
            self.resultLayout.addLayout(self.Hlayout)

        self.Vlayout.addLayout(self.resultLayout)  # 插入四个结果布局

        self.abandonlayout = QHBoxLayout()  #放弃按钮布局
        abandonButton = BtnLabel(self, 5)
        abandonButton.setText('放弃')
        abandonButton.setAlignment(Qt.AlignCenter)
        abandonButton.setFixedWidth(200)
        abandonButton.setStyleSheet(
            "QLabel{color:rgb(255,255,255);font-size:18px;}"
            "QLabel{width:200}"
            "QLabel:hover{background-color:#871926}"
            "QLabel{background-color:#871926;}"
            "QLabel{border:2px}"
            "QLabel{border-radius:4px}"
            "QLabel{padding:5px 4px}")
        # abandonButton.clicked.connect(self.abandonClick)
        self.abandonlayout.addWidget(abandonButton)
        self.Vlayout.addLayout(self.abandonlayout)

        self.setLayout(self.Vlayout)

        self.buttonGroup.button(0).setChecked(True)

    #放弃
    # def abandonClick(self):
    # 	self.widget.abandon()
    # 	self.close()

    def abandonClick(self, i):
        self.selectId = i
        # self.selectAlpha = self.selectAlphas[self.selectId]
        self.selectTrue = True
        self.accept()

    def selectImg(self, i):
        self.selectId = i
        if self.selectId <= 3:
            self.selectAlpha = self.selectAlphas[self.selectId]
            self.selectTrue = True
            self.accept()
        else:
            self.selectAlpha = self.selectAlphas[0]
            self.accept()
            # self.widget.newSet()

    def changeBackground(self, bgr, alpha, background):
        show = bgr * alpha + (1 - alpha) * background
        return show
class BreakpointDialog(QDialog):
    """
    Dialog to edit breakpoints.
    """
    def __init__(self,
                 breakpoint_: Breakpoint,
                 workspace: 'Workspace',
                 parent=None):
        super().__init__(parent)
        self.breakpoint = breakpoint_
        self.workspace = workspace
        self.setWindowTitle('Edit Breakpoint')
        self.main_layout: QVBoxLayout = QVBoxLayout()
        self._type_radio_group: Optional[QButtonGroup] = None
        self._address_box: Optional[QAddressInput] = None
        self._size_box: Optional[QLineEdit] = None
        self._comment_box: Optional[QLineEdit] = None
        self._status_label: Optional[QLabel] = None
        self._ok_button: Optional[QPushButton] = None
        self._init_widgets()
        self.setLayout(self.main_layout)
        self._validate()

    #
    # Private methods
    #

    def _init_widgets(self):
        layout = QGridLayout()
        self.main_layout.addLayout(layout)
        self._status_label = QLabel(self)

        row = 0
        layout.addWidget(QLabel('Break on:', self), row, 0, Qt.AlignRight)
        self._type_radio_group = QButtonGroup(self)
        self._type_radio_group.addButton(QRadioButton('Execute', self),
                                         BreakpointType.Execute.value)
        self._type_radio_group.addButton(QRadioButton('Write', self),
                                         BreakpointType.Write.value)
        self._type_radio_group.addButton(QRadioButton('Read', self),
                                         BreakpointType.Read.value)
        for b in self._type_radio_group.buttons():
            layout.addWidget(b, row, 1)
            row += 1

        self._type_radio_group.button(
            self.breakpoint.type.value).setChecked(True)

        layout.addWidget(QLabel('Address:', self), row, 0, Qt.AlignRight)
        self._address_box = QAddressInput(self._on_address_changed,
                                          self.workspace,
                                          parent=self,
                                          default=f'{self.breakpoint.addr:#x}')
        layout.addWidget(self._address_box, row, 1)
        row += 1

        layout.addWidget(QLabel('Size:', self), row, 0, Qt.AlignRight)
        self._size_box = QLineEdit(self)
        self._size_box.setText(f'{self.breakpoint.size:#x}')
        self._size_box.textChanged.connect(self._on_size_changed)
        layout.addWidget(self._size_box, row, 1)
        row += 1

        layout.addWidget(QLabel('Comment:', self), row, 0, Qt.AlignRight)
        self._comment_box = QLineEdit(self)
        self._comment_box.setText(self.breakpoint.comment)
        layout.addWidget(self._comment_box, row, 1)
        row += 1

        self.main_layout.addWidget(self._status_label)

        buttons = QDialogButtonBox(parent=self)
        buttons.setStandardButtons(QDialogButtonBox.StandardButton.Cancel
                                   | QDialogButtonBox.StandardButton.Ok)
        buttons.accepted.connect(self._on_ok_clicked)
        buttons.rejected.connect(self.close)
        self._ok_button = buttons.button(QDialogButtonBox.Ok)
        self._ok_button.setEnabled(False)
        self.main_layout.addWidget(buttons)

    def _set_valid(self, valid: bool):
        if not valid:
            self._status_label.setText('Invalid')
            self._status_label.setProperty('class', 'status_invalid')
        else:
            self._status_label.setText('Valid')
            self._status_label.setProperty('class', 'status_valid')

        self._ok_button.setEnabled(valid)
        self._status_label.style().unpolish(self._status_label)
        self._status_label.style().polish(self._status_label)

    def _get_size(self):
        try:
            return int(self._size_box.text(), 0)
        except ValueError:
            pass
        return None

    #
    # Event handlers
    #

    def _validate(self):
        self._set_valid(
            bool(self._address_box.target is not None and self._get_size()))

    def _on_address_changed(self, new_text):  # pylint: disable=unused-argument
        self._validate()

    def _on_size_changed(self, new_text):  # pylint: disable=unused-argument
        self._validate()

    def _on_ok_clicked(self):
        self.breakpoint.type = BreakpointType(
            self._type_radio_group.checkedId())
        self.breakpoint.addr = self._address_box.target
        self.breakpoint.size = self._get_size()
        self.breakpoint.comment = self._comment_box.text()
        self.workspace.instance.breakpoint_mgr.breakpoints.am_event()
        self.accept()
Exemple #5
0
class SettingsWindow(QDialog):
    """Settings menu with two tabs for settings models and components"""
    def __init__(self, master, enigma_api):
        """
        Submenu for setting Enigma model and component settings
        :param master: Qt parent object
        :param enigma_api: {EnigmaAPI}
        """
        super().__init__(master)

        # QT WINDOW SETTINGS ===================================================

        main_layout = QVBoxLayout(self)
        self.__settings_frame = QFrame(self)
        self.__settings_layout = QHBoxLayout(self.__settings_frame)
        self.setWindowTitle("Settings")
        self.setLayout(main_layout)
        self.setFixedHeight(620)
        self.__reflector_group = []
        self.__rotor_frames = []

        # SAVE ATTRIBUTES ======================================================

        self.__enigma_api = enigma_api
        self.__rotor_selectors = []
        self.__ring_selectors = []
        self.__ukwd_window = UKWDSettingsWindow(self, enigma_api)

        # ROTORS AND REFLECTOR SETTINGS ========================================

        self.__ukwd_button = QPushButton("UKW-D pairs")
        self.__ukwd_button.clicked.connect(self.open_ukwd_window)

        # TAB WIDGET ===========================================================

        tab_widget = QTabWidget()

        self.__stacked_wikis = _ViewSwitcherWidget(self,
                                                   self.regenerate_for_model)
        tab_widget.addTab(self.__stacked_wikis, "Enigma model")
        tab_widget.addTab(self.__settings_frame, "Component settings")

        # BUTTONS ==============================================================

        button_frame = QFrame(self)
        button_layout = QHBoxLayout(button_frame)
        button_layout.setAlignment(Qt.AlignRight)

        self.__apply_btn = QPushButton("Apply")
        self.__apply_btn.clicked.connect(self.collect)

        storno = QPushButton("Storno")
        storno.clicked.connect(self.close)

        button_layout.addWidget(storno)
        button_layout.addWidget(self.__apply_btn)

        # SHOW WIDGETS =========================================================

        model_i = list(VIEW_DATA.keys()).index(self.__enigma_api.model())
        self.__stacked_wikis.select_model(model_i)
        self.__stacked_wikis.highlight(model_i)
        main_layout.addWidget(tab_widget)
        main_layout.addWidget(button_frame)

    def open_ukwd_window(self):
        """Opens UKWD wiring menu"""
        logging.info("Opened UKW-D wiring menu...")
        self.__ukwd_window.exec_()
        self.refresh_ukwd()

    def refresh_ukwd(self):
        """Refreshes Apply button according to criteria (UKW-D rotor must be
        selected to edit its settings)"""
        if self.__reflector_group.checkedButton().text() == "UKW-D":
            logging.info("UKW-D reflector selected, enabling UKW-D button...")

            if len(self.__ukwd_window.pairs()) != 12:
                self.__apply_btn.setDisabled(True)
                self.__apply_btn.setToolTip(
                    "Connect all 12 pairs in UKW-D wiring!")
            else:
                self.__apply_btn.setDisabled(False)
                self.__apply_btn.setToolTip(None)

            self.__ukwd_button.setDisabled(False)
            self.__ukwd_button.setToolTip(
                "Select the UKW-D rotor to edit settings")
            if len(self.__rotor_frames) == 4:  # IF THIN ROTORS
                logging.info("Disabling thin rotor radiobuttons...")
                self.__rotor_frames[0].setDisabled(True)
        else:
            logging.info(
                "UKW-D reflector deselected, disabling UKW-D button...")
            self.__apply_btn.setDisabled(False)
            self.__apply_btn.setToolTip(None)

            self.__ukwd_button.setDisabled(True)
            self.__ukwd_button.setToolTip(None)
            if len(self.__rotor_frames) == 4:  # IF THIN ROTORS
                logging.info("Enabling thin rotor radiobuttons...")
                self.__rotor_frames[0].setDisabled(False)

    def generate_components(self, reflectors, rotors, rotor_n, charset):
        """Generates currently displayed components based on Enigma model
        :param reflectors: {str} Reflector labels
        :param rotors: {[str, str, str]} Rotor labels
        :param rotor_n: {int} Number of rotors the Enigma model has
        """
        # REFLECTOR SETTINGS ===================================================
        spacing = 15
        style = "font-size: 18px; text-align: center;"

        reflector_frame = QFrame(self.__settings_frame)
        reflector_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)

        reflector_layout = QVBoxLayout(reflector_frame)
        reflector_layout.setSpacing(spacing)
        reflector_layout.addWidget(
            QLabel("REFLECTOR", reflector_frame, styleSheet=style),
            alignment=Qt.AlignHCenter,
        )

        self.__reflector_group = QButtonGroup(reflector_frame)
        reflector_layout.setAlignment(Qt.AlignTop)

        for i, model in enumerate(reflectors):
            radio = QRadioButton(model, reflector_frame)
            radio.setToolTip(
                "Reflector is an Enigma component that \nreflects "
                "letters from the rotors back to the lightboard")
            self.__reflector_group.addButton(radio)
            self.__reflector_group.setId(radio, i)
            reflector_layout.addWidget(radio, alignment=Qt.AlignTop)

        reflector_layout.addStretch()
        reflector_layout.addWidget(self.__ukwd_button)

        self.__reflector_group.button(0).setChecked(True)
        self.__reflector_group.buttonClicked.connect(self.refresh_ukwd)
        self.__settings_layout.addWidget(reflector_frame)

        # ROTOR SETTINGS =======================================================

        self.__rotor_selectors = []
        self.__ring_selectors = []
        self.__rotor_frames = []

        for rotor in range(rotor_n):
            rotor_frame = QFrame(self.__settings_frame)
            rotor_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)
            rotor_layout = QVBoxLayout(rotor_frame)
            rotor_layout.setAlignment(Qt.AlignTop)
            rotor_layout.setSpacing(spacing)
            rotor_frame.setLayout(rotor_layout)

            # ROTOR RADIOS =====================================================

            label = QLabel(SELECTOR_LABELS[-rotor_n:][rotor],
                           rotor_frame,
                           styleSheet=style)
            label.setToolTip(SELECTOR_TOOLTIPS[-rotor_n:][rotor])

            rotor_layout.addWidget(label, alignment=Qt.AlignHCenter)

            button_group = QButtonGroup(rotor_frame)

            final_rotors = rotors

            if "Beta" in rotors:
                logging.info(
                    "Enigma M4 rotors detected, adjusting radiobuttons...")
                if rotor == 0:
                    final_rotors = ["Beta", "Gamma"]
                else:
                    final_rotors.remove("Beta")
                    final_rotors.remove("Gamma")

            for i, model in enumerate(final_rotors):
                radios = QRadioButton(model, rotor_frame)
                button_group.addButton(radios)
                button_group.setId(radios, i)
                rotor_layout.addWidget(radios, alignment=Qt.AlignTop)

            button_group.button(0).setChecked(True)

            # RINGSTELLUNG =====================================================

            combobox = QComboBox(rotor_frame)
            for i, label in enumerate(LABELS[:len(charset)]):
                combobox.addItem(label, i)

            h_rule = QFrame(rotor_frame)
            h_rule.setFrameShape(QFrame.HLine)
            h_rule.setFrameShadow(QFrame.Sunken)

            self.__ring_selectors.append(combobox)
            self.__rotor_selectors.append(button_group)

            rotor_layout.addStretch()
            rotor_layout.addWidget(h_rule)
            rotor_layout.addWidget(
                QLabel("RING SETTING", rotor_frame, styleSheet=style),
                alignment=Qt.AlignHCenter,
            )
            rotor_layout.addWidget(combobox)

            self.__settings_layout.addWidget(rotor_frame)
            self.__rotor_frames.append(rotor_frame)

    def clear_components(self):
        """Deletes all components settings widgets"""
        while True:
            child = self.__settings_layout.takeAt(0)
            if not child:
                break
            wgt = child.widget()
            wgt.deleteLater()
            del wgt

    def regenerate_for_model(self, new_model):
        """Regenerates component settings
        :param new_model: {str} Enigma model
        """
        logging.info("Regenerating component settings...")
        self.clear_components()

        reflectors = self.__enigma_api.model_labels(new_model)["reflectors"]
        rotors = self.__enigma_api.model_labels(new_model)["rotors"]
        rotor_n = self.__enigma_api.rotor_n(new_model)
        charset = HISTORICAL[new_model]["charset"]

        self.generate_components(reflectors, rotors[::], rotor_n, charset)

        defaults = self.__enigma_api.default_cfg(new_model, rotor_n)[1]
        for selected, i in zip(defaults, range(rotor_n)):
            self.__rotor_selectors[i].button(selected).setChecked(True)

        self.__ukwd_window.clear_pairs()
        self.__ukwd_window._old_pairs = {}
        if new_model == self.__enigma_api.model():
            self.load_from_api()
            self.__ukwd_window.refresh_pairs()
        self.refresh_ukwd()

    def load_from_api(self):
        """Loads displayed settings from shared EnigmaAPI instance"""
        logging.info("Loading component settings from EnigmaAPI...")

        model = self.__enigma_api.model()
        reflectors = self.__enigma_api.model_labels(model)["reflectors"]
        rotors = self.__enigma_api.model_labels(model)["rotors"]

        if "Beta" in rotors:
            rotors.remove("Beta")
            rotors.remove("Gamma")

        reflector_i = reflectors.index(self.__enigma_api.reflector())
        self.__reflector_group.button(reflector_i).setChecked(True)

        for i, rotor in enumerate(self.__enigma_api.rotors()):
            if (model == "Enigma M4"
                    and self.__enigma_api.reflector() != "UKW-D" and i == 0):
                rotor_i = ["Beta", "Gamma"].index(rotor)
            else:
                rotor_i = rotors.index(rotor)

            self.__rotor_selectors[i].button(rotor_i).setChecked(True)

        for i, ring in enumerate(self.__enigma_api.ring_settings()):
            self.__ring_selectors[i].setCurrentIndex(ring - 1)

    def collect(self):
        """Collects all selected settings for rotors and other components,
        applies them to the EnigmaAPI as new settings"""
        logging.info("Collecting new settings...")

        new_model = self.__stacked_wikis.currently_selected
        new_reflector = self.__reflector_group.checkedButton().text(
        )  # REFLECTOR CHOICES
        reflector_pairs = self.__ukwd_window.pairs()

        if new_reflector == "UKW-D" and new_model == "Enigma M4":
            new_rotors = [
                group.checkedButton().text()
                for group in self.__rotor_selectors[1:]
            ]
        else:
            new_rotors = [
                group.checkedButton().text()
                for group in self.__rotor_selectors
            ]

        ring_settings = [
            ring.currentIndex() + 1 for ring in self.__ring_selectors
        ]

        logging.info("EnigmaAPI state before applying settings:\n%s",
                     str(self.__enigma_api))

        if new_model != self.__enigma_api.model():
            self.__enigma_api.model(new_model)

        if new_reflector != self.__enigma_api.reflector():
            self.__enigma_api.reflector(new_reflector)

        if new_reflector == "UKW-D":
            self.__enigma_api.reflector_pairs(reflector_pairs)

        if new_rotors != self.__enigma_api.rotors():
            self.__enigma_api.rotors(new_rotors)

        if ring_settings != self.__enigma_api.ring_settings():
            self.__enigma_api.ring_settings(ring_settings)

        logging.info("EnigmaAPI state when closing settings:\n%s",
                     str(self.__enigma_api))

        self.close()

    def pairs(self):
        """Returns current UKW-D pairs for collection"""
        return self._pairs
Exemple #6
0
    def generate_components(self, reflectors, rotors, rotor_n, charset):
        """Generates currently displayed components based on Enigma model
        :param reflectors: {str} Reflector labels
        :param rotors: {[str, str, str]} Rotor labels
        :param rotor_n: {int} Number of rotors the Enigma model has
        """
        # REFLECTOR SETTINGS ===================================================
        spacing = 15
        style = "font-size: 18px; text-align: center;"

        reflector_frame = QFrame(self.__settings_frame)
        reflector_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)

        reflector_layout = QVBoxLayout(reflector_frame)
        reflector_layout.setSpacing(spacing)
        reflector_layout.addWidget(
            QLabel("REFLECTOR", reflector_frame, styleSheet=style),
            alignment=Qt.AlignHCenter,
        )

        self.__reflector_group = QButtonGroup(reflector_frame)
        reflector_layout.setAlignment(Qt.AlignTop)

        for i, model in enumerate(reflectors):
            radio = QRadioButton(model, reflector_frame)
            radio.setToolTip(
                "Reflector is an Enigma component that \nreflects "
                "letters from the rotors back to the lightboard")
            self.__reflector_group.addButton(radio)
            self.__reflector_group.setId(radio, i)
            reflector_layout.addWidget(radio, alignment=Qt.AlignTop)

        reflector_layout.addStretch()
        reflector_layout.addWidget(self.__ukwd_button)

        self.__reflector_group.button(0).setChecked(True)
        self.__reflector_group.buttonClicked.connect(self.refresh_ukwd)
        self.__settings_layout.addWidget(reflector_frame)

        # ROTOR SETTINGS =======================================================

        self.__rotor_selectors = []
        self.__ring_selectors = []
        self.__rotor_frames = []

        for rotor in range(rotor_n):
            rotor_frame = QFrame(self.__settings_frame)
            rotor_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)
            rotor_layout = QVBoxLayout(rotor_frame)
            rotor_layout.setAlignment(Qt.AlignTop)
            rotor_layout.setSpacing(spacing)
            rotor_frame.setLayout(rotor_layout)

            # ROTOR RADIOS =====================================================

            label = QLabel(SELECTOR_LABELS[-rotor_n:][rotor],
                           rotor_frame,
                           styleSheet=style)
            label.setToolTip(SELECTOR_TOOLTIPS[-rotor_n:][rotor])

            rotor_layout.addWidget(label, alignment=Qt.AlignHCenter)

            button_group = QButtonGroup(rotor_frame)

            final_rotors = rotors

            if "Beta" in rotors:
                logging.info(
                    "Enigma M4 rotors detected, adjusting radiobuttons...")
                if rotor == 0:
                    final_rotors = ["Beta", "Gamma"]
                else:
                    final_rotors.remove("Beta")
                    final_rotors.remove("Gamma")

            for i, model in enumerate(final_rotors):
                radios = QRadioButton(model, rotor_frame)
                button_group.addButton(radios)
                button_group.setId(radios, i)
                rotor_layout.addWidget(radios, alignment=Qt.AlignTop)

            button_group.button(0).setChecked(True)

            # RINGSTELLUNG =====================================================

            combobox = QComboBox(rotor_frame)
            for i, label in enumerate(LABELS[:len(charset)]):
                combobox.addItem(label, i)

            h_rule = QFrame(rotor_frame)
            h_rule.setFrameShape(QFrame.HLine)
            h_rule.setFrameShadow(QFrame.Sunken)

            self.__ring_selectors.append(combobox)
            self.__rotor_selectors.append(button_group)

            rotor_layout.addStretch()
            rotor_layout.addWidget(h_rule)
            rotor_layout.addWidget(
                QLabel("RING SETTING", rotor_frame, styleSheet=style),
                alignment=Qt.AlignHCenter,
            )
            rotor_layout.addWidget(combobox)

            self.__settings_layout.addWidget(rotor_frame)
            self.__rotor_frames.append(rotor_frame)
Exemple #7
0
class SelectDialog(QDialog):
    def __init__(self, image, trimaps, images):
        super(SelectDialog, self).__init__()

        self.image = image.copy()
        # self.candidateTrimaps = []
        self.candidateResults = []
        for image in images:
            # self.candidateTrimaps.append(image)
            self.candidateResults.append(image)
        labelW = 200  #控制图片大小
        h, w = self.image.shape[:2]
        if w > labelW:
            ratio = float(labelW) / float(w)
        else:
            labelW = w
            ratio = 1
        labelH = int(h * ratio)
        self.image = cv2.resize(self.image, (labelW, labelH))
        for i in range(len(self.candidateResults)):
            self.candidateResults[i] = cv2.resize(
                self.candidateResults[i], (labelW, labelH),
                interpolation=cv2.INTER_NEAREST)
        self.resize(labelW * 3 + 150, 800)
        #设置图片宽高结束

        widget = QWidget()
        self.Vlayout = QVBoxLayout()  #整体布局
        self.Vlayout.setAlignment(Qt.AlignCenter)
        image_label = QLabel()
        image_label.setFixedSize(QSize(labelW, labelH))
        image_label.setPixmap(numpytoPixmap(self.image))

        self.firstImageLayout = QHBoxLayout()  #原图的布局
        self.firstImageLayout.addWidget(image_label)
        self.Vlayout.addLayout(self.firstImageLayout)  #插入原图布局
        self.buttonGroup = QButtonGroup()
        id = 0
        self.resultLayout = QHBoxLayout()  #四个结果图的布局
        for i, result in enumerate(self.candidateResults):
            # foreground = self.changeBackground1(result,self.image)
            # result = self.saveBoth1(result, trimaps[i])
            image_label = QLabel()
            image_label.setFixedSize(QSize(labelW, labelH))
            image_label.setPixmap(numpytoPixmap(self.image))
            final_label = QLabel()
            final_label.setFixedSize(QSize(labelW, labelH))
            final_label.setPixmap(numpytoPixmap(result))
            radioButton = QRadioButton(str(id))  # 创建单选按钮
            self.buttonGroup.addButton(radioButton, id)
            id += 1
            self.Hlayout = QVBoxLayout()  #一张图和一个radio的布局
            self.Hlayout.addWidget(final_label)
            self.Hlayout.addWidget(radioButton)
            self.resultLayout.addLayout(self.Hlayout)

        self.Vlayout.addLayout(self.resultLayout)  #插入四个结果布局

        #控制ok按钮逻辑
        self.button = QPushButton('OK')
        self.button.setFixedSize(QSize(100, 50))
        self.button.clicked.connect(self.select)
        self.btnLayout = QHBoxLayout()
        self.btnLayout.setAlignment(Qt.AlignCenter)
        self.btnLayout.addWidget(self.button)

        self.Vlayout.addLayout(self.btnLayout)  #插入按钮布局

        # scrollArea = QScrollArea()
        # scrollArea.setWidget(widget)
        # layout = QVBoxLayout(self)
        # layout.addWidget(scrollArea)
        self.setLayout(self.Vlayout)

        self.buttonGroup.button(0).setChecked(True)
        self.selectId = 0

    def select(self):
        self.selectId = self.buttonGroup.checkedId()
        self.accept()

    def saveBoth1(self, alpha, foreground):
        b_channel, g_channel, r_channel = cv2.split(foreground)
        a_channel = alpha.mean(axis=2)
        img_bgra = cv2.merge((b_channel, g_channel, r_channel, a_channel))
        return img_bgra
Exemple #8
0
        class MyWidget(QWidget):
            def __init__(self, parent):
                super().__init__(parent)
                self.buttons_id_value = {
                    1: ('comma', ','),
                    2: ('space', '\b'),
                    3: ('tab', '\t'),
                    4: ('semicolon', ';')
                }
                self.separator = QButtonGroup()
                lab = QLabel()
                lab.setText('Choose a separator:')
                for bid, value in self.buttons_id_value.items():
                    self.separator.addButton(QRadioButton(value[0]), id=bid)
                self.separator.setExclusive(True)
                self.default_button = self.separator.button(1)
                button_layout = QHBoxLayout()
                for button in self.separator.buttons():
                    button_layout.addWidget(button)
                self.default_button.click()

                openFileChooser = QPushButton('Choose')
                fileChooser = QFileDialog(self, 'Open csv', str(os.getcwd()),
                                          'Csv (*.csv *.tsv *.dat)')
                fileChooser.setFileMode(QFileDialog.ExistingFile)
                self.filePath = QLineEdit()
                openFileChooser.released.connect(fileChooser.show)
                fileChooser.fileSelected.connect(self.filePath.setText)
                self.filePath.textChanged.connect(self.checkFileExists)
                nameLabel = QLabel('Select a name:', self)
                self.nameField = QLineEdit(self)
                self.nameErrorLabel = QLabel(self)

                self.file_layout = QVBoxLayout()
                fileChooserLayout = QHBoxLayout()
                nameRowLayout = QHBoxLayout()
                fileChooserLayout.addWidget(openFileChooser)
                fileChooserLayout.addWidget(self.filePath)
                nameRowLayout.addWidget(nameLabel)
                nameRowLayout.addWidget(self.nameField)
                self.fileErrorLabel = QLabel(self)
                self.file_layout.addLayout(fileChooserLayout)
                self.file_layout.addWidget(self.fileErrorLabel)
                self.file_layout.addLayout(nameRowLayout)
                self.file_layout.addWidget(self.nameErrorLabel)
                self.fileErrorLabel.hide()
                self.nameErrorLabel.hide()
                self.tablePreview = SearchableAttributeTableWidget(self, True)
                self.tableSpinner = QtWaitingSpinner(
                    self.tablePreview,
                    centerOnParent=True,
                    disableParentWhenSpinning=True)
                self.nameField.textEdited.connect(self.nameErrorLabel.hide)

                # Split file by row
                splitRowLayout = QHBoxLayout()
                self.checkSplit = QCheckBox('Split file by rows', self)
                self.numberRowsChunk = QLineEdit(self)
                self.numberRowsChunk.setPlaceholderText(
                    'Number of rows per chunk')
                self.numberRowsChunk.setValidator(QIntValidator(self))
                splitRowLayout.addWidget(self.checkSplit)
                splitRowLayout.addWidget(self.numberRowsChunk)
                self.checkSplit.stateChanged.connect(self.toggleSplitRows)

                layout = QVBoxLayout()
                layout.addLayout(self.file_layout)
                layout.addWidget(lab)
                layout.addLayout(button_layout)
                layout.addLayout(splitRowLayout)
                layout.addWidget(QLabel('Preview'))
                layout.addWidget(self.tablePreview)
                self.setLayout(layout)

                self.filePath.textChanged.connect(self.loadPreview)
                self.separator.buttonClicked.connect(self.loadPreview)

            @Slot(object)
            def loadPreview(self) -> None:
                if not os.path.isfile(self.filePath.text()):
                    return

                class WorkerThread(QThread):
                    resultReady = Signal(Frame)

                    def __init__(self, path: str, separ: str, parent=None):
                        super().__init__(parent)
                        self.__path = path
                        self.__sep = separ

                    def run(self):
                        header = pd.read_csv(self.__path,
                                             sep=self.__sep,
                                             index_col=False,
                                             nrows=0)
                        self.resultReady.emit(Frame(header))

                sep: int = self.separator.checkedId()
                sep_s: str = self.buttons_id_value[sep][
                    1] if sep != -1 else None
                assert sep_s is not None

                # Async call to load header
                worker = WorkerThread(path=self.filePath.text(),
                                      separ=sep_s,
                                      parent=self)
                worker.resultReady.connect(self.onPreviewComputed)
                worker.finished.connect(worker.deleteLater)
                self.tableSpinner.start()
                worker.start()

            @Slot(Frame)
            def onPreviewComputed(self, header: Frame):
                self.tablePreview.setSourceFrameModel(FrameModel(self, header))
                self.tablePreview.model().setAllChecked(True)
                self.tableSpinner.stop()

            @Slot(str)
            def checkFileExists(self, path: str) -> None:
                file_exists = os.path.isfile(path)
                if not file_exists:
                    self.fileErrorLabel.setText('File does not exists!')
                    self.fileErrorLabel.setStyleSheet('color: red')
                    self.filePath.setToolTip('File does not exists!')
                    self.filePath.setStyleSheet('border: 1px solid red')
                    # self.file_layout.addWidget(self.fileErrorLabel)
                    self.parentWidget().disableOkButton()
                    self.fileErrorLabel.show()
                else:
                    # self.file_layout.removeWidget(self.fileErrorLabel)
                    self.fileErrorLabel.hide()
                    self.filePath.setStyleSheet('')
                    self.parentWidget().enableOkButton()
                    if not self.nameField.text():
                        name: str = os.path.splitext(os.path.basename(path))[0]
                        self.nameField.setText(name)

            @Slot(Qt.CheckState)
            def toggleSplitRows(self, state: Qt.CheckState) -> None:
                if state == Qt.Checked:
                    self.numberRowsChunk.setEnabled(True)
                else:
                    self.numberRowsChunk.setDisabled(True)

            def showNameError(self, msg: str) -> None:
                self.nameErrorLabel.setText(msg)
                self.nameErrorLabel.setStyleSheet('color: red')
                self.nameErrorLabel.show()
Exemple #9
0
class _RemoveNanEditor(AbsOperationEditor):
    _baseText = {
        0: 'Remove with more than: <b>{}</b> nan',
        1: 'Remove with more than: <b>{}%</b> nan'
    }

    def __init__(self, mode: str, parent: QWidget = None):
        """ Builds the editor

        :param mode: one of 'col' or 'row'
        :param parent: a parent widget

        """
        self.__mode: str = mode
        super().__init__(parent)

    def editorBody(self) -> QWidget:
        self.__group = QButtonGroup()
        self.__group.setExclusive(True)
        lab = QLabel('Choose how to remove:')
        self.__group.addButton(QRadioButton('By number'), id=0)
        self.__group.addButton(QRadioButton('By percentage'), id=1)
        self.__currId = None

        self.__summaryLabel = QLabel()
        self.__slider = QSlider(Qt.Horizontal, self)
        self.__slider.setMinimum(0)
        self.__slider.setTracking(True)
        self.__slider.setSingleStep(1)

        self.__numBox = QSpinBox()
        self.__numBox.setMinimum(0)
        self.__numBox.setMaximum(10000000)

        radioLayout = QHBoxLayout()
        radioLayout.addWidget(self.__group.button(0))
        radioLayout.addWidget(self.__group.button(1))
        self.__bodyLayout = QVBoxLayout()
        self.__bodyLayout.addWidget(lab)
        self.__bodyLayout.addLayout(radioLayout)
        self.__bodyLayout.addSpacing(20)
        self.__bodyLayout.addWidget(QLabel('Move the slider to set removal parameter:'))
        self.__bodyLayout.addSpacing(10)
        self.__bodyLayout.addWidget(self.__slider if self.__mode == 'row' else self.__numBox)
        self.__bodyLayout.addWidget(self.__summaryLabel)

        self.__group.buttonClicked[int].connect(self._toggleMode)
        # Both are connected, only one is shown
        self.__slider.valueChanged.connect(self._onValueChanged)
        self.__numBox.valueChanged[int].connect(self._onValueChanged)
        # Set a default button and label text
        self.__group.button(0).click()
        self.__summaryLabel.setText(self._baseText[0].format(self.__slider.minimum()))

        body = QWidget()
        body.setLayout(self.__bodyLayout)
        return body

    @Slot(int)
    def _toggleMode(self, bid: int) -> None:
        # NOTE: could be refactored
        if bid == self.__currId:
            return
        self.__currId = bid
        if bid == 0:
            if not (self.inputShapes and self.inputShapes[0]) and self.__mode == 'row':
                self.__slider.setDisabled(True)
                self._onValueChanged(self.__slider.value())
            elif not self.__slider.isEnabled():
                self.__slider.setEnabled(True)
            else:
                if self.__mode == 'row':
                    self.__slider.setMaximum(self.inputShapes[0].nColumns)
                    self._onValueChanged(self.__slider.value())
                else:
                    self.__bodyLayout.replaceWidget(self.__slider, self.__numBox)
                    self.__slider.hide()
                    self.__numBox.show()
                    self._onValueChanged(self.__numBox.value())
        else:
            if self.__mode == 'row':
                if not self.__slider.isEnabled():
                    self.__slider.setEnabled(True)
            else:
                self.__bodyLayout.replaceWidget(self.__numBox, self.__slider)
                self.__numBox.hide()
                self.__slider.show()
            self._onValueChanged(self.__slider.value())
            self.__slider.setMaximum(100)

    @Slot(int)
    def _onValueChanged(self, value: int):
        self.__summaryLabel.setText(self._baseText[self.__currId].format(value))

    def getOptions(self) -> Iterable:
        if self.__group.checkedId() == 0:
            # By number
            return None, self.__slider.value() if self.__mode == 'row' else self.__numBox.value()
        else:
            # By perc
            return self.__slider.value() / 100, None

    def setOptions(self, percentage: float, number: int) -> None:
        if percentage is not None:
            self.__group.button(1).click()
            self.__slider.setValue(percentage * 100)
        elif number is not None:
            self.__group.button(0).click()
            self.__slider.setValue(number) if self.__mode == 'row' else self.__numBox.setValue(number)
        else:
            # Both None
            self.__slider.setValue(0)
            self.__numBox.setValue(0)

    def refresh(self) -> None:
        if self.__mode == 'row' and self.__group.checkedId() == 0:
            self.__slider.setMaximum(self.inputShapes[0].nColumns)
            self.__slider.setEnabled(True)
Exemple #10
0
class ParserGUI(QWidget):
    def __init__(self, format_config):
        """The main GUI of the ref parser

        The class take output formats as an input
        """
        super().__init__()

        self.resize(800, 600)
        self.setWindowTitle("RefParse")
        self.init_layout(format_config)
        self.format_config = format_config
        self.api_object = None
        self.__threads = []

    def init_layout(self, format_config):
        """Initiate layouts

        For simplicity, the widget uses gridlayout with 2 columns
        """
        grid = QGridLayout()

        # - reference line
        self.ref_line = QLineEdit(self)
        grid.addWidget(QLabel("doi/arXiv:"), 0, 0)
        grid.addWidget(self.ref_line, 0, 1)

        # - search button
        self.search_btn = QPushButton("Search")
        self.search_btn.setShortcut("Return")
        self.search_btn.clicked.connect(self.access_reference)
        search = QHBoxLayout()
        search.addStretch(1)
        search.addWidget(self.search_btn)
        grid.addLayout(search, 1, 1)

        # - Output
        grid.addWidget(QLabel("Output:"), 2, 0)

        # - output format, set the first button to be true
        format_opt = QVBoxLayout()
        self.format_btns = QButtonGroup(self)
        self.format_btns.buttonClicked.connect(self.change_format)
        for format_type in format_config.keys():
            btn = QRadioButton(format_type, self)
            format_opt.addWidget(btn)
            self.format_btns.addButton(btn)
        # set first button checked
        self.format_btns.button(-2).setChecked(True)
        self.output_box = QTextEdit(self)

        grid.addLayout(format_opt, 3, 0, Qt.AlignTop)
        grid.addWidget(self.output_box, 3, 1)

        # - copy button
        self.copy_btn = QPushButton("Copy")
        self.copy_btn.clicked.connect(self.copy)
        copy = QHBoxLayout()
        copy.addStretch(1)
        copy.addWidget(self.copy_btn)
        grid.addLayout(copy, 4, 1)

        # - log box (use custom logging handler that stream log to text box)
        self.log_box = QTextEdit(self)
        self.log_box.setReadOnly(True)
        self.log_box.setStyleSheet("background-color: transparent;")
        # set the log box to half of the size
        self.log_box.setMaximumHeight(self.log_box.sizeHint().height() / 2)

        # custom signal handler
        log_handler = QLogHandler(self)
        log_handler.setFormatter(
            logging.Formatter("[%(levelname)s] %(name)s - %(message)s"))
        root_logger.addHandler(log_handler)
        log_handler.signal.log_str.connect(self.log_box.append)

        grid.addWidget(QLabel("Log:"), 5, 0, Qt.AlignTop)
        grid.addWidget(self.log_box, 5, 1)

        # setup the overall layout for the widget
        self.setLayout(grid)

        # debug shortcuts
        self.debug = QShortcut(QKeySequence("Shift+F8"), self)
        self.debug.activated.connect(self.toggle_debug)

    def access_reference(self):
        """Create thread to run the api

        When the search button is pressed, the contents is reset
        and a new thread is created to run the api object
        """

        self.reset_content()
        reference = self.ref_line.text()

        if reference:
            gui_logger.info(f"Search reference: {reference}")
            ref_thread = RefThread(reference, self.format_config, parent=self)
            ref_thread.response_obj.connect(self.output)
            ref_thread.start()

        else:
            gui_logger.warning(f"Please enter doi or arXiv ID")

    @Slot(object)
    def output(self, api_object):
        """Link the api object to GUI class

        The slot for the signal from access_reference
        store the api object to the GUI
        """
        self.api_object = api_object
        self.change_format()

    def change_format(self):
        """Test the group of button if it is checked"""
        ref_format = self.format_btns.checkedButton().text()
        self.output_box.clear()
        if self.api_object.status:
            gui_logger.debug(f"{ref_format} format")
            format_thread = FormatThread(self.api_object,
                                         ref_format,
                                         parent=self)
            format_thread.response_str.connect(self.update_output)
            format_thread.start()

    @Slot(str)
    def update_output(self, output_str):
        """Update output from stored parsed api object

        This is done by first checking which format button is clicked
        """
        self.output_box.setText(output_str)

    def reset_content(self):
        """Clear and reset the contents"""
        self.output_box.clear()
        self.log_box.clear()

    def copy(self):
        """Copy the output text"""
        clipboard = QApplication.clipboard()
        clipboard.setText(self.output_box.toPlainText())
        gui_logger.info("text copied to clipboard")

    def toggle_debug(self):
        """Secret short cut shift + F8 to switch to debug mode"""

        if root_logger.isEnabledFor(logging.DEBUG):
            gui_logger.info("switch to regular mode")
            root_logger.setLevel(logging.INFO)
        else:
            gui_logger.info("switch to debug mode")
            root_logger.setLevel(logging.DEBUG)

    def closeEvent(self, event):
        """Exit and wait for the thread to finish"""
        for thread in self.__threads:
            thread[0].quit()
            thread[0].wait()
Exemple #11
0
class AppearanceTab(QWidget):
    def __init__(self, parent):
        """Initialize the appearance tab
        """

        super(AppearanceTab, self).__init__(parent)

        self.prefs = parent.prefs

        # Default column width code
        col_width_label = QLabel("Default Column Width:")
        self.col_width_edit = QLineEdit(str(self.prefs['default_column_width']))
        self.col_width_edit.setMinimumWidth(40)
        self.col_width_edit.setMaximumWidth(100)
        validator = QIntValidator(10, 200, self)
        self.col_width_edit.setValidator(validator)

        # Visual style code
        style_label = QLabel("Visual Style of Application:")
        self.style_edit = QComboBox(self)
        self.style_edit.addItems(list(QStyleFactory.keys()))
        self.style_edit.setMaximumWidth(200)
        self.style_edit.setCurrentIndex(self.style_edit.findText(self.prefs['style']))

        # Units display code
        units_header_label = QLabel("Units Display:")
        self.header_units_check = QCheckBox('Show Units in Table Headers', self)
        checked_header = Qt.Checked if self.prefs['show_units_in_headers'] == 1 else Qt.Unchecked
        self.header_units_check.setCheckState(checked_header)
        self.cells_units_check = QCheckBox('Show Units in Table Cells', self)
        checked_cells = Qt.Checked if self.prefs['show_units_in_cells'] == 1 else Qt.Unchecked
        self.cells_units_check.setCheckState(checked_cells)

        # Handling of file options code
        default_handling_text = QLabel("Select how options saved directly within an IDF "
                                             "file are treated. See \"Save Options\" tab "
                                             "for the options in question.")
        default_handling_text.setWordWrap(True)
        default_handling_text.setMaximumWidth(450)
        self.button_force = QRadioButton("Force Session Options:", self)
        force_text = QLabel("Options from the current session will be used for all "
                                  "files, ignoring any options saved in the IDF file.")
        force_text.setWordWrap(True)
        force_text.setMaximumWidth(450)
        force_text.setMinimumHeight(30)
        force_text.setIndent(25)
        self.button_obey = QRadioButton("Obey IDF Options if Present:", self)
        obey_text = QLabel("Obey options saved in the IDF file. If none are "
                                 "present, use the current session's options.")
        obey_text.setWordWrap(True)
        obey_text.setMaximumWidth(450)
        obey_text.setMinimumHeight(30)
        obey_text.setIndent(25)

        # Handling of file options group box
        self.behaviour_group_box = QGroupBox("Handling of File-based Options")
        self.behaviour_group_box.setMinimumHeight(220)
        self.behaviour_group_box.setMinimumWidth(450)
        behaviour_box = QVBoxLayout()
        behaviour_box.addWidget(default_handling_text)
        behaviour_box.addWidget(self.button_force)
        behaviour_box.addWidget(force_text)
        behaviour_box.addSpacing(5)
        behaviour_box.addWidget(self.button_obey)
        behaviour_box.addWidget(obey_text)
        behaviour_box.addStretch(1)
        self.behaviour_group_box.setLayout(behaviour_box)

        self.behaviour_button_group = QButtonGroup(self)
        self.behaviour_button_group.addButton(self.button_force)
        self.behaviour_button_group.addButton(self.button_obey)
        self.behaviour_button_group.setId(self.button_force, 0)
        self.behaviour_button_group.setId(self.button_obey, 1)
        self.behaviour_button_group.button(self.prefs['obey_idf_options']).setChecked(True)

        # Main layout code
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(col_width_label)
        mainLayout.addWidget(self.col_width_edit)
        mainLayout.addSpacing(10)
        mainLayout.addWidget(style_label)
        mainLayout.addWidget(self.style_edit)
        mainLayout.addSpacing(10)
        mainLayout.addWidget(self.behaviour_group_box)
        mainLayout.addSpacing(10)
        mainLayout.addWidget(units_header_label)
        mainLayout.addWidget(self.header_units_check)
        mainLayout.addWidget(self.cells_units_check)
        mainLayout.addStretch(1)
        self.setLayout(mainLayout)

        # Update settings
        self.behaviour_button_group.buttonClicked.connect(self.update)
        self.col_width_edit.textChanged.connect(self.update)
        self.style_edit.currentIndexChanged.connect(self.update)
        self.header_units_check.stateChanged.connect(self.update)
        self.cells_units_check.stateChanged.connect(self.update)

    def update(self):
        self.prefs['default_column_width'] = self.col_width_edit.text()
        self.prefs['style'] = self.style_edit.currentText()
        self.prefs['obey_idf_options'] = self.behaviour_button_group.checkedId()
        self.prefs['show_units_in_headers'] = 1 if self.header_units_check.checkState() else 0
        self.prefs['show_units_in_cells'] = 1 if self.cells_units_check.checkState() else 0