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}")