import sysimport osimport jsonfrom pathlib import Pathfrom PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog, QTextEdit, QProgressBar, QGroupBox, QMessageBox, QLineEdit, QGridLayout)from PyQt6.QtCore import Qt, QThread, pyqtSignalfrom PyQt6.QtGui import QFont# --- 配置文件路径 ---CONFIG_FILE = "watermark_remover_config.json"# --- 配置管理函数 ---def load_config(): """加载配置文件""" default_config = { "api_key": "", # 默认API Key "output_dir": "output", "last_folder": "", # 上次选择的文件夹路径 } if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, 'r', encoding='utf-8') as f: config = json.load(f) # 确保所有必要的键都存在 for key in default_config: if key not in config: config[key] = default_config[key] return config except Exception as e: print(f"读取配置文件失败: {str(e)},使用默认配置") return default_config else: return default_configdef save_config(config): """保存配置文件""" try: with open(CONFIG_FILE, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=2) return True except Exception as e: print(f"保存配置文件失败: {str(e)}") return False# --- 工作线程类(仅保留框架,移除业务逻辑)---class WatermarkRemovalWorker(QThread): """公章抠图工作线程(框架)""" progress_signal = pyqtSignal(str) progress_value_signal = pyqtSignal(int) result_signal = pyqtSignal(dict) error_signal = pyqtSignal(str) def __init__(self, image_paths, api_key, prompt_text): super().__init__() self.image_paths = image_paths self.api_key = api_key self.prompt_text = prompt_text self.running = True def run(self): """线程执行函数(模拟处理过程)""" total_images = len(self.image_paths) for idx, image_path in enumerate(self.image_paths): if not self.running: break # 模拟处理进度 progress_value = int(((idx + 1) / total_images) * 100) self.progress_value_signal.emit(progress_value) # 模拟处理消息 self.progress_signal.emit(f"正在处理第 {idx + 1}/{len(self.image_paths)} 张图片: {os.path.basename(image_path)}") # 模拟处理延迟 self.msleep(500) # 模拟处理结果 result = { 'success': True, 'original_file': image_path, 'processed_files': [f"output/{os.path.basename(image_path)}"], 'original_size': "1920x1080", 'file_size': "2.5MB", 'new_size': "1024x1024" } self.result_signal.emit(result) # 模拟错误(可选) # if idx == 2: # self.error_signal.emit(f"处理图片 {os.path.basename(image_path)} 时出错: 模拟错误") self.progress_signal.emit("处理完成!") def stop(self): """停止线程""" self.running = False# --- 主界面类 ---class WatermarkRemovalApp(QMainWindow): """公章抠图应用主窗口""" def __init__(self): super().__init__() self.image_files = [] self.worker = None self.current_image_index = 0 # 加载配置 self.config = load_config() self.api_key = self.config.get("api_key", "") self.init_ui() def init_ui(self): """初始化UI""" self.setWindowTitle("公章抠图工具(界面框架)") self.setGeometry(100, 100, 800, 700) # 设置样式 self.setStyleSheet(""" QMainWindow { background-color: #f0f2f5; } QGroupBox { font-size: 14px; font-weight: bold; border: 2px solid #d1d9e6; border-radius: 8px; margin-top: 10px; padding-top: 10px; background-color: white; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; color: #2c3e50; } QPushButton { font-size: 14px; padding: 10px 20px; border-radius: 6px; border: none; font-weight: bold; } QPushButton:hover { opacity: 0.9; } QPushButton:disabled { background-color: #bdc3c7; }#startButton { background-color: #2ecc71; color: white; font-size: 16px; padding: 15px 30px; }#folderButton { background-color: #3498db; color: white; }#saveKeyButton { background-color: #9b59b6; color: white; padding: 10px 15px; min-width: 120px; } QLabel { font-size: 14px; } QLineEdit { font-size: 14px; padding: 10px; border: 1px solid #d1d9e6; border-radius: 6px; background-color: white; } QTextEdit { border: 1px solid #d1d9e6; border-radius: 6px; padding: 8px; font-size: 13px; background-color: white; } QProgressBar { border: 1px solid #d1d9e6; border-radius: 6px; text-align: center; background-color: white; font-weight: bold; } QProgressBar::chunk { background-color: #3498db; border-radius: 6px; } """) # 创建中心部件 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(15) main_layout.setContentsMargins(15, 15, 15, 15) # API设置组 api_group = QGroupBox("API设置") api_layout = QVBoxLayout() # API Key输入框和保存按钮(水平布局) api_key_layout = QHBoxLayout() api_key_label = QLabel("API Key:") self.api_key_edit = QLineEdit() self.api_key_edit.setText(self.api_key) self.api_key_edit.textChanged.connect(self.on_api_key_changed) self.api_key_edit.setPlaceholderText("请输入您的API Key") # 保存API Key按钮 save_key_button = QPushButton("💾 保存API Key") save_key_button.setObjectName("saveKeyButton") save_key_button.clicked.connect(self.save_api_key) api_key_layout.addWidget(api_key_label) api_key_layout.addWidget(self.api_key_edit) api_key_layout.addWidget(save_key_button) # 设置比例,让输入框占据更多空间 api_key_layout.setStretch(0, 1) api_key_layout.setStretch(1, 4) api_key_layout.setStretch(2, 1) api_layout.addLayout(api_key_layout) api_group.setLayout(api_layout) main_layout.addWidget(api_group) # 图片管理组 image_group = QGroupBox("图片管理") image_layout = QVBoxLayout() # 文件夹选择部分(使用网格布局) folder_layout = QGridLayout() folder_layout.setSpacing(10) # 文件夹路径输入框 folder_label = QLabel("文件夹路径:") self.folder_edit = QLineEdit() self.folder_edit.setPlaceholderText("请选择或输入图片文件夹路径") self.folder_edit.setText(self.config.get("last_folder", "")) # 文件夹选择按钮 self.folder_button = QPushButton("📂 浏览文件夹") self.folder_button.setObjectName("folderButton") self.folder_button.clicked.connect(self.add_folder) # 将控件添加到网格布局 folder_layout.addWidget(folder_label, 0, 0) folder_layout.addWidget(self.folder_edit, 0, 1) folder_layout.addWidget(self.folder_button, 0, 2) # 设置列宽比例 folder_layout.setColumnStretch(0, 1) folder_layout.setColumnStretch(1, 3) folder_layout.setColumnStretch(2, 1) # 添加到主布局 image_layout.addLayout(folder_layout) image_group.setLayout(image_layout) main_layout.addWidget(image_group) # 提示词设置组 prompt_group = QGroupBox("提示词设置") prompt_layout = QVBoxLayout() prompt_label = QLabel("请输入提示词(描述需要如何处理图片):") self.prompt_edit = QTextEdit() default_prompt = "请仔细识别图片中的公章,确保:\n1. 对公章进行抠图\n2. 保持公章实际尺寸\n3. 去除背景文字\n4. 保持公章颜色一致" self.prompt_edit.setText(default_prompt) self.prompt_edit.setMinimumHeight(120) prompt_layout.addWidget(prompt_label) prompt_layout.addWidget(self.prompt_edit) prompt_group.setLayout(prompt_layout) main_layout.addWidget(prompt_group) # 进度条 self.progress_bar = QProgressBar() self.progress_bar.setTextVisible(True) self.progress_bar.setFormat("%p%") main_layout.addWidget(self.progress_bar) # 开始按钮 self.start_button = QPushButton("🚀 开始处理(模拟)") self.start_button.setObjectName("startButton") self.start_button.clicked.connect(self.start_processing) main_layout.addWidget(self.start_button) # 日志显示 log_group = QGroupBox("处理日志") log_layout = QVBoxLayout() self.log_text = QTextEdit() self.log_text.setReadOnly(True) self.log_text.setMaximumHeight(200) log_layout.addWidget(self.log_text) log_group.setLayout(log_layout) main_layout.addWidget(log_group) main_layout.addStretch() def add_folder(self): """添加文件夹中的所有图片""" current_path = self.folder_edit.text().strip() if not os.path.exists(current_path): current_path = self.config.get("last_folder", "") folder = QFileDialog.getExistingDirectory( self, "选择图片文件夹", current_path ) if folder: self.folder_edit.setText(folder) self.config["last_folder"] = folder save_config(self.config) # 清空之前的图片列表 self.image_files.clear() # 查找支持的图片文件 image_extensions = ['.png', '.jpg', '.jpeg', '.bmp', '.webp', '.gif'] image_files = [] for ext in image_extensions: image_files.extend(list(Path(folder).glob(f'*{ext}'))) image_files.extend(list(Path(folder).glob(f'*{ext.upper()}'))) if image_files: unique_files = list(set(image_files)) unique_files.sort() self.image_files.extend([str(f) for f in unique_files]) self.log_message(f"从文件夹添加了 {len(unique_files)} 张图片") self.log_message(f"文件夹: {folder}") else: QMessageBox.warning(self, "警告", "选择的文件夹中没有找到支持的图片文件") def on_api_key_changed(self): """API Key变化时更新""" self.api_key = self.api_key_edit.text().strip() def save_api_key(self): """保存API Key到配置文件""" self.api_key = self.api_key_edit.text().strip() if not self.api_key: QMessageBox.warning(self, "警告", "API Key不能为空!") return self.config["api_key"] = self.api_key if save_config(self.config): QMessageBox.information(self, "成功", "API Key已保存!") self.log_message("API Key已保存到配置文件") else: QMessageBox.warning(self, "警告", "保存API Key失败") def log_message(self, message): """添加日志消息""" import time timestamp = time.strftime("%H:%M:%S", time.localtime()) self.log_text.append(f"[{timestamp}] {message}") self.log_text.verticalScrollBar().setValue( self.log_text.verticalScrollBar().maximum() ) def start_processing(self): """开始处理图片""" if not self.image_files: QMessageBox.warning(self, "警告", "请先选择要处理的图片文件夹!") return if not self.api_key: QMessageBox.warning(self, "警告", "请输入API Key!") return current_prompt = self.prompt_edit.toPlainText().strip() if not current_prompt: QMessageBox.warning(self, "警告", "请输入提示词!") return self.progress_bar.setMaximum(100) self.progress_bar.setValue(0) self.current_image_index = 0 # 创建并启动工作线程 self.worker = WatermarkRemovalWorker( self.image_files, self.api_key, current_prompt ) # 连接信号 self.worker.progress_signal.connect(self.update_progress_message) self.worker.progress_value_signal.connect(self.update_progress_value) self.worker.result_signal.connect(self.on_processing_complete) self.worker.error_signal.connect(self.on_processing_error) self.worker.finished.connect(self.on_worker_finished) # 更新UI状态 self.start_button.setEnabled(False) # 开始处理 self.log_message(f"开始处理 {len(self.image_files)} 张图片...") self.log_message(f"使用提示词:{current_prompt}") self.worker.start() def update_progress_message(self, message): """更新进度消息""" self.log_message(message) def update_progress_value(self, value): """更新进度条值""" self.progress_bar.setValue(value) def on_processing_complete(self, result): """处理完成回调""" original_file = result['original_file'] self.current_image_index += 1 message = f"✓ 完成处理: {os.path.basename(original_file)}" if result['processed_files']: message += f" -> {result['processed_files'][0]}" self.log_message(message) def on_processing_error(self, error_msg): """处理错误回调""" self.log_message(f"❌ 错误: {error_msg}") self.current_image_index += 1 def on_worker_finished(self): """工作线程完成""" self.start_button.setEnabled(True) self.progress_bar.setValue(100) self.log_message("所有图片处理完成!") # 询问是否打开结果文件夹 output_dir = self.config.get("output_dir", "output") if os.path.exists(output_dir): reply = QMessageBox.question( self, "处理完成", "处理完成!是否打开结果文件夹?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: import subprocess try: if sys.platform == "win32": os.startfile(output_dir) elif sys.platform == "darwin": subprocess.run(["open", output_dir]) else: subprocess.run(["xdg-open", output_dir]) except Exception as e: self.log_message(f"无法打开文件夹: {str(e)}") def closeEvent(self, event): """关闭事件处理""" # 保存当前API Key current_api_key = self.api_key_edit.text().strip() if current_api_key and current_api_key != self.config.get("api_key"): self.config["api_key"] = current_api_key # 保存当前文件夹路径 current_folder = self.folder_edit.text().strip() if current_folder and current_folder != self.config.get("last_folder"): self.config["last_folder"] = current_folder save_config(self.config) if self.worker and self.worker.isRunning(): reply = QMessageBox.question( self, "确认退出", "处理正在进行中,确定要退出吗?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: self.worker.stop() self.worker.wait() event.accept() else: event.ignore() else: event.accept()# --- 主程序入口 ---def main(): app = QApplication(sys.argv) app.setStyle('Fusion') window = WatermarkRemovalApp() window.show() sys.exit(app.exec())if __name__ == "__main__": main()