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)
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()
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
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)
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
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()
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)
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()
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