Files
WQ_GUI/.qwen/skills/wq_gui_data_flow/SKILL.md

7.9 KiB
Raw Blame History

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 核心 APIsrc/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 / step7step5 → 下游训练

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) 实现通用动态获取,无需维护硬编码字典
  • step1 Shapefile 模式下,不能直接广播 .shp 输入文件,必须拼接 {work_dir}/1_water_mask/water_mask_from_shp.dat 作为产物路径