My Qtile Config (Mocha)
Table of Contents
1. Goals
1.1. We want the configuration to have efficient and ergonomic keybindings.
Commonly used keybindings should be two keys, less common ones should use more.
1.2. We want it to work with other programs that it references.
For example, the qutebrowser and qtile configurations should not cause conflict and they should have mutual consistency.
1.2.1. This will therefore make the configuration self sufficient.
1.3. General enough that it will work for most purposes.
Specialized programs or tools should be in a separate section which will make it easy to parse and remove.
2. Configuration
2.1. Imports and Definitions
All of this is also in the default configuration.
from libqtile import bar, layout, widget from libqtile.config import Click, Drag, Group, Key, Match, Screen from libqtile.lazy import lazy from libqtile.utils import guess_terminal from libqtile import hook from libqtile.backend.wayland import InputConfig import os import subprocess mod = "mod4" terminal = "kitty" wl_import_rules = None auto_minimize = True wmname = "LG3D"
2.2. Hex Colors
We then load the catppuccin colors for the bar and window borders.
def get_colors(theme): if theme == "city-lights": return [ # Normal colors '#45475a', '#f38ba8', '#a6e3a1', '#f9e2af', '#89b4fa', '#f5c2e7', '#94e2d5', '#bac2de', # Bright colors '#585b70', '#f38ba8', '#a6e3a1', '#f9e2af', '#89b4fa', '#f5c2e7', '#94e2d5', '#a6adc8', # background '#1e1e2e', # foreground '#cdd6f4', ] elif theme == "gruvbox": return [ # normal colors '#282828', '#cc241d', '#98971a', '#d79921', '#458588', '#b16286', '#689d6a', '#a89984', #bright colors '#928374', '#fb4934', '#b8bb26', '#fabd2f', '#83a598', '#d3869b', '#8ec07c', '#ebdbb2', # background '#282828', # foreground '#ebdbb2', ] colors = get_colors("gruvbox")
2.3. Keybindings
The keys variable is going to be our final list of keybindings. We start by initializing it wth our window manipulation bindings with vim keys:
2.3.1. Focus controls
Vi inspired keybindings to manipulate focus:
keys = [ Key([mod], "h", lazy.layout.left(), desc="Move focus to left"), Key([mod], "l", lazy.layout.right(), desc="Move focus to right"), Key([mod], "j", lazy.layout.down(), desc="Move focus down"), Key([mod], "k", lazy.layout.up(), desc="Move focus up"), Key([mod], "space", lazy.layout.next(), desc="Move window focus to other window"), Key([mod, "shift"], "h", lazy.layout.shuffle_left(), desc="Move window to the left"), Key([mod, "shift"], "l", lazy.layout.shuffle_right(), desc="Move window to the right"), Key([mod, "shift"], "j", lazy.layout.shuffle_down(), desc="Move window down"), Key([mod, "shift"], "k", lazy.layout.shuffle_up(), desc="Move window up"), Key([mod, "control"], "h", lazy.layout.grow_left(), desc="Grow window to the left"), Key([mod, "control"], "l", lazy.layout.grow_right(), desc="Grow window to the right"), Key([mod, "control"], "j", lazy.layout.grow_down(), desc="Grow window down"), Key([mod, "control"], "k", lazy.layout.grow_up(), desc="Grow window up"), Key([mod], "n", lazy.layout.normalize(), desc="Reset all window sizes"), Key( [mod, "shift"], "Return", lazy.layout.toggle_split(), desc="Toggle between split and unsplit sides of stack", ), Key([mod], "Tab", lazy.next_layout(), desc="Toggle between layouts"), ]
2.3.2. Quit/Restart
keys.extend([ Key([mod], "q", lazy.window.kill(), desc="Kill focused window"), Key([mod, "control"], "r", lazy.reload_config(), desc="Reload the config"), Key([mod, "control"], "q", lazy.shutdown(), desc="Shutdown Qtile"), ])
2.3.3. Programs
These are our keybindings for user programs.
keys.extend([ Key([mod], "r", lazy.spawncmd(), desc="Spawn a command using a prompt widget"), Key([mod], "Return", lazy.spawn(terminal), desc="Launch terminal"), Key([mod], "e", lazy.spawn("emacs"), desc="Run emacs"), Key([mod], "w", lazy.spawn("qutebrowser"), desc="Run Qutebrowser"), Key([mod], "f", lazy.spawn("firefox"), desc="Run Firefox"), Key([mod], "b", lazy.spawn("blender"), desc="Run Blender"), Key([mod], "p", lazy.spawn("krita"), desc="Run Krita"), Key([mod], "v", lazy.spawn("inkscape"), desc="Run Inkscape"), Key([mod], "g", lazy.spawn("gimp"), desc="Run GIMP"), Key([mod], "t", lazy.spawn("torbrowser-launcher"), desc="Run Tor Browser"), Key([mod], "i", lazy.spawn("emacsclient --eval \"(emacs-everywhere)\""), desc="Emacs Everywhere!"), Key([mod], "d", lazy.spawn("rofi -show run"), desc="rofi command launcher"), ])
2.3.4. XF86
Now we need keybindings for the function keys:
keys.extend([ Key([], 'XF86AudioLowerVolume', lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ -5%")), Key([], 'XF86AudioRaiseVolume', lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ +5%")), Key([], 'XF86AudioMute', lazy.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle")), Key([], 'XF86MonBrightnessUp', lazy.spawn("light -A 10")), Key([], 'XF86MonBrightnessDown', lazy.spawn("light -U 10")), Key([], 'XF86AudioNext', lazy.spawn("mpc next")), Key([], 'XF86AudioPrev', lazy.spawn("mpc prev")), Key([], "XF86AudioPlay", lazy.spawn("mpc toggle"), desc="Play/Pause player"), Key([], "Print", lazy.spawn("scrot '%Y-%m-%d-%s_screenshot_$wx$h.jpg' -e 'mv $f ~/img/scrot")), ])
2.4. Groups
Now we name our groups:
groups = [Group(i) for i in "123456789"] for i in groups: keys.extend( [ Key( [mod], i.name, lazy.group[i.name].toscreen(), desc="Switch to group {}".format(i.name), ), Key( [mod, "shift"], i.name, lazy.window.togroup(i.name, switch_group=True), desc="Switch to & move focused window to group {}".format(i.name), ), ] )
2.5. Layouts
This is our list of enabled layouts. You can enable more of them if you want.
layouts = [ layout.Columns(border_focus=colors[2], border_normal=colors[0], border_width=4, margin=7), layout.Max(), # Try more layouts by unleashing below layouts. # layout.Stack(num_stacks=2), # layout.Bsp(), # layout.Matrix(), layout.MonadTall(border_focus=colors[2], border_normal=colors[0], border_width=4, margin=7), # layout.MonadWide(), # layout.RatioTile(), # layout.Tile(), # layout.TreeTab(), # layout.VerticalTile(), # layout.Zoomy(), ]
2.6. Bar
Now we define our bar. I only have the need to see the time, current workspace, battery percentage, and MPD. Also, you may need to manually change your font size depending on your screen.
widget_defaults = dict( font="FiraCode Nerd Font", fontsize=16, padding=4, foreground=colors[17], background=colors[16], ) extension_defaults = widget_defaults.copy() # screens = [ # Screen( # top=bar.Bar( # [ # # widget.CurrentLayout(), # widget.GroupBox(active=colors[6], inactive=colors[15], this_current_screen_border=colors[4], highlight_colorsr=colors[3]), # widget.Prompt(), # widget.WindowName(), # widget.Chord( # chords_colors={ # "launch": ("#ff0000", "#ffffff"), # }, # name_transform=lambda name: name.upper(), # ), # # widget.StatusNotifier(), # widget.Systray(), # widget.Battery(charge_char="🔋", discharge_char="🔋", full_char="🔋", format="{char} {percent:2.0%}"), # # widget.TextBox("|", foreground=colors[1]), # widget.Sep(padding=16, size_percent=80, foreground=colors[1]), # widget.Clock(format="🕒 %a %I:%M %p"), # widget.Sep(padding=16, size_percent=80, foreground=colors[1]), # widget.Mpd2(), # widget.TextBox(" "), # ], # 24, # # border_width=[2, 0, 2, 0], # Draw top and bottom borders # # border_colorsr=["ff00ff", "000000", "ff00ff", "000000"] # Borders are magenta # ), # bottom=bar.Gap(4), # left=bar.Gap(3), # right=bar.Gap(3), # ), # ] def pline(rl, fg, bg): if rl == 0: uc = "" else: uc = "" return widget.TextBox(text = uc, padding = 0, fontsize = 22, foreground=fg, background=bg) screens = [ Screen( wallpaper="~/.config/qtile/wallpaper", wallpaper_mode="fill", top=bar.Bar( [ widget.CurrentLayoutIcon( scale=0.75, background=colors[3] ), pline(0, colors[3], colors[6]), widget.GroupBox( highlight_method="block", background=colors[6], this_current_screen_border="#7daea3" ), pline(0, colors[6], colors[7]), widget.TaskList( highlight_method="block", max_title_width=300, border="#d3869b", padding=2, background=colors[7] ), pline(0, colors[7], colors[0]), widget.Spacer(), pline(1, colors[2], colors[0]), widget.Net( # requires python-psutil interface="wlp0s20f3", format="📡 {total}", update_interval=30, background=colors[2] ), pline(1, colors[5], colors[2]), widget.Backlight( format="💡 {percent:2.0%}", backlight_name="intel_backlight", background=colors[5] ), pline(1, colors[3], colors[5]), widget.Volume( emoji=True, background=colors[3] ), widget.Volume( background=colors[3] ), pline(1, colors[4], colors[3]), widget.BatteryIcon( background=colors[4] ), widget.Battery( charge_char="now ", discharge_char="left", format="{percent:2.0%} {char}", background=colors[4] ), pline(1, colors[1], colors[4]), widget.Clock( format="%Y-%m-%d %a %I:%M %p", background=colors[1] ), ], 26, ), ), ]
2.7. Mouse
We configure the mouse to interact with floating windows.
mouse = [ Drag([mod], "Button1", lazy.window.set_position_floating(), start=lazy.window.get_position()), Drag([mod], "Button3", lazy.window.set_size_floating(), start=lazy.window.get_size()), Click([mod], "Button2", lazy.window.bring_to_front()), ]
Also, we need to toggle some options:
dgroups_app_rules = [] # type: list follow_mouse_focus = True bring_front_click = False cursor_warp = False
And then we add the applications that need to start in floating:
floating_layout = layout.Floating( float_rules=[ # Run the utility of `xprop` to see the wm class and name of an X client. *layout.Floating.default_float_rules, Match(wm_class="confirmreset"), # gitk Match(wm_class="makebranch"), # gitk Match(wm_class="maketag"), # gitk Match(wm_class="ssh-askpass"), # ssh-askpass Match(title="branchdialog"), # gitk Match(title="pinentry"), # GPG key password entry ] )
2.8. I have no idea what these are
but they work for some reason.
auto_fullscreen = True focus_on_window_activation = "smart" reconfigure_screens = True
2.9. Autostart
If we used wayland, then we must autostart here:
@hook.subscribe.startup_once def autostart(): home = os.path.expanduser("~") subprocess.call([home + '/.config/qtile/autostart.sh'])
2.10. Input Rules
in wayland, setxkbmap is not possible. Therefore:
wl_input_rules = { "1267:12377:ELAN1300:00 04F3:3059 Touchpad": InputConfig(left_handed=True), "*": InputConfig(left_handed=True, pointer_accel=True), "type:keyboard": InputConfig(kb_options="caps:swapescape,compose:ralt"), }