imput导入修正,去除掉废弃内容

This commit is contained in:
duxin
2026-06-30 11:32:40 +08:00
parent f05916bc3a
commit 8ff5b08190
13 changed files with 232 additions and 191 deletions

View File

@ -15,16 +15,11 @@ Step10 面板 - 水色指数反演(散点 CSV 模式)
"""
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, scan_work_dir_for_input
from src.gui.panels._step_path_resolver import resolve_subdir, scan_work_dir_for_input
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout,

View File

@ -5,15 +5,10 @@ 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 src.gui.panels._step_path_resolver import resolve_subdir, get_step_output_path, scan_work_dir_for_input
from PyQt5.QtCore import Qt, QThread, pyqtSignal
@ -649,13 +644,13 @@ class Step11MapPanel(QWidget):
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)
output_png = os.path.join(out_dir, f"{chinese_name}_专题图.png")
self.run_button.setEnabled(False)
try:
from src.postprocessing.map import ContentMapper
mapper = ContentMapper()
chinese_name = mapper._get_chinese_title(tif_stem)
output_png = os.path.join(out_dir, f"{chinese_name}_专题图.png")
result_path = mapper.visualize_raster(
raster_tif_path=geotiff_path,
output_file=output_png,

View File

@ -6,7 +6,6 @@ VisualizationPanel - 可视化分析面板
"""
import os
import sys
import traceback
from pathlib import Path
from typing import Optional, List, Union
@ -14,11 +13,7 @@ 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 src.gui.panels._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
@ -1716,7 +1711,7 @@ class Step12VizPanel(QWidget):
factory = getattr(main_window, '_panel_factory', None) if main_window else None
# [新增] 直接从 Step 1 面板读取原始 .shp 的绝对路径,突破工作目录限制
step1_panel = factory.get_panel('step1_mask') if factory else None
step1_panel = factory.get_panel('step1') if factory else None
if step1_panel:
s1_conf = step1_panel.get_config()
s1_mask = s1_conf.get('mask_path')

View File

