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

570 lines
22 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.
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)}")