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}")