import json from typing import Dict from PyQt5.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QMessageBox, QLabel, QLineEdit, QPushButton, QFrame, QTextEdit, QDesktopWidget, QSystemTrayIcon, QMenu, QAction ) from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QFont, QIcon, QDesktopServices from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QApplication from pathlib import Path from services.cursor_service import CursorService from gui.components.widgets import ( LogWidget, StatusBar, ActionButton, ActivationWidget, MemberStatusWidget, ActivationStatusWidget, LoadingDialog ) from gui.components.workers import ResetWorker, DisableWorker, RefreshTokenWorker from common_utils import get_hardware_id from utils.version_manager import VersionManager class DeviceIdWidget(QFrame): """设备识别码显示组件""" def __init__(self, device_id: str, parent=None): super().__init__(parent) self.setup_ui(device_id) def setup_ui(self, device_id: str): layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # 移除边距 # 标签 label = QLabel("设备识别码(勿动):") label.setStyleSheet("color: #dc3545; font-weight: bold;") # 红色警示 layout.addWidget(label) # 显示设备ID的文本框 self.id_input = QLineEdit(device_id) self.id_input.setReadOnly(True) layout.addWidget(self.id_input) # 复制按钮 copy_btn = QPushButton("复制ID") copy_btn.clicked.connect(self.copy_device_id) layout.addWidget(copy_btn) self.setStyleSheet(""" QLineEdit { background-color: #f8f9fa; border: 1px solid #ced4da; border-radius: 4px; padding: 5px; color: #495057; } QPushButton { background-color: #6c757d; color: white; border: none; padding: 5px 10px; border-radius: 4px; } QPushButton:hover { background-color: #5a6268; } """) def copy_device_id(self): """复制设备ID到剪贴板""" self.id_input.selectAll() self.id_input.copy() QMessageBox.information(self, "成功", "设备ID已复制到剪贴板") class InstructionsWidget(QFrame): """使用步骤说明组件""" def __init__(self, parent=None): super().__init__(parent) self.setup_ui() def setup_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 10) # 移除边距 # 标题 title = QLabel("使用步骤说明:") title.setStyleSheet("color: #17a2b8; font-size: 14px; font-weight: bold;") # 青色标题 layout.addWidget(title) # 步骤说明 steps = [ ("第一步", "输入激活码点击【激活】按钮完成激活"), ("第二步", "点击【刷新Cursor编辑器授权】,一般情况下刷新即可正常使用"), ("如果无法对话", "点击【实现Cursor0.45.x限制】,然后重新刷新授权"), ("建议操作", "点击【禁用Cursor版本更新】,保持软件稳定运行") ] for step_title, step_content in steps: step_label = QLabel(f"{step_title}:{step_content}") step_label.setWordWrap(True) step_label.setStyleSheet(""" QLabel { color: #495057; margin: 3px 0; } """) layout.addWidget(step_label) # 给标题部分添加颜色 text = step_label.text() step_label.setText(f'{step_title}:{step_content}') class MainWindow(QMainWindow): """主窗口""" def __init__(self): super().__init__() self.cursor_service = CursorService() self.version_manager = VersionManager() self.current_worker = None self.loading_dialog = None # 初始化托盘图标 self.setup_tray() self.setup_ui() # 移除直接自检 # self.auto_check() def setup_tray(self): """初始化托盘图标""" # 创建托盘图标 self.tray_icon = QSystemTrayIcon(self) # 设置图标 icon_path = Path(__file__).parent.parent.parent / "two.ico" if icon_path.exists(): self.tray_icon.setIcon(QIcon(str(icon_path))) # 创建托盘菜单 self.tray_menu = QMenu() # 显示主窗口动作 show_action = QAction("显示主窗口", self) show_action.triggered.connect(self.show_main_window) self.tray_menu.addAction(show_action) # 刷新授权动作 refresh_action = QAction("刷新 Cursor 编辑器授权", self) refresh_action.triggered.connect(lambda: self.start_task('refresh')) self.tray_menu.addAction(refresh_action) # 退出动作 quit_action = QAction("退出", self) quit_action.triggered.connect(self.quit_application) self.tray_menu.addAction(quit_action) # 设置托盘菜单 self.tray_icon.setContextMenu(self.tray_menu) # 托盘图标双击事件 self.tray_icon.activated.connect(self.tray_icon_activated) # 显示托盘图标 self.tray_icon.show() def show_main_window(self): """显示主窗口""" self.show() self.activateWindow() def quit_application(self): """退出应用程序""" # 停止所有任务 if self.current_worker: self.current_worker.stop() # 移除托盘图标 self.tray_icon.setVisible(False) # 退出应用 QApplication.quit() def tray_icon_activated(self, reason): """托盘图标激活事件""" if reason == QSystemTrayIcon.DoubleClick: # 双击显示主窗口 self.show_main_window() def showEvent(self, event): """窗口显示事件""" super().showEvent(event) # 在窗口显示后执行自检 QTimer.singleShot(500, self.auto_check) # 延迟500ms执行,确保界面完全显示 def show_loading(self, message: str): """显示加载对话框""" if not self.loading_dialog: self.loading_dialog = LoadingDialog(self) self.loading_dialog.set_message(message) self.loading_dialog.show() QApplication.processEvents() # 确保对话框立即显示 def hide_loading(self): """隐藏加载对话框""" if self.loading_dialog: self.loading_dialog.hide() def auto_check(self): """启动时自检""" try: # 1. 检查更新 has_update, msg, update_info = self.version_manager.check_update() if has_update and update_info: reply = QMessageBox.question( self, "发现新版本", f"发现新版本: {update_info['version']}\n\n" f"更新内容:\n{update_info['message']}\n\n" "是否立即下载更新?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) if reply == QMessageBox.Yes: QDesktopServices.openUrl(QUrl(update_info['download_url'])) # 2. 检查激活状态 self.update_member_status() # 3. 恢复状态栏 self.status_bar.set_status("就绪") except Exception as e: self.status_bar.set_status("自检过程出现错误") self.logger.error(f"自检失败: {str(e)}") def setup_ui(self): """初始化UI""" self.setWindowTitle("听泉助手 v4.0.0.1") self.setMinimumSize(600, 500) # 设置窗口图标 icon_path = Path(__file__).parent.parent.parent / "two.ico" if icon_path.exists(): self.setWindowIcon(QIcon(str(icon_path))) # 设置窗口背景为白色 self.setStyleSheet(""" QMainWindow { background-color: white; } QWidget { background-color: white; } """) # 创建中心部件和布局 central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) layout.setSpacing(15) layout.setContentsMargins(15, 15, 15, 15) # 设备识别码区域 device_id = get_hardware_id() # 使用common_utils中的函数 self.device_id_widget = DeviceIdWidget(device_id) layout.addWidget(self.device_id_widget) # 会员状态区域 self.member_status = MemberStatusWidget() layout.addWidget(self.member_status) # 激活区域 self.activation_widget = ActivationWidget(self) # 使用 self 作为父组件 layout.addWidget(self.activation_widget) # 使用说明区域 self.instructions = InstructionsWidget() layout.addWidget(self.instructions) # 功能按钮区域 button_layout = QVBoxLayout() button_layout.setSpacing(10) # 刷新授权按钮 - 蓝色 self.refresh_button = QPushButton("刷新 Cursor 编辑器授权") self.refresh_button.clicked.connect(lambda: self.start_task('refresh')) self.refresh_button.setStyleSheet(""" QPushButton { background-color: #007bff; color: white; border: none; padding: 12px; border-radius: 4px; font-size: 14px; } QPushButton:hover { background-color: #0056b3; } QPushButton:disabled { background-color: #ccc; } """) button_layout.addWidget(self.refresh_button) # 实现限制按钮 - 绿色 self.limit_button = QPushButton("实现 Cursor 0.45.x 限制") self.limit_button.clicked.connect(lambda: self.start_task('limit')) self.limit_button.setStyleSheet(""" QPushButton { background-color: #28a745; color: white; border: none; padding: 12px; border-radius: 4px; font-size: 14px; } QPushButton:hover { background-color: #218838; } QPushButton:disabled { background-color: #ccc; } """) button_layout.addWidget(self.limit_button) # 禁用更新按钮 - 红色 self.disable_button = QPushButton("禁用 Cursor 版本更新") self.disable_button.clicked.connect(lambda: self.start_task('disable')) self.disable_button.setStyleSheet(""" QPushButton { background-color: #dc3545; color: white; border: none; padding: 12px; border-radius: 4px; font-size: 14px; } QPushButton:hover { background-color: #c82333; } QPushButton:disabled { background-color: #ccc; } """) button_layout.addWidget(self.disable_button) layout.addLayout(button_layout) # 检查更新按钮 - 灰色 self.check_update_button = QPushButton("检查更新") self.check_update_button.clicked.connect(self.check_update) self.check_update_button.setStyleSheet(""" QPushButton { background-color: #6c757d; color: white; border: none; padding: 8px; border-radius: 4px; margin-top: 5px; font-size: 13px; min-width: 100px; } QPushButton:hover { background-color: #5a6268; } QPushButton:disabled { background-color: #cccccc; } """) layout.addWidget(self.check_update_button) # 状态栏 self.status_bar = StatusBar() layout.addWidget(self.status_bar) # 初始化状态 self.update_member_status() # 检查是否有未完成的更新 self.check_pending_update() def update_member_status(self): """更新会员状态""" status = self.cursor_service.check_activation_status() self.member_status.update_status(status) # 根据激活状态更新按钮可用性 buttons_enabled = status["is_activated"] self.refresh_button.setEnabled(buttons_enabled) self.limit_button.setEnabled(buttons_enabled) self.disable_button.setEnabled(buttons_enabled) def handle_activation(self, code: str): """处理激活码激活""" try: print(f"MainWindow 收到激活请求,激活码:{code}") # 更新状态栏 print("更新状态栏:正在激活...") self.status_bar.set_status("正在激活...") # 禁用激活按钮 print("禁用激活按钮") self.activation_widget.activate_btn.setEnabled(False) # 调用激活服务 print("调用激活服务...") success, msg = self.cursor_service.activate_with_code(code) print(f"激活结果:success={success}, msg={msg}") if success: QMessageBox.information(self, "成功", msg) self.update_member_status() else: QMessageBox.warning(self, "失败", msg) # 清空输入框 self.activation_widget.clear_input() except Exception as e: print(f"激活过程发生错误:{str(e)}") QMessageBox.critical(self, "错误", f"激活过程发生错误:{str(e)}") finally: # 恢复状态栏 print("恢复状态栏和按钮状态") self.status_bar.set_status("就绪") # 恢复激活按钮 self.activation_widget.activate_btn.setEnabled(True) def start_task(self, task_type: str): """启动任务""" # 检查激活状态 if not self.cursor_service.check_activation_status()["is_activated"]: self.activation_widget.show_activation_required() return # 停止当前任务(如果有) if self.current_worker: self.current_worker.stop() # 禁用所有按钮 self.refresh_button.setEnabled(False) self.limit_button.setEnabled(False) self.disable_button.setEnabled(False) # 创建并启动工作线程 if task_type == 'refresh': self.status_bar.set_status("正在刷新授权...") worker = RefreshTokenWorker() worker.progress.connect(self.update_progress) worker.finished.connect(self.handle_result) worker.error.connect(self.handle_error) worker.start() self.current_worker = worker elif task_type == 'limit': self.status_bar.set_status("正在设置版本限制...") worker = ResetWorker() worker.progress.connect(self.update_progress) worker.finished.connect(self.handle_result) worker.error.connect(self.handle_error) worker.start() self.current_worker = worker elif task_type == 'disable': self.status_bar.set_status("正在禁用更新...") worker = DisableWorker() worker.progress.connect(self.update_progress) worker.finished.connect(self.handle_result) worker.error.connect(self.handle_error) worker.start() self.current_worker = worker def update_progress(self, info: dict): """更新进度信息""" self.status_bar.set_status(info.get('message', '处理中...')) def handle_result(self, result: tuple): """处理任务结果""" task_type, data = result success, msg = data if isinstance(data, tuple) else (False, str(data)) if success: QMessageBox.information(self, "成功", msg) else: QMessageBox.warning(self, "失败", msg) # 重新启用按钮 self.update_member_status() self.status_bar.set_status("就绪") # 清理工作线程 if self.current_worker: self.current_worker.stop() self.current_worker = None def handle_error(self, error_msg: str): """处理错误""" self.status_bar.set_status("发生错误") QMessageBox.critical(self, "错误", f"操作失败:{error_msg}") # 重新启用按钮 self.update_member_status() # 清理工作线程 if self.current_worker: self.current_worker.stop() self.current_worker = None def check_update(self): """手动检查更新""" try: # 禁用更新按钮 self.check_update_button.setEnabled(False) self.status_bar.set_status("正在检查更新...") # 检查更新 has_update, msg, update_info = self.version_manager.check_update() if has_update and update_info: # 创建自定义消息框 msg_box = QMessageBox(self) msg_box.setWindowTitle("发现新版本") msg_box.setIcon(QMessageBox.Information) # 设置文本 text = ( f"

