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