# styles.py """界面样式集中管理模块:PySide6 QSS 样式表。 颜色主题:品牌蓝 #0960D1,主背景 #F8FAFC,成功绿 #0F955D。 通过 apply_app_style(app) 应用全局 QSS 样式,并拿到配色字典。 """ from PySide6.QtWidgets import QApplication from PySide6.QtGui import QPalette, QColor # ========================================== # 统一配色架构(与 Tkinter 版本保持一致) # ========================================== COLORS = { "BG_COLOR": "#F8FAFC", # 浅色大背景(微冷超浅灰蓝) "CARD_BG": "#FFFFFF", # 容器卡片背景 "BORDER_COLOR": "#E2E8F0", # 扁平化细边框 "TEXT_MAIN": "#1A1A1A", # 主文字颜色(高清晰度深灰) "TEXT_MUTED": "#757575", # 辅助文字颜色(中灰) "ACCENT_LIGHT": "#0960D1", # 主品牌色/高亮蓝 "ACCENT_DARK": "#0960D1", # 品牌色(统一) "HOVER_BLUE": "#0856B8", # 悬停过渡色(品牌色加深) "SUCCESS_GREEN": "#0F955D", # 连接成功绿 "SUCCESS_ACTIVE": "#0D8250", # 成功按钮按下态 "DISABLED": "#CBD5E1", # 禁用态灰 "ERROR_RED": "#FF2424", # 状态警示红 "NAV_BG": "#FFFFFF", # 导航栏背景(白色) "NAV_BORDER": "#E2E8F0", # 导航栏底部边框 "NAV_TAB_ACTIVE_TEXT": "#0960D1", # Tab激活态文字色 "NAV_TAB_HOVER": "#F1F5F9", # Tab悬停背景 "FORM_LABEL": "#333333", # 表单标签色 "REFRESH_BORDER": "#CCDBF0", # 刷新按钮边框 } # 科技蓝核心高亮统一为浅色主题色 COLORS["ACCENT_BLUE"] = COLORS["ACCENT_LIGHT"] # 字体配置 BASE_FONT_FAMILY = "Microsoft YaHei, PingFang SC, SimHei, sans-serif" BASE_FONT_SIZE = "14px" BASE_FONT_SIZE_SM = "12px" BASE_FONT_SIZE_LG = "16px" BASE_FONT_SIZE_XL = "28px" BASE_FONT_SIZE_NAV_TITLE = "22px" QSS_STYLESHEET = f""" /* ===== 全局默认 ===== */ QMainWindow, QWidget {{ background-color: {COLORS["BG_COLOR"]}; color: {COLORS["TEXT_MAIN"]}; font-family: "{BASE_FONT_FAMILY}"; font-size: {BASE_FONT_SIZE}; }} /* ===== 标题行(Layer 1:纯白背景) ===== */ QWidget[cssClass="titleRow"] {{ background-color: #FFFFFF; }} /* ===== Tab 栏容器(Layer 2:一体化浅灰背景 #F8FAFC) ===== */ QWidget[cssClass="tabRow"] {{ background-color: #F8FAFC; }} /* ===== QTabBar 导航标签 ===== */ QTabBar[cssClass="mainTab"]::tab {{ background: transparent; padding: 10px 24px 10px 16px; font-size: 15px; color: #64748B; border: none; border-bottom: 3px solid transparent; font-weight: normal; }} QTabBar[cssClass="mainTab"]::tab:selected {{ color: #0960D1; font-weight: bold; border-bottom: 3px solid #0960D1; }} QTabBar[cssClass="mainTab"]::tab:hover:!selected {{ color: #0960D1; }} /* ===== QGroupBox ===== */ QGroupBox {{ background-color: {COLORS["CARD_BG"]}; border: 1px solid {COLORS["BORDER_COLOR"]}; border-radius: 12px; margin-top: 14px; padding: 16px 12px 12px 12px; font-weight: bold; color: {COLORS["TEXT_MAIN"]}; font-size: {BASE_FONT_SIZE_LG}; }} QGroupBox::title {{ subcontrol-origin: margin; subcontrol-position: top left; padding: 0 10px; color: {COLORS["ACCENT_DARK"]}; font-weight: bold; font-size: {BASE_FONT_SIZE_LG}; background-color: transparent; }} /* ===== Section 卡片(替代 QGroupBox 的轻量方案) ===== */ QFrame[cssClass="sectionCard"] {{ background-color: {COLORS["CARD_BG"]}; border: 1px solid {COLORS["BORDER_COLOR"]}; border-radius: 12px; }} /* ===== Section 标题 ===== */ QLabel[cssClass="sectionTitle"] {{ color: {COLORS["TEXT_MAIN"]}; font-weight: bold; font-size: {BASE_FONT_SIZE_LG}; background-color: transparent; }} /* Section 标题左侧蓝色竖线(4px x 16px) */ QWidget[cssClass="sectionAccent"] {{ background-color: {COLORS["ACCENT_LIGHT"]}; border-radius: 2px; }} /* ===== 表单标签 ===== */ QLabel[cssClass="formLabel"] {{ color: {COLORS["FORM_LABEL"]}; font-size: 14px; font-weight: bold; background-color: transparent; min-width: 140px; }} /* ===== QPushButton 基础 ===== */ QPushButton {{ background-color: {COLORS["ACCENT_BLUE"]}; color: white; border: none; border-radius: 6px; padding: 9px 20px; font-weight: bold; font-size: {BASE_FONT_SIZE}; }} QPushButton:hover {{ background-color: {COLORS["HOVER_BLUE"]}; }} QPushButton:pressed {{ background-color: {COLORS["ACCENT_DARK"]}; }} QPushButton:disabled {{ background-color: {COLORS["DISABLED"]}; color: #94A3B8; }} /* 主操作按钮(绿色 - 连接设备) */ QPushButton[cssClass="action"] {{ background-color: {COLORS["SUCCESS_GREEN"]}; border: none; color: #FFFFFF; padding: 8px 24px; font-size: 14px; font-weight: bold; border-radius: 6px; }} QPushButton[cssClass="action"]:hover {{ background-color: #0D8250; }} QPushButton[cssClass="action"]:pressed {{ background-color: {COLORS["SUCCESS_ACTIVE"]}; }} /* 次要按钮 / 刷新按钮 */ QPushButton[cssClass="refresh"] {{ background-color: #FFFFFF; border: 1px solid {COLORS["REFRESH_BORDER"]}; color: {COLORS["ACCENT_LIGHT"]}; padding: 8px 20px; font-size: 14px; font-weight: bold; border-radius: 6px; }} QPushButton[cssClass="refresh"]:hover {{ background-color: #F1F5F9; border-color: {COLORS["ACCENT_LIGHT"]}; }} QPushButton[cssClass="refresh"]:pressed {{ background-color: #E2E8F0; }} /* ===== 底部操作按钮(ID 选择器,精确控制高度与内边距,修复文字截断) ===== */ QPushButton#refresh_btn {{ background-color: #FFFFFF; border: 1px solid #CCDBF0; color: #0960D1; border-radius: 6px; min-height: 34px; max-height: 34px; padding: 0px 20px; font-weight: bold; font-size: 14px; outline: none; }} QPushButton#refresh_btn:hover {{ background-color: #F0F4FA; }} QPushButton#refresh_btn:focus {{ outline: none; }} QPushButton#connect_btn {{ background-color: #0F955D; border: none; color: #FFFFFF; border-radius: 6px; min-height: 34px; max-height: 34px; padding: 0px 24px; font-weight: bold; font-size: 14px; outline: none; }} QPushButton#connect_btn:hover {{ background-color: #0D8250; }} QPushButton#connect_btn:focus {{ outline: none; }} /* 危险按钮(红色,用于停止) */ QPushButton[cssClass="danger"] {{ background-color: #EF4444; border: none; color: #FFFFFF; padding: 8px 24px; font-size: 14px; font-weight: bold; border-radius: 6px; }} QPushButton[cssClass="danger"]:hover {{ background-color: #DC2626; }} QPushButton[cssClass="danger"]:pressed {{ background-color: #B91C1C; }} /* ===== QLineEdit 输入框 ===== */ QLineEdit {{ background-color: #FFFFFF; border: 1px solid {COLORS["BORDER_COLOR"]}; border-radius: 6px; padding-left: 12px; color: #333333; font-size: 14px; min-height: 36px; max-height: 36px; }} QLineEdit:focus {{ border: 1px solid {COLORS["ACCENT_LIGHT"]}; }} QLineEdit:hover:!focus {{ border-color: #94A3B8; }} QLineEdit:disabled {{ background-color: #F1F5F9; color: {COLORS["TEXT_MUTED"]}; border-color: #E2E8F0; }} /* ===== QComboBox 下拉框(增强鲁棒性,防止打包后黑底) ===== */ QComboBox {{ background-color: #FFFFFF; border: 1px solid {COLORS["BORDER_COLOR"]}; border-radius: 6px; padding: 7px 10px; color: {COLORS["TEXT_MAIN"]}; font-size: 14px; min-width: 100px; min-height: 36px; max-height: 36px; outline: none; }} QComboBox:hover {{ border-color: #94A3B8; }} QComboBox:focus {{ border-color: {COLORS["ACCENT_LIGHT"]}; border-width: 1px; }} QComboBox:disabled {{ background-color: #F1F5F9; color: {COLORS["TEXT_MUTED"]}; border-color: #E2E8F0; }} QComboBox::drop-down {{ subcontrol-origin: padding; subcontrol-position: top right; width: 28px; border: none; border-left: 1px solid {COLORS["BORDER_COLOR"]}; border-top-right-radius: 6px; border-bottom-right-radius: 6px; background-color: #F8FAFC; }} QComboBox::down-arrow {{ width: 12px; height: 12px; }} /* 下拉弹出视图(最关键的修复点——保证白色背景) */ QComboBox QAbstractItemView {{ background-color: #FFFFFF; border: 1px solid {COLORS["BORDER_COLOR"]}; border-radius: 4px; selection-background-color: {COLORS["ACCENT_LIGHT"]}; selection-color: #FFFFFF; color: {COLORS["TEXT_MAIN"]}; font-size: {BASE_FONT_SIZE}; padding: 4px; outline: none; }} QComboBox QAbstractItemView::item {{ padding: 6px 10px; border-radius: 3px; color: {COLORS["TEXT_MAIN"]}; background-color: #FFFFFF; }} QComboBox QAbstractItemView::item:selected {{ background-color: {COLORS["ACCENT_LIGHT"]}; color: #FFFFFF; }} QComboBox QAbstractItemView::item:hover {{ background-color: #EFF6FF; }} /* 防止下拉滚动条区域也变黑 */ QComboBox QAbstractScrollArea {{ background-color: #FFFFFF; color: {COLORS["TEXT_MAIN"]}; }} /* ===== QRadioButton / QCheckBox ===== */ QRadioButton, QCheckBox {{ background-color: transparent; color: {COLORS["TEXT_MAIN"]}; font-size: {BASE_FONT_SIZE}; spacing: 8px; }} QRadioButton::indicator {{ width: 18px; height: 18px; border-radius: 9px; border: 2px solid {COLORS["BORDER_COLOR"]}; background-color: #FFFFFF; }} QRadioButton::indicator:checked {{ background-color: {COLORS["ACCENT_BLUE"]}; border-color: {COLORS["ACCENT_BLUE"]}; }} QRadioButton::indicator:hover {{ border-color: {COLORS["ACCENT_BLUE"]}; }} QRadioButton::indicator:checked:hover {{ background-color: {COLORS["ACCENT_DARK"]}; border-color: {COLORS["ACCENT_DARK"]}; }} QRadioButton:disabled {{ color: {COLORS["DISABLED"]}; }} QRadioButton::indicator:disabled {{ background-color: #F1F5F9; border-color: {COLORS["DISABLED"]}; }} QCheckBox::indicator {{ width: 18px; height: 18px; border-radius: 4px; border: 2px solid {COLORS["BORDER_COLOR"]}; background-color: #FFFFFF; }} QCheckBox::indicator:checked {{ background-color: {COLORS["ACCENT_BLUE"]}; border-color: {COLORS["ACCENT_BLUE"]}; }} QCheckBox::indicator:hover {{ border-color: {COLORS["ACCENT_BLUE"]}; }} QCheckBox::indicator:checked:hover {{ background-color: {COLORS["ACCENT_DARK"]}; border-color: {COLORS["ACCENT_DARK"]}; }} QCheckBox:disabled {{ color: {COLORS["DISABLED"]}; }} QCheckBox::indicator:disabled {{ background-color: #F1F5F9; border-color: {COLORS["DISABLED"]}; }} /* ===== 仪表板卡片 ===== */ QFrame[cssClass="dashboardCard"] {{ background-color: {COLORS["CARD_BG"]}; border: 1px solid {COLORS["BORDER_COLOR"]}; border-radius: 12px; padding: 12px; }} QFrame[cssClass="dashboardCard"] QLabel {{ background-color: transparent; }} QLabel[cssClass="dashboardLabel"] {{ color: {COLORS["TEXT_MUTED"]}; font-size: {BASE_FONT_SIZE_SM}; font-weight: normal; }} QLabel[cssClass="dashboardValue"] {{ font-family: "SF Mono, Menlo, Consolas, monospace"; font-size: {BASE_FONT_SIZE_XL}; font-weight: bold; }} /* ===== 底部状态栏 ===== */ QWidget[cssClass="bottomBar"] {{ background-color: #FFFFFF; border-top: 1px solid {COLORS["BORDER_COLOR"]}; }} QLabel[cssClass="logLabel"] {{ color: #757575; font-family: "SF Mono, Consolas, Menlo, monospace"; font-size: 12px; background-color: transparent; }} QLabel[cssClass="statusLabel"] {{ font-weight: bold; font-size: 14px; background-color: transparent; }} /* ===== 透明背景容器(避免 inline stylesheet 覆盖子控件 QSS) ===== */ QWidget[cssClass="transparentBg"] {{ background-color: transparent; }} /* ===== QStackedWidget 页面 ===== */ QWidget[cssClass="tabPage"] {{ background-color: #FFFFFF; }} /* ===== 分组辅助标签 ===== */ QLabel[cssClass="section"] {{ color: {COLORS["ACCENT_DARK"]}; font-weight: bold; font-size: {BASE_FONT_SIZE_LG}; background-color: transparent; }} /* ===== 导航栏标题 ===== */ QLabel[cssClass="navTitle"] {{ color: {COLORS["TEXT_MAIN"]}; font-size: 22px; font-weight: bold; background-color: transparent; letter-spacing: 0px; }} QLabel[cssClass="navSubtitle"] {{ color: {COLORS["ACCENT_DARK"]}; font-size: 11px; font-weight: normal; background-color: transparent; }} QLabel[cssClass="navDivider"] {{ color: {COLORS["BORDER_COLOR"]}; background-color: transparent; }} /* 设置按钮(圆形,右上角) */ QPushButton[cssClass="navSettings"] {{ background-color: transparent; border: 1.5px solid {COLORS["BORDER_COLOR"]}; border-radius: 18px; padding: 4px; min-width: 36px; max-width: 36px; min-height: 36px; max-height: 36px; }} QPushButton[cssClass="navSettings"]:hover {{ background-color: {COLORS["BG_COLOR"]}; border-color: #CBD5E1; }} QPushButton[cssClass="navSettings"]:pressed {{ background-color: #E2E8F0; }} """ def apply_app_style(app: QApplication): """配置全局 QSS 样式表与窗口默认调色板,返回配色字典供布局复用。""" # ---- 强制使用 Fusion 风格(跨平台一致,避免 Windows 原生风格/深色模式干扰 QSS) ---- app.setStyle("Fusion") # 应用 QSS 样式表 app.setStyleSheet(QSS_STYLESHEET) # 设置默认字体 font = app.font() font.setFamily(BASE_FONT_FAMILY.split(",")[0].strip().strip('"')) font.setPointSize(10) app.setFont(font) # 配置默认调色板(仅设置 Window/Base 等基础角色,不污染 Button/ComboBox) palette = QPalette() palette.setColor(QPalette.Window, QColor(COLORS["BG_COLOR"])) palette.setColor(QPalette.WindowText, QColor(COLORS["TEXT_MAIN"])) palette.setColor(QPalette.Base, QColor("#FFFFFF")) palette.setColor(QPalette.Text, QColor(COLORS["TEXT_MAIN"])) palette.setColor(QPalette.Button, QColor("#FFFFFF")) # 白色底,避免黑色 palette.setColor(QPalette.ButtonText, QColor(COLORS["TEXT_MAIN"])) palette.setColor(QPalette.Highlight, QColor(COLORS["ACCENT_LIGHT"])) palette.setColor(QPalette.HighlightedText, QColor("#FFFFFF")) app.setPalette(palette) return COLORS