certflow.views.common.theme_manager 源代码

# src/certflow/views/common/theme_manager.py
"""主题管理器

提供应用级别的浅色/深色主题切换引擎。
通过 QApplication.setStyleSheet() 全局替换样式表实现即时切换,
无需重启应用。

架构层次:
    Controller (SettingsController) → View/Common (ThemeManager) → QApplication

Usage:
    manager = ThemeManager(app)
    manager.apply_theme("dark")   # 切换到深色
    manager.apply_theme("light")  # 切换到浅色
"""

from __future__ import annotations

from PySide6.QtCore import QObject
from PySide6.QtWidgets import QApplication

from certflow.utils.logger import logger


[文档] class ThemeManager(QObject): """主题管理器 — 浅色/深色主题切换 维护两套完整的 QSS (Qt Style Sheets),通过替换 application-level stylesheet 实现全局主题切换。 """ # ============================================================ # 浅色主题 QSS # ============================================================ LIGHT_THEME = """ /* ===== 全局 ===== */ QMainWindow, QDialog, QWidget { background-color: #f5f6fa; color: #2c3e50; font-family: "Microsoft YaHei", "SimHei", sans-serif; } /* ===== 导航栏 ===== */ QWidget#navBar { background-color: #2c3e50; } QWidget#navBar QPushButton#navLogo { background-color: transparent; color: #ecf0f1; border: none; padding: 8px 16px; font-weight: bold; font-size: 16px; } QWidget#navBar QPushButton { background-color: transparent; color: #ecf0f1; border: none; padding: 8px 16px; font-size: 14px; } QWidget#navBar QPushButton:hover { background-color: #34495e; } QWidget#navBar QPushButton:checked { background-color: #1abc9c; color: white; } QWidget#navBar QPushButton#themeToggleBtn { background-color: transparent; color: #ecf0f1; border: 1px solid #4a6278; border-radius: 4px; padding: 4px 12px; font-size: 16px; } QWidget#navBar QPushButton#themeToggleBtn:hover { background-color: #34495e; } /* ===== 主按钮 ===== */ QPushButton { background-color: #2c7be5; color: white; border: none; padding: 6px 16px; border-radius: 4px; font-size: 13px; } QPushButton:hover { background-color: #1a68d1; } QPushButton:pressed { background-color: #1557b0; } QPushButton:disabled { background-color: #adb5bd; color: #6c757d; } /* ===== 次要按钮 ===== */ QPushButton[secondary="true"] { background-color: #6c757d; color: white; } QPushButton[secondary="true"]:hover { background-color: #5a6268; } /* ===== 成功按钮 ===== */ QPushButton[success="true"] { background-color: #28a745; color: white; } QPushButton[success="true"]:hover { background-color: #218838; } /* ===== 输入框 ===== */ QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox, QDateEdit { background-color: white; color: #2c3e50; border: 1px solid #ced4da; border-radius: 4px; padding: 4px 8px; font-size: 13px; } QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus { border-color: #2c7be5; } QComboBox::drop-down { border: none; padding-right: 8px; } QComboBox QAbstractItemView { background-color: white; color: #2c3e50; selection-background-color: #2c7be5; selection-color: white; } /* ===== 表格 ===== */ QTableWidget, QTableView { background-color: white; alternate-background-color: #f8f9fa; color: #2c3e50; gridline-color: #dee2e6; border: 1px solid #dee2e6; font-size: 13px; selection-background-color: #cce5ff; selection-color: #2c3e50; } QHeaderView::section { background-color: #e9ecef; color: #495057; padding: 6px; border: 1px solid #dee2e6; font-weight: bold; } /* ===== 分组框 ===== */ QGroupBox { border: 1px solid #dee2e6; border-radius: 6px; margin-top: 8px; padding-top: 16px; font-weight: bold; color: #495057; } QGroupBox::title { subcontrol-origin: margin; left: 12px; padding: 0 6px; } /* ===== 标签页 ===== */ QTabWidget::pane { border: 1px solid #dee2e6; background-color: white; } QTabBar::tab { background-color: #e9ecef; color: #495057; padding: 8px 20px; border: 1px solid #dee2e6; border-bottom: none; border-top-left-radius: 4px; border-top-right-radius: 4px; } QTabBar::tab:selected { background-color: white; color: #2c7be5; font-weight: bold; } QTabBar::tab:hover:!selected { background-color: #dee2e6; } /* ===== 滚动条 ===== */ QScrollBar:vertical { background: #f5f6fa; width: 10px; border-radius: 5px; } QScrollBar::handle:vertical { background: #ced4da; border-radius: 5px; min-height: 20px; } QScrollBar::handle:vertical:hover { background: #adb5bd; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0; } /* ===== 复选框/单选框 ===== */ QCheckBox, QRadioButton { color: #2c3e50; spacing: 6px; } QCheckBox::indicator { width: 16px; height: 16px; } /* ===== 标签 ===== */ QLabel { color: #2c3e50; } /* ===== 进度条 ===== */ QProgressBar { background-color: #e9ecef; border: none; border-radius: 4px; height: 8px; text-align: center; } QProgressBar::chunk { background-color: #2c7be5; border-radius: 4px; } /* ===== 树形控件 ===== */ QTreeWidget { background-color: white; alternate-background-color: #f8f9fa; color: #2c3e50; border: 1px solid #dee2e6; } QTreeWidget::item:selected { background-color: #cce5ff; color: #2c3e50; } /* ===== 状态栏 ===== */ QStatusBar { background-color: #e9ecef; color: #6c757d; } """ # ============================================================ # 深色主题 QSS # ============================================================ DARK_THEME = """ /* ===== 全局 ===== */ QMainWindow, QDialog, QWidget { background-color: #1e1e2e; color: #cdd6f4; font-family: "Microsoft YaHei", "SimHei", sans-serif; } /* ===== 导航栏 ===== */ QWidget#navBar { background-color: #11111b; } QWidget#navBar QPushButton#navLogo { background-color: transparent; color: #bac2de; border: none; padding: 8px 16px; font-weight: bold; font-size: 16px; } QWidget#navBar QPushButton { background-color: transparent; color: #bac2de; border: none; padding: 8px 16px; font-size: 14px; } QWidget#navBar QPushButton:hover { background-color: #313244; } QWidget#navBar QPushButton:checked { background-color: #89b4fa; color: #1e1e2e; } QWidget#navBar QPushButton#themeToggleBtn { background-color: transparent; color: #bac2de; border: 1px solid #45475a; border-radius: 4px; padding: 4px 12px; font-size: 16px; } QWidget#navBar QPushButton#themeToggleBtn:hover { background-color: #313244; } /* ===== 主按钮 ===== */ QPushButton { background-color: #89b4fa; color: #1e1e2e; border: none; padding: 6px 16px; border-radius: 4px; font-size: 13px; } QPushButton:hover { background-color: #74a8f7; } QPushButton:pressed { background-color: #5e9cf5; } QPushButton:disabled { background-color: #45475a; color: #6c7086; } /* ===== 次要按钮 ===== */ QPushButton[secondary="true"] { background-color: #585b70; color: #cdd6f4; } QPushButton[secondary="true"]:hover { background-color: #6c7086; } /* ===== 成功按钮 ===== */ QPushButton[success="true"] { background-color: #a6e3a1; color: #1e1e2e; } QPushButton[success="true"]:hover { background-color: #94e2d5; } /* ===== 输入框 ===== */ QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox, QDateEdit { background-color: #313244; color: #cdd6f4; border: 1px solid #45475a; border-radius: 4px; padding: 4px 8px; font-size: 13px; } QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus { border-color: #89b4fa; } QComboBox::drop-down { border: none; padding-right: 8px; } QComboBox QAbstractItemView { background-color: #313244; color: #cdd6f4; selection-background-color: #89b4fa; selection-color: #1e1e2e; } /* ===== 表格 ===== */ QTableWidget, QTableView { background-color: #1e1e2e; alternate-background-color: #313244; color: #cdd6f4; gridline-color: #45475a; border: 1px solid #45475a; font-size: 13px; selection-background-color: #45475a; selection-color: #cdd6f4; } QHeaderView::section { background-color: #313244; color: #bac2de; padding: 6px; border: 1px solid #45475a; font-weight: bold; } /* ===== 分组框 ===== */ QGroupBox { border: 1px solid #45475a; border-radius: 6px; margin-top: 8px; padding-top: 16px; font-weight: bold; color: #bac2de; } QGroupBox::title { subcontrol-origin: margin; left: 12px; padding: 0 6px; } /* ===== 标签页 ===== */ QTabWidget::pane { border: 1px solid #45475a; background-color: #1e1e2e; } QTabBar::tab { background-color: #313244; color: #6c7086; padding: 8px 20px; border: 1px solid #45475a; border-bottom: none; border-top-left-radius: 4px; border-top-right-radius: 4px; } QTabBar::tab:selected { background-color: #1e1e2e; color: #89b4fa; font-weight: bold; } QTabBar::tab:hover:!selected { background-color: #45475a; } /* ===== 滚动条 ===== */ QScrollBar:vertical { background: #1e1e2e; width: 10px; border-radius: 5px; } QScrollBar::handle:vertical { background: #45475a; border-radius: 5px; min-height: 20px; } QScrollBar::handle:vertical:hover { background: #585b70; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0; } /* ===== 复选框/单选框 ===== */ QCheckBox, QRadioButton { color: #cdd6f4; spacing: 6px; } QCheckBox::indicator { width: 16px; height: 16px; } /* ===== 标签 ===== */ QLabel { color: #cdd6f4; } /* ===== 进度条 ===== */ QProgressBar { background-color: #313244; border: none; border-radius: 4px; height: 8px; text-align: center; } QProgressBar::chunk { background-color: #89b4fa; border-radius: 4px; } /* ===== 树形控件 ===== */ QTreeWidget { background-color: #1e1e2e; alternate-background-color: #313244; color: #cdd6f4; border: 1px solid #45475a; } QTreeWidget::item:selected { background-color: #45475a; color: #cdd6f4; } /* ===== 状态栏 ===== */ QStatusBar { background-color: #313244; color: #6c7086; } """ def __init__(self, app: QApplication, parent: QObject | None = None) -> None: """初始化主题管理器 Args: app: QApplication 实例 parent: Qt 父对象 """ super().__init__(parent) self._app = app self._current_theme: str | None = None @property def current_theme(self) -> str: """当前主题名称""" return self._current_theme
[文档] def apply_theme(self, theme: str) -> None: """应用指定主题 Args: theme: 主题名称,可选 "light" 或 "dark" Returns: None: 切换成功时更新 _current_theme 并替换全局样式表 """ if theme not in ("light", "dark"): logger.warning(f"未知主题: {theme},使用 light") theme = "light" if theme == self._current_theme: return qss = self.LIGHT_THEME if theme == "light" else self.DARK_THEME self._app.setStyleSheet(qss) self._current_theme = theme logger.info(f"主题已应用: {theme}")