Base theme, built on a class-based model. 9 workspaces based on function, basic system tray with cpu monitor, battery monitor and update indicator. Built-in multi-monitor support.

This commit is contained in:
Kevin Alberts 2017-06-06 00:15:28 +02:00
parent 5b2c93d89a
commit a5ef8739db
16 changed files with 549 additions and 0 deletions

1
.gitignore vendored
View file

@ -139,3 +139,4 @@ ENV/
# Rope project settings
.ropeproject
.idea

94
config.py Normal file
View file

@ -0,0 +1,94 @@
# Copyright (c) 2010 Aldo Cortesi
# Copyright (c) 2010, 2014 dequis
# Copyright (c) 2012 Randall Ma
# Copyright (c) 2012-2014 Tycho Andersen
# Copyright (c) 2012 Craig Barnes
# Copyright (c) 2013 horsik
# Copyright (c) 2013 Tao Sauvage
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Import Theme
from libqtile import hook
try:
from kuro.theme import Kuro
Theme = Kuro()
except ImportError:
from kuro.base import BaseTheme
Kuro = None
Theme = BaseTheme()
# Import theme configuration
try:
from kuro.config import Config
except ImportError:
try:
from kuro.baseconfig import BaseConfig as Config
except ImportError:
Config = None
raise ImportError("Could not load theme Config or BaseConfig!")
# Initialize the Theme
Theme.initialize()
# Hook theme into all hooks we know of
hook.subscribe.startup_once(Theme.callback_startup_once)
hook.subscribe.startup(Theme.callback_startup)
hook.subscribe.startup_complete(Theme.callback_startup_complete)
hook.subscribe.setgroup(Theme.callback_setgroup)
hook.subscribe.addgroup(Theme.callback_addgroup)
hook.subscribe.delgroup(Theme.callback_delgroup)
hook.subscribe.changegroup(Theme.callback_changegroup)
hook.subscribe.focus_change(Theme.callback_focus_change)
hook.subscribe.float_change(Theme.callback_float_change)
hook.subscribe.group_window_add(Theme.callback_group_window_add)
hook.subscribe.window_name_change(Theme.callback_window_name_change)
hook.subscribe.client_new(Theme.callback_client_new)
hook.subscribe.client_managed(Theme.callback_client_managed)
hook.subscribe.client_killed(Theme.callback_client_killed)
hook.subscribe.client_state_changed(Theme.callback_client_state_changed)
hook.subscribe.client_type_changed(Theme.callback_client_type_changed)
hook.subscribe.client_focus(Theme.callback_client_focus)
hook.subscribe.client_mouse_enter(Theme.callback_client_mouse_enter)
hook.subscribe.client_name_updated(Theme.callback_client_name_updated)
hook.subscribe.client_urgent_hint_changed(Theme.callback_client_urgent_hint_changed)
hook.subscribe.layout_change(Theme.callback_layout_change)
hook.subscribe.net_wm_icon_change(Theme.callback_net_wm_icon_change)
hook.subscribe.selection_notify(Theme.callback_selection_notify)
hook.subscribe.selection_change(Theme.callback_selection_change)
hook.subscribe.screen_change(Theme.callback_screen_change)
hook.subscribe.current_screen_change(Theme.callback_current_screen_change)
# Initialize variables from theme
keys = Theme.keys
groups = Theme.groups
layouts = Theme.layouts
widget_defaults = Theme.widget_defaults
screens = Theme.screens
dgroups_key_binder = Theme.dgroups_key_binder
dgroups_app_rules = Theme.dgroups_app_rules
main = Theme.main
follow_mouse_focus = Theme.follow_mouse_focus
bring_front_click = Theme.bring_front_click
cursor_warp = Theme.cursor_warp
floating_layout = Theme.floating_layout
auto_fullscreen = Theme.auto_fullscreen
focus_on_window_activation = Theme.focus_on_window_activation
extentions = Theme.extensions

