feat: 优化更新下载界面 v3.4.2 #patch - 1.优化下载进度对话框布局和样式 2.增大信息显示区域,提升可读性 3.美化进度条和状态显示 4.添加实时下载速度和剩余时间显示 5.更新版本截图

This commit is contained in:
huangzhenpc
2025-02-13 16:27:25 +08:00
parent d81244c7e4
commit b5cbf0779b
2 changed files with 354 additions and 20 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

@@ -10,6 +10,8 @@ from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtGui import QIcon, QPixmap
import time import time
import requests
from urllib.parse import quote
sys.path.append(str(Path(__file__).parent.parent)) sys.path.append(str(Path(__file__).parent.parent))
@@ -152,6 +154,150 @@ class UpdateWorker(QThread):
except Exception as e: except Exception as e:
self.finished.emit((False, False, str(e))) self.finished.emit((False, False, str(e)))
class DownloadProgressDialog(QDialog):
"""下载进度对话框"""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("正在下载更新")
self.setFixedSize(400, 300)
self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
layout = QVBoxLayout()
# 添加图标
icon_label = QLabel()
icon_label.setPixmap(self.style().standardIcon(QStyle.SP_DesktopIcon).pixmap(32, 32))
icon_label.setAlignment(Qt.AlignCenter)
layout.addWidget(icon_label)
# 下载状态标签
self.status_label = QLabel("正在连接服务器...")
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setStyleSheet("""
color: #0d6efd;
font-size: 14px;
font-weight: bold;
padding: 10px;
""")
layout.addWidget(self.status_label)
# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setStyleSheet("""
QProgressBar {
border: 2px solid #e9ecef;
border-radius: 5px;
text-align: center;
min-height: 20px;
background-color: #f8f9fa;
}
QProgressBar::chunk {
background-color: #0d6efd;
border-radius: 3px;
}
""")
layout.addWidget(self.progress_bar)
# 下载信息框
info_frame = QFrame()
info_frame.setStyleSheet("""
QFrame {
background-color: #f8f9fa;
border-radius: 8px;
border: 1px solid #dee2e6;
margin: 10px;
padding: 20px;
}
QLabel {
color: #495057;
font-size: 14px;
padding: 8px;
margin: 2px;
background: white;
border-radius: 4px;
border: 1px solid #e9ecef;
}
""")
info_layout = QVBoxLayout(info_frame)
info_layout.setSpacing(10) # 增加标签之间的间距
# 下载速度
self.speed_label = QLabel("下载速度: --")
self.speed_label.setMinimumHeight(35) # 设置最小高度
info_layout.addWidget(self.speed_label)
# 文件大小
self.size_label = QLabel("文件大小: --")
self.size_label.setMinimumHeight(35) # 设置最小高度
info_layout.addWidget(self.size_label)
# 预计剩余时间
self.time_label = QLabel("预计剩余时间: --")
self.time_label.setMinimumHeight(35) # 设置最小高度
info_layout.addWidget(self.time_label)
layout.addWidget(info_frame)
self.setLayout(layout)
# 初始化变量
self.start_time = time.time()
self.last_update_time = time.time()
self.last_downloaded = 0
def update_progress(self, downloaded_size, total_size):
"""更新进度信息"""
if total_size > 0:
percentage = (downloaded_size / total_size) * 100
self.progress_bar.setValue(int(percentage))
# 计算下载速度
current_time = time.time()
time_diff = current_time - self.last_update_time
if time_diff >= 0.5: # 每0.5秒更新一次
speed = (downloaded_size - self.last_downloaded) / time_diff
self.last_downloaded = downloaded_size
self.last_update_time = current_time
# 更新下载信息
speed_text = self._format_speed(speed)
self.speed_label.setText(f"下载速度: {speed_text}")
# 更新文件大小信息
total_mb = total_size / (1024 * 1024)
downloaded_mb = downloaded_size / (1024 * 1024)
self.size_label.setText(f"文件大小: {downloaded_mb:.1f} MB / {total_mb:.1f} MB")
# 更新预计剩余时间
if speed > 0:
remaining_bytes = total_size - downloaded_size
remaining_time = remaining_bytes / speed
time_text = self._format_time(remaining_time)
self.time_label.setText(f"预计剩余时间: {time_text}")
# 更新状态文本
self.status_label.setText(f"正在下载更新... {percentage:.1f}%")
def _format_speed(self, speed_bytes):
"""格式化速度显示"""
if speed_bytes > 1024 * 1024:
return f"{speed_bytes / (1024 * 1024):.1f} MB/s"
elif speed_bytes > 1024:
return f"{speed_bytes / 1024:.1f} KB/s"
else:
return f"{speed_bytes:.1f} B/s"
def _format_time(self, seconds):
"""格式化时间显示"""
if seconds < 60:
return f"{seconds:.0f}"
elif seconds < 3600:
minutes = seconds / 60
return f"{minutes:.0f}分钟"
else:
hours = seconds / 3600
return f"{hours:.1f}小时"
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@@ -787,7 +933,6 @@ class MainWindow(QMainWindow):
border-radius: 4px; border-radius: 4px;
font-size: 13px; font-size: 13px;
min-width: 100px; min-width: 100px;
margin: 10px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #bb2d3b; background-color: #bb2d3b;
@@ -1623,33 +1768,222 @@ class MainWindow(QMainWindow):
return return
try: try:
# 创建下载目录 # 创建下载目录优先使用D盘如果不存在则使用当前程序目录
download_dir = Path.home() / "Downloads" / "CursorHelper" if Path("D:/").exists():
download_dir = Path("D:/CursorHelper/updates")
else:
download_dir = Path(__file__).parent.parent / "updates"
download_dir.mkdir(parents=True, exist_ok=True) download_dir.mkdir(parents=True, exist_ok=True)
# 下载文件名 # 下载文件名
file_name = download_url.split('/')[-1] file_name = download_url.split('/')[-1]
save_path = download_dir / file_name save_path = download_dir / file_name
self.show_loading_dialog("正在下载更新...") # 创建并显示进度对话框
progress_dialog = DownloadProgressDialog(self)
progress_dialog.show()
# 开始下载 # 开始下载
success, message = self.version_manager.download_update(download_url, str(save_path)) try:
self.hide_loading_dialog() # 处理下载地址中的中文字符
url_parts = download_url.split('/')
if success: url_parts[-1] = quote(url_parts[-1])
self.show_custom_message( encoded_url = '/'.join(url_parts)
"下载完成",
"更新包下载成功", # 设置请求头
f"更新包已下载到:\n{save_path}\n\n请关闭程序后运行更新包完成更新。", headers = {
QStyle.SP_DialogApplyButton, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
"#198754" 'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive'
}
response = requests.get(
encoded_url,
stream=True,
headers=headers,
timeout=30
) )
# 退出程序 response.raise_for_status()
self.quit_application()
else: # 获取文件大小
self.show_custom_error("下载失败", message) total_size = int(response.headers.get('content-length', 0))
if total_size == 0:
progress_dialog.hide()
self.show_custom_error("下载失败", "无法获取文件大小,下载地址可能无效")
return False, "无法获取文件大小"
# 更新进度条范围
progress_dialog.progress_bar.setRange(0, 100)
# 下载文件
downloaded_size = 0
block_size = 8192
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=block_size):
if chunk:
f.write(chunk)
downloaded_size += len(chunk)
# 更新进度
progress_dialog.update_progress(downloaded_size, total_size)
QApplication.processEvents() # 保持界面响应
# 验证文件大小
actual_size = os.path.getsize(save_path)
if actual_size != total_size:
progress_dialog.hide()
os.remove(save_path)
self.show_custom_error("下载失败", "文件下载不完整,请重试")
return False, "文件下载不完整"
progress_dialog.hide()
# 显示下载完成对话框
self._show_download_complete_dialog(save_path)
return True, "下载成功"
except requests.exceptions.Timeout:
progress_dialog.hide()
self.show_custom_error("下载失败", "下载超时,请检查网络连接后重试")
return False, "下载超时"
except requests.exceptions.RequestException as e:
progress_dialog.hide()
self.show_custom_error("下载失败", f"下载出错: {str(e)}")
return False, str(e)
except Exception as e:
progress_dialog.hide()
self.show_custom_error("下载失败", f"下载过程中发生错误: {str(e)}")
return False, str(e)
except Exception as e: except Exception as e:
self.hide_loading_dialog() self.show_custom_error("下载失败", f"下载更新时发生错误: {str(e)}")
self.show_custom_error("下载失败", f"下载更新时发生错误: {str(e)}") return False, str(e)
def _show_download_complete_dialog(self, save_path):
"""显示下载完成对话框"""
# 创建自定义消息框
msg = QDialog(self)
msg.setWindowTitle("下载完成")
msg.setFixedWidth(500)
msg.setWindowFlags(msg.windowFlags() & ~Qt.WindowContextHelpButtonHint)
layout = QVBoxLayout()
# 添加图标
icon_label = QLabel()
icon_label.setPixmap(self.style().standardIcon(QStyle.SP_DialogApplyButton).pixmap(32, 32))
icon_label.setAlignment(Qt.AlignCenter)
layout.addWidget(icon_label)
# 添加标题
title_label = QLabel("更新包下载成功")
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("""
font-size: 16px;
font-weight: bold;
color: #198754;
padding: 10px;
""")
layout.addWidget(title_label)
# 添加文件信息框
info_frame = QFrame()
info_frame.setStyleSheet("""
QFrame {
background-color: #f8f9fa;
border-radius: 8px;
border: 1px solid #dee2e6;
margin: 10px;
padding: 15px;
}
QLabel {
color: #333333;
font-size: 14px;
padding: 5px;
}
""")
info_layout = QVBoxLayout(info_frame)
# 文件路径
path_label = QLabel(f"文件保存在:\n{save_path}")
path_label.setWordWrap(True)
path_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
info_layout.addWidget(path_label)
# 文件大小
size_mb = save_path.stat().st_size / (1024 * 1024)
size_label = QLabel(f"文件大小:{size_mb:.2f} MB")
info_layout.addWidget(size_label)
layout.addWidget(info_frame)
# 提示信息
tip_label = QLabel("请关闭程序后运行更新包完成更新。")
tip_label.setStyleSheet("color: #dc3545; font-weight: bold; padding: 10px;")
layout.addWidget(tip_label)
# 按钮区域
btn_layout = QHBoxLayout()
# 打开文件按钮
open_file_btn = QPushButton("打开文件")
open_file_btn.setStyleSheet("""
QPushButton {
background-color: #0d6efd;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
font-size: 13px;
min-width: 100px;
}
QPushButton:hover {
background-color: #0b5ed7;
}
""")
open_file_btn.clicked.connect(lambda: os.startfile(str(save_path)))
btn_layout.addWidget(open_file_btn)
# 打开文件夹按钮
open_dir_btn = QPushButton("打开文件夹")
open_dir_btn.setStyleSheet("""
QPushButton {
background-color: #198754;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
font-size: 13px;
min-width: 100px;
}
QPushButton:hover {
background-color: #157347;
}
""")
open_dir_btn.clicked.connect(lambda: os.startfile(str(save_path.parent)))
btn_layout.addWidget(open_dir_btn)
# 退出按钮
quit_btn = QPushButton("立即退出")
quit_btn.setStyleSheet("""
QPushButton {
background-color: #dc3545;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
font-size: 13px;
min-width: 100px;
font-weight: bold;
}
QPushButton:hover {
background-color: #bb2d3b;
}
""")
quit_btn.clicked.connect(lambda: (msg.accept(), self.quit_application()))
btn_layout.addWidget(quit_btn)
layout.addLayout(btn_layout)
msg.setLayout(layout)
msg.exec_()