home | section main page | rss feed


QTile Configuration

Table of Contents

1. Introduction

This is a QTile configuration meant to be a monolithic configuration of QTile along with widgets i.e. run launcher, bar, and notification panel. This enables the window manager to be integrated with these things entirely, making for a unified experience in terms of configuration, styling, and functionality.

This configuration imports WALLPAPER and SOUND from my NixOS Configuration, and therefore if you use this standalone you must replace those variables.

2. Imports

We start with imports:

from paths import WALLPAPER, SOUND
from libqtile import bar, layout, widget, qtile
from libqtile.config import Key, Group, Screen, Click, Drag, Match
from libqtile.lazy import lazy
from libqtile.backend.wayland import InputConfig
from qtile_extras.layout.decorations import RoundedCorners

import re

We're using wayland because it's better.

3. Data

mod = "mod4"
terminal = "kitty"
wallpaper = WALLPAPER

colors = {
    "rosewater": "#f5e0dc",
    "flamingo": "#f2cdcd",
    "pink": "#f5c2e7",
    "mauve": "#cba6f7",
    "red": "#f38ba8",
    "maroon": "#eba0ac",
    "peach": "#fab387",
    "yellow": "#f9e2af",
    "green": "#a6e3a1",
    "teal": "#94e2d5",
    "sky": "#89dceb",
    "sapphire": "#74c7ec",
    "blue": "#89b4fa",
    "lavender": "#b4befe",
    "text": "#cdd6f4",
    "subtext1": "#bac2de",
    "subtext0": "#a6adc8",
    "overlay2": "#9399b2",
    "overlay1": "#7f849c",
    "overlay0": "#6c7086",
    "surface2": "#585b70",
    "surface1": "#45475a",
    "surface0": "#313244",
    "base": "#1e1e2e",
    "mantle": "#181825",
    "crust": "#11111b",
}

workspace_keys = {
    "1": "1",
    "2": "2",
    "3": "3",
    "4": "4",
    "5": "5",
    "6": "6",
    "7": "7",
    "8": "8",
    "9": "9",
}

4. Config

groups = [
    Group("1", matches=[Match(wm_class=re.compile(r"^emacs$", re.IGNORECASE))]),
    Group("2", matches=[Match(wm_class=re.compile(r"(qutebrowser|org\.qutebrowser\.qutebrowser)", re.IGNORECASE))]),
    Group(
        "3",
        matches=[
            Match(wm_class=re.compile(r"^signal.*", re.IGNORECASE)),
            Match(wm_class=re.compile(r"^element.*", re.IGNORECASE)),
        ],
    ),
    Group("4", matches=[Match(wm_class=re.compile(r"(pavucontrol|org\.pulseaudio\.pavucontrol)", re.IGNORECASE))]),
    Group("5"),
    Group("6"),
    Group("7"),
    Group("8"),
    Group("9"),
]

wl_input_rules = {
    "type:keyboard": InputConfig(
        kb_options="caps:escape",
        kb_repeat_rate=50,
        kb_repeat_delay=250,
    ),

    "type:pointer": InputConfig(
        scroll_method="on_button_down",
        scroll_button=276,  # Your exact trackball button code
    ),
}

class QuietBattery(widget.Battery):
    def poll(self):
        try:
            status = self._battery.update_status()
        except RuntimeError:
            return ""
        return self.build_string(status)
     
def logout():
    qtile.stop()


def reboot():
    qtile.spawn("systemctl reboot")


def shutdown():
    qtile.spawn("systemctl poweroff")


def workspace_bindings():
    bindings = []
    for group_name, key_name in workspace_keys.items():
        bindings.append(Key([mod], key_name, lazy.group[group_name].toscreen()))
        bindings.append(Key([mod, "shift"], key_name, lazy.window.togroup(group_name)))
    return bindings


