Nov 4, 2026

qtile desktop: a tiling window manager in Python

A modular qtile configuration in Python with a live hub menu, picture-in-picture, wallpaper-driven theming across apps and safeguards that keep the desktop usable when a reload fails.

  • Python
  • Linux
  • X11
  • qtile
  • Desktop

Overview

This is my daily desktop: a tiling window manager configured entirely in Python on top of qtile. It runs across two monitors, falls back cleanly to one, and themes itself and the rest of the desktop from the current wallpaper. The configuration is split into small modules (keys, groups, layouts, screens, hooks, theme, hub, picture-in-picture) and kept in git.

Feature summary

Feature Implementation Purpose
Hub menu A rofi menu built from qtile’s live state One key to find any window, app, group or action
Key binding viewer Generated from the loaded bindings and their descriptions Always matches the config; picking an entry runs it
Picture-in-picture A pinned window that follows you across groups, floating or tiled Keep a video or call visible while working
Tiling keys Vim-style focus, move and resize for the master/stack layouts Arrange windows without the mouse
Scratchpads Drop-down terminal, file manager and notes Instant access without leaving the current workspace
Theming pipeline pywal palette fed to the bar, borders and per-app build scripts One consistent color scheme from the wallpaper
Single-screen fallback Monitor count read at load; screens and group routing adapt Works on one monitor or two without edits

Technical deep dive

The hub. Super+Space opens a rofi menu that qtile assembles from live state: open windows with their group, pinned apps (shown as “Launch” only if they are not already open), scratchpads, groups with window counts, and actions such as switching the picture-in-picture mode, changing the layout, locking the screen or reloading the config. Because the menu runs inside qtile as an asyncio subprocess, picking an entry calls the window or group object directly instead of going through IPC. The key binding viewer (Super+/) uses the same path: it lists every binding with its description and replays the chosen one the way a real key press would.

Picture-in-picture. One window can be pinned as PiP; browser PiP windows are pinned automatically by title. In float mode it sits above everything in a chosen corner, sized to 16:9. In tile mode it docks in a strip reserved on the right edge and the tiled windows shrink to make room. The PiP state lives on the qtile object and the reserved space is tracked per screen, so a config reload neither loses the pinned window nor reserves the strip twice.

Groups and screens. Nine groups are split between two monitors with screen affinity, and dedicated keys switch to a group or move the focused window there and follow it. Apps are routed to groups by window class, and the gaming group uses a borderless full-screen layout. When only one monitor is connected, the bar shows every group, and group switching falls back to the current screen instead of a monitor that isn’t there.

Theming. pywal generates a 16-color palette from the wallpaper. The bar, borders and widgets read it at load time, and small build scripts push the same palette into GTK 3/4 apps, the notification daemon, the clipboard manager, the lock screen, folder icons, the code editor and other apps. Scratchpads for single-instance apps match on window class rather than process ID, since a second launch hands off to the running process.

Key challenge: making reloads safe. A Python config that throws on import leaves the session with no key bindings, and qtile’s own reload ungrabs every key before rebuilding. I made each failure mode recoverable: the theme falls back to a built-in palette when the pywal cache is missing or half-written, a recovery key bound outside qtile tries a graceful reload and then restarts the service, and a hook removes orphaned bar windows left behind when a screen setup fails partway, which otherwise cover the live bar as a blank strip.

← All projects