2025-10-28 15:59:18 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
现代化圆形进度条更新对话框
|
|
|
|
|
|
展示完整更新流程:下载 → UAC授权 → 安装 → 重启
|
2025-10-28 15:59:18 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QLabel,
|
2025-10-29 14:58:09 +08:00
|
|
|
|
QPushButton, QHBoxLayout, QWidget)
|
|
|
|
|
|
from PyQt5.QtCore import Qt, QTimer, QPropertyAnimation, QEasingCurve, pyqtProperty
|
|
|
|
|
|
from PyQt5.QtGui import QFont, QPainter, QColor, QPen, QLinearGradient, QConicalGradient
|
|
|
|
|
|
from PyQt5.QtCore import QRectF, QPointF
|
|
|
|
|
|
import math
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
class CircularProgressBar(QWidget):
|
2025-10-28 15:59:18 +08:00
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
自定义圆形进度条组件
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
特性:
|
2025-10-29 14:58:09 +08:00
|
|
|
|
- 顺时针旋转动画
|
|
|
|
|
|
- 渐变色圆环
|
|
|
|
|
|
- 中心显示百分比
|
|
|
|
|
|
- 支持平滑过渡
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self._value = 0 # 当前进度值(0-100)
|
|
|
|
|
|
self._max_value = 100
|
|
|
|
|
|
self._text = "0%"
|
|
|
|
|
|
self.setMinimumSize(200, 200)
|
|
|
|
|
|
|
|
|
|
|
|
# 动画效果
|
|
|
|
|
|
self.animation = QPropertyAnimation(self, b"value")
|
|
|
|
|
|
self.animation.setDuration(500) # 500ms过渡动画
|
|
|
|
|
|
self.animation.setEasingCurve(QEasingCurve.OutCubic)
|
|
|
|
|
|
|
|
|
|
|
|
@pyqtProperty(int)
|
|
|
|
|
|
def value(self):
|
|
|
|
|
|
return self._value
|
|
|
|
|
|
|
|
|
|
|
|
@value.setter
|
|
|
|
|
|
def value(self, val):
|
|
|
|
|
|
self._value = val
|
|
|
|
|
|
self.update() # 触发重绘
|
|
|
|
|
|
|
|
|
|
|
|
def setValue(self, val):
|
|
|
|
|
|
"""设置进度值(带动画)"""
|
|
|
|
|
|
if val != self._value:
|
|
|
|
|
|
self.animation.stop()
|
|
|
|
|
|
self.animation.setStartValue(self._value)
|
|
|
|
|
|
self.animation.setEndValue(val)
|
|
|
|
|
|
self.animation.start()
|
|
|
|
|
|
|
|
|
|
|
|
def setText(self, text):
|
|
|
|
|
|
"""设置中心文本"""
|
|
|
|
|
|
self._text = text
|
|
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
|
|
|
|
def paintEvent(self, event):
|
|
|
|
|
|
"""绘制圆形进度条"""
|
|
|
|
|
|
painter = QPainter(self)
|
|
|
|
|
|
painter.setRenderHint(QPainter.Antialiasing) # 抗锯齿
|
|
|
|
|
|
|
|
|
|
|
|
# 计算中心和半径
|
|
|
|
|
|
width = self.width()
|
|
|
|
|
|
height = self.height()
|
|
|
|
|
|
size = min(width, height)
|
|
|
|
|
|
center = QPointF(width / 2, height / 2)
|
|
|
|
|
|
radius = size / 2 - 15 # 留出边距
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制背景圆环(灰色)
|
|
|
|
|
|
pen = QPen()
|
|
|
|
|
|
pen.setWidth(12)
|
|
|
|
|
|
pen.setColor(QColor(230, 230, 230))
|
|
|
|
|
|
pen.setCapStyle(Qt.RoundCap)
|
|
|
|
|
|
painter.setPen(pen)
|
|
|
|
|
|
|
|
|
|
|
|
rect = QRectF(center.x() - radius, center.y() - radius,
|
|
|
|
|
|
radius * 2, radius * 2)
|
|
|
|
|
|
painter.drawArc(rect, 90 * 16, -360 * 16) # 绘制完整圆环
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制进度圆环(渐变色)
|
|
|
|
|
|
if self._value > 0:
|
|
|
|
|
|
# 创建圆锥渐变(顺时针旋转效果)
|
|
|
|
|
|
gradient = QConicalGradient(center, 90) # 从顶部开始
|
|
|
|
|
|
gradient.setColorAt(0.0, QColor(66, 133, 244)) # 蓝色
|
|
|
|
|
|
gradient.setColorAt(0.5, QColor(25, 118, 210)) # 深蓝
|
|
|
|
|
|
gradient.setColorAt(1.0, QColor(21, 101, 192)) # 更深蓝
|
|
|
|
|
|
|
|
|
|
|
|
pen.setBrush(gradient)
|
|
|
|
|
|
pen.setColor(QColor(66, 133, 244))
|
|
|
|
|
|
painter.setPen(pen)
|
|
|
|
|
|
|
|
|
|
|
|
# 计算进度角度(顺时针,从顶部开始)
|
|
|
|
|
|
span_angle = int(-(self._value / self._max_value) * 360 * 16)
|
|
|
|
|
|
painter.drawArc(rect, 90 * 16, span_angle)
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制中心文本(百分比)
|
|
|
|
|
|
painter.setPen(QColor(44, 62, 80))
|
|
|
|
|
|
font = QFont('Microsoft YaHei', 28, QFont.Bold)
|
|
|
|
|
|
painter.setFont(font)
|
|
|
|
|
|
painter.drawText(rect, Qt.AlignCenter, self._text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateProgressDialog(QDialog):
|
2025-10-28 15:59:18 +08:00
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
现代化更新进度对话框
|
|
|
|
|
|
|
|
|
|
|
|
更新流程阶段:
|
|
|
|
|
|
1. 下载安装包 (0-50%)
|
|
|
|
|
|
2. 等待UAC授权 (50-60%)
|
|
|
|
|
|
3. 执行安装 (60-90%)
|
|
|
|
|
|
4. 等待程序重启 (90-100%)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# 流程阶段定义
|
|
|
|
|
|
STAGE_DOWNLOAD = 0 # 下载阶段 (0-50%)
|
|
|
|
|
|
STAGE_UAC = 1 # UAC授权阶段 (50-60%)
|
|
|
|
|
|
STAGE_INSTALL = 2 # 安装阶段 (60-90%)
|
|
|
|
|
|
STAGE_RESTART = 3 # 重启阶段 (90-100%)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
def __init__(self, version, parent=None):
|
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self.version = version
|
|
|
|
|
|
self.downloader = None
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.current_stage = self.STAGE_DOWNLOAD
|
|
|
|
|
|
self.download_progress = 0 # 下载进度(0-100)
|
|
|
|
|
|
self.restart_progress = 0 # 重启阶段进度(0-100),用于外部检查
|
|
|
|
|
|
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.init_ui()
|
|
|
|
|
|
|
|
|
|
|
|
def init_ui(self):
|
|
|
|
|
|
"""初始化UI"""
|
|
|
|
|
|
self.setWindowTitle("正在更新")
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.setFixedSize(500, 600)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
# 禁止关闭按钮(只能通过取消按钮关闭)
|
|
|
|
|
|
self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.CustomizeWindowHint)
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# 主布局
|
2025-10-28 15:59:18 +08:00
|
|
|
|
layout = QVBoxLayout()
|
2025-10-29 14:58:09 +08:00
|
|
|
|
layout.setSpacing(20)
|
|
|
|
|
|
layout.setContentsMargins(30, 30, 30, 30)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# ============ 标题区域 ============
|
|
|
|
|
|
self.title_label = QLabel(f"正在更新到 v{self.version}")
|
|
|
|
|
|
self.title_label.setFont(QFont('Microsoft YaHei', 16, QFont.Bold))
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.title_label.setAlignment(Qt.AlignCenter)
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.title_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
|
2025-10-28 15:59:18 +08:00
|
|
|
|
layout.addWidget(self.title_label)
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# ============ 圆形进度条 ============
|
|
|
|
|
|
self.circular_progress = CircularProgressBar()
|
|
|
|
|
|
layout.addWidget(self.circular_progress, alignment=Qt.AlignCenter)
|
|
|
|
|
|
|
|
|
|
|
|
# ============ 阶段提示 ============
|
|
|
|
|
|
self.stage_label = QLabel("📥 正在下载安装包...")
|
|
|
|
|
|
self.stage_label.setFont(QFont('Microsoft YaHei', 12))
|
|
|
|
|
|
self.stage_label.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
self.stage_label.setStyleSheet("color: #34495e; margin-top: 10px;")
|
|
|
|
|
|
layout.addWidget(self.stage_label)
|
|
|
|
|
|
|
|
|
|
|
|
# ============ 详细状态 ============
|
|
|
|
|
|
self.status_label = QLabel("准备开始下载...")
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.status_label.setAlignment(Qt.AlignCenter)
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.status_label.setStyleSheet("color: #7f8c8d; font-size: 11px;")
|
|
|
|
|
|
self.status_label.setMinimumHeight(25)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
layout.addWidget(self.status_label)
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# ============ 重试信息(默认隐藏)============
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.retry_label = QLabel("")
|
|
|
|
|
|
self.retry_label.setAlignment(Qt.AlignCenter)
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.retry_label.setStyleSheet("color: #ff9800; font-size: 11px; font-weight: bold;")
|
|
|
|
|
|
self.retry_label.setMinimumHeight(25)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.retry_label.hide()
|
|
|
|
|
|
layout.addWidget(self.retry_label)
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# ============ 按钮区域 ============
|
2025-10-28 15:59:18 +08:00
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.cancel_button = QPushButton("取消更新")
|
|
|
|
|
|
self.cancel_button.setFixedWidth(120)
|
|
|
|
|
|
self.cancel_button.setFixedHeight(36)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.cancel_button.clicked.connect(self.cancel_download)
|
|
|
|
|
|
button_layout.addWidget(self.cancel_button)
|
|
|
|
|
|
|
|
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
|
|
|
|
|
|
# 应用样式
|
|
|
|
|
|
self.apply_styles()
|
|
|
|
|
|
|
|
|
|
|
|
def apply_styles(self):
|
|
|
|
|
|
"""应用现代化样式"""
|
|
|
|
|
|
self.setStyleSheet("""
|
|
|
|
|
|
QDialog {
|
|
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #f8fafb,
|
2025-10-29 14:58:09 +08:00
|
|
|
|
stop:0.5 #f1f4f6,
|
2025-10-28 15:59:18 +08:00
|
|
|
|
stop:1 #e8ecef
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
QLabel {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
QPushButton {
|
|
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #f5f5f5,
|
|
|
|
|
|
stop:1 #e0e0e0
|
|
|
|
|
|
);
|
2025-10-29 14:58:09 +08:00
|
|
|
|
border: 2px solid #ccc;
|
|
|
|
|
|
border-radius: 8px;
|
2025-10-28 15:59:18 +08:00
|
|
|
|
padding: 8px 20px;
|
2025-10-29 14:58:09 +08:00
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: bold;
|
2025-10-28 15:59:18 +08:00
|
|
|
|
font-family: 'Microsoft YaHei';
|
2025-10-29 14:58:09 +08:00
|
|
|
|
color: #555;
|
2025-10-28 15:59:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
QPushButton:hover {
|
|
|
|
|
|
background: qlineargradient(
|
|
|
|
|
|
x1:0, y1:0, x2:0, y2:1,
|
|
|
|
|
|
stop:0 #e8e8e8,
|
|
|
|
|
|
stop:1 #d0d0d0
|
|
|
|
|
|
);
|
2025-10-29 14:58:09 +08:00
|
|
|
|
border: 2px solid #999;
|
2025-10-28 15:59:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
QPushButton:pressed {
|
|
|
|
|
|
background: #d0d0d0;
|
|
|
|
|
|
}
|
2025-10-29 14:58:09 +08:00
|
|
|
|
QPushButton:disabled {
|
|
|
|
|
|
background: #f0f0f0;
|
|
|
|
|
|
color: #aaa;
|
|
|
|
|
|
border: 2px solid #ddd;
|
|
|
|
|
|
}
|
2025-10-28 15:59:18 +08:00
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
def update_progress(self, downloaded, total):
|
|
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
更新下载进度(阶段1:0-50%)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
downloaded: 已下载字节数
|
|
|
|
|
|
total: 总字节数
|
|
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
if self.current_stage != self.STAGE_DOWNLOAD:
|
|
|
|
|
|
return # 不在下载阶段,忽略
|
|
|
|
|
|
|
2025-10-28 15:59:18 +08:00
|
|
|
|
if total > 0:
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# 计算下载百分比(0-100)
|
|
|
|
|
|
download_percent = int((downloaded / total) * 100)
|
|
|
|
|
|
self.download_progress = download_percent
|
|
|
|
|
|
|
|
|
|
|
|
# 映射到总进度的0-50%
|
|
|
|
|
|
overall_progress = int(download_percent * 0.5)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新圆形进度条
|
|
|
|
|
|
self.circular_progress.setValue(overall_progress)
|
|
|
|
|
|
self.circular_progress.setText(f"{overall_progress}%")
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
# 转换为MB显示
|
|
|
|
|
|
downloaded_mb = downloaded / (1024 * 1024)
|
|
|
|
|
|
total_mb = total / (1024 * 1024)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新状态文本
|
|
|
|
|
|
self.status_label.setText(f"已下载 {downloaded_mb:.1f} MB / {total_mb:.1f} MB")
|
|
|
|
|
|
else:
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# 无法获取总大小时显示不确定状态
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.status_label.setText("正在下载,请稍候...")
|
|
|
|
|
|
|
|
|
|
|
|
def show_retry_info(self, current_retry, max_retries):
|
|
|
|
|
|
"""
|
|
|
|
|
|
显示重试信息
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
current_retry: 当前重试次数
|
|
|
|
|
|
max_retries: 最大重试次数
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.retry_label.setText(f"⚠️ 下载失败,正在重试... ({current_retry}/{max_retries})")
|
|
|
|
|
|
self.retry_label.show()
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# 重置下载进度
|
|
|
|
|
|
self.download_progress = 0
|
|
|
|
|
|
self.circular_progress.setValue(0)
|
|
|
|
|
|
self.circular_progress.setText("0%")
|
|
|
|
|
|
self.status_label.setText("正在重试连接...")
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
# 更新标题
|
|
|
|
|
|
self.title_label.setText(f"正在重试下载 v{self.version}")
|
|
|
|
|
|
|
|
|
|
|
|
def download_finished(self, file_path):
|
|
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
下载完成,进入UAC授权阶段(阶段2:50-60%)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
file_path: 下载的文件路径
|
|
|
|
|
|
"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.current_stage = self.STAGE_UAC
|
|
|
|
|
|
|
|
|
|
|
|
# 更新进度到50%
|
|
|
|
|
|
self.circular_progress.setValue(50)
|
|
|
|
|
|
self.circular_progress.setText("50%")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新阶段提示
|
|
|
|
|
|
self.stage_label.setText("🔐 等待管理员授权...")
|
|
|
|
|
|
self.title_label.setText(f"✅ 下载完成")
|
|
|
|
|
|
self.status_label.setText("请在UAC窗口点击「允许」以继续...")
|
|
|
|
|
|
self.status_label.setStyleSheet("color: #28a745; font-size: 11px; font-weight: bold;")
|
|
|
|
|
|
|
|
|
|
|
|
# 禁用取消按钮
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.cancel_button.setEnabled(False)
|
|
|
|
|
|
self.retry_label.hide()
|
2025-10-29 14:58:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 启动UAC等待动画(50% → 60%)
|
|
|
|
|
|
self.start_uac_waiting_animation()
|
|
|
|
|
|
|
|
|
|
|
|
def start_uac_waiting_animation(self):
|
|
|
|
|
|
"""启动UAC等待动画(进度条在50-60%之间缓慢移动)"""
|
|
|
|
|
|
self.uac_timer = QTimer(self)
|
|
|
|
|
|
self.uac_progress = 50
|
|
|
|
|
|
|
|
|
|
|
|
def update_uac_progress():
|
|
|
|
|
|
self.uac_progress += 0.5 # 每次增加0.5%
|
|
|
|
|
|
if self.uac_progress <= 60:
|
|
|
|
|
|
self.circular_progress.setValue(int(self.uac_progress))
|
|
|
|
|
|
self.circular_progress.setText(f"{int(self.uac_progress)}%")
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 超过60%停止(等待用户授权)
|
|
|
|
|
|
self.uac_timer.stop()
|
|
|
|
|
|
|
|
|
|
|
|
self.uac_timer.timeout.connect(update_uac_progress)
|
|
|
|
|
|
self.uac_timer.start(500) # 每500ms更新一次
|
|
|
|
|
|
|
|
|
|
|
|
def uac_authorized(self):
|
|
|
|
|
|
"""
|
|
|
|
|
|
用户已授权UAC,进入安装阶段(阶段3:60-90%)
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 停止UAC等待动画
|
|
|
|
|
|
if hasattr(self, 'uac_timer'):
|
|
|
|
|
|
self.uac_timer.stop()
|
|
|
|
|
|
|
|
|
|
|
|
self.current_stage = self.STAGE_INSTALL
|
|
|
|
|
|
|
|
|
|
|
|
# 更新进度到60%
|
|
|
|
|
|
self.circular_progress.setValue(60)
|
|
|
|
|
|
self.circular_progress.setText("60%")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新阶段提示
|
|
|
|
|
|
self.stage_label.setText("⚙️ 正在安装新版本...")
|
|
|
|
|
|
self.title_label.setText("正在安装")
|
|
|
|
|
|
self.status_label.setText("正在替换程序文件,请稍候...")
|
|
|
|
|
|
self.status_label.setStyleSheet("color: #007bff; font-size: 11px; font-weight: bold;")
|
|
|
|
|
|
|
|
|
|
|
|
# 启动安装进度动画(60% → 90%)
|
|
|
|
|
|
self.start_install_animation()
|
|
|
|
|
|
|
|
|
|
|
|
def start_install_animation(self):
|
|
|
|
|
|
"""启动安装进度动画(60% → 90%)"""
|
|
|
|
|
|
self.install_timer = QTimer(self)
|
|
|
|
|
|
self.install_progress = 60
|
|
|
|
|
|
|
|
|
|
|
|
def update_install_progress():
|
|
|
|
|
|
self.install_progress += 1 # 每次增加1%
|
|
|
|
|
|
if self.install_progress <= 90:
|
|
|
|
|
|
self.circular_progress.setValue(self.install_progress)
|
|
|
|
|
|
self.circular_progress.setText(f"{self.install_progress}%")
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 安装完成,进入重启阶段
|
|
|
|
|
|
self.install_timer.stop()
|
|
|
|
|
|
self.prepare_restart()
|
|
|
|
|
|
|
|
|
|
|
|
self.install_timer.timeout.connect(update_install_progress)
|
|
|
|
|
|
self.install_timer.start(800) # 每800ms增加1%(共24秒)
|
|
|
|
|
|
|
|
|
|
|
|
def prepare_restart(self):
|
|
|
|
|
|
"""
|
|
|
|
|
|
准备重启(阶段4:90-100%)
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.current_stage = self.STAGE_RESTART
|
|
|
|
|
|
|
|
|
|
|
|
# 更新进度到90%
|
|
|
|
|
|
self.circular_progress.setValue(90)
|
|
|
|
|
|
self.circular_progress.setText("90%")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新阶段提示
|
|
|
|
|
|
self.stage_label.setText("🚀 准备启动新版本...")
|
|
|
|
|
|
self.title_label.setText("即将完成")
|
|
|
|
|
|
self.status_label.setText("程序即将重启,请稍候...")
|
|
|
|
|
|
self.status_label.setStyleSheet("color: #28a745; font-size: 11px; font-weight: bold;")
|
|
|
|
|
|
|
|
|
|
|
|
# 启动重启倒计时动画(90% → 100%)
|
|
|
|
|
|
self.start_restart_animation()
|
|
|
|
|
|
|
|
|
|
|
|
def start_restart_animation(self):
|
|
|
|
|
|
"""启动重启倒计时动画(90% → 100%)"""
|
|
|
|
|
|
self.restart_timer = QTimer(self)
|
|
|
|
|
|
self.restart_progress = 90
|
|
|
|
|
|
|
|
|
|
|
|
def update_restart_progress():
|
|
|
|
|
|
self.restart_progress += 2 # 每次增加2%
|
|
|
|
|
|
if self.restart_progress <= 100:
|
|
|
|
|
|
self.circular_progress.setValue(self.restart_progress)
|
|
|
|
|
|
self.circular_progress.setText(f"{self.restart_progress}%")
|
|
|
|
|
|
|
|
|
|
|
|
if self.restart_progress == 100:
|
|
|
|
|
|
# 🔥 到达100%,更新提示但不自动关闭
|
|
|
|
|
|
self.status_label.setText("✅ 安装完成!等待启动确认...")
|
|
|
|
|
|
self.stage_label.setText("🎉 更新成功!")
|
|
|
|
|
|
self.restart_timer.stop() # 停止定时器
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.restart_timer.stop()
|
|
|
|
|
|
|
|
|
|
|
|
self.restart_timer.timeout.connect(update_restart_progress)
|
|
|
|
|
|
self.restart_timer.start(500) # 每500ms增加2%(共2.5秒)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
def download_error(self, error_msg):
|
|
|
|
|
|
"""
|
|
|
|
|
|
下载失败
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
error_msg: 错误信息
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.title_label.setText(f"❌ 下载失败")
|
|
|
|
|
|
|
|
|
|
|
|
# 错误信息截断(避免太长)
|
|
|
|
|
|
if len(error_msg) > 80:
|
|
|
|
|
|
display_msg = error_msg[:80] + "..."
|
|
|
|
|
|
else:
|
|
|
|
|
|
display_msg = error_msg
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.stage_label.setText("⚠️ 更新失败")
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.status_label.setText(display_msg)
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.status_label.setStyleSheet("color: #dc3545; font-size: 11px; font-weight: bold;")
|
|
|
|
|
|
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.cancel_button.setText("关闭")
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.cancel_button.setEnabled(True)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
self.retry_label.hide()
|
|
|
|
|
|
|
|
|
|
|
|
def cancel_download(self):
|
|
|
|
|
|
"""取消下载"""
|
2025-10-29 14:58:09 +08:00
|
|
|
|
if self.current_stage == self.STAGE_DOWNLOAD and self.downloader and self.downloader.isRunning():
|
|
|
|
|
|
# 下载阶段可以取消
|
2025-10-28 15:59:18 +08:00
|
|
|
|
from PyQt5.QtWidgets import QMessageBox
|
|
|
|
|
|
reply = QMessageBox.question(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"确认取消",
|
2025-10-29 14:58:09 +08:00
|
|
|
|
"确定要取消更新吗?",
|
2025-10-28 15:59:18 +08:00
|
|
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
|
|
|
|
QMessageBox.No
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
|
|
|
self.downloader.cancel()
|
|
|
|
|
|
self.downloader.wait()
|
2025-10-29 14:58:09 +08:00
|
|
|
|
self.status_label.setText("更新已取消")
|
|
|
|
|
|
self.reject() # 会自动清理定时器
|
2025-10-28 15:59:18 +08:00
|
|
|
|
else:
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# 其他阶段或下载已完成,直接关闭
|
|
|
|
|
|
self.reject() # 会自动清理定时器
|
|
|
|
|
|
|
|
|
|
|
|
def cleanup_timers(self):
|
|
|
|
|
|
"""清理所有定时器"""
|
|
|
|
|
|
if hasattr(self, 'uac_timer'):
|
|
|
|
|
|
self.uac_timer.stop()
|
|
|
|
|
|
if hasattr(self, 'install_timer'):
|
|
|
|
|
|
self.install_timer.stop()
|
|
|
|
|
|
if hasattr(self, 'restart_timer'):
|
|
|
|
|
|
self.restart_timer.stop()
|
|
|
|
|
|
|
|
|
|
|
|
def accept(self):
|
|
|
|
|
|
"""对话框接受时清理资源"""
|
|
|
|
|
|
self.cleanup_timers()
|
|
|
|
|
|
super().accept()
|
|
|
|
|
|
|
|
|
|
|
|
def reject(self):
|
|
|
|
|
|
"""对话框拒绝时清理资源"""
|
|
|
|
|
|
self.cleanup_timers()
|
|
|
|
|
|
super().reject()
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
# 测试代码
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from PyQt5.QtWidgets import QApplication
|
|
|
|
|
|
|
|
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建测试对话框
|
2025-10-29 14:58:09 +08:00
|
|
|
|
dialog = UpdateProgressDialog("1.5.55")
|
2025-10-28 15:59:18 +08:00
|
|
|
|
dialog.show()
|
|
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
# 模拟完整更新流程
|
|
|
|
|
|
def simulate_update():
|
|
|
|
|
|
# 阶段1:模拟下载 (0-50%)
|
|
|
|
|
|
def simulate_download():
|
|
|
|
|
|
total_size = 100 * 1024 * 1024 # 100MB
|
|
|
|
|
|
for i in range(0, 101, 5):
|
|
|
|
|
|
downloaded = int(i * total_size / 100)
|
|
|
|
|
|
dialog.update_progress(downloaded, total_size)
|
|
|
|
|
|
QApplication.processEvents()
|
|
|
|
|
|
QTimer.singleShot(100, lambda: None) # 延迟
|
|
|
|
|
|
|
|
|
|
|
|
# 下载完成
|
|
|
|
|
|
QTimer.singleShot(2000, lambda: dialog.download_finished("/tmp/test.exe"))
|
|
|
|
|
|
|
|
|
|
|
|
# 阶段2:模拟UAC授权 (50-60%)
|
|
|
|
|
|
QTimer.singleShot(5000, lambda: dialog.uac_authorized())
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
simulate_download()
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
2025-10-29 14:58:09 +08:00
|
|
|
|
QTimer.singleShot(500, simulate_update)
|
2025-10-28 15:59:18 +08:00
|
|
|
|
|
|
|
|
|
|
sys.exit(app.exec_())
|