测试workbuddy写硬件监控软件
本帖最后由 chinajz 于 2026-3-15 10:35 编辑workbuddy写的硬件监控软件,测试结果,达到预期要求。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CPU/GPU 温度监控 — 图形界面 (gui.py)
依赖:tkinter(Python 内置)、matplotlib
启动方式:
python main.py --gui
python gui.py (直接运行)
"""
import sys
import time
import threading
from datetime import datetime
from collections import deque
if sys.platform == "win32":
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
try:
import tkinter as tk
from tkinter import ttk, font as tkfont
except ImportError:
raise ImportError("tkinter 不可用,请确认 Python 安装时包含了 Tk 支持")
try:
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
HAS_MPL = True
except ImportError:
HAS_MPL = False
# 导入数据采集模块(与 main.py 同目录)
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from main import (
get_cpu_temperature, get_gpu_temperature,
get_cpu_info, get_gpu_info,
CPU_WARN_TEMP, CPU_CRIT_TEMP,
GPU_WARN_TEMP, GPU_CRIT_TEMP,
HAS_PYNVML, HAS_PSUTIL,
)
# ── 颜色主题 ─────────────────────────────────────────────────────────────────
COLORS = {
"bg": "#1e1e2e",
"panel": "#2a2a3e",
"border": "#44475a",
"text": "#cdd6f4",
"subtext":"#6c7086",
"green": "#a6e3a1",
"yellow": "#f9e2af",
"red": "#f38ba8",
"blue": "#89b4fa",
"mauve": "#cba6f7",
"cpu_line": "#89b4fa",
"gpu_line": "#f38ba8",
}
REFRESH_MS= 2000 # 界面刷新间隔(毫秒)
HISTORY_LEN = 60 # 曲线历史长度
# ════════════════════════════════════════════════════════════════════════════
# 数据模型
# ════════════════════════════════════════════════════════════════════════════
class HardwareData:
def __init__(self):
self.cpu_temp_history = deque( * HISTORY_LEN, maxlen=HISTORY_LEN)
self.gpu_temp_history = deque( * HISTORY_LEN, maxlen=HISTORY_LEN)
self.lock = threading.Lock()
# 最新一帧数据
self.cpu_temps = {}
self.gpu_temps = {}
self.cpu_info = {}
self.gpu_list = []
self.timestamp = ""
self.fetching = False
def fetch(self):
"""在后台线程里采集数据"""
self.fetching = True
try:
cpu_temps = get_cpu_temperature()
gpu_temps = get_gpu_temperature()
cpu_info= get_cpu_info()
gpu_list= get_gpu_info()
ts = datetime.now().strftime("%H:%M:%S")
# 取物理核心(过滤 Thread 和 Package/Tdie 聚合传感器)的最高温度作为曲线代表值
_PKG_KW = ("package", "tdie", "tctl", "average", "max", "distance")
phys_temps = {k: v for k, v in cpu_temps.items() if "Thread" not in k}
src = phys_temps if phys_temps else cpu_temps
core_vals = [v for k, v in src.items()
if not any(kw in k.lower() for kw in _PKG_KW)]
cpu_peak = max(core_vals) if core_vals else (max(src.values()) if src else None)
# 第一块 GPU 的温度
gpu_peak = None
if gpu_list:
i = gpu_list["index"]
name = gpu_list["name"]
gpu_peak = gpu_temps.get(f"GPU{i} {name}")
elif gpu_temps:
gpu_peak = list(gpu_temps.values())
with self.lock:
self.cpu_temps = cpu_temps
self.gpu_temps = gpu_temps
self.cpu_info= cpu_info
self.gpu_list= gpu_list
self.timestamp = ts
self.cpu_temp_history.append(cpu_peak)
self.gpu_temp_history.append(gpu_peak)
except Exception as e:
print(f"[数据采集异常] {e}")
finally:
self.fetching = False
# ════════════════════════════════════════════════════════════════════════════
# 主窗口
# ════════════════════════════════════════════════════════════════════════════
class MonitorApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("CPU / GPU 硬件监控")
self.configure(bg=COLORS["bg"])
self.resizable(True, True)
self.geometry("900x640")
self.data = HardwareData()
self._build_ui()
self._schedule_refresh()
# ── UI 构建 ────────────────────────────────────────────────────────────
def _build_ui(self):
# 顶部标题栏
title_frame = tk.Frame(self, bg=COLORS["bg"])
title_frame.pack(fill="x", padx=16, pady=(12, 4))
tk.Label(
title_frame, text="🖥硬件温度监控",
bg=COLORS["bg"], fg=COLORS["text"],
font=("Segoe UI", 16, "bold"),
).pack(side="left")
self.lbl_time = tk.Label(
title_frame, text="",
bg=COLORS["bg"], fg=COLORS["subtext"],
font=("Segoe UI", 10),
)
self.lbl_time.pack(side="right")
# 主体两列布局
body = tk.Frame(self, bg=COLORS["bg"])
body.pack(fill="both", expand=True, padx=16, pady=4)
left= tk.Frame(body, bg=COLORS["bg"])
right = tk.Frame(body, bg=COLORS["bg"])
left.pack(side="left", fill="both", expand=True, padx=(0, 8))
right.pack(side="right", fill="both", expand=True)
# 左列:CPU 面板 + GPU 面板
self._cpu_panel = self._make_panel(left, "📊CPU", expand=True)
self._gpu_panel = self._make_panel(left, "🎮GPU", expand=True)
# 右列:温度曲线图
if HAS_MPL:
chart_frame = self._make_panel(right, "📈温度趋势(近 60 次)", expand=True)
self._build_chart(chart_frame)
else:
warn = self._make_panel(right, "⚠提示", expand=True)
tk.Label(
warn, text="安装 matplotlib 以显示温度趋势图\npip install matplotlib",
bg=COLORS["panel"], fg=COLORS["yellow"],
font=("Segoe UI", 10), justify="left",
).pack(padx=12, pady=12, anchor="w")
# 底部状态栏
bar = tk.Frame(self, bg=COLORS["border"], height=1)
bar.pack(fill="x", padx=16)
self.lbl_status = tk.Label(
self, text="正在加载…",
bg=COLORS["bg"], fg=COLORS["subtext"],
font=("Segoe UI", 9),
)
self.lbl_status.pack(anchor="w", padx=16, pady=(2, 8))
def _make_panel(self, parent, title: str, expand: bool = False) -> tk.Frame:
"""创建带标题的卡片面板,返回内容区 Frame"""
outer = tk.Frame(parent, bg=COLORS["border"], bd=0)
outer.pack(fill="both", expand=expand, pady=4)
inner = tk.Frame(outer, bg=COLORS["panel"], bd=0)
inner.pack(fill="both", expand=True, padx=1, pady=1)
tk.Label(
inner, text=title,
bg=COLORS["panel"], fg=COLORS["blue"],
font=("Segoe UI", 11, "bold"),
).pack(anchor="w", padx=12, pady=(10, 4))
sep = tk.Frame(inner, bg=COLORS["border"], height=1)
sep.pack(fill="x", padx=12)
content = tk.Frame(inner, bg=COLORS["panel"])
content.pack(fill="both", expand=True, padx=12, pady=(6, 10))
return content
def _build_chart(self, parent: tk.Frame):
"""在 parent 内嵌入 matplotlib 折线图"""
fig = Figure(figsize=(4.5, 4), dpi=90, facecolor=COLORS["bg"])
self._ax = fig.add_subplot(111)
self._ax.set_facecolor(COLORS["panel"])
fig.tight_layout(pad=1.5)
self._canvas = FigureCanvasTkAgg(fig, master=parent)
self._canvas.get_tk_widget().pack(fill="both", expand=True)
self._fig = fig
self._line_cpu, = self._ax.plot(
[], [], color=COLORS["cpu_line"], linewidth=1.8, label="CPU"
)
self._line_gpu, = self._ax.plot(
[], [], color=COLORS["gpu_line"], linewidth=1.8, label="GPU"
)
self._ax.axhline(CPU_WARN_TEMP, color=COLORS["yellow"], linewidth=0.8,
linestyle="--", alpha=0.6, label=f"警告 {CPU_WARN_TEMP}°C")
self._ax.axhline(CPU_CRIT_TEMP, color=COLORS["red"], linewidth=0.8,
linestyle="--", alpha=0.6, label=f"危险 {CPU_CRIT_TEMP}°C")
self._ax.set_ylabel("温度 (°C)", color=COLORS["text"], fontsize=9)
self._ax.set_xlabel("历史记录", color=COLORS["text"], fontsize=9)
self._ax.tick_params(colors=COLORS["subtext"], labelsize=8)
for spine in self._ax.spines.values():
spine.set_edgecolor(COLORS["border"])
self._ax.legend(
facecolor=COLORS["panel"], edgecolor=COLORS["border"],
labelcolor=COLORS["text"], fontsize=8
)
self._ax.set_ylim(0, 110)
self._ax.yaxis.label.set_color(COLORS["text"])
self._ax.xaxis.label.set_color(COLORS["text"])
# ── 数据刷新 ──────────────────────────────────────────────────────────
def _schedule_refresh(self):
"""开始后台采集 + 定时 UI 刷新"""
self._do_refresh()
def _do_refresh(self):
"""启动后台采集,采集完毕后更新 UI"""
def worker():
self.data.fetch()
self.after(0, self._update_ui)
t = threading.Thread(target=worker, daemon=True)
t.start()
def _update_ui(self):
"""用最新数据更新所有 UI 控件"""
with self.data.lock:
cpu_temps = dict(self.data.cpu_temps)
gpu_temps = dict(self.data.gpu_temps)
cpu_info= dict(self.data.cpu_info)
gpu_list= list(self.data.gpu_list)
ts = self.data.timestamp
cpu_hist= list(self.data.cpu_temp_history)
gpu_hist= list(self.data.gpu_temp_history)
self.lbl_time.config(text=ts)
self._refresh_cpu_panel(cpu_temps, cpu_info)
self._refresh_gpu_panel(gpu_temps, gpu_list)
if HAS_MPL:
self._refresh_chart(cpu_hist, gpu_hist)
self.lbl_status.config(text=f"上次更新:{ts}|刷新间隔:{REFRESH_MS // 1000}s")
# 安排下次刷新
self.after(REFRESH_MS, self._do_refresh)
def _refresh_cpu_panel(self, cpu_temps: dict, cpu_info: dict):
"""清空并重绘 CPU 面板"""
for w in self._cpu_panel.winfo_children():
w.destroy()
def row(label, value, val_color=COLORS["text"]):
f = tk.Frame(self._cpu_panel, bg=COLORS["panel"])
f.pack(fill="x", pady=1)
tk.Label(f, text=label, bg=COLORS["panel"], fg=COLORS["subtext"],
font=("Segoe UI", 9), width=22, anchor="w").pack(side="left")
tk.Label(f, text=value, bg=COLORS["panel"], fg=val_color,
font=("Segoe UI", 9, "bold"), anchor="w").pack(side="left")
if cpu_info:
physical = cpu_info.get("cpu_count_physical", "?")
logical= cpu_info.get("cpu_count_logical", "?")
row("核心数", f"{physical} 物理 / {logical} 逻辑")
if "freq_current" in cpu_info:
row("当前频率", f"{cpu_info['freq_current']:.0f} MHz")
usage = cpu_info.get("usage_total")
if usage is not None:
color = _usage_color(usage)
row("CPU 使用率", f"{usage:.1f}%", color)
# 各核使用率——按物理核聚合(超线程取两逻辑核中的较大值)
per_core= cpu_info.get("usage_per_core", [])
phys_cnt= cpu_info.get("cpu_count_physical") or 0
logi_cnt= cpu_info.get("cpu_count_logical")or len(per_core)
if per_core:
if phys_cnt and logi_cnt == phys_cnt * 2:
phys_usage = [
max(per_core, per_core)
for i in range(phys_cnt)
]
else:
phys_usage = per_core
cols = 3
for start in range(0, len(phys_usage), cols):
chunk = phys_usage
txt = "".join(
f"核{start + j + 1}: {v:>5.1f}%" for j, v in enumerate(chunk)
)
tk.Label(
self._cpu_panel, text=txt,
bg=COLORS["panel"], fg=COLORS["subtext"],
font=("Consolas", 8),
).pack(anchor="w")
if "mem_used_gb" in cpu_info:
mem_color = _usage_color(cpu_info["mem_percent"])
row("内存使用",
f"{cpu_info['mem_used_gb']:.1f} / {cpu_info['mem_total_gb']:.1f} GB"
f"({cpu_info['mem_percent']:.1f}%)", mem_color)
# 温度——最高核心温度 + Package 温度
if cpu_temps:
tk.Frame(self._cpu_panel, bg=COLORS["border"], height=1).pack(fill="x", pady=4)
phys = {k: v for k, v in cpu_temps.items() if "Thread" not in k}
src= phys if phys else cpu_temps
# 最高核心温度(排除 Package/Tdie/Tctl 聚合传感器)
_PKG_KW = ("package", "tdie", "tctl", "average", "max", "distance")
core_vals = [v for k, v in src.items()
if not any(kw in k.lower() for kw in _PKG_KW)]
peak = max(core_vals) if core_vals else max(src.values())
row("温度(最高核心)", f"{peak:.1f} °C",
_temp_color(peak, CPU_WARN_TEMP, CPU_CRIT_TEMP))
# Package 温度
for k, v in src.items():
if any(kw in k.lower() for kw in ("package", "tdie", "tctl")):
row("温度(Package)", f"{v:.1f} °C",
_temp_color(v, CPU_WARN_TEMP, CPU_CRIT_TEMP))
break
else:
tk.Label(
self._cpu_panel,
text="温度不可用(需 LibreHardwareMonitor)",
bg=COLORS["panel"], fg=COLORS["yellow"],
font=("Segoe UI", 9),
).pack(anchor="w", pady=4)
def _refresh_gpu_panel(self, gpu_temps: dict, gpu_list: list):
"""清空并重绘 GPU 面板"""
for w in self._gpu_panel.winfo_children():
w.destroy()
def row(label, value, val_color=COLORS["text"]):
f = tk.Frame(self._gpu_panel, bg=COLORS["panel"])
f.pack(fill="x", pady=1)
tk.Label(f, text=label, bg=COLORS["panel"], fg=COLORS["subtext"],
font=("Segoe UI", 9), width=22, anchor="w").pack(side="left")
tk.Label(f, text=value, bg=COLORS["panel"], fg=val_color,
font=("Segoe UI", 9, "bold"), anchor="w").pack(side="left")
if not gpu_list and not gpu_temps:
tk.Label(
self._gpu_panel,
text="未检测到 GPU(NVIDIA 需安装 nvidia-ml-py3)",
bg=COLORS["panel"], fg=COLORS["yellow"],
font=("Segoe UI", 9),
).pack(anchor="w", pady=4)
return
for gpu in gpu_list:
i = gpu["index"]
name = gpu["name"]
tk.Label(
self._gpu_panel, text=f"[{i}] {name}",
bg=COLORS["panel"], fg=COLORS["mauve"],
font=("Segoe UI", 9, "bold"),
).pack(anchor="w", pady=(4, 1))
temp_key = f"GPU{i} {name}"
temp = gpu_temps.get(temp_key)
if temp is not None:
color = _temp_color(temp, GPU_WARN_TEMP, GPU_CRIT_TEMP)
row("温度", f"{temp:.1f} °C", color)
if "usage_gpu" in gpu:
row("GPU 使用率", f"{gpu['usage_gpu']:.1f}%",
_usage_color(gpu["usage_gpu"]))
if "mem_used_gb" in gpu:
row("显存使用",
f"{gpu['mem_used_gb']:.1f} / {gpu['mem_total_gb']:.1f} GB"
f"({gpu['mem_percent']:.1f}%)",
_usage_color(gpu["mem_percent"]))
if "power_w" in gpu:
row("功耗", f"{gpu['power_w']:.1f} W / {gpu.get('power_limit_w', '?'):.1f} W")
if "fan_percent" in gpu:
row("风扇转速", f"{gpu['fan_percent']}%")
if "clock_gpu_mhz" in gpu:
row("核心频率", f"{gpu['clock_gpu_mhz']} MHz")
# 其他 GPU(WMI)
known = {f"GPU{g['index']} {g['name']}" for g in gpu_list}
for name, temp in gpu_temps.items():
if name not in known:
color = _temp_color(temp, GPU_WARN_TEMP, GPU_CRIT_TEMP)
row(f"{_shorten(name, 22)}", f"{temp:.1f} °C", color)
def _refresh_chart(self, cpu_hist: list, gpu_hist: list):
"""更新 matplotlib 折线图"""
xs = list(range(HISTORY_LEN))
cpu_ys =
gpu_ys =
self._line_cpu.set_data(xs, cpu_ys)
self._line_gpu.set_data(xs, gpu_ys)
all_vals = # filter nan
if all_vals:
y_min = max(0, min(all_vals) - 10)
y_max = min(120, max(all_vals) + 15)
self._ax.set_ylim(y_min, y_max)
self._ax.set_xlim(0, HISTORY_LEN - 1)
self._canvas.draw_idle()
# ════════════════════════════════════════════════════════════════════════════
# 辅助函数
# ════════════════════════════════════════════════════════════════════════════
def _temp_color(temp: float, warn: float, crit: float) -> str:
if temp >= crit:
return COLORS["red"]
elif temp >= warn:
return COLORS["yellow"]
return COLORS["green"]
def _usage_color(pct: float) -> str:
if pct >= 90:
return COLORS["red"]
elif pct >= 70:
return COLORS["yellow"]
return COLORS["green"]
def _shorten(text: str, max_len: int) -> str:
return text if len(text) <= max_len else text[:max_len - 1] + "…"
# ════════════════════════════════════════════════════════════════════════════
# 入口
# ════════════════════════════════════════════════════════════════════════════
def launch_gui():
app = MonitorApp()
app.mainloop()
if __name__ == "__main__":
launch_gui()
鱼油,您好!很高兴得知您测试的硬件监控软件达到预期要求。硬件监控软件通常用于实时监测计算机硬件的状态,如CPU、内存、磁盘和温度等,这对于系统维护和性能优化非常有用。
如果您想了解如何编写这样的软件,这里是一个简单的Python示例,使用psutil库来监控CPU和内存使用率:
import psutil
# 获取CPU使用率
cpu_usage = psutil.cpu_percent(interval=1)
print(f"当前CPU使用率: {cpu_usage}%")
# 获取内存信息
memory = psutil.virtual_memory()
print(f"内存总量: {memory.total / (1024**3):.2f} GB")
print(f"内存使用率: {memory.percent}%")
要运行此代码,您需要先安装psutil库,可以使用pip install psutil命令。
继续加油,如果您在开发或测试中遇到具体问题,欢迎随时提问交流!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 看不懂但大为震撼{:10_257:} 项目超级厉害!
页:
[1]