certflow.controllers.settings_controller 源代码
# src/certflow/controllers/settings_controller.py
"""设置控制器
负责设置页面的业务逻辑调度,连接 UI 信号与服务端/处理器:
- 主题切换 → ThemeManager
- 语言切换 → I18nManager
- 自动保存定时器 → QTimer 调度 CertLogService.auto_save
- 覆盖确认 → 透传给导入流程
- 打印机偏移 → 透传给 CertificatePrintService
- 最近文件数 → 透传给 settings._add_recent_file 裁剪
Signals:
theme_changed(str): 主题变更时发出 (light/dark)
language_changed(str): 语言变更时发出 (zh_CN/en_US)
auto_save_triggered(): 自动保存定时器到期时发出
settings_saved(dict): 设置保存后发出完整偏好字典
"""
from __future__ import annotations
from typing import Any
from PySide6.QtCore import QObject, QTimer, Signal
from certflow.config.settings import _get_user_config, get_user_preference, merge_user_config
from certflow.utils.logger import logger
[文档]
class SettingsController(QObject):
"""设置控制器 — 集中管理用户偏好的读写与运行时生效
架构层次:
View (SettingsView) → Controller (SettingsController) → Handler (ThemeManager/I18nManager)
→ Service (CertLogService 自动保存)
Attributes:
_auto_save_timer: 自动保存定时器
_auto_save_callback: 定时器到期时的回调函数
"""
# ============================================================
# Signals
# ============================================================
theme_changed = Signal(str)
"""主题变更信号 (light / dark)"""
language_changed = Signal(str)
"""语言变更信号 (zh_CN / en_US)"""
auto_save_triggered = Signal()
"""自动保存定时器触发信号"""
settings_saved = Signal(dict)
"""设置保存完成信号,携带完整偏好字典"""
# ============================================================
# Initialization
# ============================================================
def __init__(self, parent: QObject | None = None) -> None:
"""初始化设置控制器
创建自动保存定时器并初始化回调占位,再根据当前用户偏好
决定是否启用定时器。
Args:
parent: Qt 父对象(可选)
"""
super().__init__(parent)
# 自动保存定时器
self._auto_save_timer = QTimer(self)
self._auto_save_timer.timeout.connect(self._on_auto_save_tick)
self._auto_save_callback: callable | None = None
# 启动时根据用户偏好决定是否启用定时器
self._sync_timer_from_prefs()
# ============================================================
# Public API — 偏好读写
# ============================================================
[文档]
def get_preference(self, key: str, default: Any = None) -> Any:
"""读取用户偏好(代理 settings.get_user_preference)
Args:
key: 偏好键名
default: 键不存在时返回的默认值,默认为 None
Returns:
Any: 偏好值;键不存在时返回 default
"""
return get_user_preference(key, default)
[文档]
def save_all(self, preferences: dict[str, Any]) -> None:
"""保存全部偏好设置并应用运行时变更
Args:
preferences: 偏好设置字典,可包含:
- auto_save: bool
- auto_save_interval: int (分钟)
- confirm_before_overwrite: bool
- language: str
- theme: str
- max_recent_files: int
- printer_offsets: dict {"x_offset": int, "y_offset": int}
Returns:
None
Examples:
>>> controller = SettingsController()
>>> controller.save_all({
... "theme": "dark",
... "language": "zh_CN",
... "auto_save": True,
... "auto_save_interval": 5,
... })
"""
# 打印机偏移(桶2)
patch: dict[str, Any] = {}
if "printer_offsets" in preferences:
patch["printer_offsets"] = preferences["printer_offsets"]
# 用户偏好(桶2)
pref_keys = [
"auto_save",
"auto_save_interval",
"confirm_before_overwrite",
"confirm_before_delete",
"report_export_pdf",
"language",
"theme",
"max_recent_files",
]
prefs_patch = {key: preferences[key] for key in pref_keys if key in preferences}
if prefs_patch:
patch["preferences"] = prefs_patch
# 按段增量写(只动 userconfig.yaml, 不触碰易变状态文件)
merge_user_config(patch)
logger.info("用户偏好已保存到 userconfig.yaml")
# ============================================================
# 运行时生效
# ============================================================
# 1. 主题切换
if "theme" in preferences:
new_theme = preferences["theme"]
self.theme_changed.emit(new_theme)
logger.info(f"主题已切换为: {new_theme}")
# 2. 语言切换
if "language" in preferences:
new_lang = preferences["language"]
self.language_changed.emit(new_lang)
logger.info(f"语言已切换为: {new_lang}")
# 3. 自动保存定时器
self._sync_timer_from_prefs()
# 4. 最近文件裁剪 (立即生效)
if "max_recent_files" in preferences:
self._trim_recent_files(preferences["max_recent_files"])
# 通知所有监听者
self.settings_saved.emit(preferences)
# ============================================================
# 自动保存定时器
# ============================================================
[文档]
def set_auto_save_callback(self, callback: callable | None) -> None:
"""设置自动保存定时器到期时的回调
Args:
callback: 回调函数,签名为 () -> None,传 None 禁用
Returns:
None
"""
self._auto_save_callback = callback
def _sync_timer_from_prefs(self) -> None:
"""根据当前偏好设置同步自动保存定时器状态"""
auto_save = get_user_preference("auto_save", True)
interval_min = get_user_preference("auto_save_interval", 5)
if auto_save and self._auto_save_callback:
self._auto_save_timer.start(interval_min * 60 * 1000) # 分钟 → 毫秒
logger.info(f"自动保存定时器已启动: 间隔 {interval_min} 分钟")
else:
self._auto_save_timer.stop()
logger.info("自动保存定时器已停止")
def _on_auto_save_tick(self) -> None:
"""自动保存定时器到期处理"""
logger.debug("自动保存定时器触发")
self.auto_save_triggered.emit()
if self._auto_save_callback:
try:
self._auto_save_callback()
except Exception as e:
logger.error(f"自动保存回调执行失败: {e}")
# ============================================================
# 最近文件裁剪
# ============================================================
def _trim_recent_files(self, max_count: int) -> None:
"""裁剪最近文件列表到指定数量
Args:
max_count: 最大保留数量
"""
try:
# 裁剪 recent_files(桶3)
recent_files: list = _get_user_config().get("recent_files", [])
if len(recent_files) > max_count:
merge_user_config({"recent_files": recent_files[:max_count]})
logger.info(f"已裁剪 recent_files 到 {max_count} 条")
# 裁剪 recent_import_files(桶3)
recent_import: list = _get_user_config().get("recent_import_files", [])
if len(recent_import) > max_count:
merge_user_config({"recent_import_files": recent_import[:max_count]})
logger.info(f"已裁剪 recent_import_files 到 {max_count} 条")
except Exception as e:
logger.error(f"裁剪最近文件失败: {e}")