@ -5,16 +5,11 @@ 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 src.gui.panels._step_path_resolver import resolve_subdir
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSettings
from PyQt5.QtWidgets import (

View File

@ -5,13 +5,9 @@ Step1 面板 - 水域掩膜生成 (已移除“启用此步骤”)
"""
import os
import sys
from pathlib import Path
_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 src.gui.panels._step_path_resolver import resolve_subdir
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QLabel,

View File

@ -5,13 +5,9 @@ Step2 面板 - 耀斑区域识别 (已移除“启用此步骤”)
"""
import os
import sys
from pathlib import Path
_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, scan_work_dir_for_input
from src.gui.panels._step_path_resolver import resolve_subdir, scan_work_dir_for_input
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QFormLayout,

View File

@ -5,13 +5,9 @@ Step3 面板 - 耀斑去除 (已移除“启用此步骤”)
"""
import os
import sys
from pathlib import Path
_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, scan_work_dir_for_input
from src.gui.panels._step_path_resolver import resolve_subdir, scan_work_dir_for_input
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QFormLayout,
@ -268,7 +264,7 @@ class Step3Panel(QWidget):
form_layout.addRow(row_layout)
def open_interactive_viewer(self):
from src.gui.water_quality_gui import InteractiveViewerDialog
from src.gui.components.chart_dialogs import InteractiveViewerDialog
img_path = self.img_file.get_path()
if not img_path or not os.path.isfile(img_path):
QMessageBox.warning(self, "警告", "请先选择影像文件!")
@ -381,7 +377,7 @@ class Step3Panel(QWidget):
config['sugar_iter'] = self.sugar_iter.value() if self.sugar_iter.value() > 0 else None
config['sugar_sigma'] = self._safe_float(self.sugar_sigma, 1.0)
config['sugar_estimate_background'] = self.sugar_estimate_background.isChecked()
config['sugar_glint_mask_method'] = self.sugar_glint_mask_method.currentData()
config['sugar_glint_mask_method'] = self.sugar_glint_mask_method.currentText()
config['sugar_termination_thresh'] = self._safe_float(self.sugar_termination_thresh, 20.0)
try:
import ast

View File

@ -12,7 +12,6 @@ Step4 面板 - 采样点布设(内嵌交互式光谱探针视图)
"""
import os
import sys
from pathlib import Path
import matplotlib.pyplot as plt
@ -23,10 +22,7 @@ import pandas as pd
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'sans-serif']
plt.rcParams['axes.unicode_minus'] = False
_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, scan_work_dir_for_input
from src.gui.panels._step_path_resolver import resolve_subdir, scan_work_dir_for_input
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import (

View File

@ -5,14 +5,9 @@ Step5 面板 - 数据清洗 (完美对齐无跳动重构版)
"""
import os
import sys
from pathlib import Path
# 路径归一化 helper
_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 src.gui.panels._step_path_resolver import resolve_subdir
import pandas as pd
from PyQt5.QtWidgets import (
@ -85,22 +80,32 @@ class Step5CleanPanel(QWidget):
controls_layout = QHBoxLayout()
controls_layout.setContentsMargins(0, 0, 0, 0)
# ── 【核心修复】:剥离 Suffix,恢复微调按钮,提升输入体验 ──
lbl = QLabel("加载预览行数:")
lbl.setMinimumWidth(100)
controls_layout.addWidget(lbl)
self.preview_rows_spin = QSpinBox()
self.preview_rows_spin.setRange(1, 200)
self.preview_rows_spin.setRange(1, 1000) # 放宽一下预览上限
self.preview_rows_spin.setValue(10)
self.preview_rows_spin.setSuffix(" 行")
self.preview_rows_spin.setButtonSymbols(QSpinBox.NoButtons)
self.preview_rows_spin.setMinimumWidth(80)
self.preview_rows_spin.setAlignment(Qt.AlignCenter) # 数字居中,体验更好
self.preview_rows_spin.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.preview_rows_spin.setMinimumWidth(90)
controls_layout.addWidget(self.preview_rows_spin)
suffix_lbl = QLabel("行") # 将单位作为独立标签,避免光标被困住
controls_layout.addWidget(suffix_lbl)
controls_layout.addSpacing(16)
self.preview_btn = QPushButton("刷新预览")
self.preview_btn.setStyleSheet(ModernStylesheet.get_button_stylesheet('normal'))
self.preview_btn.setMinimumWidth(100)
self.preview_btn.clicked.connect(self.load_csv_preview)
# 使用封装的方法实现严格对齐的行
self._add_row_with_fixed_label(controls_layout, "加载预览行数:", self.preview_rows_spin)
controls_layout.addWidget(self.preview_btn)
controls_layout.addStretch()
# ─────────────────────────────────────────────────────────
self.preview_table = QTableView()
self.preview_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
@ -155,7 +160,7 @@ class Step5CleanPanel(QWidget):
self.reset_preview()
def _add_row_with_fixed_label(self, layout, label_text, widget):
"""辅助方法:创建绝对锁死宽度的对齐行"""
"""辅助方法:创建绝对锁死宽度的对齐行 (保留原始代码结构) - 已废弃,保留仅用于兼容"""
lbl = QLabel(label_text)
lbl.setMinimumWidth(100)
layout.addWidget(lbl)
@ -215,19 +220,12 @@ class Step5CleanPanel(QWidget):
def reset_preview(self, message="请选择 CSV 文件并点击刷新预览"):
"""重置预览表格"""
try:
from src.gui.water_quality_gui import PandasTableModel
from src.gui.components.data_models import PandasTableModel
empty_model = PandasTableModel(pd.DataFrame())
self.preview_table.setModel(empty_model)
self.preview_status_label.setText(message)
except ImportError:
# 兼容水质软件 V2 版的重构包结构
try:
from src.new.main_view import PandasTableModel
empty_model = PandasTableModel(pd.DataFrame())
self.preview_table.setModel(empty_model)
self.preview_status_label.setText(message)
except ImportError:
self.preview_status_label.setText("数据预览模块加载失败,请检查 PandasTableModel 依赖")
self.preview_status_label.setText("数据预览模块加载失败")
def load_csv_preview(self):
"""加载 CSV 预览数据"""
@ -240,13 +238,10 @@ class Step5CleanPanel(QWidget):
return
try:
from src.gui.water_quality_gui import PandasTableModel
from src.gui.components.data_models import PandasTableModel
except ImportError:
try:
from src.new.main_view import PandasTableModel
except ImportError:
self.reset_preview("数据预览模块加载失败")
return
self.reset_preview("数据预览模块加载失败")
return
try:
rows_to_preview = max(1, self.preview_rows_spin.value())

View File

@ -5,13 +5,8 @@ Step6 面板 - 光谱特征提取 (完美对齐无跳动重构版)
"""
import os
import sys
from pathlib import Path
# 路径归一化 helper
_HERE = os.path.dirname(os.path.abspath(__file__))
if _HERE not in sys.path:
sys.path.insert(0, _HERE)
from src.gui.panels._step_path_resolver import resolve_subdir, scan_work_dir_for_input
from PyQt5.QtWidgets import (

View File

@ -6,7 +6,6 @@ Step7 视图 - 水质光谱指数计算 (完美对齐卡片化重构版)
import os
import sys
import csv
from pathlib import Path
import pandas as pd
@ -25,7 +24,7 @@ from src.gui.components.custom_widgets import FileSelectWidget
from src.gui.styles import ModernStylesheet
# ==========================================
# 模块级字典 + 防穿透 ListWidget(与 Step10 对齐)
# 模块级字典 + 防穿透 ListWidget
# ==========================================
CATEGORY_CHINESE_MAP = {
'Total_Suspended_Matter': '总悬浮物 (TSM)',
@ -43,7 +42,8 @@ CATEGORY_CHINESE_MAP = {
class NoScrollPassListWidget(QListWidget):
"""一个绝对不会把滚轮事件传给外层父组件的列表控件(与 Step10 同源)。"""
"""一个绝对不会把滚轮事件传给外层父组件的列表控件"""
def wheelEvent(self, event):
super().wheelEvent(event)
event.accept()
@ -52,7 +52,7 @@ class NoScrollPassListWidget(QListWidget):
class Step7InversionPanel(QWidget):
"""步骤7:水质光谱指数计算"""
# --- 保持原有的类级别映射表不变 ---
# --- 保留原有的类级别映射表作为兜底 ---
_formula_type_map = {
'NDWI': 'ratio', 'MNDWI': 'ratio', 'AWEI': 'ratio', 'AWEInsh': 'ratio',
'NDTI': 'ratio', 'WRI': 'ratio', 'NDSS': 'ratio', 'NDSSI': 'ratio',
@ -74,48 +74,10 @@ class Step7InversionPanel(QWidget):
'COD_4': 'concentration'
}
_formula_color_map = {
'ratio': ModernStylesheet.COLORS['panel_bg'], # 比值型用白色底
'concentration': ModernStylesheet.COLORS['primary_light'] # 浓度型用浅蓝底
}
_formula_coef_map = {
'Chla_3B': [25.0456, 116.8924],
'Chla_2B': [5.8753, 56.4025],
'Chla_2B_sim': [38.16, 21.36],
'Chla_4B': [27.79, 137.9],
'Chla_NDCI': [14.039, 86.115, 194.325],
'Chla_NDI': [1.321, 23.94],
'Chla_Peak': [3.614, 5.097],
'Chla_SABG': [10.2, 5.6],
'Chla_MCI': [0.5, 0.2],
'Chla_FAI': [0.8, 0.1],
'TP_1': [0.1, 0.05],
'TP_2': [0.2, 0.1],
'TP_3': [0.15, 0.08],
'TP_4': [0.12, 0.06],
'TP_5': [0.18, 0.09],
'TP_6': [0.25, 0.12],
'TN_1': [0.5, 0.2],
'TN_2': [0.6, 0.3],
'TN_3': [0.45, 0.15],
'TN_4': [0.55, 0.25],
'NH3_1': [0.05, 0.02],
'NH3_2': [0.06, 0.03],
'NH3_3': [0.04, 0.015],
'NH3_4': [0.07, 0.035],
'COD_1': [10.0, 5.0],
'COD_2': [12.0, 6.0],
'COD_3': [8.5, 4.0],
'COD_4': [11.0, 5.5],
}
def __init__(self, parent=None):
self._formula_df = None
self._categories = []
self._current_type_filter = "all" # 'all', 'ratio', 'concentration'
super().__init__(parent)
self.init_ui()
@ -123,7 +85,7 @@ class Step7InversionPanel(QWidget):
# 注入全局样式
self.setStyleSheet(ModernStylesheet.get_main_stylesheet())
# 主布局:增加四周留白(24px)和呼吸间距(20px)
# 主布局
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(24, 24, 24, 24)
main_layout.setSpacing(20)
@ -157,7 +119,6 @@ class Step7InversionPanel(QWidget):
self.formula_file.line_edit.setReadOnly(True)
self.formula_file.label.setMinimumWidth(120)
# 智能查找自带的公式库文件(兼容开发 + PyInstaller 打包)
builtin_csv = self._resolve_builtin_csv()
if builtin_csv:
self.formula_file.set_path(builtin_csv)
@ -181,7 +142,6 @@ class Step7InversionPanel(QWidget):
params_layout.setSpacing(16)
params_layout.setContentsMargins(20, 24, 20, 20)
# 辅助布局:公式选择列表
list_row = QHBoxLayout()
list_row.setContentsMargins(0, 0, 0, 0)
@ -197,7 +157,17 @@ class Step7InversionPanel(QWidget):
# 按钮工具栏
btn_layout = QHBoxLayout()
btn_layout.setContentsMargins(0, 0, 0, 0)
btn_layout.setSpacing(10) # 👉 [新增] 增加按钮之间的水平间距,不再挤在一起
btn_layout.setSpacing(10)
# 👉 【新增】: 按类别筛选下拉框 (对齐 Step 10)
btn_layout.addWidget(QLabel("按类别筛选:"))
self.category_combo = QComboBox()
self.category_combo.setStyleSheet("""
QComboBox { padding: 4px 8px; border: 1px solid #C0C0C0; border-radius: 4px; min-height: 24px; }
""")
self.category_combo.currentIndexChanged.connect(self._on_category_changed)
btn_layout.addWidget(self.category_combo)
btn_layout.addSpacing(10)
self.btn_select_all = QPushButton("全选")
self.btn_select_none = QPushButton("清空")
@ -207,26 +177,28 @@ class Step7InversionPanel(QWidget):
for btn in [self.btn_select_all, self.btn_select_none,
self.btn_select_ratio, self.btn_select_conc, self.btn_reload]:
base_style = ModernStylesheet.get_button_stylesheet('normal')
# 👉 [修改] 追加左右内边距 (padding-left/right),保证无论文字长短都不会被截断
btn.setStyleSheet(base_style + "\nQPushButton { padding-left: 14px; padding-right: 14px; }")
# 👉 [修改] 高度从 28 稍微调高到 30,让中文字体上下有更好的呼吸感
btn.setFixedHeight(30)
# 👉 [新增] 设置尺寸策略:高度固定,宽度根据文字内容自动撑开
btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
btn_layout.addWidget(btn)
btn_layout.addStretch()
# 👉 【升级】: 统计标签 (显示选中、当前可见、总计)
self.formula_count_label = QLabel("已选: 0 | 显示: 0 / 共 0 个")
self.formula_count_label.setStyleSheet(
"color: #666666; font-size: 13px; font-weight: bold; padding-right: 10px;")
btn_layout.addWidget(self.formula_count_label)
right_layout.addLayout(btn_layout)
# 列表框 (去掉 setSelectionMode,改为默认行为)
self.formula_list = QListWidget()
self.formula_list.setMinimumHeight(150)
# 用样式表去掉默认的边框和丑陋的焦点框,增加 hover 和复选框大小调整
# 列表框 (使用自定义防穿透类)
self.formula_list = NoScrollPassListWidget()
self.formula_list.setMinimumHeight(220) # 稍微加高以展示复杂内容
self.formula_list.setWordWrap(True) # 允许公式换行
# 禁用原生的 selected 高亮样式,并设置透明背景
self.formula_list.setStyleSheet(f"""
QListWidget {{
border: 1px solid {ModernStylesheet.COLORS['border']};
@ -235,19 +207,28 @@ class Step7InversionPanel(QWidget):
outline: none;
}}
QListWidget::item {{
padding: 4px 8px; /* 稍微减小上下间距,给复选框留空间 */
padding: 8px 8px; /* 加大 padding 让多行显示更舒服 */
border-bottom: 1px solid {ModernStylesheet.COLORS['border_light']};
}}
QListWidget::item:hover {{
background-color: {ModernStylesheet.COLORS['hover']};
}}
/* 放大复选框并拉开间距 */
QListWidget::item:selected {{
background-color: transparent;
color: #333333;
}}
QListWidget::indicator {{
width: 16px;
height: 16px;
margin-right: 6px;
}}
""")
# 绑定状态改变事件
self.formula_list.itemChanged.connect(self._update_formula_count)
# 绑定行选中事件,实现点击整行打勾
self.formula_list.itemSelectionChanged.connect(self._on_selection_changed)
right_layout.addWidget(self.formula_list)
list_row.addWidget(list_label)
@ -300,14 +281,7 @@ class Step7InversionPanel(QWidget):
self._load_formulas_from_csv()
def _resolve_builtin_csv(self):
"""探测内置 waterindex.csv 的实际位置。
按优先级探测 4 个候选路径(兼容开发 + PyInstaller 打包):
1. sys._MEIPASS/_internal/model/ (PyInstaller onedir 带 _MEIPASS)
2. sys._MEIPASS/model/ (PyInstaller onefile 或 onedir)
3. <panel_dir>/../model/ (开发模式真实位置:src/gui/model/)
4. <panel_dir>/../../model/ (项目根 model/,兼容旧布局)
"""
"""探测内置 waterindex.csv 的实际位置。"""
panel_dir = os.path.dirname(os.path.abspath(__file__))
candidates = [
os.path.join(panel_dir, '..', 'model', 'waterindex.csv'),
@ -325,9 +299,8 @@ class Step7InversionPanel(QWidget):
def get_config(self) -> dict:
"""收集当前界面的配置字典"""
# 只筛选出打勾状态为 Checked 的项
selected_names = [
self.formula_list.item(i).text()
self.formula_list.item(i).data(Qt.UserRole) # 从隐藏属性中提取纯净的名称
for i in range(self.formula_list.count())
if self.formula_list.item(i).checkState() == Qt.Checked
]
@ -336,7 +309,7 @@ class Step7InversionPanel(QWidget):
'training_csv_path': self.training_data_widget.get_path(),
'formula_csv_file': self.formula_file.get_path(),
'formula_names': selected_names,
'enabled': True # 默认启用
'enabled': True
}
output_path = self.output_file.get_path()
if output_path:
@ -350,15 +323,16 @@ class Step7InversionPanel(QWidget):
if 'formula_csv_file' in config:
self.formula_file.set_path(config['formula_csv_file'])
# 恢复勾选状态
if 'formula_names' in config:
target_names = set(config['formula_names'])
self.formula_list.blockSignals(True)
for i in range(self.formula_list.count()):
item = self.formula_list.item(i)
state = Qt.Checked if item.text() in target_names else Qt.Unchecked
item_name = item.data(Qt.UserRole)
state = Qt.Checked if item_name in target_names else Qt.Unchecked
item.setCheckState(state)
self.formula_list.blockSignals(False)
self._update_formula_count()
if 'output_path' in config:
self.output_file.set_path(config['output_path'])
@ -383,48 +357,180 @@ class Step7InversionPanel(QWidget):
break
if not target_col:
QMessageBox.warning(self, "错误", f"CSV 文件格式不正确,列名应为 'Index_Name' 或 'Formula_Name'。当前列为: {list(df.columns)}")
QMessageBox.warning(self, "错误",
f"CSV 文件格式不正确,列名应为 'Index_Name' 或 'Formula_Name'。当前列为: {list(df.columns)}")
self.formula_list.blockSignals(False)
return
for idx_name in df[target_col]:
item = QListWidgetItem(idx_name)
# 👉 【提取所有分类并更新下拉框】
cats = set()
for _, row in df.iterrows():
if 'Category' in df.columns and pd.notna(row['Category']):
cats.add(str(row['Category']).strip())
else:
cats.add("综合/未分类")
# 开启复选框功能,并设置为默认选中
self._categories = sorted(list(cats))
self.category_combo.blockSignals(True)
self.category_combo.clear()
self.category_combo.addItem("全部类别", "全部")
for cat in self._categories:
display_name = CATEGORY_CHINESE_MAP.get(cat, cat)
self.category_combo.addItem(display_name, cat)
self.category_combo.blockSignals(False)
# 👉 【构建美观的列表项】
for _, row in df.iterrows():
idx_name = str(row[target_col]).strip()
item = QListWidgetItem()
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Checked)
ftype = self._formula_type_map.get(idx_name, 'ratio')
# 简化底色判断,保留类型的色彩提示
bg_color = Qt.transparent if ftype == 'ratio' else Qt.GlobalColor.cyan
# 解析公式和分类
formula_str = str(row['Formula']).strip() if 'Formula' in df.columns else ""
cat = str(row['Category']).strip() if 'Category' in df.columns and pd.notna(
row['Category']) else "综合/未分类"
# 解析精确的 Formula_Type
ftype = ''
if 'Formula_Type' in df.columns and pd.notna(row['Formula_Type']):
ftype = str(row['Formula_Type']).strip().lower()
if 'conc' in ftype:
ftype = 'concentration'
elif 'ratio' in ftype:
ftype = 'ratio'
else:
ftype = self._formula_type_map.get(idx_name, 'ratio')
# 保存隐式数据 (极其重要,用于过滤和配置提取)
item.setData(Qt.UserRole, idx_name)
item.setData(Qt.UserRole + 1, cat)
item.setData(Qt.UserRole + 2, ftype)
# 设置带分类和公式的美观文本 (对齐 Step 10)
cat_cn = CATEGORY_CHINESE_MAP.get(cat, cat)
ftype_cn = "比值型" if ftype == 'ratio' else "浓度型"
if formula_str:
item.setText(f"【{cat_cn}】 {idx_name} ({ftype_cn})\n公式: {formula_str}")
else:
item.setText(f"【{cat_cn}】 {idx_name} ({ftype_cn})")
# 设置底色
bg_color = QColor("#FFFFFF") if ftype == 'ratio' else QColor("#E8F4F8")
item.setBackground(bg_color)
self.formula_list.addItem(item)
self.formula_list.blockSignals(False)
self._update_formula_count()
except Exception as e:
QMessageBox.critical(self, "加载错误", f"加载公式文件时发生错误:\n{str(e)}")
def _select_all_formulas(self):
def _refresh_visibility(self):
"""核心过滤引擎:根据下拉框和类型按钮动态隐藏/显示公式"""
current_category = self.category_combo.currentData()
if not current_category:
current_category = "全部"
for i in range(self.formula_list.count()):
self.formula_list.item(i).setCheckState(Qt.Checked)
item = self.formula_list.item(i)
item_cat = item.data(Qt.UserRole + 1)
item_type = item.data(Qt.UserRole + 2)
cat_match = (current_category == "全部" or item_cat == current_category)
type_match = (self._current_type_filter == "all" or item_type == self._current_type_filter)
if cat_match and type_match:
item.setHidden(False)
else:
item.setHidden(True)
self._update_formula_count()
def _on_category_changed(self, index: int):
"""下拉框改变分类时触发"""
self._current_type_filter = "all" # 切换分类时默认重置类型过滤
self._refresh_visibility()
def _update_formula_count(self):
"""计算并更新多维度统计标签"""
total = self.formula_list.count()
visible = 0
selected = 0
for i in range(total):
item = self.formula_list.item(i)
if not item.isHidden():
visible += 1
if item.checkState() == Qt.Checked:
selected += 1
type_str = ""
if self._current_type_filter == 'ratio':
type_str = " (仅比值型)"
elif self._current_type_filter == 'concentration':
type_str = " (仅浓度型)"
self.formula_count_label.setText(f"已选: {selected} | 显示: {visible}{type_str} / 共 {total} 个")
def _on_selection_changed(self):
"""实现点击整行任意位置即可切换勾选状态,并立刻取消高亮"""
self.formula_list.blockSignals(True)
for item in self.formula_list.selectedItems():
new_state = Qt.Unchecked if item.checkState() == Qt.Checked else Qt.Checked
item.setCheckState(new_state)
item.setSelected(False) # 立刻取消原生选中状态,避免变白
self.formula_list.blockSignals(False)
self._update_formula_count()
def _select_all_formulas(self):
"""全选并显示所有(当前分类下的)公式"""
self._current_type_filter = "all"
self._refresh_visibility()
self.formula_list.blockSignals(True)
for i in range(self.formula_list.count()):
item = self.formula_list.item(i)
if not item.isHidden():
item.setCheckState(Qt.Checked)
self.formula_list.blockSignals(False)
self._update_formula_count()
def _clear_formula_selection(self):
"""只清空当前显示项的勾选"""
self.formula_list.blockSignals(True)
for i in range(self.formula_list.count()):
self.formula_list.item(i).setCheckState(Qt.Unchecked)
item = self.formula_list.item(i)
if not item.isHidden():
item.setCheckState(Qt.Unchecked)
self.formula_list.blockSignals(False)
self._update_formula_count()
def _select_ratio_formulas(self):
"""仅显示比值型,并勾选"""
self._current_type_filter = "ratio"
self._refresh_visibility()
self.formula_list.blockSignals(True)
for i in range(self.formula_list.count()):
item = self.formula_list.item(i)
ftype = self._formula_type_map.get(item.text(), 'ratio')
item.setCheckState(Qt.Checked if ftype == 'ratio' else Qt.Unchecked)
if not item.isHidden():
item.setCheckState(Qt.Checked)
self.formula_list.blockSignals(False)
self._update_formula_count()
def _select_conc_formulas(self):
"""仅显示浓度型,并勾选"""
self._current_type_filter = "concentration"
self._refresh_visibility()
self.formula_list.blockSignals(True)
for i in range(self.formula_list.count()):
item = self.formula_list.item(i)
ftype = self._formula_type_map.get(item.text(), 'ratio')
item.setCheckState(Qt.Checked if ftype == 'concentration' else Qt.Unchecked)
if not item.isHidden():
item.setCheckState(Qt.Checked)
self.formula_list.blockSignals(False)
self._update_formula_count()
def _on_run_single_clicked(self):
"""点击独立运行按钮:包装参数并通过 EventBus 派发任务"""
@ -433,7 +539,6 @@ class Step7InversionPanel(QWidget):
QMessageBox.warning(self, "输入错误", "请先选择输入的样本数据 (CSV)!")
return
# 检查是否有任何一项处于打勾状态
has_checked = any(
self.formula_list.item(i).checkState() == Qt.Checked
for i in range(self.formula_list.count())
@ -444,7 +549,6 @@ class Step7InversionPanel(QWidget):
return
config = self.get_config()
# 兼容旧架构的数据流,外层包装为 {'step_id': config}
payload = {
'step_name': 'step7_index',
'config': {'step7_index': config}
@ -452,12 +556,6 @@ class Step7InversionPanel(QWidget):
global_event_bus.publish('RequestRunSingleStep', payload)
def update_from_config(self, work_dir=None, pipeline=None):
"""从全局配置/Pipeline 同步工作目录。
step6 的训练数据已由 PANEL_REGISTRY 的 dependencies 自动通过 set_config
注入到 self.training_data_widget;此处仅缓存 work_dir 并填入默认输出路径,
不重复拉取,避免与 panel_factory 注入路径冲突。
"""
if work_dir:
self.work_dir = work_dir
elif hasattr(self, 'work_dir') and self.work_dir:
@ -465,10 +563,9 @@ class Step7InversionPanel(QWidget):
else:
self.work_dir = None
# 仅在输出框为空时填入默认路径(不创建目录,留给 pipeline 执行时创建)
if self.work_dir and not self.output_file.get_path():
output_dir = os.path.join(self.work_dir, "7_Water_Quality_Indices")
default_path = os.path.join(output_dir, "training_spectra_indices.csv").replace('\\', '/')
self.output_file.set_path(default_path)
elif not self.work_dir:
self.output_file.set_path("")
self.output_file.set_path("")

View File

@ -5,13 +5,8 @@ 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 src.gui.panels._step_path_resolver import get_step_output_path, resolve_step_widget, resolve_subdir, scan_work_dir_for_input
import pandas as pd

View File

@ -5,16 +5,11 @@ Step11 面板 - 机器学习预测
"""
import os
import sys
from pathlib import Path
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, scan_work_dir_for_input
from src.gui.panels._step_path_resolver import get_step_output_path, resolve_step_widget, resolve_subdir, scan_work_dir_for_input
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QGroupBox, QFormLayout,