145
kuro/base.py Normal file
View file

@ -0,0 +1,145 @@
from libqtile import layout as libqtile_layout
class BaseConfig:
@classmethod
def get(cls, key, default):
if hasattr(cls, key):
return cls.__dict__[key]
else:
return default
class BaseTheme:
# Changing variables initialized by function
keys = None
groups = None
layouts = None
widget_defaults = None
screens = None
# 'Static' variables
dgroups_key_binder = None
dgroups_app_rules = []
main = None
follow_mouse_focus = True
bring_front_click = False
cursor_warp = False
floating_layout = libqtile_layout.Floating()
auto_fullscreen = True
focus_on_window_activation = "smart"
extensions = []
# 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"
def initialize(self):
self.widget_defaults = self.init_widget_defaults()
self.keys = self.init_keys()
self.groups = self.init_groups()
self.layouts = self.init_layouts()
self.screens = self.init_screens()
def init_keys(self):
return []
def init_groups(self):
return []
def init_layouts(self):
return []
def init_widget_defaults(self):
return {}
def init_screens(self):
return []
def init_mouse(self):
return []
# Callbacks
def callback_startup_once(self, *args, **kwargs):
pass
def callback_startup(self, *args, **kwargs):
pass
def callback_startup_complete(self, *args, **kwargs):
pass
def callback_setgroup(self, *args, **kwargs):
pass
def callback_addgroup(self, *args, **kwargs):
pass
def callback_delgroup(self, *args, **kwargs):
pass
def callback_changegroup(self, *args, **kwargs):
pass
def callback_focus_change(self, *args, **kwargs):
pass
def callback_float_change(self, *args, **kwargs):
pass
def callback_group_window_add(self, *args, **kwargs):
pass
def callback_window_name_change(self, *args, **kwargs):
pass
def callback_client_new(self, *args, **kwargs):
pass
def callback_client_managed(self, *args, **kwargs):
pass
def callback_client_killed(self, *args, **kwargs):
pass
def callback_client_state_changed(self, *args, **kwargs):
pass
def callback_client_type_changed(self, *args, **kwargs):
pass
def callback_client_focus(self, *args, **kwargs):
pass
def callback_client_mouse_enter(self, *args, **kwargs):
pass
def callback_client_name_updated(self, *args, **kwargs):
pass
def callback_client_urgent_hint_changed(self, *args, **kwargs):
pass
def callback_layout_change(self, *args, **kwargs):
pass
def callback_net_wm_icon_change(self, *args, **kwargs):
pass
def callback_selection_notify(self, *args, **kwargs):
pass
def callback_selection_change(self, *args, **kwargs):
pass
def callback_screen_change(self, *args, **kwargs):
pass
def callback_current_screen_change(self, *args, **kwargs):
pass

58
kuro/config.py Normal file
View file

@ -0,0 +1,58 @@
from kuro.base import BaseConfig
# Config variables used in the main configuration
class Config(BaseConfig):
# Show debug messages
debug = True
# Default Applications
app_terminal = "terminator"
# Images
desktop_bg = "/home/kevin/Pictures/wallpapers/desktop.png"
# Fonts
font_default = "Noto Sans"
font_topbar = "Noto Sans"
font_clock = "Noto Sans"
font_titlebar = "Noto Sans"
font_groupbox = "FontAwesome"
# Font sizes
fontsize_default = 11
fontsize_topbar = 11
fontsize_clock = 11
fontsize_titlebar = 11
fontsize_groupbox = 15
# Sizes
width_border = 1
margin_layout = 4
width_groupbox_border = 1
height_groupbox = 24
margin_groupbox = 0
width_spacer = 1
padding_spacer = 4
# Variables
bool_groupbox_disable_drag = True
bool_groupbox_rounded_borders = True
# Colours
colour_border_normal = "#333333"
colour_border_focus = "#ffffff"
colour_groupbox_border_normal = "#333333"
colour_groupbox_border_focus = "#aaaaaa"
colour_groupbox_icon_active = "#ffffff"
colour_groupbox_icon_inactive = "#777777"
colour_spacer_background = "#777777"
# Battery variables
battery_theme_path = "/home/kevin/.config/qtile/kuro/resources/battery"
battery_update_delay = 2
# CheckUpdates variables
updates_display_format = "{updates}"
updates_execute_command = "terminator -e 'echo Updating\ via\ yaourt\ -Sayu...; yaourt -Sayu'"
updates_colour_available = '#f4d742'

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

