feat: 优化更新下载界面 v3.4.2 #patch - 1.优化下载进度对话框布局和样式 2.增大信息显示区域,提升可读性 3.美化进度条和状态显示 4.添加实时下载速度和剩余时间显示 5.更新版本截图
This commit is contained in:
BIN
banbenjietu.png
BIN
banbenjietu.png
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 56 KiB |
@@ -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_()
|
||||||
Reference in New Issue
Block a user