Monitorowanie temperatury CPU przez Xlib w Pythonie: krosplatformowy overlay
Skrypt w Pythonie o objętości ok. 120 linii umożliwia monitorowanie temperatury procesora z wyświetlaniem tekstu bezpośrednio na pulpicie X11. Wykorzystuje bibliotekę python-xlib do rysowania w oknie root lub na pulpicie bez dodatkowych uprawnień. Obsługuje FreeBSD (sysctl z modułami coretemp/amdtemp) oraz Linux (/sys/class/thermal). Aktualizacja co 15 sekund, czyszczenie obszaru przy Ctrl-C.
Pobieranie danych z czujników
Na FreeBSD temperatura dostępna jest poprzez sysctl po załadowaniu modułów jądra:
- Intel:
kldload coretemp, odczytsysctl dev.cpu |grep temperature - AMD:
kldload amdtemp, odczytsysctl dev.amdtemp.0 |grep core
W systemie Linux używany jest sysfs:
cat /sys/class/thermal/thermal_zone*/temp
Wartości podawane są w mili stopniach Celsjusza (dzielić przez 1000). Skrypt parsuje dane z subprocess.Popen, tworzy ciąg temperatur oddzielonych przecinkami.
Praca z serwerem X
Kluczowy trik – rysowanie w innych oknach bez powiadomienia użytkownika. Xlib pozwala na:
- Pobranie okna root lub pulpitu poprzez _NET_CLIENT_LIST.
- Stworzenie GC (kontekstu graficznego) z białym tekstem na czarnym tle.
- Wyczyszczenie obszaru za pomocą fill_rectangle przed rysowaniem.
- Wyświetlenie tekstu draw_text z ustalonym fontem.
Funkcja get_root_window przeszukuje okna najwyższego poziomu, szukając nazwy zawierającej 'Desktop' w WM_NAME (KDE/Xfce). Domyślna opcja to okno root.
Pełny kod źródłowy
Samodzielny skrypt (.sh z shebangiem). Jedyna zależność to python-xlib.
#!/usr/bin/env python3
import Xlib
from Xlib import display, X # display i X - nie są importowane automatycznie
import subprocess,time,logging
# ustawienia logowania
logging.basicConfig(level=logging.INFO)
#logging.basicConfig(level=logging.DEBUG)
# współrzędne na ekranie do wyświetlenia
POS_X = 150
POS_Y = 50
# dla FreeBSD i maszyn AMD
PATTERN = 'sysctl dev.amdtemp.0 |grep core'
# dla FreeBSD z modułem coretemp
#PATTERN = 'sysctl dev.cpu |grep temperature'
# dla Linuxa
#PATTERN = "cat /sys/class/thermal/thermal_zone*/temp | awk '{ print "temp: " ($1 / 1000) "C" }'"
# czcionka
FONT = '-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1'
# okres aktualizacji
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('Znaleziono %d okien.',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("Znaleziono ID pulpitu: %d - Nazwa: %s",
windowID,window_name)
return window
else:
logging.debug("ID: %d - Nazwa: None (brak właściwości WM_NAME)",
windowID)
except X.BadWindow:
logging.debug("ID: %d - Okno już nie istnieje",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 okna root: %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()
Konfiguracja parametrów
Wszystkie stałe znajdują się na początku skryptu:
POS_X,POS_Y: współrzędne overlay.PATTERN: polecenie do czujników (dostosować do platformy).FONT: czcionka X11 w formacie XLFD.REFRESH_SECS: interwał aktualizacji.- Logowanie: INFO/DEBUG.
Instalacja python-xlib:
- FreeBSD:
pkg install py311-python-xlib - Linux: pakiet apt/yum python3-xlib
Co jest ważne
- Krosplatformowość: FreeBSD/Linux, Intel/AMD bez Wayland.
- Minimalizm: 120 linii, bez OOP/threads, jedna zależność.
- Hack X11: rysowanie w root/Desktop bez uprawnień i sygnalizacji.
- Czyszczenie: fill_rectangle + clear_area przy wyjściu.
- Parse: subprocess + split dla sysctl/sysfs.
— Editorial Team
Brak komentarzy.