refactor: 引入 EventBus 事件总线,实现各步骤面板间的去中心化自动参数传导,完成最终解耦
This commit is contained in:
@ -124,6 +124,8 @@ from src.gui.core.panel_registry import (
|
||||
get_step_id_by_tab_index,
|
||||
get_entry,
|
||||
)
|
||||
from src.gui.core.event_bus import global_event_bus
|
||||
from src.gui.core.dependency_subscriber import subscribe_panel_to_dependencies
|
||||
from src.gui.dialogs import BandConfirmDialog, AISettingsDialog
|
||||
|
||||
# Pipeline 核心异常(用于预检弹窗)
|
||||
@ -155,6 +157,22 @@ from src.gui.core.pipeline_mode_dialog import PipelineModeDialog
|
||||
from src.gui.core.viz_thread import VisualizationWorkerThread, _viz_training_spectra_csv_path
|
||||
from src.core.workspace_manager import WorkspaceManager
|
||||
|
||||
# pipeline step_id → panel step_id 映射(pipeline 内部编号与 GUI 面板编号不同)
|
||||
PIPELINE_TO_PANEL_STEP = {
|
||||
'step1': 'step1',
|
||||
'step2': 'step2',
|
||||
'step3': 'step3',
|
||||
'step4': 'step5_clean',
|
||||
'step5': 'step6_feature',
|
||||
'step7': 'step7_index',
|
||||
'step8': 'step8_ml_train',
|
||||
'step9': 'step10_watercolor',
|
||||
'step10': 'step4_sampling',
|
||||
'step11_ml': 'step9_ml_predict',
|
||||
'step11': 'step11_map',
|
||||
'step14': 'step11_map',
|
||||
}
|
||||
|
||||
|
||||
class WaterQualityGUI(QMainWindow):
|
||||
"""水质参数反演分析系统主窗口"""
|
||||
@ -181,6 +199,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
# 工作空间管理器(文件扫描、路径发现、配置裁剪)
|
||||
self.workspace_manager = WorkspaceManager()
|
||||
self.workspace_manager.set_step_id_mapping(PIPELINE_TO_PANEL_STEP)
|
||||
|
||||
# 面板实例字典(step_id → panel instance),由 create_content_area 填充
|
||||
self._panels = {}
|
||||
@ -669,6 +688,11 @@ class WaterQualityGUI(QMainWindow):
|
||||
QIcon(self.get_icon_path(icon_name)),
|
||||
title
|
||||
)
|
||||
|
||||
# ★ 事件驱动:面板根据注册表依赖自动订阅 OutputUpdated 事件
|
||||
deps = entry.get('dependencies')
|
||||
if deps:
|
||||
subscribe_panel_to_dependencies(panel, step_id, deps)
|
||||
|
||||
# 连接Tab切换信号,实现双向同步(必须在step_stack创建后)
|
||||
self.step_stack.currentChanged.connect(self.on_tab_changed)
|
||||
@ -809,8 +833,6 @@ class WaterQualityGUI(QMainWindow):
|
||||
tab_index = get_tab_index(item_data)
|
||||
if tab_index >= 0:
|
||||
self.step_stack.setCurrentIndex(tab_index)
|
||||
# 切换到步骤时自动填充输入路径
|
||||
self.auto_populate_step_inputs(item_data)
|
||||
|
||||
def on_tab_changed(self, index):
|
||||
"""Tab页面切换时同步更新左侧步骤列表"""
|
||||
@ -911,88 +933,38 @@ class WaterQualityGUI(QMainWindow):
|
||||
return config
|
||||
|
||||
def auto_populate_step_inputs(self, step_id):
|
||||
"""自动填充指定步骤的输入路径,返回填充的字段数量"""
|
||||
if step_id not in self.step_dependencies:
|
||||
return 0 # 该步骤没有依赖关系
|
||||
|
||||
# 获取对应的面板
|
||||
panel = self.get_step_panel(step_id)
|
||||
if not panel:
|
||||
return 0
|
||||
|
||||
work_dir = getattr(self, 'work_dir', './work_dir')
|
||||
work_path = Path(work_dir)
|
||||
|
||||
dependencies = self.step_dependencies[step_id]
|
||||
filled_count = 0
|
||||
|
||||
ref_img_path = None
|
||||
step1_panel = self._panels.get('step1')
|
||||
if step1_panel and hasattr(step1_panel, 'img_file'):
|
||||
ref_img_path = step1_panel.img_file.get_path()
|
||||
|
||||
for input_field, (dep_step, output_type, panel_attr) in dependencies.items():
|
||||
# 检查面板是否有对应的属性
|
||||
if not hasattr(panel, panel_attr):
|
||||
continue
|
||||
|
||||
file_widget = getattr(panel, panel_attr)
|
||||
|
||||
# ★ 兼容 FileSelectWidget 与原生 QLineEdit
|
||||
current_text = (
|
||||
file_widget.get_path().strip()
|
||||
if hasattr(file_widget, 'get_path')
|
||||
else file_widget.text().strip()
|
||||
)
|
||||
|
||||
# 如果输入框已经有内容,跳过自动填充
|
||||
if current_text:
|
||||
continue
|
||||
|
||||
# 查找依赖步骤的输出文件
|
||||
output_path = self.workspace_manager.find_step_output(work_path, dep_step, output_type, ref_img_path=ref_img_path)
|
||||
|
||||
if output_path and Path(output_path).exists():
|
||||
# ★ 兼容 FileSelectWidget 与原生 QLineEdit
|
||||
if hasattr(file_widget, 'set_path'):
|
||||
file_widget.set_path(str(output_path))
|
||||
else:
|
||||
file_widget.setText(str(output_path))
|
||||
self.log_message(f"自动填充 {step_id}.{input_field}: {output_path}", "info")
|
||||
filled_count += 1
|
||||
|
||||
return filled_count
|
||||
"""自动填充指定步骤的输入路径(事件总线已接管,保留接口兼容)。"""
|
||||
return 0
|
||||
|
||||
def get_step_panel(self, step_id):
|
||||
"""根据步骤ID获取对应的面板对象(从动态注册表查找)"""
|
||||
return self._panels.get(step_id)
|
||||
|
||||
def auto_populate_all_steps(self):
|
||||
"""自动填充所有步骤的输入路径"""
|
||||
"""扫描工作目录并触发事件总线自动填充所有步骤的输入路径。"""
|
||||
work_dir = getattr(self, 'work_dir', './work_dir')
|
||||
work_path = Path(work_dir)
|
||||
|
||||
|
||||
if not work_path.exists():
|
||||
QMessageBox.warning(self, "警告", f"工作目录不存在: {work_dir}\n请先设置正确的工作目录。")
|
||||
return
|
||||
|
||||
# 首先扫描工作目录发现已有的输出文件
|
||||
|
||||
# 扫描工作目录 → WorkspaceManager 发布 OutputUpdated 事件 → 面板自动填充
|
||||
self.workspace_manager.scan_work_directory_for_files(work_path)
|
||||
|
||||
# 从注册表推导步骤顺序(跳过 step1,从 step2 开始)
|
||||
step_order = [e['step_id'] for e in PANEL_REGISTRY if e['step_id'] != 'step1']
|
||||
|
||||
filled_count = 0
|
||||
for step_id in step_order:
|
||||
old_count = filled_count
|
||||
filled_count += self.auto_populate_step_inputs(step_id)
|
||||
|
||||
if filled_count > 0:
|
||||
self.log_message(f"已完成所有步骤的自动路径填充,共填充 {filled_count} 个输入字段", "info")
|
||||
QMessageBox.information(self, "完成", f"自动填充完成!\n共填充了 {filled_count} 个输入字段。")
|
||||
else:
|
||||
self.log_message("未发现可自动填充的路径", "info")
|
||||
QMessageBox.information(self, "完成", "未发现可自动填充的路径。\n请确保工作目录中有相关的输出文件。")
|
||||
|
||||
# 补充发布 reference_img(step1 的输入影像,非 step1 产出但下游依赖它)
|
||||
step1_panel = self._panels.get('step1')
|
||||
if step1_panel and hasattr(step1_panel, 'img_file'):
|
||||
ref_img = step1_panel.img_file.get_path()
|
||||
if ref_img:
|
||||
global_event_bus.publish('OutputUpdated', {
|
||||
'step_id': 'step1',
|
||||
'output_type': 'reference_img',
|
||||
'path': ref_img,
|
||||
})
|
||||
|
||||
self.log_message("✓ 工作目录扫描完成,事件总线已通知所有面板自动填充", "info")
|
||||
QMessageBox.information(self, "完成", "工作目录扫描完成!\n各步骤输入路径已自动填充。")
|
||||
|
||||
def add_auto_fill_buttons_to_panels(self):
|
||||
"""为各个步骤面板添加自动填充按钮(动态遍历所有面板)"""
|
||||
@ -1409,46 +1381,19 @@ class WaterQualityGUI(QMainWindow):
|
||||
QMessageBox.critical(self, "失败", f"流程执行失败:\n\n{message[:200]}")
|
||||
|
||||
def on_step_completed(self, step_name, success, message):
|
||||
"""步骤完成回调:记录输出路径并更新后续步骤"""
|
||||
"""步骤完成回调:记录输出路径,WorkspaceManager 自动发布 EventBus 事件。"""
|
||||
if not success:
|
||||
return
|
||||
|
||||
# 记录步骤输出路径到内存
|
||||
|
||||
work_dir = getattr(self, 'work_dir', './work_dir')
|
||||
work_path = Path(work_dir)
|
||||
|
||||
# 根据步骤名称和约定路径,记录实际输出
|
||||
|
||||
if step_name not in self.workspace_manager.step_outputs:
|
||||
self.workspace_manager.step_outputs[step_name] = {}
|
||||
|
||||
# 扫描工作目录,更新该步骤的输出路径
|
||||
# WorkspaceManager.update_step_outputs 内部会发布 OutputUpdated 事件
|
||||
# 下游面板通过 DependencySubscriber 自动接收并填充
|
||||
self.workspace_manager.update_step_outputs(step_name, work_path)
|
||||
|
||||
# 自动填充依赖该步骤输出的后续步骤
|
||||
self.auto_populate_dependent_steps(step_name)
|
||||
|
||||
def auto_populate_dependent_steps(self, completed_step):
|
||||
"""自动填充依赖于已完成步骤的后续步骤"""
|
||||
ref_img_path = None
|
||||
step1_panel = self._panels.get('step1')
|
||||
if step1_panel and hasattr(step1_panel, 'img_file'):
|
||||
ref_img_path = step1_panel.img_file.get_path()
|
||||
|
||||
for step_id, dependencies in self.step_dependencies.items():
|
||||
for input_field, (dep_step, output_type, panel_attr) in dependencies.items():
|
||||
if dep_step == completed_step:
|
||||
# 找到依赖于刚完成步骤的后续步骤,尝试自动填充
|
||||
panel = self.get_step_panel(step_id)
|
||||
if panel and hasattr(panel, panel_attr):
|
||||
file_widget = getattr(panel, panel_attr)
|
||||
# 如果输入框为空,则自动填充
|
||||
if not file_widget.get_path().strip():
|
||||
work_dir = getattr(self, 'work_dir', './work_dir')
|
||||
work_path = Path(work_dir)
|
||||
output_path = self.workspace_manager.find_step_output(work_path, dep_step, output_type, ref_img_path=ref_img_path)
|
||||
if output_path and Path(output_path).exists():
|
||||
file_widget.set_path(output_path)
|
||||
self.log_message(f"步骤完成后自动填充 {step_id}.{input_field}: {output_path}", "info")
|
||||
|
||||
def run_single_step(self, step_name, config):
|
||||
"""运行单个步骤"""
|
||||
|
||||
Reference in New Issue
Block a user