feat: overhaul process monitoring UI - more info, tree view, details, batch ops

User feedback: '进程比较关键,以下显示太少了,需要继续优化下监控UI'

Process monitor panel was rebuilt:

1. core/monitor.py: 200 -> 500 processes, added PPID / PRI / NICE
   New ps fields: pid ppid user pcpu pmem vsz rss stat pri nice etimes times args
   Python parser: new fields ppid, pri, nice

2. ui/widgets.py MonitorPanel:
   a) Toolbar: search box + sort dropdown (CPU/MEM/PID/start/user/comm)
      + view toggle (Flat / Tree) + CPU% min filter (SpinBox 0-100%)
      + batch kill button
   b) Table: 12 columns (Flat) / 13 cols (Tree + indent col)
      PID PPID USER CPU% MEM% RSS STAT PRI NI '启动' 'CPU时间' 命令
      - Compact row height 20px (was default 30)
      - Extended selection (Ctrl/Shift multi)
      - Color highlights:
          CPU% >=80 red bg+fg bold, >=50 red fg bold, >=20 orange
          MEM% >=15 orange bg+fg bold, >=5 orange
          STAT: Z orange bg + orange-red fg bold, R green, D purple
          NI: negative green, positive orange
      - Red bg on EXTREME rows for instant visibility
   c) Tree view: builds PPID tree with ├─/└─/│ connectors;
      root processes (PID 1) at depth 0, sorted by CPU within siblings.
      Useful for 'which process is spawning these things?'
   d) Right side panel:
      - 进程详情 (QTextEdit dark theme): full PID/PPID/USER/STAT/PRI/NI/
        CPU/MEM/RSS/VSZ/etime/cputime, command, parent process name,
        child processes list (top 10)
      - 🔥 CPU TOP 5 (QListWidget dark): click to jump+select in main table
      - 💾 内存 TOP 5 (QListWidget dark): same
   e) Right-click menu enhanced:
      kill (SIGTERM) | kill -9 (SIGKILL) | batch kill (if multi-select)
      | copy PID | copy command | filter by command | view details
   f) Batch kill: collects all selected PIDs, runs 'kill p1 p2 p3 ...'
      in a single SSH call, then loops 'kill -0' to report survivors.
      One confirmation dialog lists top 10 + '还有 N 个'.
   g) Status row at bottom: 'Ctrl+A 全选, Shift+点击多选, 右键批量操作'

3. test_process_monitor.py: 5/5 tests pass with 500 rows, tree view,
   TOP 5, CPU min filter, etc.

All tests pass: core 6/6 + UI 3/3 + E2E 6/6 + terminal 5/5 + proc 5/5
Build: 57 MB single-file exe.
This commit is contained in:
Hermes
2026-07-28 22:39:26 +08:00
parent 5aa833ebee
commit 5e69b1c3e2
3 changed files with 524 additions and 108 deletions
+46 -1
View File
@@ -65,8 +65,53 @@ def main():
# 验证第一列 (PID) 是数字
pid_text = panel.proc_table.item(0, 0).text()
assert pid_text.isdigit(), f"PID 列不是数字: {pid_text!r}"
# 验证 PPID 列存在
ppid_text = panel.proc_table.item(0, 1).text()
assert ppid_text.lstrip("-").isdigit() or ppid_text == "-", f"PPID 错: {ppid_text!r}"
# 验证 PRI/NI 列
pri_text = panel.proc_table.item(0, 7).text()
assert pri_text.lstrip("-").isdigit(), f"PRI 错: {pri_text!r}"
print(f" ✓ 进程表行数: {panel.proc_table.rowCount()}")
print(f" ✓ 第 1 行 PID={pid_text} CPU={panel.proc_table.item(0, 2).text()}%")
print(f" ✓ 第 1 行 PID={pid_text} PPID={ppid_text} CPU={panel.proc_table.item(0, 3).text()}% PRI={pri_text}")
# TOP 5
assert panel.proc_top_cpu.count() == 5
assert panel.proc_top_mem.count() == 5
top_cpu_first = panel.proc_top_cpu.item(0).text()
top_mem_first = panel.proc_top_mem.item(0).text()
assert "%" in top_cpu_first and "%" in top_mem_first
print(f" ✓ TOP CPU: {top_cpu_first[:60]}")
print(f" ✓ TOP MEM: {top_mem_first[:60]}")
# 树形切换
panel.proc_view_combo.setCurrentIndex(1)
app.processEvents()
# 第一行应该有缩进符号(树根没有符号,但深度=0)
first_cell = panel.proc_table.item(0, 0).text()
assert "├─" in first_cell or first_cell == "", f"树形第 1 行异常: {first_cell!r}"
# 列数应该 13(树形多 1 列)
assert panel.proc_table.columnCount() == 13
print(f" ✓ 树形视图生效 (第 1 行: {first_cell!r}, 13 列)")
# 切回扁平
panel.proc_view_combo.setCurrentIndex(0)
app.processEvents()
assert panel.proc_table.columnCount() == 12
print(f" ✓ 切回扁平 (12 列)")
# CPU 过滤
panel.proc_min_cpu.setValue(10)
app.processEvents()
after_min = panel.proc_table.rowCount()
if after_min > 0:
# 验证所有行的 CPU 列都 >= 10
for r in range(min(after_min, 10)):
cpu_val = float(panel.proc_table.item(r, 3).text())
assert cpu_val >= 10, f"CPU 过滤失败: 行 {r} CPU={cpu_val}"
print(f" ✓ CPU>=10% 过滤后剩 {after_min} 行,全部 CPU≥10%")
panel.proc_min_cpu.setValue(0)
app.processEvents()
assert panel.proc_table.rowCount() == len(m["processes"])
print(f" ✓ CPU 过滤清空恢复 {panel.proc_table.rowCount()}")
print("[4/5] 搜索过滤")
# 找一个常见的进程名