#!/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)}")