初始化

This commit is contained in:
2026-04-22 12:27:49 +08:00
commit 4857cb6e45
73 changed files with 20927 additions and 0 deletions

View 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