feat(gui): 一键运行智能预检

4 段预检彻底解决切换 PipelineRunner 后报 TypeError/静默跳过等问题, 并升级一键运行 UX:

- 预检 1: work_path + log + scan + auto_populate(无需弹窗, 静默回填)

- 预检 2: step3 波段越界 60s 倒计时弹窗(BandConfirmDialog) + gdal 主线程同步读 RasterCount, 越界时 SpinBox 回写 UI

- 预检 3: img_path 硬校验(warning + 跳 step1 + return)

- 预检 4: csv_path 软提示(information + 不 return, 让用户在 QMessageBox.question 二次确认时自己决定是否跳过训练)

新增 src/gui/dialogs.py: BandConfirmDialog(QDialog 子类, 60s 倒计时)
This commit is contained in:
DXC
2026-06-04 10:38:46 +08:00
parent 2139715829
commit 4efe5b871e
2 changed files with 281 additions and 9 deletions

View File

@ -128,6 +128,7 @@ 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.dialogs import BandConfirmDialog
from src.gui.panels.visualization_panel import VisualizationPanel
from src.gui.panels.report_generation_panel import ReportGenerationPanel
@ -2743,7 +2744,110 @@ class WaterQualityGUI(QMainWindow):
"电话010-51292601\n"
"邮箱hanshanlong@iris-rs.cn\n"
)
def _precheck_step3_bands(self) -> bool:
"""步骤 3 波段越界预检(主线程同步执行,避多线程弹窗坑)
读取 step1 影像的 RasterCount校验 step3 面板当前方法下所有波段索引
nir_lower/nir_upper/nir_band/oxy_band/lower_oxy/upper_oxy/hedley_nir_band
是否越界。若越界,弹 BandConfirmDialog60s 倒计时)让用户调整或取消。
Returns:
True: 预检通过或已自动调整run_full_pipeline 继续
False: 用户点"取消运行"run_full_pipeline 应 return
"""
# 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
except Exception as e:
self.log_message(f"⚠ step3 波段预检:读取面板状态失败 - {e}", "warning")
return True # 失败不阻断(防御性:放行比误杀好)
# 早退条件step3 禁用 / 无 img_path / 无 cfg
if not step3_enabled:
return True
if not img_path or not os.path.isfile(img_path):
self.log_message("⚠ step3 波段预检:未找到参考影像,跳过", "info")
return True
if not step3_cfg:
return True
# 2) 读 RasterCountgdal 头信息读取,毫秒级不卡 UI
try:
dataset = gdal.Open(img_path)
if dataset is None:
self.log_message(f"⚠ step3 波段预检gdal 无法打开影像 {img_path}", "warning")
return True
max_band = dataset.RasterCount
dataset = None
except Exception as e:
self.log_message(f"⚠ step3 波段预检:读取 RasterCount 失败 - {e}", "warning")
return True
if max_band <= 0:
return True
# 3) 不同方法对应不同的波段字段cfg_key, panel_attr, 推荐值, 标签)
method = step3_cfg.get('method', 'goodman')
if method == 'goodman':
band_fields = [
('nir_lower', 'nir_lower', 65, 'NIR下波段'),
('nir_upper', 'nir_upper', 91, 'NIR上波段'),
]
elif method == 'kutser':
band_fields = [
('oxy_band', 'oxy_band', 38, '氧吸收波段'),
('lower_oxy', 'lower_oxy', 36, '下氧吸收波段'),
('upper_oxy', 'upper_oxy', 49, '上氧吸收波段'),
('nir_band', 'nir_band', 47, 'NIR波段'),
]
elif method == 'hedley':
band_fields = [
('hedley_nir_band', 'hedley_nir_band', 47, 'NIR波段'),
]
else: # sugar 无波段索引
return True
# 4) 逐字段检查;遇到第一个越界就弹窗(用户处理完继续检查下一个)
for cfg_key, panel_attr, recommended, label in band_fields:
requested = step3_cfg.get(cfg_key)
if requested is None or requested <= max_band:
continue # 没设 / 没越界
self.log_message(
f"⚠ step3 波段越界:{label}={requested} > 影像波段数 {max_band}",
"warning",
)
dlg = BandConfirmDialog(
self,
requested_band=requested,
max_band=max_band,
recommended_band=recommended,
method_label=label,
)
result = dlg.exec_()
if result == QDialog.Rejected:
self.log_message("✗ 用户取消运行step3 波段越界未解决)", "warning")
return False
new_band = dlg.selected_band()
try:
spin = getattr(self.step3_panel, panel_attr)
spin.setValue(new_band)
except AttributeError:
self.log_message(f"⚠ step3 panel 缺控件 {panel_attr},跳过回写", "warning")
continue
self.log_message(
f"{label}{requested}{new_band}(影像最多 {max_band} 波段)",
"info",
)
return True
def run_full_pipeline(self):
"""运行完整流程"""
if not PIPELINE_AVAILABLE:
@ -2752,21 +2856,42 @@ class WaterQualityGUI(QMainWindow):
"无法导入pipeline模块请确保water_quality_inversion_pipeline_GUI.py文件存在"
)
return
# 验证配置
# ── 1) 运行前智能预检与自动回填(硬盘已有产物自动跳过) ──
work_path = Path(getattr(self, 'work_dir', './work_dir'))
self.log_message("正在进行运行前环境预检与自动扫描...", "info")
self.scan_work_directory_for_files(work_path)
self.auto_populate_all_steps()
self.log_message("✓ 预检完成:已扫描工作目录并自动回填已落盘的产物", "info")
# ── 1.5) step3 波段越界预检60s 倒计时弹窗,主线程同步,避开多线程弹窗坑) ──
if not self._precheck_step3_bands():
return # 用户点"取消运行"
# ── 2) 刷新配置(拿到自动填充后的"满血版" config ──
config = self.get_current_config()
# 基本验证
if not config['step1'].get('mask_path'):
QMessageBox.warning(self, "警告", "请先配置步骤1的掩膜文件")
# 找到第一个可选的步骤项
# ── 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
# ── 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 中上传水质数据。",
)
# 确认执行
reply = QMessageBox.question(
self, "确认",