diff --git a/banbenjietu.png b/banbenjietu.png index c090d6e..9ae94f2 100644 Binary files a/banbenjietu.png and b/banbenjietu.png differ diff --git a/gui/main_window.py b/gui/main_window.py index fc0af90..40f845f 100644 --- a/gui/main_window.py +++ b/gui/main_window.py @@ -10,6 +10,8 @@ from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal from PyQt5.QtGui import QIcon, QPixmap import time +import requests +from urllib.parse import quote sys.path.append(str(Path(__file__).parent.parent)) @@ -152,6 +154,150 @@ class UpdateWorker(QThread): except Exception as 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): def __init__(self): super().__init__() @@ -787,7 +933,6 @@ class MainWindow(QMainWindow): border-radius: 4px; font-size: 13px; min-width: 100px; - margin: 10px; } QPushButton:hover { background-color: #bb2d3b; @@ -1623,33 +1768,222 @@ class MainWindow(QMainWindow): return try: - # 创建下载目录 - download_dir = Path.home() / "Downloads" / "CursorHelper" + # 创建下载目录(优先使用D盘,如果不存在则使用当前程序目录) + 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) # 下载文件名 file_name = download_url.split('/')[-1] 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)) - self.hide_loading_dialog() - - if success: - self.show_custom_message( - "下载完成", - "更新包下载成功", - f"更新包已下载到:\n{save_path}\n\n请关闭程序后运行更新包完成更新。", - QStyle.SP_DialogApplyButton, - "#198754" + try: + # 处理下载地址中的中文字符 + url_parts = download_url.split('/') + url_parts[-1] = quote(url_parts[-1]) + encoded_url = '/'.join(url_parts) + + # 设置请求头 + headers = { + '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', + 'Accept': '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive' + } + + response = requests.get( + encoded_url, + stream=True, + headers=headers, + timeout=30 ) - # 退出程序 - self.quit_application() - else: - self.show_custom_error("下载失败", message) + response.raise_for_status() + + # 获取文件大小 + 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: - self.hide_loading_dialog() - self.show_custom_error("下载失败", f"下载更新时发生错误: {str(e)}") \ No newline at end of file + 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_() \ No newline at end of file