import multiprocessing import os from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QFileDialog, QMessageBox, QListWidget, QComboBox, QSpinBox, QGroupBox, QFormLayout, QCheckBox) from ..runners.script_runner import ScriptRunner from ..components.file_list_group import FileListGroup class ExportImageTab(QWidget): """导出成果图标签页""" def __init__(self, parent=None): super().__init__(parent) self.main_window = parent self.runner = ScriptRunner(self) self.connect_signals() self.init_ui() self.load_settings() self.on_load_files() def connect_signals(self): """连接脚本运行器信号""" # 连接ScriptRunner的信号 self.runner.task_started.connect(self.on_script_started) self.runner.task_finished.connect(self.on_script_finished) self.runner.task_error.connect(self.on_script_error) self.runner.task_log.connect(self.on_script_log) def init_ui(self): main_layout = QVBoxLayout(self) # 输入输出设置组 io_group = QGroupBox("输入输出设置") io_layout = QFormLayout() # 地图文档文件夹选择 data_layout = QHBoxLayout() self.input_aprx_path_edit = QLineEdit() data_layout.addWidget(self.input_aprx_path_edit) browse_data_btn = QPushButton("浏览...") browse_data_btn.clicked.connect(self.browse_data_source) data_layout.addWidget(browse_data_btn) io_layout.addRow("工程文件夹:", data_layout) # 输出路径选择 output_layout = QHBoxLayout() self.output_image_path_edit = QLineEdit() output_layout.addWidget(self.output_image_path_edit) browse_output_btn = QPushButton("浏览...") browse_output_btn.clicked.connect(self.browse_output) output_layout.addWidget(browse_output_btn) io_layout.addRow("输出文件夹:", output_layout) io_group.setLayout(io_layout) main_layout.addWidget(io_group) # 添加文件列表布局 self.file_list_group = FileListGroup(self, "选择要导出的文件") self.file_list_group.load_files.connect(self.on_load_files) main_layout.addWidget(self.file_list_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.image_force_regenerate_check = QCheckBox("强制重新生成工程文件") self.image_force_regenerate_check.setChecked(False) self.image_force_regenerate_check.setToolTip("勾选后将忽略已存在的工程文件,重新生成") export_layout.addRow("处理选项:", self.image_force_regenerate_check) # 多进程设置 self.use_multiprocessing = QCheckBox("使用多进程导出") self.use_multiprocessing.setChecked(True) export_layout.addRow("批量模式:", self.use_multiprocessing) # 进程数量设置 self.process_count = QSpinBox() self.process_count.setRange(1, multiprocessing.cpu_count()) self.process_count.setValue(max(1, multiprocessing.cpu_count() - 1)) # 默认使用CPU核心数-1 export_layout.addRow("进程数量:", self.process_count) export_group.setLayout(export_layout) main_layout.addWidget(export_group) # 操作按钮 self.export_existing_btn = QPushButton("导出成果图") self.export_existing_btn.setToolTip("仅导出输出文件夹中已存在的工程文件") self.export_existing_btn.clicked.connect(self.on_export_existing) main_layout.addWidget(self.export_existing_btn) # main_layout.addStretch() def browse_data_source(self): """浏览工程目录""" initial_dir = "" if os.path.exists(self.input_aprx_path_edit.text()): initial_dir = os.path.dirname(self.input_aprx_path_edit.text()) dir_path = QFileDialog.getExistingDirectory(self, "选择工程目录", initial_dir) if dir_path: self.input_aprx_path_edit.setText(dir_path) self.on_load_files() def browse_output(self): """选择输出路径""" initial_dir = "" if os.path.exists(self.output_image_path_edit.text()): initial_dir = os.path.dirname(self.output_image_path_edit.text()) dir_path = QFileDialog.getExistingDirectory(self, "选择输出路径", initial_dir) if dir_path: self.output_image_path_edit.setText(dir_path) def on_load_files(self): """加载图层列表""" try: input_aprx_path = self.input_aprx_path_edit.text() if not os.path.exists(input_aprx_path): QMessageBox.warning(self, "错误", "请先选择输入文件夹") return # 清空列表 self.file_list_group.file_list.clear() # 添加文件 for file_name in os.listdir(input_aprx_path): file_path = os.path.join(input_aprx_path, file_name) if file_name.endswith(".aprx") and os.path.isfile(file_path): self.file_list_group.file_list.addItem(file_path) self.log_message(f"已加载 {self.file_list_group.file_list.count()} 个文件") except Exception as e: self.log_message(f"加载文件失败: {str(e)}") 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) if success: QMessageBox.information(self, "成功", "成果图导出完成!") else: QMessageBox.warning(self, "失败", "导出过程中出现错,请查看日志详情") def on_script_error(self, error_msg): """脚本执行错误回调""" self.set_buttons_enabled(True) self.log_message(f"错误: {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) def set_buttons_enabled(self, enabled): """设置按钮启用状态""" self.export_existing_btn.setEnabled(enabled) def validate_and_params(self): """验证输入参数并返回参数""" # 验证工程文件 aprx_files = self.file_list_group.file_list.selectedItems() if not aprx_files or len(aprx_files) == 0: QMessageBox.warning(self, "警告", "请选择要导出的文件") return None # 验证工程目录 input_aprx_folder = self.input_aprx_path_edit.text() if not os.path.exists(input_aprx_folder): QMessageBox.warning(self, "警告", "请选择有效的工程文件目录") return None # 验证输出路径 output_image_path = self.output_image_path_edit.text() if not os.path.exists(output_image_path): QMessageBox.warning(self, "警告", "请选择输出路径") return None aprx_file_list = [file.text() for file in aprx_files] output_image_path = os.path.join(output_image_path, self.format_combo.currentText().lower()) return { 'input_aprx_folder': input_aprx_folder, 'aprx_file_list': aprx_file_list, 'output_image_path': output_image_path } def get_layout_settings(self): """ 获取布局设置 """ config = { 'input_aprx_path': self.input_aprx_path_edit.text(), 'output_image_path': self.output_image_path_edit.text(), 'default_format': self.format_combo.currentText(), 'resolution': self.resolution_spinbox.value(), 'force_regenerate': self.image_force_regenerate_check.isChecked(), 'use_multiprocessing': self.use_multiprocessing.isChecked(), 'process_count': self.process_count.value() } return config def load_settings(self): """加载设置""" if not self.main_window: return try: settings = self.main_window.settings export_image_settings = settings.get('export_image_settings', {}) self.input_aprx_path_edit.setText(export_image_settings.get('input_aprx_path', '')) self.output_image_path_edit.setText(export_image_settings.get('output_image_path', '')) # 设置导出格式 format_index = self.format_combo.findText(export_image_settings.get('default_format', 'PDF')) if format_index >= 0: self.format_combo.setCurrentIndex(format_index) # 设置分辨率 self.resolution_spinbox.setValue(export_image_settings.get('resolution', 300)) except Exception as e: self.log_message(f"加载设置失败: {str(e)}") def on_export_layout(self): """仅导出布局按钮点击事件""" # 验证并获取参数 layout_params = self.validate_and_params() if not layout_params: return # 准备导出布局参数 layout_params['export_format'] = self.format_combo.currentText() layout_params['resolution'] = self.resolution_spinbox.value() layout_params['image_force_regenerate'] = self.image_force_regenerate_check.isChecked() print(layout_params) # 调用导出布局脚本 # self.runner.run_export_layout(layout_params) def on_export_existing(self): """导出已有工程按钮点击事件""" # 验证并获取参数 layout_params = self.validate_and_params() if not layout_params: return # 确认是否导出所有工程文件 result = QMessageBox.question( self, "确认", f"将导出 {len(layout_params['aprx_file_list'])} 个工程文件的布局,是否继续?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No ) if result == QMessageBox.StandardButton.No: return # 准备批量导出布局参数 layout_params['export_format'] = self.format_combo.currentText() layout_params['resolution'] = self.resolution_spinbox.value() layout_params['image_force_regenerate'] = self.image_force_regenerate_check.isChecked() layout_params['use_multiprocessing'] = self.use_multiprocessing.isChecked() layout_params['process_count'] = self.process_count.value() # 调用批量导出布局脚本 # print(layout_params) self.runner.run_export_layout(layout_params)