import json import os from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit, QFileDialog, QMessageBox, QComboBox, QSpinBox, QGroupBox, QFormLayout, QTableWidget, QHeaderView, QSizePolicy, QTableWidgetItem, QCheckBox) import arcpy from ..runners.script_runner import ScriptRunner class TestTab(QWidget): """导出成果图标签页""" def __init__(self, parent=None): super().__init__(parent) self.main_window = parent self.script_runner = ScriptRunner(self) self.connect_signals() self.init_ui() self.load_settings() def connect_signals(self): """连接脚本运行器信号""" # 连接ScriptRunner的信号 self.script_runner.started.connect(self.on_script_started) self.script_runner.finished.connect(self.on_script_finished) self.script_runner.error.connect(self.on_script_error) self.script_runner.log.connect(self.log_message) def init_ui(self): layout = QVBoxLayout(self) # 配置文件设置组 config_group = QGroupBox("配置设置") config_layout = QVBoxLayout(config_group) # 配置文件选择 config_file_layout = QHBoxLayout() config_file_layout.addWidget(QLabel("配置文件:")) self.config_file_path = QLineEdit() config_file_layout.addWidget(self.config_file_path) config_file_btn = QPushButton("浏览...") config_file_btn.clicked.connect(self.browse_config_file) config_file_layout.addWidget(config_file_btn) config_layout.addLayout(config_file_layout) # 区县名字设置 county_name_layout = QHBoxLayout() county_name_layout.addWidget(QLabel("区县名称:")) self.county_name = QLineEdit() self.county_name.setPlaceholderText("请输入区县名称") county_name_layout.addWidget(self.county_name) config_layout.addLayout(county_name_layout) # 图层名称选择 layer_select_layout = QHBoxLayout() layer_select_layout.addWidget(QLabel("选择图层:")) self.layer_name_combo = QComboBox() self.layer_name_combo.setMinimumWidth(200) self.layer_name_combo.currentIndexChanged.connect(self.on_layer_name_changed) layer_select_layout.addWidget(self.layer_name_combo) layer_select_layout.addStretch() config_layout.addLayout(layer_select_layout) # 配置文件设置显示区域 export_layout = QVBoxLayout() export_layout.addWidget(QLabel("配置文件设置:")) export_layout.minimumHeightForWidth(200) self.export_config_display = QTableWidget(0, 2) self.export_config_display.setHorizontalHeaderLabels(["元素名称", "元素内容"]) header = self.export_config_display.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.ResizeMode.Interactive) header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) header.setDefaultSectionSize(150) self.export_config_display.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) export_layout.addWidget(self.export_config_display) config_layout.addLayout(export_layout) layout.addWidget(config_group) # 输入输出设置组 io_group = QGroupBox("输入输出设置") io_layout = QVBoxLayout() # 地图文档选择 doc_layout = QHBoxLayout() doc_layout.addWidget(QLabel("模板文件:")) self.template_aprx_file = QLineEdit() doc_layout.addWidget(self.template_aprx_file) browse_doc_btn = QPushButton("浏览...") browse_doc_btn.clicked.connect(self.browse_doc) doc_layout.addWidget(browse_doc_btn) io_layout.addLayout(doc_layout) # 数据源选择 data_layout = QHBoxLayout() data_layout.addWidget(QLabel("数据源:")) self.data_source_path = QLineEdit() data_layout.addWidget(self.data_source_path) browse_data_btn = QPushButton("浏览...") browse_data_btn.clicked.connect(self.browse_data_source) data_layout.addWidget(browse_data_btn) io_layout.addLayout(data_layout) # 符号系统文件夹选择 symbol_layout = QHBoxLayout() symbol_layout.addWidget(QLabel("符号系统:")) self.symbol_path = QLineEdit() symbol_layout.addWidget(self.symbol_path) browse_symbol_btn = QPushButton("浏览...") browse_symbol_btn.clicked.connect(self.browse_symbol_file) symbol_layout.addWidget(browse_symbol_btn) io_layout.addLayout(symbol_layout) # 输出路径选择 output_layout = QHBoxLayout() output_layout.addWidget(QLabel("输出路径:")) self.output_path = QLineEdit() output_layout.addWidget(self.output_path) browse_output_btn = QPushButton("浏览...") browse_output_btn.clicked.connect(self.browse_output) output_layout.addWidget(browse_output_btn) io_layout.addLayout(output_layout) io_group.setLayout(io_layout) layout.addWidget(io_group) # 导出设置组 export_group = QGroupBox("导出设置") export_layout = QFormLayout() # 格式选择 self.format_combo = QComboBox() self.format_combo.addItems(["PDF", "PNG", "JPG", "TIFF", "EPS", "SVG", "AI"]) export_layout.addRow("导出格式:", self.format_combo) # 分辨率设置 self.resolution_spinbox = QSpinBox() self.resolution_spinbox.setRange(72, 1200) self.resolution_spinbox.setSingleStep(12) self.resolution_spinbox.setValue(300) self.resolution_spinbox.setSuffix(" DPI") export_layout.addRow("分辨率:", self.resolution_spinbox) # 强制重新生成选项 self.force_regenerate = QCheckBox("强制重新生成工程文件") self.force_regenerate.setChecked(False) self.force_regenerate.setToolTip("勾选后将忽略已存在的工程文件,重新生成") export_layout.addRow("处理选项:", self.force_regenerate) export_group.setLayout(export_layout) layout.addWidget(export_group) # 操作按钮 button_layout = QHBoxLayout() self.export_layout_btn = QPushButton("仅导出布局") self.export_layout_btn.clicked.connect(self.on_export_layout) self.export_btn = QPushButton("导出") self.export_btn.clicked.connect(self.on_export) self.batch_export_btn = QPushButton("批量导出") self.batch_export_btn.clicked.connect(self.on_batch_export) self.export_existing_btn = QPushButton("导出已有工程") self.export_existing_btn.setToolTip("仅导出输出文件夹中已存在的工程文件") self.export_existing_btn.clicked.connect(self.on_export_existing) self.test_btn = QPushButton("测试功能") self.test_btn.setToolTip("测试功能") self.test_btn.clicked.connect(self.on_test_functions) button_layout.addStretch() button_layout.addWidget(self.export_layout_btn) button_layout.addWidget(self.export_btn) button_layout.addWidget(self.batch_export_btn) button_layout.addWidget(self.export_existing_btn) button_layout.addWidget(self.test_btn) layout.addLayout(button_layout) layout.addStretch() def browse_config_file(self): """浏览配置文件""" initial_path = os.getcwd() file_path, _ = QFileDialog.getOpenFileName(self, "选择配置文件", initial_path, "JSON Files (*.json);;All Files (*.*)") if file_path: self.config_file_path.setText(file_path) self.load_export_config(file_path) def browse_doc(self): """浏览地图文档""" file_path, _ = QFileDialog.getOpenFileName( self, "选择地图文档", "", "ArcGIS Pro Project (*.aprx);;All Files (*.*)" ) if file_path: self.template_aprx_file.setText(file_path) def browse_data_source(self): """浏览数据源""" dir_path = QFileDialog.getExistingDirectory( self, "选择数据源目录" ) if dir_path: self.data_source_path.setText(dir_path) def browse_symbol_file(self): """浏览符号系统文件夹""" dir_path = QFileDialog.getExistingDirectory( self, "选择符号系统文件夹" ) if dir_path: self.symbol_path.setText(dir_path) def browse_output(self): """选择输出路径""" dir_path = QFileDialog.getExistingDirectory( self, "选择输出路径" ) if dir_path: self.output_path.setText(dir_path) def load_export_config(self, file_path): """加载导出配置文件""" try: with open(file_path, 'r', encoding='utf-8') as f: config = json.load(f) export_config = config.get('export_config', {}) if not export_config: QMessageBox.warning(self, "警告", "配置文件中未找到导出设置") return # 保存配置到实例变量 self._config = config self._export_config = export_config # 清空并添加图层名称到下拉框 self.layer_name_combo.clear() self.layer_name_combo.addItems(export_config.keys()) # 如果有图层,自动选择第一个 if self.layer_name_combo.count() > 0: self.layer_name_combo.setCurrentIndex(0) self.log_message(f"已加载配置文件: {file_path}") except Exception as e: QMessageBox.critical(self, "错误", f"加载配置文件失败: {str(e)}") def on_layer_name_changed(self, index): """图层名称变更时更新配置显示""" if index < 0 or not hasattr(self, '_export_config'): return layer_name = self.layer_name_combo.currentText() layer_config = self._export_config.get(layer_name, {}) # 清空并重新添加行 self.export_config_display.setRowCount(0) for i, (key, value) in enumerate(layer_config.items()): self.export_config_display.insertRow(i) self.export_config_display.setItem(i, 0, QTableWidgetItem(key)) self.export_config_display.setItem(i, 1, QTableWidgetItem(str(value))) def on_script_started(self, message): """脚本开始执行回调""" self.set_buttons_enabled(False) self.log_message(message) 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, error_msg): """脚本执行错误回调""" self.set_buttons_enabled(True) self.log_message(f"错误: {error_msg}") QMessageBox.critical(self, "错误", error_msg) def set_buttons_enabled(self, enabled): """设置按钮启用状态""" self.export_btn.setEnabled(enabled) self.batch_export_btn.setEnabled(enabled) self.export_layout_btn.setEnabled(enabled) self.export_existing_btn.setEnabled(enabled) self.test_btn.setEnabled(enabled) def on_export(self): """导出按钮点击事件""" # 验证输入 if not self.validate_inputs(): return # 获取参数 params = self.get_export_params() if not params: return # 添加配置文件参数 params['config_file'] = self.config_file_path.text() # 调用导出地图脚本 self.script_runner.run_export_map(params) def on_batch_export(self): """批量导出按钮点击事件""" # 验证输入 if not self.validate_inputs(check_layer=False): return # 获取参数 params = self.get_export_params(include_layer=False) if not params: return # 检查配置是否包含图层 if not hasattr(self, '_export_config') or not self._export_config: QMessageBox.warning(self, "警告", "请先加载配置文件") return # 添加配置文件路径参数 params['config_file'] = self.config_file_path.text() params['export_config'] = self._export_config # 检查可用图层 polygon_list = list(self._export_config.keys()) available_layers = [] for layer_name in polygon_list: layer_path = os.path.join(params.get("data_source_path"), layer_name) if arcpy.Exists(layer_path): available_layers.append(layer_name) params['polygon_list'] = available_layers # 确认是否批量导出所有图层 layer_count = len(available_layers) result = QMessageBox.question( self, "确认", f"数据源可用图层 {layer_count} 个,是否继续?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if result == QMessageBox.No: return # 调用批量导出脚本 self.script_runner.run_batch_export_map(params) def validate_inputs(self, check_layer=True): """验证输入参数""" if check_layer and self.layer_name_combo.currentIndex() < 0: QMessageBox.warning(self, "警告", "请选择一个图层") return False if not self.county_name.text(): QMessageBox.warning(self, "警告", "请输入区县名称") return False if not self.template_aprx_file.text() or not os.path.exists(self.template_aprx_file.text()): QMessageBox.warning(self, "警告", "请选择有效的模板文件") return False if not self.data_source_path.text() or not os.path.exists(self.data_source_path.text()): QMessageBox.warning(self, "警告", "请选择有效的数据源") return False if not self.output_path.text(): QMessageBox.warning(self, "警告", "请选择输出路径") return False return True def get_export_params(self, include_layer=True): """获取导出参数""" try: params = { 'county_name': self.county_name.text(), 'template_aprx_file': self.template_aprx_file.text(), 'data_source_path': self.data_source_path.text(), 'symbol_path': self.symbol_path.text(), 'output_path': self.output_path.text(), 'force_regenerate': self.force_regenerate.isChecked() } if include_layer: params['layer_name'] = self.layer_name_combo.currentText() return params except Exception as e: QMessageBox.critical(self, "错误", f"获取参数失败: {str(e)}") return None def load_settings(self): """加载设置""" if not self.main_window: return try: settings = self.main_window.settings last_paths = settings.get('last_paths', {}) if last_paths.get('config_file'): self.config_file_path.setText(last_paths.get('config_file', '')) if os.path.exists(last_paths.get('config_file', '')): self.load_export_config(last_paths.get('config_file', '')) self.county_name.setText(last_paths.get('county_name', '')) self.template_aprx_file.setText(last_paths.get('template_aprx_file', '')) self.data_source_path.setText(last_paths.get('data_source_path', '')) self.symbol_path.setText(last_paths.get('symbol_path', '')) self.output_path.setText(last_paths.get('output_path', '')) export_settings = settings.get('export_settings', {}) # 设置导出格式 format_index = self.format_combo.findText(export_settings.get('default_format', 'PDF')) if format_index >= 0: self.format_combo.setCurrentIndex(format_index) # 设置分辨率 self.resolution_spinbox.setValue(export_settings.get('resolution', 300)) except Exception as e: self.log_message(f"加载设置失败: {str(e)}") def on_export_layout(self): """仅导出布局按钮点击事件""" # 验证输入 if not self.validate_inputs(): return # 获取参数 export_params = self.get_export_params() if not export_params: return # 检查导出配置和图层名称 layer_name = self.layer_name_combo.currentText() if not hasattr(self, '_export_config') or layer_name not in self._export_config: QMessageBox.warning(self, "警告", "请先加载配置文件并选择图层") return # 准备工程文件路径 single_export_config = self._export_config.get(layer_name, {}) temp_file_name = single_export_config['项目名称'].split('\n')[1] file_name = temp_file_name.replace('{区县占位符}', export_params['county_name']) aprx_path = os.path.join(export_params['output_path'], f"{file_name}.aprx") # 检查工程文件是否存在 if not os.path.exists(aprx_path): result = QMessageBox.question( self, "确认", f"工程文件不存在: {aprx_path}\n是否先生成工程文件?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) if result == QMessageBox.Yes: # 先生成工程文件 # 添加配置文件路径参数 export_params['config_file'] = self.config_file_path.text() # 执行生成工程文件 self.script_runner.run_export_map(export_params) return else: return # 准备导出布局参数 layout_params = { 'aprx_path': aprx_path, 'output_path': export_params['output_path'], 'export_format': self.format_combo.currentText(), 'resolution': self.resolution_spinbox.value(), 'output_name': file_name } # 调用导出布局脚本 self.script_runner.run_export_layout(layout_params) def on_export_existing(self): """导出已有工程按钮点击事件""" # 验证输出路径 if not self.output_path.text() or not os.path.exists(self.output_path.text()): QMessageBox.warning(self, "警告", "请选择有效的输出路径") return output_path = self.output_path.text() # 查找所有aprx文件 aprx_files = [] for file in os.listdir(output_path): if file.lower().endswith('.aprx'): aprx_files.append(os.path.join(output_path, file)) if not aprx_files: QMessageBox.warning(self, "警告", f"在指定目录中未找到aprx文件: {output_path}") return # 确认是否导出所有工程文件 result = QMessageBox.question( self, "确认", f"将导出 {len(aprx_files)} 个工程文件的布局,是否继续?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if result == QMessageBox.No: return # 准备批量导出布局参数 batch_params = { 'aprx_folder': output_path, 'output_path': os.path.join(output_path, self.format_combo.currentText().lower()), 'export_format': self.format_combo.currentText(), 'resolution': self.resolution_spinbox.value() } # 调用批量导出布局脚本 self.script_runner.run_batch_export_layout(batch_params) 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) def on_test_functions(self): """测试功能按钮点击事件""" self.log_message("正在执行测试功能...") try: # 调用测试脚本 test_params = { 'message': "测试成功!这是来自脚本的消息。", 'count': 1 } # 使用script_runner的run_test_script方法 if hasattr(self.script_runner, 'run_test_script'): self.script_runner.run_test_script(test_params) self.log_message("测试脚本调用已启动,请查看日志") else: # 如果没有run_test_script方法,尝试调用test_script.py script_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'core', 'test_script.py')) if os.path.exists(script_path): self.log_message(f"找到测试脚本: {script_path}") args = { 'message': test_params['message'], 'count': test_params['count'] } self.script_runner.run_script(script_path, args) else: QMessageBox.information(self, "测试", "找不到测试脚本: test_script.py") self.log_message(f"找不到测试脚本: {script_path}") except Exception as e: self.log_message(f"测试功能调用失败: {str(e)}") QMessageBox.critical(self, "错误", f"测试功能调用失败: {str(e)}")