refactor: 重构所有面板 update_from_config 为文件系统扫描模式

=== 核心变更 ===
- _step_path_resolver 新增 scan_work_dir_for_input() 统一文件扫描工具
- 基于 _SCAN_TABLE 映射表,按 output_type 自动扫描 work_dir 子目录
- 支持扩展名匹配(.dat/.tif/.bsq/.csv 等)和文件名关键词匹配
- 按 mtime 排序,返回最新匹配文件

=== 各面板重构 ===
- step2/3/4/6: 废弃 main_window.stepX_panel 跨面板读取
  统一改为 scan_work_dir_for_input(work_dir, 'water_mask/glint_mask/deglint_image')
- step6: 移除对 step1/2/3/5 panel 的4处跨面板依赖
- step8: 替换 _resolve_training_csv_from_workdir 为 scan_work_dir_for_input
  优先级:training_spectra_indices -> training_spectra
- step9: 替换 _resolve_latest_wqi_test_csv 为 scan_work_dir_for_input
  废弃 factory.get_panel('step4_sampling') 和 get_panel('step8_ml_train')
- step10: 替换3层回退链为 scan_work_dir_for_input('sampling_points')
- step11: 替换 factory.get_panel('step9')/('step1') 为文件扫描

=== 删除的冗余方法 ===
- step8._resolve_training_csv_from_workdir (~55行)
- step9._resolve_latest_wqi_test_csv (~56行)

=== 数据流原则 ===
1. pipeline context 为第一顺位(执行时内存状态)
2. 文件系统扫描为回退(仅信任硬盘上真实存在的文件)
3. 彻底禁止面板间 UI 控件互相读取
This commit is contained in:
duxin
2026-06-30 09:53:23 +08:00
parent 1bdd623fe7
commit 73cb019a4a
9 changed files with 194 additions and 312 deletions

View File

@ -183,9 +183,91 @@ def resolve_subdir(work_dir, subdir_key: str) -> str:
return wd
# ═══════════════════════════════════════════════════════════════
# 文件系统扫描表 —— 定义每个上游产出类型对应的搜索策略
# ═══════════════════════════════════════════════════════════════
_SCAN_TABLE = {
# output_type → (subdir_key, extensions, file_name_matcher)
'water_mask': ('water_mask', ['.dat', '.tif', '.tiff'], None),
'glint_mask': ('glint_detection', ['.dat'], 'severe_glint'),
'deglint_image': ('deglint', ['.bsq', '.dat', '.tif', '.tiff'], None),
'sampling_points': ('sampling', ['.csv'], 'sampling_spectra'),
'processed_data': ('data_cleaning', ['.csv'], 'processed_data'),
'training_spectra': ('spectral_feature',['.csv'], 'training_spectra'),
'training_spectra_indices': ('indices', ['.csv'], 'training_spectra_indices'),
'ml_models_dir': ('supervised_models', None, None), # 目录
'ml_predictions_dir': ('ml_prediction', None, None), # 目录
'water_index_csv_dir': ('watercolor', None, None), # 目录
'visualization_dir': ('visualization', None, None), # 目录
'reference_img': ('water_mask', ['.bsq', '.dat', '.tif', '.tiff'], None),
}
def scan_work_dir_for_input(work_dir: str, output_type: str):
"""基于文件系统扫描查找上游步骤的产出文件。
这是 update_from_config 重构的核心工具函数。
仅信任硬盘上真实存在的文件,彻底消除面板间 UI 控件互相读取。
Args:
work_dir: 工作目录路径
output_type: 产出类型,如 'water_mask', 'deglint_image', 'sampling_points'
Returns:
找到的文件/目录绝对路径字符串,未找到返回 None
"""
if not work_dir:
return None
wd = Path(work_dir)
entry = _SCAN_TABLE.get(output_type)
if entry is None:
return None
subdir_key, extensions, matcher = entry
target_dir = wd / _FALLBACK_DIR_TABLE.get(subdir_key, subdir_key)
if not target_dir.is_dir():
return None
# 目录类型(extensions 为 None)→ 只要目录存在就返回
if extensions is None:
return str(target_dir).replace('\\', '/')
# 扫描目录中的文件
candidates = []
for ext in extensions:
for f in target_dir.glob(f'*{ext}'):
if not f.is_file():
continue
candidates.append(f)
# 也搜 ext 的大写变体
for f in target_dir.glob(f'*{ext.upper()}'):
if not f.is_file():
continue
candidates.append(f)
if not candidates:
return None
# 按 matcher 优先级 + mtime 排序
if matcher:
matched = [f for f in candidates if matcher.lower() in f.stem.lower()]
if matched:
matched.sort(key=lambda p: p.stat().st_mtime, reverse=True)
return str(matched[0]).replace('\\', '/')
# matcher 未命中时,仍返回最新文件(宽松匹配)
candidates.sort(key=lambda p: p.stat().st_mtime, reverse=True)
return str(candidates[0]).replace('\\', '/')
# 无 matcher → 返回最新的
candidates.sort(key=lambda p: p.stat().st_mtime, reverse=True)
return str(candidates[0]).replace('\\', '/')
__all__ = [
'STEP_DATA_SOURCE',
'resolve_step_widget',
'get_step_output_path',
'resolve_subdir',
'scan_work_dir_for_input',
]