667 lines
24 KiB
Python
667 lines
24 KiB
Python
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'<span style="color: #17a2b8;">{step_title}</span>:{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"<p style='font-size: 14px;'><b>发现新版本: {update_info['version']}</b></p>"
|
||
f"<p style='font-size: 13px; margin: 10px 0;'><b>更新内容:</b></p>"
|
||
f"<p style='font-size: 13px; white-space: pre-wrap;'>{update_info['message']}</p>"
|
||
)
|
||
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"<p style='font-size: 13px;'>{msg}</p>")
|
||
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"<p style='font-size: 13px;'>检查更新失败: {str(e)}</p>")
|
||
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() |