可打包运行版本,该版本具有模块化特性。如果重置脚本换了,直接改相应类就可以了
This commit is contained in:
1
gui/__init__.py
Normal file
1
gui/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""GUI 包"""
|
||||
1
gui/components/__init__.py
Normal file
1
gui/components/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""GUI 组件包"""
|
||||
637
gui/components/widgets.py
Normal file
637
gui/components/widgets.py
Normal file
@@ -0,0 +1,637 @@
|
||||
from typing import Dict
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QTextEdit, QLabel, QFrame, QLineEdit, QMessageBox, QApplication, QDialog, QStyle, QProgressBar
|
||||
)
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QTimer, QPropertyAnimation, QEasingCurve,
|
||||
QPoint, QRect, QSize, pyqtProperty
|
||||
)
|
||||
from PyQt5.QtGui import QPainter, QColor, QPen
|
||||
|
||||
class ActivationStatusWidget(QFrame):
|
||||
"""激活状态显示组件"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# 状态标题
|
||||
title_label = QLabel("激活状态", self)
|
||||
title_label.setStyleSheet("font-size: 16px; font-weight: bold;")
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# 状态信息
|
||||
self.status_label = QLabel(self)
|
||||
self.status_label.setStyleSheet("font-size: 14px;")
|
||||
layout.addWidget(self.status_label)
|
||||
|
||||
self.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
|
||||
def update_status(self, status: Dict):
|
||||
"""更新状态显示"""
|
||||
if status["is_activated"]:
|
||||
status_text = f"已激活 | 到期时间: {status['expire_time']} | 剩余天数: {status['days_left']}天"
|
||||
self.status_label.setStyleSheet("color: #28a745;")
|
||||
else:
|
||||
status_text = "未激活"
|
||||
self.status_label.setStyleSheet("color: #dc3545;")
|
||||
self.status_label.setText(status_text)
|
||||
|
||||
class ActivationWidget(QFrame):
|
||||
"""激活码输入组件
|
||||
|
||||
职责:
|
||||
1. 提供激活码输入界面
|
||||
2. 处理输入框和激活按钮的基础交互
|
||||
3. 将激活请求传递给父窗口处理
|
||||
4. 处理激活相关的提示信息
|
||||
|
||||
属性:
|
||||
code_input (QLineEdit): 激活码输入框
|
||||
activate_btn (QPushButton): 激活按钮
|
||||
|
||||
信号流:
|
||||
activate_btn.clicked -> activate() -> parent.handle_activation()
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
# 连接回车键到激活功能
|
||||
self.code_input.returnPressed.connect(self.activate)
|
||||
|
||||
def show_activation_required(self):
|
||||
"""显示需要激活的提示"""
|
||||
msg = QDialog(self)
|
||||
msg.setWindowTitle("提示")
|
||||
msg.setFixedWidth(360)
|
||||
msg.setWindowFlags(msg.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
|
||||
# 创建布局
|
||||
layout = QVBoxLayout()
|
||||
layout.setSpacing(12)
|
||||
layout.setContentsMargins(20, 15, 20, 15)
|
||||
|
||||
# 创建顶部警告框
|
||||
header_frame = QFrame()
|
||||
header_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #fff3cd;
|
||||
border: 1px solid #ffeeba;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QLabel {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
header_layout = QHBoxLayout(header_frame)
|
||||
header_layout.setContentsMargins(12, 10, 12, 10)
|
||||
header_layout.setSpacing(10)
|
||||
|
||||
icon_label = QLabel()
|
||||
icon_label.setPixmap(self.style().standardIcon(QStyle.SP_MessageBoxWarning).pixmap(20, 20))
|
||||
header_layout.addWidget(icon_label)
|
||||
|
||||
text_label = QLabel("请输入激活码")
|
||||
text_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #856404; background: transparent; ")
|
||||
header_layout.addWidget(text_label)
|
||||
header_layout.addStretch()
|
||||
|
||||
layout.addWidget(header_frame)
|
||||
|
||||
# 添加提示文本
|
||||
info_text = QLabel(
|
||||
"获取会员激活码,请通过以下方式:\n\n"
|
||||
"官方自助网站:cursorpro.com.cn\n"
|
||||
"微信客服号:behikcigar\n"
|
||||
"商店铺:xxx\n\n"
|
||||
"诚挚祝愿,欢迎加盟合作!"
|
||||
)
|
||||
info_text.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 13px;
|
||||
color: #333333;
|
||||
margin: 5px 0;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(info_text)
|
||||
|
||||
# 添加按钮区域
|
||||
btn_layout = QHBoxLayout()
|
||||
btn_layout.setSpacing(8)
|
||||
|
||||
# 复制网站按钮
|
||||
copy_web_btn = QPushButton("复制网址")
|
||||
copy_web_btn.setCursor(Qt.PointingHandCursor)
|
||||
copy_web_btn.clicked.connect(lambda: self._copy_to_clipboard("cursorpro.com.cn", "已复制官网地址"))
|
||||
copy_web_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
""")
|
||||
btn_layout.addWidget(copy_web_btn)
|
||||
|
||||
# 复制微信按钮
|
||||
copy_wx_btn = QPushButton("复制微信")
|
||||
copy_wx_btn.setCursor(Qt.PointingHandCursor)
|
||||
copy_wx_btn.clicked.connect(lambda: self._copy_to_clipboard("bshkcigar", "已复制微信号"))
|
||||
copy_wx_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #198754;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #157347;
|
||||
}
|
||||
""")
|
||||
btn_layout.addWidget(copy_wx_btn)
|
||||
|
||||
# 确定按钮
|
||||
ok_btn = QPushButton("确定")
|
||||
ok_btn.setCursor(Qt.PointingHandCursor)
|
||||
ok_btn.clicked.connect(msg.accept)
|
||||
ok_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #5c636a;
|
||||
}
|
||||
""")
|
||||
btn_layout.addWidget(ok_btn)
|
||||
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
# 设置对话框样式和布局
|
||||
msg.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: white;
|
||||
}
|
||||
""")
|
||||
msg.setLayout(layout)
|
||||
|
||||
# 显示对话框
|
||||
msg.exec_()
|
||||
|
||||
def _copy_to_clipboard(self, text: str, status_msg: str):
|
||||
"""复制文本到剪贴板并更新状态栏"""
|
||||
QApplication.clipboard().setText(text)
|
||||
main_window = self.window()
|
||||
if main_window and hasattr(main_window, 'status_bar'):
|
||||
main_window.status_bar.set_status(status_msg)
|
||||
|
||||
def setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 10) # 移除边距
|
||||
|
||||
# 标题
|
||||
title = QLabel("激活(听泉)会员,多个激活码可叠加整体时长")
|
||||
title.setStyleSheet("color: #28a745; font-size: 14px; font-weight: bold; border: none;") # 绿色标题
|
||||
layout.addWidget(title)
|
||||
|
||||
# 激活码输入区域
|
||||
input_layout = QHBoxLayout()
|
||||
self.code_input = QLineEdit()
|
||||
self.code_input.setPlaceholderText("请输入激活码")
|
||||
self.code_input.setMinimumWidth(300)
|
||||
self.code_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
color: #495057;
|
||||
}
|
||||
""")
|
||||
input_layout.addWidget(self.code_input)
|
||||
|
||||
# 激活按钮
|
||||
self.activate_btn = QPushButton("激活")
|
||||
self.activate_btn.clicked.connect(self.activate)
|
||||
self.activate_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 22px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
""")
|
||||
input_layout.addWidget(self.activate_btn)
|
||||
|
||||
layout.addLayout(input_layout)
|
||||
|
||||
self.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
""")
|
||||
|
||||
def activate(self):
|
||||
"""处理激活"""
|
||||
try:
|
||||
code = self.code_input.text().strip()
|
||||
|
||||
# 获取主窗口实例
|
||||
main_window = self.window()
|
||||
|
||||
if not main_window:
|
||||
print("错误:找不到主窗口")
|
||||
return
|
||||
|
||||
if not hasattr(main_window, 'handle_activation'):
|
||||
print("错误:主窗口没有 handle_activation 方法")
|
||||
return
|
||||
|
||||
if not code:
|
||||
print("警告:激活码为空")
|
||||
self.show_activation_required()
|
||||
return
|
||||
|
||||
print(f"正在处理激活码:{code}")
|
||||
main_window.handle_activation(code)
|
||||
|
||||
except Exception as e:
|
||||
print(f"激活处理发生错误:{str(e)}")
|
||||
|
||||
def clear_input(self):
|
||||
"""清空输入框"""
|
||||
self.code_input.clear()
|
||||
|
||||
class LogWidget(QTextEdit):
|
||||
"""日志显示组件"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setReadOnly(True)
|
||||
self.setStyleSheet("""
|
||||
QTextEdit {
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
font-family: 'Consolas', 'Microsoft YaHei', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
""")
|
||||
|
||||
def append_log(self, text: str):
|
||||
"""添加日志并滚动到底部"""
|
||||
self.append(text)
|
||||
self.verticalScrollBar().setValue(
|
||||
self.verticalScrollBar().maximum()
|
||||
)
|
||||
|
||||
class StatusBar(QFrame):
|
||||
"""状态栏组件"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
"""初始化UI"""
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
self.status_label = QLabel("就绪")
|
||||
self.status_label.setStyleSheet("font-size: 14px; color: #333;")
|
||||
layout.addWidget(self.status_label)
|
||||
|
||||
def set_status(self, text: str):
|
||||
"""设置状态文本"""
|
||||
self.status_label.setText(text)
|
||||
|
||||
class ActionButton(QPushButton):
|
||||
"""操作按钮基类"""
|
||||
def __init__(self, text: str, color: str, parent=None):
|
||||
super().__init__(text, parent)
|
||||
self.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background-color: {color};
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
min-height: 36px;
|
||||
}}
|
||||
QPushButton:hover {{
|
||||
background-color: {self._get_hover_color(color)};
|
||||
}}
|
||||
QPushButton:disabled {{
|
||||
background-color: #ccc;
|
||||
}}
|
||||
""")
|
||||
|
||||
def _get_hover_color(self, color: str) -> str:
|
||||
"""获取悬停颜色"""
|
||||
# 简单的颜色加深处理
|
||||
if color == "#007bff": # 蓝色
|
||||
return "#0056b3"
|
||||
elif color == "#28a745": # 绿色
|
||||
return "#218838"
|
||||
return color
|
||||
|
||||
class MemberStatusWidget(QFrame):
|
||||
"""会员状态显示组件
|
||||
|
||||
职责:
|
||||
1. 显示会员状态信息
|
||||
2. 显示设备信息
|
||||
3. 根据不同状态显示不同样式
|
||||
|
||||
属性:
|
||||
status_text (QTextEdit): 状态信息显示区域
|
||||
|
||||
显示信息:
|
||||
- 会员状态(正常/未激活)
|
||||
- 到期时间
|
||||
- 剩余天数
|
||||
- 设备信息(系统、设备名、IP、地区)
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 10)
|
||||
|
||||
# 创建一个容器 Frame
|
||||
container = QFrame(self)
|
||||
container.setObjectName("memberStatusContainer")
|
||||
container_layout = QVBoxLayout(container)
|
||||
container_layout.setSpacing(5) # 减小组件间距
|
||||
container_layout.setContentsMargins(0,0,0,0) # 减小内边距
|
||||
container.setFixedHeight(220) # 根据需要调整这个值
|
||||
|
||||
# 状态信息
|
||||
self.status_text = QTextEdit()
|
||||
self.status_text.setReadOnly(True)
|
||||
|
||||
self.status_text.setStyleSheet("""
|
||||
QTextEdit {
|
||||
background-color: #f8fafc;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
color: #4a5568;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
/* 滚动条整体样式 */
|
||||
QScrollBar:vertical {
|
||||
border: none;
|
||||
background: #f8fafc;
|
||||
width: 6px;
|
||||
margin: 0px;
|
||||
}
|
||||
/* 滚动条滑块 */
|
||||
QScrollBar::handle:vertical {
|
||||
background: #e2e8f0;
|
||||
border-radius: 3px;
|
||||
min-height: 20px;
|
||||
}
|
||||
QScrollBar::handle:vertical:hover {
|
||||
background: #cbd5e1;
|
||||
}
|
||||
/* 滚动条上下按钮 */
|
||||
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
|
||||
border: none;
|
||||
background: none;
|
||||
height: 0px;
|
||||
}
|
||||
/* 滚动条背景 */
|
||||
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||
background: none;
|
||||
}
|
||||
""")
|
||||
container_layout.addWidget(self.status_text)
|
||||
|
||||
# 设置容器样式
|
||||
container.setStyleSheet("""
|
||||
#memberStatusContainer {
|
||||
background-color: white;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
#titleContainer {
|
||||
background: transparent;
|
||||
}
|
||||
""")
|
||||
|
||||
layout.addWidget(container)
|
||||
|
||||
def update_status(self, status: Dict):
|
||||
"""更新状态显示"""
|
||||
try:
|
||||
# 获取状态信息
|
||||
is_activated = status.get("is_activated", False)
|
||||
expire_time = status.get("expire_time", "未知")
|
||||
days_left = status.get("days_left", 0)
|
||||
device_info = status.get("device_info", {})
|
||||
|
||||
# 构建状态文本
|
||||
status_text = []
|
||||
|
||||
# 会员状态
|
||||
status_color = "#10B981" if is_activated else "#EF4444" # 绿色或红色
|
||||
status_text.append(f'会员状态:<span style="color: {status_color};">{("正常" if is_activated else "未激活")}</span>')
|
||||
|
||||
# 到期时间和剩余天数
|
||||
if is_activated:
|
||||
status_text.append(f"到期时间:{expire_time}")
|
||||
# 根据剩余天数显示不同颜色
|
||||
days_color = "#10B981" if days_left > 30 else "#F59E0B" if days_left > 7 else "#EF4444"
|
||||
status_text.append(f'剩余天数:<span style="color: {days_color};">{days_left}天</span>')
|
||||
|
||||
|
||||
|
||||
status_text.append("<br/>")
|
||||
# 设备信息
|
||||
status_text.append("\n设备信息:")
|
||||
status_text.append(f"系统:{device_info.get('os', 'Windows')}")
|
||||
status_text.append(f"设备名:{device_info.get('device_name', '未知')}")
|
||||
status_text.append(f"IP地址:{device_info.get('ip', '未知')}")
|
||||
status_text.append(f"地区:{device_info.get('location', '未知')}")
|
||||
|
||||
# 更新文本
|
||||
self.status_text.setHtml("<br>".join(status_text))
|
||||
|
||||
except Exception as e:
|
||||
# 如果发生异常,显示错误信息
|
||||
error_text = f'<span style="color: #EF4444;">状态更新失败: {str(e)}</span>'
|
||||
self.status_text.setHtml(error_text)
|
||||
|
||||
class LoadingSpinner(QWidget):
|
||||
"""自定义加载动画组件"""
|
||||
def __init__(self, parent=None, size=40, line_width=3):
|
||||
super().__init__(parent)
|
||||
self.setFixedSize(size, size)
|
||||
self.angle = 0
|
||||
self.line_width = line_width
|
||||
self._size = size
|
||||
self._speed = 30 # 每次更新转动的角度
|
||||
|
||||
# 设置动画
|
||||
self.animation = QPropertyAnimation(self, b"rotation")
|
||||
self.animation.setDuration(800) # 缩短动画时间到800ms使其更流畅
|
||||
self.animation.setStartValue(0)
|
||||
self.animation.setEndValue(360)
|
||||
self.animation.setLoopCount(-1) # 无限循环
|
||||
|
||||
# 使用 InOutQuad 缓动曲线让动画更自然
|
||||
curve = QEasingCurve(QEasingCurve.InOutQuad)
|
||||
self.animation.setEasingCurve(curve)
|
||||
|
||||
# 创建定时器用于额外的动画效果
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.rotate)
|
||||
self.timer.setInterval(50) # 20fps的刷新率
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""绘制旋转的圆弧"""
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
# 计算圆的位置
|
||||
rect = QRect(self.line_width, self.line_width,
|
||||
self._size - 2*self.line_width,
|
||||
self._size - 2*self.line_width)
|
||||
|
||||
# 设置画笔
|
||||
pen = QPen()
|
||||
pen.setWidth(self.line_width)
|
||||
pen.setCapStyle(Qt.RoundCap)
|
||||
|
||||
# 绘制渐变圆弧
|
||||
segments = 8
|
||||
for i in range(segments):
|
||||
# 计算每个段的透明度
|
||||
alpha = int(255 * (1 - i / segments))
|
||||
# 使用更鲜艳的蓝色
|
||||
pen.setColor(QColor(0, 122, 255, alpha))
|
||||
painter.setPen(pen)
|
||||
|
||||
# 计算每个段的起始角度和跨度
|
||||
start_angle = (self.angle - i * (360 // segments)) % 360
|
||||
span = 30 # 每个段的跨度
|
||||
|
||||
# 转换角度为16分之一度(Qt的角度单位)
|
||||
start_angle_16 = start_angle * 16
|
||||
span_16 = span * 16
|
||||
|
||||
painter.drawArc(rect, -start_angle_16, -span_16)
|
||||
|
||||
def rotate(self):
|
||||
"""更新旋转角度"""
|
||||
self.angle = (self.angle + self._speed) % 360
|
||||
self.update()
|
||||
|
||||
@pyqtProperty(int)
|
||||
def rotation(self):
|
||||
return self.angle
|
||||
|
||||
@rotation.setter
|
||||
def rotation(self, angle):
|
||||
self.angle = angle
|
||||
self.update()
|
||||
|
||||
def start(self):
|
||||
"""开始动画"""
|
||||
self.animation.start()
|
||||
self.timer.start()
|
||||
|
||||
def stop(self):
|
||||
"""停止动画"""
|
||||
self.animation.stop()
|
||||
self.timer.stop()
|
||||
|
||||
class LoadingDialog(QDialog):
|
||||
"""加载对话框组件"""
|
||||
def __init__(self, parent=None, message="请稍候..."):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("处理中")
|
||||
self.setFixedSize(300, 120)
|
||||
self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setSpacing(15)
|
||||
|
||||
# 消息标签
|
||||
self.message_label = QLabel(message)
|
||||
self.message_label.setAlignment(Qt.AlignCenter)
|
||||
self.message_label.setStyleSheet("""
|
||||
color: #0d6efd;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
""")
|
||||
layout.addWidget(self.message_label)
|
||||
|
||||
# 加载动画
|
||||
self.spinner = LoadingSpinner(self, size=48, line_width=4) # 增大尺寸
|
||||
spinner_layout = QHBoxLayout()
|
||||
spinner_layout.addStretch()
|
||||
spinner_layout.addWidget(self.spinner)
|
||||
spinner_layout.addStretch()
|
||||
layout.addLayout(spinner_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# 设置样式
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
""")
|
||||
|
||||
def showEvent(self, event):
|
||||
"""显示时启动动画"""
|
||||
super().showEvent(event)
|
||||
self.spinner.start()
|
||||
|
||||
def hideEvent(self, event):
|
||||
"""隐藏时停止动画"""
|
||||
self.spinner.stop()
|
||||
super().hideEvent(event)
|
||||
|
||||
def set_message(self, message: str):
|
||||
"""更新消息文本"""
|
||||
self.message_label.setText(message)
|
||||
98
gui/components/workers.py
Normal file
98
gui/components/workers.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from services.cursor_service import CursorService
|
||||
from common_utils import get_hardware_id
|
||||
from exit_cursor import ExitCursor
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
|
||||
class BaseWorker(QThread):
|
||||
"""基础工作线程类"""
|
||||
progress = pyqtSignal(dict) # 进度信号
|
||||
finished = pyqtSignal(tuple) # 完成信号
|
||||
error = pyqtSignal(str) # 错误信号
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._is_running = True
|
||||
|
||||
def stop(self):
|
||||
"""停止线程"""
|
||||
self._is_running = False
|
||||
self.wait()
|
||||
|
||||
class RefreshTokenWorker(BaseWorker):
|
||||
"""刷新 Cursor Token 工作线程"""
|
||||
def run(self):
|
||||
try:
|
||||
service = CursorService()
|
||||
machine_id = get_hardware_id()
|
||||
|
||||
# 更新进度 - 退出 Cursor
|
||||
self.progress.emit({
|
||||
"status": "exiting",
|
||||
"message": "正在关闭 Cursor 进程..."
|
||||
})
|
||||
|
||||
# 退出 Cursor
|
||||
if not ExitCursor():
|
||||
raise Exception("无法完全关闭 Cursor 进程,请手动关闭后重试")
|
||||
|
||||
# 更新进度 - 开始刷新
|
||||
self.progress.emit({
|
||||
"status": "refreshing",
|
||||
"message": "正在刷新 Cursor 授权..."
|
||||
})
|
||||
|
||||
# 调用刷新服务
|
||||
success, msg, account_info = service.refresh_account(machine_id)
|
||||
|
||||
if success and account_info:
|
||||
# 更新进度 - 开始重置机器码
|
||||
self.progress.emit({
|
||||
"status": "resetting",
|
||||
"message": "正在重置机器码..."
|
||||
})
|
||||
|
||||
# 调用重置机器码
|
||||
reset_success, reset_msg, reset_data = service.reset_machine()
|
||||
|
||||
if reset_success:
|
||||
# 更新进度
|
||||
self.progress.emit({
|
||||
"status": "success",
|
||||
"message": f"授权刷新成功: {account_info.get('email')}"
|
||||
})
|
||||
|
||||
result_msg = (
|
||||
f"授权刷新成功\n"
|
||||
f"账号: {account_info.get('email')}\n"
|
||||
f"机器码重置成功\n"
|
||||
f"请重新启动 Cursor 编辑器"
|
||||
)
|
||||
else:
|
||||
result_msg = f"授权刷新成功,但机器码重置失败: {reset_msg}"
|
||||
else:
|
||||
result_msg = f"授权刷新失败: {msg}"
|
||||
|
||||
self.finished.emit(('refresh', (success, result_msg)))
|
||||
|
||||
except Exception as e:
|
||||
self.error.emit(str(e))
|
||||
|
||||
class ResetWorker(BaseWorker):
|
||||
"""重置机器码工作线程"""
|
||||
def run(self):
|
||||
try:
|
||||
service = CursorService()
|
||||
success, msg, data = service.reset_machine()
|
||||
self.finished.emit(('reset', (success, msg, data)))
|
||||
except Exception as e:
|
||||
self.error.emit(str(e))
|
||||
|
||||
class DisableWorker(BaseWorker):
|
||||
"""禁用更新工作线程"""
|
||||
def run(self):
|
||||
try:
|
||||
service = CursorService()
|
||||
success, msg = service.disable_update()
|
||||
self.finished.emit(('disable', (success, msg)))
|
||||
except Exception as e:
|
||||
self.error.emit(str(e))
|
||||
1
gui/windows/__init__.py
Normal file
1
gui/windows/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""GUI 窗口包"""
|
||||
667
gui/windows/main_window.py
Normal file
667
gui/windows/main_window.py
Normal file
@@ -0,0 +1,667 @@
|
||||
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, get_current_version
|
||||
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(f"听泉助手 v{get_current_version()}")
|
||||
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()
|
||||
Reference in New Issue
Block a user