Merge branch 'master' into icon-themes

* separate color themes from icons
* each theme can now use one or more icon themes
* icons are chosen in a "first match" fashion

fixes #17
This commit is contained in:
Tobi-wan Kenobi 2016-11-30 18:30:01 +01:00
commit a93fa4aa5c
45 changed files with 296 additions and 24 deletions

View file

@ -63,20 +63,20 @@ Here are some screenshots for all themes that currently exist:
Gruvbox Powerline (`-t gruvbox-powerline`) (contributed by [@paxy97](https://github.com/paxy97)):
![Gruvbox Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/powerline-gruvbox.png)
![Gruvbox Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-gruvbox.png)
Solarized Powerline (`-t solarized-powerline`):
![Solarized Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/powerline-solarized.png)
![Solarized Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-solarized.png)
Solarized (`-t solarized`):
![Solarized](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/solarized.png)
![Solarized](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/solarized.png)
Powerline (`-t powerline`):
![Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/powerline.png)
![Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline.png)
Default (nothing or `-t default`):
![Default](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/default.png)
![Default](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/default.png)

26
bin/load-i3-bars.sh Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
if [ ! -f ~/.i3/config.template ]; then
cp ~/.i3/config ~/.i3/config.template
else
cp ~/.i3/config.template ~/.i3/config
fi
screens=$(xrandr -q|grep ' connected'| grep -P '\d+x\d+' |cut -d' ' -f1)
echo "screens: $screens"
while read -r line; do
screen=$(echo $line | cut -d' ' -f1)
others=$(echo $screens|tr ' ' '\n'|grep -v $screen|tr '\n' '-'|sed 's/.$//')
if [ -f ~/.i3/config.$screen-$others ]; then
cat ~/.i3/config.$screen-$others >> ~/.i3/config
else
if [ -f ~/.i3/config.$screen ]; then
cat ~/.i3/config.$screen >> ~/.i3/config
fi
fi
done <<< "$screens"
i3-msg restart

12
bin/toggle-display.sh Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
echo $(dirname $(readlink -f "$0"))
i3bar_update=$(dirname $(readlink -f "$0"))/load-i3-bars.sh
xrandr "$@"
if [ -f $i3bar_update ]; then
sleep 1
$i3bar_update
fi

View file

@ -16,7 +16,7 @@ class Module(bumblebee.module.Module):
super(Module, self).__init__(output, config, alias)
self._path = self._config.parameter("path", "/")
output.add_callback(module=self.instance(), button=1, cmd="nautilus {instance}")
output.add_callback(module=self.instance(), button=1, cmd="nautilus {}".format(self._path))
def widgets(self):
st = os.statvfs(self._path)

View file

@ -0,0 +1,67 @@
import subprocess
import shlex
import bumblebee.module
import bumblebee.util
def description():
return "Showws current keyboard layout and change it on click."
def parameters():
return [
"layout.lang: pipe-separated list of languages to cycle through (e.g. us|rs|de). Default: en"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config, alias):
super(Module, self).__init__(output, config, alias)
self._languages = self._config.parameter("lang", "en").split("|")
self._idx = 0
output.add_callback(module=self.instance(), button=1, cmd=self.next_keymap)
output.add_callback(module=self.instance(), button=3, cmd=self.prev_keymap)
def next_keymap(self, event, widget):
self._idx = self._idx + 1 if self._idx < len(self._languages) - 1 else 0
self.set_keymap()
def prev_keymap(self, event, widget):
self._idx = self._idx - 1 if self._idx > 0 else len(self._languages) - 1
self.set_keymap()
def set_keymap(self):
tmp = self._languages[self._idx].split(":")
layout = tmp[0]
variant = ""
if len(tmp) > 1:
variant = "-variant {}".format(tmp[1])
bumblebee.util.execute("setxkbmap -layout {} {}".format(layout, variant))
def widgets(self):
res = bumblebee.util.execute("setxkbmap -query")
layout = None
variant = None
for line in res.split("\n"):
if not line:
continue
if "layout" in line:
layout = line.split(":")[1].strip()
if "variant" in line:
variant = line.split(":")[1].strip()
if variant:
layout += ":" + variant
lang = self._languages[self._idx]
if lang != layout:
if layout in self._languages:
self._idx = self._languages.index(layout)
else:
self._languages.append(layout)
self._idx = len(self._languages) - 1
lang = layout
return bumblebee.output.Widget(self, lang)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

37
bumblebee/modules/load.py Normal file
View file

@ -0,0 +1,37 @@
import bumblebee.module
import multiprocessing
import os
def description():
return "Displays system load."
def parameters():
return [
"load.warning: Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs)",
"load.critical: Critical threshold for the one-minute load average (defaults 80% of the number of CPUs)"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config, alias):
super(Module, self).__init__(output, config, alias)
self._cpus = 1
try:
self._cpus = multiprocessing.cpu_count()
except multiprocessing.NotImplementedError as e:
pass
output.add_callback(module=self.instance(), button=1, cmd="gnome-system-monitor")
def widgets(self):
self._load = os.getloadavg()
return bumblebee.output.Widget(self, "{:.02f}/{:.02f}/{:.02f}".format(
self._load[0], self._load[1], self._load[2]))
def warning(self, widget):
return self._load[0] > self._config.parameter("warning", self._cpus*0.7)
def critical(self, widget):
return self._load[0] > self._config.parameter("critical", self._cpus*0.8)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -5,12 +5,14 @@ def description():
return "Displays the names, IP addresses and status of each available interface."
def parameters():
return [ "none" ]
return [
"nic.exclude: Comma-separated list of interface prefixes to exlude (defaults to: \"lo,virbr,docker,vboxnet,veth\")"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config, alias):
super(Module, self).__init__(output, config, alias)
self._exclude = ( "lo", "virbr", "docker", "vboxnet" )
self._exclude = tuple(filter(len, self._config.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
self._state = "down"
self._typecache = {}

View file

@ -0,0 +1,89 @@
import bumblebee.module
import bumblebee.util
import re
import os
import sys
import subprocess
def description():
return "Shows all connected screens"
def parameters():
return [
]
class Module(bumblebee.module.Module):
def __init__(self, output, config, alias):
super(Module, self).__init__(output, config, alias)
self._widgets = []
def toggle(self, event, widget):
path = os.path.dirname(os.path.abspath(__file__))
toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
if widget.get("state") == "on":
bumblebee.util.execute("{} --output {} --off".format(toggle_cmd, widget.get("display")))
else:
neighbor = None
for w in self._widgets:
if w.get("state") == "on":
neighbor = w
if event.get("button") == 1:
break
if neighbor == None:
bumblebee.util.execute("{} --output {} --auto".format(toggle_cmd,
widget.get("display")))
else:
bumblebee.util.execute("{} --output {} --auto --{}-of {}".format(toggle_cmd,
widget.get("display"), "left" if event.get("button") == 1 else "right",
neighbor.get("display")))
def widgets(self):
process = subprocess.Popen([ "xrandr", "-q" ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = process.communicate()
widgets = []
for line in output.split("\n"):
if not " connected" in line:
continue
display = line.split(" ", 2)[0]
m = re.search(r'\d+x\d+\+(\d+)\+\d+', line)
widget = bumblebee.output.Widget(self, display, instance=display)
widget.set("display", display)
# not optimal (add callback once per interval), but since
# add_callback() just returns if the callback has already
# been registered, it should be "ok"
self._output.add_callback(module=display, button=1,
cmd=self.toggle)
self._output.add_callback(module=display, button=3,
cmd=self.toggle)
if m:
widget.set("state", "on")
widget.set("pos", int(m.group(1)))
else:
widget.set("state", "off")
widget.set("pos", sys.maxint)
widgets.append(widget)
widgets.sort(key=lambda widget : widget.get("pos"))
self._widgets = widgets
return widgets
def state(self, widget):
return widget.get("state", "off")
def warning(self, widget):
return False
def critical(self, widget):
return False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,6 +15,8 @@ class Widget(object):
self._store = {}
self._instance = instance
obj._output.register_widget(self.instance(), self)
def set(self, key, value):
self._store[key] = value
@ -40,17 +42,23 @@ class Widget(object):
return self._text
class Command(object):
def __init__(self, command):
def __init__(self, command, event, widget):
self._command = command
self._event = event
self._widget = widget
def __call__(self, *args, **kwargs):
if not isinstance(self._command, list):
self._command = [ self._command ]
for cmd in self._command:
if not cmd: continue
if inspect.ismethod(cmd):
cmd(self._event, self._widget)
else:
c = cmd.format(*args, **kwargs)
DEVNULL = open(os.devnull, 'wb')
subprocess.Popen(shlex.split(c), stdout=DEVNULL, stderr=DEVNULL).communicate()
subprocess.Popen(shlex.split(c), stdout=DEVNULL, stderr=DEVNULL)
class Output(object):
def __init__(self, config):
@ -58,6 +66,10 @@ class Output(object):
self._callbacks = {}
self._wait = threading.Condition()
self._wait.acquire()
self._widgets = {}
def register_widget(self, identity, widget):
self._widgets[identity] = widget
def redraw(self):
self._wait.acquire()
@ -82,11 +94,11 @@ class Output(object):
), None)
cb = self._callbacks.get((
event.get("button", -1),
event.get("instance", event.get("name", None)),
event.get("instance", event.get("module", None)),
), cb)
if inspect.isfunction(cb) or cb is None: return cb
return Command(cb)
identity = event.get("instance", event.get("module", None))
return Command(cb, event, self._widgets.get(identity, None))
def wait(self):
self._wait.wait(self._config.parameter("interval", 1))

View file

@ -51,6 +51,10 @@ class Output(bumblebee.output.Output):
"separator_block_width": 0,
})
sep = theme.default_separators(widget)
sep = sep if sep else False
width = theme.separator_block_width(widget)
width = width if width else 0
self._data.append({
u"full_text": " {} {} {}".format(
theme.prefix(widget),
@ -61,8 +65,8 @@ class Output(bumblebee.output.Output):
"background": theme.background(widget),
"name": widget.module(),
"instance": widget.instance(),
"separator": theme.default_separators(widget),
"separator_block_width": theme.separator_block_width(widget),
"separator": sep,
"separator_block_width": width,
})
theme.next_widget()

View file

@ -46,7 +46,7 @@ class Theme:
item = args[0]
if not isinstance(item, dict):
return item
for key, value in item.iteritems():
for key, value in item.items():
if key in target and isinstance(target[key], dict):
self.merge(target[key], value)
else:
@ -64,10 +64,10 @@ class Theme:
self._cycle = self._cycles[idx] if len(self._cycles) > idx else {}
def prefix(self, widget):
return self._get(widget, "prefix")
return self._get(widget, "prefix", "")
def suffix(self, widget):
return self._get(widget, "suffix")
return self._get(widget, "suffix", "")
def color(self, widget):
result = self._get(widget, "fg")
@ -101,7 +101,7 @@ class Theme:
def separator_block_width(self, widget):
return self._get(widget, "separator-block-width")
def _get(self, widget, name):
def _get(self, widget, name, default = None):
module = widget.module()
state = widget.state()
inst = widget.instance()
@ -125,6 +125,6 @@ class Theme:
self._config.increase(key, len(value), 0)
value = value[idx]
return value
return value if value else default
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,3 +1,10 @@
import shlex
import subprocess
try:
from exceptions import RuntimeError
except ImportError:
# Python3 doesn't require this anymore
pass
def bytefmt(num):
for unit in [ "", "Ki", "Mi", "Gi" ]:
@ -13,3 +20,13 @@ def durationfmt(duration):
if hours > 0: res = "{:02d}:{}".format(hours, res)
return res
def execute(cmd):
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, err = p.communicate()
if p.returncode != 0:
raise RuntimeError("{} exited with {}".format(cmd, p.returncode))
return out.decode("utf-8")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 992 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
screenshots/date.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
screenshots/load.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
screenshots/pacman.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

BIN
screenshots/pasink.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
screenshots/pasource.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
screenshots/time.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
screenshots/xrandr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -112,5 +112,8 @@
},
"caffeine": {
"states": { "activated": {"prefix": "caf-on" }, "deactivated": { "prefix": "caf-off " } }
},
"xrandr": {
"states": { "on": { "prefix": " off "}, "off": { "prefix": " on "} }
}
}

View file

@ -127,5 +127,8 @@
},
"caffeine": {
"states": { "activated": {"prefix": " " }, "deactivated": { "prefix": " " } }
},
"xrandr": {
"states": { "on": { "prefix": " "}, "off": { "prefix": " "} }
}
}

View file

@ -17,7 +17,7 @@
"bg-warning": "#b58900",
"default_separators": false,
"separator": "|"
"separator": ""
},
"dnf": {
"states": {