refactor: 引入 EventBus 事件总线,实现各步骤面板间的去中心化自动参数传导,完成最终解耦

This commit is contained in:
DXC
2026-06-17 16:27:26 +08:00
parent a58744cfbb
commit bb5c2a50f8
5 changed files with 220 additions and 120 deletions

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
依赖订阅混入模块
提供 subscribe_panel_to_dependencies() 函数,让步骤面板根据
PANEL_REGISTRY 中声明的 dependencies 自动向 global_event_bus
订阅 OutputUpdated 事件。当上游步骤产出落地时,面板自动将路径
填入对应的 FileSelectWidget,无需主窗口手工传导。
"""
from src.gui.core.event_bus import global_event_bus
def subscribe_panel_to_dependencies(panel, step_id, dependencies):
"""为面板订阅其依赖的上游步骤产出事件。
当 global_event_bus 发布 OutputUpdated 事件且 step_id/output_type
匹配时,自动将路径填入面板对应的 FileSelectWidget。
Args:
panel: 步骤面板实例(QWidget 子类)
step_id: 当前面板的 step_id(仅用于日志,非匹配键)
dependencies: dict, {input_field: (dep_step, output_type, panel_attr)}
"""
if not dependencies:
return
for _input_field, (dep_step, output_type, panel_attr) in dependencies.items():
_make_subscription(panel, dep_step, output_type, panel_attr)
def _make_subscription(panel, dep_step, output_type, panel_attr):
"""为单个依赖项创建事件订阅。使用工厂函数避免闭包变量延迟绑定。"""
def callback(data):
if data.get('step_id') != dep_step:
return
if data.get('output_type') != output_type:
return
widget = getattr(panel, panel_attr, None)
if widget is None:
return
current = ''
if hasattr(widget, 'get_path'):
current = widget.get_path().strip()
elif hasattr(widget, 'text'):
current = widget.text().strip()
if current:
return
path = data.get('path', '')
if not path:
return
if hasattr(widget, 'set_path'):
widget.set_path(path)
elif hasattr(widget, 'setText'):
widget.setText(path)
global_event_bus.subscribe('OutputUpdated', callback)

34
src/gui/core/event_bus.py Normal file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
轻量级事件总线
支持 subscribe(event_name, callback) 和 publish(event_name, data),
用于步骤面板间的去中心化参数传导。
"""
from collections import defaultdict
from typing import Any, Callable, Dict, List
class EventBus:
"""发布-订阅事件总线"""
def __init__(self):
self._subscribers: Dict[str, List[Callable]] = defaultdict(list)
def subscribe(self, event_name: str, callback: Callable[[dict], None]):
"""订阅事件。callback 接收一个 dict 作为事件数据。"""
self._subscribers[event_name].append(callback)
def publish(self, event_name: str, data: Dict[str, Any]):
"""发布事件,通知所有订阅者。"""
for callback in self._subscribers.get(event_name, []):
try:
callback(data)
except Exception:
pass
# 全局单例
global_event_bus = EventBus()

View File

@ -251,3 +251,20 @@ def get_entry(step_id):
if entry['step_id'] == step_id:
return entry
return None
def build_output_types():
"""从 PANEL_REGISTRY 的 dependencies 反向推导每个步骤产出的 output_type 列表。
Returns:
dict: {step_id: [output_type, ...]}
"""
output_types = {}
for entry in PANEL_REGISTRY:
if entry['dependencies']:
for _input_field, (dep_step, output_type, _panel_attr) in entry['dependencies'].items():
if dep_step not in output_types:
output_types[dep_step] = []
if output_type not in output_types[dep_step]:
output_types[dep_step].append(output_type)
return output_types