diff --git a/tests/test_transfer.py b/tests/test_transfer.py index 08b8dad..c056c7a 100644 --- a/tests/test_transfer.py +++ b/tests/test_transfer.py @@ -254,3 +254,19 @@ def test_apply_policy_global_overwrite_keeps_explicit_no(): answers = {"a.txt": "n", "*": "a"} skipped = transfer.apply_policy(conflicts, "ask", answers) assert skipped == ["a.txt"] + +# ---------- 速度统计 ---------- + +def test_speed_stats_basic(): + h = [10 * 1024 * 1024, 20 * 1024 * 1024, 15 * 1024 * 1024] + cur, peak, avg = transfer.speed_stats(h) + assert cur == 15 * 1024 * 1024 + assert peak == 20 * 1024 * 1024 + assert avg == 15 * 1024 * 1024 + +def test_speed_stats_empty(): + assert transfer.speed_stats([]) == (0.0, 0.0, 0.0) + +def test_speed_stats_single(): + cur, peak, avg = transfer.speed_stats([8 * 1024 * 1024]) + assert (cur, peak, avg) == (8 * 1024 * 1024, 8 * 1024 * 1024, 8 * 1024 * 1024) diff --git a/transfer.py b/transfer.py index 7327542..74bcbdb 100644 --- a/transfer.py +++ b/transfer.py @@ -55,6 +55,16 @@ def human_speed(bps: float) -> str: return f"{human_size(bps)}/s" +def speed_stats(history) -> tuple[float, float, float]: + """速度统计: (当前, 峰值, 平均) 字节/秒""" + if not history: + return 0.0, 0.0, 0.0 + cur = float(history[-1]) + peak = max(history) + avg = sum(history) / len(history) + return cur, peak, avg + + # ---------------------------------------------------------------- 数据结构 @dataclass @@ -689,7 +699,8 @@ TUI_CSS = """ Screen { layout: vertical; } #top { height: 7; border: round $primary; padding: 0 1; } #stats-line { height: 1; content-align: left middle; } -#chart-box { height: 5; border: round $secondary; padding: 0 1; } +#chart-box { height: 6; border: round $secondary; padding: 0 1; } +#speed-stats { height: 1; color: $text-muted; } #logs { height: 1fr; } #file-log { width: 1fr; border: round $success; } #error-log { width: 1fr; border: round $error; } @@ -734,8 +745,9 @@ class TransferApp(App): yield ProgressBar(total=100, show_eta=True, id="total-bar") yield ProgressBar(total=100, id="file-bar") with Vertical(id="chart-box"): - yield Static("速度曲线 (最近 80 秒)") + yield Static("速度曲线 (最近 80 秒)", id="speed-title") yield Sparkline([0], summary_function=max, id="speed-spark") + yield Static("", id="speed-stats") with Horizontal(id="logs"): yield RichLog(id="file-log", highlight=True, markup=True) yield RichLog(id="error-log", highlight=True, markup=True) @@ -920,6 +932,10 @@ class TransferApp(App): if st.speed_bps: self.speed_history.append(st.speed_bps) self.query_one("#speed-spark", Sparkline).data = list(self.speed_history) + cur, peak, avg = speed_stats(self.speed_history) + self.query_one("#speed-stats", Static).update( + f"当前 {human_speed(cur)} | 峰值 {human_speed(peak)} | " + f"平均 {human_speed(avg)} ({len(self.speed_history)} 样本)") parts = [f"总进度 {st.pct:5.1f}%"] if st.bytes_total: parts.append(f"{human_size(st.bytes_done)}/{human_size(st.bytes_total)}")