"""现代化 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)), ], )