feat(gui): 全流程面板合并 + 一键式运行 GUI 入口集成
This commit is contained in:
@ -119,19 +119,22 @@ from src.gui.panels.step2_panel import Step2Panel
|
||||
from src.gui.panels.step3_panel import Step3Panel
|
||||
from src.gui.panels.step4_panel import Step4Panel
|
||||
from src.gui.panels.step5_panel import Step5Panel
|
||||
from src.gui.panels.step5_5_panel import Step5_5Panel
|
||||
from src.gui.panels.step6_panel import Step6Panel
|
||||
from src.gui.panels.step6_5_panel import Step6_5Panel
|
||||
from src.gui.panels.step6_75_panel import Step6_75Panel
|
||||
from src.gui.panels.step7_panel import Step7Panel
|
||||
from src.gui.panels.step8_panel import Step8Panel
|
||||
from src.gui.panels.step8_5_panel import Step8_5Panel
|
||||
from src.gui.panels.step8_75_panel import Step8_75Panel
|
||||
from src.gui.panels.step9_panel import Step9Panel
|
||||
from src.gui.panels.step8_panel import Step8Panel # was step5_5_panel
|
||||
from src.gui.panels.step7_panel import Step7Panel # was step6_panel
|
||||
from src.gui.panels.step8_non_empirical_panel import Step8NonEmpiricalPanel # was step6_5_panel
|
||||
from src.gui.panels.step9_panel import Step9Panel # was step6_75_panel
|
||||
from src.gui.panels.step10_panel import Step10Panel # was step7_panel
|
||||
from src.gui.panels.step11_ml_panel import Step11MlPanel # ML prediction (step11_ml)
|
||||
from src.gui.panels.step11_panel import Step11Panel # was step8_5_panel
|
||||
from src.gui.panels.step12_panel import Step12Panel # was step8_75_panel
|
||||
from src.gui.panels.step14_panel import Step14Panel # was step9_panel
|
||||
from src.gui.dialogs import BandConfirmDialog, AISettingsDialog
|
||||
from src.gui.panels.visualization_panel import VisualizationPanel
|
||||
from src.gui.panels.report_generation_panel import ReportGenerationPanel
|
||||
|
||||
# Pipeline 核心异常(用于预检弹窗)
|
||||
from src.core.pipeline.runner import PipelineHalt
|
||||
|
||||
# Matplotlib相关导入 (推迟并加入底层防爆保护)
|
||||
import matplotlib
|
||||
try:
|
||||
@ -152,6 +155,9 @@ from src.gui.core.worker_thread import (
|
||||
check_pipeline_dependencies,
|
||||
diagnose_pipeline_import_error,
|
||||
)
|
||||
# 预检交互对话框
|
||||
from src.gui.core.preflight_dialog import PreflightDialog
|
||||
from src.gui.core.pipeline_mode_dialog import PipelineModeDialog
|
||||
|
||||
|
||||
def _viz_training_spectra_csv_path(work_path: Path) -> Path:
|
||||
@ -1384,31 +1390,31 @@ class WaterQualityGUI(QMainWindow):
|
||||
'step5': {
|
||||
'training_spectra': '5_training_spectra/training_spectra.csv'
|
||||
},
|
||||
'step5_5': {
|
||||
'step8': {
|
||||
'water_indices': '6_water_quality_indices/water_quality_indices.csv'
|
||||
},
|
||||
'step6': {
|
||||
'step7': {
|
||||
'models': '7_Supervised_Model_Training/' # 目录,包含各参数子目录
|
||||
},
|
||||
'step6_5': {
|
||||
'step8_non_empirical_modeling': {
|
||||
'regression_models': '8_Regression_Modeling/' # 目录,包含各参数子目录
|
||||
},
|
||||
'step6_75': {
|
||||
'step9': {
|
||||
'custom_regression_models': '9_Custom_Regression_Modeling/' # 目录
|
||||
},
|
||||
'step7': {
|
||||
'step10': {
|
||||
'sampling_points': '10_sampling/sampling_spectra.csv'
|
||||
},
|
||||
'step8': {
|
||||
'step11_ml': {
|
||||
'predictions': '11_12_13_predictions/Machine_Learning_Prediction/' # 目录,包含机器学习预测结果
|
||||
},
|
||||
'step8_5': {
|
||||
'step11': {
|
||||
'regression_predictions': '11_12_13_predictions/Non_Empirical_Prediction/' # 目录,包含非经验模型预测结果
|
||||
},
|
||||
'step8_75': {
|
||||
'step12': {
|
||||
'custom_predictions': '11_12_13_predictions/Custom_Regression_Prediction/' # 目录,包含自定义回归预测结果
|
||||
},
|
||||
'step9': {
|
||||
'step14': {
|
||||
'distribution_maps': '14_visualization/' # 目录,包含专题图
|
||||
}
|
||||
}
|
||||
@ -1432,37 +1438,37 @@ class WaterQualityGUI(QMainWindow):
|
||||
'boundary_mask_path': ('step1', 'water_mask', 'boundary_mask_file'), # 步骤5可选水体掩膜
|
||||
'glint_mask_path': ('step2', 'glint_mask', 'glint_mask_file') # 步骤5可选耀斑掩膜
|
||||
},
|
||||
'step5_5': {
|
||||
'training_csv_path': ('step5', 'training_spectra', 'output_file') # 步骤5.5需要步骤5输出的训练光谱
|
||||
},
|
||||
'step6': {
|
||||
'csv_path': ('step5', 'training_spectra', 'csv_file') # 步骤6需要训练光谱数据
|
||||
},
|
||||
'step6_5': {
|
||||
'csv_path': ('step5', 'training_spectra', 'csv_file') # 步骤6.5需要训练光谱数据
|
||||
},
|
||||
'step6_75': {
|
||||
'csv_path': ('step5', 'training_spectra', 'csv_file') # 步骤6.75需要训练光谱数据
|
||||
'step8': {
|
||||
'training_csv_path': ('step5', 'training_spectra', 'output_file') # 步骤8需要步骤5输出的训练光谱
|
||||
},
|
||||
'step7': {
|
||||
'deglint_img_path': ('step3', 'deglint_image', 'deglint_img_file'), # 步骤7需要去耀斑影像
|
||||
'water_mask_path': ('step1', 'water_mask', 'water_mask_file'), # 步骤7需要水域掩膜
|
||||
'glint_mask_path': ('step2', 'glint_mask', 'glint_mask_file') # 步骤7可选耀斑掩膜
|
||||
'csv_path': ('step5', 'training_spectra', 'csv_file') # 步骤7需要训练光谱数据
|
||||
},
|
||||
'step8': {
|
||||
'sampling_csv_path': ('step7', 'sampling_points', 'sampling_csv_file'), # 步骤8需要采样点
|
||||
'models_dir': ('step6', 'models', 'models_dir_file') # 步骤8需要训练好的模型
|
||||
},
|
||||
'step8_5': {
|
||||
'sampling_csv_path': ('step7', 'sampling_points', 'sampling_csv_file'), # 步骤8.5需要采样点
|
||||
'models_dir': ('step6_5', 'regression_models', 'models_dir') # 步骤8.5需要回归模型
|
||||
},
|
||||
'step8_75': {
|
||||
'sampling_csv_path': ('step7', 'sampling_points', 'sampling_csv_file'), # 步骤8.75需要采样点
|
||||
'models_dir': ('step6_75', 'custom_regression_models', 'models_dir') # 步骤8.75需要自定义回归模型
|
||||
'step8_non_empirical_modeling': {
|
||||
'csv_path': ('step5', 'training_spectra', 'csv_file') # 步骤8非经验建模需要训练光谱数据
|
||||
},
|
||||
'step9': {
|
||||
'prediction_csv_path': ('step8', 'predictions', 'prediction_csv_file') # 步骤9需要预测结果CSV
|
||||
'csv_path': ('step5', 'training_spectra', 'csv_file') # 步骤9需要训练光谱数据
|
||||
},
|
||||
'step10': {
|
||||
'deglint_img_path': ('step3', 'deglint_image', 'deglint_img_file'), # 步骤10需要去耀斑影像
|
||||
'water_mask_path': ('step1', 'water_mask', 'water_mask_file'), # 步骤10需要水域掩膜
|
||||
'glint_mask_path': ('step2', 'glint_mask', 'glint_mask_file') # 步骤10可选耀斑掩膜
|
||||
},
|
||||
'step11_ml': {
|
||||
'sampling_csv_path': ('step10', 'sampling_points', 'sampling_csv_file'), # 步骤11ML需要采样点
|
||||
'models_dir': ('step7', 'models', 'models_dir_file') # 步骤11ML需要训练好的模型
|
||||
},
|
||||
'step11': {
|
||||
'sampling_csv_path': ('step10', 'sampling_points', 'sampling_csv_file'), # 步骤11需要采样点
|
||||
'models_dir': ('step8_non_empirical_modeling', 'regression_models', 'models_dir') # 步骤11需要回归模型
|
||||
},
|
||||
'step12': {
|
||||
'sampling_csv_path': ('step10', 'sampling_points', 'sampling_csv_file'), # 步骤12需要采样点
|
||||
'models_dir': ('step9', 'custom_regression_models', 'models_dir') # 步骤12需要自定义回归模型
|
||||
},
|
||||
'step14': {
|
||||
'prediction_csv_path': ('step11_ml', 'predictions', 'prediction_csv_file') # 步骤14需要预测结果CSV
|
||||
}
|
||||
}
|
||||
|
||||
@ -1545,7 +1551,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
self.setWindowTitle("MegaCube-Water Quality V1.1")
|
||||
self.setWindowTitle("MegaCube-Water Quality V1.2")
|
||||
|
||||
# 获取屏幕可用区域(排除任务栏)
|
||||
screen_geometry = QApplication.primaryScreen().availableGeometry()
|
||||
@ -1730,7 +1736,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
def create_banner_widget(self):
|
||||
"""创建横幅区域 - 支持自适应等比缩放"""
|
||||
# 横幅标题文字(方便后续直接修改版本号)
|
||||
self._APP_TITLE = "MegaCube-Water Quality V1.1"
|
||||
self._APP_TITLE = "MegaCube-Water Quality V1.2"
|
||||
|
||||
# 创建横幅容器
|
||||
banner_widget = QWidget()
|
||||
@ -1844,19 +1850,19 @@ class WaterQualityGUI(QMainWindow):
|
||||
"阶段二:样本数据准备 ": [
|
||||
("step4", "4. 数据标准化处理"),
|
||||
("step5", "5. 光谱特征提取"),
|
||||
("step5_5", "6. 水质参数指数计算"),
|
||||
("step8", "6. 水质参数指数计算"),
|
||||
],
|
||||
"阶段三:模型构建与训练": [
|
||||
("step6", "7. 机器学习模型训练"),
|
||||
("step6_5", "8. 回归模型训练"),
|
||||
("step6_75", "9. 自定义回归模型训练"),
|
||||
("step7", "7. 机器学习模型训练"),
|
||||
("step8_non_empirical_modeling", "8. 回归模型训练"),
|
||||
("step9", "9. 自定义回归模型训练"),
|
||||
],
|
||||
"阶段四:预测与成果输出 ": [
|
||||
("step7", "10. 采样点布设"),
|
||||
("step8", "11. 机器学习预测"),
|
||||
("step8_5", "12. 回归预测"),
|
||||
("step8_75", "13. 自定义回归预测"),
|
||||
("step9", "14. 专题图生成"),
|
||||
("step10", "10. 采样点布设"),
|
||||
("step11_ml", "11. 机器学习预测"),
|
||||
("step11", "12. 回归预测"),
|
||||
("step12", "13. 自定义回归预测"),
|
||||
("step14", "14. 专题图生成"),
|
||||
("step9_viz", "15. 可视化分析"),
|
||||
("step_report", "16. 分析报告生成"),
|
||||
]
|
||||
@ -1878,7 +1884,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step_list.addItem(stage_item)
|
||||
|
||||
# 添加该阶段的所有步骤
|
||||
HIDDEN_STEP_IDS = {"step6_5", "step6_75", "step8_5", "step8_75"}
|
||||
HIDDEN_STEP_IDS = {"step8_non_empirical_modeling", "step9", "step11", "step12"}
|
||||
for step_id, step_display in steps:
|
||||
if step_id in HIDDEN_STEP_IDS:
|
||||
continue
|
||||
@ -1958,36 +1964,36 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step5_panel = Step5Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step5_panel), QIcon(self.get_icon_path("5.png")), "特征构建")
|
||||
|
||||
self.step5_5_panel = Step5_5Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step5_5_panel), QIcon(self.get_icon_path("5.png")), "水质指数")
|
||||
|
||||
self.step6_panel = Step6Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step6_panel), QIcon(self.get_icon_path("6.png")), "监督建模")
|
||||
|
||||
self.step6_5_panel = Step6_5Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step6_5_panel), QIcon(self.get_icon_path("6.png")), "回归建模")
|
||||
self.step_stack.tabBar().setTabVisible(7, False) # 隐藏回归建模 Tab
|
||||
|
||||
self.step6_75_panel = Step6_75Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step6_75_panel), QIcon(self.get_icon_path("6.png")), "自定义回归建模")
|
||||
self.step_stack.tabBar().setTabVisible(8, False) # 隐藏自定义回归建模 Tab
|
||||
self.step8_panel = Step8Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step8_panel), QIcon(self.get_icon_path("5.png")), "水质指数")
|
||||
|
||||
self.step7_panel = Step7Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step7_panel), QIcon(self.get_icon_path("7.png")), "采样点布设")
|
||||
|
||||
self.step8_panel = Step8Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step8_panel), QIcon(self.get_icon_path("8.png")), "监督预测")
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step7_panel), QIcon(self.get_icon_path("6.png")), "监督建模")
|
||||
|
||||
self.step8_5_panel = Step8_5Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step8_5_panel), QIcon(self.get_icon_path("8.png")), "回归预测")
|
||||
self.step_stack.tabBar().setTabVisible(11, False) # 隐藏回归预测 Tab
|
||||
|
||||
self.step8_75_panel = Step8_75Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step8_75_panel), QIcon(self.get_icon_path("8.png")), "自定义回归预测")
|
||||
self.step_stack.tabBar().setTabVisible(12, False) # 隐藏自定义回归预测 Tab
|
||||
self.step8_non_empirical_panel = Step8NonEmpiricalPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step8_non_empirical_panel), QIcon(self.get_icon_path("6.png")), "回归建模")
|
||||
self.step_stack.tabBar().setTabVisible(7, False) # 隐藏回归建模 Tab
|
||||
|
||||
self.step9_panel = Step9Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step9_panel), QIcon(self.get_icon_path("10.png")), "专题图生成")
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step9_panel), QIcon(self.get_icon_path("6.png")), "自定义回归建模")
|
||||
self.step_stack.tabBar().setTabVisible(8, False) # 隐藏自定义回归建模 Tab
|
||||
|
||||
self.step10_panel = Step10Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step10_panel), QIcon(self.get_icon_path("7.png")), "采样点布设")
|
||||
|
||||
self.step11_ml_panel = Step11MlPanel() # ML prediction panel (step11_ml)
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step11_ml_panel), QIcon(self.get_icon_path("8.png")), "监督预测")
|
||||
|
||||
self.step11_panel = Step11Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step11_panel), QIcon(self.get_icon_path("8.png")), "回归预测")
|
||||
self.step_stack.tabBar().setTabVisible(11, False) # 隐藏回归预测 Tab
|
||||
|
||||
self.step12_panel = Step12Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step12_panel), QIcon(self.get_icon_path("8.png")), "自定义回归预测")
|
||||
self.step_stack.tabBar().setTabVisible(12, False) # 隐藏自定义回归预测 Tab
|
||||
|
||||
self.step14_panel = Step14Panel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.step14_panel), QIcon(self.get_icon_path("10.png")), "专题图生成")
|
||||
|
||||
self.viz_panel = VisualizationPanel()
|
||||
self.step_stack.addTab(self.create_scroll_area(self.viz_panel), QIcon(self.get_icon_path("9.png")), "可视化")
|
||||
@ -2137,15 +2143,15 @@ class WaterQualityGUI(QMainWindow):
|
||||
'step3': 2,
|
||||
'step4': 3,
|
||||
'step5': 4,
|
||||
'step5_5': 5,
|
||||
'step6': 6,
|
||||
'step6_5': 7,
|
||||
'step6_75': 8,
|
||||
'step7': 9,
|
||||
'step8': 10,
|
||||
'step8_5': 11,
|
||||
'step8_75': 12,
|
||||
'step9': 13,
|
||||
'step8': 5,
|
||||
'step7': 6,
|
||||
'step8_non_empirical_modeling': 7,
|
||||
'step9': 8,
|
||||
'step10': 9,
|
||||
'step11_ml': 10,
|
||||
'step11': 11,
|
||||
'step12': 12,
|
||||
'step14': 13,
|
||||
'step9_viz': 14,
|
||||
'step_report': 15,
|
||||
}
|
||||
@ -2168,15 +2174,15 @@ class WaterQualityGUI(QMainWindow):
|
||||
2: 'step3',
|
||||
3: 'step4',
|
||||
4: 'step5',
|
||||
5: 'step5_5',
|
||||
6: 'step6',
|
||||
7: 'step6_5',
|
||||
8: 'step6_75',
|
||||
9: 'step7',
|
||||
10: 'step8',
|
||||
11: 'step8_5',
|
||||
12: 'step8_75',
|
||||
13: 'step9',
|
||||
5: 'step8',
|
||||
6: 'step7',
|
||||
7: 'step8_non_empirical_modeling',
|
||||
8: 'step9',
|
||||
9: 'step10',
|
||||
10: 'step11_ml',
|
||||
11: 'step11',
|
||||
12: 'step12',
|
||||
13: 'step14',
|
||||
14: 'step9_viz',
|
||||
15: 'step_report',
|
||||
}
|
||||
@ -2213,41 +2219,41 @@ class WaterQualityGUI(QMainWindow):
|
||||
elif index == 4:
|
||||
self.step5_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step5_5 切换时自动填充输出路径
|
||||
# Step8(水质指数)切换时自动填充输出路径
|
||||
elif index == 5:
|
||||
self.step5_5_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
self.step8_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step6 切换时自动填充训练数据和输出路径
|
||||
# Step7(监督建模)切换时自动填充训练数据和输出路径
|
||||
elif index == 6:
|
||||
self.step6_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step6.5(非经验回归建模)切换时自动填充训练数据和模型目录
|
||||
elif index == 7:
|
||||
self.step6_5_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step6.75(自定义回归建模)切换时自动填充训练数据和模型目录
|
||||
elif index == 8:
|
||||
self.step6_75_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step7(采样点布设)切换时自动填充掩膜和输出路径
|
||||
elif index == 9:
|
||||
self.step7_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step8非经验建模切换时自动填充训练数据和模型目录
|
||||
elif index == 7:
|
||||
self.step8_non_empirical_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step9(自定义回归建模)切换时自动填充训练数据和模型目录
|
||||
elif index == 8:
|
||||
self.step9_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step10(采样点布设)切换时自动填充掩膜和输出路径
|
||||
elif index == 9:
|
||||
self.step10_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step8(机器学习预测)切换时自动填充采样光谱和模型目录
|
||||
elif index == 10:
|
||||
self.step8_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
self.step11_ml_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step8.5(非经验模型预测)切换时自动填充采样光谱和回归模型目录
|
||||
# Step11(回归预测)切换时自动填充采样光谱和回归模型目录
|
||||
elif index == 11:
|
||||
self.step8_5_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
self.step11_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step8.75(自定义回归预测)切换时自动填充采样光谱和自定义回归模型目录
|
||||
# Step12(自定义回归预测)切换时自动填充采样光谱和自定义回归模型目录
|
||||
elif index == 12:
|
||||
self.step8_75_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
self.step12_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# Step9(专题图生成)切换时自动填充预测结果目录
|
||||
# Step14(专题图生成)切换时自动填充预测结果目录
|
||||
elif index == 13:
|
||||
self.step9_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
self.step14_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||
|
||||
# 可视化分析面板切换时自动推断图像目录并加载目录树
|
||||
elif index == 14:
|
||||
@ -2294,22 +2300,22 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.step4_panel.set_config(config['step4'])
|
||||
if 'step5' in config:
|
||||
self.step5_panel.set_config(config['step5'])
|
||||
if 'step5_5' in config:
|
||||
self.step5_5_panel.set_config(config['step5_5'])
|
||||
if 'step6' in config:
|
||||
self.step6_panel.set_config(config['step6'])
|
||||
if 'step6_5' in config:
|
||||
self.step6_5_panel.set_config(config['step6_5'])
|
||||
if 'step6_75' in config:
|
||||
self.step6_75_panel.set_config(config['step6_75'])
|
||||
if 'step7' in config:
|
||||
self.step7_panel.set_config(config['step7'])
|
||||
if 'step8' in config:
|
||||
self.step8_panel.set_config(config['step8'])
|
||||
if 'step8_5' in config:
|
||||
self.step8_5_panel.set_config(config['step8_5'])
|
||||
if 'step7' in config:
|
||||
self.step7_panel.set_config(config['step7'])
|
||||
if 'step8_non_empirical_modeling' in config:
|
||||
self.step8_non_empirical_panel.set_config(config['step8_non_empirical_modeling'])
|
||||
if 'step9' in config:
|
||||
self.step9_panel.set_config(config['step9'])
|
||||
if 'step10' in config:
|
||||
self.step10_panel.set_config(config['step10'])
|
||||
if 'step11_ml' in config:
|
||||
self.step11_ml_panel.set_config(config['step11_ml'])
|
||||
if 'step11' in config:
|
||||
self.step11_panel.set_config(config['step11'])
|
||||
if 'step14' in config:
|
||||
self.step14_panel.set_config(config['step14'])
|
||||
if 'visualization' in config:
|
||||
self.viz_panel.set_config(config['visualization'])
|
||||
if 'report_generation' in config:
|
||||
@ -2352,14 +2358,14 @@ class WaterQualityGUI(QMainWindow):
|
||||
'step3': self.step3_panel.get_config(),
|
||||
'step4': self.step4_panel.get_config(),
|
||||
'step5': self.step5_panel.get_config(),
|
||||
'step5_5': self.step5_5_panel.get_config(),
|
||||
'step6': self.step6_panel.get_config(),
|
||||
'step6_5': self.step6_5_panel.get_config(),
|
||||
'step6_75': self.step6_75_panel.get_config(),
|
||||
'step7': self.step7_panel.get_config(),
|
||||
'step8': self.step8_panel.get_config(),
|
||||
'step8_5': self.step8_5_panel.get_config(),
|
||||
'step7': self.step7_panel.get_config(),
|
||||
'step8_non_empirical_modeling': self.step8_non_empirical_panel.get_config(),
|
||||
'step9': self.step9_panel.get_config(),
|
||||
'step10': self.step10_panel.get_config(),
|
||||
'step11_ml': self.step11_ml_panel.get_config(),
|
||||
'step11': self.step11_panel.get_config(),
|
||||
'step14': self.step14_panel.get_config(),
|
||||
'visualization': self.viz_panel.get_config(),
|
||||
'report_generation': self.report_panel.get_config(),
|
||||
}
|
||||
@ -2410,15 +2416,15 @@ class WaterQualityGUI(QMainWindow):
|
||||
'step3': self.step3_panel,
|
||||
'step4': self.step4_panel,
|
||||
'step5': self.step5_panel,
|
||||
'step5_5': self.step5_5_panel,
|
||||
'step6': self.step6_panel,
|
||||
'step6_5': self.step6_5_panel,
|
||||
'step6_75': self.step6_75_panel,
|
||||
'step7': self.step7_panel,
|
||||
'step8': self.step8_panel,
|
||||
'step8_5': self.step8_5_panel,
|
||||
'step8_75': self.step8_75_panel,
|
||||
'step7': self.step7_panel,
|
||||
'step8_non_empirical_modeling': self.step8_non_empirical_panel,
|
||||
'step9': self.step9_panel,
|
||||
'step10': self.step10_panel,
|
||||
'step11_ml': self.step11_ml_panel,
|
||||
'step11': self.step11_panel,
|
||||
'step12': self.step12_panel,
|
||||
'step14': self.step14_panel,
|
||||
}
|
||||
return panel_map.get(step_id)
|
||||
|
||||
@ -2426,15 +2432,38 @@ class WaterQualityGUI(QMainWindow):
|
||||
"""查找指定步骤的输出文件"""
|
||||
if step_id not in self.step_default_outputs:
|
||||
return None
|
||||
|
||||
|
||||
step_outputs = self.step_default_outputs[step_id]
|
||||
|
||||
|
||||
# ★ 掩膜类型列表:这些类型只接受科学数据格式
|
||||
mask_types = {'water_mask', 'glint_mask', 'boundary_mask'}
|
||||
# ★ 白名单机制:只允许 .dat .tif .tiff .shp,拒绝其他一切格式
|
||||
scientific_extensions = {'.dat', '.tif', '.tiff', '.shp'}
|
||||
# ★ 临时文件关键词黑名单
|
||||
tmp_keywords = ('__tmp', '_tmp')
|
||||
|
||||
def _is_scientific_mask(path_str):
|
||||
"""白名单判断:只有 .dat .tif .tiff .shp 才算科学数据格式"""
|
||||
p = Path(path_str)
|
||||
name_lower = str(path_str).lower()
|
||||
# 拒绝临时文件
|
||||
if any(kw in name_lower for kw in tmp_keywords):
|
||||
return False
|
||||
# 白名单校验
|
||||
return p.suffix.lower() in scientific_extensions
|
||||
|
||||
# 特殊处理:从step_outputs记录中查找实际输出路径
|
||||
if step_id in self.step_outputs:
|
||||
actual_outputs = self.step_outputs[step_id]
|
||||
if output_type in actual_outputs:
|
||||
return actual_outputs[output_type]
|
||||
|
||||
candidate = actual_outputs[output_type]
|
||||
# ★ 掩膜类型白名单二次校验:不在白名单内的一律拒绝
|
||||
if output_type in mask_types and not _is_scientific_mask(candidate):
|
||||
# 非科学格式被拒绝,不使用 step_outputs 中的值
|
||||
pass
|
||||
else:
|
||||
return candidate
|
||||
|
||||
# 根据输出类型查找对应的文件
|
||||
if output_type == 'water_mask':
|
||||
# 水域掩膜:优先查找NDWI生成的,其次是shp生成的
|
||||
@ -2485,19 +2514,19 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 扫描各个子目录
|
||||
subdirs = {
|
||||
'1_water_mask': 'step1',
|
||||
'2_glint': 'step2',
|
||||
'2_glint': 'step2',
|
||||
'3_deglint': 'step3',
|
||||
'4_processed_data': 'step4',
|
||||
'5_training_spectra': 'step5',
|
||||
'6_water_quality_indices': 'step5_5',
|
||||
'7_Supervised_Model_Training': 'step6',
|
||||
'8_Regression_Modeling': 'step6_5',
|
||||
'9_Custom_Regression_Modeling': 'step6_75',
|
||||
'10_sampling': 'step7',
|
||||
'11_12_13_predictions/Machine_Learning_Prediction': 'step8',
|
||||
'11_12_13_predictions/Non_Empirical_Prediction': 'step8_5',
|
||||
'11_12_13_predictions/Custom_Regression_Prediction': 'step8_75',
|
||||
'14_visualization': 'step9'
|
||||
'6_water_quality_indices': 'step8',
|
||||
'7_Supervised_Model_Training': 'step7',
|
||||
'8_Regression_Modeling': 'step8_non_empirical_modeling',
|
||||
'9_Custom_Regression_Modeling': 'step9',
|
||||
'10_sampling': 'step10',
|
||||
'11_12_13_predictions/Machine_Learning_Prediction': 'step11_ml',
|
||||
'11_12_13_predictions/Non_Empirical_Prediction': 'step11',
|
||||
'11_12_13_predictions/Custom_Regression_Prediction': 'step12',
|
||||
'14_visualization': 'step14'
|
||||
}
|
||||
|
||||
for subdir, step_ids in subdirs.items():
|
||||
@ -2517,23 +2546,37 @@ class WaterQualityGUI(QMainWindow):
|
||||
for step_id in step_ids:
|
||||
if step_id not in discovered_outputs:
|
||||
discovered_outputs[step_id] = {}
|
||||
|
||||
|
||||
# ★ 掩膜文件白名单过滤:只有 .dat .tif .tiff .shp 才通过,拒绝 .hdr .xml .png 等
|
||||
scientific_extensions = {'.dat', '.tif', '.tiff', '.shp'}
|
||||
tmp_keywords = ('__tmp', '_tmp')
|
||||
|
||||
def _is_scientific_mask(path_str):
|
||||
"""白名单判断:拒绝 .hdr .xml 临时文件等,只接受科学数据格式"""
|
||||
p = Path(path_str)
|
||||
name_lower = str(path_str).lower()
|
||||
if any(kw in name_lower for kw in tmp_keywords):
|
||||
return False
|
||||
return p.suffix.lower() in scientific_extensions
|
||||
|
||||
# 匹配不同的文件类型
|
||||
if 'water_mask' in file_name and step_id == 'step1':
|
||||
discovered_outputs[step_id]['water_mask'] = str(file_path)
|
||||
if _is_scientific_mask(file_path):
|
||||
discovered_outputs[step_id]['water_mask'] = str(file_path)
|
||||
elif 'glint' in file_name and 'mask' in file_name and step_id == 'step2':
|
||||
discovered_outputs[step_id]['glint_mask'] = str(file_path)
|
||||
if _is_scientific_mask(file_path):
|
||||
discovered_outputs[step_id]['glint_mask'] = str(file_path)
|
||||
elif 'deglint' in file_name and step_id == 'step3':
|
||||
discovered_outputs[step_id]['deglint_image'] = str(file_path)
|
||||
elif 'processed_data' in file_name and step_id == 'step4':
|
||||
discovered_outputs[step_id]['processed_data'] = str(file_path)
|
||||
elif 'training_spectra' in file_name and step_id == 'step5':
|
||||
discovered_outputs[step_id]['training_spectra'] = str(file_path)
|
||||
elif 'water_quality_indices' in file_name and step_id == 'step5_5':
|
||||
elif 'water_quality_indices' in file_name and step_id == 'step8':
|
||||
discovered_outputs[step_id]['water_indices'] = str(file_path)
|
||||
elif 'sampling_spectra' in file_name and step_id == 'step7':
|
||||
elif 'sampling_spectra' in file_name and step_id == 'step10':
|
||||
discovered_outputs[step_id]['sampling_points'] = str(file_path)
|
||||
elif file_name.endswith('.csv') and step_id in ['step8', 'step8_5', 'step8_75']:
|
||||
elif file_name.endswith('.csv') and step_id in ['step11_ml', 'step11', 'step12']:
|
||||
discovered_outputs[step_id]['predictions'] = str(file_path)
|
||||
|
||||
# 更新内部记录
|
||||
@ -2556,8 +2599,8 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 首先扫描工作目录发现已有的输出文件
|
||||
self.scan_work_directory_for_files(work_path)
|
||||
|
||||
step_order = ['step2', 'step3', 'step4', 'step5', 'step5_5', 'step6', 'step6_5', 'step6_75',
|
||||
'step7', 'step8', 'step8_5', 'step8_75', 'step9']
|
||||
step_order = ['step2', 'step3', 'step4', 'step5', 'step8', 'step7', 'step8_non_empirical_modeling', 'step9',
|
||||
'step10', 'step11_ml', 'step11', 'step12', 'step14']
|
||||
|
||||
filled_count = 0
|
||||
for step_id in step_order:
|
||||
@ -2579,15 +2622,15 @@ class WaterQualityGUI(QMainWindow):
|
||||
('step2', self.step2_panel),
|
||||
('step3', self.step3_panel),
|
||||
('step5', self.step5_panel),
|
||||
('step5_5', self.step5_5_panel),
|
||||
('step6', self.step6_panel),
|
||||
('step6_5', self.step6_5_panel),
|
||||
('step6_75', self.step6_75_panel),
|
||||
('step7', self.step7_panel),
|
||||
('step8', self.step8_panel),
|
||||
('step8_5', self.step8_5_panel),
|
||||
('step8_75', self.step8_75_panel),
|
||||
('step9', self.step9_panel)
|
||||
('step7', self.step7_panel),
|
||||
('step8_non_empirical_modeling', self.step8_non_empirical_panel),
|
||||
('step9', self.step9_panel),
|
||||
('step10', self.step10_panel),
|
||||
('step11_ml', self.step11_ml_panel),
|
||||
('step11', self.step11_panel),
|
||||
('step12', self.step12_panel),
|
||||
('step14', self.step14_panel)
|
||||
]
|
||||
|
||||
for step_id, panel in panels_with_dependencies:
|
||||
@ -2735,7 +2778,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
"""显示关于对话框"""
|
||||
QMessageBox.about(
|
||||
self, "关于",
|
||||
"MegaCube-Water Quality V1.1\n\n"
|
||||
"MegaCube-Water Quality V1.2\n\n"
|
||||
"一个完整的水质参数反演工作流程工具\n\n"
|
||||
"功能包括:\n"
|
||||
"- 水域掩膜生成\n"
|
||||
@ -2858,6 +2901,41 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# ★ 全流程模式动态裁剪
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _prune_config_for_prediction_mode(self, config: dict) -> dict:
|
||||
"""Prediction-only 模式:禁用训练相关步骤,保留预测和成图步骤。
|
||||
|
||||
被禁用的 step dict 中统一写入 'enabled': False,
|
||||
这些配置最终传给 PipelineRunner,Runner 会跳过它们。
|
||||
同时,被跳过的步骤的 required_input_files 在 build_missing_items
|
||||
中不会被检查,从而自然规避了"CSV 缺失"等训练模式下的误报。
|
||||
|
||||
Args:
|
||||
config: 完整配置字典(来自 get_current_config)
|
||||
|
||||
Returns:
|
||||
裁剪后的 config(深拷贝,原 config 不被修改)
|
||||
"""
|
||||
cfg = copy.deepcopy(config)
|
||||
|
||||
# 在每个训练相关步骤的 dict 中写入 enabled=False
|
||||
training_steps = [
|
||||
"step4", # CSV 实测数据清洗
|
||||
"step5", # 实测点光谱提取(→ training_csv_path)
|
||||
"step7", # ML 监督建模
|
||||
"step8", # 水质指数计算(辅助训练)
|
||||
"step8_non_empirical_modeling", # 非经验回归建模
|
||||
"step9", # 自定义回归建模
|
||||
]
|
||||
for step_id in training_steps:
|
||||
step_cfg = cfg.setdefault(step_id, {})
|
||||
step_cfg["enabled"] = False
|
||||
|
||||
return cfg
|
||||
|
||||
def run_full_pipeline(self):
|
||||
"""运行完整流程"""
|
||||
if not PIPELINE_AVAILABLE:
|
||||
@ -2867,8 +2945,14 @@ class WaterQualityGUI(QMainWindow):
|
||||
)
|
||||
return
|
||||
|
||||
# ── 0) 强制获取 work_dir(禁止依赖外部或全局变量) ──
|
||||
work_dir = getattr(self, 'work_dir', None)
|
||||
if not work_dir:
|
||||
QMessageBox.warning(self, "警告", "未选择工作目录,请先设置工作目录。")
|
||||
return
|
||||
|
||||
# ── 1) 运行前智能预检与自动回填(硬盘已有产物自动跳过) ──
|
||||
work_path = Path(getattr(self, 'work_dir', './work_dir'))
|
||||
work_path = Path(work_dir)
|
||||
self.log_message("正在进行运行前环境预检与自动扫描...", "info")
|
||||
self.scan_work_directory_for_files(work_path)
|
||||
self.auto_populate_all_steps()
|
||||
@ -2878,31 +2962,52 @@ class WaterQualityGUI(QMainWindow):
|
||||
if not self._precheck_step3_bands():
|
||||
return # 用户点"取消运行"
|
||||
|
||||
# ── 1.6) ★ 全流程模式选择弹窗 ──
|
||||
mode_dlg = PipelineModeDialog(main_window=self, parent=self)
|
||||
if mode_dlg.exec() != QDialog.Accepted:
|
||||
return # 用户点"取消"
|
||||
selected_mode = mode_dlg.selected_mode
|
||||
self.log_message(f"[模式选择] 选定模式: {'训练新模型' if selected_mode == 'training' else '使用已有模型直接预测'}", "info")
|
||||
|
||||
# ── 2) 刷新配置(拿到自动填充后的"满血版" config) ──
|
||||
config = self.get_current_config()
|
||||
|
||||
# ── 3) 根基数据校验:step1.img_path(参考影像) ──
|
||||
if not config['step1'].get('img_path'):
|
||||
QMessageBox.warning(self, "警告", "缺失核心数据:请先在步骤 1 中上传【参考影像】!")
|
||||
for i in range(self.step_list.count()):
|
||||
item = self.step_list.item(i)
|
||||
if item.data(Qt.UserRole) == 'step1':
|
||||
self.step_list.setCurrentRow(i)
|
||||
break
|
||||
return
|
||||
# ── 2.1) ★ 根据模式动态裁剪配置 ──
|
||||
if selected_mode == "prediction_only":
|
||||
config = self._prune_config_for_prediction_mode(config)
|
||||
self.log_message("[模式选择] 已裁剪训练相关步骤(step4/5/7/8),进入仅预测模式", "info")
|
||||
|
||||
# ── 4) 软提示:csv_path 缺失 → 模型训练步骤会被静默跳过(不阻断) ──
|
||||
csv_path = config.get('step4', {}).get('csv_path') or config.get('step5', {}).get('csv_path')
|
||||
if not csv_path:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"提示:模型训练将被跳过",
|
||||
"未检测到实测水质数据 (CSV)。\n"
|
||||
"流程将自动跳过模型训练(步骤 4-6),仅执行预测与制图。\n"
|
||||
"如果需要训练新模型,请先在步骤 4 中上传水质数据。",
|
||||
)
|
||||
# ── 3) ★ 一次性全预检 + 用户交互式决策 ──
|
||||
missing_items = PreflightDialog.build_missing_items(config)
|
||||
if missing_items:
|
||||
critical_items = [it for it in missing_items if it.is_critical]
|
||||
if critical_items:
|
||||
lines = "\n".join(f" - [{it.step_name}] {it.reason}" for it in critical_items)
|
||||
QMessageBox.critical(
|
||||
self, "预检失败(阻断性错误)",
|
||||
f"以下为阻断性缺失,流程无法启动:\n\n{lines}\n\n"
|
||||
"请填写后重新运行。"
|
||||
)
|
||||
return
|
||||
dialog = PreflightDialog(missing_items, parent=self)
|
||||
if dialog.exec() != QDialog.Accepted:
|
||||
return
|
||||
result = dialog.get_result()
|
||||
if result is None:
|
||||
return
|
||||
action, *payload = result
|
||||
if action == "fill":
|
||||
_, step_id, tab_index = result
|
||||
self.step_stack.setCurrentIndex(tab_index)
|
||||
self.log_message(f"[预检] 用户选择填写 {step_id},已切换到对应面板。", "info")
|
||||
return
|
||||
skip_list: List[str] = payload[0] if payload else []
|
||||
if skip_list:
|
||||
self.log_message(f"[预检] 用户强制跳过 {len(skip_list)} 个步骤: {skip_list}", "info")
|
||||
else:
|
||||
skip_list = []
|
||||
|
||||
# 确认执行
|
||||
# ── 4) 确认执行
|
||||
reply = QMessageBox.question(
|
||||
self, "确认",
|
||||
"是否开始执行完整流程?\n\n这可能需要较长时间,请确保配置正确。",
|
||||
@ -2913,19 +3018,18 @@ class WaterQualityGUI(QMainWindow):
|
||||
return
|
||||
|
||||
# 创建pipeline实例
|
||||
work_dir = getattr(self, 'work_dir', './work_dir')
|
||||
self.log_message(f"初始化pipeline,工作目录: {work_dir}", "info")
|
||||
|
||||
# 准备实际运行配置(排除未启用的步骤)
|
||||
worker_config = copy.deepcopy(config)
|
||||
step5_5_cfg = worker_config.get('step5_5')
|
||||
if step5_5_cfg:
|
||||
enabled = step5_5_cfg.pop('enabled', True)
|
||||
step8_cfg = worker_config.get('step8')
|
||||
if step8_cfg:
|
||||
enabled = step8_cfg.pop('enabled', True)
|
||||
if not enabled:
|
||||
worker_config.pop('step5_5', None)
|
||||
worker_config.pop('step8', None)
|
||||
|
||||
# 工作线程内创建 Pipeline,避免主线程阻塞及 Qt5Agg 子线程绘图卡死
|
||||
self.worker = WorkerThread(work_dir, worker_config, mode='full')
|
||||
self.worker = WorkerThread(work_dir, worker_config, mode='full', skip_list=skip_list)
|
||||
self.worker.log_message.connect(self.log_message, Qt.QueuedConnection)
|
||||
self.worker.progress_update.connect(self.update_progress, Qt.QueuedConnection)
|
||||
self.worker.step_completed.connect(self.on_step_completed, Qt.QueuedConnection)
|
||||
@ -3152,14 +3256,14 @@ class WaterQualityGUI(QMainWindow):
|
||||
def update_ui_for_training_mode(self):
|
||||
"""根据训练数据模式更新UI状态"""
|
||||
# 需要禁用的步骤ID(对应无训练数据模式下需要禁用的步骤)
|
||||
disabled_step_ids = ['step4', 'step5', 'step5_5', 'step6', 'step6_5', 'step6_75']
|
||||
disabled_step_ids = ['step4', 'step5', 'step8', 'step7', 'step8_non_empirical_modeling', 'step9']
|
||||
|
||||
# 更新标签页的启用/禁用状态
|
||||
step_id_to_tab = {
|
||||
'step1': 0, 'step2': 1, 'step3': 2, 'step4': 3,
|
||||
'step5': 4, 'step5_5': 5, 'step6': 6, 'step6_5': 7,
|
||||
'step6_75': 8, 'step7': 9, 'step8': 10, 'step8_5': 11,
|
||||
'step8_75': 12, 'step9': 13, 'step9_viz': 14
|
||||
'step5': 4, 'step8': 5, 'step7': 6, 'step8_non_empirical_modeling': 7,
|
||||
'step9': 8, 'step10': 9, 'step11_ml': 10, 'step11': 11,
|
||||
'step12': 12, 'step14': 13, 'step9_viz': 14
|
||||
}
|
||||
|
||||
for step_id in disabled_step_ids:
|
||||
|
||||
Reference in New Issue
Block a user