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:
+20
-16
@@ -54,13 +54,14 @@ echo "===PROC==="
|
||||
ps -e -o stat= 2>/dev/null | awk '{r+=($1~/^R/); s+=($1~/^S/); d+=($1~/^D/); z+=($1~/^Z/); t++} END{printf "PROC_TOTAL=%d\nPROC_RUNNING=%d\nPROC_SLEEP=%d\nPROC_DISK=%d\nPROC_ZOMBIE=%d\n", t, r, s, d, z}'
|
||||
# 进程列表:etimes/times 都是纯数字,避免 start 字段含 "Jul 21" 多列错位
|
||||
# comm 字段可能含空格,前 9 列用 \t 拼,comm 用换行做记录结束
|
||||
ps -eo pid,user,pcpu,pmem,vsz,rss,stat,etimes,times,args --sort=-pcpu --no-headers 2>/dev/null \
|
||||
| head -200 \
|
||||
# 字段:pid ppid user pcpu pmem vsz rss stat pri nice etimes times args
|
||||
ps -eo pid,ppid,user,pcpu,pmem,vsz,rss,stat,pri,nice,etimes,times,args --sort=-pcpu --no-headers 2>/dev/null \
|
||||
| head -500 \
|
||||
| awk '{
|
||||
out="PROC\t";
|
||||
for(i=1;i<=9;i++) out=out $i "\t";
|
||||
for(i=1;i<=12;i++) out=out $i "\t";
|
||||
rest="";
|
||||
for(i=10;i<=NF;i++) rest=(i==10?$i:rest " " $i);
|
||||
for(i=13;i<=NF;i++) rest=(i==13?$i:rest " " $i);
|
||||
print out rest
|
||||
}'
|
||||
echo "===HOST==="
|
||||
@@ -167,29 +168,32 @@ echo "OS=$(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || uname -s)"
|
||||
result["proc_disk"] = 0
|
||||
result["proc_zombie"] = 0
|
||||
|
||||
# 进程列表(远程用 \t 分隔前 9 列,第 10 列开始是 args/comm,可能含空格)
|
||||
# 进程列表(远程用 \t 分隔前 12 列,第 13 列开始是 args/comm,可能含空格)
|
||||
result["processes"] = []
|
||||
for line in out.splitlines():
|
||||
if not line.startswith("PROC\t"):
|
||||
continue
|
||||
payload = line[5:] # 去掉 "PROC\t" 前缀
|
||||
fields = payload.split("\t", 9) # 只切前 9 次
|
||||
if len(fields) < 10:
|
||||
fields = payload.split("\t", 12) # 只切前 12 次
|
||||
if len(fields) < 13:
|
||||
continue
|
||||
try:
|
||||
result["processes"].append({
|
||||
"pid": int(fields[0]),
|
||||
"user": fields[1],
|
||||
"pcpu": float(fields[2]),
|
||||
"pmem": float(fields[3]),
|
||||
"vsz": int(fields[4]),
|
||||
"rss": int(fields[5]),
|
||||
"stat": fields[6],
|
||||
"ppid": int(fields[1]) if fields[1].isdigit() else 0,
|
||||
"user": fields[2],
|
||||
"pcpu": float(fields[3]),
|
||||
"pmem": float(fields[4]),
|
||||
"vsz": int(fields[5]),
|
||||
"rss": int(fields[6]),
|
||||
"stat": fields[7],
|
||||
"pri": int(fields[8]) if fields[8].lstrip("-").isdigit() else 0,
|
||||
"nice": int(fields[9]) if fields[9].lstrip("-").isdigit() else 0,
|
||||
# etimes: 自启动以来的秒数(整数)
|
||||
"etime": int(fields[7]) if fields[7].isdigit() else 0,
|
||||
"etime": int(fields[10]) if fields[10].isdigit() else 0,
|
||||
# times: 累计 CPU 时间秒数
|
||||
"time": int(fields[8]) if fields[8].isdigit() else 0,
|
||||
"comm": fields[9].strip(),
|
||||
"time": int(fields[11]) if fields[11].isdigit() else 0,
|
||||
"comm": fields[12].strip(),
|
||||
})
|
||||
except (ValueError, IndexError):
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user