keys = [
    # apps / session
    Key([mod], "Return", lazy.spawn(terminal)),
    Key([mod], "d", lazy.spawncmd()),
    Key([mod], "q", lazy.window.kill()),
    Key([mod], "f", lazy.window.toggle_fullscreen()),
    Key([mod], "Tab", lazy.next_layout()),
    Key([mod, "control"], "r", lazy.reload_config()),
    Key([mod], "t", lazy.window.toggle_floating()),

    # focus
    Key([mod], "h", lazy.layout.left()),
    Key([mod], "j", lazy.layout.down()),
    Key([mod], "k", lazy.layout.up()),
    Key([mod], "l", lazy.layout.right()),

    # move windows
    Key([mod, "shift"], "h", lazy.layout.shuffle_left()),
    Key([mod, "shift"], "j", lazy.layout.shuffle_down()),
    Key([mod, "shift"], "k", lazy.layout.shuffle_up()),
    Key([mod, "shift"], "l", lazy.layout.shuffle_right()),

    # resize windows
    Key([mod, "control"], "h", lazy.layout.grow_left()),
    Key([mod, "control"], "j", lazy.layout.grow_down()),
    Key([mod, "control"], "k", lazy.layout.grow_up()),
    Key([mod, "control"], "l", lazy.layout.grow_right()),
    Key([mod], "n", lazy.layout.normalize()),

    # layout helpers
    Key([mod], "space", lazy.layout.next()),
    Key([mod, "shift"], "space", lazy.layout.toggle_split()),

    # session shortcuts
    Key([mod, "shift"], "q", lazy.shutdown()),
    Key([mod, "control"], "Delete", lazy.spawn("systemctl reboot")),
    Key([mod, "shift"], "Delete", lazy.spawn("systemctl poweroff")),

    # app launchers
    Key([mod], "e", lazy.spawn("emacs")),
    Key([mod], "w", lazy.spawn("qutebrowser")),
    Key([mod], "c", lazy.spawn("signal-desktop")),
    Key([mod], "v", lazy.spawn("element-desktop")),
    Key([mod], "s", lazy.spawn("pavucontrol")),

    Key([], "XF86AudioPlay", lazy.spawn("mpc toggle")),
    Key([], "XF86AudioNext", lazy.spawn("mpc next")),
    Key([], "XF86AudioPrev", lazy.spawn("mpc prev")),
    Key([], "XF86AudioStop", lazy.spawn("mpc stop")),

    Key([], "XF86AudioRaiseVolume", lazy.spawn("wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+")),
    Key([], "XF86AudioLowerVolume", lazy.spawn("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-")),
    Key([], "XF86AudioMute", lazy.spawn("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")),
    Key([], "Print", lazy.spawn("sh -c 'grim ~/screenshot-$(date +%Y%m%d-%H%M%S).png'")),
] + workspace_bindings()

layouts = [
    layout.Columns(
        border_focus=colors["blue"],
        border_normal=colors["surface0"],
        
        border_focus_stack=colors["mauve"],
        border_normal_stack=colors["surface1"],
        
        border_width=3,
        margin=8,
        insert_position=1,
    ),
    layout.Max(
        border_focus=colors["blue"],
        border_normal=colors["surface0"],
        border_width=3,
        margin=8,
    ),
]

widget_defaults = {
    "font": "Iosevka Nerd Font",
    "fontsize": 13,
    "padding": 6,
    "background": colors["mantle"],
    "foreground": colors["text"],
}
extension_defaults = widget_defaults.copy()

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


def segment_icon(text, color, background=None, padding=8, font=None, fontsize=None):
    return widget.TextBox(
        text=text,
        foreground=color,
        background=background or colors["surface0"],
        padding=padding,
        font=font or "Iosevka Nerd Font",
        fontsize=fontsize,
    )


def segment_sep(padding=6):
    return widget.Sep(
        linewidth=0,
        padding=padding,
        background=colors["mantle"],
    )


