feat: Step2~Step7 Handler 批量生成 + WorkerThread 接入新调度器
- 新增 6 个 Handler:Step2GlintDetection / Step3GlintRemoval / Step4Sampling / Step5ProcessCsv / Step6ExtractSpectra / Step7CalcIndices - 新增 register_handlers.py:register_all_handlers() 一键注册 Step1~Step7 - 更新 __init__.py:导出全部 7 个 Handler - 重构 worker_thread.py:移除旧 WaterQualityInversionPipeline 导入,改用 PipelineScheduler + register_all_handlers - run_single_step 改为 scheduler.run_step() 调用,保留外部模型透传逻辑
This commit is contained in:
@ -6,8 +6,9 @@ import os
|
||||
import traceback
|
||||
from typing import Dict, List
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
from src.core.pipeline.runner import PipelineRunner, PipelineHalt
|
||||
from src.core.pipeline.context import PipelineContext
|
||||
from src.core.pipeline.runner import PipelineHalt
|
||||
from src.core.handlers.pipeline_scheduler import PipelineScheduler
|
||||
from src.core.handlers.register_handlers import register_all_handlers
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@ -113,9 +114,10 @@ PIPELINE_ERROR_INFO = []
|
||||
|
||||
try:
|
||||
error_info = diagnose_pipeline_import_error()
|
||||
from src.core.water_quality_inversion_pipeline_GUI import WaterQualityInversionPipeline
|
||||
from src.core.handlers.pipeline_scheduler import PipelineScheduler
|
||||
from src.core.handlers.register_handlers import register_all_handlers
|
||||
PIPELINE_AVAILABLE = True
|
||||
print("[OK] 成功导入pipeline模块")
|
||||
print("[OK] 成功导入 Handler 调度器模块")
|
||||
PIPELINE_ERROR_INFO = error_info
|
||||
|
||||
except ImportError as e:
|
||||
@ -140,12 +142,11 @@ except ImportError as e:
|
||||
print(" 2. 如果需要修复,可以在.spec文件中添加unittest模块:")
|
||||
print(" a = Analysis(..., hiddenimports=['unittest', 'unittest.mock'])")
|
||||
print(" 3. 或在PyInstaller命令中添加: --hidden-import unittest")
|
||||
elif "water_quality_inversion_pipeline_GUI" in str(e):
|
||||
elif "handlers" in str(e) or "pipeline_scheduler" in str(e):
|
||||
print("[INFO] 可能的解决方案:")
|
||||
print(" 1. 检查src/core/water_quality_inversion_pipeline_GUI.py文件是否存在")
|
||||
print(" 2. 确保Python路径设置正确")
|
||||
print(" 1. 检查 src/core/handlers/ 目录是否存在")
|
||||
print(" 2. 确保 Python 路径设置正确")
|
||||
print(" 3. 尝试重新安装依赖: pip install -r requirements.txt")
|
||||
print(" 4. 检查Python版本是否兼容(推荐Python 3.8-3.11)")
|
||||
|
||||
import traceback
|
||||
print("\n完整错误追踪:")
|
||||
@ -257,35 +258,32 @@ class WorkerThread(QThread):
|
||||
except Exception:
|
||||
mpl_prev = None
|
||||
try:
|
||||
from src.core.water_quality_inversion_pipeline_GUI import WaterQualityInversionPipeline
|
||||
|
||||
self.pipeline = WaterQualityInversionPipeline(work_dir=self.work_dir)
|
||||
# ── 新架构:PipelineScheduler + Handler 注册表 ──
|
||||
scheduler = PipelineScheduler(work_dir=self.work_dir)
|
||||
scheduler.set_callback(self.pipeline_callback)
|
||||
register_all_handlers(scheduler)
|
||||
self.pipeline = scheduler # 保持兼容(stop() 等引用 self.pipeline)
|
||||
|
||||
if self.mode == 'full':
|
||||
self.log_message.emit("开始运行完整流程 (Runner 调度模式)...", "info")
|
||||
if hasattr(self.pipeline, 'set_callback'):
|
||||
self.pipeline.set_callback(self.pipeline_callback)
|
||||
self.log_message.emit("开始运行完整流程 (Handler 调度模式)...", "info")
|
||||
|
||||
# ── ★ 预检已由 GUI 层 perform_preflight() 完成,此处不再重复预检 ──
|
||||
|
||||
# 构造上下文 (Ctx),将 config 整体注入 user_config
|
||||
ctx = PipelineContext(
|
||||
img_path=self.config.get('step1', {}).get('img_path'),
|
||||
water_mask_path=self.config.get('step1', {}).get('mask_path'),
|
||||
csv_path=self.config.get('step4_sampling', {}).get('csv_path'),
|
||||
boundary_path=self.config.get('step5_clean', {}).get('boundary_path'),
|
||||
boundary_shp_path=self.config.get('step11_map', {}).get('boundary_shp_path'),
|
||||
formula_csv_path=self.config.get('step8_non_empirical_modeling', {}).get('formula_csv_path'),
|
||||
work_dir=self.work_dir,
|
||||
user_config=self.config
|
||||
)
|
||||
# 过滤 skip_list 中的步骤
|
||||
active_config = {
|
||||
k: v for k, v in self.config.items()
|
||||
if k not in self.skip_list
|
||||
}
|
||||
|
||||
# 启动新调度器
|
||||
runner = PipelineRunner(self.pipeline)
|
||||
result_ctx = runner.run(ctx, config=self.config, skip_list=self.skip_list)
|
||||
result = scheduler.run_full_pipeline(active_config)
|
||||
|
||||
if result_ctx.last_error:
|
||||
raise RuntimeError(f"流水线执行失败: {result_ctx.last_error}")
|
||||
errors = result.get('errors', {})
|
||||
if errors:
|
||||
error_lines = [f" {k}: {v}" for k, v in errors.items()]
|
||||
raise RuntimeError(
|
||||
f"流水线部分步骤执行失败 ({len(errors)} 个):\n"
|
||||
+ "\n".join(error_lines)
|
||||
)
|
||||
|
||||
self.progress_update.emit(100, "流程执行完成")
|
||||
self.finished.emit(True, "完整流程执行成功!")
|
||||
@ -293,10 +291,7 @@ class WorkerThread(QThread):
|
||||
self.log_message.emit(f"开始独立运行步骤: {self.step_name}", "info")
|
||||
self.progress_update.emit(0, f"正在执行: {self.step_name}")
|
||||
|
||||
if hasattr(self.pipeline, 'set_callback'):
|
||||
self.pipeline.set_callback(self.pipeline_callback)
|
||||
|
||||
self.run_single_step(self.step_name, self.config)
|
||||
self.run_single_step(scheduler, self.step_name, self.config)
|
||||
|
||||
self.progress_update.emit(100, f"步骤 {self.step_name} 执行完成")
|
||||
self.finished.emit(True, f"步骤 {self.step_name} 独立运行成功!")
|
||||
@ -317,56 +312,24 @@ class WorkerThread(QThread):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def run_single_step(self, step_name, config):
|
||||
"""运行单个步骤"""
|
||||
step_method_map = {
|
||||
'step1': 'step1_generate_water_mask',
|
||||
'step2': 'step2_find_glint_area',
|
||||
'step3': 'step3_remove_glint',
|
||||
'step4_sampling': 'step4_sampling',
|
||||
'step5_clean': 'step5_process_csv',
|
||||
'step6_feature': 'step6_extract_spectra',
|
||||
'step7_index': 'step7_calc_indices',
|
||||
'step8_ml_train': 'step8_train_ml',
|
||||
'step8_non_empirical_modeling': 'step8_non_empirical_modeling',
|
||||
'step8_qaa': 'step8_qaa_inversion',
|
||||
'step9_ml_predict': 'step9_predict_ml',
|
||||
'step10_watercolor': 'step9_watercolor_inversion',
|
||||
'step11_map': 'step10_map',
|
||||
}
|
||||
|
||||
if step_name not in step_method_map:
|
||||
raise ValueError(f"未知的步骤名称: {step_name}")
|
||||
|
||||
method_name = step_method_map[step_name]
|
||||
def run_single_step(self, scheduler, step_name, config):
|
||||
"""使用新调度器运行单个步骤。"""
|
||||
step_config = dict(config.get(step_name, {}))
|
||||
|
||||
# step8_qaa_inversion 内部使用 config.get('step8_qaa', {}) 读取内层,
|
||||
# 必须透传完整 config dict(含外层 step_name key)
|
||||
if step_name == 'step8_qaa':
|
||||
method = getattr(self.pipeline, method_name)
|
||||
result = method(**config)
|
||||
return result
|
||||
|
||||
# 透传面板顶层传入的外部预训练模型(GUI step11_prediction_panel 通过 config['_external_model'] 传入)
|
||||
# 非空才覆盖(遵循 feedback_never_overwrite_with_empty 原则)
|
||||
# 透传外部预训练模型(非空才覆盖)
|
||||
for key in ('_external_model', '_external_model_path',
|
||||
'_external_models_dict', '_external_model_dir'):
|
||||
val = config.get(key)
|
||||
if val is not None and val != "":
|
||||
step_config[key] = val
|
||||
if key == '_external_models_dict':
|
||||
print(f"[Worker] 提取到的外部字典 Keys: {list(val.keys())}")
|
||||
else:
|
||||
print(f"[Worker] 透传 {key}: {val}")
|
||||
|
||||
step_config['skip_dependency_check'] = True
|
||||
|
||||
if step_name in ['step2', 'step3', 'step4_sampling', 'step5_clean', 'step7_index', 'step9_ml_predict']:
|
||||
step_config.pop('output_path', None)
|
||||
|
||||
method = getattr(self.pipeline, method_name)
|
||||
result = method(**step_config)
|
||||
# step8_qaa 特殊处理:透传完整 config(含外层 step8_qaa key)
|
||||
if step_name == 'step8_qaa':
|
||||
result = scheduler.run_step(step_name, config)
|
||||
else:
|
||||
result = scheduler.run_step(step_name, step_config)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user