pythonpython-3.xxlib

How would I render an image using python-xlib?


I am trying to make a window manager in python 3 and I need to find out how to render 16x16 window icons.

I have used Pillow and Xlib to render a dummy image, and I was expecting that it would work, but nope! It didn't.

The test program is mostly just initializing the window and generating the image, the faulty part is (at least one problem, there probably are more): screen.default_visual. I can't find any documentation on ANY of this, any help would be appreciated.

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)

And the code gives me this error:

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'

UPDATE: I changed default_visual to root_visual.


Solution

  • I couldn't find default_visual but there is root_visual

    I couldn't find X.Image but there is put_pil_image() which can put directly PIL.Image without converting to bytes and without X.Image

    from Xlib import display, X  #, Xutil
    from PIL import Image, ImageDraw
    import os
    
    def render_png(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  # no exists `default_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
        )
    
        while True:
            event = disp.next_event()
            if event.type == X.Expose:
                if event.count == 0:
                    win.put_pil_image(gc, 0, 0, img)
                    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)
    
        render_png(dummy, 200, 200, 100, 100)
    

    enter image description here