路径归一化:统一 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:
DXC
2026-06-16 12:54:18 +08:00
parent 03c788a16c
commit 0238aa66ab
19 changed files with 544 additions and 222 deletions

View File

@ -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)