201
kuro/theme.py Normal file
View file

@ -0,0 +1,201 @@
import logging
import os
from libqtile.config import Key, Screen, Group, Drag, Click
from libqtile.command import lazy
from libqtile import layout, bar, widget
# Import theme util functions
from kuro import utils
# Import variables
from kuro.base import BaseTheme
try:
from kuro.config import Config
except ImportError:
try:
from kuro.baseconfig import BaseConfig as Config
except ImportError:
Config = None
raise ImportError("Could not load theme Config or BaseConfig!")
# Initialize logging
log = logging.getLogger(__name__)
class Kuro(BaseTheme):
# Shorthand for modifier key
mod = Config.get("modifier", "mod4")
# Show debug messages
debug = Config.get('debug', False)
def initialize(self):
log.debug("Initializing Kuro Theme...")
super(Kuro, self).initialize()
# Update keys with keys for groups
self.update_keys()
def init_keys(self):
log.debug("Initializing keys")
return [
# Switch between windows in current stack pane
Key([self.mod], "k", lazy.layout.down()),
Key([self.mod], "j", lazy.layout.up()),
# Move windows up or down in current stack
Key([self.mod, "control"], "k", lazy.layout.shuffle_down()),
Key([self.mod, "control"], "j", lazy.layout.shuffle_up()),
# Switch window focus to other pane(s) of stack
Key([self.mod], "space", lazy.layout.next()),
# Swap panes of split stack
Key([self.mod, "shift"], "space", lazy.layout.rotate()),
# 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
Key([self.mod, "shift"], "Return", lazy.layout.toggle_split()),
Key([self.mod], "Return", lazy.spawn(Config.get('app_terminal', "xterm"))),
# Toggle between different layouts as defined below
Key([self.mod], "Tab", lazy.next_layout()),
Key([self.mod], "w", lazy.window.kill()),
Key([self.mod, "control"], "r", lazy.restart()),
Key([self.mod, "control"], "q", lazy.shutdown()),
Key([self.mod], "r", lazy.spawncmd()),
# Key([self.mod, "shift"], "e", self.evaluate()),
]
def init_groups(self):
log.debug("Initializing groups")
# http://fontawesome.io/cheatsheet
return [Group(i) for i in ""]
def init_layouts(self):
log.debug("Initializing layouts")
return [
layout.Matrix(columns=2,
border_focus=Config.get('colour_border_focus', "#ffffff"),
border_normal=Config.get('colour_border_normal', "#777777"),
border_width=Config.get('width_border', "1"),
margin=Config.get('margin_layout', "0"),
),
layout.Max(),
layout.Stack(num_stacks=2)
]
def init_widget_defaults(self):
log.debug("Initializing widget_defaults")
return {
"font": Config.get('font_default', "Sans"),
"fontsize": Config.get('fontsize_default', 16),
"padding": 3,
}
def init_screens(self):
log.debug("Initializing screens")
num_screens = utils.get_screen_count()
screens = []
for x in range(num_screens):
widgets = []
widgets.extend([
utils.bar_separator(Config),
widget.GroupBox(
active=Config.get('colour_groupbox_icon_active', '#ffffff'),
borderwidth=Config.get('width_groupbox_border', 1),
disable_drag=Config.get('bool_groupbox_disable_drag', False),
font=Config.get('font_groupbox', 'Arial'),
fontsize=Config.get('fontsize_groupbox', 15),
highlight_color=Config.get("colour_groupbox_border_normal", '#444444'),
inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
rounded=Config.get('bool_groupbox_rounded_borders', True),
this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
margin=Config.get('margin_groupbox', 0)
),
utils.bar_separator(Config),
widget.Prompt(**self.widget_defaults),
widget.WindowName(**self.widget_defaults),
widget.CPUGraph(
width=25,
border_color="#000000",
border_width=0,
line_width=1,
samples=10,
),
widget.BatteryIcon(
battery_name=Config.get('battery_name', 'BAT0'),
energy_full_file=Config.get('battery_energy_full_file', 'charge_full'),
energy_now_file=Config.get('battery_energy_now_file', 'charge_now'),
theme_path=Config.get('battery_theme_path', '/home/docs/checkouts/readthedocs.org/user_builds/qtile/checkouts/latest/libqtile/resources/battery-icons'),
update_delay=Config.get('battery_update_delay', 30)
),
])
# Systray only on first screen
if x == 0:
widgets.append(widget.Systray(**self.widget_defaults))
widgets.extend([
utils.bar_separator(Config),
widget.Clock(format="%a %d %b, %H:%M", **self.widget_defaults),
utils.CheckUpdatesYaourt(
colour_no_updates=Config.get('updates_colour_none', '#ffffff'),
colour_have_updates=Config.get('updates_colour_available', '#ff0000'),
display_format=Config.get('updates_display_format', 'Updates: {updates}'),
execute=Config.get('updates_execute_command', None),
**self.widget_defaults
),
widget.TextBox("#{}".format(x), name="default", **self.widget_defaults),
])
screens.append(Screen(top=bar.Bar(
widgets=widgets,
size=Config.get('height_groupbox', 30)
)))
return screens
def init_mouse(self):
log.debug("Initializing mouse")
# Drag floating layouts.
return [
Drag([self.mod], "Button1", lazy.window.set_position_floating(),
start=lazy.window.get_position()),
Drag([self.mod], "Button3", lazy.window.set_size_floating(),
start=lazy.window.get_size()),
Click([self.mod], "Button2", lazy.window.bring_to_front())
]
def update_keys(self):
log.debug("Updating keys")
for i, g in enumerate(self.groups):
# mod1 + number = switch to group
self.keys.append(
Key([self.mod], str(i + 1), lazy.group[g.name].toscreen())
)
# mod1 + shift + number = switch to & move focused window to group
self.keys.append(
Key([self.mod, "shift"], str(i + 1), lazy.window.togroup(g.name))
)
def callback_startup(self):
utils.execute("sleep 3")
utils.execute_once("nitrogen --restore")

50
kuro/utils.py Normal file
View file

@ -0,0 +1,50 @@
import re
import subprocess
from libqtile import widget, bar
from libqtile.widget.check_updates import CheckUpdates
def is_running(process):
s = subprocess.Popen(["ps", "axuw"], stdout=subprocess.PIPE)
for x in s.stdout:
if re.search(process, x.decode('utf-8')):
return True
return False
def execute(process):
return subprocess.Popen(process.split())
def execute_once(process):
if not is_running(process):
return subprocess.Popen(process.split())
def get_screen_count():
try:
output = subprocess.check_output("xrandr -q".split()).decode('utf-8')
output = [x for x in output.split("\n") if " connected" in x]
except subprocess.CalledProcessError:
return 1
if output:
return len(output)
else:
return 1
def bar_separator(config):
return widget.Sep(foreground=config.get('colour_spacer_background', '#777777'),
linewidth=config.get('width_spacer', 1),
padding=config.get('padding_spacer', 4),
)
class CheckUpdatesYaourt(CheckUpdates):
def __init__(self, **config):
super(CheckUpdatesYaourt, self).__init__(**config)
# Override command and output with yaourt command
self.cmd = "yaourt -Qua".split()
self.subtr = 0