界面优化
This commit is contained in:
282
src/gui/panels/step1_panel.py
Normal file
282
src/gui/panels/step1_panel.py
Normal file
@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Step1 面板 - 水域掩膜生成
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QLabel,
|
||||
QDoubleSpinBox, QCheckBox, QPushButton, QFormLayout, QRadioButton,
|
||||
QMessageBox,
|
||||
)
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 从公共组件库导入
|
||||
from src.gui.components.custom_widgets import FileSelectWidget
|
||||
from src.gui.styles import ModernStylesheet
|
||||
|
||||
|
||||
class Step1Panel(QWidget):
|
||||
"""1. 水域掩膜生成"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# 标题
|
||||
|
||||
|
||||
# 掩膜生成方式选择
|
||||
method_group = QGroupBox("掩膜生成方式")
|
||||
method_layout = QVBoxLayout()
|
||||
|
||||
# 使用现有掩膜文件
|
||||
self.use_existing_radio = QRadioButton("使用现有掩膜文件")
|
||||
self.use_existing_radio.setChecked(True)
|
||||
method_layout.addWidget(self.use_existing_radio)
|
||||
|
||||
# 使用NDWI自动生成
|
||||
self.use_ndwi_radio = QRadioButton("使用NDWI自动生成")
|
||||
method_layout.addWidget(self.use_ndwi_radio)
|
||||
|
||||
# 应用QRadioButton样式(实心选中点)
|
||||
radio_style = """
|
||||
QRadioButton::indicator {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #999;
|
||||
}
|
||||
QRadioButton::indicator:checked {
|
||||
background-color: #0078D7;
|
||||
border: 2px solid #0078D7;
|
||||
}
|
||||
QRadioButton::indicator:unchecked {
|
||||
background-color: white;
|
||||
border: 2px solid #999;
|
||||
}
|
||||
QRadioButton::indicator:hover {
|
||||
border: 2px solid #0078D7;
|
||||
}
|
||||
"""
|
||||
self.use_existing_radio.setStyleSheet(radio_style)
|
||||
self.use_ndwi_radio.setStyleSheet(radio_style)
|
||||
|
||||
method_group.setLayout(method_layout)
|
||||
layout.addWidget(method_group)
|
||||
|
||||
# 掩膜文件选择
|
||||
self.mask_file = FileSelectWidget(
|
||||
"掩膜文件:",
|
||||
"Shapefiles (*.shp);;Raster Files (*.dat *.tif);;All Files (*.*)"
|
||||
)
|
||||
layout.addWidget(self.mask_file)
|
||||
|
||||
# 影像文件选择(用于shp栅格化或NDWI生成)
|
||||
self.img_file = FileSelectWidget(
|
||||
"参考影像:",
|
||||
"Image Files (*.bsq *.dat *.tif);;All Files (*.*)"
|
||||
)
|
||||
layout.addWidget(self.img_file)
|
||||
|
||||
# NDWI参数设置
|
||||
self.ndwi_group = QGroupBox("NDWI参数设置")
|
||||
ndwi_layout = QVBoxLayout()
|
||||
|
||||
# NDWI阈值
|
||||
threshold_layout = QHBoxLayout()
|
||||
threshold_layout.addWidget(QLabel("NDWI阈值:"))
|
||||
self.ndwi_threshold = QDoubleSpinBox()
|
||||
self.ndwi_threshold.setRange(0.0, 1.0)
|
||||
self.ndwi_threshold.setSingleStep(0.05)
|
||||
self.ndwi_threshold.setValue(0.4)
|
||||
self.ndwi_threshold.setDecimals(2)
|
||||
threshold_layout.addWidget(self.ndwi_threshold)
|
||||
threshold_layout.addStretch()
|
||||
ndwi_layout.addLayout(threshold_layout)
|
||||
|
||||
self.ndwi_group.setLayout(ndwi_layout)
|
||||
layout.addWidget(self.ndwi_group)
|
||||
|
||||
# 输出文件路径(使用save模式)
|
||||
self.output_file = FileSelectWidget(
|
||||
"输出掩膜:",
|
||||
"Mask Files (*.dat *.tif);;All Files (*.*)",
|
||||
mode="save"
|
||||
)
|
||||
self.output_file.line_edit.setPlaceholderText("water_mask.dat")
|
||||
layout.addWidget(self.output_file)
|
||||
|
||||
# 提示信息 - 专业的 Info Alert 样式
|
||||
hint = QLabel("💡 提示: 如果掩膜文件是Shapefile(.shp),需要提供参考影像用于栅格化;如果使用NDWI自动生成,只需要提供参考影像")
|
||||
hint.setWordWrap(True) # 允许自动换行
|
||||
hint.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #0055D4;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
background-color: #E8F4FF;
|
||||
border: 2px solid #0055D4;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin: 8px 0px;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(hint)
|
||||
|
||||
# 启用步骤
|
||||
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)
|
||||
|
||||
# 连接信号
|
||||
self.use_existing_radio.toggled.connect(self.update_ui_state)
|
||||
self.use_ndwi_radio.toggled.connect(self.update_ui_state)
|
||||
|
||||
layout.addStretch()
|
||||
self.setLayout(layout)
|
||||
|
||||
# 初始UI状态
|
||||
self.update_ui_state()
|
||||
|
||||
def update_ui_state(self):
|
||||
"""根据选择的掩膜生成方式更新UI状态(使用显示/隐藏控制)"""
|
||||
use_ndwi = self.use_ndwi_radio.isChecked()
|
||||
|
||||
# 动态显示/隐藏组件
|
||||
if use_ndwi:
|
||||
# 使用NDWI模式:隐藏掩膜文件,显示NDWI参数和输出掩膜
|
||||
self.mask_file.setVisible(False)
|
||||
self.ndwi_group.setVisible(True)
|
||||
self.output_file.setVisible(True) # 显示输出掩膜路径
|
||||
|
||||
# 当切换到NDWI模式时,如果工作目录已设置,自动填充输出路径
|
||||
if hasattr(self, 'work_dir') and self.work_dir:
|
||||
self._auto_fill_output_path()
|
||||
else:
|
||||
# 使用现有掩膜模式:显示掩膜文件,隐藏NDWI参数和输出掩膜
|
||||
self.mask_file.setVisible(True)
|
||||
self.ndwi_group.setVisible(False)
|
||||
self.output_file.setVisible(False) # 隐藏输出掩膜路径
|
||||
|
||||
# 参考影像在两种模式下都显示
|
||||
self.img_file.setVisible(True)
|
||||
|
||||
def update_work_directory(self, work_dir):
|
||||
"""
|
||||
保存工作目录引用,用于后续自动填充路径
|
||||
|
||||
Args:
|
||||
work_dir: 工作目录路径
|
||||
"""
|
||||
if not work_dir:
|
||||
return
|
||||
|
||||
# 保存工作目录引用
|
||||
self.work_dir = work_dir
|
||||
|
||||
# 如果当前选中的是NDWI模式,立即填充输出路径
|
||||
if self.use_ndwi_radio.isChecked():
|
||||
self._auto_fill_output_path()
|
||||
|
||||
def _auto_fill_output_path(self):
|
||||
"""
|
||||
自动填充输出掩膜路径(仅在NDWI模式下)
|
||||
确保路径使用正斜杠,避免斜杠混用
|
||||
"""
|
||||
if not hasattr(self, 'work_dir') or not self.work_dir:
|
||||
return
|
||||
|
||||
# 生成输出掩膜的完整路径
|
||||
output_dir = os.path.join(self.work_dir, "1_water_mask")
|
||||
os.makedirs(output_dir, exist_ok=True) # 确保目录存在
|
||||
|
||||
# 统一使用正斜杠,避免 \ 和 / 混用
|
||||
default_output_path = os.path.join(output_dir, "water_mask_out.dat").replace('\\', '/')
|
||||
self.output_file.set_path(default_output_path)
|
||||
|
||||
def get_config(self):
|
||||
"""获取配置"""
|
||||
use_ndwi = self.use_ndwi_radio.isChecked()
|
||||
|
||||
config = {
|
||||
'mask_path': None if use_ndwi else self.mask_file.get_path(),
|
||||
'use_ndwi': use_ndwi,
|
||||
'ndwi_threshold': self.ndwi_threshold.value()
|
||||
}
|
||||
|
||||
# 参考影像路径(两种模式都可能需要)
|
||||
img_path = self.img_file.get_path()
|
||||
if img_path:
|
||||
config['img_path'] = img_path
|
||||
|
||||
# 输出路径:仅在NDWI模式下有效
|
||||
if use_ndwi:
|
||||
output_path = self.output_file.get_path()
|
||||
if output_path:
|
||||
config['output_path'] = output_path
|
||||
else:
|
||||
# 使用现有掩膜时,不传递output_path,避免底层错误尝试保存文件
|
||||
config['output_path'] = None
|
||||
|
||||
return config
|
||||
|
||||
def set_config(self, config):
|
||||
"""设置配置"""
|
||||
if 'mask_path' in config:
|
||||
self.mask_file.set_path(config['mask_path'])
|
||||
if 'img_path' in config:
|
||||
self.img_file.set_path(config['img_path'])
|
||||
if 'output_path' in config:
|
||||
self.output_file.set_path(config['output_path'])
|
||||
if 'use_ndwi' in config:
|
||||
if config['use_ndwi']:
|
||||
self.use_ndwi_radio.setChecked(True)
|
||||
else:
|
||||
self.use_existing_radio.setChecked(True)
|
||||
if 'ndwi_threshold' in config:
|
||||
self.ndwi_threshold.setValue(config['ndwi_threshold'])
|
||||
|
||||
self.update_ui_state()
|
||||
|
||||
def run_step(self):
|
||||
"""独立运行步骤1"""
|
||||
# 验证输入
|
||||
if self.use_ndwi_radio.isChecked():
|
||||
# NDWI模式:需要影像文件
|
||||
img_path = self.img_file.get_path()
|
||||
if not img_path:
|
||||
QMessageBox.warning(self, "输入错误", "请选择参考影像文件!")
|
||||
return
|
||||
else:
|
||||
# 现有掩膜模式:需要掩膜文件
|
||||
mask_path = self.mask_file.get_path()
|
||||
if not mask_path:
|
||||
QMessageBox.warning(self, "输入错误", "请选择掩膜文件!")
|
||||
return
|
||||
|
||||
# 如果是shp文件,还需要影像文件
|
||||
if mask_path.lower().endswith('.shp'):
|
||||
img_path = self.img_file.get_path()
|
||||
if not img_path:
|
||||
QMessageBox.warning(self, "输入错误", "当使用shp文件时,需要提供参考影像用于栅格化!")
|
||||
return
|
||||
|
||||
# 获取父窗口并运行步骤
|
||||
parent = self.parent()
|
||||
while parent and not hasattr(parent, 'run_single_step'):
|
||||
parent = parent.parent()
|
||||
|
||||
if parent and hasattr(parent, 'run_single_step'):
|
||||
config = {'step1': self.get_config()}
|
||||
parent.run_single_step("step1", config)
|
||||
Reference in New Issue
Block a user