certflow.actions.registry 源代码

"""统一 action 注册表(Phase 3,蓝图 §1.6)。

将分散在 ``query_view`` / ``correction_queue_view`` / ``main_window`` 的
action 派发收敛为单一信源:

- ``action 名`` → ``视图方法名``(``ActionSpec.method``);
- 派发统一走 ``dispatch(view, action_name, item=None)``,经 ``getattr(view, method)``
  解析并调用,替换 query_view 的三套内联字典、correction_queue 的 ``getattr`` 反射、
  main_window 的硬编码导航 lambda。

设计要点:
- 注册表为**纯 Python、无 Qt 依赖**,便于单元测试;
- ``query_view`` 的 lambda action(``select_all`` / ``hide_selected`` / ``mark_inspected`` 等)
  已提升为 ``_action_*`` 命名方法并注册(任务 1.2);工具栏与右键菜单现已 100% 经
  ``dispatch`` 派发,无内联 if/分支回退;``set_status_*`` / ``set_supply_type_*`` 等配置驱动
  动态 action 经前缀通用 action(``set_status`` / ``set_supply_type``)承接。
- ``main_window`` 导航按钮(视图切换)已在 Phase 2.1 接入(``nav.*`` action → ``_nav_*`` 方法)。
"""

from __future__ import annotations

import inspect
from dataclasses import dataclass
from typing import Any


