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'会员状态:{("正常" if is_activated else "未激活")}') # 到期时间和剩余天数 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'剩余天数:{days_left}天') status_text.append("
") # 设备信息 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("
".join(status_text)) except Exception as e: # 如果发生异常,显示错误信息 error_text = f'状态更新失败: {str(e)}' 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)