refactor: 引入 EventBus 事件总线,实现各步骤面板间的去中心化自动参数传导,完成最终解耦
This commit is contained in:
64
src/gui/core/dependency_subscriber.py
Normal file
64
src/gui/core/dependency_subscriber.py
Normal 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
34
src/gui/core/event_bus.py
Normal 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()
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user