路径归一化:统一 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:
@ -185,7 +185,71 @@ class WaterQualityInversionPipeline:
|
||||
self.callback = None
|
||||
|
||||
print(f"工作目录已创建: {self.work_dir}")
|
||||
|
||||
|
||||
# ---- 步骤输出目录查找接口(归一化所有 panel 的路径访问)----
|
||||
|
||||
# 用户口语编号 → 权威子目录对象 的映射
|
||||
# 同时支持 "stepN"、"stepN_alias"、"subdir名" 三种 key 形式查找
|
||||
_STEP_OUTPUT_DIR_MAP = None # 延迟到首次访问时构造
|
||||
|
||||
def _ensure_step_dir_map(self):
|
||||
"""延迟构造 step_name → 目录对象 映射表(首次访问时执行)"""
|
||||
if WaterQualityInversionPipeline._STEP_OUTPUT_DIR_MAP is not None:
|
||||
return WaterQualityInversionPipeline._STEP_OUTPUT_DIR_MAP
|
||||
wp = self.work_dir
|
||||
m = {
|
||||
# 基础步骤
|
||||
"step1": wp / "1_water_mask",
|
||||
"step2": wp / "2_Glint_Detection",
|
||||
"step3": wp / "3_deglint",
|
||||
"step4_sampling": wp / "4_sampling",
|
||||
"step5_clean": wp / "5_Data_Cleaning",
|
||||
"step6_feature": wp / "6_Spectral_Feature_Extraction",
|
||||
"step7_index": wp / "7_Water_Quality_Indices",
|
||||
"step8_ml_train": wp / "8_Supervised_Model_Training",
|
||||
"step9_ml_predict": wp / "8_Non_Empirical_Regression",
|
||||
"step10_watercolor": wp / "10_WaterIndex_Images",
|
||||
"step11_map": wp / "14_visualization",
|
||||
"step12_viz": wp / "14_visualization",
|
||||
"step13_report": wp / "14_visualization",
|
||||
# 合并目录(提供单一访问点,避免分散硬编码)
|
||||
"step11_predictions": wp / "11_12_13_predictions",
|
||||
"step12_predictions": wp / "11_12_13_predictions",
|
||||
"step13_predictions": wp / "11_12_13_predictions",
|
||||
"custom_regression": wp / "13_Custom_Regression",
|
||||
"prediction_dir": wp / "11_12_13_predictions",
|
||||
"visualization": wp / "14_visualization",
|
||||
"reports": wp / "reports",
|
||||
# 兼容主流程 step_id(数字+短名)
|
||||
"step8": wp / "8_Supervised_Model_Training",
|
||||
"step9": wp / "8_Non_Empirical_Regression",
|
||||
"step10": wp / "10_WaterIndex_Images",
|
||||
"step11": wp / "11_12_13_predictions",
|
||||
"step12": wp / "13_Custom_Regression",
|
||||
"step13": wp / "reports",
|
||||
"step14": wp / "14_visualization",
|
||||
}
|
||||
WaterQualityInversionPipeline._STEP_OUTPUT_DIR_MAP = m
|
||||
return m
|
||||
|
||||
def get_step_output_dir(self, step_name: str):
|
||||
"""根据步骤名称返回权威输出目录 Path 对象。
|
||||
|
||||
这是 panel 端访问子目录的**唯一**入口。接收以下任意形式 key:
|
||||
- 完整 panel 属性名: "step11_map", "step12_viz", "step8_ml_train" 等
|
||||
- 主流程 step_id: "step8"~"step14"
|
||||
- 业务别名: "prediction_dir", "visualization", "reports", "custom_regression"
|
||||
- 兼容口语: "step11_predictions" (=11_12_13_predictions)
|
||||
|
||||
未知 key 一律回退到 work_dir 本身,并打 warning。
|
||||
"""
|
||||
mapping = self._ensure_step_dir_map()
|
||||
key = (step_name or "").strip()
|
||||
if key in mapping:
|
||||
return mapping[key]
|
||||
print(f"[pipeline.get_step_output_dir] 未知 step_name={key!r},回退到 work_dir")
|
||||
return self.work_dir
|
||||
|
||||
def set_callback(self, callback):
|
||||
"""
|
||||
设置回调函数,用于向GUI报告进度
|
||||
|
||||
186
src/gui/panels/_step_path_resolver.py
Normal file
186
src/gui/panels/_step_path_resolver.py
Normal file
@ -0,0 +1,186 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Step 路径解析器——统一消灭 panel 端的硬编码路径与"张冠李戴"跨面板引用。
|
||||
|
||||
提供三个公共 API:
|
||||
- resolve_step_widget(main_window, step_key)
|
||||
- get_step_output_path(main_window, step_key, work_dir=None)
|
||||
- STEP_DATA_SOURCE 映射表
|
||||
|
||||
典型使用:
|
||||
from src.gui.panels._step_path_resolver import (
|
||||
resolve_step_widget, get_step_output_path
|
||||
)
|
||||
|
||||
# 替换前:getattr(main_window.step11_panel, 'output_file', None) # 死代码
|
||||
# 替换后:
|
||||
widget = resolve_step_widget(main_window, 'step11_predictions') # 找到正确 widget
|
||||
if widget: ...
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
# 用户口语编号 / 业务别名 → main_window 上真实属性名的映射
|
||||
# 这是"张冠李戴"修复的核心——之前代码写的 step11_panel 实际不存在,
|
||||
# 真实存在的属性见 water_quality_gui.py:1891-1928
|
||||
STEP_DATA_SOURCE = {
|
||||
# 数据流 step 编号(用户口语) → main_window 真实属性
|
||||
'step5_clean_output': 'step5_clean_panel',
|
||||
'step7_index_output': 'step7_index_panel',
|
||||
'step8_ml_train_output': 'step8_ml_train_panel',
|
||||
'step8_5_non_empirical': 'step8_non_empirical_panel', # 之前写错成 step11_panel
|
||||
'step9_ml_predict_output': 'step9_ml_predict_panel',
|
||||
'step10_watercolor_output': 'step10_watercolor_panel',
|
||||
'step11_ml_prediction': 'step9_ml_predict_panel', # 主流程 step11 = ML 预测
|
||||
'step12_regression_prediction': 'step8_non_empirical_panel', # 主流程 step12 = 非经验预测
|
||||
'step13_custom_regression': 'step13_report_panel', # 占位(自定义回归本身没有专属 panel)
|
||||
'sampling_csv': 'step4_sampling_panel',
|
||||
'training_spectra_csv': 'step5_clean_panel',
|
||||
'indices_csv': 'step7_index_panel',
|
||||
'models_dir': 'step8_ml_train_panel',
|
||||
'watercolor_dir': 'step10_watercolor_panel',
|
||||
'prediction_csv_dir': 'step9_ml_predict_panel', # 默认从 ML 预测读
|
||||
}
|
||||
|
||||
|
||||
def _get_widget(main_window, attr_name: str, widget_attr: str = 'output_file'):
|
||||
"""从 main_window.<attr_name> 取出指定子组件,失败时返回 None。"""
|
||||
if main_window is None:
|
||||
return None
|
||||
panel = getattr(main_window, attr_name, None)
|
||||
if panel is None:
|
||||
return None
|
||||
return getattr(panel, widget_attr, None)
|
||||
|
||||
|
||||
def _read_widget_path(widget) -> str:
|
||||
"""统一从 widget 读 path(兼容 FileSelectWidget / QLineEdit / 字符串)。"""
|
||||
if widget is None:
|
||||
return ""
|
||||
if hasattr(widget, 'get_path'):
|
||||
try:
|
||||
return str(widget.get_path() or "").strip()
|
||||
except Exception:
|
||||
return ""
|
||||
if hasattr(widget, 'text'):
|
||||
try:
|
||||
return str(widget.text() or "").strip()
|
||||
except Exception:
|
||||
return ""
|
||||
if isinstance(widget, str):
|
||||
return widget.strip()
|
||||
return ""
|
||||
|
||||
|
||||
def resolve_step_widget(main_window, step_key: str, widget_attr: str = 'output_file'):
|
||||
"""根据业务 step_key 解析出正确的 widget(消除张冠李戴)。
|
||||
|
||||
Returns:
|
||||
widget 对象 or None(找不到时返回 None,调用方需自行兜底)
|
||||
"""
|
||||
attr_name = STEP_DATA_SOURCE.get(step_key)
|
||||
if attr_name is None:
|
||||
return None
|
||||
return _get_widget(main_window, attr_name, widget_attr)
|
||||
|
||||
|
||||
_FALLBACK_DIR_TABLE = {
|
||||
# pipeline key(与 _ensure_step_dir_map 对齐)→ 子目录名
|
||||
'step1': '1_water_mask',
|
||||
'step2': '2_Glint_Detection',
|
||||
'step3': '3_deglint',
|
||||
'step4_sampling': '4_sampling',
|
||||
'step5_clean': '5_Data_Cleaning',
|
||||
'step6_feature': '6_Spectral_Feature_Extraction',
|
||||
'step7_index': '7_Water_Quality_Indices',
|
||||
'step8_ml_train': '8_Supervised_Model_Training',
|
||||
'step8': '8_Supervised_Model_Training',
|
||||
'step9_ml_predict': '8_Non_Empirical_Regression',
|
||||
'step9': '8_Non_Empirical_Regression',
|
||||
'step10_watercolor': '10_WaterIndex_Images',
|
||||
'step10': '10_WaterIndex_Images',
|
||||
'step11_map': '14_visualization',
|
||||
'step11': '11_12_13_predictions',
|
||||
'step11_predictions': '11_12_13_predictions',
|
||||
'step12': '13_Custom_Regression',
|
||||
'step12_predictions': '11_12_13_predictions',
|
||||
'step13': 'reports',
|
||||
'step13_predictions': '11_12_13_predictions',
|
||||
'step14': '14_visualization',
|
||||
'prediction_dir': '11_12_13_predictions',
|
||||
'visualization': '14_visualization',
|
||||
'reports': 'reports',
|
||||
'custom_regression': '13_Custom_Regression',
|
||||
# 扩展:覆盖 panel 内部使用的子目录别名
|
||||
'water_mask': '1_water_mask',
|
||||
'glint_detection': '2_Glint_Detection',
|
||||
'deglint': '3_deglint',
|
||||
'sampling': '4_sampling',
|
||||
'data_cleaning': '5_Data_Cleaning',
|
||||
'spectral_feature': '6_Spectral_Feature_Extraction',
|
||||
'indices': '7_Water_Quality_Indices',
|
||||
'supervised_models': '8_Supervised_Model_Training',
|
||||
'non_empirical': '8_Non_Empirical_Regression',
|
||||
'qaa_inversion': '8_QAA_Inversion',
|
||||
'regression_modeling': '8_Regression_Modeling',
|
||||
'watercolor': '10_WaterIndex_Images',
|
||||
'ml_prediction': '9_ML_Prediction',
|
||||
'sampling_csv_path': '4_sampling/sampling_spectra.csv',
|
||||
}
|
||||
|
||||
|
||||
def get_step_output_path(
|
||||
main_window,
|
||||
step_key: str,
|
||||
work_dir: Optional[Union[str, Path]] = None,
|
||||
widget_attr: str = 'output_file',
|
||||
fallback_key: Optional[str] = None,
|
||||
) -> str:
|
||||
"""获取 step_key 指向的输出路径(带 main_window 解析 + 兜底路径)。
|
||||
|
||||
解析顺序:
|
||||
1. STEP_DATA_SOURCE[step_key] 找到对应 panel,从 widget 读用户填的 path
|
||||
2. 若为空字符串,用 _FALLBACK_DIR_TABLE[fallback_key or step_key] + work_dir 拼兜底
|
||||
3. 全失败返回 str(work_dir)
|
||||
|
||||
注意:不创建 pipeline 实例(避免触发 osgeo 导入),用本地子目录字典兜底。
|
||||
"""
|
||||
wd = str(work_dir) if work_dir else ""
|
||||
widget = resolve_step_widget(main_window, step_key, widget_attr)
|
||||
p = _read_widget_path(widget)
|
||||
if p:
|
||||
if not Path(p).is_absolute() and wd:
|
||||
p = str(Path(wd) / p).replace('\\', '/')
|
||||
return p
|
||||
|
||||
# 兜底:本地子目录字典(与 pipeline._ensure_step_dir_map 一致)
|
||||
key = fallback_key or step_key
|
||||
sub = _FALLBACK_DIR_TABLE.get(key)
|
||||
if sub and wd:
|
||||
return str(Path(wd) / sub).replace('\\', '/')
|
||||
return wd
|
||||
|
||||
|
||||
def resolve_subdir(work_dir, subdir_key: str) -> str:
|
||||
"""纯子目录拼装:把 pipeline key 解析为 work_dir 下的子目录路径。
|
||||
|
||||
用法:resolve_subdir(self.work_dir, 'visualization')
|
||||
→ '<work_dir>/14_visualization'
|
||||
|
||||
与 pipeline.get_step_output_dir 同源(都查同一份 _FALLBACK_DIR_TABLE 子集)。
|
||||
"""
|
||||
wd = str(work_dir) if work_dir else ""
|
||||
sub = _FALLBACK_DIR_TABLE.get(subdir_key)
|
||||
if sub and wd:
|
||||
return str(Path(wd) / sub).replace('\\', '/')
|
||||
return wd
|
||||
|
||||
|
||||
__all__ = [
|
||||
'STEP_DATA_SOURCE',
|
||||
'resolve_step_widget',
|
||||
'get_step_output_path',
|
||||
'resolve_subdir',
|
||||
]
|
||||
@ -8,10 +8,17 @@ Step10 面板 - 水色指数反演(直接处理去耀斑 BSQ 影像)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout,
|
||||
QGroupBox, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton,
|
||||
@ -451,7 +458,7 @@ class Step10WatercolorPanel(QWidget):
|
||||
|
||||
# 3. 终极回退:智能扫描 3_deglint 目录,取最新的 .bsq 或 .dat 文件
|
||||
if not deglint_path and self.work_dir:
|
||||
deglint_dir = os.path.join(self.work_dir, "3_deglint")
|
||||
deglint_dir = resolve_subdir(self.work_dir, 'deglint')
|
||||
if os.path.isdir(deglint_dir):
|
||||
import glob
|
||||
candidates = glob.glob(os.path.join(deglint_dir, "*.bsq")) + glob.glob(os.path.join(deglint_dir, "*.dat"))
|
||||
@ -472,7 +479,7 @@ class Step10WatercolorPanel(QWidget):
|
||||
|
||||
# 自动填入输出目录
|
||||
if self.work_dir:
|
||||
out_dir = os.path.join(self.work_dir, "10_WaterIndex_Images").replace('\\', '/')
|
||||
out_dir = resolve_subdir(self.work_dir, 'watercolor')
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
if not self.output_dir.get_path():
|
||||
self.output_dir.set_path(out_dir)
|
||||
@ -503,7 +510,7 @@ class Step10WatercolorPanel(QWidget):
|
||||
return
|
||||
if not output_dir:
|
||||
work_dir = self._get_default_work_dir()
|
||||
output_dir = os.path.join(work_dir, "10_WaterIndex_Images").replace('\\', '/')
|
||||
output_dir = resolve_subdir(work_dir, 'watercolor')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
self.output_dir.set_path(output_dir)
|
||||
|
||||
@ -518,7 +525,7 @@ class Step10WatercolorPanel(QWidget):
|
||||
|
||||
# ── 自动扫描工作目录下的水域掩膜文件 ────────────────────────────
|
||||
work_dir = self.work_dir or str(Path(bsq_path).parent)
|
||||
mask_dir = os.path.join(work_dir, "1_water_mask")
|
||||
mask_dir = resolve_subdir(work_dir, 'water_mask')
|
||||
water_mask_path: Optional[str] = None
|
||||
if os.path.isdir(mask_dir):
|
||||
# ★★★ glob 智能扫描:取任意 .dat 或 .tif 文件 ★★★
|
||||
|
||||
@ -5,10 +5,17 @@ Step10 面板 - 专题图生成
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QHBoxLayout,
|
||||
@ -372,7 +379,7 @@ class Step11MapPanel(QWidget):
|
||||
def browse_prediction_csv_dir(self):
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "11_12_13_predictions")
|
||||
default = resolve_subdir(default, 'prediction_dir')
|
||||
d = QFileDialog.getExistingDirectory(self, "选择预测结果 CSV 所在文件夹", default)
|
||||
if d:
|
||||
self.prediction_csv_dir_edit.setText(d)
|
||||
@ -392,7 +399,7 @@ class Step11MapPanel(QWidget):
|
||||
"""浏览 GeoTIFF 文件夹(批量模式)"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "10_WaterIndex_Images")
|
||||
default = resolve_subdir(default, 'watercolor')
|
||||
d = QFileDialog.getExistingDirectory(
|
||||
self, "选择水色指数 GeoTIFF 文件夹", default
|
||||
)
|
||||
@ -510,49 +517,36 @@ class Step11MapPanel(QWidget):
|
||||
if not main_window:
|
||||
return
|
||||
|
||||
# 1. 尝试从 Step8 界面读取机器学习预测输出目录(最优先)
|
||||
# 1. 优先:从 Step9(机器学习预测)读输出目录,9_ML_Prediction 子目录
|
||||
# 修复张冠李戴:原 main_window.step11_prediction_panel 不存在,真实属性是 step9_ml_predict_panel
|
||||
pred_dir = None
|
||||
if hasattr(main_window, 'step11_prediction_panel'):
|
||||
step8_widget = getattr(main_window.step11_prediction_panel, 'output_file', None)
|
||||
step10_output = ""
|
||||
if hasattr(step8_widget, 'get_path'):
|
||||
step10_output = step8_widget.get_path() or ""
|
||||
elif hasattr(step8_widget, 'text'):
|
||||
step10_output = step8_widget.text() or ""
|
||||
|
||||
if step10_output:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step10_output):
|
||||
step10_output = os.path.join(self.work_dir or '', step10_output).replace('\\', '/')
|
||||
# 提取父目录后追加 9_ML_Prediction(最底层真实子目录)
|
||||
base_pred_dir = str(Path(step10_output).parent)
|
||||
ml_pred_dir = Path(base_pred_dir) / "9_ML_Prediction"
|
||||
pred_dir = str(ml_pred_dir) if ml_pred_dir.exists() else base_pred_dir
|
||||
|
||||
# 2. 备选:从 Step11 界面读取非经验预测输出目录
|
||||
if not pred_dir and hasattr(main_window, 'step11_panel'):
|
||||
step8_5_widget = getattr(main_window.step11_panel, 'output_file', None)
|
||||
step8_5_output = ""
|
||||
if hasattr(step8_5_widget, 'get_path'):
|
||||
step8_5_output = step8_5_widget.get_path() or ""
|
||||
elif hasattr(step8_5_widget, 'text'):
|
||||
step8_5_output = step8_5_widget.text() or ""
|
||||
step10_output = get_step_output_path(
|
||||
main_window, 'step11_ml_prediction', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step9_ml_predict',
|
||||
)
|
||||
if step10_output:
|
||||
# 提取父目录后追加 9_ML_Prediction(最底层真实子目录)
|
||||
base_pred_dir = str(Path(step10_output).parent)
|
||||
ml_pred_dir = Path(base_pred_dir) / "9_ML_Prediction"
|
||||
pred_dir = str(ml_pred_dir) if ml_pred_dir.exists() else base_pred_dir
|
||||
|
||||
# 2. 备选:从 Step8(非经验预测)读输出目录
|
||||
# 修复张冠李戴:原 main_window.step11_panel 不存在,真实属性是 step8_non_empirical_panel
|
||||
if not pred_dir:
|
||||
step8_5_output = get_step_output_path(
|
||||
main_window, 'step12_regression_prediction', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step8_ml_train',
|
||||
)
|
||||
if step8_5_output:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step8_5_output):
|
||||
step8_5_output = os.path.join(self.work_dir or '', step8_5_output).replace('\\', '/')
|
||||
pred_dir = str(Path(step8_5_output).parent)
|
||||
|
||||
# 3. 备选:从 Step12 界面读取自定义回归预测输出目录
|
||||
if not pred_dir and hasattr(main_window, 'step12_panel'):
|
||||
step8_75_widget = getattr(main_window.step12_panel, 'output_dir_widget', None)
|
||||
step8_75_output = ""
|
||||
if hasattr(step8_75_widget, 'get_path'):
|
||||
step8_75_output = step8_75_widget.get_path() or ""
|
||||
elif hasattr(step8_75_widget, 'text'):
|
||||
step8_75_output = step8_75_widget.text() or ""
|
||||
|
||||
# 3. 备选:从 Step13 panel(自定义回归)读输出目录
|
||||
# 修复张冠李戴:原 main_window.step12_panel 不存在;自定义回归 panel 是 step13_panel 类(main_window 上无此名)
|
||||
if not pred_dir:
|
||||
step8_75_output = get_step_output_path(
|
||||
main_window, 'step13_custom_regression', work_dir=self.work_dir,
|
||||
widget_attr='output_dir_widget', fallback_key='custom_regression',
|
||||
)
|
||||
if step8_75_output:
|
||||
pred_dir = step8_75_output
|
||||
|
||||
@ -566,7 +560,7 @@ class Step11MapPanel(QWidget):
|
||||
|
||||
# 4. 自动填充输出目录(14_visualization)
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "14_visualization")
|
||||
output_dir = resolve_subdir(self.work_dir, 'visualization')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
existing_out = self.output_dir.get_path()
|
||||
if not existing_out or not existing_out.strip():
|
||||
@ -612,7 +606,7 @@ class Step11MapPanel(QWidget):
|
||||
"""浏览输出目录"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "14_visualization")
|
||||
default = resolve_subdir(default, 'visualization')
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出分布图目录", default)
|
||||
if dir_path:
|
||||
self.output_dir.set_path(dir_path)
|
||||
@ -704,7 +698,7 @@ class Step11MapPanel(QWidget):
|
||||
|
||||
out_dir = (self.output_dir.get_path() or "").strip()
|
||||
if not out_dir:
|
||||
out_dir = os.path.join(self._get_default_work_dir(), "14_visualization")
|
||||
out_dir = resolve_subdir(self._get_default_work_dir(), 'visualization')
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
self.run_button.setEnabled(False)
|
||||
@ -760,7 +754,7 @@ class Step11MapPanel(QWidget):
|
||||
# 构造输出路径
|
||||
out_dir = (self.output_dir.get_path() or "").strip()
|
||||
if not out_dir:
|
||||
out_dir = os.path.join(self._get_default_work_dir(), "14_visualization")
|
||||
out_dir = resolve_subdir(self._get_default_work_dir(), 'visualization')
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
tif_stem = Path(geotiff_path).stem
|
||||
chinese_name = mapper._get_chinese_title(tif_stem)
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -5,6 +5,14 @@ Step12 面板 - 自定义回归预测
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox,
|
||||
@ -94,51 +102,38 @@ class Step12Panel(QWidget):
|
||||
|
||||
main_window = self.window()
|
||||
|
||||
# 1. 尝试从 Step7 界面读取全湖采样点 CSV 路径
|
||||
if main_window and hasattr(main_window, 'step7_panel'):
|
||||
step7_widget = getattr(main_window.step7_panel, 'output_file', None)
|
||||
step7_output_path = ""
|
||||
if hasattr(step7_widget, 'get_path'):
|
||||
step7_output_path = step7_widget.get_path() or ""
|
||||
elif hasattr(step7_widget, 'text'):
|
||||
step7_output_path = step7_widget.text() or ""
|
||||
# 1. 尝试从 Step7(水质光谱指数)界面读取全湖采样点 CSV 路径
|
||||
# 修复张冠李戴:原 main_window.step7_panel 不存在,真实属性是 step7_index_panel
|
||||
step7_output_path = get_step_output_path(
|
||||
main_window, 'sampling_csv', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step7_index',
|
||||
)
|
||||
if step7_output_path:
|
||||
existing = self.sampling_csv_file.get_path()
|
||||
if not existing or not existing.strip():
|
||||
self.sampling_csv_file.set_path(step7_output_path)
|
||||
|
||||
if step7_output_path:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step7_output_path):
|
||||
step7_output_path = os.path.join(self.work_dir or '', step7_output_path).replace('\\', '/')
|
||||
existing = self.sampling_csv_file.get_path()
|
||||
if not existing or not existing.strip():
|
||||
self.sampling_csv_file.set_path(step7_output_path)
|
||||
|
||||
# 2. 尝试从 Step9 界面读取自定义回归模型目录
|
||||
if main_window and hasattr(main_window, 'step12_panel'):
|
||||
step9_widget = getattr(main_window.step9_panel, 'output_dir', None)
|
||||
step9_models_dir = ""
|
||||
if hasattr(step9_widget, 'get_path'):
|
||||
step9_models_dir = step9_widget.get_path() or ""
|
||||
elif hasattr(step9_widget, 'text'):
|
||||
step9_models_dir = step9_widget.text() or ""
|
||||
step9_models_dir = step9_models_dir.strip()
|
||||
|
||||
if step9_models_dir:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step9_models_dir):
|
||||
step9_models_dir = os.path.join(self.work_dir or '', step9_models_dir).replace('\\', '/')
|
||||
existing_models = self.regression_models_dir.get_path()
|
||||
if not existing_models or not existing_models.strip():
|
||||
self.regression_models_dir.set_path(step9_models_dir)
|
||||
# 2. 尝试从 Step8(非经验回归/自定义回归源)读取模型目录
|
||||
# 修复张冠李戴:原 main_window.step12_panel 不存在;按代码原意是 step9 的 output_dir
|
||||
step9_models_dir = get_step_output_path(
|
||||
main_window, 'models_dir', work_dir=self.work_dir,
|
||||
widget_attr='output_dir', fallback_key='step8_ml_train',
|
||||
)
|
||||
if step9_models_dir:
|
||||
existing_models = self.regression_models_dir.get_path()
|
||||
if not existing_models or not existing_models.strip():
|
||||
self.regression_models_dir.set_path(step9_models_dir)
|
||||
|
||||
# 3. 自动填充回归模型目录(如果 step9 未提供)
|
||||
if self.work_dir:
|
||||
models_dir = self.regression_models_dir.get_path().strip()
|
||||
if not models_dir:
|
||||
default_models_dir = os.path.join(self.work_dir, "13_Custom_Regression").replace('\\', '/')
|
||||
default_models_dir = resolve_subdir(self.work_dir, 'custom_regression')
|
||||
self.regression_models_dir.set_path(default_models_dir)
|
||||
|
||||
# 4. 自动填充输出目录(自定义回归预测目录)
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "13_Custom_Regression/Custom_Regression_Prediction")
|
||||
output_dir = os.path.join(resolve_subdir(self.work_dir, 'custom_regression'), "Custom_Regression_Prediction")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
existing_out = self.output_dir_widget.get_path()
|
||||
if not existing_out or not existing_out.strip():
|
||||
@ -161,7 +156,7 @@ class Step12Panel(QWidget):
|
||||
"""浏览回归模型目录"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "13_Custom_Regression")
|
||||
default = resolve_subdir(default, 'custom_regression')
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择回归模型目录", default)
|
||||
if dir_path:
|
||||
self.regression_models_dir.set_path(dir_path)
|
||||
|
||||
@ -5,10 +5,17 @@ ReportGenerationPanel - Word 分析报告生成面板
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSettings
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QFormLayout,
|
||||
@ -206,7 +213,7 @@ class Step13ReportPanel(QWidget):
|
||||
def browse_output_dir(self):
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "14_visualization")
|
||||
default = resolve_subdir(default, 'visualization')
|
||||
d = QFileDialog.getExistingDirectory(self, "选择报告输出目录", default)
|
||||
if d:
|
||||
self.output_dir_edit.setText(d)
|
||||
|
||||
@ -5,10 +5,17 @@ Step14 面板 - 分布图生成
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
# 路径归一化 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
|
||||
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QHBoxLayout,
|
||||
@ -372,7 +379,7 @@ class Step14Panel(QWidget):
|
||||
def browse_prediction_csv_dir(self):
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "11_12_13_predictions")
|
||||
default = resolve_subdir(default, 'prediction_dir')
|
||||
d = QFileDialog.getExistingDirectory(self, "选择预测结果 CSV 所在文件夹", default)
|
||||
if d:
|
||||
self.prediction_csv_dir_edit.setText(d)
|
||||
@ -392,7 +399,7 @@ class Step14Panel(QWidget):
|
||||
"""浏览 GeoTIFF 文件夹(批量模式)"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "10_WaterIndex_Images")
|
||||
default = resolve_subdir(default, 'watercolor')
|
||||
d = QFileDialog.getExistingDirectory(
|
||||
self, "选择水色指数 GeoTIFF 文件夹", default
|
||||
)
|
||||
@ -510,49 +517,35 @@ class Step14Panel(QWidget):
|
||||
if not main_window:
|
||||
return
|
||||
|
||||
# 1. 尝试从 Step8 界面读取机器学习预测输出目录(最优先)
|
||||
# 1. 优先:从 Step9(机器学习预测)读输出目录,9_ML_Prediction 子目录
|
||||
# 修复张冠李戴:原 main_window.step11_prediction_panel 不存在
|
||||
pred_dir = None
|
||||
if hasattr(main_window, 'step11_prediction_panel'):
|
||||
step8_widget = getattr(main_window.step11_prediction_panel, 'output_file', None)
|
||||
step10_output = ""
|
||||
if hasattr(step8_widget, 'get_path'):
|
||||
step10_output = step8_widget.get_path() or ""
|
||||
elif hasattr(step8_widget, 'text'):
|
||||
step10_output = step8_widget.text() or ""
|
||||
|
||||
if step10_output:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step10_output):
|
||||
step10_output = os.path.join(self.work_dir or '', step10_output).replace('\\', '/')
|
||||
# 提取父目录后追加 9_ML_Prediction(最底层真实子目录)
|
||||
base_pred_dir = str(Path(step10_output).parent)
|
||||
ml_pred_dir = Path(base_pred_dir) / "9_ML_Prediction"
|
||||
pred_dir = str(ml_pred_dir) if ml_pred_dir.exists() else base_pred_dir
|
||||
|
||||
# 2. 备选:从 Step11 界面读取非经验预测输出目录
|
||||
if not pred_dir and hasattr(main_window, 'step11_panel'):
|
||||
step8_5_widget = getattr(main_window.step11_panel, 'output_file', None)
|
||||
step8_5_output = ""
|
||||
if hasattr(step8_5_widget, 'get_path'):
|
||||
step8_5_output = step8_5_widget.get_path() or ""
|
||||
elif hasattr(step8_5_widget, 'text'):
|
||||
step8_5_output = step8_5_widget.text() or ""
|
||||
step10_output = get_step_output_path(
|
||||
main_window, 'step11_ml_prediction', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step9_ml_predict',
|
||||
)
|
||||
if step10_output:
|
||||
base_pred_dir = str(Path(step10_output).parent)
|
||||
ml_pred_dir = Path(base_pred_dir) / "9_ML_Prediction"
|
||||
pred_dir = str(ml_pred_dir) if ml_pred_dir.exists() else base_pred_dir
|
||||
|
||||
# 2. 备选:从 Step8(非经验预测)读输出目录
|
||||
# 修复张冠李戴:原 main_window.step11_panel 不存在
|
||||
if not pred_dir:
|
||||
step8_5_output = get_step_output_path(
|
||||
main_window, 'step12_regression_prediction', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step8_ml_train',
|
||||
)
|
||||
if step8_5_output:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step8_5_output):
|
||||
step8_5_output = os.path.join(self.work_dir or '', step8_5_output).replace('\\', '/')
|
||||
pred_dir = str(Path(step8_5_output).parent)
|
||||
|
||||
# 3. 备选:从 Step12 界面读取自定义回归预测输出目录
|
||||
if not pred_dir and hasattr(main_window, 'step12_panel'):
|
||||
step8_75_widget = getattr(main_window.step12_panel, 'output_dir_widget', None)
|
||||
step8_75_output = ""
|
||||
if hasattr(step8_75_widget, 'get_path'):
|
||||
step8_75_output = step8_75_widget.get_path() or ""
|
||||
elif hasattr(step8_75_widget, 'text'):
|
||||
step8_75_output = step8_75_widget.text() or ""
|
||||
|
||||
# 3. 备选:从 Step13 panel(自定义回归)读输出目录
|
||||
# 修复张冠李戴:原 main_window.step12_panel 不存在
|
||||
if not pred_dir:
|
||||
step8_75_output = get_step_output_path(
|
||||
main_window, 'step13_custom_regression', work_dir=self.work_dir,
|
||||
widget_attr='output_dir_widget', fallback_key='custom_regression',
|
||||
)
|
||||
if step8_75_output:
|
||||
pred_dir = step8_75_output
|
||||
|
||||
@ -566,7 +559,7 @@ class Step14Panel(QWidget):
|
||||
|
||||
# 4. 自动填充输出目录(14_visualization)
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "14_visualization")
|
||||
output_dir = resolve_subdir(self.work_dir, 'visualization')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
existing_out = self.output_dir.get_path()
|
||||
if not existing_out or not existing_out.strip():
|
||||
@ -612,7 +605,7 @@ class Step14Panel(QWidget):
|
||||
"""浏览输出目录"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "14_visualization")
|
||||
default = resolve_subdir(default, 'visualization')
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出分布图目录", default)
|
||||
if dir_path:
|
||||
self.output_dir.set_path(dir_path)
|
||||
@ -704,7 +697,7 @@ class Step14Panel(QWidget):
|
||||
|
||||
out_dir = (self.output_dir.get_path() or "").strip()
|
||||
if not out_dir:
|
||||
out_dir = os.path.join(self._get_default_work_dir(), "14_visualization")
|
||||
out_dir = resolve_subdir(self._get_default_work_dir(), 'visualization')
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
self.run_button.setEnabled(False)
|
||||
@ -760,7 +753,7 @@ class Step14Panel(QWidget):
|
||||
# 构造输出路径
|
||||
out_dir = (self.output_dir.get_path() or "").strip()
|
||||
if not out_dir:
|
||||
out_dir = os.path.join(self._get_default_work_dir(), "14_visualization")
|
||||
out_dir = resolve_subdir(self._get_default_work_dir(), 'visualization')
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
tif_stem = Path(geotiff_path).stem
|
||||
chinese_name = mapper._get_chinese_title(tif_stem)
|
||||
|
||||
@ -5,6 +5,14 @@ Step1 面板 - 水域掩膜生成
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QLabel,
|
||||
@ -197,8 +205,8 @@ class Step1Panel(QWidget):
|
||||
if not hasattr(self, 'work_dir') or not self.work_dir:
|
||||
return
|
||||
|
||||
# 生成输出掩膜的完整路径
|
||||
output_dir = os.path.join(self.work_dir, "1_water_mask")
|
||||
# 生成输出掩膜的完整路径(用 resolve_subdir 归一化,消除硬编码)
|
||||
output_dir = resolve_subdir(self.work_dir, 'water_mask')
|
||||
os.makedirs(output_dir, exist_ok=True) # 确保目录存在
|
||||
|
||||
# 统一使用正斜杠,避免 \ 和 / 混用
|
||||
|
||||
@ -5,6 +5,14 @@ Step2 面板 - 耀斑区域识别
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||
@ -187,7 +195,7 @@ class Step2Panel(QWidget):
|
||||
# 3. 自动填充输出路径(基于工作目录)
|
||||
if self.work_dir:
|
||||
# 生成输出耀斑掩膜的标准路径:workspace/2_Glint_Detection/severe_glint_area.dat
|
||||
output_dir = os.path.join(self.work_dir, "2_Glint_Detection")
|
||||
output_dir = resolve_subdir(self.work_dir, 'glint_detection')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
default_output_path = os.path.join(output_dir, "severe_glint_area.dat").replace('\\', '/')
|
||||
self.output_file.set_path(default_output_path)
|
||||
|
||||
@ -5,6 +5,14 @@ Step3 面板 - 耀斑去除
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||
@ -299,7 +307,7 @@ class Step3Panel(QWidget):
|
||||
|
||||
# 自动填充输出路径(基于工作目录)
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "3_deglint")
|
||||
output_dir = resolve_subdir(self.work_dir, 'deglint')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
default_output_path = os.path.join(output_dir, "deglint_image.bsq").replace('\\', '/')
|
||||
self.output_file.set_path(default_output_path)
|
||||
|
||||
@ -5,6 +5,14 @@ Step4 面板 - 采样点布设
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
from PyQt5.QtWidgets import (
|
||||
@ -186,7 +194,7 @@ class Step4SamplingPanel(QWidget):
|
||||
water_mask_path = main_window.step1_panel.output_file.get_path()
|
||||
# 备选:扫描 1_water_mask 目录下的 .dat 文件
|
||||
if not water_mask_path and self.work_dir:
|
||||
mask_dir = os.path.join(self.work_dir, "1_water_mask")
|
||||
mask_dir = resolve_subdir(self.work_dir, 'water_mask')
|
||||
if os.path.isdir(mask_dir):
|
||||
dat_files = [f for f in os.listdir(mask_dir) if f.lower().endswith('.dat')]
|
||||
if dat_files:
|
||||
@ -213,7 +221,7 @@ class Step4SamplingPanel(QWidget):
|
||||
|
||||
# 3. 自动填充输出路径(绝对路径)
|
||||
if self.work_dir:
|
||||
output_path = os.path.join(self.work_dir, "4_sampling", "sampling_spectra.csv")
|
||||
output_path = resolve_subdir(self.work_dir, 'sampling_csv_path')
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
self.output_file.set_path(output_path.replace('\\', '/'))
|
||||
|
||||
|
||||
@ -5,6 +5,14 @@ Step4 面板 - 数据预处理
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
import pandas as pd
|
||||
from PyQt5.QtWidgets import (
|
||||
@ -127,7 +135,7 @@ class Step5CleanPanel(QWidget):
|
||||
self.work_dir = None
|
||||
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "5_Data_Cleaning")
|
||||
output_dir = resolve_subdir(self.work_dir, 'data_cleaning')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
default_output_path = os.path.join(output_dir, "processed_data.csv").replace('\\', '/')
|
||||
self.output_file.set_path(default_output_path)
|
||||
|
||||
@ -5,6 +5,14 @@ Step6 面板 - 光谱特征提取
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QLabel,
|
||||
@ -193,7 +201,7 @@ class Step6FeaturePanel(QWidget):
|
||||
|
||||
# 4. 自动填充输出路径(基于工作目录)
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "6_Spectral_Feature_Extraction")
|
||||
output_dir = resolve_subdir(self.work_dir, 'spectral_feature')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
default_output_path = os.path.join(output_dir, "training_spectra.csv").replace('\\', '/')
|
||||
self.output_file.set_path(default_output_path)
|
||||
|
||||
@ -10,6 +10,12 @@ import pandas as pd
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QGridLayout,
|
||||
QHBoxLayout, QLabel, QCheckBox, QPushButton, QMessageBox,
|
||||
@ -261,7 +267,7 @@ class Step7IndexPanel(QWidget):
|
||||
|
||||
work_dir = self._get_work_dir()
|
||||
if work_dir:
|
||||
track_a_dir = os.path.join(work_dir, "7_Water_Quality_Indices")
|
||||
track_a_dir = resolve_subdir(work_dir, 'indices')
|
||||
os.makedirs(track_a_dir, exist_ok=True)
|
||||
config['output_file'] = os.path.join(track_a_dir, "training_spectra_indices.csv").replace('\\', '/')
|
||||
|
||||
|
||||
@ -5,6 +5,14 @@ Step8 面板 - 机器学习建模
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QGridLayout,
|
||||
@ -271,7 +279,7 @@ class Step8MlTrainPanel(QWidget):
|
||||
if not initial_dir or not os.path.isdir(initial_dir):
|
||||
# 默认定位到 indices 目录
|
||||
work_dir = self._get_default_work_dir()
|
||||
initial_dir = os.path.join(work_dir, "7_Water_Quality_Indices") if work_dir else ""
|
||||
initial_dir = resolve_subdir(work_dir, 'indices') if work_dir else ""
|
||||
if initial_dir and not os.path.isdir(initial_dir):
|
||||
os.makedirs(initial_dir, exist_ok=True)
|
||||
|
||||
@ -350,18 +358,19 @@ class Step8MlTrainPanel(QWidget):
|
||||
self.work_dir = None
|
||||
|
||||
# 1. 尝试从 Step5 界面读取训练数据路径,并确保为绝对路径
|
||||
# 修复张冠李戴:原代码 main_window.step5_panel 不存在,正确属性是 step5_clean_panel
|
||||
main_window = self.window()
|
||||
if hasattr(main_window, 'step5_panel'):
|
||||
# 优先直接从 Step5 的输出 widget 读取
|
||||
step5_output = main_window.step5_panel.output_file.get_path()
|
||||
if step5_output:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step5_output):
|
||||
step5_output = os.path.join(self.work_dir or '', step5_output).replace('\\', '/')
|
||||
self.training_csv_file.set_path(step5_output)
|
||||
elif hasattr(main_window, 'step5_panel') and hasattr(main_window.step5_panel, 'get_config'):
|
||||
# 回退:从 Step5 的 config 字典中查找可能的键名
|
||||
step5_cfg = main_window.step5_panel.get_config()
|
||||
step5_output = get_step_output_path(
|
||||
main_window, 'training_spectra_csv', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step6_feature',
|
||||
)
|
||||
if step5_output:
|
||||
self.training_csv_file.set_path(step5_output)
|
||||
else:
|
||||
# 回退:从 Step5 的 config 字典中查找可能的键名
|
||||
step5_panel = getattr(main_window, 'step5_clean_panel', None)
|
||||
if step5_panel and hasattr(step5_panel, 'get_config'):
|
||||
step5_cfg = step5_panel.get_config()
|
||||
step5_csv = (
|
||||
step5_cfg.get('training_csv_path')
|
||||
or step5_cfg.get('output_file')
|
||||
@ -369,7 +378,6 @@ class Step8MlTrainPanel(QWidget):
|
||||
or step5_cfg.get('output_csv')
|
||||
)
|
||||
if step5_csv:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step5_csv):
|
||||
step5_csv = os.path.join(self.work_dir or '', step5_csv).replace('\\', '/')
|
||||
self.training_csv_file.set_path(step5_csv)
|
||||
@ -378,7 +386,7 @@ class Step8MlTrainPanel(QWidget):
|
||||
# 输入是 training_spectra.csv → 输出 {work_dir}/7_Water_Quality_Indices/training_spectra_indices.csv
|
||||
# 输入是 sampling_spectra.csv → 输出 {work_dir}/7_Water_Quality_Indices/sampling_spectra_indices.csv
|
||||
if self.work_dir:
|
||||
indices_dir = os.path.join(self.work_dir, "7_Water_Quality_Indices")
|
||||
indices_dir = resolve_subdir(self.work_dir, 'indices')
|
||||
os.makedirs(indices_dir, exist_ok=True)
|
||||
training_csv = self.training_csv_file.get_path()
|
||||
if training_csv:
|
||||
|
||||
@ -5,8 +5,15 @@ Step8 面板 - 非经验统计回归建模
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QGridLayout,
|
||||
QHBoxLayout, QLabel, QCheckBox, QSpinBox, QPushButton,
|
||||
@ -231,25 +238,19 @@ class Step8NonEmpiricalPanel(QWidget):
|
||||
# 借用父组件的 window() 方法,安全绕过当前类的命名冲突
|
||||
parent_widget = self.parentWidget()
|
||||
main_window = parent_widget.window() if parent_widget else None
|
||||
if main_window and hasattr(main_window, 'step5_panel'):
|
||||
step5_widget = getattr(main_window.step5_panel, 'output_file', None)
|
||||
step5_output_path = ""
|
||||
if hasattr(step5_widget, 'get_path'):
|
||||
step5_output_path = step5_widget.get_path() or ""
|
||||
elif hasattr(step5_widget, 'text'):
|
||||
step5_output_path = step5_widget.text() or ""
|
||||
|
||||
if step5_output_path:
|
||||
# 若为相对路径,使用 work_dir 合成为绝对路径
|
||||
if not os.path.isabs(step5_output_path):
|
||||
step5_output_path = os.path.join(self.work_dir or '', step5_output_path).replace('\\', '/')
|
||||
existing = self.training_csv_file.get_path()
|
||||
if not existing or not existing.strip():
|
||||
self.training_csv_file.set_path(step5_output_path)
|
||||
# 1. 尝试从 Step5(数据清洗)读取训练光谱 CSV(修复张冠李戴:原 main_window.step5_panel 不存在)
|
||||
step5_output_path = get_step_output_path(
|
||||
main_window, 'training_spectra_csv', work_dir=self.work_dir,
|
||||
widget_attr='output_file', fallback_key='step6_feature',
|
||||
)
|
||||
if step5_output_path:
|
||||
existing = self.training_csv_file.get_path()
|
||||
if not existing or not existing.strip():
|
||||
self.training_csv_file.set_path(step5_output_path)
|
||||
|
||||
# 2. 自动填充输出目录(8_Regression_Modeling)
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "8_Regression_Modeling")
|
||||
output_dir = resolve_subdir(self.work_dir, 'regression_modeling')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
existing_out = self.output_dir.get_path()
|
||||
if not existing_out or not existing_out.strip():
|
||||
@ -274,7 +275,7 @@ class Step8NonEmpiricalPanel(QWidget):
|
||||
"""浏览输出目录"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "8_Regression_Modeling")
|
||||
default = resolve_subdir(default, 'regression_modeling')
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出模型目录", default)
|
||||
if dir_path:
|
||||
self.output_dir.set_path(dir_path)
|
||||
|
||||
@ -5,6 +5,13 @@ Step8 面板 - QAA 物理反演(非经验模型)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 路径归一化 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 resolve_subdir
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QGridLayout,
|
||||
@ -143,7 +150,7 @@ class Step8QAAPanel(QWidget):
|
||||
|
||||
if not initial_dir or not os.path.isdir(initial_dir):
|
||||
work_dir = self._get_default_work_dir()
|
||||
initial_dir = os.path.join(work_dir, "8_QAA_Inversion") if work_dir else ""
|
||||
initial_dir = resolve_subdir(work_dir, 'qaa_inversion') if work_dir else ""
|
||||
if initial_dir and not os.path.isdir(initial_dir):
|
||||
os.makedirs(initial_dir, exist_ok=True)
|
||||
|
||||
@ -198,7 +205,7 @@ class Step8QAAPanel(QWidget):
|
||||
self.spectrum_csv_file.set_path(step5_output)
|
||||
|
||||
if self.work_dir:
|
||||
qaa_dir = os.path.join(self.work_dir, "8_QAA_Inversion")
|
||||
qaa_dir = resolve_subdir(self.work_dir, 'qaa_inversion')
|
||||
os.makedirs(qaa_dir, exist_ok=True)
|
||||
output_path = os.path.join(qaa_dir, "a_lambda_results.csv").replace('\\', '/')
|
||||
self.output_path.set_path(output_path)
|
||||
@ -228,7 +235,7 @@ class Step8QAAPanel(QWidget):
|
||||
|
||||
if not output_path:
|
||||
work_dir = self._get_default_work_dir()
|
||||
qaa_dir = os.path.join(work_dir, "8_QAA_Inversion") if work_dir else ""
|
||||
qaa_dir = resolve_subdir(work_dir, 'qaa_inversion') if work_dir else ""
|
||||
if qaa_dir and not os.path.isdir(qaa_dir):
|
||||
os.makedirs(qaa_dir, exist_ok=True)
|
||||
output_path = os.path.join(qaa_dir, "a_lambda_results.csv").replace('\\', '/')
|
||||
|
||||
@ -5,8 +5,15 @@ Step11 面板 - 机器学习预测
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 路径归一化 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.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
||||
@ -190,7 +197,7 @@ class Step9MlPredictPanel(QWidget):
|
||||
"""浏览模型母文件夹,自动扫描子目录中的 .joblib 文件"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "9_ML_Prediction")
|
||||
default = resolve_subdir(default, 'ml_prediction')
|
||||
dir_path = QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
"选择模型母文件夹",
|
||||
@ -334,25 +341,20 @@ class Step9MlPredictPanel(QWidget):
|
||||
if not existing or not existing.strip():
|
||||
self.sampling_csv_file.set_path(step4_output_path)
|
||||
|
||||
# 2. 尝试从 Step9(监督建模)读取模型目录
|
||||
if main_window and hasattr(main_window, 'step9_panel'):
|
||||
step9_widget = getattr(main_window.step9_panel, 'output_dir', None)
|
||||
step9_models_dir = ""
|
||||
if hasattr(step9_widget, 'get_path'):
|
||||
step9_models_dir = step9_widget.get_path() or ""
|
||||
elif hasattr(step9_widget, 'text'):
|
||||
step9_models_dir = step9_widget.text() or ""
|
||||
# 2. 尝试从 Step8(监督建模)读取模型目录(修复张冠李戴:原代码 main_window.step9_panel 不存在)
|
||||
step8_models_dir = get_step_output_path(
|
||||
main_window, 'models_dir', work_dir=self.work_dir,
|
||||
widget_attr='output_dir', fallback_key='step8_ml_train',
|
||||
)
|
||||
if step8_models_dir:
|
||||
existing_models = self.models_dir_file.get_path()
|
||||
if not existing_models or not existing_models.strip():
|
||||
self.models_dir_file.set_path(step8_models_dir)
|
||||
|
||||
if step9_models_dir:
|
||||
if not os.path.isabs(step9_models_dir):
|
||||
step9_models_dir = os.path.join(self.work_dir or '', step9_models_dir).replace('\\', '/')
|
||||
existing_models = self.models_dir_file.get_path()
|
||||
if not existing_models or not existing_models.strip():
|
||||
self.models_dir_file.set_path(step9_models_dir)
|
||||
|
||||
# 3. 自动填充输出路径(机器学习预测目录)
|
||||
# 3. 自动填充输出路径(机器学习预测目录,归属 step9 → 9_ML_Prediction)
|
||||
# 注:9_ML_Prediction 是 prediction_dir 的子目录,用本地约定
|
||||
if self.work_dir:
|
||||
output_dir = os.path.join(self.work_dir, "9_ML_Prediction")
|
||||
output_dir = resolve_subdir(self.work_dir, 'ml_prediction')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
existing_out = self.output_file.get_path()
|
||||
if not existing_out or not existing_out.strip():
|
||||
@ -375,7 +377,7 @@ class Step9MlPredictPanel(QWidget):
|
||||
"""浏览模型目录"""
|
||||
default = self._get_default_work_dir()
|
||||
if default:
|
||||
default = os.path.join(default, "9_ML_Prediction")
|
||||
default = resolve_subdir(default, 'ml_prediction')
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择模型目录", default)
|
||||
if dir_path:
|
||||
self.models_dir_file.set_path(dir_path)
|
||||
|
||||
Reference in New Issue
Block a user