第7篇. 宏脚本编辑器设计与解释器实现
✨ 引言
在上篇键盘优化子系列中,我们完成了左侧 UI 的全面完善,包括布局、按键和反馈机制,为模拟器注入了真实的操作手感。现在,随着项目从键盘复刻转向完整维护工具,我们引入宏系统:一种自定义脚本机制,用于自动化巡航、报警联动和调试场景。本篇文章将深入宏系统的设计与实现,从语法定义到解释器执行,比较单文件版(无宏,仅基本命令)与最终版的完整引擎(core/macro/)。宏的作用在于简化重复操作(如循环调用预置位),回顾 UI:键盘的模式切换(如 "MACRO")可触发宏编辑面板。这一功能基于 Python 3.7 和 PyQt5,确保 Windows 7 下的线程安全执行。让我们探索从 parser 到 engine 的全链路。
宏示例:cruise_alarm.macro 的 loop/delay/send_preset,展示了巡航 + 报警联动的实用性。
🧬 语法设计
宏语法简单却强大,支持 loop/for 循环、delay 延时、command(如 send_preset/aux_on)和变量(符号表)。设计原则:易学(类似简化 Lua),针对安防场景(预置位/辅助开关)。示例 cruise_alarm.macro:
- loop(2){ send_preset(1,1); delay(3000); ... } # 循环巡航
- aux_on(1,1); delay(2000); # 报警联动
单文件版无宏(命令硬编码);最终版用 AST 表示语法树,便于扩展(未来加 IF/ELSE)。

代码示例(从最终代码提取):
python
# 从 resources/macros/cruise_alarm.macro(示例语法)
loop(2){
send_preset(1, 1)
delay(3000)
# ... (预置位 2-4)
}
aux_on(1, 1)
delay(2000)
这一设计强调可读性,参数用 {{}} 支持模板化。
🔍 Lexer/Parser
Lexer 将脚本 token 化(e.g., integer/ID/LOOP),Parser 构建 AST(Compound/Loop/For/Command)。Token 类型包括 INTEGER/ID/LPAREN 等。过程:advance 跳字符,eat 消费 token,parse() 递归构建树。
对比单文件版:无解析(直接函数调用);最终版支持错误处理(self.error("Invalid expr"))。
代码示例(从最终代码提取):
python
# 从 core/macro/parser.py(Lexer 示例)
class Lexer:
def integer(self):
result = ''
while self.current_char is not None and self.current_char.isdigit():
result += self.current_char
self.advance()
return Token(INTEGER, int(result))
# Parser 示例(构建 Loop)
def loop(self):
self.eat(LOOP)
self.eat(LPAREN)
count = self.expr() # 循环次数
self.eat(RPAREN)
self.eat(LBRACE)
body = self.compound()
self.eat(RBRACE)
return Loop(count, body)
# 单文件版对比:无宏,建议"早期原型无宏,后扩展为 engine"
🚀 解释器
MacroEngine 是执行核心:visit() 递归遍历 AST(_visit_loop 执行 count 次 body)。线程化:moveToThread(macro_thread),run() 在子线程。支持进度(_update_progress emit)和停止(_running=False)。
对比单文件版:无解释器;最终版用 _symbol_table 处理变量,_eval 评估 expr。
代码示例:
python
# 从 core/macro/engine.py(解释器执行)
def _visit_loop(self, node):
count = self._eval(node.count)
for _ in range(count):
if not self._running:
return
self._visit(node.body)
self._update_progress() # 进度 emit
# 线程化
self.macro_engine.moveToThread(self.macro_thread)
self.macro_thread.started.connect(self.macro_engine.run)
🔧 UI 编辑器
macro_editor.py 用 QsciScintilla(QsciLexerLua 高亮)实现编辑,支持保存/删除。新/运行按钮连接 load_template/run_macro emit。进度条(set_progress)实时反馈。
集成:main_window.py 的 self.macro_engine.started.connect(self.right.set_progress(0))。
代码示例:
python
# 从 ui/right_panel/macro_editor.py(UI 编辑器)
self.editor = QsciScintilla()
lexer = QsciLexerLua(self.editor)
self.editor.setLexer(lexer) # 高亮
self.run_btn.clicked.connect(self._run) # emit run_macro
🔗 集成
main_window.py 用 self.macro_engine 连接 UI:_run_macro(script) set_script + run。键盘 "MACRO" 模式 tabs.setCurrentWidget(macro_editor)。
🛡️ 调试
错误处理:parse_macro_script 捕获 ValueError emit error。符号表 (_symbol_table) 调试变量(logger.info)。线程异常用 logger.exception。
对比单文件版:无调试;最终版支持。
🏁 结尾
通过本篇,我们完成了宏编辑器与解释器的设计与实现,将模拟器从静态键盘扩展到自动化工具。这一功能突显了 AST 的可扩展性(未来 IF)。下一篇文章《7.5 Python 专题:线程安全与信号槽机制》将深入探讨宏/串口的并发优化。欢迎在评论区分享你的宏语法设计想法!
基于 Python 3.7(Windows 7)开发 Pelco KBD300A 模拟器兼 Pelco-D / Pelco-P 协议现场维护工具.