Imports

from libqtile import bar, layout, widget, hook
from libqtile.config import Click, Drag, Group, Key, Match, Screen
from libqtile.lazy import lazy, LazyCommandInterface
from libqtile.utils import guess_terminal

import os
import subprocess

Variables & Constants

Variable Explanation
PRIMARY_COLOR Primary color used in QTile Panel.
MOD_KEYS List of potential modifier keys mapped to their names. Used by the Mod-Key widget
MOD_KEY_FILE The path to the file that houses the current modifier key
keys List of keybindings. Initialized empty and added to throughout the config
PRIMARY_COLOR = ['#323945', '#323945'] 

MOD_KEYS = {
    'mod1': 'Alt',
    'mod4': 'Super',
    'shift': 'Shift',
    'control': 'Control'
}

MOD_KEY_FILE = os.path.expanduser('~/.config/qtile/modkey')

keys = []

Modifier Key

This section loads the modifier key from the MOD_KEY_FILE.

See Mod Key Management for more information.

def get_mod():
    with open(MOD_KEY_FILE, 'r') as f:
        return f.read().strip() or 'mod4'


mod = get_mod()

Groups (Workspaces)

Groups in Qtile are synonymous with workspaces (or desktops) in other operating systems. In our configuration, the groups are displayed at the top left of our screen (in the panel). Groups hold windows and allow you to switch between them easily.

In this case, we are initializing 10 group (1 - 10).

See Groups for key bindings

groups = [Group(i) for i in "1234567890"]

Layouts

Layouts control how windows are positioned on the screen. In our case, we are initializing two layouts:

  • MonadTall - Position the windows in a master+stack layout vertically
  • MonadWide - Position the windows in a master+stack layout horizontally

We set the border_focus (the border that surrounds active windows) to the color (#848cab - This is a hex color code). We set the width of that border to 2.

We also set the margin (The empty space surrounding windows) to 8.

See Qtile Built-In Layouts for more information.

default_layout_config = {
    "border_focus": "#848cab",
    "border_width": 2,
    "margin": 8
}

layouts = [
    #layout.Columns(border_focus_stack=["#d75f5f", "#8f3d3d"], border_width=4),
    #layout.Max(),
    # Try more layouts by unleashing below layouts.
    # layout.Stack(num_stacks=2),
    # layout.Bsp(),
    # layout.Matrix(),
    layout.MonadTall(**default_layout_config),
    layout.MonadWide(**default_layout_config),
    # layout.RatioTile(),
    # layout.Tile(),
    # layout.TreeTab(),
    # layout.VerticalTile(),
    # layout.Zoomy(),
]



Floating Layout

This adds a floating layout to the layouts. A floating layout allows you to drag, resize and place windows anywhere on the screen.

Here we have three bindings that allow us to control floating windows:

Binding Action
mod+left click and drag Move window
mod+right click Bring window to front
mod+middle click and drag Resize window
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()),
]

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
    ]
)

layouts.append(floating_layout)

Panel

This section controls the panel at the top of our screen. Below is an explanation of the default settings and all of the widgets within the panel.

Setting Value Explanation
font DejaVuSansMono The font used for text in the widgets
fontsize 16 The font size used for text in the widgets
background PRIMARY_COLOR The background color used by the widget
Widget Purpose
Current Layout Displays the current-active Qtile layout
Sep A separator line
GroupBox A list of groups (workspaces)
Sep A separator line
WindowName Shows the currently active window's name
TextBox (ModKey) A TextBox widget that displays the current modifier key
Sep A separator line
Memory Current memory usage percent
CPU Current CPU usage percent
Sep A separator line
Spacer Just adds some space between the sperator line and the battery widget, as the battery widget did not have any padding
BatteryIcon Show an icon of the current battery level. This may not work in virtual machines
Battery Shows the current battery percentage
Sep A separator line
Clock Shows the current date/time
Sep A separator line
Systray System tray. If system tray applications are installed, they will appear here.
widget_defaults = {
    'font': "DejaVuSansMono",
    'fontsize': 16,
    'background': PRIMARY_COLOR
}

