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

353 lines
15 KiB
Python

# -*- coding: utf-8 -*-
"""
栅格处理界面: 提供栅格重分类、栅格转矢量和小面积图斑消除的界面操作
"""
import os
import traceback
import arcpy
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QFileDialog, QGridLayout,
QGroupBox, QMessageBox)
from tools.ui.components.file_list_group import FileListGroup
from tools.ui.runners.script_runner import ScriptRunner
from tools.ui.tabs.config_editor_dialog import ConfigEditorDialogVisual
class SoilPropStatsTab(QWidget):
"""栅格处理窗口部件类"""
def __init__(self, parent=None):
super(SoilPropStatsTab, self).__init__(parent)
self.main_window = parent
# 创建的导出线程
self.script_runner = ScriptRunner()
self.connect_signals()
self.init_ui()
self.setWindowTitle("土壤属性统计")
def connect_signals(self):
# 连接ScriptRunner的信号
self.script_runner.task_started.connect(self.on_script_started)
self.script_runner.task_finished.connect(self.on_script_finished)
self.script_runner.task_error.connect(self.on_script_error)
self.script_runner.task_log.connect(self.on_script_log)
self.script_runner.manager_log.connect(self.log_message)
def init_ui(self):
"""初始化用户界面"""
main_layout = QVBoxLayout(self)
# 批量处理区域
self.batch_mode_group = QGroupBox("批量处理设置")
batch_layout = QGridLayout()
# 配置文件选择 (公用)
batch_layout.addWidget(QLabel("配置文件:"), 0, 0)
self.config_file_edit = QLineEdit()
self.config_file_edit.setEnabled(False)
batch_layout.addWidget(self.config_file_edit, 0, 1,1,2)
# self.browse_config_btn = QPushButton("浏览...")
# self.browse_config_btn.clicked.connect(self.browse_config_file)
# self.browse_config_btn.setEnabled(False)
# batch_layout.addWidget(self.browse_config_btn, 0, 2)
# # 添加编辑配置文件的按钮 <--- Add this button
# self.edit_config_btn = QPushButton("编辑配置内容...")
# self.edit_config_btn.setEnabled(False)
# self.edit_config_btn.clicked.connect(self.open_config_editor)
# batch_layout.addWidget(self.edit_config_btn, 0, 3)
# 输入行政区名称
batch_layout.addWidget(QLabel("行政区名称:"), 1, 0)
self.input_xzqmc_edit = QLineEdit()
batch_layout.addWidget(self.input_xzqmc_edit, 1, 1)
# 工作空间路径 - 使用水平布局容器
label_container = QWidget()
label_layout = QHBoxLayout(label_container)
label_layout.setContentsMargins(0, 0, 0, 0) # 去掉边距
label = QLabel("")
label.setToolTip("""<font color='blue'>各属性样点<br>地类图斑<br>土壤类型图<br>母岩母质</font>""")
text_label = QLabel("数据源路径:")
label_layout.addWidget(label)
label_layout.addWidget(text_label)
batch_layout.addWidget(label_container, 2, 0) # 将整个容器放在第2行第0列
self.data_source_path_edit = QLineEdit()
batch_layout.addWidget(self.data_source_path_edit, 2, 1)
self.browse_input_workspace_btn = QPushButton("选择GDB")
self.browse_input_workspace_btn.clicked.connect(self.browse_input_workspace)
batch_layout.addWidget(self.browse_input_workspace_btn, 2, 2)
# 选择三普属性栅格文件夹
batch_layout.addWidget(QLabel("三普属性栅格:"), 3, 0)
self.input_sanpu_prop_tif_edit = QLineEdit()
batch_layout.addWidget(self.input_sanpu_prop_tif_edit, 3, 1)
self.browse_input_sanpu_prop_tif_btn = QPushButton("选择文件夹")
self.browse_input_sanpu_prop_tif_btn.clicked.connect(self.browse_input_sanpu_prop_tif)
batch_layout.addWidget(self.browse_input_sanpu_prop_tif_btn, 3, 2)
# 选择三普土壤属性重分类后的面要素
batch_layout.addWidget(QLabel("属性重分类面:"), 4, 0)
self.input_reclassed_feature_folder_edit = QLineEdit()
batch_layout.addWidget(self.input_reclassed_feature_folder_edit, 4, 1)
self.browse_input_reclassed_feature_folder_btn = QPushButton("选择文件夹")
self.browse_input_reclassed_feature_folder_btn.clicked.connect(self.browse_input_reclassed_feature_folder)
batch_layout.addWidget(self.browse_input_reclassed_feature_folder_btn, 4, 2)
# 批量输出文件夹
batch_layout.addWidget(QLabel("输出文件夹:"), 5, 0)
self.batch_output_folder_edit = QLineEdit()
batch_layout.addWidget(self.batch_output_folder_edit, 5, 1)
self.browse_batch_output_btn = QPushButton("选择文件夹")
self.browse_batch_output_btn.clicked.connect(self.browse_batch_output_folder)
batch_layout.addWidget(self.browse_batch_output_btn, 5, 2)
self.batch_mode_group.setLayout(batch_layout)
# 文件列表布局
self.file_list_group = FileListGroup(self, "选择要导出的三普属性样点:")
self.file_list_group.load_files.connect(self.on_load_polygon)
# 操作按钮
btn_layout = QHBoxLayout()
# 导出酸化统计表
self.generate_sh_stat_btn = QPushButton("生成土壤属性统计表")
self.generate_sh_stat_btn.clicked.connect(self.on_generate_area_stat)
self.cancel_btn = QPushButton("取消")
# self.cancel_btn.clicked.connect(self.close)
btn_layout.addWidget(self.generate_sh_stat_btn)
btn_layout.addWidget(self.cancel_btn)
# 添加所有组件到主布局
main_layout.addWidget(self.batch_mode_group)
main_layout.addWidget(self.file_list_group)
main_layout.addLayout(btn_layout)
self.setLayout(main_layout)
def browse_config_file(self):
"""浏览选择配置文件"""
file_path, _ = QFileDialog.getOpenFileName(
self, "选择配置文件", "",
"JSON 文件 (*.json);;所有文件 (*)"
)
if file_path:
self.config_file_edit.setText(file_path)
# self.validate_config_file(file_path)
# self.edit_config_btn.setEnabled(True)
def on_load_polygon(self):
"""加载图层列表"""
try:
data_source_paths = self.data_source_path_edit.text()
# 清空列表
self.file_list_group.file_list.clear()
# 添加图层
if arcpy.Exists(data_source_paths):
arcpy.env.workspace = data_source_paths
for polygon in arcpy.ListFeatureClasses(feature_type="Point"):
self.file_list_group.file_list.addItem(polygon)
self.log_message(f"已加载 {self.file_list_group.file_list.count()} 个图层")
except Exception as e:
self.log_message(f"加载图层列表失败: {str(e)}")
traceback.print_exc()
def browse_input_workspace(self):
"""浏览选择输入GDB"""
folder_path = QFileDialog.getExistingDirectory(self, "选择GDB数据库")
if folder_path:
self.data_source_path_edit.setText(folder_path)
def browse_input_sanpu_prop_tif(self):
"""浏览选择输入GDB"""
folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹啊")
if folder_path:
self.input_sanpu_prop_tif_edit.setText(folder_path)
def browse_input_reclassed_feature_folder(self):
"""浏览选择输入GDB"""
folder_path = QFileDialog.getExistingDirectory(self, "选择GDB数据库")
if folder_path:
self.input_reclassed_feature_folder_edit.setText(folder_path)
def browse_batch_output_folder(self):
"""浏览选择表格输出文件夹"""
folder_path = QFileDialog.getExistingDirectory(self, "选择表格输出文件夹")
if folder_path:
self.batch_output_folder_edit.setText(folder_path)
def validate_inputs(self):
"""验证输入参数"""
# 验证行政区名称
if not self.input_xzqmc_edit.text():
QMessageBox.warning(self, "输入错误", "请输入行政区名称")
return False
# 验证工作空间路径
if not self.data_source_path_edit.text() or not arcpy.Exists(self.data_source_path_edit.text()):
QMessageBox.warning(self, "输入错误", "请选择有效的工作空间")
return False
if not self.batch_output_folder_edit.text() or not arcpy.Exists(self.batch_output_folder_edit.text()):
QMessageBox.warning(self, "输入错误", "请选择批量处理输出文件夹")
return False
if not self.input_sanpu_prop_tif_edit.text() or not arcpy.Exists(self.input_sanpu_prop_tif_edit.text()):
QMessageBox.warning(self, "输入错误", "请选择有效的三普属性栅格文件夹")
return False
# 验证选择的要素类是否有对应的属性栅格
missing_items = []
for item in self.file_list_group.file_list.selectedItems():
if not arcpy.Exists(os.path.join(self.input_sanpu_prop_tif_edit.text(), f"{item.text()}.tif")):
missing_items.append(item.text())
if missing_items:
QMessageBox.warning(self, "输入错误", f"选择的样点中无相应的栅格文件: {missing_items}")
return False
# 验证数据源中是否存在 地类图斑、土壤类型图斑、母岩母质图斑 等要素
if not arcpy.Exists(os.path.join(self.data_source_path_edit.text(), "地类图斑")):
QMessageBox.warning(self, "输入错误", "工作空间中不存在 地类图斑.shp")
return False
if not arcpy.Exists(os.path.join(self.data_source_path_edit.text(), "土壤类型图")):
QMessageBox.warning(self, "输入错误", "工作空间中不存在土壤类型图.shp")
return False
# if not arcpy.Exists(os.path.join(self.data_source_path_edit.text(), "母岩母质图斑")):
# QMessageBox.warning(self, "输入错误", "工作空间中不存在母岩母质图斑.shp")
# return False
# 验证文件列表中是否已选中项目
if not self.file_list_group.file_list.selectedItems():
QMessageBox.warning(self, "输入错误", "请至少选择一个属性")
return False
return True
def open_config_editor(self):
"""打开配置文件编辑器对话框"""
config_path = self.config_file_edit.text()
if not config_path:
reply = QMessageBox.question(self, "编辑配置", "没有选择配置文件,是否创建一个新配置文件?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.No:
return
config_path = ""
# 使用配置编辑对话框
dialog = ConfigEditorDialogVisual(config_path, self)
if dialog.exec() == ConfigEditorDialogVisual.DialogCode.Accepted:
new_path = dialog.get_new_config_path()
if new_path and new_path != self.config_file_edit.text():
self.config_file_edit.setText(new_path)
# self.validate_config_file(new_path)
# 执行生成酸化统计表
def on_generate_area_stat(self):
"""执行生成面积统计表"""
if not self.validate_inputs():
return
try:
if self.main_window and hasattr(self.main_window, 'save_settings'):
self.main_window.update_settings()
self.main_window.save_settings()
settings_file = self.main_window.settings_file
params = {
"settings_path": settings_file,
}
task_id = self.script_runner.run_soil_prop_stat(params)
if task_id:
self.log_message(f"[GUI] 属性表格生成任务 {task_id} 已添加到列队")
except Exception as e:
error_msg = f"处理过程中出错: {str(e)}"
QMessageBox.critical(self, "处理错误", error_msg)
self.log_message(error_msg)
def get_soil_prop_stat_settings(self):
"""保存当前配置到JSON文件"""
config = {
"xzqmc": self.input_xzqmc_edit.text(),
"config_file": self.config_file_edit.text(),
"data_source_path": self.data_source_path_edit.text(),
"sanpu_prop_tif_folder": self.input_sanpu_prop_tif_edit.text(),
"reclassed_feature_folder": self.input_reclassed_feature_folder_edit.text(),
"output_folder": self.batch_output_folder_edit.text(),
"sample_list": [item.text() for item in self.file_list_group.file_list.selectedItems()]
}
return config
def load_settings(self, settings):
"""从字典加载配置"""
try:
# 批处理参数
self.data_source_path_edit.setText(settings.get("data_source_path", ""))
self.input_sanpu_prop_tif_edit.setText(settings.get("sanpu_prop_tif_folder", ""))
self.batch_output_folder_edit.setText(settings.get("output_folder", ""))
self.input_reclassed_feature_folder_edit.setText(settings.get("reclassed_feature_folder", ""))
return True
except Exception as e:
QMessageBox.critical(self, "加载配置错误", f"加载配置时出错: {str(e)}")
return False
def set_buttons_enabled(self, enabled):
"""设置按钮是否可用"""
self.generate_sh_stat_btn.setEnabled(enabled)
def update_config_file(self, config_file_path):
"""更新配置文件路径"""
self.config_file_edit.setText(config_file_path)
def on_script_started(self, task_id, task_description):
"""脚本开始执行"""
self.set_buttons_enabled(False)
self.log_message(f"{task_id}: 正在运行 - {task_description}")
def on_script_finished(self, task_id:str, success:bool, message:str):
"""脚本执行完成"""
self.set_buttons_enabled(True)
self.log_message("脚本执行完成")
def on_script_error(self,task_id, error_msg):
"""脚本执行出错"""
self.set_buttons_enabled(True)
self.log_message(f"错误:{task_id}-{error_msg}")
# QMessageBox.critical(self, "错误", error_msg)
def on_script_log(self, task_id, message):
"""脚本输出日志"""
self.log_message(f"{task_id}: {message}")
def log_message(self, message):
"""日志输出"""
if self.main_window and hasattr(self.main_window, 'log_signal'):
self.main_window.log_signal.emit(message)
else:
print(message)
if __name__ == "__main__":
from PyQt6.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
window = SoilPropStatsTab()
window.show()
sys.exit(app.exec())