Pelco KBD300A 模拟器:08.模板库 + 一键场景加载

第 8 篇:模板库 + 一键场景加载

引言

在之前的开发中,我们已经实现了宏编辑器(MacroEditorPanel),允许用户手动编写和运行宏脚本。但对于现场维护工具来说,许多场景(如停车场巡航、周界警戒)是常见的重复操作。如果每次都从零编写宏,不仅效率低下,还容易出错。为此,本篇引入模板库功能:预置常见宏模板,支持参数化占位符,用户可一键加载到编辑器中编辑/运行。

这大大简化了用户操作,例如,选择"停车场巡航"模板,即可自动填充脚本,用户只需调整参数(如循环次数)即可运行。模板基于 JSON 文件管理,便于扩展和维护。同时,我们在 UI 中集成模板选择界面,实现"一键场景加载"。此外,模板系统支持参数输入对话框(通过 ParamProvider 类),允许用户在加载或运行时动态填充值。

关键收益:

  • 标准化:统一常见场景的宏逻辑,避免用户自行编写出错。
  • 参数化:使用 Mustache-style 占位符(如 {{preset}}),加载后可通过 UI 对话框自动填充或手动替换。
  • 可扩展:用户或开发者可添加新模板,无需修改代码。
  • 集成性:模板库作为独立面板,嵌入 RightPanel 的 Tabs 中,便于切换。

本篇将逐步说明模板文件结构、参数化机制、UI 集成及配套代码。通过此功能,Pelco KBD300A 模拟器进一步向"现场维护工具"转型,提供开箱即用的场景解决方案。

1. 模板文件结构:templates.json

模板库的核心是 templates.json 文件,位于 resources/ 目录下(可动态加载)。每个模板是一个 JSON 对象,包含名称、描述、脚本及参数列表。

文件结构示例(基于当前项目文件):

json 复制代码
{
    "templates": [
        {
            "name": "停车场巡航",
            "description": "调用预置位 1-4,每位停留 5 秒,循环 3 次",
            "script": "loop(3){\n    send_preset(1)\n    delay(5000)\n    send_preset(2)\n    delay(5000)\n    send_preset(3)\n    delay(5000)\n    send_preset(4)\n    delay(5000)\n}",
            "params": []
        },
        {
            "name": "周界警戒",
            "description": "辅助开关 + PTZ 移动,带参数",
            "script": "aux_on({{aux_id}})\npan_tilt({{pan}}, {{tilt}})\ndelay(2000)\nsend_preset({{preset}})",
            "params": ["aux_id", "pan", "tilt", "preset"]
        },
        {
            "name": "球机调试",
            "description": "球机基本调试:Zoom/Focus/Iris 测试",
            "script": "zoom(1, in)\ndelay(1000)\nzoom(1, out)\ndelay(1000)\nfocus(1, far)\ndelay(1000)\nfocus(1, near)\ndelay(1000)\niris(1, open)\ndelay(1000)\niris(1, close)",
            "params": []
        },
        {
            "name": "巡航 + 报警联动",
            "description": "带参数的巡航宏:可设置巡航次数、预置位范围、报警 AUX 编号",
            "script": "loop({{loops}}){\n    # 巡航预置位范围\n    for(p={{start}}; p<={{end}}; p++){\n        send_preset(1, p)\n        delay(3000)\n    }\n}\n\n# 报警联动动作\naux_on(1, {{aux_id}})\ndelay(2000)\nsend_preset(1, {{alarm_preset}})\n",
            "params": ["loops", "start", "end", "aux_id", "alarm_preset"]
        }
    ]
}
  • name:模板名称,用于 UI 显示。
  • description:简要描述,用于提示用户。
  • script:宏脚本主体,支持我们的宏语法(loop/for/delay/send_preset 等)。
  • params:参数列表(数组),表示脚本中需要替换的占位符(如 {{aux_id}})。如果为空,则模板无参数,直接加载。

