239 lines
8.6 KiB
Python
239 lines
8.6 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Step10 面板 - 浓度反演(基于 QAA 物理反演的二次反演)
|
||
"""
|
||
|
||
import os
|
||
|
||
from PyQt5.QtWidgets import (
|
||
QWidget, QVBoxLayout, QGroupBox, QFormLayout, QHBoxLayout,
|
||
QLabel, QCheckBox, QPushButton, QMessageBox, QComboBox,
|
||
QFileDialog,
|
||
)
|
||
from PyQt5.QtGui import QFont
|
||
from PyQt5.QtCore import Qt
|
||
|
||
from src.gui.components.custom_widgets import FileSelectWidget
|
||
from src.gui.styles import ModernStylesheet
|
||
|
||
|
||
class Step10ConcentrationPanel(QWidget):
|
||
"""步骤10:浓度反演(物理模型二次反演)"""
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.init_ui()
|
||
|
||
def init_ui(self):
|
||
layout = QVBoxLayout()
|
||
|
||
title = QLabel("步骤10:浓度反演(物理模型二次反演)")
|
||
title.setFont(QFont("Arial", 12, QFont.Bold))
|
||
layout.addWidget(title)
|
||
|
||
# 输入 QAA 结果文件
|
||
self.input_file = FileSelectWidget(
|
||
"QAA 结果文件:",
|
||
"CSV Files (*.csv);;All Files (*.*)"
|
||
)
|
||
self.input_file.line_edit.setPlaceholderText(
|
||
"选择 Step 8 输出的 a_lambda_results.csv"
|
||
)
|
||
layout.addWidget(self.input_file)
|
||
|
||
# 输出路径
|
||
self.output_file = FileSelectWidget(
|
||
"输出文件:",
|
||
"CSV Files (*.csv);;All Files (*.*)",
|
||
mode="save"
|
||
)
|
||
self.output_file.line_edit.setPlaceholderText(
|
||
"自动生成到 9_Concentration,或手动指定..."
|
||
)
|
||
layout.addWidget(self.output_file)
|
||
|
||
# 选择反演指标
|
||
indicators_group = QGroupBox("选择反演指标")
|
||
indicators_layout = QFormLayout()
|
||
|
||
self.chla_check = QCheckBox("叶绿素 A (Chl-a)")
|
||
self.chla_check.setChecked(True)
|
||
self.cdom_check = QCheckBox("CDOM 吸收系数 a_dg(440)")
|
||
self.cdom_check.setChecked(True)
|
||
self.turbidity_check = QCheckBox("浊度 (Turbidity)")
|
||
self.turbidity_check.setChecked(True)
|
||
self.tn_check = QCheckBox("总氮 (TN)")
|
||
self.tn_check.setChecked(True)
|
||
self.tp_check = QCheckBox("总磷 (TP)")
|
||
self.tp_check.setChecked(True)
|
||
|
||
chk_layout = QVBoxLayout()
|
||
chk_layout.setSpacing(6)
|
||
for cb in [self.chla_check, self.cdom_check,
|
||
self.turbidity_check, self.tn_check, self.tp_check]:
|
||
chk_layout.addWidget(cb)
|
||
|
||
indicators_layout.addRow("水质参数:", chk_layout)
|
||
indicators_group.setLayout(indicators_layout)
|
||
layout.addWidget(indicators_group)
|
||
|
||
# 水体类型(用于比吸收系数自适应)
|
||
lake_group = QGroupBox("水体类型")
|
||
lake_layout = QFormLayout()
|
||
self.lake_case_combo = QComboBox()
|
||
self.lake_case_combo.addItems([
|
||
"通用 (medium)",
|
||
"oligotrophic_clear(寡营养清澈)",
|
||
"bloom_dominant(藻华主导)",
|
||
"turbid_mixed(高浊混合)",
|
||
])
|
||
self.lake_case_combo.setCurrentIndex(0)
|
||
lake_layout.addRow("水体类型:", self.lake_case_combo)
|
||
lake_group.setLayout(lake_layout)
|
||
layout.addWidget(lake_group)
|
||
|
||
# 启用步骤
|
||
self.enable_checkbox = QCheckBox("启用此步骤")
|
||
self.enable_checkbox.setChecked(False)
|
||
layout.addWidget(self.enable_checkbox)
|
||
|
||
# 独立运行按钮
|
||
self.run_btn = QPushButton("执行浓度反演")
|
||
self.run_btn.setStyleSheet(ModernStylesheet.get_button_stylesheet('success'))
|
||
self.run_btn.clicked.connect(self.run_step)
|
||
layout.addWidget(self.run_btn)
|
||
|
||
layout.addStretch()
|
||
self.setLayout(layout)
|
||
|
||
def _get_default_work_dir(self) -> str:
|
||
if hasattr(self, 'work_dir') and self.work_dir:
|
||
return str(self.work_dir)
|
||
mw = self.window()
|
||
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||
return str(mw.work_dir)
|
||
return ""
|
||
|
||
def browse_output_path(self):
|
||
current = self.output_file.get_path().strip()
|
||
if current:
|
||
initial_dir = os.path.dirname(current)
|
||
initial_file = os.path.basename(current)
|
||
else:
|
||
initial_dir = ""
|
||
initial_file = ""
|
||
|
||
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, "9_Concentration") if work_dir else ""
|
||
if initial_dir and not os.path.isdir(initial_dir):
|
||
os.makedirs(initial_dir, exist_ok=True)
|
||
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self, "保存输出文件",
|
||
os.path.join(initial_dir, initial_file) if initial_file else initial_dir,
|
||
"CSV Files (*.csv);;All Files (*.*)"
|
||
)
|
||
if file_path:
|
||
self.output_file.set_path(file_path)
|
||
|
||
def get_config(self) -> dict:
|
||
enabled_indicators = []
|
||
if self.chla_check.isChecked():
|
||
enabled_indicators.append('chla')
|
||
if self.cdom_check.isChecked():
|
||
enabled_indicators.append('cdom')
|
||
if self.turbidity_check.isChecked():
|
||
enabled_indicators.append('turbidity')
|
||
if self.tn_check.isChecked():
|
||
enabled_indicators.append('tn')
|
||
if self.tp_check.isChecked():
|
||
enabled_indicators.append('tp')
|
||
|
||
lake_case_map = {
|
||
0: "medium",
|
||
1: "oligotrophic_clear",
|
||
2: "bloom_dominant",
|
||
3: "turbid_mixed",
|
||
}
|
||
lake_case = lake_case_map.get(self.lake_case_combo.currentIndex(), "medium")
|
||
|
||
return {
|
||
'input_csv': self.input_file.get_path(),
|
||
'output_csv': self.output_file.get_path(),
|
||
'enabled_indicators': enabled_indicators,
|
||
'lake_case': lake_case,
|
||
}
|
||
|
||
def set_config(self, config: dict):
|
||
if 'input_csv' in config:
|
||
self.input_file.set_path(config['input_csv'])
|
||
if 'output_csv' in config:
|
||
self.output_file.set_path(config['output_csv'])
|
||
|
||
def update_from_config(self, work_dir=None, pipeline=None):
|
||
if work_dir:
|
||
self.work_dir = work_dir
|
||
elif hasattr(self, 'work_dir') and self.work_dir:
|
||
pass
|
||
else:
|
||
self.work_dir = None
|
||
|
||
if self.work_dir:
|
||
step8_dir = os.path.join(self.work_dir, "8_QAA_Inversion")
|
||
if os.path.isdir(step8_dir):
|
||
candidates = []
|
||
for f in sorted(os.listdir(step8_dir)):
|
||
if f.lower().endswith('.csv'):
|
||
candidates.append(os.path.join(step8_dir, f))
|
||
if candidates:
|
||
self.input_file.set_path(candidates[0])
|
||
|
||
conc_dir = os.path.join(self.work_dir, "9_Concentration")
|
||
os.makedirs(conc_dir, exist_ok=True)
|
||
output_path = os.path.join(conc_dir, "final_concentrations.csv").replace('\\', '/')
|
||
self.output_file.set_path(output_path)
|
||
|
||
def run_step(self):
|
||
input_path = self.input_file.get_path()
|
||
if not input_path:
|
||
QMessageBox.warning(self, "输入错误", "请选择 QAA 结果文件!")
|
||
return
|
||
|
||
main_window = self.window()
|
||
if hasattr(main_window, 'run_single_step'):
|
||
config = {'step9_concentration': self.get_config()}
|
||
main_window.run_single_step('step9_concentration', config)
|
||
else:
|
||
self._run_concentration_direct()
|
||
|
||
def _run_concentration_direct(self):
|
||
from src.core.algorithms.concentration_inversion import ConcentrationPipeline
|
||
|
||
input_path = self.input_file.get_path()
|
||
output_path = self.output_file.get_path()
|
||
|
||
if not output_path:
|
||
work_dir = self._get_default_work_dir()
|
||
conc_dir = os.path.join(work_dir, "9_Concentration") if work_dir else ""
|
||
if conc_dir and not os.path.isdir(conc_dir):
|
||
os.makedirs(conc_dir, exist_ok=True)
|
||
output_path = os.path.join(conc_dir, "final_concentrations.csv").replace('\\', '/')
|
||
|
||
lake_case_map = {
|
||
0: "medium",
|
||
1: "oligotrophic_clear",
|
||
2: "bloom_dominant",
|
||
3: "turbid_mixed",
|
||
}
|
||
lake_case = lake_case_map.get(self.lake_case_combo.currentIndex(), "medium")
|
||
|
||
try:
|
||
pipeline = ConcentrationPipeline(lake_case=lake_case)
|
||
result_csv = pipeline.run_pipeline(input_path, output_path)
|
||
QMessageBox.information(
|
||
self, "执行成功",
|
||
f"浓度反演完成!\n结果已保存到:\n{result_csv}"
|
||
)
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "执行错误", f"浓度反演失败:\n{str(e)}") |