570 lines
15 KiB
Python
570 lines
15 KiB
Python
# 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
|