事件总线
双向事件:插件 → 宿主/前端 用
emit_event;宿主 → 插件 用handle_host_event。禁止轮询宿主私有状态。
1. 插件 → 宿主 / 前端
python
self.emit_event(event_name: str, payload: Any = None) -> None- 线程安全,任意线程可调。
- 桥接为 QML 信号
pluginEvent(plugin_id, event_name, payload: dict)(QML 侧监听它)。 payload非 dict 时自动包成{"value": payload};None变{}。- 前端主动调插件用
bridge.invokePluginAction/invokePluginActionAsync→ 插件的action_*。
常用事件名(自定义即可,但下面这些宿主有约定行为):
| 事件 | payload | 宿主行为 |
|---|---|---|
device.switching | {"device": str, "reason": str} | 设置页设备行显示「切换中」并禁用下拉;优先展示 reason |
device.ready | {"device": str} | 结束「切换中」状态 |
stream_chunk / stream_done | 自定(assistant 打字机) | 由插件的 QML 自行消费 |
2. 宿主 → 插件(下行)
覆盖基类方法:
python
def handle_host_event(self, event_name: str, payload: dict) -> None: ...订阅方式(二选一,可叠加):
mod.json:
json
"host_events": ["subtitle.updated", "pipeline.state"]- 运行时:
python
host.subscribe_event(event_name: str,
callback: Callable[[str, dict], None] | None = None,
plugin_id: str | None = None) -> bool
host.unsubscribe_event(event_name: str, plugin_id: str | None = None) -> bool
host.unsubscribe_all_events(plugin_id: str | None = None) -> boolcallback省略时回调插件的handle_host_event。- 未订阅的事件不会推给该插件;
host_events: ["*"]订阅全部。 - 插件
shutdown时宿主自动unsubscribe_all_events。
3. 标准宿主事件
payload 均为 dict,字段只增不删:
| 事件 | payload | 时机 |
|---|---|---|
subtitle.updated | {"source", "translation", "speaker", "is_partial"} | 字幕变更(含打字机中间态 is_partial=True) |
subtitle.final | 同上且 is_partial=False | 定稿句 |
history.appended | {"id", "speaker", "transcript", "translation", "time"} | 历史新增一条 |
history.updated | {"id", "speaker", "transcript", "translation"} | 历史既有条目被更新(如补齐译文/延迟) |
pipeline.state | 与 get_pipeline_state() 同构 + "reason" | 启动/停止/加载/场景切换 |
provider.changed | {"plugin_type", "plugin_id", "track"} | 引擎/主题切换(track 形如 "inbound") |
locale.changed | {"locale"} | UI 语言切换 |
config.changed | {"plugin_id", "key", "value"} | 本插件配置被 UI 修改 |
plugin.enabled / plugin.disabled | {"plugin_id"} | 任意插件启停 |
hotkey.triggered | {"plugin_id", "action", "key"} | 本插件热键按下(在 handle_action 之前) |
作用域限制
config.changed 与 hotkey.triggered 的 payload 含本插件私有数据, 宿主只投递给 plugin_id 对应的插件——其它插件即使订阅了 * 也收不到。 其余事件按订阅广播。
4. 典型用法
python
class MyPlugin(BasePlugin):
def initialize(self, config=None):
super().initialize(config)
self.host.subscribe_event("pipeline.state", self._on_pipeline) # 运行时订阅
# 或 mod.json 里写 host_events 后只实现 handle_host_event
def _on_pipeline(self, event_name: str, payload: dict) -> None:
self.host.log("info", f"pipeline running={payload.get('running')}")
def handle_host_event(self, event_name: str, payload: dict) -> None:
if event_name == "subtitle.final":
...