Files
nezhacursormac/gui/main_mac.py

538 lines
20 KiB
Python

# -*- coding: utf-8 -*-
import sys
import os
import traceback
from datetime import datetime
def get_app_path():
"""获取应用程序路径"""
if getattr(sys, 'frozen', False):
# 如果是打包后的应用
return os.path.dirname(sys.executable)
else:
# 如果是开发环境
return os.path.dirname(os.path.abspath(__file__))
def setup_logging():
"""设置日志"""
app_path = get_app_path()
log_dir = os.path.join(app_path, 'logs')
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, 'app.log')
error_log = os.path.join(app_path, 'error.log')
# 配置日志
logging.basicConfig(
filename=log_file,
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 设置错误日志处理
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
error_msg = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
try:
with open(error_log, 'a') as f:
f.write(f"\n{'-'*60}\n")
f.write(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(error_msg)
logging.error(f"Uncaught exception:\n{error_msg}")
except Exception as e:
print(f"Error writing to log file: {str(e)}")
print(error_msg)
sys.excepthook = handle_exception
# 添加父目录到系统路径
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
sys.path.append(parent_dir)
try:
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QPushButton, QLabel, QLineEdit, QTextEdit, QMessageBox,
QHBoxLayout, QFrame, QStackedWidget)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QFont, QIcon, QPalette, QColor
from update_cursor_token import CursorTokenUpdater
from logger import logging
except Exception as e:
error_path = os.path.join(get_app_path(), 'error.log')
with open(error_path, 'w') as f:
f.write(f"Import Error: {str(e)}\n")
f.write(traceback.format_exc())
sys.exit(1)
# macOS 风格的颜色
MACOS_COLORS = {
'background': '#F5F5F7', # 浅灰色背景
'button': '#0066CC', # 蓝色按钮
'button_hover': '#0052A3', # 深蓝色悬停
'button_pressed': '#003D7A', # 更深的蓝色按下
'text': '#1D1D1F', # 深色文字
'frame': '#FFFFFF', # 白色框架
'input': '#FFFFFF', # 白色输入框
'input_text': '#1D1D1F', # 深色输入文字
'border': '#E5E5E5' # 边框颜色
}
class UpdateWorker(QThread):
"""后台更新线程"""
finished = pyqtSignal(bool, str)
progress = pyqtSignal(str)
def __init__(self, updater):
super().__init__()
self.updater = updater
def run(self):
try:
success = self.updater.full_update_process()
if success:
self.finished.emit(True, "更新成功!")
else:
self.finished.emit(False, "更新失败,请查看日志")
except Exception as e:
self.finished.emit(False, f"发生错误: {str(e)}")
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
try:
logging.info("正在初始化主窗口...")
self.updater = CursorTokenUpdater()
self.init_ui()
# 检查会员状态
self.check_member_status()
# 启动定时检测
self.setup_status_timer()
logging.info("主窗口初始化完成")
except Exception as e:
logging.error(f"初始化主窗口时发生错误: {str(e)}")
QMessageBox.critical(self, "错误", f"初始化失败: {str(e)}")
def init_ui(self):
# 设置窗口基本属性
self.setWindowTitle('听泉助手')
self.setMinimumSize(600, 500)
# 设置macOS风格的样式
self.setStyleSheet(f"""
QMainWindow {{
background-color: {MACOS_COLORS['background']};
}}
QPushButton {{
background-color: {MACOS_COLORS['button']};
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 13px;
min-width: 100px;
}}
QPushButton:hover {{
background-color: {MACOS_COLORS['button_hover']};
}}
QPushButton:pressed {{
background-color: {MACOS_COLORS['button_pressed']};
}}
QLabel {{
color: {MACOS_COLORS['text']};
font-size: 13px;
}}
QLabel[title="true"] {{
font-size: 24px;
font-weight: bold;
}}
QTextEdit {{
background-color: {MACOS_COLORS['input']};
color: {MACOS_COLORS['input_text']};
border: 1px solid {MACOS_COLORS['border']};
border-radius: 6px;
padding: 8px;
font-size: 13px;
}}
QLineEdit {{
background-color: {MACOS_COLORS['input']};
color: {MACOS_COLORS['input_text']};
border: 1px solid {MACOS_COLORS['border']};
border-radius: 6px;
padding: 8px;
font-size: 13px;
}}
QFrame {{
background-color: {MACOS_COLORS['frame']};
border: 1px solid {MACOS_COLORS['border']};
border-radius: 8px;
}}
QMessageBox {{
background-color: {MACOS_COLORS['background']};
}}
QMessageBox QPushButton {{
min-width: 80px;
padding: 6px 12px;
}}
""")
# 创建主窗口部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
layout.setSpacing(16)
layout.setContentsMargins(20, 20, 20, 20)
# 标题
title_label = QLabel("听泉助手")
title_label.setProperty("title", "true")
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
# 设备ID显示区域
device_frame = QFrame()
device_layout = QHBoxLayout(device_frame)
device_layout.setContentsMargins(16, 16, 16, 16)
device_id_label = QLabel("设备标识:")
self.device_id_text = QLineEdit()
self.device_id_text.setReadOnly(True)
try:
hardware_id = self.updater.hardware_id
logging.info(f"获取到硬件ID: {hardware_id}")
self.device_id_text.setText(hardware_id)
except Exception as e:
logging.error(f"获取硬件ID失败: {str(e)}")
self.device_id_text.setText("获取失败")
copy_button = QPushButton("复制ID")
copy_button.setMaximumWidth(100)
copy_button.clicked.connect(self.copy_device_id)
device_layout.addWidget(device_id_label)
device_layout.addWidget(self.device_id_text)
device_layout.addWidget(copy_button)
layout.addWidget(device_frame)
# 会员状态区域
member_frame = QFrame()
member_layout = QVBoxLayout(member_frame)
member_layout.setContentsMargins(16, 16, 16, 16)
member_title = QLabel("会员状态")
member_title.setAlignment(Qt.AlignCenter)
member_layout.addWidget(member_title)
self.member_info = QTextEdit()
self.member_info.setReadOnly(True)
self.member_info.setFixedHeight(80)
self.member_info.setText("会员状态: 正常\n到期时间: 2025-02-22 20:44:23")
member_layout.addWidget(self.member_info)
layout.addWidget(member_frame)
# 激活区域
activate_frame = QFrame()
activate_layout = QVBoxLayout(activate_frame)
activate_layout.setContentsMargins(16, 16, 16, 16)
activate_title = QLabel("激活会员")
activate_title.setAlignment(Qt.AlignCenter)
input_layout = QHBoxLayout()
self.activate_input = QLineEdit()
self.activate_input.setPlaceholderText("请输入激活码")
activate_button = QPushButton("激活")
activate_button.setMaximumWidth(100)
activate_button.clicked.connect(self.activate_license)
input_layout.addWidget(self.activate_input)
input_layout.addWidget(activate_button)
activate_layout.addWidget(activate_title)
activate_layout.addLayout(input_layout)
layout.addWidget(activate_frame)
# 功能按钮区域
button_frame = QFrame()
button_layout = QVBoxLayout(button_frame)
button_layout.setSpacing(12)
button_layout.setContentsMargins(16, 16, 16, 16)
self.update_button = QPushButton("刷新授权")
self.update_button.clicked.connect(self.start_update)
self.reset_button = QPushButton("重置机器码")
self.reset_button.clicked.connect(self.reset_machine)
self.disable_update_button = QPushButton("禁用更新")
self.disable_update_button.clicked.connect(self.disable_cursor_update)
button_layout.addWidget(self.update_button)
button_layout.addWidget(self.reset_button)
button_layout.addWidget(self.disable_update_button)
layout.addWidget(button_frame)
def copy_device_id(self):
clipboard = QApplication.clipboard()
clipboard.setText(self.device_id_text.text())
QMessageBox.information(self, "成功", "设备ID已复制到剪贴板")
def append_log(self, text):
self.member_info.append(text)
def start_update(self):
self.update_button.setEnabled(False)
self.worker = UpdateWorker(self.updater)
self.worker.finished.connect(self.update_finished)
self.worker.start()
def update_finished(self, success, message):
self.update_button.setEnabled(True)
if success:
QMessageBox.information(self, "成功", message)
else:
QMessageBox.warning(self, "失败", message)
def reset_machine(self):
try:
self.updater.reset_machine_id()
QMessageBox.information(self, "成功", "机器ID已重置")
except Exception as e:
QMessageBox.critical(self, "错误", f"重置失败: {str(e)}")
def activate_license(self):
"""激活许可证"""
try:
license_key = self.activate_input.text().strip()
if not license_key:
QMessageBox.warning(self, "提示", "请输入激活码")
return
# 禁用激活按钮,防止重复点击
self.activate_input.setEnabled(False)
self.update_button.setEnabled(False)
# 显示处理中的提示
self.member_info.clear()
self.member_info.append("正在激活,请稍候...")
QApplication.processEvents()
# 调用激活接口
success, message, account_info = self.updater.check_activation_code(license_key)
if success:
# 更新界面显示
self.member_info.clear()
self.member_info.append(f"会员状态: 已激活")
self.member_info.append(f"到期时间: {account_info['expire_time']}")
self.member_info.append(f"剩余天数: {account_info['days_left']}")
# 清空激活码输入框并更新提示(不禁用输入框)
self.activate_input.clear()
self.activate_input.setPlaceholderText("输入激活码可叠加时长")
# 启用更新按钮
self.update_button.setEnabled(True)
# 显示成功消息
QMessageBox.information(self, "激活成功",
f"激活成功!\n"
f"到期时间: {account_info['expire_time']}\n"
f"剩余天数: {account_info['days_left']}\n"
f"您可以继续输入其他激活码叠加时长")
logging.info(f"设备激活成功,到期时间: {account_info['expire_time']}")
else:
# 恢复激活输入框
self.activate_input.setPlaceholderText("请输入激活码")
# 更新状态显示
self.member_info.clear()
self.member_info.append("会员状态: 未激活")
self.member_info.append("请输入激活码进行激活")
QMessageBox.warning(self, "激活失败", message)
logging.error(f"激活失败: {message}")
except Exception as e:
# 恢复激活输入框
self.activate_input.setPlaceholderText("请输入激活码")
# 更新状态显示
self.member_info.clear()
self.member_info.append("会员状态: 激活失败")
self.member_info.append("请重新尝试")
logging.error(f"激活过程中发生错误: {str(e)}")
QMessageBox.critical(self, "错误", f"激活过程发生错误: {str(e)}")
finally:
# 根据激活状态设置更新按钮状态
self.update_button.setEnabled(success if 'success' in locals() else False)
def disable_cursor_update(self):
try:
# 这里添加禁用更新逻辑
QMessageBox.information(self, "成功", "已禁用Cursor更新")
except Exception as e:
QMessageBox.critical(self, "错误", f"操作失败: {str(e)}")
def check_member_status(self):
"""检查会员状态"""
try:
logging.info("正在检查会员状态...")
self.member_info.clear()
self.member_info.append("正在检查会员状态...")
QApplication.processEvents()
# 调用API检查状态
success, message, account_info = self.updater.check_member_status()
if success and account_info:
# 更新会员信息显示
self.member_info.clear()
self.member_info.append(f"会员状态: 已激活")
self.member_info.append(f"到期时间: {account_info['expire_time']}")
self.member_info.append(f"剩余天数: {account_info['days_left']}")
# 更新激活输入框提示(不禁用输入框)
self.activate_input.setPlaceholderText("输入激活码可叠加时长")
# 启用更新按钮
self.update_button.setEnabled(True)
logging.info(f"会员状态检查完成 - 到期时间: {account_info['expire_time']}")
else:
# 显示未激活状态
self.member_info.clear()
self.member_info.append("会员状态: 未激活")
self.member_info.append("请输入激活码进行激活")
# 更新激活输入框提示
self.activate_input.setPlaceholderText("请输入激活码")
# 禁用更新按钮
self.update_button.setEnabled(False)
logging.warning("会员状态检查结果:未激活")
if message and "设备未激活" not in message:
QMessageBox.warning(self, "提示", message)
except Exception as e:
self.member_info.clear()
self.member_info.append("会员状态: 检查失败")
self.member_info.append("请稍后重试")
# 启用激活输入框和按钮
self.activate_input.setEnabled(True)
self.activate_input.setPlaceholderText("请输入激活码")
# 禁用更新按钮
self.update_button.setEnabled(False)
logging.error(f"检查会员状态时发生错误: {str(e)}")
QMessageBox.warning(self, "错误", f"检查会员状态失败: {str(e)}")
def setup_status_timer(self):
"""设置定时检测会员状态"""
try:
self.status_timer = QTimer(self)
self.status_timer.timeout.connect(self.silent_check_member_status)
# 设置3分钟检测一次 (180000毫秒)
self.status_timer.start(180000)
logging.info("会员状态定时检测已启动")
except Exception as e:
logging.error(f"设置定时器时发生错误: {str(e)}")
def silent_check_member_status(self):
"""静默检查会员状态"""
try:
logging.info("开始静默检查会员状态...")
success, message, account_info = self.updater.check_member_status()
if success and account_info:
# 更新会员信息显示
self.member_info.clear()
self.member_info.append(f"会员状态: 已激活")
self.member_info.append(f"到期时间: {account_info['expire_time']}")
self.member_info.append(f"剩余天数: {account_info['days_left']}")
# 禁用激活输入框和按钮
self.activate_input.setEnabled(False)
self.activate_input.setPlaceholderText("已激活")
# 启用更新按钮
self.update_button.setEnabled(True)
logging.info(f"静默检查完成 - 会员状态正常,到期时间: {account_info['expire_time']}")
else:
# 显示未激活状态
self.member_info.clear()
self.member_info.append("会员状态: 未激活")
self.member_info.append("请输入激活码进行激活")
# 启用激活输入框和按钮
self.activate_input.setEnabled(True)
self.activate_input.setPlaceholderText("请输入激活码")
# 禁用更新按钮
self.update_button.setEnabled(False)
logging.warning("静默检查结果:会员未激活")
except Exception as e:
logging.error(f"静默检查会员状态时发生错误: {str(e)}")
# 静默检查出错时不显示错误提示,只记录日志
def closeEvent(self, event):
"""窗口关闭事件"""
try:
# 停止定时器
if hasattr(self, 'status_timer'):
self.status_timer.stop()
logging.info("应用程序正常关闭")
event.accept()
except Exception as e:
logging.error(f"关闭窗口时发生错误: {str(e)}")
event.accept()
def main():
try:
# 设置日志
setup_logging()
logging.info("应用程序启动")
app = QApplication(sys.argv)
app.setApplicationName("听泉助手")
app.setOrganizationName("听泉")
app.setOrganizationDomain("cursor.pro")
try:
window = MainWindow()
window.show()
logging.info("主窗口已显示")
return app.exec_()
except Exception as e:
logging.error(f"主窗口创建失败: {str(e)}")
logging.error(traceback.format_exc())
QMessageBox.critical(None, "错误", f"程序启动失败: {str(e)}")
return 1
except Exception as e:
error_path = os.path.join(get_app_path(), 'error.log')
with open(error_path, 'a') as f:
f.write(f"\nApplication Error: {str(e)}\n")
f.write(traceback.format_exc())
return 1
if __name__ == '__main__':
sys.exit(main())