#!/usr/bin/env python # -*- coding: utf-8 -*- """ Step2 面板 - 耀斑区域识别 """ import os from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QGroupBox, QFormLayout, QDoubleSpinBox, QSpinBox, QComboBox, QCheckBox, QPushButton, QMessageBox, ) from PyQt5.QtCore import Qt # 从公共组件库导入 from src.gui.components.custom_widgets import FileSelectWidget from src.gui.styles import ModernStylesheet class Step2Panel(QWidget): """2. 耀斑区域识别""" def __init__(self, parent=None): super().__init__(parent) self.work_dir = None self.init_ui() def init_ui(self): layout = QVBoxLayout() # 标题 # 影像文件 self.img_file = FileSelectWidget( "影像文件:", "Image Files (*.bsq *.dat *.tif);;All Files (*.*)" ) layout.addWidget(self.img_file) # 水域掩膜文件(可选,用于独立运行) self.water_mask_file = FileSelectWidget( "水域掩膜:", "Mask Files (*.dat *.tif);;All Files (*.*)" ) self.water_mask_file.label.setText("水域掩膜:") layout.addWidget(self.water_mask_file) # 参数设置 params_group = QGroupBox("检测参数") params_layout = QFormLayout() # 耀斑波长 self.glint_wave = QDoubleSpinBox() self.glint_wave.setRange(300, 1000) self.glint_wave.setValue(750.0) self.glint_wave.setSuffix(" nm") params_layout.addRow("耀斑检测波长:", self.glint_wave) # 检测方法 self.method = QComboBox() self.method.addItem("Otsu 阈值法", "otsu") self.method.addItem("Z-Score 方法", "zscore") self.method.addItem("百分位数法", "percentile") self.method.addItem("IQR 四分位距法", "iqr") self.method.addItem("自适应阈值法", "adaptive") self.method.addItem("多波段综合法", "multi_band") params_layout.addRow("检测方法:", self.method) # 最大连通域面积 self.max_area = QSpinBox() self.max_area.setRange(0, 100000) self.max_area.setValue(50) self.max_area.setSpecialValueText("不过滤") params_layout.addRow("最大连通域面积:", self.max_area) # 岸边缓冲区 self.buffer_size = QSpinBox() self.buffer_size.setRange(0, 200) self.buffer_size.setValue(10) self.buffer_size.setSpecialValueText("不设置") params_layout.addRow("岸边缓冲区大小:", self.buffer_size) params_group.setLayout(params_layout) layout.addWidget(params_group) # 输出文件路径 self.output_file = FileSelectWidget( "输出耀斑掩膜:", "Mask Files (*.dat *.tif);;All Files (*.*)" ) self.output_file.line_edit.setPlaceholderText("") layout.addWidget(self.output_file) # 启用步骤 self.enable_checkbox = QCheckBox("启用此步骤") self.enable_checkbox.setChecked(True) 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_config(self): """获取配置""" config = { 'img_path': self.img_file.get_path(), 'glint_wave': self.glint_wave.value(), 'method': self.method.currentData(), # 使用 currentData() 获取英文ID } if self.max_area.value() > 0: config['max_area'] = self.max_area.value() if self.buffer_size.value() > 0: config['buffer_size'] = self.buffer_size.value() # 添加水域掩膜路径(用于独立运行) water_mask_path = self.water_mask_file.get_path() if water_mask_path: config['water_mask_path'] = water_mask_path # 添加输出路径 output_path = self.output_file.get_path() if output_path: config['output_path'] = output_path return config def set_config(self, config): """设置配置""" if 'img_path' in config: self.img_file.set_path(config['img_path']) if 'glint_wave' in config: self.glint_wave.setValue(config['glint_wave']) if 'method' in config: idx = self.method.findData(config['method']) # 使用 findData() if idx >= 0: self.method.setCurrentIndex(idx) if 'max_area' in config: self.max_area.setValue(config['max_area']) if 'buffer_size' in config: self.buffer_size.setValue(config['buffer_size']) if 'water_mask_path' in config: self.water_mask_file.set_path(config['water_mask_path']) if 'output_path' in config: self.output_file.set_path(config['output_path']) def update_from_config(self, work_dir=None, pipeline=None): """ 从全局配置/Pipeline 或 Step1Panel 自动填充路径,实现上下游数据流转 Args: work_dir: 工作目录路径 pipeline: Pipeline 实例,用于获取步骤1生成的水域掩膜路径 """ # 保存工作目录引用 if work_dir: self.work_dir = work_dir elif hasattr(self, 'work_dir') and self.work_dir: pass # 保持现有工作目录 else: self.work_dir = None # 1. 尝试从 Pipeline 获取 mask_path = None if pipeline and hasattr(pipeline, 'water_mask_path') and pipeline.water_mask_path: mask_path = pipeline.water_mask_path # 2. 如果 Pipeline 中没有,则尝试直接从 Step1 界面读取(关键修复) main_window = self.window() if not mask_path and hasattr(main_window, 'step1_panel'): if main_window.step1_panel.use_ndwi_radio.isChecked(): # NDWI模式,读取输出框的路径 mask_path = main_window.step1_panel.output_file.get_path() else: # 导入现有模式,读取输入框的路径 mask_path = main_window.step1_panel.mask_file.get_path() # 填充获取到的路径 if mask_path: # 若为相对路径,使用 work_dir 合成为绝对路径 if not os.path.isabs(mask_path): mask_path = os.path.join(self.work_dir or '', mask_path).replace('\\', '/') self.water_mask_file.set_path(mask_path) # 3. 自动填充输出路径(基于工作目录) if self.work_dir: # 生成输出耀斑掩膜的标准路径:workspace/2_glint_mask/glint_mask_out.dat output_dir = os.path.join(self.work_dir, "2_glint_mask") os.makedirs(output_dir, exist_ok=True) default_output_path = os.path.join(output_dir, "glint_mask_out.dat").replace('\\', '/') self.output_file.set_path(default_output_path) else: # 没有工作目录时,清空输出路径 self.output_file.set_path("") def run_step(self): """独立运行步骤2""" # 验证输入 img_path = self.img_file.get_path() if not img_path: QMessageBox.warning(self, "输入错误", "请选择影像文件!") return # 获取主窗口并运行步骤 main_window = self.window() if hasattr(main_window, 'run_single_step'): config = {'step2': self.get_config()} main_window.run_single_step('step2', config)