imput导入修正,去除掉废弃内容
This commit is contained in:
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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("")
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user