用Python在X11上实现CPU温度实时叠加显示
一个约120行的Python脚本可实时监测CPU温度,并将文本直接叠加在X11桌面之上。该脚本利用python-xlib库,在不需管理员权限的情况下,向根窗口或桌面窗口绘制内容。支持FreeBSD(通过sysctl配合coretemp/amdtemp模块)和Linux(/sys/class/thermal)。每15秒更新一次,按Ctrl-C可清除显示区域。
读取传感器数据
在FreeBSD上,加载内核模块后可通过sysctl获取温度信息:
- Intel处理器:
kldload coretemp,执行sysctl dev.cpu |grep temperature - AMD处理器:
kldload amdtemp,执行sysctl dev.amdtemp.0 |grep core
在Linux系统中,使用sysfs接口:
cat /sys/class/thermal/thermal_zone*/temp
返回值单位为毫摄氏度(除以1000即得实际温度)。脚本通过subprocess.Popen捕获输出,解析并格式化为逗号分隔的温度字符串。
与X服务器交互
核心技巧:在其他窗口中绘图而不触发通知。Xlib实现方式如下:
- 通过 _NET_CLIENT_LIST 获取根窗口或桌面窗口。
- 创建一个前景为白色、背景为黑色的图形上下文(GC)。
- 绘制前先用fill_rectangle清空目标区域。
- 使用等宽字体调用draw_text绘制文本。
get_root_window函数会扫描顶层窗口,查找WM_NAME中包含"Desktop"的窗口(适用于KDE/Xfce),若未找到则回退至根窗口。
完整源码
自执行脚本(带shebang的.sh文件)。唯一依赖项:python-xlib。
#!/usr/bin/env python3
import Xlib
from Xlib import display, X # display and X are not imported automatically
import subprocess,time,logging
# 日志配置
logging.basicConfig(level=logging.INFO)
#logging.basicConfig(level=logging.DEBUG)
# 叠加显示位置
POS_X = 150
POS_Y = 50
# FreeBSD及AMD系统适用
PATTERN = 'sysctl dev.amdtemp.0 |grep core'
# FreeBSD使用coretemp模块
#PATTERN = 'sysctl dev.cpu |grep temperature'
# Linux系统
#PATTERN = "cat /sys/class/thermal/thermal_zone*/temp | awk '{ print "temp: " ($1 / 1000) "C" }'"
# 字体设置
FONT = '-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1'
# 刷新间隔(秒)
REFRESH_SECS = 15
last_dim = [0,0]
def get_root_window(d):
screen = d.screen()
root = screen.root
windowIDs = root.get_full_property(d.intern_atom('_NET_CLIENT_LIST'),
X.AnyPropertyType).value
logging.debug('发现 %d 个窗口。',len(windowIDs))
for windowID in windowIDs:
window = d.create_resource_object('window', windowID)
try:
window_name = window.get_wm_name()
if window_name:
if 'Desktop' in window_name:
logging.debug("找到桌面窗口 ID: %d - 名称: %s",
windowID,window_name)
return window
else:
logging.debug("ID: %d - 名称: 无 (无WM_NAME属性)",
windowID)
except X.BadWindow:
logging.debug("ID: %d - 窗口已不存在",windowID)
return root
def clear_rect(msg):
global last_dim
if last_dim[0] > 0:
root.fill_rectangle(gc2, POS_X,POS_Y-last_dim[1],
last_dim[0]-20, last_dim[1]+5)
else:
text_extents = font.query_text_extents(msg)
tw = text_extents.overall_width
th = text_extents.font_ascent + text_extents.font_descent
root.fill_rectangle(gc2, POS_X, POS_Y-th, tw-20, th+5)
last_dim = [tw,th]
def draw_message(msg):
clear_rect(msg)
root.draw_text(gc, POS_X, POS_Y, msg)
display.flush()
display = Xlib.display.Display()
screen = display.screen()
root = get_root_window(display)
root_id = root.id
logging.debug("根窗口ID: %d",root_id)
root.change_attributes(event_mask=X.ExposureMask)
gc = root.create_gc(foreground = screen.white_pixel,
background = screen.black_pixel)
colormap = screen.default_colormap
color = colormap.alloc_named_color('black')
gc2 = root.create_gc(foreground=color.pixel)
try:
font = display.open_font(FONT)
gc.font = font.id
except Exception as e:
logging.exception(e)
exit(1)
try:
while 1:
process = subprocess.Popen(PATTERN,
shell=True, text=True,
stdout=subprocess.PIPE)
stdout_list = process.communicate()[0].split('\n')
out = ''
for s in stdout_list:
if ':' not in s: continue
kv = s.split(':')
if len(out) > 0: out+= ','
out+= kv[1]
logging.debug(out)
draw_message(out.encode())
time.sleep(REFRESH_SECS)
except KeyboardInterrupt:
x = POS_X // 2
y = POS_Y // 2
root.clear_area(x,y,last_dim[0]+x,last_dim[1]+y,True)
display.flush()
自定义设置
所有常量均位于脚本顶部:
POS_X,POS_Y:叠加显示在屏幕上的坐标位置。PATTERN:传感器命令(根据平台调整)。FONT:X11字体,采用XLFD格式。REFRESH_SECS:刷新频率(秒)。- 日志级别:设为INFO或DEBUG。
安装python-xlib:
- FreeBSD:
pkg install py311-python-xlib - Linux:通过apt/yum安装
python3-xlib
核心亮点
- 跨平台兼容:支持FreeBSD/Linux,Intel/AMD处理器,无需Wayland。
- 极简设计:仅120行代码,无面向对象结构或多线程,单一依赖。
- X11黑科技:无需权限即可在根窗口/桌面绘制,无视觉提示。
- 优雅清理:退出时使用fill_rectangle和clear_area清除残留。
- 高效解析:依赖subprocess + split处理sysctl/sysfs数据。
— Editorial Team
暂无评论。