添加深色模式
This commit is contained in:
@@ -45,10 +45,10 @@ def apply_modern_styles() -> None:
|
||||
fg = theme.fg # 主前景
|
||||
primary = theme.primary
|
||||
secondary = theme.secondary
|
||||
info = theme.info
|
||||
dark = theme.dark
|
||||
border = theme.border
|
||||
inputbg = theme.inputbg
|
||||
selectbg = theme.selectbg
|
||||
selectfg = theme.selectfg
|
||||
|
||||
dark_theme = _is_dark(bg)
|
||||
|
||||
@@ -61,6 +61,11 @@ def apply_modern_styles() -> None:
|
||||
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)
|
||||
sidebar_bg = _mix(dark, bg, 0.18) if dark_theme else _mix(primary, "#000000", 0.10)
|
||||
sidebar_hover = _mix(sidebar_bg, "#ffffff", 0.07) if dark_theme else _mix(sidebar_bg, "#000000", 0.06)
|
||||
sidebar_selected = _mix(sidebar_bg, "#ffffff", 0.14) if dark_theme else _mix(sidebar_bg, "#000000", 0.10)
|
||||
sidebar_fg = _mix(fg, bg, 0.05)
|
||||
sidebar_muted = _mix(fg, sidebar_bg, 0.45)
|
||||
|
||||
# ---------------- 卡片 ----------------
|
||||
style.configure(
|
||||
@@ -73,20 +78,20 @@ def apply_modern_styles() -> None:
|
||||
style.configure(
|
||||
"CardTitle.TLabel",
|
||||
background=card_bg,
|
||||
foreground=primary,
|
||||
font=("微软雅黑", 10, "bold"),
|
||||
foreground=fg,
|
||||
font=("Segoe UI", 10, "bold"),
|
||||
)
|
||||
style.configure(
|
||||
"CardBody.TLabel",
|
||||
background=card_bg,
|
||||
foreground=fg,
|
||||
font=("微软雅黑", 9),
|
||||
font=("Segoe UI", 9),
|
||||
)
|
||||
style.configure(
|
||||
"CardIcon.TLabel",
|
||||
background=card_bg,
|
||||
foreground=primary,
|
||||
font=("Segoe UI Emoji", 14),
|
||||
foreground=info if dark_theme else primary,
|
||||
font=("Segoe UI", 9, "bold"),
|
||||
)
|
||||
|
||||
# 内嵌于 Card 的容器(与 Card.TFrame 同背景,无边框)
|
||||
@@ -107,13 +112,13 @@ def apply_modern_styles() -> None:
|
||||
"ConfigHeader.TLabel",
|
||||
background=header_bg,
|
||||
foreground=header_fg,
|
||||
font=("微软雅黑", 10, "bold"),
|
||||
font=("Segoe UI", 10, "bold"),
|
||||
)
|
||||
style.configure(
|
||||
"ConfigHeaderHover.TLabel",
|
||||
background=header_hover_bg,
|
||||
foreground=header_fg,
|
||||
font=("微软雅黑", 10, "bold"),
|
||||
font=("Segoe UI", 10, "bold"),
|
||||
)
|
||||
style.configure(
|
||||
"ConfigChevron.TLabel",
|
||||
@@ -125,7 +130,7 @@ def apply_modern_styles() -> None:
|
||||
"ConfigPreview.TLabel",
|
||||
background=header_bg,
|
||||
foreground=preview_fg,
|
||||
font=("微软雅黑", 9),
|
||||
font=("Segoe UI", 9),
|
||||
)
|
||||
|
||||
# ---------------- 顶部工具条 ----------------
|
||||
@@ -133,7 +138,7 @@ def apply_modern_styles() -> None:
|
||||
# 工具条上的次要按钮(清理配置等)
|
||||
style.configure(
|
||||
"ToolbarMuted.TButton",
|
||||
font=("微软雅黑", 9),
|
||||
font=("Segoe UI", 9),
|
||||
padding=(10, 5),
|
||||
)
|
||||
|
||||
@@ -142,14 +147,28 @@ def apply_modern_styles() -> None:
|
||||
"SectionTitle.TLabel",
|
||||
background=bg,
|
||||
foreground=_mix(fg, bg, 0.45),
|
||||
font=("微软雅黑", 8, "bold"),
|
||||
font=("Segoe UI", 8, "bold"),
|
||||
)
|
||||
style.configure("Sidebar.TFrame", background=sidebar_bg, borderwidth=0)
|
||||
# 侧栏内的小区段标题(侧栏背景是 primary)
|
||||
style.configure(
|
||||
"SidebarSection.TLabel",
|
||||
background=primary,
|
||||
foreground=_mix("#ffffff", primary, 0.35),
|
||||
font=("微软雅黑", 8, "bold"),
|
||||
background=sidebar_bg,
|
||||
foreground=sidebar_muted,
|
||||
font=("Segoe UI", 8, "bold"),
|
||||
)
|
||||
# 侧栏顶部品牌区
|
||||
brand_bg = _mix(sidebar_bg, "#ffffff", 0.05) if dark_theme else _mix(sidebar_bg, "#000000", 0.05)
|
||||
style.configure(
|
||||
"SidebarBrand.TFrame",
|
||||
background=brand_bg,
|
||||
borderwidth=0,
|
||||
)
|
||||
style.configure(
|
||||
"SidebarBrand.TLabel",
|
||||
background=brand_bg,
|
||||
foreground="#ffffff",
|
||||
font=("Segoe UI Semibold", 12),
|
||||
)
|
||||
|
||||
# ---------------- 结果区无边框标题行 ----------------
|
||||
@@ -158,7 +177,7 @@ def apply_modern_styles() -> None:
|
||||
"ResultHeader.TLabel",
|
||||
background=bg,
|
||||
foreground=fg,
|
||||
font=("微软雅黑", 11, "bold"),
|
||||
font=("Segoe UI", 11, "bold"),
|
||||
)
|
||||
|
||||
# ---------------- 状态栏 ----------------
|
||||
@@ -173,47 +192,48 @@ def apply_modern_styles() -> None:
|
||||
"StatusBar.TLabel",
|
||||
background=statusbar_bg,
|
||||
foreground=statusbar_fg,
|
||||
font=("微软雅黑", 9),
|
||||
font=("Segoe UI", 9),
|
||||
padding=(10, 4),
|
||||
)
|
||||
style.configure(
|
||||
"StatusBarAccent.TLabel",
|
||||
background=statusbar_bg,
|
||||
foreground=primary,
|
||||
font=("微软雅黑", 9, "bold"),
|
||||
foreground=info if dark_theme else primary,
|
||||
font=("Segoe UI", 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),
|
||||
background=sidebar_bg,
|
||||
foreground=sidebar_fg,
|
||||
font=("Segoe UI", 10),
|
||||
padding=(18, 9),
|
||||
borderwidth=0,
|
||||
anchor="w",
|
||||
)
|
||||
style.map(
|
||||
"Sidebar.TButton",
|
||||
background=[
|
||||
("active", _mix(primary, "#ffffff", 0.10)),
|
||||
("pressed", _mix(primary, "#000000", 0.10)),
|
||||
("active", sidebar_hover),
|
||||
("pressed", sidebar_selected),
|
||||
],
|
||||
foreground=[("active", "#ffffff" if dark_theme else sidebar_fg)],
|
||||
)
|
||||
style.configure(
|
||||
"SidebarSelected.TButton",
|
||||
background=_mix(primary, "#000000", 0.18),
|
||||
background=sidebar_selected,
|
||||
foreground="#ffffff",
|
||||
font=("微软雅黑", 10, "bold"),
|
||||
padding=(12, 10),
|
||||
font=("Segoe UI Semibold", 10),
|
||||
padding=(18, 9),
|
||||
borderwidth=0,
|
||||
anchor="w",
|
||||
)
|
||||
style.map(
|
||||
"SidebarSelected.TButton",
|
||||
background=[
|
||||
("active", _mix(primary, "#000000", 0.10)),
|
||||
("pressed", _mix(primary, "#000000", 0.25)),
|
||||
("active", _mix(sidebar_selected, "#ffffff", 0.06) if dark_theme else _mix(sidebar_selected, "#000000", 0.06)),
|
||||
("pressed", _mix(sidebar_selected, "#000000", 0.08)),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -76,17 +76,17 @@ def create_floating_config_panel(self: "PQAutomationApp"):
|
||||
config_row_frame.pack(fill=tk.X, expand=False)
|
||||
|
||||
# 设备连接 卡片
|
||||
connection_card = _make_card(config_row_frame, icon="\U0001F4E1", title="设备连接") # 📡
|
||||
connection_card = _make_card(config_row_frame, icon="01", title="设备连接")
|
||||
connection_card.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 6))
|
||||
self.connection_frame = connection_card._body # type: ignore[attr-defined]
|
||||
|
||||
# 测试项目 卡片
|
||||
test_items_card = _make_card(config_row_frame, icon="\u2714", title="测试项目") # ✔
|
||||
test_items_card = _make_card(config_row_frame, icon="02", title="测试项目")
|
||||
test_items_card.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=6)
|
||||
self.test_items_frame = test_items_card._body # type: ignore[attr-defined]
|
||||
|
||||
# 信号格式 卡片
|
||||
signal_card = _make_card(config_row_frame, icon="\u2699", title="信号格式") # ⚙
|
||||
signal_card = _make_card(config_row_frame, icon="03", title="信号格式")
|
||||
signal_card.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(6, 0))
|
||||
self.signal_format_frame = signal_card._body # type: ignore[attr-defined]
|
||||
|
||||
@@ -590,15 +590,37 @@ def create_connection_content(self: "PQAutomationApp"):
|
||||
|
||||
|
||||
def create_test_type_frame(self: "PQAutomationApp"):
|
||||
"""创建测试类型选择区域(侧边栏形式)"""
|
||||
"""创建测试类型选择区域(侧边栏形式)。
|
||||
|
||||
新版(v3)改进:
|
||||
- 深灰分层背景,接近 Calman 的侧栏密度;
|
||||
- 纯文字按钮,不使用 emoji;
|
||||
- 用更克制的字号 / 间距做层级区分;
|
||||
- 不再使用 padding=10 硬覆盖(交给 Sidebar.TButton 样式统一管理)。
|
||||
"""
|
||||
# 设置测试类型变量
|
||||
self.test_type_var = tk.StringVar(value="screen_module")
|
||||
|
||||
# 创建测试类型按钮并放置在侧边栏
|
||||
# ---------- 顶部品牌区 ----------
|
||||
brand = ttk.Frame(self.sidebar_frame, style="SidebarBrand.TFrame")
|
||||
brand.pack(fill=tk.X, pady=(2, 10))
|
||||
ttk.Label(
|
||||
brand,
|
||||
text="PQ AUTOMATION",
|
||||
style="SidebarBrand.TLabel",
|
||||
).pack(side=tk.LEFT, padx=16, pady=11)
|
||||
|
||||
# ---------- 分组:测试类型 ----------
|
||||
ttk.Label(
|
||||
self.sidebar_frame,
|
||||
text="测试类型",
|
||||
style="SidebarSection.TLabel",
|
||||
).pack(fill=tk.X, padx=16, pady=(2, 6), anchor="w")
|
||||
|
||||
test_types = [
|
||||
("屏模组性能测试", "screen_module"),
|
||||
("SDR Movie测试", "sdr_movie"),
|
||||
("HDR Movie测试", "hdr_movie"),
|
||||
("SDR Movie", "sdr_movie"),
|
||||
("HDR Movie", "hdr_movie"),
|
||||
]
|
||||
|
||||
for text, type_value in test_types:
|
||||
@@ -606,87 +628,37 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
master=self.sidebar_frame,
|
||||
text=text,
|
||||
style="Sidebar.TButton",
|
||||
padding=10,
|
||||
command=lambda v=type_value: self.change_test_type(v),
|
||||
takefocus=False,
|
||||
)
|
||||
btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
|
||||
# 保存按钮引用以便后续更新样式
|
||||
setattr(self, f"{type_value}_btn", btn)
|
||||
|
||||
# 添加分隔线
|
||||
ttk.Separator(self.sidebar_frame, orient="horizontal").pack(
|
||||
fill=tk.X, padx=10, pady=10
|
||||
)
|
||||
|
||||
# 只保留日志按钮
|
||||
self.log_btn = ttk.Button(
|
||||
# ---------- 分组:工具面板 ----------
|
||||
ttk.Label(
|
||||
self.sidebar_frame,
|
||||
text="测试日志",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_log_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.log_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
text="工具面板",
|
||||
style="SidebarSection.TLabel",
|
||||
).pack(fill=tk.X, padx=16, pady=(16, 6), anchor="w")
|
||||
|
||||
# Local Dimming 测试按钮
|
||||
self.local_dimming_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="Local Dimming",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_local_dimming_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.local_dimming_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
|
||||
# AI 图片对话按钮
|
||||
self.ai_image_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="AI 图片",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_ai_image_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.ai_image_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
|
||||
# self.single_step_btn = ttk.Button(
|
||||
# self.sidebar_frame,
|
||||
# text="单步调试",
|
||||
# style="Sidebar.TButton",
|
||||
# command=self.toggle_single_step_panel,
|
||||
# takefocus=False,
|
||||
# )
|
||||
# self.single_step_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
|
||||
self.pantone_baseline_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="Pantone认证摸底测试",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_pantone_baseline_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.pantone_baseline_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
|
||||
# Gamma 测试图案配置按钮
|
||||
self.gamma_pattern_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="Gamma 图案配置",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_gamma_pattern_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.gamma_pattern_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
|
||||
# CALMAN 风格灰阶测试按钮
|
||||
self.calman_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="CALMAN 灰阶",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_calman_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.calman_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
panel_buttons = [
|
||||
("log_btn", "测试日志", self.toggle_log_panel),
|
||||
("local_dimming_btn", "Local Dimming", self.toggle_local_dimming_panel),
|
||||
("ai_image_btn", "AI 图片", self.toggle_ai_image_panel),
|
||||
("pantone_baseline_btn", "Pantone 摸底", self.toggle_pantone_baseline_panel),
|
||||
("gamma_pattern_btn", "Gamma 图案", self.toggle_gamma_pattern_panel),
|
||||
("calman_btn", "CALMAN 灰阶", self.toggle_calman_panel),
|
||||
]
|
||||
for attr, text, cmd in panel_buttons:
|
||||
btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text=text,
|
||||
style="Sidebar.TButton",
|
||||
command=cmd,
|
||||
takefocus=False,
|
||||
)
|
||||
btn.pack(fill=tk.X, padx=0, pady=1)
|
||||
setattr(self, attr, btn)
|
||||
|
||||
# 测试版水印标签(版本 x.x.0.0 时显示)
|
||||
from app_version import is_beta_version, APP_VERSION
|
||||
@@ -701,6 +673,17 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
)
|
||||
beta_lbl.pack(fill=tk.X, side=tk.BOTTOM, padx=4, pady=(6, 4))
|
||||
|
||||
# ---------- 主题切换(底部固定) ----------
|
||||
self.theme_toggle_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="切换深色模式",
|
||||
style="Sidebar.TButton",
|
||||
command=self._on_toggle_theme,
|
||||
takefocus=False,
|
||||
)
|
||||
self.theme_toggle_btn.pack(fill=tk.X, padx=0, pady=(0, 2), side=tk.BOTTOM)
|
||||
_refresh_theme_toggle_label(self)
|
||||
|
||||
# 注册面板按钮
|
||||
if hasattr(self, "panels"):
|
||||
if "log" in self.panels:
|
||||
@@ -710,7 +693,7 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
if "ai_image" in self.panels:
|
||||
self.panels["ai_image"]["button"] = self.ai_image_btn
|
||||
if "single_step" in self.panels:
|
||||
self.panels["single_step"]["button"] = self.single_step_btn
|
||||
self.panels["single_step"]["button"] = getattr(self, "single_step_btn", None)
|
||||
if "pantone_baseline" in self.panels:
|
||||
self.panels["pantone_baseline"]["button"] = self.pantone_baseline_btn
|
||||
if "gamma_pattern" in self.panels:
|
||||
@@ -719,6 +702,30 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
self.panels["calman"]["button"] = self.calman_btn
|
||||
|
||||
|
||||
def _refresh_theme_toggle_label(self: "PQAutomationApp") -> None:
|
||||
"""根据当前主题刷新切换按钮的文字。"""
|
||||
from app.views.theme_manager import is_dark
|
||||
if not hasattr(self, "theme_toggle_btn"):
|
||||
return
|
||||
if is_dark():
|
||||
self.theme_toggle_btn.configure(text="切换浅色模式")
|
||||
else:
|
||||
self.theme_toggle_btn.configure(text="切换深色模式")
|
||||
|
||||
|
||||
def _on_toggle_theme(self: "PQAutomationApp") -> None:
|
||||
"""切换主题:重新应用 ttk 样式并刷新所有自定义样式相关的标签。"""
|
||||
from app.views.theme_manager import toggle_theme
|
||||
toggle_theme()
|
||||
_refresh_theme_toggle_label(self)
|
||||
# 同步刷新侧栏选中态(高亮样式跟随新色板)
|
||||
if hasattr(self, "update_sidebar_selection"):
|
||||
try:
|
||||
self.update_sidebar_selection()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def update_config_info_display(self: "PQAutomationApp"):
|
||||
"""更新配置信息显示"""
|
||||
if hasattr(self, "config") and hasattr(self.config, "get_current_config"):
|
||||
@@ -1046,6 +1053,7 @@ class MainLayoutMixin:
|
||||
create_test_type_frame = create_test_type_frame
|
||||
update_config_info_display = update_config_info_display
|
||||
create_operation_frame = create_operation_frame
|
||||
_on_toggle_theme = _on_toggle_theme
|
||||
on_screen_module_timing_changed = on_screen_module_timing_changed
|
||||
on_screen_module_signal_format_changed = on_screen_module_signal_format_changed
|
||||
on_sdr_timing_changed = on_sdr_timing_changed
|
||||
|
||||
134
app/views/theme_manager.py
Normal file
134
app/views/theme_manager.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""主题管理:注册 Calman 风格深色主题 + 提供运行时切换。
|
||||
|
||||
主题在启动时通过 ``apply_initial_theme(root_style)`` 注入到 ttkbootstrap,
|
||||
当前选择持久化到 ``settings/ui_preferences.json``。运行时调用
|
||||
``toggle_theme(root_style)`` / ``set_theme(root_style, name)`` 可即时切换,
|
||||
并自动重新调用 ``apply_modern_styles()`` 让自定义样式跟上新色板。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from ttkbootstrap.style import Style, ThemeDefinition
|
||||
|
||||
from app.views.modern_styles import apply_modern_styles
|
||||
|
||||
|
||||
_PREFS_PATH = Path("settings/ui_preferences.json")
|
||||
|
||||
# 浅色主题:沿用旧的 yeti(首发布兼容)
|
||||
LIGHT_THEME = "yeti"
|
||||
# 深色主题:自定义 Calman 风格
|
||||
DARK_THEME = "calman_dark"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Calman 风格深色主题色板(参考实测截图取色)
|
||||
# ----------------------------------------------------------------------
|
||||
_CALMAN_DARK_COLORS = {
|
||||
"primary": "#343A41", # 主色改为炭灰,避免大面积亮蓝
|
||||
"secondary": "#444A51", # 中性深灰(用于 header / 分组背景)
|
||||
"success": "#4FB960",
|
||||
"info": "#6FAFCC", # 降低饱和度,只做少量点缀
|
||||
"warning": "#F2A93B",
|
||||
"danger": "#E0524A",
|
||||
"light": "#BFC6CE", # 高亮文本
|
||||
"dark": "#0D1014", # 最深背景(侧栏底色)
|
||||
"bg": "#1B1F24", # 主窗口背景
|
||||
"fg": "#E4E8EE", # 主文本颜色
|
||||
"selectbg": "#5A6169",
|
||||
"selectfg": "#E4E8EE",
|
||||
"border": "#2A2F36",
|
||||
"inputfg": "#E4E8EE",
|
||||
"inputbg": "#24292F",
|
||||
"active": "#2A2F36",
|
||||
}
|
||||
|
||||
|
||||
def register_themes() -> None:
|
||||
"""把自定义深色主题注册到 ttkbootstrap(可重复调用,幂等)。"""
|
||||
style = Style()
|
||||
if DARK_THEME in style.theme_names():
|
||||
return
|
||||
theme_def = ThemeDefinition(
|
||||
name=DARK_THEME,
|
||||
themetype="dark",
|
||||
colors=_CALMAN_DARK_COLORS,
|
||||
)
|
||||
style.register_theme(theme_def)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# 偏好持久化
|
||||
# ----------------------------------------------------------------------
|
||||
def _read_prefs() -> dict:
|
||||
try:
|
||||
return json.loads(_PREFS_PATH.read_text(encoding="utf-8"))
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return {}
|
||||
|
||||
|
||||
def _write_prefs(data: dict) -> None:
|
||||
try:
|
||||
_PREFS_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
_PREFS_PATH.write_text(
|
||||
json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
except OSError:
|
||||
# 写入失败不应影响 UI
|
||||
pass
|
||||
|
||||
|
||||
def get_saved_theme() -> Optional[str]:
|
||||
return _read_prefs().get("theme")
|
||||
|
||||
|
||||
def save_theme(name: str) -> None:
|
||||
prefs = _read_prefs()
|
||||
prefs["theme"] = name
|
||||
_write_prefs(prefs)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# 主题应用 / 切换
|
||||
# ----------------------------------------------------------------------
|
||||
def apply_initial_theme() -> str:
|
||||
"""启动时调用:注册主题 + 加载偏好 + 切到对应主题。
|
||||
|
||||
返回最终生效的主题名。
|
||||
"""
|
||||
register_themes()
|
||||
name = get_saved_theme() or LIGHT_THEME
|
||||
style = Style()
|
||||
if name not in style.theme_names():
|
||||
name = LIGHT_THEME
|
||||
style.theme_use(name)
|
||||
apply_modern_styles()
|
||||
return name
|
||||
|
||||
|
||||
def set_theme(name: str) -> str:
|
||||
"""切换到指定主题,持久化偏好,并刷新自定义样式。"""
|
||||
register_themes()
|
||||
style = Style()
|
||||
if name not in style.theme_names():
|
||||
name = LIGHT_THEME
|
||||
style.theme_use(name)
|
||||
apply_modern_styles()
|
||||
save_theme(name)
|
||||
return name
|
||||
|
||||
|
||||
def toggle_theme() -> str:
|
||||
"""在浅 / 深之间切换。返回新主题名。"""
|
||||
style = Style()
|
||||
current = style.theme.name
|
||||
target = DARK_THEME if current != DARK_THEME else LIGHT_THEME
|
||||
return set_theme(target)
|
||||
|
||||
|
||||
def is_dark() -> bool:
|
||||
return Style().theme.name == DARK_THEME
|
||||
Reference in New Issue
Block a user