feat: 优化GUI界面和用户体验
1. 优化按钮样式和布局:增加按钮尺寸和内边距,使用不同颜色区分功能按钮,添加按钮悬停和点击效果,优化按钮间距和对齐方式 2. 添加会员状态检查:实现状态缓存机制减少API请求,添加状态自动更新定时器,根据剩余时间动态调整更新间隔 3. 改进加载提示:添加加载对话框组件,实现异步API请求机制,优化加载状态显示 4. 优化购买提示界面:美化提示框样式和排版,添加多个购买渠道信息,优化文本对齐和间距 5. 其他改进:修复tkinter相关打包问题,优化错误提示信息,改进日志记录,完善异常处理
This commit is contained in:
@@ -5,8 +5,9 @@ import os
|
||||
from PIL import Image
|
||||
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QLineEdit, QPushButton, QFrame, QTextEdit,
|
||||
QMessageBox, QApplication, QSystemTrayIcon, QMenu)
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
QMessageBox, QApplication, QSystemTrayIcon, QMenu,
|
||||
QDialog, QProgressBar)
|
||||
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon, QPixmap
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
@@ -23,12 +24,78 @@ def get_version():
|
||||
logging.error(f"读取版本号失败: {str(e)}")
|
||||
return "未知版本"
|
||||
|
||||
class LoadingDialog(QDialog):
|
||||
"""加载对话框"""
|
||||
def __init__(self, parent=None, message="请稍候..."):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("处理中")
|
||||
self.setFixedSize(300, 100)
|
||||
self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
# 添加消息标签
|
||||
self.message_label = QLabel(message)
|
||||
self.message_label.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(self.message_label)
|
||||
|
||||
# 添加进度条
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setTextVisible(False)
|
||||
self.progress_bar.setRange(0, 0) # 设置为循环模式
|
||||
layout.addWidget(self.progress_bar)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# 设置样式
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
}
|
||||
QProgressBar {
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
QProgressBar::chunk {
|
||||
background-color: #0d6efd;
|
||||
width: 10px;
|
||||
margin: 0.5px;
|
||||
}
|
||||
""")
|
||||
|
||||
class ApiWorker(QThread):
|
||||
"""API请求工作线程"""
|
||||
finished = pyqtSignal(tuple) # 发送结果信号
|
||||
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
super().__init__()
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
result = self.func(*self.args, **self.kwargs)
|
||||
self.finished.emit((True, result))
|
||||
except Exception as e:
|
||||
self.finished.emit((False, str(e)))
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.config = Config()
|
||||
self.switcher = AccountSwitcher()
|
||||
|
||||
# 添加激活状态缓存
|
||||
self._activation_status = None # 缓存的激活状态
|
||||
self._status_timer = None # 状态更新定时器
|
||||
|
||||
version = get_version()
|
||||
cursor_version = self.switcher.get_cursor_version()
|
||||
self.setWindowTitle(f"听泉Cursor助手 v{version} (本机Cursor版本: {cursor_version})")
|
||||
@@ -110,15 +177,52 @@ class MainWindow(QMainWindow):
|
||||
# 操作按钮区域
|
||||
btn_frame = QFrame()
|
||||
btn_layout = QVBoxLayout(btn_frame)
|
||||
|
||||
# 设置按钮样式
|
||||
button_style = """
|
||||
QPushButton {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
min-width: 200px;
|
||||
margin: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #0a58ca;
|
||||
}
|
||||
"""
|
||||
|
||||
# 刷新授权按钮
|
||||
refresh_btn = QPushButton("刷新Cursor编辑器授权")
|
||||
refresh_btn.setStyleSheet(button_style)
|
||||
refresh_btn.clicked.connect(self.refresh_cursor_auth)
|
||||
refresh_btn.setMinimumHeight(50)
|
||||
btn_layout.addWidget(refresh_btn)
|
||||
|
||||
# 突破限制按钮
|
||||
bypass_btn = QPushButton("突破Cursor0.45.x限制")
|
||||
bypass_btn.setStyleSheet(button_style.replace("#0d6efd", "#198754").replace("#0b5ed7", "#157347").replace("#0a58ca", "#146c43"))
|
||||
bypass_btn.clicked.connect(self.dummy_function)
|
||||
bypass_btn.setMinimumHeight(50)
|
||||
btn_layout.addWidget(bypass_btn)
|
||||
|
||||
# 禁用更新按钮
|
||||
disable_update_btn = QPushButton("禁用Cursor版本更新")
|
||||
disable_update_btn.setStyleSheet(button_style.replace("#0d6efd", "#dc3545").replace("#0b5ed7", "#bb2d3b").replace("#0a58ca", "#b02a37"))
|
||||
disable_update_btn.clicked.connect(self.disable_cursor_update)
|
||||
disable_update_btn.setMinimumHeight(50)
|
||||
btn_layout.addWidget(disable_update_btn)
|
||||
|
||||
# 设置按钮间距
|
||||
btn_layout.setSpacing(10)
|
||||
btn_layout.setContentsMargins(20, 10, 20, 10)
|
||||
|
||||
main_layout.addWidget(btn_frame)
|
||||
|
||||
# 启动时检查一次状态
|
||||
@@ -144,6 +248,10 @@ class MainWindow(QMainWindow):
|
||||
else:
|
||||
event.accept()
|
||||
|
||||
if self._status_timer:
|
||||
self._status_timer.stop()
|
||||
super().closeEvent(event)
|
||||
|
||||
def copy_device_id(self):
|
||||
"""复制设备ID到剪贴板"""
|
||||
if not self.check_status():
|
||||
@@ -151,32 +259,208 @@ class MainWindow(QMainWindow):
|
||||
QApplication.clipboard().setText(self.hardware_id_edit.text())
|
||||
QMessageBox.information(self, "提示", "设备ID已复制到剪贴板")
|
||||
|
||||
def show_loading_dialog(self, message="请稍候..."):
|
||||
"""显示加载对话框"""
|
||||
self.loading_dialog = LoadingDialog(self, message)
|
||||
self.loading_dialog.show()
|
||||
|
||||
def hide_loading_dialog(self):
|
||||
"""隐藏加载对话框"""
|
||||
if hasattr(self, 'loading_dialog'):
|
||||
self.loading_dialog.hide()
|
||||
self.loading_dialog.deleteLater()
|
||||
|
||||
def activate_account(self):
|
||||
"""激活账号"""
|
||||
code = self.activation_edit.text().strip()
|
||||
if not code:
|
||||
QMessageBox.warning(self, "提示", "请输入激活码")
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle("提示")
|
||||
msg.setText("请输入激活码")
|
||||
msg.setIcon(QMessageBox.Warning)
|
||||
msg.setStyleSheet("""
|
||||
QMessageBox {
|
||||
background-color: #f8f9fa;
|
||||
min-width: 400px;
|
||||
padding: 20px;
|
||||
}
|
||||
QMessageBox QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
min-height: 40px;
|
||||
padding: 10px;
|
||||
qproperty-alignment: AlignCenter;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 25px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
min-width: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
""")
|
||||
msg.exec_()
|
||||
return
|
||||
|
||||
try:
|
||||
success, message, account_info = self.switcher.check_activation_code(code)
|
||||
# 显示加载对话框
|
||||
self.show_loading_dialog("正在验证激活码,请稍候...")
|
||||
|
||||
# 创建工作线程
|
||||
self.worker = ApiWorker(self.switcher.check_activation_code, code)
|
||||
self.worker.finished.connect(self.on_activation_complete)
|
||||
self.worker.start()
|
||||
|
||||
except Exception as e:
|
||||
self.hide_loading_dialog()
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle("错误")
|
||||
msg.setText(f"激活失败: {str(e)}")
|
||||
msg.setIcon(QMessageBox.Critical)
|
||||
msg.setStyleSheet("""
|
||||
QMessageBox {
|
||||
background-color: #f8f9fa;
|
||||
min-width: 450px;
|
||||
padding: 20px;
|
||||
}
|
||||
QMessageBox QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
QMessageBox QLabel:first-child {
|
||||
font-weight: bold;
|
||||
color: #dc3545;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 25px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
min-width: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #bb2d3b;
|
||||
}
|
||||
""")
|
||||
msg.exec_()
|
||||
|
||||
def on_activation_complete(self, result):
|
||||
"""激活完成回调"""
|
||||
success, data = result
|
||||
self.hide_loading_dialog()
|
||||
|
||||
if isinstance(data, tuple):
|
||||
success, message, account_info = data
|
||||
if success:
|
||||
# 更新会员信息显示
|
||||
self.update_status_display(account_info)
|
||||
QMessageBox.information(self, "激活成功", "激活成功!\n" + message)
|
||||
# 更新激活状态缓存
|
||||
self._activation_status = True
|
||||
|
||||
# 更新状态定时器
|
||||
days_left = account_info.get('days_left', 0)
|
||||
seconds_left = days_left * 24 * 60 * 60
|
||||
if self._status_timer:
|
||||
self._status_timer.stop()
|
||||
self._status_timer = QTimer(self)
|
||||
self._status_timer.setSingleShot(True)
|
||||
self._status_timer.timeout.connect(self.check_status)
|
||||
update_interval = min(seconds_left - 60, 24 * 60 * 60) # 最长1天更新一次
|
||||
if update_interval > 0:
|
||||
self._status_timer.start(update_interval * 1000) # 转换为毫秒
|
||||
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle("激活成功")
|
||||
msg.setText("激活成功!")
|
||||
msg.setInformativeText(message)
|
||||
msg.setIcon(QMessageBox.Information)
|
||||
msg.setStyleSheet("""
|
||||
QMessageBox {
|
||||
background-color: #f8f9fa;
|
||||
min-width: 500px;
|
||||
padding: 20px;
|
||||
}
|
||||
QMessageBox QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
QMessageBox QLabel:first-child {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
color: #198754;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #198754;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 25px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
min-width: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #157347;
|
||||
}
|
||||
""")
|
||||
msg.exec_()
|
||||
# 清空激活码输入框
|
||||
self.activation_edit.clear()
|
||||
else:
|
||||
QMessageBox.critical(self, "激活失败", message)
|
||||
|
||||
# 激活后检查一次状态
|
||||
self.check_status()
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"激活失败: {str(e)}")
|
||||
# 出错后也检查状态
|
||||
self.check_status()
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle("激活失败")
|
||||
msg.setText(message)
|
||||
msg.setIcon(QMessageBox.Critical)
|
||||
msg.setStyleSheet("""
|
||||
QMessageBox {
|
||||
background-color: #f8f9fa;
|
||||
min-width: 450px;
|
||||
padding: 20px;
|
||||
}
|
||||
QMessageBox QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
QMessageBox QLabel:first-child {
|
||||
font-weight: bold;
|
||||
color: #dc3545;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 25px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
min-width: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #bb2d3b;
|
||||
}
|
||||
""")
|
||||
msg.exec_()
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", f"激活失败: {data}")
|
||||
|
||||
# 激活后检查一次状态
|
||||
self.check_status()
|
||||
|
||||
def update_status_display(self, status_info: dict):
|
||||
"""更新状态显示"""
|
||||
# 打印API返回的原始数据
|
||||
@@ -227,42 +511,168 @@ class MainWindow(QMainWindow):
|
||||
self.status_text.setPlainText("\n".join(status_lines))
|
||||
|
||||
def check_status(self):
|
||||
"""检查会员状态"""
|
||||
"""检查会员状态(从API获取)"""
|
||||
try:
|
||||
status = self.switcher.get_member_status()
|
||||
if status:
|
||||
self.update_status_display(status)
|
||||
return status.get('status') != 'inactive'
|
||||
else:
|
||||
# 更新为未激活状态
|
||||
inactive_status = {
|
||||
"hardware_id": self.switcher.hardware_id,
|
||||
"expire_time": "",
|
||||
"days_left": 0,
|
||||
"total_days": 0,
|
||||
"status": "inactive",
|
||||
"activation_records": []
|
||||
}
|
||||
self.update_status_display(inactive_status)
|
||||
return False
|
||||
|
||||
# 显示加载对话框
|
||||
self.show_loading_dialog("正在检查会员状态,请稍候...")
|
||||
|
||||
# 创建工作线程
|
||||
self.worker = ApiWorker(self.switcher.get_member_status)
|
||||
self.worker.finished.connect(self.on_status_check_complete)
|
||||
self.worker.start()
|
||||
|
||||
except Exception as e:
|
||||
self.hide_loading_dialog()
|
||||
QMessageBox.critical(self, "错误", f"检查状态失败: {str(e)}")
|
||||
self._activation_status = False
|
||||
return False
|
||||
|
||||
def on_status_check_complete(self, result):
|
||||
"""状态检查完成回调"""
|
||||
success, data = result
|
||||
self.hide_loading_dialog()
|
||||
|
||||
if success and data:
|
||||
self.update_status_display(data)
|
||||
# 更新缓存的激活状态
|
||||
self._activation_status = data.get('status') == 'active'
|
||||
|
||||
# 设置状态更新定时器
|
||||
if self._activation_status:
|
||||
# 获取剩余时间(秒)
|
||||
days_left = data.get('days_left', 0)
|
||||
seconds_left = days_left * 24 * 60 * 60
|
||||
|
||||
# 创建定时器,在到期前1分钟更新状态
|
||||
if self._status_timer:
|
||||
self._status_timer.stop()
|
||||
self._status_timer = QTimer(self)
|
||||
self._status_timer.setSingleShot(True)
|
||||
self._status_timer.timeout.connect(self.check_status)
|
||||
update_interval = min(seconds_left - 60, 24 * 60 * 60) # 最长1天更新一次
|
||||
if update_interval > 0:
|
||||
self._status_timer.start(update_interval * 1000) # 转换为毫秒
|
||||
|
||||
return self._activation_status
|
||||
else:
|
||||
# 更新为未激活状态
|
||||
inactive_status = {
|
||||
"hardware_id": self.switcher.hardware_id,
|
||||
"expire_time": "",
|
||||
"days_left": 0,
|
||||
"total_days": 0,
|
||||
"status": "inactive",
|
||||
"activation_records": []
|
||||
}
|
||||
self.update_status_display(inactive_status)
|
||||
self._activation_status = False
|
||||
return False
|
||||
|
||||
def check_activation_status(self) -> bool:
|
||||
"""检查是否已激活(使用缓存)
|
||||
|
||||
Returns:
|
||||
bool: 是否已激活
|
||||
"""
|
||||
if self._activation_status is None:
|
||||
# 首次检查,从API获取
|
||||
return self.check_status()
|
||||
|
||||
if not self._activation_status:
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle("会员未激活")
|
||||
msg.setText("您还未激活会员或会员已过期")
|
||||
msg.setInformativeText(
|
||||
"获取会员激活码,请通过以下方式:\n\n"
|
||||
" • 官方自助网站:cursor.nosqli.com\n"
|
||||
" • 微信客服:behikcigar\n"
|
||||
" • 闲鱼店铺:xxx\n\n"
|
||||
"————————————————————\n"
|
||||
"诚招代理商,欢迎加盟合作!"
|
||||
)
|
||||
msg.setIcon(QMessageBox.Information)
|
||||
msg.setStyleSheet("""
|
||||
QMessageBox {
|
||||
background-color: #f8f9fa;
|
||||
min-width: 400px;
|
||||
padding: 20px;
|
||||
}
|
||||
QMessageBox QLabel {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
QMessageBox QLabel:first-child {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
color: #0d6efd;
|
||||
qproperty-alignment: AlignCenter;
|
||||
}
|
||||
QMessageBox QLabel:last-child {
|
||||
font-family: 'Microsoft YaHei', sans-serif;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
padding: 15px;
|
||||
margin: 5px;
|
||||
line-height: 1.8;
|
||||
qproperty-alignment: AlignLeft;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 25px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
min-width: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
""")
|
||||
msg.exec_()
|
||||
|
||||
return self._activation_status
|
||||
|
||||
def refresh_cursor_auth(self):
|
||||
"""刷新Cursor授权"""
|
||||
if not self.check_activation_status():
|
||||
return
|
||||
|
||||
try:
|
||||
success, message = self.switcher.refresh_cursor_auth()
|
||||
# 显示加载对话框
|
||||
self.show_loading_dialog("正在刷新授权,请稍候...")
|
||||
|
||||
# 创建工作线程
|
||||
self.worker = ApiWorker(self.switcher.refresh_cursor_auth)
|
||||
self.worker.finished.connect(self.on_refresh_auth_complete)
|
||||
self.worker.start()
|
||||
|
||||
except Exception as e:
|
||||
self.hide_loading_dialog()
|
||||
QMessageBox.critical(self, "错误", f"刷新授权失败: {str(e)}")
|
||||
|
||||
def on_refresh_auth_complete(self, result):
|
||||
"""刷新授权完成回调"""
|
||||
success, data = result
|
||||
self.hide_loading_dialog()
|
||||
|
||||
if isinstance(data, tuple):
|
||||
success, message = data
|
||||
if success:
|
||||
QMessageBox.information(self, "成功", message)
|
||||
else:
|
||||
QMessageBox.critical(self, "失败", message)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"刷新授权失败: {str(e)}")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", f"刷新授权失败: {data}")
|
||||
|
||||
def disable_cursor_update(self):
|
||||
"""禁用Cursor更新"""
|
||||
if not self.check_activation_status():
|
||||
return
|
||||
|
||||
try:
|
||||
success, message = self.switcher.disable_cursor_update()
|
||||
if success:
|
||||
@@ -273,5 +683,8 @@ class MainWindow(QMainWindow):
|
||||
QMessageBox.critical(self, "错误", f"禁用更新失败: {str(e)}")
|
||||
|
||||
def dummy_function(self):
|
||||
"""占位函数"""
|
||||
"""突破版本限制"""
|
||||
if not self.check_activation_status():
|
||||
return
|
||||
|
||||
QMessageBox.information(self, "提示", "此功能暂未实现")
|
||||
Reference in New Issue
Block a user