7.9 KiB
7.9 KiB
name, description, source, extracted_at
| name | description | source | extracted_at |
|---|---|---|---|
| WQ_GUI 数据流转架构 | WQ_GUI ProjectSession 事件总线驱动的步骤间数据传递机制(完整重构版) | auto-skill | 2026-05-28T09:07:34.967Z |
WQ_GUI 数据流转架构
核心结论
整个系统是基于文件路径驱动的管道,所有数据存储在本地磁盘。重构后通过 ProjectSession 事件总线实现 Panel 间完全解耦。
1. 旧架构(旧代码中已删除)
主窗口通过 self.step_outputs 字典 + step_dependencies 配置 + auto_populate_* 系列方法管理步骤间路径填充。存在高度耦合问题:
# 已废弃并删除
self.step_outputs = {}
self._init_step_dependencies()
self.update_step_outputs(step_name, work_path)
self.auto_populate_dependent_steps(completed_step)
self.auto_populate_step_inputs(step_id)
self.find_step_output(work_path, step_id, output_type)
self.add_auto_fill_buttons_to_panels()
self.scan_work_directory_for_files(work_path)
2. 新架构:ProjectSession 事件总线
Session 核心 API(src/core/project_session.py)
class ProjectSession(QObject):
path_updated = pyqtSignal(str, str, str) # step, out_type, path
step_outputs_ready = pyqtSignal(str, str) # step, out_type
def update_output(step, out_type, path):
"""Panel 完成后广播输出路径"""
def update_outputs(step, {out_type: path, ...}):
"""Panel 完成后批量广播多个输出路径"""
def get_output(step, out_type):
"""Panel 可主动查询上游路径(用于自动填充)"""
def get_step_outputs(step):
"""返回该 step 的全部输出字典"""
def scan_work_directory():
"""主窗口 on_step_completed 末尾调用,扫描并广播所有已知路径"""
Panel 重构模板
class StepXPanel(QWidget):
def __init__(self, session=None, parent=None):
super().__init__(parent)
self.session = session
self.work_dir = None
self.init_ui()
self._bind_session_signals()
def _bind_session_signals(self):
if not self.session:
return
self.session.path_updated.connect(
self._on_session_path_updated, Qt.QueuedConnection
)
@pyqtSlot(str, str, str)
def _on_session_path_updated(self, step_name, output_type, path):
print(f"[StepX Debug] 收到广播: step={step_name}, type={output_type}, path={path}")
if step_name == 'step1':
if output_type == 'reference_img':
if not self.img_file.get_path().strip():
self.img_file.set_path(path)
print(f"[StepX] 自动填充参考影像: {path}")
elif output_type == 'water_mask':
if not self.water_mask_file.get_path().strip():
self.water_mask_file.set_path(path)
print(f"[StepX] 自动填充水域掩膜: {path}")
# ...
def on_step_finished(self, success, message):
"""由主窗口 on_step_completed 通过 getattr 动态调用"""
if not success:
return
if self.session:
outputs = {}
path = self.output_widget.get_path().strip()
if path:
outputs['output_type'] = path
if outputs:
self.session.update_outputs('stepX', outputs)
主窗口两处改动
# 1. __init__ 中注入 session(所有 Panel 统一注入)
self.step1_panel = Step1Panel(session=self.session)
self.step2_panel = Step2Panel(session=self.session)
self.step3_panel = Step3Panel(session=self.session)
self.step4_panel = Step4Panel(session=self.session)
self.step5_panel = Step5Panel(session=self.session)
self.step5_5_panel = Step5_5Panel(session=self.session)
self.step6_panel = Step6Panel(session=self.session)
self.step6_5_panel = Step6_5Panel(session=self.session)
self.step6_75_panel = Step6_75Panel(session=self.session)
self.step7_panel = Step7Panel(session=self.session)
self.step8_panel = Step8Panel(session=self.session)
self.step8_5_panel = Step8_5Panel(session=self.session)
self.step8_75_panel = Step8_75Panel(session=self.session)
self.step9_panel = Step9Panel(session=self.session)
# 2. on_step_completed(通用动态获取,无需维护字典)
def on_step_completed(self, step_name, success, message):
if not success:
return
if hasattr(self, 'session') and self.session:
self.session.scan_work_directory()
panel = getattr(self, f"{step_name}_panel", None)
if panel and hasattr(panel, 'on_step_finished'):
panel.on_step_finished(success, message)
3. 全链路事件流
step1 → step2 / step3 路径(通过 Shapefile 栅格化产物)
| 场景 | 广播的 water_mask 路径 |
|---|---|
| NDWI 模式 | output_file 用户指定路径 |
| Shapefile 模式 | {work_dir}/1_water_mask/water_mask_from_shp.dat(优先)若文件不存在则 fallback 回 mask_file.get_path() |
step1 完成
→ step1_panel.on_step_finished()
→ session.update_outputs('step1', {
'reference_img': img_path,
'water_mask': mask_path # 可能是 .dat 或 .shp(见上表)
})
→ step2_panel._on_session_path_updated()
→ step3_panel._on_session_path_updated()
step3 → step5 / step7;step5 → 下游训练
step3.deglint_image ──┬─→ step5.deglint_image(填充 img_file)
└─→ step7.deglint_image(填充 img_file)
step5.training_spectra ──┬─→ step5_5.index_features
├─→ step6.models_dir ──→ step8.predictions
├─→ step6_5.models_dir ──→ step8_5.predictions
└─→ step6_75.models_dir ──→ step8_75.predictions
step7.sampling_points ──┬─→ step8
├─→ step8_5
└─→ step8_75
step8/8_5/8_75.predictions ──→ step9.distribution_map
各 Panel 监听/发布对照表(完整版)
| Panel | 监听 | 发布 |
|---|---|---|
| step1 | — | reference_img, water_mask |
| step2 | step1.reference_img, step1.water_mask |
glint_mask |
| step3 | step1.reference_img, step1.water_mask, step2.glint_mask |
deglint_image |
| step4 | — | processed_data |
| step5 | step3.deglint_image, step4.processed_data, step2.glint_mask |
training_spectra |
| step5_5 | step5.training_spectra |
index_features |
| step6 | step5.training_spectra |
models_dir |
| step6_5 | step5.training_spectra |
models_dir |
| step6_75 | step5.training_spectra |
models_dir |
| step7 | step3.deglint_image, step1.water_mask, step2.glint_mask |
sampling_points |
| step8 | step7.sampling_points, step6.models_dir |
predictions |
| step8_5 | step7.sampling_points, step6_5.models_dir |
predictions |
| step8_75 | step7.sampling_points, step6_75.models_dir |
predictions |
| step9 | step8.predictions, step8_5.predictions, step8_75.predictions |
distribution_map |
4. 关键约束
__init__参数session=None(向后兼容,主窗口可继续不传)- 所有 Panel 的
init_ui / get_config / set_config / update_from_config完整保留 - 删除所有
self.window().stepX_panel跨界访问 - 使用
self.session.get_output()替代直接读取其他 panel 的 widget - 监听使用
Qt.QueuedConnection确保跨线程安全 - 仅在 field 为空时自动填充(
not widget.get_path().strip()) update_from_config中优先从 Session 获取路径,再用 Session 广播- 主窗口
on_step_completed中使用getattr(self, f"{step_name}_panel", None)实现通用动态获取,无需维护硬编码字典 step1Shapefile 模式下,不能直接广播.shp输入文件,必须拼接{work_dir}/1_water_mask/water_mask_from_shp.dat作为产物路径