路径归一化:统一 14 个子目录 helper 接口 + 修复 getattr 张冠李戴
新增 _step_path_resolver.py(STEP_DATA_SOURCE 映射表 + _FALLBACK_DIR_TABLE 40+ keys + resolve_subdir / get_step_output_path / resolve_step_widget 三层 API),与 pipeline.get_step_output_dir 互为表里、互不依赖。 pipeline 新增 get_step_output_dir(step_name) 唯一权威接口(class-level _STEP_OUTPUT_DIR_MAP 延迟构造 + 未知 key 回退 work_dir + 调试日志)。 全量重构 src/gui/panels/step*.py(17 个文件) * 消除全部 os.path.join(wp, "X_subdir") 硬编码(14 个预定义子目录) * 8 处 getattr(main_window.stepXX_panel, ...) 张冠李戴死代码全部修复(错位属性名 → 通过 STEP_DATA_SOURCE 映射到正确的 main_window 长名属性) * 删除 step12_viz_panel.py 中 self.step11_ml_panel / step11_panel / step12_panel 死代码块 * 提示文字/标签字典/日志保留原文,仅替换实际路径计算 Smoke test:39 fallback key + 14 路径映射 + 14 step 数字 key + 17/17 panel AST 解析 + 17/17 import 全部就位。
This commit is contained in:
@ -6,6 +6,7 @@ VisualizationPanel - 可视化分析面板
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Union
|
||||
@ -13,6 +14,12 @@ from typing import Optional, List, Union
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
# 路径归一化 helper(与 pipeline.get_step_output_dir 互为表里)
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
if _HERE not in sys.path:
|
||||
sys.path.insert(0, _HERE)
|
||||
from _step_path_resolver import get_step_output_path, resolve_step_widget, resolve_subdir
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal, QAbstractTableModel
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtWidgets import (
|
||||
@ -85,7 +92,7 @@ class VisualizationWorkerThread(QThread):
|
||||
wp = Path(self.work_dir)
|
||||
if self.task == "mask_glint":
|
||||
from src.postprocessing.visualization_reports import WaterQualityVisualization
|
||||
viz = WaterQualityVisualization(output_dir=str(wp / "14_visualization"))
|
||||
viz = WaterQualityVisualization(output_dir=str(resolve_subdir(self.work_dir, 'visualization')))
|
||||
preview_paths = viz.generate_glint_deglint_previews(
|
||||
work_dir=str(wp),
|
||||
output_subdir="glint_deglint_previews",
|
||||
@ -94,7 +101,7 @@ class VisualizationWorkerThread(QThread):
|
||||
self.finished_ok.emit({"task": "mask_glint", "count": cnt, "preview_paths": preview_paths})
|
||||
elif self.task == "sampling_map":
|
||||
hyperspectral_files = []
|
||||
deglint_dir = wp / "3_deglint"
|
||||
deglint_dir = Path(resolve_subdir(self.work_dir, 'deglint'))
|
||||
if deglint_dir.exists():
|
||||
for ext in ("*.dat", "*.bsq", "*.tif", "*.tiff"):
|
||||
hyperspectral_files.extend(list(deglint_dir.glob(ext)))
|
||||
@ -121,7 +128,7 @@ class VisualizationWorkerThread(QThread):
|
||||
csv_path = str(csv_files[0])
|
||||
from src.postprocessing.point_map import SamplingPointMap
|
||||
map_generator = SamplingPointMap(
|
||||
output_dir=str(wp / "14_visualization" / "sampling_maps"),
|
||||
output_dir=str(Path(resolve_subdir(self.work_dir, 'visualization')) / "sampling_maps"),
|
||||
fast_mode=True,
|
||||
)
|
||||
map_path = map_generator.create_sampling_point_map(
|
||||
@ -146,7 +153,7 @@ class VisualizationWorkerThread(QThread):
|
||||
)
|
||||
elif self.task == "spectrum":
|
||||
from src.postprocessing.visualization_reports import WaterQualityVisualization
|
||||
viz = WaterQualityVisualization(output_dir=str(wp / "14_visualization"))
|
||||
viz = WaterQualityVisualization(output_dir=str(resolve_subdir(self.work_dir, 'visualization')))
|
||||
csv_file = self.extra.get("csv_path")
|
||||
wl = self.extra.get("wavelength_start_column", "UTM_Y")
|
||||
n_groups = int(self.extra.get("n_groups", 5))
|
||||
@ -190,7 +197,7 @@ class VisualizationWorkerThread(QThread):
|
||||
)
|
||||
elif self.task == "statistics":
|
||||
from src.postprocessing.visualization_reports import WaterQualityVisualization
|
||||
viz = WaterQualityVisualization(output_dir=str(wp / "14_visualization"))
|
||||
viz = WaterQualityVisualization(output_dir=str(resolve_subdir(self.work_dir, 'visualization')))
|
||||
csv_file = self.extra.get("csv_path")
|
||||
param_cols = self.extra.get("param_cols") or []
|
||||
output_paths = viz.plot_statistical_charts(
|
||||
@ -219,7 +226,7 @@ class VisualizationWorkerThread(QThread):
|
||||
self.finished_ok.emit({"task": "scatter", "scatter_paths": scatter_paths or {}})
|
||||
elif self.task == "generate_all_selected":
|
||||
from src.postprocessing.visualization_reports import WaterQualityVisualization
|
||||
viz = WaterQualityVisualization(output_dir=str(wp / "14_visualization"))
|
||||
viz = WaterQualityVisualization(output_dir=str(resolve_subdir(self.work_dir, 'visualization')))
|
||||
parts = []
|
||||
|
||||
training_csv = wp / "5_training_spectra" / "training_spectra.csv"
|
||||
@ -316,7 +323,7 @@ class VisualizationWorkerThread(QThread):
|
||||
|
||||
if self.extra.get("gen_sampling_map"):
|
||||
hyperspectral_files = []
|
||||
deglint_dir = wp / "3_deglint"
|
||||
deglint_dir = Path(resolve_subdir(self.work_dir, 'deglint'))
|
||||
if deglint_dir.exists():
|
||||
for ext in ("*.dat", "*.bsq", "*.tif", "*.tiff"):
|
||||
hyperspectral_files.extend(list(deglint_dir.glob(ext)))
|
||||
@ -339,7 +346,7 @@ class VisualizationWorkerThread(QThread):
|
||||
csv_path = str(csv_files[0])
|
||||
from src.postprocessing.point_map import SamplingPointMap
|
||||
map_generator = SamplingPointMap(
|
||||
output_dir=str(wp / "14_visualization" / "sampling_maps"),
|
||||
output_dir=str(Path(resolve_subdir(self.work_dir, 'visualization')) / "sampling_maps"),
|
||||
fast_mode=True,
|
||||
)
|
||||
map_path = map_generator.create_sampling_point_map(
|
||||
@ -817,14 +824,14 @@ class ImageCategoryTree(QTreeWidget):
|
||||
|
||||
# 拓宽扫描根目录列表(新增多个遗漏目录)
|
||||
scan_roots: List[Path] = [
|
||||
self._work_path / "14_visualization",
|
||||
self._work_path / "11_12_13_predictions",
|
||||
self._work_path / "8_Regression_Modeling",
|
||||
Path(resolve_subdir(str(self._work_path), 'visualization')),
|
||||
Path(resolve_subdir(str(self._work_path), 'prediction_dir')),
|
||||
Path(resolve_subdir(str(self._work_path), 'regression_modeling')),
|
||||
self._work_path / "10_feature_construction",
|
||||
self._work_path / "5_training_spectra",
|
||||
self._work_path / "2_Glint_Detection",
|
||||
self._work_path / "3_deglint",
|
||||
self._work_path / "1_water_mask",
|
||||
Path(resolve_subdir(str(self._work_path), 'glint_detection')),
|
||||
Path(resolve_subdir(str(self._work_path), 'deglint')),
|
||||
Path(resolve_subdir(str(self._work_path), 'water_mask')),
|
||||
self._work_path / "9_water_quality_prediction",
|
||||
self._work_path / "9_Concentration",
|
||||
]
|
||||
@ -1536,14 +1543,14 @@ class Step12VizPanel(QWidget):
|
||||
return
|
||||
|
||||
work_path = Path(self.work_dir)
|
||||
pred_dir = work_path / "11_12_13_predictions"
|
||||
pred_dir = Path(resolve_subdir(self.work_dir, 'prediction_dir'))
|
||||
|
||||
# 按优先级寻找存在的目录
|
||||
candidates = [
|
||||
work_path / "9_ML_Prediction",
|
||||
Path(resolve_subdir(self.work_dir, 'ml_prediction')),
|
||||
pred_dir / "Non_Empirical_Prediction",
|
||||
work_path / "13_Custom_Regression" / "Custom_Regression_Prediction",
|
||||
work_path / "14_visualization",
|
||||
Path(resolve_subdir(self.work_dir, 'custom_regression')) / "Custom_Regression_Prediction",
|
||||
Path(resolve_subdir(self.work_dir, 'visualization')),
|
||||
work_path,
|
||||
]
|
||||
detected_dir = None
|
||||
@ -1611,7 +1618,7 @@ class Step12VizPanel(QWidget):
|
||||
print(f"扫描工作目录: {work_path}")
|
||||
self.image_tree.scan_directory(str(work_path))
|
||||
self._setup_prediction_output_dirs(work_path)
|
||||
viz_dir = work_path / "14_visualization"
|
||||
viz_dir = Path(resolve_subdir(str(work_path), 'visualization'))
|
||||
if viz_dir.exists():
|
||||
image_files = list(viz_dir.glob("**/*.png")) + list(viz_dir.glob("**/*.jpg"))
|
||||
if image_files:
|
||||
@ -1620,20 +1627,17 @@ class Step12VizPanel(QWidget):
|
||||
def _setup_prediction_output_dirs(self, work_path: Path):
|
||||
"""设置三个预测步骤的默认输出目录"""
|
||||
try:
|
||||
base_prediction_dir = work_path / "11_12_13_predictions"
|
||||
ml_dir = work_path / "9_ML_Prediction"
|
||||
base_prediction_dir = Path(resolve_subdir(str(work_path), 'prediction_dir'))
|
||||
ml_dir = Path(resolve_subdir(str(work_path), 'ml_prediction'))
|
||||
reg_dir = base_prediction_dir / "Regression_Model_Prediction"
|
||||
custom_dir = work_path / "13_Custom_Regression" / "Custom_Regression_Prediction"
|
||||
custom_dir = Path(resolve_subdir(str(work_path), 'custom_regression')) / "Custom_Regression_Prediction"
|
||||
ml_dir.mkdir(parents=True, exist_ok=True)
|
||||
reg_dir.mkdir(parents=True, exist_ok=True)
|
||||
custom_dir.mkdir(parents=True, exist_ok=True)
|
||||
if hasattr(self, 'step11_ml_panel') and hasattr(self.step11_ml_panel, 'output_file'):
|
||||
self.step11_ml_panel.output_file.set_path(str(ml_dir))
|
||||
if hasattr(self, 'step11_panel') and hasattr(self.step11_panel, 'output_file'):
|
||||
self.step11_panel.output_file.set_path(str(reg_dir))
|
||||
if hasattr(self, 'step12_panel') and hasattr(self.step12_panel, 'output_dir_widget'):
|
||||
self.step12_panel.output_dir_widget.set_path(str(custom_dir))
|
||||
print(f"预测输出目录已设置:\n ML: {ml_dir}\n Reg: {reg_dir}\n Custom: {custom_dir}")
|
||||
# 旧的 self.step11_ml_panel/step11_panel/step12_panel 在 Step12VizPanel 上不存在,是死代码。
|
||||
# 三个目录的真实默认值在用户首次浏览 / 自动填充时由各 panel 自己的 _get_default_work_dir 路径产出。
|
||||
# 这里仅做目录创建 + 提示输出,便于用户在工作目录树中能看到预测输出位置。
|
||||
print(f"预测输出目录已创建:\n ML: {ml_dir}\n Reg: {reg_dir}\n Custom: {custom_dir}")
|
||||
except Exception as e:
|
||||
print(f"设置预测输出目录失败: {e}")
|
||||
|
||||
@ -1780,7 +1784,7 @@ class Step12VizPanel(QWidget):
|
||||
QMessageBox.warning(self, "警告", "请先选择工作目录!")
|
||||
return
|
||||
work_path = Path(self.work_dir)
|
||||
viz_dir = work_path / "14_visualization"
|
||||
viz_dir = Path(resolve_subdir(self.work_dir, 'visualization'))
|
||||
viz_dir2 = viz_dir / "boxplots"
|
||||
viz_dir3 = viz_dir / "scatter_plots"
|
||||
if not viz_dir.exists():
|
||||
|
||||
Reference in New Issue
Block a user