diff --git a/WebSocket/backend_singleton.py b/WebSocket/backend_singleton.py index eabcd13..2d42eb9 100644 --- a/WebSocket/backend_singleton.py +++ b/WebSocket/backend_singleton.py @@ -245,11 +245,18 @@ class WebSocketManager: self._log(f"❌ 启动版本检查器失败: {e}", "ERROR") def _on_update_available(self, latest_version, download_url): - """发现新版本时的处理""" + """发现新版本时的处理(在子线程中调用)""" self._log(f"🔔 发现新版本 {latest_version}", "INFO") - # 通知主GUI显示更新提醒 + # 通知主GUI显示更新提醒(通过 Qt 信号机制,线程安全) if hasattr(self, 'gui_update_callback') and self.gui_update_callback: - self.gui_update_callback(latest_version, download_url) + try: + # 直接调用回调(回调内部使用信号机制调度到主线程) + self.gui_update_callback(latest_version, download_url) + self._log(f"✅ 已调用更新回调", "DEBUG") + except Exception as e: + self._log(f"❌ 调用更新回调失败: {e}", "ERROR") + import traceback + self._log(f"详细错误: {traceback.format_exc()}", "ERROR") def _start_jd_listener(self, store_id: str, cookies: str): """启动京东平台监听""" diff --git a/main.py b/main.py index bad78c5..7f3b786 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ import sys -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, pyqtSignal, QObject from PyQt5.QtGui import QFont, QPalette, QColor from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QLineEdit, @@ -19,8 +19,13 @@ setup_file_logging() # 生产环境启用自动日志功能 print("文件日志系统已在main.py中初始化") -# 新增: 用户名密码输入对话框类 +# 新增: 版本更新信号类(用于线程安全的 GUI 通知) +class UpdateSignals(QObject): + """版本更新信号""" + update_available = pyqtSignal(str, str) # (latest_version, download_url) + +# 新增: 用户名密码输入对话框类 class LoginWindow(QMainWindow): def __init__(self): super().__init__() @@ -41,6 +46,10 @@ class LoginWindow(QMainWindow): # 日志管理相关变量已删除 + # 创建版本更新信号对象(线程安全) + self.update_signals = UpdateSignals() + self.update_signals.update_available.connect(self._show_update_dialog) + self.initUI() # 延迟设置版本检查器,确保WebSocket连接已建立 @@ -708,12 +717,35 @@ class LoginWindow(QMainWindow): from WebSocket.backend_singleton import get_websocket_manager ws_manager = get_websocket_manager() if ws_manager: - ws_manager.gui_update_callback = self.show_update_notification + # 设置回调为信号发射函数(线程安全) + ws_manager.gui_update_callback = self.emit_update_signal self.add_log("✅ 版本检查器GUI回调已设置", "SUCCESS") else: self.add_log("⚠️ WebSocket管理器未初始化", "WARNING") except Exception as e: self.add_log(f"❌ 设置版本检查器失败: {e}", "ERROR") + + def emit_update_signal(self, latest_version, download_url): + """发射更新信号(可以在任何线程中调用)""" + try: + self.add_log(f"📡 收到更新通知,准备发射信号: v{latest_version}", "INFO") + # 发射信号(Qt 自动调度到主线程) + self.update_signals.update_available.emit(latest_version, download_url) + self.add_log(f"✅ 更新信号已发射", "DEBUG") + except Exception as e: + self.add_log(f"❌ 发射更新信号失败: {e}", "ERROR") + import traceback + self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR") + + def _show_update_dialog(self, latest_version, download_url): + """显示更新对话框(信号槽函数,始终在主线程中执行)""" + try: + self.add_log(f"🎯 主线程收到更新信号: v{latest_version}", "INFO") + self.show_update_notification(latest_version, download_url) + except Exception as e: + self.add_log(f"❌ 显示更新对话框失败: {e}", "ERROR") + import traceback + self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR") def show_update_notification(self, latest_version, download_url): """显示版本更新通知""" @@ -757,7 +789,6 @@ class LoginWindow(QMainWindow): def trigger_update(self, download_url, latest_version): """触发更新下载""" - import webbrowser try: # 检查下载地址是否有效 if not download_url or download_url.strip() == "": @@ -770,11 +801,32 @@ class LoginWindow(QMainWindow): ) return - self.add_log(f"📂 打开下载页面: {download_url}", "INFO") - webbrowser.open(download_url) - self.add_log("✅ 已打开更新下载页面", "SUCCESS") + self.add_log(f"📂 准备打开下载页面: {download_url}", "INFO") + + # 使用线程安全的方式打开浏览器 + try: + import webbrowser + import threading + + def open_browser(): + """在独立线程中打开浏览器,避免阻塞主线程""" + try: + webbrowser.open(download_url) + self.add_log("✅ 浏览器已打开", "SUCCESS") + except Exception as e: + self.add_log(f"❌ 打开浏览器失败: {e}", "ERROR") + + # 在新线程中打开浏览器 + browser_thread = threading.Thread(target=open_browser, daemon=True) + browser_thread.start() + + self.add_log(f"✅ 已启动浏览器线程", "INFO") + + except Exception as browser_error: + self.add_log(f"❌ 浏览器线程启动失败: {browser_error}", "ERROR") + except Exception as e: - self.add_log(f"❌ 打开下载页面失败: {e}", "ERROR") + self.add_log(f"❌ 触发更新失败: {e}", "ERROR") import traceback self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR")