Merge branch 'master' of github.com:tobi-wan-kenobi/bumblebee-status
Pulling
This commit is contained in:
commit
5b53ed5cf3
9 changed files with 221 additions and 10 deletions
|
@ -13,3 +13,4 @@ ratings:
|
||||||
- "**.py"
|
- "**.py"
|
||||||
exclude_paths:
|
exclude_paths:
|
||||||
- tests/
|
- tests/
|
||||||
|
- thirdparty/
|
||||||
|
|
64
bumblebee/modules/layout-xkb.py
Normal file
64
bumblebee/modules/layout-xkb.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# pylint: disable=C0111,R0903
|
||||||
|
|
||||||
|
"""Displays the current keyboard layout using libX11
|
||||||
|
|
||||||
|
Requires the following library:
|
||||||
|
* libX11.so.6
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
* layout-xkb.showname: Boolean that indicate whether the full name should be displayed. Defaults to false (only the symbol will be displayed)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import bumblebee.input
|
||||||
|
import bumblebee.output
|
||||||
|
import bumblebee.engine
|
||||||
|
|
||||||
|
has_xkb = True
|
||||||
|
try:
|
||||||
|
from xkbgroup import *
|
||||||
|
except ImportError:
|
||||||
|
has_xkb = False
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Module(bumblebee.engine.Module):
|
||||||
|
def __init__(self, engine, config):
|
||||||
|
super(Module, self).__init__(engine, config,
|
||||||
|
bumblebee.output.Widget(full_text=self.current_layout)
|
||||||
|
)
|
||||||
|
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
|
||||||
|
cmd=self._next_keymap)
|
||||||
|
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE,
|
||||||
|
cmd=self._prev_keymap)
|
||||||
|
|
||||||
|
def _next_keymap(self, event):
|
||||||
|
self._set_keymap(1)
|
||||||
|
|
||||||
|
def _prev_keymap(self, event):
|
||||||
|
self._set_keymap(-1)
|
||||||
|
|
||||||
|
def _set_keymap(self, rotation):
|
||||||
|
if not has_xkb: return
|
||||||
|
|
||||||
|
xkb = XKeyboard()
|
||||||
|
if xkb.groups_count < 2: return # nothing to doA
|
||||||
|
|
||||||
|
layouts = xkb.groups_symbols[rotation:] + xkb.groups_symbols[:rotation]
|
||||||
|
variants = xkb.groups_variants[rotation:] + xkb.groups_variants[:rotation]
|
||||||
|
|
||||||
|
try:
|
||||||
|
bumblebee.util.execute("setxkbmap -layout {} -variant {}".format(",".join(layouts), ",".join(variants)))
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def current_layout(self, widget):
|
||||||
|
try:
|
||||||
|
xkb = XKeyboard()
|
||||||
|
log.debug("group num: {}".format(xkb.group_num))
|
||||||
|
name = xkb.group_name if bumblebee.util.asbool(self.parameter("showname")) else xkb.group_symbol
|
||||||
|
return "{} ({})".format(name, xkb.group_variant) if xkb.group_variant else name
|
||||||
|
except Exception:
|
||||||
|
return "n/a"
|
||||||
|
|
||||||
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Displays GPU name, temperature and memory usage.
|
"""Displays GPU name, temperature and memory usage.
|
||||||
|
|
70
bumblebee/modules/rotation.py
Normal file
70
bumblebee/modules/rotation.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# pylint: disable=C0111,R0903
|
||||||
|
|
||||||
|
"""Shows a widget for each connected screen and allows the user to loop through different orientations.
|
||||||
|
|
||||||
|
Requires the following executable:
|
||||||
|
* xrandr
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import bumblebee.util
|
||||||
|
import bumblebee.input
|
||||||
|
import bumblebee.output
|
||||||
|
import bumblebee.engine
|
||||||
|
|
||||||
|
possible_orientations = ["normal", "left", "inverted", "right"]
|
||||||
|
|
||||||
|
class Module(bumblebee.engine.Module):
|
||||||
|
def __init__(self, engine, config):
|
||||||
|
widgets = []
|
||||||
|
self._engine = engine
|
||||||
|
super(Module, self).__init__(engine, config, widgets)
|
||||||
|
self.update_widgets(widgets)
|
||||||
|
|
||||||
|
def update_widgets(self, widgets):
|
||||||
|
new_widgets = []
|
||||||
|
for line in bumblebee.util.execute("xrandr -q").split("\n"):
|
||||||
|
if not " connected" in line:
|
||||||
|
continue
|
||||||
|
display = line.split(" ", 2)[0]
|
||||||
|
|
||||||
|
orientation = "normal"
|
||||||
|
for curr_orient in possible_orientations:
|
||||||
|
if((line.split(" ")).count(curr_orient) > 1):
|
||||||
|
orientation = curr_orient
|
||||||
|
break
|
||||||
|
|
||||||
|
widget = self.widget(display)
|
||||||
|
if not widget:
|
||||||
|
widget = bumblebee.output.Widget(full_text=display, name=display)
|
||||||
|
self._engine.input.register_callback(widget, button=bumblebee.input.LEFT_MOUSE, cmd=self._toggle)
|
||||||
|
new_widgets.append(widget)
|
||||||
|
widget.set("orientation", orientation)
|
||||||
|
|
||||||
|
while len(widgets) > 0:
|
||||||
|
del widgets[0]
|
||||||
|
for widget in new_widgets:
|
||||||
|
widgets.append(widget)
|
||||||
|
|
||||||
|
def update(self, widgets):
|
||||||
|
self.update_widgets(widgets)
|
||||||
|
|
||||||
|
def state(self, widget):
|
||||||
|
return widget.get("orientation", "normal")
|
||||||
|
|
||||||
|
def _toggle(self, event):
|
||||||
|
widget = self.widget_by_id(event["instance"])
|
||||||
|
|
||||||
|
# compute new orientation based on current orientation
|
||||||
|
idx = possible_orientations.index(widget.get("orientation"))
|
||||||
|
idx = (idx + 1) % len(possible_orientations)
|
||||||
|
new_orientation = possible_orientations[idx]
|
||||||
|
|
||||||
|
widget.set("orientation", new_orientation)
|
||||||
|
|
||||||
|
bumblebee.util.execute("xrandr --output {} --rotation {}".format(widget.name, new_orientation))
|
||||||
|
|
||||||
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
|
@ -47,6 +47,10 @@ class Module(bumblebee.engine.Module):
|
||||||
return temperature
|
return temperature
|
||||||
|
|
||||||
def get_mhz( self ):
|
def get_mhz( self ):
|
||||||
|
try:
|
||||||
|
output = open("/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq").read()
|
||||||
|
mhz = int(float(output)/1000.0)
|
||||||
|
except:
|
||||||
output = open("/proc/cpuinfo").read()
|
output = open("/proc/cpuinfo").read()
|
||||||
m = re.search(r"cpu MHz\s+:\s+(\d+)", output)
|
m = re.search(r"cpu MHz\s+:\s+(\d+)", output)
|
||||||
mhz = int(m.group(1))
|
mhz = int(m.group(1))
|
||||||
|
|
|
@ -16,6 +16,7 @@ Parameters:
|
||||||
import bumblebee.input
|
import bumblebee.input
|
||||||
import bumblebee.output
|
import bumblebee.output
|
||||||
import bumblebee.engine
|
import bumblebee.engine
|
||||||
|
import re
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
try:
|
try:
|
||||||
|
@ -27,11 +28,12 @@ except ImportError:
|
||||||
class Module(bumblebee.engine.Module):
|
class Module(bumblebee.engine.Module):
|
||||||
def __init__(self, engine, config):
|
def __init__(self, engine, config):
|
||||||
super(Module, self).__init__(engine, config,
|
super(Module, self).__init__(engine, config,
|
||||||
bumblebee.output.Widget(full_text=self.temperature)
|
bumblebee.output.Widget(full_text=self.output)
|
||||||
)
|
)
|
||||||
self._temperature = 0
|
self._temperature = 0
|
||||||
self._apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088")
|
self._apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088")
|
||||||
self._location = self.parameter("location", "auto")
|
self._location = self.parameter("location", "auto")
|
||||||
|
self._city = self.parameter("location", "")
|
||||||
self._interval = int(self.parameter("interval", "15"))
|
self._interval = int(self.parameter("interval", "15"))
|
||||||
self._unit = self.parameter("unit", "metric")
|
self._unit = self.parameter("unit", "metric")
|
||||||
self._nextcheck = 0
|
self._nextcheck = 0
|
||||||
|
@ -46,10 +48,17 @@ class Module(bumblebee.engine.Module):
|
||||||
return "F"
|
return "F"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def temperature(self, widget):
|
def temperature(self):
|
||||||
|
return u"{}°{}".format(self._temperature, self._unit_suffix())
|
||||||
|
|
||||||
|
def city(self):
|
||||||
|
self._city = re.sub('[_-]', ' ', self._city)
|
||||||
|
return u"{} ".format(self._city)
|
||||||
|
|
||||||
|
def output(self, widget):
|
||||||
if not self._valid:
|
if not self._valid:
|
||||||
return u"?"
|
return u"?"
|
||||||
return u"{}°{}".format(self._temperature, self._unit_suffix())
|
return self.city() + self.temperature()
|
||||||
|
|
||||||
def state( self, widget ):
|
def state( self, widget ):
|
||||||
if self._valid:
|
if self._valid:
|
||||||
|
@ -83,6 +92,7 @@ class Module(bumblebee.engine.Module):
|
||||||
location_url = "http://ipinfo.io/json"
|
location_url = "http://ipinfo.io/json"
|
||||||
location = json.loads(requests.get(location_url).text)
|
location = json.loads(requests.get(location_url).text)
|
||||||
coord = location["loc"].split(",")
|
coord = location["loc"].split(",")
|
||||||
|
self._city = location["city"]
|
||||||
weather_url = "{url}&lat={lat}&lon={lon}".format(url=weather_url, lat=coord[0], lon=coord[1])
|
weather_url = "{url}&lat={lat}&lon={lon}".format(url=weather_url, lat=coord[0], lon=coord[1])
|
||||||
else:
|
else:
|
||||||
weather_url = "{url}&q={city}".format(url=weather_url, city=self._location)
|
weather_url = "{url}&q={city}".format(url=weather_url, city=self._location)
|
||||||
|
|
|
@ -25,17 +25,20 @@ def themes():
|
||||||
class Theme(object):
|
class Theme(object):
|
||||||
"""Represents a collection of icons and colors"""
|
"""Represents a collection of icons and colors"""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self._init(self.load(name))
|
|
||||||
self._widget = None
|
self._widget = None
|
||||||
self._cycle_idx = 0
|
self._cycle_idx = 0
|
||||||
self._cycle = {}
|
self._cycle = {}
|
||||||
self._prevbg = None
|
self._prevbg = None
|
||||||
|
self._colorset = {}
|
||||||
|
self._init(self.load(name))
|
||||||
|
|
||||||
def _init(self, data):
|
def _init(self, data):
|
||||||
"""Initialize theme from data structure"""
|
"""Initialize theme from data structure"""
|
||||||
self._theme = data
|
self._theme = data
|
||||||
for iconset in data.get("icons", []):
|
for iconset in data.get("icons", []):
|
||||||
self._merge(data, self._load_icons(iconset))
|
self._merge(data, self._load_icons(iconset))
|
||||||
|
for colorset in data.get("colors", []):
|
||||||
|
self._merge(self._colorset, self._load_colors(colorset))
|
||||||
self._defaults = data.get("defaults", {})
|
self._defaults = data.get("defaults", {})
|
||||||
self._cycles = self._theme.get("cycle", [])
|
self._cycles = self._theme.get("cycle", [])
|
||||||
self.reset()
|
self.reset()
|
||||||
|
@ -99,6 +102,21 @@ class Theme(object):
|
||||||
"""Return the SBW"""
|
"""Return the SBW"""
|
||||||
return self._get(widget, "separator-block-width", None)
|
return self._get(widget, "separator-block-width", None)
|
||||||
|
|
||||||
|
def _load_wal_colors(self):
|
||||||
|
walfile = os.path.expanduser("~/.cache/wal/colors.json")
|
||||||
|
result = {}
|
||||||
|
with io.open(walfile) as data:
|
||||||
|
colors = json.load(data)
|
||||||
|
for field in ["special", "colors"]:
|
||||||
|
for key in colors[field]:
|
||||||
|
result[key] = colors[field][key]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _load_colors(self, name):
|
||||||
|
"""Load colors for a theme"""
|
||||||
|
if name == "wal":
|
||||||
|
return self._load_wal_colors()
|
||||||
|
|
||||||
def _load_icons(self, name):
|
def _load_icons(self, name):
|
||||||
"""Load icons for a theme"""
|
"""Load icons for a theme"""
|
||||||
path = "{}/icons/".format(theme_path())
|
path = "{}/icons/".format(theme_path())
|
||||||
|
@ -155,7 +173,9 @@ class Theme(object):
|
||||||
widget.set(key, (idx + 1) % len(value))
|
widget.set(key, (idx + 1) % len(value))
|
||||||
value = value[idx]
|
value = value[idx]
|
||||||
|
|
||||||
|
if isinstance(value, list) or isinstance(value, dict):
|
||||||
return value
|
return value
|
||||||
|
return self._colorset.get(value, value)
|
||||||
|
|
||||||
# algorithm copied from
|
# algorithm copied from
|
||||||
# http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts
|
# http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"brightness": { "prefix": "" },
|
"brightness": { "prefix": "" },
|
||||||
"load": { "prefix": "" },
|
"load": { "prefix": "" },
|
||||||
"layout": { "prefix": "" },
|
"layout": { "prefix": "" },
|
||||||
|
"layout-xkb": { "prefix": "" },
|
||||||
"todo": { "empty": {"prefix": "" },
|
"todo": { "empty": {"prefix": "" },
|
||||||
"items": {"prefix": "" }
|
"items": {"prefix": "" }
|
||||||
},
|
},
|
||||||
|
|
42
themes/wal-powerline.json
Normal file
42
themes/wal-powerline.json
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"icons": [ "awesome-fonts" ],
|
||||||
|
"colors": [ "wal" ],
|
||||||
|
"defaults": {
|
||||||
|
"separator-block-width": 0,
|
||||||
|
"critical": {
|
||||||
|
"fg": "cursor",
|
||||||
|
"bg": "color5"
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"fg": "cursor",
|
||||||
|
"bg": "color6"
|
||||||
|
},
|
||||||
|
"default_separators": false
|
||||||
|
},
|
||||||
|
"cycle": [
|
||||||
|
{
|
||||||
|
"fg": "foreground",
|
||||||
|
"bg": "background"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fg": "background",
|
||||||
|
"bg": "foreground"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dnf": {
|
||||||
|
"good": {
|
||||||
|
"fg": "background",
|
||||||
|
"bg": "color3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"charged": {
|
||||||
|
"fg": "background",
|
||||||
|
"bg": "color3"
|
||||||
|
},
|
||||||
|
"AC": {
|
||||||
|
"fg": "background",
|
||||||
|
"bg": "color3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue