refactor: 建立动态面板注册表,消除硬编码,实现步骤界面的数据驱动渲染与依赖路由

This commit is contained in:
DXC
2026-06-17 16:02:17 +08:00
parent 1949711cda
commit a58744cfbb
2 changed files with 362 additions and 283 deletions

View File

@ -0,0 +1,253 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
面板注册中心
集中定义所有步骤面板的结构化配置,包括:
- 步骤 ID / 类引用 / 标题 / 图标 / 阶段 / 导航显示名
- 步骤间依赖关系(输入字段 → 上游步骤/输出类型/面板属性)
- 构造参数(如 Step13ReportPanel 需要 main_window)
WaterQualityGUI 通过遍历 PANEL_REGISTRY 动态生成导航树、Tab 页、
依赖传递和配置读写,彻底消除硬编码。
"""
from src.gui.panels.step1_panel import Step1Panel
from src.gui.panels.step2_panel import Step2Panel
from src.gui.panels.step3_panel import Step3Panel
from src.gui.panels.step4_sampling_panel import Step4SamplingPanel
from src.gui.panels.step5_clean_panel import Step5CleanPanel
from src.gui.panels.step6_feature_panel import Step6FeaturePanel
from src.gui.panels.step7_index_panel import Step7IndexPanel
from src.gui.panels.step8_ml_train_panel import Step8MlTrainPanel
from src.gui.panels.step9_ml_predict_panel import Step9MlPredictPanel
from src.gui.panels.step10_watercolor_panel import Step10WatercolorPanel
from src.gui.panels.step11_map_panel import Step11MapPanel
from src.gui.panels.step12_viz_panel import Step12VizPanel
from src.gui.panels.step13_report_panel import Step13ReportPanel
PANEL_REGISTRY = [
# ═══════════════════════════════════════════════════════════════
# 阶段一:影像预处理
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step1',
'class_ref': Step1Panel,
'title': '水域掩膜',
'icon': '1.png',
'stage': '阶段一:影像预处理',
'display_name': '1. 水域掩膜生成',
'dependencies': None,
'constructor_kwargs': None,
},
{
'step_id': 'step2',
'class_ref': Step2Panel,
'title': '耀斑检测',
'icon': '2.png',
'stage': '阶段一:影像预处理',
'display_name': '2. 耀斑区域识别',
'dependencies': {
'img_path': ('step1', 'reference_img', 'img_file'),
'water_mask_path': ('step1', 'water_mask', 'water_mask_file'),
},
'constructor_kwargs': None,
},
{
'step_id': 'step3',
'class_ref': Step3Panel,
'title': '耀斑去除',
'icon': '3.png',
'stage': '阶段一:影像预处理',
'display_name': '3. 耀斑去除与修复',
'dependencies': {
'img_path': ('step1', 'reference_img', 'img_file'),
'water_mask': ('step1', 'water_mask', 'water_mask_file'),
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 阶段二:样本数据准备
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step4_sampling',
'class_ref': Step4SamplingPanel,
'title': '采样点布设',
'icon': '4.png',
'stage': '阶段二:样本数据准备',
'display_name': '4. 采样点布设',
'dependencies': {
'deglint_img_path': ('step3', 'deglint_image', 'deglint_img_file'),
'water_mask_path': ('step1', 'water_mask', 'water_mask_file'),
},
'constructor_kwargs': None,
},
{
'step_id': 'step5_clean',
'class_ref': Step5CleanPanel,
'title': '数据清洗',
'icon': '5.png',
'stage': '阶段二:样本数据准备',
'display_name': '5. 数据清洗',
# 业务要求保持输入源独立,不自动抓取 step4_sampling 的输出
'dependencies': None,
'constructor_kwargs': None,
},
{
'step_id': 'step6_feature',
'class_ref': Step6FeaturePanel,
'title': '光谱特征',
'icon': '6.png',
'stage': '阶段二:样本数据准备',
'display_name': '6. 光谱特征提取',
'dependencies': {
'deglint_img_path': ('step3', 'deglint_image', 'deglint_img_file'),
'csv_path': ('step5_clean', 'processed_data', 'csv_file'),
'boundary_mask_path': ('step1', 'water_mask', 'water_mask_file'),
'glint_mask_path': ('step2', 'glint_mask', 'glint_mask_file'),
},
'constructor_kwargs': None,
},
{
'step_id': 'step7_index',
'class_ref': Step7IndexPanel,
'title': '水质光谱指数计算',
'icon': '7.png',
'stage': '阶段二:样本数据准备',
'display_name': '7. 水质指数计算',
'dependencies': {
'training_csv_path': ('step6_feature', 'training_spectra', 'training_data_widget'),
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 阶段三:模型构建与训练
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step8_ml_train',
'class_ref': Step8MlTrainPanel,
'title': '机器学习建模',
'icon': '8.png',
'stage': '阶段三:模型构建与训练',
'display_name': '8. 机器学习建模',
'dependencies': {
'training_csv_file': ('step7_index', 'training_spectra_indices', 'training_csv_file'),
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 阶段四:预测与成果输出
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step9_ml_predict',
'class_ref': Step9MlPredictPanel,
'title': '机器学习预测',
'icon': '10.png',
'stage': '阶段四:预测与成果输出',
'display_name': '9. 机器学习预测',
'dependencies': {
'models_dir': ('step8_ml_train', 'Supervised_Model_Training', 'models_dir_widget'),
},
'constructor_kwargs': None,
},
{
'step_id': 'step10_watercolor',
'class_ref': Step10WatercolorPanel,
'title': '水色指数反演',
'icon': '10.png',
'stage': '阶段四:预测与成果输出',
'display_name': '10. 水色指数反演',
'dependencies': {
'bsq_file': ('step3', 'deglint_image', 'bsq_file'),
},
'constructor_kwargs': None,
},
{
'step_id': 'step11_map',
'class_ref': Step11MapPanel,
'title': '专题图生成',
'icon': '10.png',
'stage': '阶段四:预测与成果输出',
'display_name': '11. 专题图生成',
'dependencies': {
'prediction_csv_dir_edit': ('step9_ml_predict', '9_ML_Prediction', 'prediction_csv_dir_edit'),
'geotiff_dir_edit': ('step10_watercolor', 'WaterIndex_Images', 'geotiff_dir_edit'),
},
'constructor_kwargs': None,
},
{
'step_id': 'step12_viz',
'class_ref': Step12VizPanel,
'title': '可视化',
'icon': '9.png',
'stage': '阶段四:预测与成果输出',
'display_name': '12. 可视化展示',
'dependencies': None,
'constructor_kwargs': None,
},
{
'step_id': 'step13_report',
'class_ref': Step13ReportPanel,
'title': '报告生成',
'icon': '10.png',
'stage': '阶段四:预测与成果输出',
'display_name': '13. 分析报告生成',
'dependencies': None,
'constructor_kwargs': {'main_window'}, # 需要注入 main_window=self
},
]
def build_step_dependencies():
"""从 PANEL_REGISTRY 构建 step_dependencies 字典。
Returns:
dict: {step_id: {input_field: (dep_step, output_type, panel_attr)}}
"""
deps = {}
for entry in PANEL_REGISTRY:
if entry['dependencies']:
deps[entry['step_id']] = entry['dependencies']
return deps
def build_stage_groups():
"""从 PANEL_REGISTRY 构建阶段分组字典。
Returns:
dict: {stage_name: [(step_id, display_name), ...]}
"""
groups = {}
for entry in PANEL_REGISTRY:
stage = entry['stage']
if stage not in groups:
groups[stage] = []
groups[stage].append((entry['step_id'], entry['display_name']))
return groups
def get_tab_index(step_id):
"""根据 step_id 获取其在 PANEL_REGISTRY 中的索引(即 Tab 索引)。"""
for i, entry in enumerate(PANEL_REGISTRY):
if entry['step_id'] == step_id:
return i
return -1
def get_step_id_by_tab_index(tab_index):
"""根据 Tab 索引获取 step_id。"""
if 0 <= tab_index < len(PANEL_REGISTRY):
return PANEL_REGISTRY[tab_index]['step_id']
return None
def get_entry(step_id):
"""根据 step_id 获取注册表条目。"""
for entry in PANEL_REGISTRY:
if entry['step_id'] == step_id:
return entry
return None