You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用python-xlib渲染16x16窗口图标?代码报错求助

问题

我正在用Python 3开发窗口管理器,需要实现16x16窗口图标的渲染功能。使用Pillow和python-xlib编写了测试程序,已将代码中的screen.default_visual修改为root_visual,但仍报错。

测试代码:

from Xlib import display, X, Xutil
from PIL import Image, ImageDraw
import os

def renderPng(image_path, width, height, x=50, y=50):
    img = Image.open(image_path)
    if img.mode != 'RGB':
        img = img.convert('RGB')
    image_data = img.tobytes("raw", "RGB")
    disp = display.Display()
    screen = disp.screen()
    root = screen.root
    visual = screen.root_visual
    depth = screen.root_depth
    win = root.create_window(
        x, y,
        width, height,
        1,
        depth,
        X.InputOutput,
        visual,
        background_pixel=screen.white_pixel,
        event_mask=X.ExposureMask | X.KeyPressMask | X.StructureNotifyMask
    )
    win.set_wm_name("PNG image")
    win.map()
    gc = win.create_gc(
        foreground=screen.black_pixel,
        background=screen.white_pixel
    )
    ximage = X.Image(
        data=image_data,
        width=16,
        height=16,
        depth=depth,
        bitmap_unit=8,
        bitmap_pad=32,
        byte_order=X.LSBFirst,
        bitmap_bit_order=X.LSBFirst,
        format=X.ZPixmap,
        bytes_per_line=16 * 3
    )
    while True:
        event = disp.next_event()
        if event.type == X.Expose:
            if event.count == 0:
                win.put_image(gc, ximage, 0, 0, 0, 0, 16, 16)
                disp.flush()
        elif event.type == X.KeyPress:
            break
        elif event.type == X.ConfigureNotify:
            pass
    win.destroy()
    disp.close()

if __name__ == '__main__':
    dummy = "dummy.png"
    if not os.path.exists(dummy):
        img = Image.new('RGB', (16, 16), color = 'red')
        d = ImageDraw.Draw(img)
        d.text((1,1), "abc", fill=(255,255,255))
        img.save(dummy)
    renderPng(dummy, 200, 200, 100, 100)

运行后报错:

Traceback (most recent call last):
  File "/home/spincube/x/test.py", line 63, in <module>
    renderPng(dummy, 200, 200, 100, 100)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/spincube/x/test.py", line 31, in renderPng
    ximage = X.Image(
             ^^^^^^^
AttributeError: module 'Xlib.X' has no attribute 'Image'
解决方案

错误原因

Xlib.X模块中没有Image类,创建XImage对象需要使用Xlib.XImage类,或者通过Display实例的create_image方法。此外,直接传入RGB原始数据可能和窗口的视觉格式不匹配,需要根据屏幕的depth转换像素格式。

修正后的代码

from Xlib import display, X, Xutil
from Xlib.XImage import XImage  # 导入正确的XImage类
from PIL import Image, ImageDraw
import os

def renderPng(image_path, width, height, x=50, y=50):
    img = Image.open(image_path)
    # 确保图像大小是16x16
    img = img.resize((16,16))
    disp = display.Display()
    screen = disp.screen()
    root = screen.root
    visual = screen.root_visual
    depth = screen.root_depth
    
    # 根据屏幕depth转换图像格式
    if depth == 24 or depth == 32:
        # 对于24/32位深度,转换为RGBX格式(Xlib需要的顺序)
        img = img.convert('RGBX')
        image_data = img.tobytes("raw", "RGBX")
        bytes_per_line = 16 * 4
    else:
        # 8位深度的情况(很少见,这里做兼容处理)
        img = img.convert('P', palette=Image.ADAPTIVE)
        image_data = img.tobytes()
        bytes_per_line = 16

    win = root.create_window(
        x, y,
        width, height,
        1,
        depth,
        X.InputOutput,
        visual,
        background_pixel=screen.white_pixel,
        event_mask=X.ExposureMask | X.KeyPressMask | X.StructureNotifyMask
    )
    win.set_wm_name("PNG image")
    win.map()
    gc = win.create_gc(
        foreground=screen.black_pixel,
        background=screen.white_pixel
    )
    
    # 使用正确的XImage类创建对象
    ximage = XImage(
        data=image_data,
        width=16,
        height=16,
        depth=depth,
        bitmap_unit=8,
        bitmap_pad=32,
        byte_order=disp.byte_order(),  # 使用Display的字节序,更可靠
        bitmap_bit_order=X.LSBFirst,
        format=X.ZPixmap,
        bytes_per_line=bytes_per_line
    )
    
    while True:
        event = disp.next_event()
        if event.type == X.Expose:
            if event.count == 0:
                win.put_image(gc, ximage, 0, 0, 0, 0, 16, 16)
                disp.flush()
        elif event.type == X.KeyPress:
            break
        elif event.type == X.ConfigureNotify:
            pass
    win.destroy()
    disp.close()

if __name__ == '__main__':
    dummy = "dummy.png"
    if not os.path.exists(dummy):
        img = Image.new('RGB', (16, 16), color = 'red')
        d = ImageDraw.Draw(img)
        d.text((1,1), "abc", fill=(255,255,255))
        img.save(dummy)
    renderPng(dummy, 200, 200, 100, 100)

关键修改点

  • 导入Xlib.XImage.XImage类,替换原错误的X.Image调用。
  • 根据屏幕的depth转换图像格式:24/32位深度使用RGBX格式(Xlib的ZPixmap需要4字节对齐),避免格式不兼容。
  • 使用disp.byte_order()获取Display的字节序,替代硬编码的X.LSBFirst,提升兼容性。
  • 强制将输入图像缩放到16x16,确保和渲染尺寸匹配。

内容的提问来源于stack exchange,提问作者SpinningCubes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:05:16