设计考虑:

  • 使用 JSON 便于解析和扩展(通过 TemplateLibrary 类加载)。
  • 脚本中占位符采用 {{var}} 或 {{{var}}} 格式(类似 Mustache),优先匹配 {{{var}}} 以避免冲突。
  • 默认提供多个示例模板,覆盖巡航、警戒、调试、联动场景。
  • 文件路径:resources/templates.json,不存在时自动创建默认模板(DEFAULT_TEMPLATES)。

2. 模板参数化机制

参数化是模板的核心,提升复用性。核心类包括:

  • TemplateRenderer(core/template/renderer.py):负责替换占位符。

    • 支持 {{key}} 和 {{{key}}} 两种格式,先替换 {{{key}}} 避免冲突。
    • 示例:TemplateRenderer.render(script, {"aux_id": 1}) 将 {{aux_id}} 替换为 1。
  • ParamProvider(core/template/params.py):提供参数输入对话框。

    • 支持多种类型:int, float, str, bool 等。
    • 通过 QInputDialog 获取用户输入,支持默认值和描述。
    • 示例:param_provider.get_params([{"name": "aux_id", "type": "int"}]) 返回 {"aux_id": 用户输入值}。
  • TemplateControllerTemplateRunner(core/template/controller.py 和 runner.py):集成渲染和运行。

    • Controller 处理加载到编辑器;Runner 使用 MacroEngine 执行渲染后的脚本。

示例流程:

  1. 用户选择"周界警戒"模板。
  2. 系统弹出参数对话框:输入 aux_id, pan, tilt, preset。
  3. 渲染脚本:替换 {{aux_id}} 等为用户值。
  4. 填充到宏编辑器或直接运行。

这比纯静态模板更灵活,适合现场自定义。

3. UI 集成:模板库面板

我们在 RightPanel 的 Tabs 中添加一个新面板:TemplateLibraryPanel,包含模板列表、描述、操作按钮。

UI 布局:

  • 搜索框:QLineEdit,支持名称/描述过滤。

  • 模板列表:QListWidget 显示所有模板名称,支持右键菜单(编辑/删除)。

  • 描述区域:QTextEdit 显示选中模板的描述和脚本预览。

  • 按钮栏:加载到编辑器、直接运行、新增/编辑/删除模板。

  • 集成到 RightPanel :在 ui/right_panel/panel.py 的 __init__ 中添加:

    python 复制代码
    self.template_panel = TemplateLibraryPanel(self)
    self.tabs.addTab(self.template_panel, "模板库")

用户交互:

  • 选择模板 → 显示描述 → 点击"加载" → 参数对话框(若有) → 渲染脚本填充到宏编辑器(切换到宏编辑 Tab)。
  • 点击"运行" → 参数对话框(若有) → 渲染并直接执行模板脚本。
  • 新增/编辑:弹出 TemplateEditDialog,支持输入名称、描述、脚本、参数列表。

信号连接(在 RightPanel):

  • template_panel.load_template.connect(self.macro_editor.set_script):加载到编辑器。

  • template_panel.run_template.connect(self.run_macro):直接运行。

4. 配套代码:模板管理类与面板实现

模板管理类(TemplateLibrary)

位于 core/template/library.py:

python 复制代码
class TemplateLibrary:
    def __init__(self, path):
        self.path = path
        self.templates = []
        self.load()

    def load(self):
        if not os.path.exists(self.path):
            logger.warning(f"模板文件不存在: {self.path}")
            self._create_default_templates()
            return self.templates
        
        try:
            with open(self.path, "r", encoding="utf-8") as f:
                data = json.load(f)
            self.templates = data.get("templates", [])
            logger.info(f"加载了 {len(self.templates)} 个模板")
        except Exception as e:
            logger.error(f"加载模板失败: {e}")
            self.templates = []
            self._create_default_templates()
        return self.templates

    def save(self):
        try:
            with open(self.path, "w", encoding="utf-8") as f:
                json.dump({"templates": self.templates}, f, indent=4, ensure_ascii=False)
            logger.info("模板已保存")
        except Exception as e:
            logger.error(f"保存模板失败: {e}")

    def find(self, name: str):
        return next((t for t in self.templates if t["name"] == name), None)
    
    def _create_default_templates(self):
        """创建默认模板"""
        self.templates = [
            # ... 默认模板列表,与 templates.json 一致
        ]
        logger.info("创建了默认模板")
  • 支持加载/保存/查找模板。
  • 错误处理:加载失败时回退到默认模板。

