fix: 全局UX修复与Step4交互可视化重构

=== 自动填入过于激进(幽灵路径级联)===
- 所有面板 update_from_config 移除 os.makedirs(),目录创建留给 pipeline 执行
- 输出路径仅 widget 为空时填入默认值,不覆盖用户已选
- 输入路径从上游读取后添加 os.path.exists() 检查,阻断幽灵路径级联
- panel_factory._replay_live_panel_inputs 广播前校验文件确实存在
- step10 update_from_config 添加 os.path.isfile() 存在性检查
- 清理 step8/9/11 中冗余局部 import os(修复 UnboundLocalError)

=== 输出目录缺失 ===
- step7 新增 output_file FileSelectWidget,默认路径 7_Water_Quality_Indices/
- step9 output_file 从文件模式改为目录模式 (Directories)

=== 空目录自动创建 ===
- step12 _setup_prediction_output_dirs 移除 mkdir() 调用,改为只读日志

=== 过期依赖与缺失 import ===
- panel_registry Step10 依赖 bsq_file→sampling_csv_file(匹配 CSV 模式重构)
- step7 添加缺失的 import pandas as pd(修复 NameError)

=== 导航与 UI 一致性 ===
- water_quality_gui_v2 新增 _select_first_nav_item(),启动时默认选中第一项
- step1 输出卡片对齐 step8 风格
- step8 补充缺失的样式表和统一边距

=== Step4 交互式光谱探针重构 ===
- 左右分栏 QSplitter 布局:左侧控制区 + 右侧 Matplotlib 视图
- 1x2 子图:ax1 散点图 + ax2 光谱曲线
- Hover 悬停 Annotation 显示坐标,Click 点击高亮+绘制光谱
- NavigationToolbar2QT 工具栏(保存/缩放/平移)
- 自动检测坐标列和波段列,完善异常处理
This commit is contained in:
duxin
2026-06-30 09:38:27 +08:00
parent e337f01312
commit 48d17ef0ca
20 changed files with 910 additions and 201 deletions

View File

@ -31,8 +31,9 @@ from PyQt5.QtWidgets import (
QPushButton, QLabel, QTabWidget, QToolBar, QSizePolicy,
QListWidget, QListWidgetItem, QGroupBox,
QTextEdit, QProgressBar, QMessageBox, QFileDialog,
QSpinBox, QDoubleSpinBox, QComboBox,
)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtCore import Qt, QTimer, QSize
from PyQt5.QtGui import QIcon, QFont, QPixmap, QColor, QTextCursor
if multiprocessing.current_process().name == 'MainProcess':
@ -104,8 +105,11 @@ class WaterQualityGUI(QMainWindow):
self._apply_stylesheet()
self._disable_wheel_for_all_spinboxes()
# 第五步:延迟启动工作目录选择
QTimer.singleShot(100, self._workspace_initializer.run)
# 第五步:默认选中第一个步骤(延迟执行,确保导航列表和 Tab 均已就位)
QTimer.singleShot(120, self._select_first_nav_item)
# 第六步:延迟启动工作目录选择
QTimer.singleShot(200, self._workspace_initializer.run)
# ================================================================
# Manager 初始化
@ -395,9 +399,8 @@ class WaterQualityGUI(QMainWindow):
# 分隔符 (不需要实体占用高度,缩小即可)
if stage_idx < len(stage_names) - 1:
from PyQt5.QtCore import QSize # 正确引入 QSize
sep = QListWidgetItem("")
sep.setSizeHint(QSize(0, 10)) # 直接使用 QSize
sep.setSizeHint(QSize(0, 10))
sep.setFlags(sep.flags() & ~Qt.ItemIsSelectable & ~Qt.ItemIsEnabled)
self._step_list.addItem(sep)
@ -467,15 +470,26 @@ class WaterQualityGUI(QMainWindow):
continue
try:
panel.update_from_config(work_dir=work_dir, pipeline=None)
except Exception:
pass
except Exception as e:
# 2026-06-30不再静默吞异常至少通过 EventBus 输出日志
self._event_bus.publish('LogMessage', {
'message': f'[警告] 面板 {step_id} 的 update_from_config 失败: {e}',
'level': 'warning',
})
# ================================================================
# 导航 → Tab 单向路由(左侧 List 驱动右侧 TabTab 头部已隐藏)
# ================================================================
def _select_first_nav_item(self):
"""默认选中左侧导航栏的第一个可导航步骤项(跳过阶段标题/分隔符)。"""
for i in range(self._step_list.count()):
item = self._step_list.item(i)
if item and item.data(Qt.UserRole) not in (None, "stage_header"):
self._step_list.setCurrentRow(i)
return
def _on_step_list_changed(self, index):
from PyQt5.QtCore import Qt
if index < 0: return
item = self._step_list.item(index)
if not item: return
@ -609,7 +623,6 @@ class WaterQualityGUI(QMainWindow):
self.setStyleSheet(ModernStylesheet.get_main_stylesheet())
def _disable_wheel_for_all_spinboxes(self):
from PyQt5.QtWidgets import QSpinBox, QDoubleSpinBox, QComboBox
for sb in self.findChildren(QSpinBox):
sb.setFocusPolicy(Qt.StrongFocus)
sb.wheelEvent = lambda e, s=sb: None