Skip to content

Instantly share code, notes, and snippets.

@alex0x08
Created March 25, 2026 11:54
Show Gist options
  • Select an option

  • Save alex0x08/8f743382cc1563c3140ac86cb9e36ed0 to your computer and use it in GitHub Desktop.

Select an option

Save alex0x08/8f743382cc1563c3140ac86cb9e36ed0 to your computer and use it in GitHub Desktop.
Simple temperature widget, Python + Xlib
#!/usr/bin/env python3
import Xlib
from Xlib import display, X # display и X - не импортируются автоматически
import subprocess,time,logging
#настройки логирования
#logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.DEBUG)
# координаты на экране для отображения
POS_X = 1150
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 = 5
last_dim = [0,0]
# определение рабочего стола
# d - display (экран)s
def get_root_window(d):
screen = d.screen()
# root window - рабочий стол по-умолчанию
root = screen.root
# Получаем ID всех окон верхнего уровня
windowIDs = root.get_full_property(d.intern_atom('_NET_CLIENT_LIST'),
X.AnyPropertyType).value
logging.debug('Found %d windows.',len(windowIDs))
for windowID in windowIDs:
# Create a window object from the ID to access its properties
window = d.create_resource_object('window', windowID)
try:
# Get the window title (WM_NAME or _NET_WM_NAME)
# Use get_wm_name() for simplicity,
# or look up EWMH properties for better compatibility
window_name = window.get_wm_name()
if window_name:
# в продвинутых DE вроде KDE/Xfce за рабочий стол отвечает
# отдельное окно и рисовать придется в нем
if 'Desktop' in window_name:
logging.debug("Found desktop ID: %d - Name: %s",
windowID,window_name)
return window
else:
# Бывает, что у окна нет заголовка
logging.debug("ID: %d - Name: None (no WM_NAME property)",
windowID)
except X.BadWindow:
# Обработка ситуации, когда считываемое окно больше не существует
logging.debug("ID: %d - Window no longer exists",windowID)
# если отдельного окна с именем Desktop не обнаружено - рисуем
# прямо на root window
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
# очистка происходит через отрисовку черного прямоугольника
# clear_area плохо работает с KDE
root.fill_rectangle(gc2, POS_X,POS_Y-th, tw-20, th+5)
# запоминаем размеры надписи для следующей очистки
last_dim = [tw,th]
# отрисовка сообщения на экране
# msg - текст сообщения
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)
# Access the window ID (an integer)
root_id = root.id
logging.debug("Root window ID: %d",root_id)
# для реакции на события
root.change_attributes(event_mask=X.ExposureMask) # "adds" this event mask
# создание графического контекста, белый текст на черном фоне
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:
# при нажатии Ctrl-C делаем очистку области экрана, где
# происходила отрисовка виджета
x = POS_X // 2
y = POS_Y // 2
root.clear_area(x,y,last_dim[0]+x,last_dim[1]+y,True)
display.flush()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment