A Python wrapper for MiniLibX — the simple X11 graphics library used at 42 school.
Lets you open windows, draw shapes, and handle keyboard/mouse events in pure Python.
from pymlx import Mlx, Color
with Mlx() as mlx:
win = mlx.new_window(800, 600, "Hello pymlx")
img = mlx.new_image(800, 600)
img.draw_circle(400, 300, 100, Color.RED)
img.draw_rect(50, 50, 200, 100, Color.BLUE)
img.draw_line(0, 0, 800, 600, Color.WHITE)
win.put_image(img)
win.on_close(mlx.loop_end)
win.on_key(lambda key: mlx.loop_end() if key == 65307 else None)
mlx.loop()- Linux with X11
- Python 3.10+
gcc,makelibX11-dev,libXext-dev
sudo apt install gcc make libx11-dev libxext-devgit clone https://github.com/yourname/pymlx.git
cd pymlx
# Build the C wrapper
make -C src/pymlx MLX_DIR=src/pymlx/lib/minilibx-linux
# Install the package
pip install -e .Main context. Use as a context manager.
mlx = Mlx()
mlx.new_window(width, height, title) -> Window
mlx.new_image(width, height) -> Image
mlx.destroy_image(image)
mlx.on_loop(callback) # callback() called every frame
mlx.loop() # start event loop (blocking)
mlx.loop_end() # stop event loop
mlx.destroy()win.put_image(image, x=0, y=0) # blit image onto window
win.pixel_put(x, y, color) # direct pixel (slow)
win.string_put(x, y, color, text) # draw text
win.on_key(callback) # callback(keycode: int)
win.on_mouse(callback) # callback(button, x, y)
win.on_close(callback) # called on window close
win.on_event(event, mask, callback) # raw X11 event
win.mouse_hide()
win.mouse_show()
win.mouse_pos() -> (x, y)
win.mouse_move(x, y)
win.destroy()All drawing methods write directly into the pixel buffer — fast enough for real-time rendering.
# Pixels
img.pixel_put(x, y, color)
img.pixel_get(x, y) -> int
img.clear(color)
# Rectangles
img.draw_rect(x, y, w, h, color)
img.draw_rect_outline(x, y, w, h, color, thickness=1)
# Lines
img.draw_line(x0, y0, x1, y1, color)
img.draw_line_thick(x0, y0, x1, y1, color, thickness=2)
# Circles
img.draw_circle(cx, cy, radius, color)
img.draw_circle_outline(cx, cy, radius, color, thickness=1)
# Triangles
img.draw_triangle(x0, y0, x1, y1, x2, y2, color)
img.draw_triangle_outline(x0, y0, x1, y1, x2, y2, color)
# Gradients
img.draw_gradient_h(x, y, w, h, color_left, color_right)
img.draw_gradient_v(x, y, w, h, color_top, color_bottom)
# Polygon outline
img.draw_polygon([(x, y), ...], color)
# Flood fill
img.flood_fill(x, y, color)Color.BLACK, Color.WHITE, Color.RED, Color.GREEN
Color.BLUE, Color.YELLOW, Color.CYAN, Color.MAGENTA
Color.GRAY, Color.ORANGE, Color.PINK
Color.rgb(r, g, b) -> int # 0–255 each
Color.to_rgb(color) -> (r, g, b)
Color.lerp(c1, c2, t) -> int # t in [0.0, 1.0]X11Event.KEY_PRESS # 2
X11Event.KEY_RELEASE # 3
X11Event.BUTTON_PRESS # 4
X11Event.BUTTON_RELEASE # 5
X11Event.MOTION_NOTIFY # 6
X11Event.DESTROY_NOTIFY # 17from pymlx import Mlx, Color
W, H = 800, 600
frame = 0
with Mlx() as mlx:
win = mlx.new_window(W, H, "Gradient")
img = mlx.new_image(W, H)
def render():
global frame
t = (frame % 256) / 255
img.draw_gradient_h(0, 0, W, H,
Color.lerp(Color.BLUE, Color.RED, t),
Color.lerp(Color.RED, Color.BLUE, t))
win.put_image(img)
frame += 1
win.on_close(mlx.loop_end)
win.on_key(lambda k: mlx.loop_end() if k == 65307 else None)
mlx.on_loop(render)
mlx.loop()MIT