Skip to content

Instantly share code, notes, and snippets.

@heitzmann
Created January 31, 2019 11:22
Show Gist options
  • Save heitzmann/06d3e515274bc9823e4a81aa09984f20 to your computer and use it in GitHub Desktop.
Save heitzmann/06d3e515274bc9823e4a81aa09984f20 to your computer and use it in GitHub Desktop.
Qtile configuration
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import subprocess
import platform
from libqtile.config import Key, Screen, Group, Drag, Click
from libqtile.command import lazy
from libqtile import layout, bar, widget, hook
import custom
home = os.path.expanduser('~')
icons = home + '/.config_sync/icons/'
hostname = platform.uname().node
num_monitors = int(subprocess.run('xrandr|grep " connected"|wc -l', shell=True, stdout=subprocess.PIPE).stdout)
space = 12
mod = 'mod4'
c_bgn = '#000000'
c_hig = '#5b5ec0'
c_alt = '#e05b5e'
c_txt = '#d9e6f2'
c_shd = '#939393'
@hook.subscribe.startup_once
def startup():
subprocess.call([home + '/.config/qtile/startup.sh'])
@hook.subscribe.screen_change
def randrchange(qtile, ev):
qtile.cmd_restart()
layout_defaults = dict(
border_focus=c_alt,
border_normal=c_bgn,
border_focus_stack=c_alt,
border_normal_stack=c_bgn,
grow_amount=10,
border_width=2,
margin=6)
layouts = [
layout.Columns(fair=True, **layout_defaults),
layout.Bsp(fair=True, **layout_defaults),
]
floating_layout = layout.Floating(float_rules=[
{
'wmclass': 'Matplotlib'
},
{
'wmclass': 'cataclysm-tiles'
},
{
'wmclass': 'file_progress'
},
{
'wmclass': 'notification'
},
{
'wmclass': 'toolbar'
},
{
'wmclass': 'splash'
},
{
'wmclass': 'dialog'
},
{
'wmclass': 'pinentry'
},
{
'wmclass': 'pinentry-gtk-2'
},
{
'wname': 'Execute File'
},
{
'wname': 'Open'
},
{
'wname': 'Confirm File Replacing'
},
{
'role': 'about'
},
])
groups = [Group(str((i + 1) % 10)) for i in range(10)]
keys = [
# System
Key(['mod1', 'control'], 'Delete',
lazy.spawn(home + '/.config/qtile/shutdown.sh')),
Key([mod, 'control'], 'q', lazy.shutdown()),
Key([mod, 'control'], 'r', lazy.restart()),
Key([mod, 'control'], 'z', lazy.spawn('slock')),
Key([mod, 'control', 'shift'], 'z', lazy.spawn('xtrlock')),
Key([mod, 'control'], 'Tab', lazy.next_layout()),
Key([mod], 'F10', lazy.window.toggle_floating()),
Key([mod], 'F11', lazy.window.toggle_fullscreen()),
# Layout Columns/Bsp
Key([mod], 'j', lazy.layout.down()),
Key([mod], 'k', lazy.layout.up()),
Key([mod], 'h', lazy.layout.left()),
Key([mod], 'l', lazy.layout.right()),
Key([mod], 'n', lazy.group.next_window()),
Key([mod], 'u', lazy.group.prev_window()),
Key([mod, 'shift'], 'j', lazy.layout.shuffle_down()),
Key([mod, 'shift'], 'k', lazy.layout.shuffle_up()),
Key([mod, 'shift'], 'h', lazy.layout.shuffle_left()),
Key([mod, 'shift'], 'l', lazy.layout.shuffle_right()),
Key([mod, "mod1"], "j", lazy.layout.flip_down()),
Key([mod, "mod1"], "k", lazy.layout.flip_up()),
Key([mod, "mod1"], "h", lazy.layout.flip_left()),
Key([mod, "mod1"], "l", lazy.layout.flip_right()),
Key([mod, 'control'], 'j', lazy.layout.grow_down()),
Key([mod, 'control'], 'k', lazy.layout.grow_up()),
Key([mod, 'control'], 'h', lazy.layout.grow_left()),
Key([mod, 'control'], 'l', lazy.layout.grow_right()),
Key([mod, 'shift'], 'n', lazy.layout.normalize()),
Key([mod], 'Return', lazy.layout.toggle_split()),
# Screen
Key([mod], 'F1', lazy.spawn('light -p -U 10')),
Key([mod], 'F2', lazy.spawn('light -p -A 10')),
Key([mod, 'control'], 'Down',
lazy.spawn(home + '/scripts/displayctl.sh inverted')),
Key([mod, 'control'], 'Left',
lazy.spawn(home + '/scripts/displayctl.sh left')),
Key([mod, 'control'], 'Right',
lazy.spawn(home + '/scripts/displayctl.sh right')),
Key([mod, 'control'], 'Up',
lazy.spawn(home + '/scripts/displayctl.sh upright')),
Key([mod, 'control'], '0', lazy.spawn(home + '/scripts/displayctl.sh')),
Key([mod], 'Tab', lazy.next_screen()),
# Audio
Key([mod], 'minus', lazy.spawn('amixer sset Master,0 5%-')),
Key([mod], 'equal', lazy.spawn('amixer sset Master,0 5%+')),
Key([], 'XF86AudioLowerVolume', lazy.spawn('amixer sset Master,0 5%-')),
Key([], 'XF86AudioRaiseVolume', lazy.spawn('amixer sset Master,0 5%+')),
Key([], 'XF86AudioMute', lazy.spawn('amixer sset Master,0 toggle')),
Key([], 'XF86AudioPlay',
lazy.spawn(
'dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify '
'/org/mpris/MediaPlayer2 '
'org.mpris.MediaPlayer2.Player.PlayPause')),
Key([], 'XF86AudioStop',
lazy.spawn(
'dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify '
'/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Stop')),
Key([], 'XF86AudioNext',
lazy.spawn(
'dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify '
'/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Next')),
Key([], 'XF86AudioPrev',
lazy.spawn(
'dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify '
'/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Previous')),
# Window
Key(['mod1'], 'F4', lazy.window.kill()),
Key([mod], 'q', lazy.window.kill()),
Key([mod], 'b', lazy.spawn('chromium')),
Key([mod], 'f', lazy.spawn('termite -t "File Manager" -e lf')),
Key([mod], 't', lazy.spawn('termite')),
Key([mod], 'r', lazy.spawn('rofi -show run')),
Key([mod], 'space', lazy.spawn('rofi -show drun')),
# Group
Key([mod], 'Left', lazy.screen.prev_group()),
Key([mod], 'Right', lazy.screen.next_group()),
]
if num_monitors > 1:
keys.extend(
Key([mod], g.name[:1], lazy.to_screen(i % num_monitors), lazy.group[g.name].toscreen())
for i, g in enumerate(groups))
else:
keys.extend(
Key([mod], g.name[:1], lazy.group[g.name].toscreen())
for g in groups)
keys.extend(
Key([mod, 'shift'], g.name[:1].lower(), lazy.window.togroup(g.name))
for g in groups)
wid_defaults = dict(
font='Lato',
fontsize=24 if hostname == 'imac03' else 12,
pading=0,
foreground=c_txt)
def host_widgets():
global hostname
if hostname == 'yocto':
return [
widget.Spacer(space),
widget.Image(filename=icons + 'monitor.png', scale=True),
widget.Backlight(backlight_name='intel_backlight', **wid_defaults),
widget.Spacer(space),
widget.Image(filename=icons + 'battery.png', scale=True),
widget.Battery(charge_char='+', discharge_char='-', error_message='error',
format='{percent:2.0%} ({char}{hour:d}:{min:02d})', hide_threshold=None,
low_percentage=0.1, low_foreground=c_alt[1:], update_delay=10, **wid_defaults),
]
else:
return [
widget.TextBox(' | ', **wid_defaults),
widget.ThermalSensor(tag_sensor='temp1', update_interval=10, foreground_alert=c_alt[1:], **wid_defaults),
]
def system_info():
global hostname
return ([] if hostname == 's328a-1' else [
custom.Host(address='XXXXX', label='', label_alert=' s328a-1 down',
foreground_alert=c_alt[1:], update_interval=30, **wid_defaults),
widget.Spacer(space),
]) + [
custom.Host(address='XXXXX', label='', label_alert=' Ras314 down',
foreground_alert=c_alt[1:], update_interval=30, **wid_defaults),
widget.Spacer(space),
widget.Image(filename=icons + 'chip.png', scale=True),
custom.Cpu(update_interval=2, foreground_alert=c_alt[1:], **wid_defaults),
widget.Spacer(space),
widget.Image(filename=icons + 'memory.png', scale=True),
custom.Memory(update_interval=2, foreground_alert=c_alt[1:], **wid_defaults),
widget.Spacer(space),
widget.Image(filename=icons + 'hdd.png', scale=True),
*[widget.DF(partition=p, warn_space=2, measure='G', format=' {p}:{uf}{m}',
visible_on_warn=False, update_interval=60, warn_color=c_alt[1:], **wid_defaults)
for p in (('/', ) if hostname in ['yocto', 'imac03'] else ('/', '/home', '/var'))],
widget.Spacer(space),
widget.Image(filename=icons + 'temperature.png', scale=True),
widget.ThermalSensor(tag_sensor='Package id 0', update_interval=10, foreground_alert=c_alt[1:], **wid_defaults),
*host_widgets(),
widget.Spacer(space),
widget.Image(filename=icons + 'volume.png', scale=True),
widget.Volume(update_interval=0.5, step=5, **wid_defaults),
]
screens = [
Screen(
top=bar.Bar([
widget.CurrentLayoutIcon(scale=0.7, **wid_defaults),
widget.GroupBox(active=c_txt[1:], inactive=c_shd[1:], urgent_text=c_bgn[1:],
this_current_screen_border=c_hig[1:], this_screen_border=c_shd[1:],
other_screen_border=c_bgn[1:], urgent_border=c_alt[1:],
highlight_method='block', urgent_alert_method='block', rounded=False,
disable_drag=True, hide_unused=True, padding=3, **wid_defaults),
widget.WindowTabs(selected=('[', ']'), separator=' | ', **wid_defaults),
*(system_info() if i == 0 else []),
widget.Spacer(space),
widget.Image(filename=icons + 'schedule.png', scale=True),
widget.Clock(format='%Y-%m-%d %H:%M', **wid_defaults),
widget.Spacer(space),
*([widget.Systray(), widget.Spacer(space)] if i == 0 else []),
widget.LaunchBar(progs=[('Exit', home + '/.config/qtile/shutdown.sh -location 3', 'Exit menu')],
default_icon=home + '/.local/share/icons/logout.png', **wid_defaults),
], 42 if hostname == 'imac03' else 26, background=c_bgn)) for i in range(num_monitors)
]
# 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()),
]
main = None
follow_mouse_focus = False
bring_front_click = False
cursor_warp = False
auto_fullscreen = True
focus_on_window_activation = 'smart'
# 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'
import subprocess
from libqtile.widget import base
from libqtile.widget import groupbox
class Cpu(base.ThreadedPollText):
"""Display CPU usage"""
defaults = [
('update_interval', 1, 'The update interval.'),
('threshold', 75, 'Alert treshold value.'),
('foreground_alert', 'ff0000', 'Alert color'),
]
def __init__(self, **config):
base.ThreadedPollText.__init__(self, **config)
self.add_defaults(Cpu.defaults)
self.stats = self.get_stats()
def get_stats(self):
with open('/proc/stat', 'r') as f:
# user, nice, system, idle
val = [int(x) for x in f.readline().split(None, 5)[1:-1]]
return sum(val[:-1]), sum(val)
def poll(self):
stats = self.get_stats()
use = (100 * (stats[0] - self.stats[0])) // (stats[1] - self.stats[1])
self.stats = stats
self.layout.colour = (self.foreground_alert
if use > self.threshold else self.foreground)
return f'{use}%'.ljust(4)
class Hdd(base.ThreadedPollText):
"""Display HDD I/O"""
defaults = [
('bytes_per_sector', 512, 'Sector size in bytes.'),
('update_interval', 1, 'The update interval.'),
]
def __init__(self, **config):
base.ThreadedPollText.__init__(self, **config)
self.add_defaults(Hdd.defaults)
self.stats = self.get_stats()
def get_unit(self, b):
for unit, div in [('GB', 2**30), ('MB', 2**20), ('kB', 2**10)]:
if b >= div:
return b / div, unit
return b, 'B'
def get_stats(self):
r = w = 0
with open('/proc/diskstats', 'r') as f:
for line in f:
info = line.split()
if len(info[2]) == 3:
r += int(info[5])
w += int(info[9])
# sectors read, written
return r, w
def poll(self):
stats = self.get_stats()
r, ru = self.get_unit((stats[0] - self.stats[0]) *
self.bytes_per_sector / self.update_interval)
w, wu = self.get_unit((stats[1] - self.stats[1]) *
self.bytes_per_sector / self.update_interval)
self.stats = stats
return f'R:{r:-.3g}{ru}/s'.ljust(11) + f'W:{w:-.3g}{wu}/s'.ljust(10)
class Memory(base.ThreadedPollText):
"""Display memory usage"""
defaults = [
('update_interval', 1, 'The update interval.'),
('threshold', 75, 'Alert treshold value.'),
('foreground_alert', 'ff0000', 'Alert color'),
]
def __init__(self, **config):
base.ThreadedPollText.__init__(self, **config)
self.add_defaults(Memory.defaults)
def get_stats(self):
total = free = None
with open('/proc/meminfo', 'r') as f:
for line in f:
if line.startswith('MemTotal'):
total = int(line.split()[1])
if free is not None:
break
elif line.startswith('MemAvailable'):
free = int(line.split()[1])
if total is not None:
break
return 100 - (100 * free) // total
def poll(self):
use = self.get_stats()
self.layout.colour = self.foreground_alert if use > self.threshold else self.foreground
return f'{use}%'.ljust(4)
class Net(base.ThreadedPollText):
"""Displays interface down and up speed"""
defaults = [
('interface', 'wlan0', 'The interface to monitor'),
('update_interval', 1, 'The update interval.'),
]
def __init__(self, **config):
base.ThreadedPollText.__init__(self, **config)
self.add_defaults(Net.defaults)
self.stats = self.get_stats()
def get_unit(self, b):
for unit, div in [('GB', 2**30), ('MB', 2**20), ('kB', 2**10)]:
if b >= div:
return b / div, unit
return b, 'B'
def get_stats(self):
with open('/proc/net/dev', 'r') as f:
for line in f.readlines()[2:]:
info = line.split()
if info[0][:-1] == self.interface:
# down, up
return float(info[1]), float(info[9])
return 0, 0
def poll(self):
stats = self.get_stats()
d, du = self.get_unit(
(stats[0] - self.stats[0]) / self.update_interval)
u, uu = self.get_unit(
(stats[1] - self.stats[1]) / self.update_interval)
self.stats = stats
return f'D:{d:-.3g}{du}/s'.ljust(11) + f'U:{u:-.3g}{uu}/s'.ljust(10)
class Host(base.ThreadedPollText):
"""Check host status."""
defaults = [
('address', 'localhost', 'Host address to check.'),
('update_interval', 10, 'The update interval.'),
('label', '{address}', 'Text to display when host is reachable.'),
('label_alert', '{address}',
'Text to display when host is unreachable.'),
('foreground_alert', 'ff0000', 'Alert color'),
]
def __init__(self, **config):
base.ThreadedPollText.__init__(self, **config)
self.add_defaults(Host.defaults)
def poll(self):
ret = subprocess.run(['ping', '-qc', '1', '-W', '1', self.address])
if ret.returncode == 0:
label = self.label.format(address=self.address)
self.layout.colour = self.foreground
else:
label = self.label_alert.format(address=self.address)
self.layout.colour = self.foreground_alert
return label
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment