Files
ArcGis_Py/tools/ui/tabs/raster_processing_common.py
2026-04-22 12:27:49 +08:00

244 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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