发现新版本: {update_info['version']}

" f"

更新内容:

" f"

{update_info['message']}

" ) msg_box.setText(text) # 设置按钮 msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) yes_btn = msg_box.button(QMessageBox.Yes) no_btn = msg_box.button(QMessageBox.No) yes_btn.setText("立即下载") no_btn.setText("暂不更新") # 设置样式 msg_box.setStyleSheet(""" QMessageBox { background-color: white; } QPushButton { min-width: 85px; min-height: 24px; padding: 4px 15px; border-radius: 3px; background-color: #007bff; color: white; border: none; } QPushButton:hover { background-color: #0056b3; } QPushButton[text='暂不更新'] { background-color: #6c757d; } QPushButton[text='暂不更新']:hover { background-color: #5a6268; } """) # 设置最小宽度 msg_box.setMinimumWidth(400) # 显示对话框 if msg_box.exec_() == QMessageBox.Yes: QDesktopServices.openUrl(QUrl(update_info['download_url'])) else: # 创建自定义消息框 msg_box = QMessageBox(self) msg_box.setWindowTitle("检查更新") msg_box.setIcon(QMessageBox.Information) msg_box.setText(f"

{msg}

") msg_box.setStyleSheet(""" QMessageBox { background-color: white; } QPushButton { min-width: 85px; min-height: 24px; padding: 4px 15px; border-radius: 3px; background-color: #007bff; color: white; border: none; } QPushButton:hover { background-color: #0056b3; } """) msg_box.setMinimumWidth(300) msg_box.exec_() except Exception as e: error_box = QMessageBox(self) error_box.setWindowTitle("错误") error_box.setIcon(QMessageBox.Warning) error_box.setText(f"

检查更新失败: {str(e)}

") error_box.setStyleSheet(""" QMessageBox { background-color: white; } QPushButton { min-width: 85px; min-height: 24px; padding: 4px 15px; border-radius: 3px; background-color: #dc3545; color: white; border: none; } QPushButton:hover { background-color: #c82333; } """) error_box.setMinimumWidth(300) error_box.exec_() finally: # 恢复按钮状态和状态栏 self.check_update_button.setEnabled(True) self.status_bar.set_status("就绪") def check_pending_update(self): """检查是否有未完成的更新""" try: update_info = self.version_manager.get_last_update_info() if update_info: reply = QMessageBox.question( self, "未完成的更新", f"检测到未完成的更新: {update_info['version']}\n\n" f"更新内容:\n{update_info['message']}\n\n" "是否现在更新?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) if reply == QMessageBox.Yes: # 打开下载链接 QDesktopServices.openUrl(QUrl(update_info['download_url'])) else: # 清除更新信息 self.version_manager.clear_update_info() except Exception as e: self.logger.error(f"检查未完成更新失败: {str(e)}") def closeEvent(self, event): """窗口关闭事件""" if self.tray_icon.isVisible(): # 最小化到托盘 QMessageBox.information( self, "提示", "程序将继续在后台运行,双击托盘图标可以重新打开主窗口。" ) self.hide() event.ignore() else: # 停止所有正在运行的任务 if self.current_worker: self.current_worker.stop() event.accept()