Files
tingquanzhushou/gui/windows/main_window.py
2025-02-20 20:20:19 +08:00

667 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()