853 lines
29 KiB
Python
853 lines
29 KiB
Python
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
|
||
import os
|
||
from pathlib import Path
|
||
from urllib.parse import quote
|
||
import requests
|
||
|
||
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"
|
||
# "代理听泉助手vx联系:behikcigar\n"
|
||
# "天猫商店:https://e.tb.cn/h.TC2gtKSiccfl5MD?tk=GZvHenPgE4o CZ193\n\n"
|
||
# "诚挚祝愿,欢迎加盟合作!"
|
||
# )
|
||
info_text = QLabel(
|
||
"获取会员激活码,请通过以下方式:\n\n"
|
||
"官方自助网站:cursorpro.com.cn\n"
|
||
"代理听泉助手vx联系:behikcigar\n"
|
||
"购买链接:https://www.houfaka.com/links/3BD4C127\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("behikcigar", "已复制微信号"))
|
||
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)
|
||
|
||
# 复制淘宝店铺按钮
|
||
# copy_tb_btn = QPushButton("复制天猫店铺")
|
||
# copy_tb_btn.setCursor(Qt.PointingHandCursor)
|
||
# copy_tb_btn.clicked.connect(lambda: self._copy_to_clipboard("https://e.tb.cn/h.TC2gtKSiccfl5MD?tk=GZvHenPgE4o CZ193", "已复制淘宝店铺"))
|
||
# copy_tb_btn.setStyleSheet("""
|
||
# QPushButton {
|
||
# background-color: #ff5100;
|
||
# color: white;
|
||
# border: none;
|
||
# padding: 6px 16px;
|
||
# border-radius: 3px;
|
||
# font-size: 13px;
|
||
# }
|
||
# QPushButton:hover {
|
||
# background-color: #c42b1c;
|
||
# }
|
||
# """)
|
||
# btn_layout.addWidget(copy_tb_btn)
|
||
|
||
copy_tb_btn = QPushButton("复制购买链接")
|
||
copy_tb_btn.setCursor(Qt.PointingHandCursor)
|
||
copy_tb_btn.clicked.connect(lambda: self._copy_to_clipboard("https://www.houfaka.com/links/3BD4C127", "已复制"))
|
||
copy_tb_btn.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #ff5100;
|
||
color: white;
|
||
border: none;
|
||
padding: 6px 16px;
|
||
border-radius: 3px;
|
||
font-size: 13px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #c42b1c;
|
||
}
|
||
""")
|
||
btn_layout.addWidget(copy_tb_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; line-height: 15px;min-height: 15px; 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;
|
||
min-height: 20px;
|
||
line-height: 20px;
|
||
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;
|
||
min-height: 20px;
|
||
line-height: 20px;
|
||
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): 状态信息显示区域
|
||
|
||
显示信息:
|
||
- 会员状态(正常/未激活)
|
||
- 到期时间
|
||
- 剩余天数
|
||
- 公告区域(从API获取,每2分钟更新一次)
|
||
"""
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setup_ui()
|
||
|
||
# 保存最后一次状态更新的数据
|
||
self.last_status = {}
|
||
|
||
# 创建定时器,每2分钟更新一次公告
|
||
self.announcement_timer = QTimer(self)
|
||
self.announcement_timer.setInterval(120000) # 2分钟 = 120000毫秒
|
||
self.announcement_timer.timeout.connect(self.refresh_announcement)
|
||
self.announcement_timer.start()
|
||
|
||
# 初始化后,延迟一秒检查并显示重要公告
|
||
QTimer.singleShot(1000, self.check_important_announcement)
|
||
|
||
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(240) # 根据需要调整这个值
|
||
|
||
# 状态信息
|
||
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 check_important_announcement(self):
|
||
"""检查并显示重要公告(level=5)"""
|
||
announcement_data = self.get_announcement(return_full_data=True)
|
||
if announcement_data and announcement_data.get("level") == 5:
|
||
self.show_announcement_dialog(announcement_data.get("txt", ""))
|
||
|
||
def show_announcement_dialog(self, content):
|
||
"""显示公告弹窗"""
|
||
msg = QDialog(self)
|
||
msg.setWindowTitle("重要公告")
|
||
msg.setMinimumWidth(400)
|
||
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: #e6f7ff;
|
||
border: 1px solid #91d5ff;
|
||
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_MessageBoxInformation).pixmap(20, 20))
|
||
header_layout.addWidget(icon_label)
|
||
|
||
text_label = QLabel("重要公告")
|
||
text_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #1890ff; background: transparent;")
|
||
header_layout.addWidget(text_label)
|
||
header_layout.addStretch()
|
||
|
||
layout.addWidget(header_frame)
|
||
|
||
# 公告内容
|
||
content_text = QTextEdit()
|
||
content_text.setReadOnly(True)
|
||
content_text.setHtml(content)
|
||
content_text.setStyleSheet("""
|
||
QTextEdit {
|
||
border: 1px solid #e8e8e8;
|
||
border-radius: 4px;
|
||
padding: 10px;
|
||
background-color: #fafafa;
|
||
min-height: 120px;
|
||
}
|
||
""")
|
||
layout.addWidget(content_text)
|
||
|
||
# 确认按钮
|
||
btn_layout = QHBoxLayout()
|
||
btn_layout.addStretch()
|
||
|
||
ok_btn = QPushButton("我知道了")
|
||
ok_btn.setCursor(Qt.PointingHandCursor)
|
||
ok_btn.clicked.connect(msg.accept)
|
||
ok_btn.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #1890ff;
|
||
color: white;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #40a9ff;
|
||
}
|
||
""")
|
||
btn_layout.addWidget(ok_btn)
|
||
|
||
layout.addLayout(btn_layout)
|
||
|
||
# 设置对话框样式和布局
|
||
msg.setStyleSheet("""
|
||
QDialog {
|
||
background-color: white;
|
||
}
|
||
""")
|
||
msg.setLayout(layout)
|
||
|
||
# 显示对话框
|
||
msg.exec_()
|
||
|
||
def refresh_announcement(self):
|
||
"""定时刷新公告内容"""
|
||
if self.last_status:
|
||
# 使用最后一次的状态数据更新界面,只刷新公告部分
|
||
self.update_status(self.last_status, refresh_announcement_only=True)
|
||
|
||
def get_announcement(self, return_full_data=False):
|
||
"""从API获取公告内容
|
||
|
||
Args:
|
||
return_full_data: 是否返回完整的公告数据字典,包含level等信息
|
||
"""
|
||
try:
|
||
response = requests.get("http://api.cursorpro.com.cn/admin/api.version/ad", timeout=5)
|
||
if response.status_code == 200:
|
||
try:
|
||
# 解析JSON响应
|
||
json_data = response.json()
|
||
# 检查code是否为0(成功)
|
||
if json_data.get("code") == 0:
|
||
# 获取公告数据
|
||
announcement_data = json_data.get("data", {})
|
||
|
||
# 根据参数决定返回完整数据还是仅文本
|
||
if return_full_data:
|
||
return announcement_data
|
||
else:
|
||
return announcement_data.get("txt", "")
|
||
else:
|
||
# 如果code不是0,返回错误信息
|
||
error_msg = f"获取公告失败: {json_data.get('msg', '未知错误')}"
|
||
return {} if return_full_data else error_msg
|
||
except ValueError:
|
||
# JSON解析错误
|
||
error_msg = "解析公告数据失败"
|
||
return {} if return_full_data else error_msg
|
||
else:
|
||
error_msg = f"获取公告失败,状态码: {response.status_code}"
|
||
return {} if return_full_data else error_msg
|
||
except Exception as e:
|
||
error_msg = f"获取公告失败: {str(e)}"
|
||
return {} if return_full_data else error_msg
|
||
|
||
def update_status(self, status: Dict, refresh_announcement_only=False):
|
||
"""更新状态显示"""
|
||
try:
|
||
# 保存最后一次状态数据用于刷新公告
|
||
if not refresh_announcement_only:
|
||
self.last_status = status.copy()
|
||
|
||
# 获取状态信息
|
||
is_activated = status.get("is_activated", False)
|
||
expire_time = status.get("expire_time", "未知")
|
||
days_left = status.get("days_left", 0)
|
||
|
||
# 构建状态文本
|
||
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/>")
|
||
|
||
# 公告区域
|
||
announcement = self.get_announcement()
|
||
status_text.append('<div style="margin-top: 2px; padding: 2px; background-color: #f0f9ff; border-left: 4px solid #3b82f6; border-radius: 4px; max-height: 80px; overflow-y: auto;">')
|
||
status_text.append('<span style="font-weight: bold; color: #3b82f6;">公告:</span><br/>')
|
||
status_text.append(f'{announcement}')
|
||
status_text.append('</div>')
|
||
|
||
# 更新文本
|
||
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)
|
||
|
||
def hideEvent(self, event):
|
||
"""窗口隐藏时停止定时器"""
|
||
self.announcement_timer.stop()
|
||
super().hideEvent(event)
|
||
|
||
def showEvent(self, event):
|
||
"""窗口显示时启动定时器"""
|
||
self.announcement_timer.start()
|
||
super().showEvent(event)
|
||
|
||
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)
|