screens = [
    Screen(
        top=bar.Bar(
            [
                widget.CurrentLayout(**widget_defaults),
                widget.Sep(linewidth=2),
                widget.GroupBox(**widget_defaults, active=["#949494", "#949494"], inactive=["#ffffff", "#ffffff"]),
                widget.Sep(linewidth=2),
                widget.WindowName(**widget_defaults, format="{name}"),
                widget.TextBox(**widget_defaults, text=f'{MOD_KEYS.get(mod)}'),
                widget.Sep(linewidth=2),
                widget.Memory(**widget_defaults, format='Memory: {MemPercent}%'),
                widget.CPU(**widget_defaults, format='CPU: {load_percent}%'),
                widget.Sep(linewidth=2),
                widget.Spacer(length=5),
                widget.BatteryIcon(**widget_defaults, battery="/sys/class/power_supply/BAT0", update_interval=1),
                widget.Battery(**widget_defaults, battery="/sys/class/power_supply/BAT0", update_interval=15, format="{percent:2.0%}"),
                widget.Sep(linewidth=2),
                widget.Clock(**widget_defaults, format="%Y-%m-%d %a %H:%M:%S"),
                widget.Systray(**widget_defaults),
            ],
            32,
        ),
    ),
]

Keybindings

Modifier Key Section Purpose
mod r Mod Key Management Toggle the modifier key
mod Return Terminal Opens alacritty
mod Left Groups Switches to previous group
mod Right Groups Switches to next group
mod <group_num> Groups Switches to <group_num> group
mod+shift <group_num> Groups Moves window to and switches to <group_num>
mod space Layouts Switch to next layout
mod h Windows Move focus left one window
mod l Windows Move focus right one window
mod j Windows Move focus down one window
mod k Windows Move focus up one window
mod+control h Windows Switch window with window to the left
mod+control l Windows Switch window with window to the right
mod+control j Windows Switch window with window below
mod+control k Windows Switch window with window above
mod+shift h Windows Decrease current window size
mod+shift l Windows Increase current window size
mod n Windows Reset window size
mod q Windows Close current window
mod+shift r QTile Reload Reload QTile config
mod+shift d Program Shortcuts Runs dmenu
mod b Program Shortcuts Runs the brave browser

Mod Key Management

This feature allows switching between modifier keys with a key binding (mod+r in this case). The current modifier key is stored in a file "~/.config/qtile/modkey". When the key is pressed, it runs "~/.scripts/toggle_qtile_mod.py". This script is shown below

The mod key is set in the Modifier Key section.

keys.extend([
    Key([mod], "r", lazy.spawn(os.path.expanduser('~/.scripts/toggle_qtile_mod.py')), lazy.reload_config())
]),

toggle_qtile_mod.py:

#!/usr/bin/python3

import os

MOD_KEY_FILE = os.path.expanduser('~/.config/qtile/modkey')

def get_mod():
    with open(MOD_KEY_FILE, 'r') as f:
        saved_mod = f.read().strip()
    if not saved_mod:
        saved_mod = 'mod1'
    return saved_mod

def set_mod(newmod):
    with open(MOD_KEY_FILE, 'w') as f:
        f.write(newmod)

def toggle_mod():
    mod = get_mod()
    if mod == 'mod4':
        set_mod('mod1')
    else:
        set_mod('mod4')


toggle_mod()

Terminal

keys.append(Key([mod], "Return", lazy.spawn('alacritty')))

Groups

keys.extend([
    Key([mod], "Left", lazy.screen.prev_group()),
    Key([mod], "Right", lazy.screen.next_group()),
])


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),
            ),
        ]
    )

Layouts

keys.extend([
    Key([mod], "space", lazy.layout.next()),
])

Windows

keys.extend([
  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, "control"], "h", lazy.layout.shuffle_left(), desc="Move window to the left"),
  Key([mod, "control"], "l", lazy.layout.shuffle_right(), desc="Move window to the right"),
  Key([mod, "control"], "j", lazy.layout.shuffle_down(), desc="Move window down"),
  Key([mod, "control"], "k", lazy.layout.shuffle_up(), desc="Move window up"),
  Key([mod, "shift"], "h",
      lazy.layout.grow_left(),
      lazy.layout.shrink(),
      lazy.layout.decrease_ratio(),
      lazy.layout.add(),
      ),
  Key([mod, "shift"], "l",
      lazy.layout.grow_right(),
      lazy.layout.grow(),
      lazy.layout.increase_ratio(),
      lazy.layout.delete(),
      ),
  Key([mod], "n", lazy.layout.normalize(), desc="Reset all window sizes"),
  Key([mod], "q", lazy.window.kill(), desc="Kill focused window"),
])

QTile Reload

keys.extend([
  Key([mod, "shift"], "r", lazy.reload_config(), desc="Reload the config"),
])

Program Shortcuts

keys.extend([
  Key([mod, "shift"], "d", lazy.spawn('dmenu_run')),
  Key([mod], "b", lazy.spawn('brave')),
])

Startup Script

This section will run a startup script after Qtile loads. This will allow us to auto-start certain programs.

@hook.subscribe.startup_once
def startup():
    autostart = os.path.expanduser('~/.config/qtile/autostart.sh')
    subprocess.run([autostart])