初始化
This commit is contained in:
244
tools/ui/tabs/raster_processing_common.py
Normal file
244
tools/ui/tabs/raster_processing_common.py
Normal file
@@ -0,0 +1,244 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
栅格处理共享组件: 提供栅格重分类、栅格转矢量和小面积图斑消除的共享UI组件
|
||||
"""
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QCheckBox,
|
||||
QGroupBox, QTableWidget, QTableWidgetItem, QHeaderView,
|
||||
QPushButton, QSpinBox, QDoubleSpinBox, QComboBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QDoubleValidator
|
||||
|
||||
class ReclassificationWidget(QWidget):
|
||||
"""重分类参数组件"""
|
||||
|
||||
paramsChanged = pyqtSignal() # 参数变化信号
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(ReclassificationWidget, self).__init__(parent)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
group_box = QGroupBox("重分类映射表")
|
||||
group_layout = QVBoxLayout(group_box)
|
||||
|
||||
self.reclass_table = QTableWidget()
|
||||
self.reclass_table.setColumnCount(3)
|
||||
self.reclass_table.setHorizontalHeaderLabels(["从", "到", "新值"])
|
||||
# PyQt6 中,horizontalHeader().setSectionResizeMode 接受 QHeaderView.ResizeMode 枚举
|
||||
self.reclass_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) # type: ignore
|
||||
# PyQt6 中,setSelectionBehavior 接受 QAbstractItemView.SelectionBehavior 枚举
|
||||
from PyQt6.QtWidgets import QAbstractItemView # 需要导入 QAbstractItemView
|
||||
self.reclass_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) # 整行选择
|
||||
|
||||
# 连接单元格变化信号
|
||||
self.reclass_table.itemChanged.connect(self.on_item_changed)
|
||||
|
||||
# 添加默认行
|
||||
self.add_row()
|
||||
|
||||
group_layout.addWidget(self.reclass_table)
|
||||
|
||||
# 添加/删除行按钮
|
||||
btn_layout = QHBoxLayout()
|
||||
add_row_btn = QPushButton("添加行")
|
||||
del_row_btn = QPushButton("删除行")
|
||||
|
||||
# PyQt6 信号槽连接
|
||||
add_row_btn.clicked.connect(self.add_row)
|
||||
del_row_btn.clicked.connect(self.del_row)
|
||||
|
||||
btn_layout.addWidget(add_row_btn)
|
||||
btn_layout.addWidget(del_row_btn)
|
||||
|
||||
group_layout.addLayout(btn_layout)
|
||||
layout.addWidget(group_box)
|
||||
|
||||
def add_row(self, from_value=None, to_value=None, new_value=None):
|
||||
"""添加一行重分类规则"""
|
||||
row_count = self.reclass_table.rowCount()
|
||||
self.reclass_table.insertRow(row_count)
|
||||
|
||||
# 设置默认值或传入的值
|
||||
from_item = QTableWidgetItem(str(from_value) if from_value is not None else "")
|
||||
to_item = QTableWidgetItem(str(to_value) if to_value is not None else "")
|
||||
new_value_item = QTableWidgetItem(str(new_value) if new_value is not None else "")
|
||||
|
||||
# 设置数值验证器 (QTableWidgetItem 本身没有 setValidator 方法)
|
||||
# 如果需要验证,可以考虑使用代理 (Delegate)
|
||||
|
||||
self.reclass_table.setItem(row_count, 0, from_item)
|
||||
self.reclass_table.setItem(row_count, 1, to_item)
|
||||
self.reclass_table.setItem(row_count, 2, new_value_item)
|
||||
|
||||
def del_row(self):
|
||||
"""删除选中的行"""
|
||||
selected_rows = self.reclass_table.selectedIndexes()
|
||||
if not selected_rows:
|
||||
return
|
||||
|
||||
# 从最后一个开始删除,以避免索引变化问题
|
||||
rows_to_delete = sorted(list(set([index.row() for index in selected_rows])), reverse=True)
|
||||
for row in rows_to_delete:
|
||||
self.reclass_table.removeRow(row)
|
||||
|
||||
# 确保至少有一行
|
||||
if self.reclass_table.rowCount() == 0:
|
||||
self.add_row()
|
||||
|
||||
self.paramsChanged.emit() # 参数变化
|
||||
|
||||
def on_item_changed(self, item):
|
||||
"""当表格单元格内容变化时触发"""
|
||||
# 在这里可以添加一些简单的验证,例如检查是否为数字
|
||||
try:
|
||||
text = item.text()
|
||||
if text:
|
||||
if item.column() in [0, 1]: # "从" 和 "到" 列
|
||||
float(text) # 尝试转换为浮点数
|
||||
elif item.column() == 2: # "新值" 列
|
||||
int(text) # 尝试转换为整数
|
||||
except ValueError:
|
||||
# 如果转换失败,可以给用户提示或清空单元格
|
||||
# item.setText("") # 或者其他处理方式
|
||||
pass # 暂时不做处理,依赖后期的validate_inputs
|
||||
|
||||
self.paramsChanged.emit() # 参数变化
|
||||
|
||||
def get_remap_table(self):
|
||||
"""从表格获取重分类映射表"""
|
||||
remap_table = []
|
||||
for row in range(self.reclass_table.rowCount()):
|
||||
try:
|
||||
from_item = self.reclass_table.item(row, 0)
|
||||
to_item = self.reclass_table.item(row, 1)
|
||||
new_value_item = self.reclass_table.item(row, 2)
|
||||
|
||||
# 检查是否为空或无效
|
||||
if not from_item or not from_item.text() or \
|
||||
not to_item or not to_item.text() or \
|
||||
not new_value_item or not new_value_item.text():
|
||||
continue # 跳过空行
|
||||
|
||||
from_value_str = from_item.text()
|
||||
to_value_str = to_item.text()
|
||||
new_value_str = new_value_item.text()
|
||||
|
||||
# 处理特殊值,例如 "inf" 表示无穷大
|
||||
from_value = float(from_value_str) if from_value_str.lower() not in ('inf', '-inf') else (float('-inf') if from_value_str.lower() == '-inf' else float('inf'))
|
||||
to_value = float(to_value_str) if to_value_str.lower() not in ('inf', '-inf') else (float('-inf') if to_value_str.lower() == '-inf' else float('inf'))
|
||||
new_value = int(new_value_str)
|
||||
|
||||
# 验证范围的有效性
|
||||
if from_value > to_value:
|
||||
print(f"警告: 无效范围 {from_value} > {to_value},跳过此行")
|
||||
continue # 跳过无效范围的行
|
||||
|
||||
remap_table.append([from_value, to_value, new_value])
|
||||
|
||||
except (ValueError, TypeError) as e:
|
||||
print(f"警告: 读取重分类表格时遇到无效数值或类型,跳过此行. 错误: {e}")
|
||||
continue
|
||||
|
||||
return remap_table
|
||||
|
||||
def set_table_data(self, data):
|
||||
"""设置重分类表格数据"""
|
||||
self.reclass_table.clearContents()
|
||||
self.reclass_table.setRowCount(0)
|
||||
|
||||
if not data:
|
||||
self.add_row() # 添加一行默认行
|
||||
return
|
||||
|
||||
for row_data in data:
|
||||
if len(row_data) == 3:
|
||||
from_value, to_value, new_value = row_data
|
||||
self.add_row(from_value, to_value, new_value)
|
||||
|
||||
class VectorParamsWidget(QWidget):
|
||||
"""矢量化参数组件"""
|
||||
|
||||
paramsChanged = pyqtSignal() # 参数变化信号
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(VectorParamsWidget, self).__init__(parent)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
layout = QHBoxLayout(self)
|
||||
|
||||
# 矢量化参数
|
||||
# 简化多边形边界
|
||||
simplify_layout = QHBoxLayout()
|
||||
simplify_layout.addWidget(QLabel("简化多边形边界:"))
|
||||
self.simplify_check = QCheckBox()
|
||||
self.simplify_check.setChecked(False) # 默认不选中
|
||||
# PyQt6 信号槽连接
|
||||
self.simplify_check.stateChanged.connect(self.on_simplify_changed)
|
||||
simplify_layout.addWidget(self.simplify_check)
|
||||
simplify_layout.addStretch()
|
||||
layout.addLayout(simplify_layout)
|
||||
|
||||
# 最小面积阈值
|
||||
min_area_layout = QHBoxLayout()
|
||||
min_area_layout.addWidget(QLabel("最小面积:"))
|
||||
self.min_area_spin = QDoubleSpinBox()
|
||||
self.min_area_spin.setRange(0.1, 1000000)
|
||||
self.min_area_spin.setValue(100)
|
||||
self.min_area_spin.setSingleStep(10)
|
||||
# PyQt6 信号槽连接
|
||||
self.min_area_spin.valueChanged.connect(lambda: self.paramsChanged.emit())
|
||||
min_area_layout.addWidget(self.min_area_spin)
|
||||
|
||||
# 面积单位
|
||||
self.area_unit_combo = QComboBox()
|
||||
self.area_unit_combo.addItems(["平方米", "公顷", "平方公里"])
|
||||
self.area_unit_map = {
|
||||
"平方米": "SQUARE_METERS",
|
||||
"公顷": "HECTARES",
|
||||
"平方公里": "SQUARE_KILOMETERS"
|
||||
}
|
||||
# PyQt6 信号槽连接
|
||||
self.area_unit_combo.currentIndexChanged.connect(lambda: self.paramsChanged.emit())
|
||||
min_area_layout.addWidget(self.area_unit_combo)
|
||||
|
||||
layout.addLayout(min_area_layout)
|
||||
|
||||
def on_simplify_changed(self, state):
|
||||
"""当简化选项状态变化时触发"""
|
||||
# PyQt6 中 Qt.Checked 仍然可用
|
||||
is_checked = state == Qt.CheckState.Checked
|
||||
self.paramsChanged.emit()
|
||||
|
||||
def get_params(self):
|
||||
"""获取矢量化参数"""
|
||||
is_simplify = self.simplify_check.isChecked()
|
||||
return {
|
||||
"simplify": is_simplify,
|
||||
"min_area": self.min_area_spin.value(),
|
||||
"area_unit": self.area_unit_map[self.area_unit_combo.currentText()]
|
||||
}
|
||||
|
||||
def set_params(self, params):
|
||||
"""设置矢量化参数"""
|
||||
if "simplify" in params:
|
||||
simplify_value = params["simplify"]
|
||||
self.simplify_check.setChecked(simplify_value)
|
||||
|
||||
if "min_area" in params:
|
||||
self.min_area_spin.setValue(params["min_area"])
|
||||
|
||||
if "area_unit" in params:
|
||||
for text, value in self.area_unit_map.items():
|
||||
if value == params["area_unit"]:
|
||||
index = self.area_unit_combo.findText(text)
|
||||
if index >= 0:
|
||||
self.area_unit_combo.setCurrentIndex(index)
|
||||
break
|
||||
Reference in New Issue
Block a user