Files
pqAutomationApp/app/views/modern_styles.py

220 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""现代化 UI 样式注册(跟随 ttkbootstrap 当前主题)。
由 backgroud_style_set() 调用一次。这里集中定义"配置项卡片化"
"现代化标题栏""工具条""状态栏" 等所需的所有 ttk Style
保持主题切换时颜色自动跟随。
"""
from __future__ import annotations
import ttkbootstrap as ttk
def _hex_to_rgb(h: str) -> tuple[int, int, int]:
h = h.lstrip("#")
return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
def _rgb_to_hex(r: int, g: int, b: int) -> str:
return f"#{r:02x}{g:02x}{b:02x}"
def _mix(c1: str, c2: str, ratio: float) -> str:
"""按 ratio (0~1) 将 c1 与 c2 线性混合。"""
r1, g1, b1 = _hex_to_rgb(c1)
r2, g2, b2 = _hex_to_rgb(c2)
return _rgb_to_hex(
int(r1 * (1 - ratio) + r2 * ratio),
int(g1 * (1 - ratio) + g2 * ratio),
int(b1 * (1 - ratio) + b2 * ratio),
)
def _is_dark(color: str) -> bool:
r, g, b = _hex_to_rgb(color)
# ITU-R BT.601 亮度
return (r * 299 + g * 587 + b * 114) / 1000 < 128
def apply_modern_styles() -> None:
"""注册或刷新现代化样式集。可在主题切换后再次调用。"""
style = ttk.Style()
theme = style.colors # ttkbootstrap.style.Colors
bg = theme.bg # 主背景
fg = theme.fg # 主前景
primary = theme.primary
secondary = theme.secondary
border = theme.border
inputbg = theme.inputbg
selectbg = theme.selectbg
selectfg = theme.selectfg
dark_theme = _is_dark(bg)
# 卡片背景:在主背景上轻微偏移,营造层级感
card_bg = _mix(bg, "#ffffff", 0.04) if dark_theme else _mix(bg, "#000000", 0.025)
card_border = _mix(bg, fg, 0.18) if dark_theme else _mix(bg, "#000000", 0.10)
# 配置项 header 用 secondary 主题色
header_bg = secondary
header_fg = "#ffffff" if _is_dark(secondary) else "#1a1a1a"
header_hover_bg = _mix(secondary, "#ffffff", 0.08) if _is_dark(secondary) else _mix(secondary, "#000000", 0.08)
preview_fg = _mix(header_fg, header_bg, 0.35)
# ---------------- 卡片 ----------------
style.configure(
"Card.TFrame",
background=card_bg,
bordercolor=card_border,
relief="solid",
borderwidth=1,
)
style.configure(
"CardTitle.TLabel",
background=card_bg,
foreground=primary,
font=("微软雅黑", 10, "bold"),
)
style.configure(
"CardBody.TLabel",
background=card_bg,
foreground=fg,
font=("微软雅黑", 9),
)
style.configure(
"CardIcon.TLabel",
background=card_bg,
foreground=primary,
font=("Segoe UI Emoji", 14),
)
# 内嵌于 Card 的容器(与 Card.TFrame 同背景,无边框)
style.configure("CardInner.TFrame", background=card_bg, borderwidth=0)
# ---------------- 配置项 Header ----------------
style.configure(
"ConfigHeader.TFrame",
background=header_bg,
borderwidth=0,
)
style.configure(
"ConfigHeaderHover.TFrame",
background=header_hover_bg,
borderwidth=0,
)
style.configure(
"ConfigHeader.TLabel",
background=header_bg,
foreground=header_fg,
font=("微软雅黑", 10, "bold"),
)
style.configure(
"ConfigHeaderHover.TLabel",
background=header_hover_bg,
foreground=header_fg,
font=("微软雅黑", 10, "bold"),
)
style.configure(
"ConfigChevron.TLabel",
background=header_bg,
foreground=header_fg,
font=("Segoe UI Symbol", 12, "bold"),
)
style.configure(
"ConfigPreview.TLabel",
background=header_bg,
foreground=preview_fg,
font=("微软雅黑", 9),
)
# ---------------- 顶部工具条 ----------------
style.configure("Toolbar.TFrame", background=bg, borderwidth=0)
# 工具条上的次要按钮(清理配置等)
style.configure(
"ToolbarMuted.TButton",
font=("微软雅黑", 9),
padding=(10, 5),
)
# ---------------- 区段标题(侧栏 / 卡片外) ----------------
style.configure(
"SectionTitle.TLabel",
background=bg,
foreground=_mix(fg, bg, 0.45),
font=("微软雅黑", 8, "bold"),
)
# 侧栏内的小区段标题(侧栏背景是 primary
style.configure(
"SidebarSection.TLabel",
background=primary,
foreground=_mix("#ffffff", primary, 0.35),
font=("微软雅黑", 8, "bold"),
)
# ---------------- 结果区无边框标题行 ----------------
style.configure("ResultHeader.TFrame", background=bg, borderwidth=0)
style.configure(
"ResultHeader.TLabel",
background=bg,
foreground=fg,
font=("微软雅黑", 11, "bold"),
)
# ---------------- 状态栏 ----------------
statusbar_bg = _mix(bg, "#000000", 0.06) if not dark_theme else _mix(bg, "#ffffff", 0.06)
statusbar_fg = _mix(fg, bg, 0.15)
style.configure(
"StatusBar.TFrame",
background=statusbar_bg,
borderwidth=0,
)
style.configure(
"StatusBar.TLabel",
background=statusbar_bg,
foreground=statusbar_fg,
font=("微软雅黑", 9),
padding=(10, 4),
)
style.configure(
"StatusBarAccent.TLabel",
background=statusbar_bg,
foreground=primary,
font=("微软雅黑", 9, "bold"),
padding=(10, 4),
)
# ---------------- Sidebar 按钮(保留兼容名) ----------------
style.configure(
"Sidebar.TButton",
background=primary,
foreground="#ffffff" if _is_dark(primary) else "#1a1a1a",
font=("微软雅黑", 10),
padding=(12, 10),
borderwidth=0,
anchor="w",
)
style.map(
"Sidebar.TButton",
background=[
("active", _mix(primary, "#ffffff", 0.10)),
("pressed", _mix(primary, "#000000", 0.10)),
],
)
style.configure(
"SidebarSelected.TButton",
background=_mix(primary, "#000000", 0.18),
foreground="#ffffff",
font=("微软雅黑", 10, "bold"),
padding=(12, 10),
borderwidth=0,
anchor="w",
)
style.map(
"SidebarSelected.TButton",
background=[
("active", _mix(primary, "#000000", 0.10)),
("pressed", _mix(primary, "#000000", 0.25)),
],
)