power_widget = widget.WidgetBox(
    text_closed="󰐥",
    text_open="󰐥",
    close_button_location="right",
    background=colors["mantle"],
    font="Iosevka Nerd Font",
    widgets=[
        widget.TextBox(
            text=" 󰑐 ",
            foreground=colors["blue"],
            background=colors["surface0"],
            padding=8,
            font="Iosevka Nerd Font",
            mouse_callbacks={"Button1": reboot},
        ),
        widget.TextBox(
            text=" 󰍃 ",
            foreground=colors["mauve"],
            background=colors["surface0"],
            padding=8,
            font="Iosevka Nerd Font",
            mouse_callbacks={"Button1": logout},
        ),
        widget.TextBox(
            text=" 󰐥 ",
            foreground=colors["red"],
            background=colors["surface0"],
            padding=8,
            font="Iosevka Nerd Font",
            mouse_callbacks={"Button1": shutdown},
        ),
    ],
)

screens = [
    Screen(
        wallpaper=wallpaper,
        wallpaper_mode="fill",
        top=bar.Bar(
            [
                widget.GroupBox(
                    font="Iosevka Nerd Font",
                    fontsize=13,
                    background=colors["mantle"],
                    active=colors["text"],
                    inactive=colors["overlay0"],
                    highlight_method="line",
                    highlight_color=[colors["mantle"], colors["mantle"]],
                    this_current_screen_border=colors["lavender"],
                    other_current_screen_border=colors["blue"],
                    this_screen_border=colors["surface2"],
                    other_screen_border=colors["surface1"],
                    urgent_border=colors["red"],
                    urgent_text=colors["red"],
                    borderwidth=2,
                    rounded=False,
                    disable_drag=True,
                    spacing=8,
                    padding_x=8,
                    padding_y=5,
                    margin_y=2,
                    margin_x=0,
                ),
                widget.Spacer(length=10),
                widget.Prompt(
                    name="prompt",
                    prompt="❯ ",
                    cursor_color=colors["blue"],
                    foreground=colors["text"],
                    background=colors["surface0"],
                    padding=10,
                    font="Iosevka Nerd Font",
                ),
                widget.Spacer(),
             
                segment_icon("󰖙", colors["blue"]),
                widget.Wttr(
                    background=colors["surface0"],
                    format="%l: %t %C",
                    update_interval=1800,
                    font="Iosevka Nerd Font",
                    foreground=colors["teal"],
                ),
                segment_sep(),
             
                segment_icon("󰎆", colors["green"]),
                widget.Mpd2(
                    background=colors["surface0"],
                    foreground=colors["green"],
                    status_format="{play_status} {artist} - {title}",
                    idle_format=" idle",
                    idle_message="idle",
                    max_chars=40,
                    padding=10,
                    hide_crash=True,
                    font="Iosevka Nerd Font",
                ),
                segment_sep(),
             
                segment_icon("󰂄", colors["yellow"]),
                QuietBattery(
                    background=colors["surface0"],
                    foreground=colors["yellow"],
                    format="{percent:2.0%}",
                    charge_char="󰂄",
                    discharge_char="󰁹",
                    full_char="󰁹",
                    empty_char="󰂎",
                    update_interval=30,
                    padding=10,
                    font="Iosevka Nerd Font",
                ),
                segment_sep(),
             
                segment_icon("󰂚", colors["peach"]),
                widget.Notify(
                    foreground=colors["peach"],
                    background=colors["surface0"],
                    default_timeout=10,
                    audiofile=SOUND,
                    padding=10,
                    font="Iosevka Nerd Font",
                ),
                segment_sep(),
             
                segment_icon("󰃭", colors["lavender"]),
                widget.Clock(
                    format="%a %d %b",
                    foreground=colors["lavender"],
                    background=colors["surface0"],
                    padding=10,
                    font="Lora",
                ),
                segment_icon("󰥔", colors["blue"]),
                widget.Clock(
                    format="%H:%M",
                    foreground=colors["blue"],
                    background=colors["surface0"],
                    padding=10,
                    font="Iosevka Nerd Font",
                ),
             
                widget.Spacer(length=10),
                power_widget,
                widget.Spacer(length=10),
            ],
            30,
            margin=[8, 10, 0, 10],
            background=colors["mantle"],
            opacity=0.97,
        ),
    ),
]

follow_mouse_focus = True
bring_front_click = False
cursor_warp = False
auto_fullscreen = True
auto_minimize = True
wmname = "Qtile"