Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e78ad55b4e |
No files matched your search
@@ -1,8 +1,8 @@
|
||||
# PGU-NetFileSyncWin 项目维护说明
|
||||
|
||||
当前应用版本:`v1.0.5`(2026-09-27)
|
||||
当前应用版本:`v1.0.6`(2026-09-27)
|
||||
|
||||
本版本变更:极速服务 V3、CPU 自适应并发、HTTP 长连接、服务版本显示与旧版升级、连接断开后的 user linger、下载重试稳定性和项目文档整理。
|
||||
本版本变更:下载完成结果弹窗、失败项目明细、用时和平均速度统计、完成后速度条清零;并保留极速服务 V3、CPU 自适应并发、HTTP 长连接、服务版本显示与旧版升级、连接断开后的 user linger。
|
||||
|
||||
## 项目定位
|
||||
|
||||
@@ -100,7 +100,7 @@ remote_path=/home/clear/Data
|
||||
|
||||
- `100MB/s` 映射到满格。
|
||||
- 下载中蓝色。
|
||||
- 下载完成后满格绿色。
|
||||
- 下载完成弹窗关闭后速度条清零,恢复为等待状态。
|
||||
|
||||
局域网扫描端口:
|
||||
|
||||
|
||||
@@ -25,13 +25,14 @@ PGU-NetFileSyncWin 是一个 Windows 桌面文件同步工具,用于连接 PGU
|
||||
|
||||
## 当前版本
|
||||
|
||||
当前应用版本:`v1.0.5`
|
||||
当前应用版本:`v1.0.6`
|
||||
|
||||
本版本重点修复极速下载稳定性:客户端会同时考虑 Windows 本机和 PGU 设备的 CPU 线程数;极速服务按远端 CPU 建立并发槽位,支持 HTTP 长连接、连接队列扩容、服务版本显示和旧版一键升级。
|
||||
|
||||
- 高速通道区域显示已安装的极速服务版本,例如 `极速 v3`。
|
||||
- 检测到旧版时显示实际版本,并提供 `升级极速`。
|
||||
- 大量小文件下载复用连接,减少速度波动和重复重试。
|
||||
- 下载结束后弹窗显示成功/失败状态、已传输大小、用时、平均速度和失败项目;底部速度条自动清零。
|
||||
|
||||
## 发布版
|
||||
|
||||
|
||||
@@ -177,13 +177,22 @@ IP、端口、用户名和远程路径可以直接用记事本修改。密码请
|
||||
|
||||
如果最终仍有文件失败,软件会在下载结束后统一提示。
|
||||
|
||||
下载结束后会弹出结果窗口,显示:
|
||||
|
||||
- 是否全部成功或被取消。
|
||||
- 已传输大小和计划大小。
|
||||
- 总用时和平均速度。
|
||||
- 失败项目数量及失败原因。
|
||||
|
||||
结果窗口关闭后,底部速度条会自动恢复为 0,等待下一次下载。
|
||||
|
||||
## 11. 速度条
|
||||
|
||||
底部进度条是速度条,不是总进度条。
|
||||
|
||||
- `100MB/s` 对应满格。
|
||||
- 下载过程中为蓝色。
|
||||
- 下载完成后会拉满并变为绿色。
|
||||
- 下载结果弹窗关闭后自动清零,恢复为等待下一次下载的状态。
|
||||
|
||||
下载完成后会显示:
|
||||
|
||||
|
||||
+44
-20
@@ -83,7 +83,7 @@ from PyQt5.QtWidgets import (
|
||||
|
||||
APP_ORG = "ClearCore"
|
||||
APP_NAME = "PGU4-Sync"
|
||||
APP_VERSION = "1.0.5"
|
||||
APP_VERSION = "1.0.6"
|
||||
CONFIG_FILE_NAME = "PGU-NetFileSyncWin.ini"
|
||||
PASSWORD_PROTECTED_KEY = "password_protected"
|
||||
PASSWORD_LEGACY_KEY = "password"
|
||||
@@ -4673,29 +4673,53 @@ class MainWindow(QMainWindow):
|
||||
elapsed = max(0.001, time.monotonic() - self._download_start_time) if self._download_start_time else 0.001
|
||||
avg_speed = self._done_bytes / elapsed
|
||||
summary = f"{human_size(self._done_bytes)} 用时 {format_duration(elapsed)} 平均 {human_size(int(avg_speed))}/s"
|
||||
if cancelled:
|
||||
self.progress.setFormat(f"已取消 {summary} / {human_size(effective_total)}")
|
||||
self.transfer_detail_label.setText(f"传输已取消 · {summary}")
|
||||
self.statusBar().showMessage("下载已取消", 5000)
|
||||
failures = list(self._download_errors)
|
||||
if failures:
|
||||
details = "\n".join(
|
||||
f"{Path(path).name}: {message}" for path, message in failures[:12]
|
||||
)
|
||||
if len(failures) > 12:
|
||||
details += f"\n... 还有 {len(failures) - 12} 个失败项目"
|
||||
else:
|
||||
self.set_meter_state("done")
|
||||
self.set_progress_value(100, animate=False)
|
||||
if self._download_errors:
|
||||
self.progress.setFormat(f"下载完成 {summary} 失败 {len(self._download_errors)} 个")
|
||||
self.transfer_detail_label.setText(
|
||||
f"下载完成 · {summary} · 失败 {len(self._download_errors)} 个"
|
||||
details = "没有失败项目。"
|
||||
|
||||
if cancelled:
|
||||
title = "下载已取消"
|
||||
message = (
|
||||
f"已取消当前下载。\n\n"
|
||||
f"已传输:{summary}\n"
|
||||
f"计划大小:{human_size(effective_total)}\n\n"
|
||||
f"失败项目:{len(failures)} 个"
|
||||
)
|
||||
self.statusBar().showMessage("下载已取消", 5000)
|
||||
QMessageBox.information(self, title, message)
|
||||
else:
|
||||
if failures:
|
||||
title = "下载完成,但有文件失败"
|
||||
message = (
|
||||
f"已完成下载流程,但有 {len(failures)} 个项目失败。\n\n"
|
||||
f"已传输:{summary}\n"
|
||||
f"计划大小:{human_size(effective_total)}\n\n"
|
||||
f"失败项目:\n{details}"
|
||||
)
|
||||
self.statusBar().showMessage(f"下载完成,失败 {len(self._download_errors)} 个文件", 8000)
|
||||
details = "\n".join(
|
||||
f"{Path(path).name}: {message}" for path, message in self._download_errors[:8]
|
||||
)
|
||||
if len(self._download_errors) > 8:
|
||||
details += f"\n... 还有 {len(self._download_errors) - 8} 个"
|
||||
QMessageBox.warning(self, "部分文件下载失败", details)
|
||||
self.statusBar().showMessage(f"下载完成,失败 {len(failures)} 个文件", 8000)
|
||||
QMessageBox.warning(self, title, message)
|
||||
else:
|
||||
self.progress.setFormat(f"下载完成 {summary}")
|
||||
self.transfer_detail_label.setText(f"下载完成 · {summary}")
|
||||
title = "下载完成"
|
||||
message = (
|
||||
f"全部文件已成功下载。\n\n"
|
||||
f"已传输:{summary}\n"
|
||||
f"计划大小:{human_size(effective_total)}\n\n"
|
||||
f"{details}"
|
||||
)
|
||||
self.statusBar().showMessage("下载完成", 5000)
|
||||
QMessageBox.information(self, title, message)
|
||||
|
||||
self.set_meter_state("speed")
|
||||
self.set_progress_value(0, animate=False)
|
||||
self.progress.setFormat("就绪 速度 0 B/s / 100.0 MB/s")
|
||||
self.speed_label.setText("速度 0 B/s")
|
||||
self.transfer_detail_label.setText("等待下载")
|
||||
self.controller = None
|
||||
|
||||
def _run_worker(self, worker: QObject, callback, start: bool = True) -> QThread:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import os
|
||||
import stat
|
||||
import time
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PyQt5.QtCore import QItemSelectionModel
|
||||
from PyQt5.QtWidgets import QApplication, QStatusBar
|
||||
from PyQt5.QtWidgets import QApplication, QMessageBox, QStatusBar
|
||||
|
||||
from pgu4_sync import ConnectionDialog, MainWindow, RemoteEntry, adaptive_worker_count
|
||||
|
||||
@@ -171,3 +173,23 @@ class CommercialUiTests(unittest.TestCase):
|
||||
|
||||
window.set_fast_button_state("upgrade", "2")
|
||||
self.assertEqual("极速 v2 · 可升级", window.fast_status_badge.text())
|
||||
|
||||
def test_download_completion_reports_metrics_and_clears_speed_meter(self):
|
||||
window = MainWindow()
|
||||
self.addCleanup(window.close)
|
||||
window.controller = object()
|
||||
window._active_download = True
|
||||
window._download_start_time = time.monotonic() - 2
|
||||
window._total_bytes = 2048
|
||||
window._done_bytes = 2048
|
||||
window._download_errors = []
|
||||
|
||||
with patch.object(QMessageBox, "information") as information:
|
||||
window.on_download_all_done(False)
|
||||
|
||||
information.assert_called_once()
|
||||
message = information.call_args.args[2]
|
||||
self.assertIn("用时", message)
|
||||
self.assertIn("平均", message)
|
||||
self.assertEqual(0, window.progress.value())
|
||||
self.assertEqual("等待下载", window.transfer_detail_label.text())
|
||||
@@ -35,7 +35,7 @@ class UpdateTests(unittest.TestCase):
|
||||
self.assertEqual([True], finished)
|
||||
|
||||
def test_version_comparison(self):
|
||||
self.assertTrue(is_newer_version("1.0.6", APP_VERSION))
|
||||
self.assertTrue(is_newer_version("1.0.7", APP_VERSION))
|
||||
self.assertTrue(is_newer_version("v1.1.0", "1.0.9"))
|
||||
self.assertTrue(is_newer_version("1.0.10", "1.0.2"))
|
||||
self.assertFalse(is_newer_version("v1.0.4", APP_VERSION))
|
||||
|
||||
Reference in new issue
Block a user