Skip to content

Instantly share code, notes, and snippets.

@arjan-s
Created September 16, 2022 09:44
Show Gist options
  • Save arjan-s/9e5a7eddecb8c7358797ced435869841 to your computer and use it in GitHub Desktop.
Save arjan-s/9e5a7eddecb8c7358797ced435869841 to your computer and use it in GitHub Desktop.
Qtile config
import asyncio
import os
import random
import subprocess
from typing import List # noqa: F401
from libqtile import bar, hook, layout, qtile
from libqtile.backend.wayland import InputConfig
from libqtile.config import Click, Drag, EzKey, Group, Match, Screen, ScratchPad, DropDown
from libqtile.lazy import lazy
from libqtile.log_utils import logger
from qtile_extras import widget
from qtile_extras.widget.decorations import PowerLineDecoration, RectDecoration
# VARIABLES
mod = "mod4"
terminal = "kitty"
home = os.path.expanduser('~')
menu_cmd = "wofi --show drun --allow-images --insensitive --no-actions"
layout_theme = {
"margin": 10,
"single_margin": 0,
"single_border_width": 0,
"border_focus": "#aaeedd",
"border_normal": "#4c566a",
"border_width": 2,
"border_on_single": True,
}
# Shadows configuration
#wl_shadows = {
# "radius": 12,
# "color": "#05001088",
# "offset": (-6, -6),
#}
wl_shadows = {
"radius": 5,
"color": "#ff000044",
"offset": (0, 0),
}
# Detect connected monitors
monitors = len(qtile.core.outputs)
logger.warning(f"Found {monitors} monitor(s)")
# Return random wallpaper
def get_random_wallpaper():
backgrounds_path = f"{home}/Nextcloud/Backgrounds/"
backgrounds = [os.path.join(backgrounds_path, file) for file in os.listdir(backgrounds_path)
if os.path.isfile(os.path.join(backgrounds_path, file))]
return random.choice(backgrounds)
# Set random wallpaper
def set_random_wallpaper():
for screen in qtile.screens:
screen.cmd_set_wallpaper(get_random_wallpaper(), "fill")
#qtile.call_later(900, set_random_wallpaper)
# INPUT DEVICES
wl_input_rules = {
"type:touchpad": InputConfig(tap=True, natural_scroll=True, middle_emulation=True),
}
# HOOKS
@hook.subscribe.startup_once
def autostart():
subprocess.run([f"{home}/.config/qtile/autostart.sh"])
#@hook.subscribe.startup
#def set_wallpaper_timer():
# qtile.call_later(900, set_random_wallpaper)
@hook.subscribe.screens_reconfigured
async def outputs_changed():
logger.warning("Screens reconfigured")
await asyncio.sleep(1)
logger.warning("Reloading config...")
qtile.cmd_reload_config()
@hook.subscribe.client_new
def log_new_client(client):
logger.warning(f"New window: name={client.name} wm_class={client.info()['wm_class']} app_id={client._app_id if hasattr(client, '_app_id') else None}")
@hook.subscribe.client_new
def auto_switch_layout(client):
#qtile.cmd_to_layout_index(1)
pass
# HOTKEYS AND KEYBOARD SHORTCUTS
def go_to_monitor(monitor: int):
def _inner(qtile):
qtile.focus_screen(monitor)
return _inner
keys = [
# Switch between windows
EzKey("M-A-h", lazy.layout.left(), desc="Move focus to left"),
EzKey("M-A-l", lazy.layout.right(), desc="Move focus to right"),
EzKey("M-A-j", lazy.layout.down(), desc="Move focus down"),
EzKey("M-A-k", lazy.layout.up(), desc="Move focus up"),
EzKey("M-A-<space>", lazy.layout.next(), desc="Move window focus to other window"),
# Move windows between left/right columns or move up/down in current stack.
# Moving out of range in Columns layout will create new column.
EzKey("M-S-h", lazy.layout.shuffle_left(), desc="Move window to the left"),
EzKey("M-S-l", lazy.layout.shuffle_right(), desc="Move window to the right"),
EzKey("M-S-j", lazy.layout.shuffle_down(), desc="Move window down"),
EzKey("M-S-k", lazy.layout.shuffle_up(), desc="Move window up"),
# Grow windows. If current window is on the edge of screen and direction
# will be to screen edge - window would shrink.
EzKey("M-C-h", lazy.layout.grow_left(), desc="Grow window to the left"),
EzKey("M-C-l", lazy.layout.grow_right(), desc="Grow window to the right"),
EzKey("M-C-j", lazy.layout.grow_down(), desc="Grow window down"),
EzKey("M-C-k", lazy.layout.grow_up(), desc="Grow window up"),
EzKey("M-n", lazy.layout.normalize(), desc="Reset all window sizes"),
# Toggle between split and unsplit sides of stack.
# Split = all windows displayed
# Unsplit = 1 window displayed, like Max layout, but still with
# multiple stack panes
EzKey("M-S-<Return>", lazy.layout.toggle_split(), desc="Toggle between split and unsplit sides of stack"),
# Toggle between different layouts as defined below
EzKey("M-<Tab>", lazy.next_layout(), desc="Toggle between layouts"),
# Toggle floating and fullscreen
EzKey("M-S-f", lazy.window.toggle_floating(), desc="Toggle floating"),
EzKey("M-f", lazy.window.toggle_fullscreen(), lazy.hide_show_bar(position='all'), desc='Toggle fullscreen and the bars'),
# Applications
EzKey("<F1>", lazy.spawn(menu_cmd), desc='Show Wofi'),
EzKey("M-s", lazy.group['scratchpad'].dropdown_toggle('term')),
EzKey("M-p", lazy.group['scratchpad'].dropdown_toggle('keepassxc')),
EzKey("M-e", lazy.spawn("Thunar"), desc="Launch file manager"),
EzKey("M-x", lazy.spawn(terminal), desc="Launch terminal"),
EzKey("M-k", lazy.spawn(f"{terminal} -e ikhal"), desc="Launch ikhal in a terminal"),
EzKey("M-m", lazy.spawn(f"{terminal} -e mutt"), desc="Launch mutt in a terminal"),
EzKey("M-r", lazy.spawn(f"{terminal} -e ranger"), desc="Launch ranger in a terminal"),
# Monitors
EzKey("M-A-1", lazy.function(go_to_monitor(0))),
EzKey("M-A-2", lazy.function(go_to_monitor(1))),
EzKey("M-A-3", lazy.function(go_to_monitor(2))),
# System keys
EzKey("<XF86AudioMute>", lazy.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle"), desc='Mute audio'),
EzKey("<XF86AudioLowerVolume>", lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ -5%"), desc='Volume down'),
EzKey("<XF86AudioRaiseVolume>", lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ +5%"), desc='Volume up'),
EzKey("M-<F10>", lazy.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle"), desc='Mute audio'),
EzKey("M-<F11>", lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ -5%"), desc='Volume down'),
EzKey("M-<F12>", lazy.spawn("pactl set-sink-volume @DEFAULT_SINK@ +5%"), desc='Volume up'),
EzKey("<XF86MonBrightnessDown>", lazy.spawn("brightnessctl set 5%-"), desc='Turn brightness down'),
EzKey("<XF86MonBrightnessUp>", lazy.spawn("brightnessctl set +5%"), desc='Turn brightness up'),
EzKey("M-q", lazy.window.kill(), desc="Kill focused window"),
EzKey("M-l", lazy.spawn("swaylock --grace 0"), desc="Lock screen"),
EzKey("M-C-r", lazy.reload_config(), desc="Reload the config"),
EzKey("M-C-q", lazy.shutdown(), desc="Shutdown Qtile"),
EzKey("M-C-p", lazy.spawn(f"{home}/Nextcloud/Tech/bin/wofi-power.sh"), desc="Power menu"),
]
# Terminal switching keys
for i in range(1, 5):
keys.append(EzKey("C-A-<F"+str(i)+">",
lazy.core.change_vt(i),
desc='Change to virtual console '+str(i)
),)
# WORKSPACES
workspaces = [
{"name": "1", "key": "1", "layout": "bsp"},
{"name": "2", "key": "2", "matches": [Match(wm_class='firefox'), Match(wm_class='firefox-default')], "layout": "monadtall"},
{"name": "3", "key": "3", "matches": [Match(wm_class='thunderbird')], "layout": "monadtall"},
{"name": "4", "key": "4", "layout": "bsp"},
{"name": "5", "key": "5", "layout": "bsp"},
{"name": "6", "key": "6", "layout": "bsp"},
{"name": "7", "key": "7", "layout": "bsp"},
{"name": "8", "key": "8", "layout": "bsp"},
{"name": "9", "key": "9", "layout": "bsp"},
]
groups = []
for index, workspace in enumerate(workspaces):
logger.warning(f"Setting up workspace {index} with key {workspace['key']} and layout {workspace['layout']}")
matches = workspace["matches"] if "matches" in workspace else None
layouts = workspace["layout"] if "layout" in workspace else None
groups.append(Group(workspace["name"], matches=matches, layout=layouts))
keys.append(EzKey("M-" + workspace["key"], lazy.group[workspace["name"]].toscreen()))
keys.append(EzKey("M-S-" + workspace["key"], lazy.window.togroup(workspace["name"])))
groups.append(ScratchPad("scratchpad", [
DropDown("term", terminal, opacity=1.0),
DropDown("keepassxc", "keepassxc", opacity=1.0, height=0.5),
]))
# LAYOUTS
layouts = [
# layout.Columns(border_focus_stack=['#d75f5f', '#8f3d3d'], border_width=4),
# Try more layouts by unleashing below layouts.
# layout.Stack(num_stacks=2, **layout_theme),
layout.Bsp(**layout_theme, fair=False),
# layout.Floating(**layout_theme),
# layout.Max(**layout_theme),
# layout.Matrix(**layout_theme),
layout.MonadTall(**layout_theme, ratio=0.6),
# layout.MonadWide(**layout_theme),
# layout.RatioTile(**layout_theme),
# layout.Tile(**layout_theme),
# layout.TreeTab(**layout_theme),
# layout.VerticalTile(**layout_theme),
# layout.Zoomy(**layout_theme),
]
# WIDGETS
widget_defaults = {
"font": "Hack Nerd Font Bold",
"fontsize": 12,
"padding": 10,
"foreground": "#2e3440",
}
# SCREENS (monitors)
extension_defaults = widget_defaults.copy()
powerline_left = {
"foreground": "#ffffff",
"decorations": [
PowerLineDecoration(path="arrow_left")
]
}
powerline_middle = {
"foreground": "#ffffff",
"decorations": [
PowerLineDecoration(path="arrow_right")
]
}
powerline_right = {
"decorations": [
PowerLineDecoration(path="arrow_right")
]
}
screens = []
for monitor in range(monitors):
screens.append(
Screen(
wallpaper=get_random_wallpaper(), wallpaper_mode='fill',
top=bar.Bar(
[
widget.GroupBox(
hide_unused=True,
margin_y=4,
margin_x=5,
padding_y=9,
padding_x=0,
borderwidth=7,
inactive=colors[4],
active=colors[7],
rounded=True,
highlight_color=colors[4],
highlight_method="text",
this_current_screen_border=colors[9],
block_highlight_text_color=colors[1],
background="88888888",
**powerline_left),
widget.GenPollText(
update_interval=600,
fmt="  {} ",
func=lambda: subprocess.run("ssh -q -oBatchMode=yes -oConnectTimeout=30 censored ./censored.sh", shell=True, capture_output=True).stdout.decode("utf-8").strip(),
background="aaaaaa88",
**powerline_left,
),
widget.GenPollText(
update_interval=600,
fmt="  {} ",
func=lambda: subprocess.run("ssh -q -oBatchMode=yes -oConnectTimeout=30 censored ./censored.sh", shell=True, capture_output=True).stdout.decode("utf-8").strip(),
background="88888888",
**powerline_left,
),
widget.GenPollText(
update_interval=600,
fmt="  {} ",
func=lambda: subprocess.run("ssh -q -oBatchMode=yes -oConnectTimeout=30 censored ./censored.sh", shell=True, capture_output=True).stdout.decode("utf-8").strip(),
background="aaaaaa88",
**powerline_left,
),
widget.GenPollText(
update_interval=600,
fmt="  {} ",
func=lambda: subprocess.run("curl -s https://example.com | jq -r .CENSORED", shell=True, capture_output=True).stdout.decode("utf-8").strip(),
background="88888888",
**powerline_left,
),
widget.WindowName(
background="aaaaaa88",
**powerline_middle,
),
widget.CurrentLayout(
fmt=" {}",
background="#aec597",
**powerline_right,
),
widget.GenPollText(
update_interval=10,
fmt=" {}",
func=lambda: subprocess.run(f"{home}/.config/qtile/wifiwidget.sh", capture_output=True).stdout.decode("utf-8").strip(),
background="#adefd1",
**powerline_right,
),
widget.Backlight(
fmt=" {}",
backlight_name='intel_backlight',
background="#FF7696",
**powerline_right,
),
widget.Battery(
low_percentage=0.2,
low_foreground="#ff0000",
update_interval=1,
charge_char='',
discharge_char='',
full_char='',
show_short_text=False,
format="{char} {percent:2.0%}",
background="#B591B0",
**powerline_right,
),
widget.PulseVolume(
fmt="墳 {}",
mouse_callbacks={'Button3': lambda: qtile.cmd_spawn("pavucontrol")},
limit_max_volume=True,
background="#ffb18f",
**powerline_right,
),
widget.StatusNotifier(
background="#282c3f",
**powerline_right,
),
widget.Clock(
format=" %d-%m-%Y  %H:%M",
background="#0ee9af",
**powerline_right,
),
],
20,
background="#00000000",
#margin=[10, 10, 10, 10], # N E S W
# border_width=[2, 0, 2, 0], # Draw top and bottom borders
# border_color=["ff00ff", "000000", "ff00ff", "000000"] # Borders are magenta
),
)
)
# Drag floating layouts.
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())
]
dgroups_key_binder = None
dgroups_app_rules = [] # type: List
follow_mouse_focus = True
bring_front_click = False
cursor_warp = False
floating_layout = layout.Floating(float_rules=[
*layout.Floating.default_float_rules,
Match(wm_class='zenity'),
Match(wm_class='ssh-askpass'),
Match(title='pinentry'),
])
auto_fullscreen = True
focus_on_window_activation = "smart"
reconfigure_screens = True
# If things like steam games want to auto-minimize themselves when losing
# focus, should we respect this or not?
auto_minimize = True
# XXX: Gasp! We're lying here. In fact, nobody really uses or cares about this
# string besides java UI toolkits; you can see several discussions on the
# mailing lists, GitHub issues, and other WM documentation that suggest setting
# this string if your java app doesn't work correctly. We may as well just lie
# and say that we're a working one by default.
#
# We choose LG3D to maximize irony: it is a 3D non-reparenting WM written in
# java that happens to be on java's whitelist.
wmname = "LG3D"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment