# -*- 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("""各属性样点
地类图斑
土壤类型图
母岩母质
""") 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())