Header image for PyQT

PyQT

For Calibre

Prompt

Looking to make a GUI for this config with pyqt 5/6. Will be opened by pressing a button. Modal. ``` # config.py from dataclasses import dataclass from qt.core import ( QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, QSpinBox, QHBoxLayout, QComboBox, QColor, QFrame, ) from calibre.utils.config import JSONConfig # This is where all preferences for this plugin will be stored prefs = JSONConfig("plugins/portal_view") # Set defaults prefs.defaults["last_portal_library"] = "" prefs.defaults["min_partial_title_length"] = 8 prefs.defaults["color_scheme"] = "default" @dataclass(frozen=True) class ColorScheme: """Color palette for Portal row tinting by mode and hidden status.""" name: str label: str mode_1: QColor # queued / in-progress mode_2: QColor # success / completed mode_3: QColor # error / failure hidden_tint: QColor # light tint for hidden rows # ── colour schemes ──────────────────────────────────────────────────── COLOR_SCHEMES: dict[str, ColorScheme] = { "default": ColorScheme( name="default", label="Default", mode_1=QColor("#ff0"), # yellow mode_2=QColor("#10B981"), # emerald green mode_3=QColor("#EF4444"), # red hidden_tint=QColor("#ffebeb"), # light red ), "color_blind_safe": ColorScheme( name="color_blind_safe", label="Color-Blind Safe", # Wong (2011) Nature Methods palette β€” discriminable under # protanopia, deuteranopia, and tritanopia. mode_1=QColor("#E69F00"), # orange mode_2=QColor("#56B4E9"), # sky blue mode_3=QColor("#CC79A7"), # reddish purple hidden_tint=QColor("#e6f0ff"), # light blue ), } def get_color_scheme() -> ColorScheme: """Return the currently selected :class:`ColorScheme`.""" name: str = prefs.get("color_scheme", "default") return COLOR_SCHEMES.get(name, COLOR_SCHEMES["default"]) def get_mode_colors() -> dict[int, QColor]: """Return ``{mode: QColor}`` for the active scheme (1=queued, 2=success, 3=error).""" current_scheme = get_color_scheme() return { 1: current_scheme.mode_1, 2: current_scheme.mode_2, 3: current_scheme.mode_3, } def get_hidden_tint() -> QColor: """Return the background tint for hidden rows in the active scheme.""" return get_color_scheme().hidden_tint class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.layout = QVBoxLayout() self.setLayout(self.layout) # Default portal library self.default_library_label = QLabel("Default Portal Library:") self.layout.addWidget(self.default_library_label) self.library_layout = QVBoxLayout() self.library_path = QLineEdit(self) self.library_path.setText(prefs["last_portal_library"]) self.library_path.setReadOnly(True) self.library_layout.addWidget(self.library_path) self.library_button = QPushButton("Choose Library...", self) self.library_button.clicked.connect(self.choose_library) self.library_layout.addWidget(self.library_button) self.layout.addLayout(self.library_layout) # Partial title match length self.partial_length_label = QLabel( "Minimum title length for partial-title matching:" ) self.layout.addWidget(self.partial_length_label) self.partial_length_row = QHBoxLayout() self.partial_length_spin = QSpinBox(self) self.partial_length_spin.setMinimum(1) self.partial_length_spin.setMaximum(50) self.partial_length_spin.setValue(prefs["min_partial_title_length"]) self.partial_length_spin.setToolTip( "Titles shorter than this are excluded from partial-title " "comparisons (Unique Titles portal type)." ) self.partial_length_row.addWidget(self.partial_length_spin) self.partial_length_row.addStretch() self.layout.addLayout(self.partial_length_row) # ── color scheme ──────────────────────────────────────────── self.color_scheme_label = QLabel("Portal row color scheme:") self.layout.addWidget(self.color_scheme_label) self.color_scheme_combo = QComboBox(self) for scheme in COLOR_SCHEMES.values(): self.color_scheme_combo.addItem(scheme.label, scheme.name) current_name = prefs.get("color_scheme", "default") idx = self.color_scheme_combo.findData(current_name) if idx >= 0: self.color_scheme_combo.setCurrentIndex(idx) self.color_scheme_combo.setToolTip( "Choose the color palette for portal row highlighting.\n" "'Color-Blind Safe' uses a palette distinguishable across all\n" "common forms of color vision deficiency." ) self.layout.addWidget(self.color_scheme_combo) # ── color swatches ───────────────────────────────────────── self._swatches: list[QLabel] = [] swatch_row = QHBoxLayout() swatch_row.setContentsMargins(0, 4, 0, 0) for label_text in ("Queued", "Success", "Error", "Hidden"): chip = QLabel(self) chip.setFixedSize(32, 14) chip.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Plain) chip.setToolTip(label_text) self._swatches.append(chip) swatch_row.addWidget(chip) swatch_row.addWidget(QLabel(label_text, self)) swatch_row.addSpacing(10) swatch_row.addStretch() self.layout.addLayout(swatch_row) self.color_scheme_combo.currentIndexChanged.connect(self._update_swatches) self._update_swatches() # Add a label with usage instructions self.layout.addSpacing(10) self.help_label = QLabel( "The Portal View plugin allows you to view a second library\n" "in read-only mode alongside your main library.\n\n" "Select a default library above to automatically load it\n" "when opening the portal view." ) self.layout.addWidget(self.help_label) def choose_library(self): from calibre.gui2.dialogs.choose_library import ChooseLibrary d = ChooseLibrary( self, "Choose Default Portal Library", "Select the default library to show in portal view", default_library_path=prefs["last_portal_library"], ) d.exec() if d.library_path: self.library_path.setText(d.library_path) def save_settings(self): prefs["last_portal_library"] = self.library_path.text() prefs["min_partial_title_length"] = self.partial_length_spin.value() prefs["color_scheme"] = self.color_scheme_combo.currentData() def _update_swatches(self): """Recolor the swatch chips to match the active scheme.""" name = self.color_scheme_combo.currentData() or "default" scheme = COLOR_SCHEMES.get(name, COLOR_SCHEMES["default"]) colors = (scheme.mode_1, scheme.mode_2, scheme.mode_3, scheme.hidden_tint) for chip, color in zip(self._swatches, colors): chip.setStyleSheet( f"background-color: {color.name()}; border: 1px solid #888;" ) ```

Drag to resize
Drag to resize
Drag to resize