211208-1509 qtile WM first impressions
Python tiling window manager, playing with it for a couple of days now.
It’s everything I expected from a tiling WM, except it’s completely configurable with Python, so basically unlimited options to do anything. Compared to my usual i3: speed is the same, documentation is a bit worse, but configuration is much more intuitive.
And it has a lot of stuff, I never heard of it but was surprised to learn it has a lot of widgets / layouts / etc., and it has even a CLI-like shell qtile shell where you can use the standard bash commands to do stuff to anything (cd/ls/etc to layouts/groups/windows, run things like cd groups/F1/windows/213; down_opacity()
).
Everything I customized in i3 via hacks can be done natively nicely and in python and I love it.
Important links
- Lazy objects — Qtile 0.1.dev50+g9c583ed.d20211208 documentation
- Built-in Layouts — Qtile 0.1.dev50+g9c583ed.d20211208 documentation
- Keys — Qtile 0.1.dev50+g9c583ed.d20211208 documentation
Notes
Checking configuration for errors before restarting
No easy way to check config for correctness I’ve found, but python3 config.py
finds most errors.
Docu suggests python3 -m py_compile config.py
but it returns no output regardless of errors. qtile shell
’s test config also is quiet.
Layouts
A lot of them. Tried all. Favourites so far. Listed here: Built-in Layouts — Qtile 0.1.dev50+g9c583ed.d20211208 documentation
Main realization so far is that I’ve been using tiling WMs wrong, in i3 I kept manually splitting the window when I needed to have it split into smaller ones. Except that this should happen automatically, because I never want three windows side-by-side at the same time.
MonadTall / MonadWide
Probably my favourite one. Splits stuff nicely in one big and multiple smaller ones in a separate columns.
Added these bits to config:
Key([modkey], "i", lazy.layout.grow()),
Key([modkey], "m", lazy.layout.shrink()),
Key([modkey], "n", lazy.layout.normalize()),
Key([modkey], "o", lazy.layout.maximize()),
<mod+o>
toggles between how big/main is the highlighted window. If it’s the big window, it gets narrower or wider, if it’s one of the smaller ones in a column, each becomes the biggest/smallest in that column.<mod+i>
/<mod+m>
grows/shrinks the current window.<mod+n>
’normalizes’ everything by resetting the layout.
Column
Nice intuitive etc, has N columns, moving windows to left-right does what I expect, including creating newer columns, or splitting existing ones as the window “travels” through it.
Bsp
The tree-thingy that splits each thing into two, ad infinitum.
These bindings use mod3
which is the physical ctrl key, that move the splits with all windows inside them (not individual windows). They seem to be used only for that layout.
Key([mod3], "j", lazy.layout.flip_down()),
Key([mod3], "k", lazy.layout.flip_up()),
Key([mod3], "h", lazy.layout.flip_left()),
Key([mod3], "l", lazy.layout.flip_right()),
Other
Tile
Two stacks, one with N “main” windows (1, but configurable), and a second stack for all the other ones. See no added value compared to the Monad ones. But add_after_last=True
makes the behaviour more intuitive to me.
Max
One single window, the rest are hidden behind it (as a stack), no configs, no way to signal if it’s the only window or there are more behind it.
TreeTab
Only layout that I can get to show the titles of the windows inside the stack. You get one stack and window titles on the right.
Meant for browsers like uzbl, and it emulates almost exactly the setup I have for qutebrowser.
Random
- From this1 sample command:
- Doing stuff based on different layout:
layout = qtile.current_layout group = qtile.current_group if layout.name == 'monadtall': layout.cmd_maximize() if len(group.windows) != 2: return
- Using python and sound software engineering like creating a class to keep constants for commands
- Doing stuff based on different layout:
Config bits / settings
Getting Obsidian to run in a Dropdown/Scratchpad
One of those two worked:
- calling Obsidian directly as binary (instead of my runner shell script)
- Using config.Match()
to identify it .
TODO
- Multiple screens/monitors
- This shows how to detect number of screens and place groups in them: qtile-examples/groups.py at master · qtile/qtile-examples
from libqtile.config import Screen from platforms import num_screens, hostname if num_screens[hostname] == 4: from bars import chat_bar, main_bar, media_bar, code_bar # ... chat_screen = Screen(top=chat_bar) # ... screens = [main_screen, media_screen, code_screen, chat_screen]
- This shows how to detect number of screens and place groups in them: qtile-examples/groups.py at master · qtile/qtile-examples
- All my usual shortcuts (volume, screenshots, etc. etc.)
- I like the idea of splitting the configs in separate python files2, especially for constants1
What’s missing
- How to have a sticky floating window? 3