refactor: 建立动态面板注册表,消除硬编码,实现步骤界面的数据驱动渲染与依赖路由
This commit is contained in:
253
src/gui/core/panel_registry.py
Normal file
253
src/gui/core/panel_registry.py
Normal 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
|
||||
@ -115,21 +115,16 @@ from src.gui.components.custom_widgets import FileSelectWidget
|
||||
from src.gui.components.chart_dialogs import ChartViewerDialog, ChartBrowserDialog, InteractiveViewerDialog, PandasTableModel
|
||||
from src.gui.components.image_viewer_components import ImageCategoryTree, ImageViewerWidget
|
||||
|
||||
# 导入面板组件
|
||||
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.step10_watercolor_panel import Step10WatercolorPanel # 水色指数反演
|
||||
from src.gui.panels.step8_ml_train_panel import Step8MlTrainPanel # 机器学习建模
|
||||
from src.gui.panels.step9_ml_predict_panel import Step9MlPredictPanel # 机器学习预测
|
||||
# 导入面板注册中心(数据驱动,替代硬编码面板导入)
|
||||
from src.gui.core.panel_registry import (
|
||||
PANEL_REGISTRY,
|
||||
build_step_dependencies,
|
||||
build_stage_groups,
|
||||
get_tab_index,
|
||||
get_step_id_by_tab_index,
|
||||
get_entry,
|
||||
)
|
||||
from src.gui.dialogs import BandConfirmDialog, AISettingsDialog
|
||||
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 # 报告生成
|
||||
|
||||
# Pipeline 核心异常(用于预检弹窗)
|
||||
from src.core.pipeline.runner import PipelineHalt
|
||||
@ -187,8 +182,11 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 工作空间管理器(文件扫描、路径发现、配置裁剪)
|
||||
self.workspace_manager = WorkspaceManager()
|
||||
|
||||
# 定义步骤依赖关系
|
||||
self._init_step_dependencies()
|
||||
# 面板实例字典(step_id → panel instance),由 create_content_area 填充
|
||||
self._panels = {}
|
||||
|
||||
# 从注册表构建步骤依赖关系
|
||||
self.step_dependencies = build_step_dependencies()
|
||||
|
||||
self.init_ui()
|
||||
self.apply_stylesheet()
|
||||
@ -198,48 +196,6 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 100ms 延迟足以让 GUI 事件循环启动并显示主窗口
|
||||
QTimer.singleShot(100, self.init_workspace)
|
||||
|
||||
def _init_step_dependencies(self):
|
||||
"""初始化步骤依赖关系"""
|
||||
# 依赖关系字典结构:
|
||||
# '当前步骤ID': { '依赖参数名': ('上游步骤ID', '上游输出类型/Key', '当前步骤接收该路径的组件属性名') }
|
||||
self.step_dependencies = {
|
||||
'step2': {
|
||||
'img_path': ('step1', 'reference_img', 'img_file'),
|
||||
'water_mask_path': ('step1', 'water_mask', 'water_mask_file')
|
||||
},
|
||||
'step3': {
|
||||
'img_path': ('step1', 'reference_img', 'img_file'),
|
||||
'water_mask': ('step1', 'water_mask', 'water_mask_file')
|
||||
},
|
||||
'step4_sampling': {
|
||||
'deglint_img_path': ('step3', 'deglint_image', 'deglint_img_file'),
|
||||
'water_mask_path': ('step1', 'water_mask', 'water_mask_file')
|
||||
},
|
||||
# 'step5_clean': 业务要求保持输入源独立,不自动抓取 step4_sampling 的输出;用户手动浏览导入
|
||||
'step6_feature': {
|
||||
'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'), # step6_panel里叫water_mask_file
|
||||
'glint_mask_path': ('step2', 'glint_mask', 'glint_mask_file')
|
||||
},
|
||||
'step7_index': {
|
||||
'training_csv_path': ('step6_feature', 'training_spectra', 'training_data_widget') # step7 找 step6 的光谱提取
|
||||
},
|
||||
'step8_ml_train': {
|
||||
'training_csv_file': ('step7_index', 'training_spectra_indices', 'training_csv_file') # step8 找 step7 的指数宽表
|
||||
},
|
||||
'step9_ml_predict': {
|
||||
'models_dir': ('step8_ml_train', 'Supervised_Model_Training', 'models_dir_widget')
|
||||
},
|
||||
'step10_watercolor': {
|
||||
'bsq_file': ('step3', 'deglint_image', 'bsq_file') # 水色反演需要去耀斑BSQ影像
|
||||
},
|
||||
'step11_map': {
|
||||
'prediction_csv_dir_edit': ('step9_ml_predict', '9_ML_Prediction', 'prediction_csv_dir_edit'),
|
||||
'geotiff_dir_edit': ('step10_watercolor', 'WaterIndex_Images', 'geotiff_dir_edit')
|
||||
}
|
||||
}
|
||||
|
||||
def get_icon_path(self, icon_filename):
|
||||
"""获取图标文件的完整路径(统一使用 get_resource_path)。"""
|
||||
return get_resource_path(f"data/icons/{icon_filename}")
|
||||
@ -314,8 +270,9 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
# Step1: 只传递工作目录引用,不直接填充路径
|
||||
# 路径填充由 Step1Panel 根据单选按钮状态自动控制
|
||||
if hasattr(self, 'step1_panel'):
|
||||
self.step1_panel.update_work_directory(self.work_dir)
|
||||
step1_panel = self._panels.get('step1')
|
||||
if step1_panel:
|
||||
step1_panel.update_work_directory(self.work_dir)
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
@ -608,35 +565,14 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step_list = QListWidget()
|
||||
self.step_list.setStyleSheet(ModernStylesheet.get_sidebar_stylesheet())
|
||||
|
||||
# 定义四阶段结构
|
||||
self.process_stages = {
|
||||
"阶段一:影像预处理": [
|
||||
("step1", "1. 水域掩膜生成"),
|
||||
("step2", "2. 耀斑区域识别"),
|
||||
("step3", "3. 耀斑去除与修复")
|
||||
],
|
||||
"阶段二:样本数据准备": [
|
||||
("step4_sampling", "4. 采样点布设"),
|
||||
("step5_clean", "5. 数据清洗"),
|
||||
("step6_feature", "6. 光谱特征提取"),
|
||||
("step7_index", "7. 水质指数计算")
|
||||
],
|
||||
"阶段三:模型构建与训练": [
|
||||
("step8_ml_train", "8. 机器学习建模")
|
||||
],
|
||||
"阶段四:预测与成果输出": [
|
||||
("step9_ml_predict", "9. 机器学习预测"),
|
||||
("step10_watercolor", "10. 水色指数反演"),
|
||||
("step11_map", "11. 专题图生成"),
|
||||
("step12_viz", "12. 可视化展示"),
|
||||
("step13_report", "13. 分析报告生成")
|
||||
]
|
||||
}
|
||||
# 从注册表动态构建阶段分组
|
||||
self.process_stages = build_stage_groups()
|
||||
|
||||
# 存储步骤映射
|
||||
self.step_name_map = {}
|
||||
|
||||
# 添加分组项到列表
|
||||
stage_names = list(self.process_stages.keys())
|
||||
for stage_idx, (stage_name, steps) in enumerate(self.process_stages.items()):
|
||||
# 添加阶段标题项(可视化分组)
|
||||
stage_item = QListWidgetItem(stage_name)
|
||||
@ -662,7 +598,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step_list.addItem(item)
|
||||
|
||||
# 在阶段间添加分隔符
|
||||
if stage_idx < len(self.process_stages) - 1:
|
||||
if stage_idx < len(stage_names) - 1:
|
||||
separator_item = QListWidgetItem("")
|
||||
separator_item.setFlags(separator_item.flags() & ~Qt.ItemIsSelectable)
|
||||
separator_item.setFlags(separator_item.flags() & ~Qt.ItemIsEnabled)
|
||||
@ -709,45 +645,30 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step_stack.setTabsClosable(False)
|
||||
self.step_stack.setStyleSheet(ModernStylesheet.get_main_stylesheet())
|
||||
|
||||
# 添加各步骤面板
|
||||
self.step1_panel = Step1Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step1_panel), QIcon(self.get_icon_path("1.png")), "水域掩膜")
|
||||
# 添加各步骤面板(从注册表动态实例化)
|
||||
for entry in PANEL_REGISTRY:
|
||||
step_id = entry['step_id']
|
||||
cls = entry['class_ref']
|
||||
title = entry['title']
|
||||
icon_name = entry['icon']
|
||||
kwargs = entry.get('constructor_kwargs')
|
||||
|
||||
self.step2_panel = Step2Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step2_panel), QIcon(self.get_icon_path("2.png")), "耀斑检测")
|
||||
# 处理特殊构造参数(如 Step13ReportPanel 需要 main_window=self)
|
||||
if kwargs:
|
||||
resolved_kwargs = {}
|
||||
for k in kwargs:
|
||||
if k == 'main_window':
|
||||
resolved_kwargs[k] = self
|
||||
panel = cls(**resolved_kwargs)
|
||||
else:
|
||||
panel = cls()
|
||||
|
||||
self.step3_panel = Step3Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step3_panel), QIcon(self.get_icon_path("3.png")), "耀斑去除")
|
||||
|
||||
self.step4_sampling_panel = Step4SamplingPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step4_sampling_panel), QIcon(self.get_icon_path("4.png")), "采样点布设")
|
||||
|
||||
self.step5_clean_panel = Step5CleanPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step5_clean_panel), QIcon(self.get_icon_path("5.png")), "数据清洗")
|
||||
|
||||
self.step6_feature_panel = Step6FeaturePanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step6_feature_panel), QIcon(self.get_icon_path("6.png")), "光谱特征")
|
||||
|
||||
self.step7_index_panel = Step7IndexPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step7_index_panel), QIcon(self.get_icon_path("7.png")), "水质光谱指数计算")
|
||||
|
||||
self.step8_ml_train_panel = Step8MlTrainPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step8_ml_train_panel), QIcon(self.get_icon_path("8.png")), "机器学习建模")
|
||||
|
||||
self.step9_ml_predict_panel = Step9MlPredictPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step9_ml_predict_panel), QIcon(self.get_icon_path("10.png")), "机器学习预测")
|
||||
|
||||
self.step10_watercolor_panel = Step10WatercolorPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step10_watercolor_panel), QIcon(self.get_icon_path("10.png")), "水色指数反演")
|
||||
|
||||
self.step11_map_panel = Step11MapPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step11_map_panel), QIcon(self.get_icon_path("10.png")), "专题图生成")
|
||||
|
||||
self.step12_viz_panel = Step12VizPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step12_viz_panel), QIcon(self.get_icon_path("9.png")), "可视化")
|
||||
|
||||
self.step13_report_panel = Step13ReportPanel(main_window=self)
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step13_report_panel), QIcon(self.get_icon_path("10.png")), "报告生成")
|
||||
self._panels[step_id] = panel
|
||||
self.step_stack.addTab(
|
||||
self.create_scroll_area(panel),
|
||||
QIcon(self.get_icon_path(icon_name)),
|
||||
title
|
||||
)
|
||||
|
||||
# 连接Tab切换信号,实现双向同步(必须在step_stack创建后)
|
||||
self.step_stack.currentChanged.connect(self.on_tab_changed)
|
||||
@ -884,17 +805,9 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 是阶段标题或分隔符,不切换
|
||||
return
|
||||
|
||||
# 根据步骤ID查找对应的tab索引
|
||||
step_id_to_tab = {
|
||||
'step1': 0, 'step2': 1, 'step3': 2, 'step4_sampling': 3,
|
||||
'step5_clean': 4, 'step6_feature': 5, 'step7_index': 6,
|
||||
'step8_ml_train': 7, 'step9_ml_predict': 8,
|
||||
'step10_watercolor': 9, 'step11_map': 10,
|
||||
'step12_viz': 11, 'step13_report': 12,
|
||||
}
|
||||
|
||||
if item_data in step_id_to_tab:
|
||||
tab_index = step_id_to_tab[item_data]
|
||||
# 根据步骤ID查找对应的tab索引(从注册表动态映射)
|
||||
tab_index = get_tab_index(item_data)
|
||||
if tab_index >= 0:
|
||||
self.step_stack.setCurrentIndex(tab_index)
|
||||
# 切换到步骤时自动填充输入路径
|
||||
self.auto_populate_step_inputs(item_data)
|
||||
@ -904,20 +817,11 @@ class WaterQualityGUI(QMainWindow):
|
||||
if index < 0:
|
||||
return
|
||||
|
||||
# Tab索引到步骤ID的反向映射(13个Tab,index 0-12)
|
||||
tab_to_step_id = {
|
||||
0: 'step1', 1: 'step2', 2: 'step3', 3: 'step4_sampling',
|
||||
4: 'step5_clean', 5: 'step6_feature', 6: 'step7_index',
|
||||
7: 'step8_ml_train', 8: 'step9_ml_predict',
|
||||
9: 'step10_watercolor', 10: 'step11_map',
|
||||
11: 'step12_viz', 12: 'step13_report',
|
||||
}
|
||||
|
||||
if index not in tab_to_step_id:
|
||||
# Tab索引到步骤ID的反向映射(从注册表动态获取)
|
||||
target_step_id = get_step_id_by_tab_index(index)
|
||||
if target_step_id is None:
|
||||
return
|
||||
|
||||
target_step_id = tab_to_step_id[index]
|
||||
|
||||
# 在step_list中查找对应的步骤项
|
||||
for row in range(self.step_list.count()):
|
||||
item = self.step_list.item(row)
|
||||
@ -929,26 +833,9 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step_list.setCurrentRow(row)
|
||||
break
|
||||
|
||||
# 面板自动填充:统一 mapping 覆盖 index 0-12
|
||||
mapping = {
|
||||
0: (self.step1_panel, "Step1"),
|
||||
1: (self.step2_panel, "Step2"),
|
||||
2: (self.step3_panel, "Step3"),
|
||||
3: (self.step4_sampling_panel, "Step4"),
|
||||
4: (self.step5_clean_panel, "Step5"),
|
||||
5: (self.step6_feature_panel, "Step6"),
|
||||
6: (self.step7_index_panel, "Step7"),
|
||||
7: (self.step8_ml_train_panel, "Step8"),
|
||||
8: (self.step9_ml_predict_panel, "Step9"),
|
||||
9: (self.step10_watercolor_panel, "Step10"), # 水色指数反演
|
||||
10: (self.step11_map_panel, "Step11"), # 专题图生成
|
||||
11: (self.step12_viz_panel, "Step12"),
|
||||
12: (self.step13_report_panel, "Step13")
|
||||
}
|
||||
|
||||
if index in mapping:
|
||||
panel, _ = mapping[index]
|
||||
if hasattr(panel, 'update_from_config'):
|
||||
# 面板自动填充:从注册表获取面板实例
|
||||
panel = self._panels.get(target_step_id)
|
||||
if panel and hasattr(panel, 'update_from_config'):
|
||||
panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
def apply_stylesheet(self):
|
||||
@ -981,29 +868,10 @@ class WaterQualityGUI(QMainWindow):
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
|
||||
# 应用配置到各面板
|
||||
if 'step1' in config:
|
||||
self.step1_panel.set_config(config['step1'])
|
||||
if 'step2' in config:
|
||||
self.step2_panel.set_config(config['step2'])
|
||||
if 'step3' in config:
|
||||
self.step3_panel.set_config(config['step3'])
|
||||
if 'step4_sampling' in config:
|
||||
self.step4_sampling_panel.set_config(config['step4_sampling'])
|
||||
if 'step5_clean' in config:
|
||||
self.step5_clean_panel.set_config(config['step5_clean'])
|
||||
if 'step6_feature' in config:
|
||||
self.step6_feature_panel.set_config(config['step6_feature'])
|
||||
if 'step7_index' in config:
|
||||
self.step7_index_panel.set_config(config['step7_index'])
|
||||
if 'step9_ml_predict' in config:
|
||||
self.step9_ml_predict_panel.set_config(config['step9_ml_predict'])
|
||||
if 'step11_map' in config:
|
||||
self.step11_map_panel.set_config(config['step11_map'])
|
||||
if 'step12_viz' in config:
|
||||
self.step12_viz_panel.set_config(config['step12_viz'])
|
||||
if 'step13_report' in config:
|
||||
self.step13_report_panel.set_config(config['step13_report'])
|
||||
# 应用配置到各面板(动态遍历,仅调用有 set_config 的面板)
|
||||
for step_id, panel in self._panels.items():
|
||||
if step_id in config and hasattr(panel, 'set_config'):
|
||||
panel.set_config(config[step_id])
|
||||
|
||||
self.config_file = file_path
|
||||
self.log_message(f"已加载配置: {file_path}", "info")
|
||||
@ -1035,21 +903,11 @@ class WaterQualityGUI(QMainWindow):
|
||||
QMessageBox.critical(self, "错误", f"保存配置失败:\n{str(e)}")
|
||||
|
||||
def get_current_config(self):
|
||||
"""获取当前配置"""
|
||||
config = {
|
||||
'step1': self.step1_panel.get_config(),
|
||||
'step2': self.step2_panel.get_config(),
|
||||
'step3': self.step3_panel.get_config(),
|
||||
'step4_sampling': self.step4_sampling_panel.get_config(),
|
||||
'step5_clean': self.step5_clean_panel.get_config(),
|
||||
'step6_feature': self.step6_feature_panel.get_config(),
|
||||
'step7_index': self.step7_index_panel.get_config(),
|
||||
'step8_ml_train': self.step8_ml_train_panel.get_config(),
|
||||
'step9_ml_predict': self.step9_ml_predict_panel.get_config(),
|
||||
'step11_map': self.step11_map_panel.get_config(),
|
||||
'step12_viz': self.step12_viz_panel.get_config(),
|
||||
'step13_report': self.step13_report_panel.get_config(),
|
||||
}
|
||||
"""获取当前配置(动态遍历所有面板,仅收集有 get_config 的面板)"""
|
||||
config = {}
|
||||
for step_id, panel in self._panels.items():
|
||||
if hasattr(panel, 'get_config'):
|
||||
config[step_id] = panel.get_config()
|
||||
return config
|
||||
|
||||
def auto_populate_step_inputs(self, step_id):
|
||||
@ -1069,8 +927,9 @@ class WaterQualityGUI(QMainWindow):
|
||||
filled_count = 0
|
||||
|
||||
ref_img_path = None
|
||||
if hasattr(self, 'step1_panel'):
|
||||
ref_img_path = self.step1_panel.img_file.get_path()
|
||||
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():
|
||||
# 检查面板是否有对应的属性
|
||||
@ -1105,22 +964,8 @@ class WaterQualityGUI(QMainWindow):
|
||||
return filled_count
|
||||
|
||||
def get_step_panel(self, step_id):
|
||||
"""根据步骤ID获取对应的面板对象"""
|
||||
panel_map = {
|
||||
'step1': self.step1_panel,
|
||||
'step2': self.step2_panel,
|
||||
'step3': self.step3_panel,
|
||||
'step4_sampling': self.step4_sampling_panel,
|
||||
'step5_clean': self.step5_clean_panel,
|
||||
'step6_feature': self.step6_feature_panel,
|
||||
'step7_index': self.step7_index_panel,
|
||||
'step8_ml_train': self.step8_ml_train_panel,
|
||||
'step9_ml_predict': self.step9_ml_predict_panel,
|
||||
'step11_map': self.step11_map_panel,
|
||||
'step12_viz': self.step12_viz_panel,
|
||||
'step13_report': self.step13_report_panel,
|
||||
}
|
||||
return panel_map.get(step_id)
|
||||
"""根据步骤ID获取对应的面板对象(从动态注册表查找)"""
|
||||
return self._panels.get(step_id)
|
||||
|
||||
def auto_populate_all_steps(self):
|
||||
"""自动填充所有步骤的输入路径"""
|
||||
@ -1134,8 +979,8 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 首先扫描工作目录发现已有的输出文件
|
||||
self.workspace_manager.scan_work_directory_for_files(work_path)
|
||||
|
||||
step_order = ['step2', 'step3', 'step4_sampling', 'step5_clean', 'step6_feature', 'step7_index',
|
||||
'step8_ml_train', 'step9_ml_predict', 'step11_map', 'step12_viz', 'step13_report']
|
||||
# 从注册表推导步骤顺序(跳过 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:
|
||||
@ -1150,25 +995,10 @@ class WaterQualityGUI(QMainWindow):
|
||||
QMessageBox.information(self, "完成", "未发现可自动填充的路径。\n请确保工作目录中有相关的输出文件。")
|
||||
|
||||
def add_auto_fill_buttons_to_panels(self):
|
||||
"""为各个步骤面板添加自动填充按钮"""
|
||||
# 这个方法会在UI初始化完成后调用
|
||||
# 为每个有依赖关系的步骤面板添加自动填充功能
|
||||
panels_with_dependencies = [
|
||||
('step2', self.step2_panel),
|
||||
('step3', self.step3_panel),
|
||||
('step4_sampling', self.step4_sampling_panel),
|
||||
('step5_clean', self.step5_clean_panel),
|
||||
('step6_feature', self.step6_feature_panel),
|
||||
('step7_index', self.step7_index_panel),
|
||||
('step8_ml_train', self.step8_ml_train_panel),
|
||||
('step9_ml_predict', self.step9_ml_predict_panel),
|
||||
('step11_map', self.step11_map_panel),
|
||||
('step12_viz', self.step12_viz_panel),
|
||||
('step13_report', self.step13_report_panel),
|
||||
]
|
||||
|
||||
for step_id, panel in panels_with_dependencies:
|
||||
if panel:
|
||||
"""为各个步骤面板添加自动填充按钮(动态遍历所有面板)"""
|
||||
for step_id, panel in self._panels.items():
|
||||
if not panel:
|
||||
continue
|
||||
# 为面板添加自动填充方法
|
||||
def create_auto_fill_method(sid):
|
||||
def auto_fill_inputs():
|
||||
@ -1233,11 +1063,11 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.log_message(f"工作目录已设置: {dir_path}", "info")
|
||||
self.statusBar().showMessage(f"工作目录: {dir_path}")
|
||||
|
||||
# 同步到可视化面板
|
||||
if hasattr(self, 'step12_viz_panel'):
|
||||
self.step12_viz_panel.set_work_dir(dir_path)
|
||||
if hasattr(self, 'step13_report_panel'):
|
||||
self.step13_report_panel.set_work_dir(dir_path)
|
||||
# 同步到可视化/报告面板
|
||||
for sid in ('step12_viz', 'step13_report'):
|
||||
panel = self._panels.get(sid)
|
||||
if panel and hasattr(panel, 'set_work_dir'):
|
||||
panel.set_work_dir(dir_path)
|
||||
|
||||
def open_work_directory(self):
|
||||
"""打开工作目录"""
|
||||
@ -1345,9 +1175,11 @@ class WaterQualityGUI(QMainWindow):
|
||||
"""
|
||||
# 1) 取 step1 影像路径 + step3 配置 + enabled 标志
|
||||
try:
|
||||
img_path = self.step1_panel.img_file.get_path() if hasattr(self, 'step1_panel') else None
|
||||
step3_cfg = self.step3_panel.get_config() if hasattr(self, 'step3_panel') else None
|
||||
step3_enabled = self.step3_panel.enable_checkbox.isChecked() if hasattr(self, 'step3_panel') else False
|
||||
step1_panel = self._panels.get('step1')
|
||||
step3_panel = self._panels.get('step3')
|
||||
img_path = step1_panel.img_file.get_path() if step1_panel else None
|
||||
step3_cfg = step3_panel.get_config() if step3_panel else None
|
||||
step3_enabled = step3_panel.enable_checkbox.isChecked() if step3_panel else False
|
||||
except Exception as e:
|
||||
self.log_message(f"⚠ step3 波段预检:读取面板状态失败 - {e}", "warning")
|
||||
return True # 失败不阻断(防御性:放行比误杀好)
|
||||
@ -1422,7 +1254,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
new_band = dlg.selected_band()
|
||||
try:
|
||||
spin = getattr(self.step3_panel, panel_attr)
|
||||
spin = getattr(step3_panel, panel_attr)
|
||||
spin.setValue(new_band)
|
||||
except AttributeError:
|
||||
self.log_message(f"⚠ step3 panel 缺控件 {panel_attr},跳过回写", "warning")
|
||||
@ -1598,8 +1430,9 @@ class WaterQualityGUI(QMainWindow):
|
||||
def auto_populate_dependent_steps(self, completed_step):
|
||||
"""自动填充依赖于已完成步骤的后续步骤"""
|
||||
ref_img_path = None
|
||||
if hasattr(self, 'step1_panel'):
|
||||
ref_img_path = self.step1_panel.img_file.get_path()
|
||||
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():
|
||||
@ -1740,17 +1573,10 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 需要禁用的步骤ID(对应无训练数据模式下需要禁用的步骤)
|
||||
disabled_step_ids = ['step4_sampling', 'step5_clean', 'step6_feature', 'step7_index', 'step9_ml_predict']
|
||||
|
||||
# 更新标签页的启用/禁用状态
|
||||
step_id_to_tab_training = {
|
||||
'step1': 0, 'step2': 1, 'step3': 2, 'step4_sampling': 3,
|
||||
'step5_clean': 4, 'step6_feature': 5, 'step7_index': 6, 'step9_ml_predict': 7,
|
||||
'step10_watercolor': 9, 'step11_map': 10, 'step12_viz': 11, 'step13_report': 12
|
||||
}
|
||||
|
||||
# 更新标签页的启用/禁用状态(从注册表动态获取 Tab 索引)
|
||||
for step_id in disabled_step_ids:
|
||||
if step_id in step_id_to_tab_training:
|
||||
tab_index = step_id_to_tab_training[step_id]
|
||||
if tab_index < self.step_stack.count():
|
||||
tab_index = get_tab_index(step_id)
|
||||
if tab_index >= 0 and tab_index < self.step_stack.count():
|
||||
self.step_stack.setTabEnabled(tab_index, self.has_training_data)
|
||||
|
||||
# 同时更新导航列表的启用状态
|
||||
|
||||
Reference in New Issue
Block a user