模板库面板(TemplateLibraryPanel)

位于 ui/right_panel/template_library.py(简化版代码):

python 复制代码
class TemplateLibraryPanel(QtWidgets.QWidget):
    load_template = QtCore.pyqtSignal(str)
    run_template = QtCore.pyqtSignal(str)
    error_occurred = QtCore.pyqtSignal(str)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.lib = TemplateLibrary("resources/templates.json")
        self.param_provider = ParamProvider()  # 参数输入提供器
        self._init_ui()
        self._reload()
    
    def _init_ui(self):
        # ... 布局代码,与文章中示例一致,包括搜索、列表、描述、按钮、右键菜单
    
    def _load_to_editor(self):
        tpl = self.lib.templates[self.template_list.currentRow()]
        script = self._prepare_script(tpl)
        if script:
            self.load_template.emit(script)
    
    def _run_template(self):
        tpl = self._current_tpl()
        if tpl:
            script = self._prepare_script(tpl)
            if script:
                self.run_template.emit(script)
    
    def _prepare_script(self, tpl):
        params = tpl.get("params", [])
        if params:
            values = self.param_provider.get_params(params)
            if values is None:
                QMessageBox.warning(self, "参数错误", "参数获取取消或失败")
                return None
            try:
                return TemplateRenderer.render(tpl["script"], values)
            except Exception as e:
                QMessageBox.critical(self, "渲染错误", f"参数渲染失败: {str(e)}")
                return None
        return tpl["script"]
  • 使用信号解耦:不直接依赖宏编辑器。
  • _prepare_script 集成参数获取和渲染。

5. 测试与优化

  • 单元测试:在 tests/right_panel/test_template_library_ui.py 中测试 JSON 加载、参数提取、渲染。
  • 集成测试:在 tests/integration/test_template_to_editor.py 中测试模板加载到编辑器的流程。
  • 端到端测试:在 tests/e2e/test_template_e2e.py 中模拟用户选择模板 → 输入参数 → 加载/运行。
  • 优化:如果 params 非空,弹出对话框自动渲染;支持参数类型校验(int/float 等);JSON 损坏时回退到 DEFAULT_TEMPLATES。
  • 边界处理:无模板时显示默认;参数取消时不加载。

结语

通过模板库,我们为 Pelco KBD300A 模拟器注入了"一键场景加载"的实用性,用户无需深究宏语法,即可快速部署常见维护任务。这标志着项目从"模拟器"向"智能工具"的转变。下篇将聚焦实时接收数据解析与协议反馈处理,进一步增强现场处理能力。

上一篇 总目录 下一篇

相关推荐
weixin_462446232 小时前
JupyterLab 禁用 Terminal 的三种方法(安装记录,仅供参考)
python·jupyter·jupyterlab
飞Link2 小时前
数据增强中的数据标注、数据重构、协同标注和非LLM驱动的增强
python·重构·数据挖掘
久绊A2 小时前
服务器 CPU2_DIMM_B10 内存 Uncorrectable ECC 故障定位与运维操作指南
运维·服务器·硬件
n***33352 小时前
Linux命令组合大赛:创意与效率的终极对决
linux·运维·服务器
惜.己2 小时前
使用python复制目录以及目录的子目录的文件到脚本运行的目录(工具+源码)
python
DYS_房东的猫2 小时前
macOS 上 C++ 开发完整指南(2026 年版)
开发语言·c++·macos
副露のmagic2 小时前
更弱智的算法学习day 38
python·学习
啊吧怪不啊吧2 小时前
C++之模版详解(进阶)
大数据·开发语言·c++
nvd112 小时前
Python 连接 MCP Server 全指南
开发语言·python