[文档] @dataclass class ActionSpec: """单个 UI action 的规范描述。 Attributes: name: action 唯一名(与 ``ui.yaml`` / ``config.yaml`` 中 ``action`` 字段一致)。 method: 承载该 action 的视图方法名(经 ``getattr(view, method)`` 解析)。 label: 显示文本(工具栏/菜单用),可空(运行时取 yaml)。 icon: 图标资源标识,可空。 tooltip: 悬停提示,可空。 danger: 是否为危险操作(红色按钮),默认 False。 category: 分类(general / query / correction / nav),用于分组。 """ name: str method: str label: str = "" icon: str = "" tooltip: str = "" danger: bool = False category: str = "general"
REGISTRY: dict[str, ActionSpec] = {}
[文档] def register(spec: ActionSpec) -> None: """注册单个 action。重复注册后者覆盖前者。""" REGISTRY[spec.name] = spec
[文档] def register_many(specs: list[ActionSpec]) -> None: """批量注册。""" for s in specs: register(s)
[文档] def get_spec(action_name: str) -> ActionSpec | None: """按名取 action 规范;不存在返回 None。""" return REGISTRY.get(action_name)
# 通用前缀处理:set_status_* / set_supply_type_* 等配置驱动动作,由注册表通用 action 承接, # 避免视图内联 if 分支(蓝图 §1.6 / §7.2 任务 1.2 全接入)。 _GENERIC_PREFIXES: dict[str, str] = { "set_status_": "set_status", "set_supply_type_": "set_supply_type", }
[文档] def dispatch(view: Any, action_name: str, item: dict | None = None) -> bool: """在 ``view`` 上派发 ``action_name``。 解析流程:``REGISTRY[action_name].method`` → ``getattr(view, method)`` → 调用。 命中且方法存在返回 True;action 未注册或视图无该方法返回 False(调用方据此告警)。 前缀通用处理:``set_status_待生产`` 等动态 action 名未精确注册时,回落到通用 ``set_status`` / ``set_supply_type`` action(仍走注册表,无视图内联分支)。 方法签名适配:声明 ``item`` 形参的方法在 ``item`` 非空时接收上下文项;声明 ``action_name`` 形参的方法始终接收 action 名(供 ``set_status`` 等从名解析取值)。 Args: view: 承载 action 方法的视图实例。 action_name: action 名。 item: 可选上下文项(右键菜单项 dict),非空时传给方法。 Returns: bool: 是否成功派发。 """ spec = REGISTRY.get(action_name) if spec is None: for prefix, generic in _GENERIC_PREFIXES.items(): if action_name.startswith(prefix): spec = REGISTRY.get(generic) break if spec is None: return False method = getattr(view, spec.method, None) if method is None: return False # 按方法签名注入 item / action_name(向后兼容:方法无对应形参则不传)。 kwargs: dict[str, Any] = {} try: sig = inspect.signature(method) params = [p for p in sig.parameters.values() if p.name != "self"] except (TypeError, ValueError): params = [] if item is not None and any(p.name == "item" for p in params): kwargs["item"] = item if any(p.name == "action_name" for p in params): kwargs["action_name"] = action_name if kwargs: method(**kwargs) else: method() return True
# --------------------------------------------------------------------------- # action 目录(单一信源) # 仅收录「方法名已存在」的 action;lambda 类(select_all 等)留待 Phase 3.2 提升为 # _action_* 命名方法后补充;main_window 导航留待 Phase 2.1。 # --------------------------------------------------------------------------- register_many( [ # --- query_view:工具栏 / 右键(命名方法部分)--- ActionSpec("search", "search", category="query"), ActionSpec("invert_selection", "_invert_selection", category="query"), ActionSpec("back_home", "_go_back_home", category="query"), ActionSpec("fill_plan_no", "_batch_fill_plan_no", category="query"), ActionSpec("set_year_month", "_set_year_month", category="query"), ActionSpec("copy_cert_info", "_copy_cert_info", category="query"), ActionSpec("edit_cert_fields", "_edit_cert_fields", category="query"), ActionSpec("edit_certificate", "_edit_certificate", category="query"), ActionSpec("goto_print", "_goto_print", category="query"), ActionSpec("print_certificates", "_print_certificates", category="query"), ActionSpec("preview_certificate", "_preview_certificate", category="query"), ActionSpec("export_selected", "_export_selected", category="query"), ActionSpec("batch_update_status", "_batch_update_production_status", category="query"), ActionSpec("manual_number", "_manual_number", category="query"), ActionSpec("auto_number", "_auto_number", category="query"), ActionSpec("copy_number", "_copy_number", category="query"), ActionSpec("delete_rows", "_delete_selected_records", category="query"), # --- correction_queue_view(ui.yaml correction_queue.* 驱动,Phase 2.2)--- ActionSpec("release", "on_release_quarantine", category="correction"), ActionSpec("delete", "on_delete_quarantine", danger=True, category="correction"), ActionSpec("edit_and_release", "on_edit_and_release_quarantine", category="correction"), ActionSpec("refresh", "refresh", category="correction"), # --- query_view:原 lambda action(Phase 3.2 提升为命名方法)--- ActionSpec("select_all", "_action_select_all", category="query"), ActionSpec("hide_selected", "_action_hide_selected", category="query"), ActionSpec("hide_rows", "_action_hide_rows", category="query"), ActionSpec("mark_inspected", "_action_mark_inspected", category="query"), ActionSpec("mark_rejected", "_action_mark_rejected", category="query"), ActionSpec("set_make", "_action_set_make", category="query"), ActionSpec("set_buy", "_action_set_buy", category="query"), ActionSpec("set_repair", "_action_set_repair", category="query"), ActionSpec("toggle_needs_numbering", "_action_toggle_needs_numbering", category="query"), # --- main_window 顶部导航(Phase 2.1,经 dispatch 派发到 MainWindow 的 _nav_* 方法)--- # action 名与 ui.yaml nav_buttons[].action 一一对应;方法名为 MainWindow 上的承载方法。 ActionSpec("nav.settings", "_nav_to_settings", label="系统设置", category="nav"), ActionSpec("nav.import", "_nav_show_import", label="计划导入", category="nav"), ActionSpec("nav.query", "_nav_to_query", label="查询与补充", category="nav"), ActionSpec("nav.output", "_nav_to_output", label="输出", category="nav"), ActionSpec("nav.history", "_nav_to_history", label="打印历史", category="nav"), ActionSpec("nav.report", "_nav_to_report", label="报告生成", category="nav"), ActionSpec( "nav.correction_queue", "_nav_show_correction_queue", label="校正队列", category="nav" ), ActionSpec( "nav.lookup_tables", "_nav_show_lookup_tables", label="基础数据维护", category="nav" ), # --- query_view:配置驱动通用动作(任务 1.2 全接入,消除视图内联 if/分支)--- # set_status_* / set_supply_type_* 经前缀通用 action(set_status / set_supply_type)承接, # 无需为每个状态值单独注册。 ActionSpec("set_status", "_action_set_status", category="query"), ActionSpec("set_supply_type", "_action_set_supply_type", category="query"), ActionSpec("set_supply_type_make", "_action_set_make", category="query"), ActionSpec("set_supply_type_buy", "_action_set_buy", category="query"), ActionSpec("set_supply_type_repair", "_action_set_repair", category="query"), ActionSpec("set_clear", "_action_set_clear", category="query"), ActionSpec("shipped_archive", "_shipped_archive", category="query"), ActionSpec("shipped_export_delete", "_shipped_export_delete", category="query"), ActionSpec("open_lookup_table", "_action_open_lookup_table", category="query"), ActionSpec("view_raw_execution", "_show_raw_execution_status", category="query"), ActionSpec("mark_certificate_done", "_action_mark_document_done", category="query"), ActionSpec("mark_nameplate_done", "_action_mark_document_done", category="query"), ActionSpec("mark_test_report_done", "_action_mark_document_done", category="query"), ActionSpec("mark_warranty_done", "_action_mark_document_done", category="query"), ] )