添加深色模式

This commit is contained in:
xinzhu.yin
2026-05-28 10:50:52 +08:00
parent f8f2d471e5
commit cf724d60d7
4 changed files with 278 additions and 114 deletions

134
app/views/theme_manager.py Normal file
View 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