[formatting] reformat using "black -t py34"
getting rid of thinking about consistent formatting...
This commit is contained in:
parent
fa98bcbdd1
commit
30c1f712a6
119 changed files with 3961 additions and 3495 deletions
179
core/config.py
179
core/config.py
|
@ -19,58 +19,66 @@ import modules.contrib
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MODULE_HELP = 'Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <module>:<alias> to provide an alias in case you want to load the same module multiple times, but specify different parameters.'
|
||||
PARAMETER_HELP = 'Provide configuration parameters in the form of <module>.<key>=<value>'
|
||||
THEME_HELP = 'Specify the theme to use for drawing modules'
|
||||
MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <module>:<alias> to provide an alias in case you want to load the same module multiple times, but specify different parameters."
|
||||
PARAMETER_HELP = (
|
||||
"Provide configuration parameters in the form of <module>.<key>=<value>"
|
||||
)
|
||||
THEME_HELP = "Specify the theme to use for drawing modules"
|
||||
|
||||
|
||||
def all_modules():
|
||||
"""Return a list of available modules"""
|
||||
result = {}
|
||||
|
||||
for path in [ modules.core.__file__, modules.contrib.__file__ ]:
|
||||
for path in [modules.core.__file__, modules.contrib.__file__]:
|
||||
path = os.path.dirname(path)
|
||||
for mod in glob.iglob('{}/*.py'.format(path)):
|
||||
result[os.path.basename(mod).replace('.py', '')] = 1
|
||||
|
||||
for mod in glob.iglob("{}/*.py".format(path)):
|
||||
result[os.path.basename(mod).replace(".py", "")] = 1
|
||||
|
||||
res = list(result.keys())
|
||||
res.sort()
|
||||
return res
|
||||
|
||||
|
||||
class print_usage(argparse.Action):
|
||||
def __init__(self, option_strings, dest, nargs=None, **kwargs):
|
||||
argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs)
|
||||
self._indent = ' '*2
|
||||
self._indent = " " * 2
|
||||
|
||||
def __call__(self, parser, namespace, value, option_string=None):
|
||||
if value == 'modules':
|
||||
if value == "modules":
|
||||
self._args = namespace
|
||||
self._format = 'plain'
|
||||
self._format = "plain"
|
||||
self.print_modules()
|
||||
elif value == 'modules-markdown':
|
||||
elif value == "modules-markdown":
|
||||
self._args = namespace
|
||||
self._format = 'markdown'
|
||||
self._format = "markdown"
|
||||
self.print_modules()
|
||||
elif value == 'themes':
|
||||
elif value == "themes":
|
||||
self.print_themes()
|
||||
sys.exit(0)
|
||||
|
||||
def print_themes(self):
|
||||
print(', '.join(core.theme.themes()))
|
||||
print(", ".join(core.theme.themes()))
|
||||
|
||||
def print_modules(self):
|
||||
if self._format == 'markdown':
|
||||
print('# Table of modules')
|
||||
print('|Name |Description |')
|
||||
print('|-----|------------|')
|
||||
if self._format == "markdown":
|
||||
print("# Table of modules")
|
||||
print("|Name |Description |")
|
||||
print("|-----|------------|")
|
||||
|
||||
for m in all_modules():
|
||||
try:
|
||||
basepath = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
|
||||
filename = os.path.join(basepath, 'modules', 'core', '{}.py'.format(m))
|
||||
basepath = os.path.abspath(
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
|
||||
)
|
||||
filename = os.path.join(basepath, "modules", "core", "{}.py".format(m))
|
||||
if not os.path.exists(filename):
|
||||
filename = os.path.join(basepath, 'modules', 'contrib', '{}.py'.format(m))
|
||||
filename = os.path.join(
|
||||
basepath, "modules", "contrib", "{}.py".format(m)
|
||||
)
|
||||
if not os.path.exists(filename):
|
||||
log.warning('module {} not found'.format(m))
|
||||
log.warning("module {} not found".format(m))
|
||||
continue
|
||||
|
||||
doc = None
|
||||
|
@ -79,72 +87,126 @@ class print_usage(argparse.Action):
|
|||
doc = ast.get_docstring(tree)
|
||||
|
||||
if not doc:
|
||||
log.warning('failed to find docstring for {}'.format(m))
|
||||
log.warning("failed to find docstring for {}".format(m))
|
||||
continue
|
||||
if self._format == 'markdown':
|
||||
doc = doc.replace('<', '\<')
|
||||
doc = doc.replace('>', '\>')
|
||||
doc = doc.replace('\n', '<br>')
|
||||
print('|{} |{} |'.format(m, doc))
|
||||
if self._format == "markdown":
|
||||
doc = doc.replace("<", "\<")
|
||||
doc = doc.replace(">", "\>")
|
||||
doc = doc.replace("\n", "<br>")
|
||||
print("|{} |{} |".format(m, doc))
|
||||
else:
|
||||
print(textwrap.fill('{}:'.format(m), 80,
|
||||
initial_indent=self._indent*2, subsequent_indent=self._indent*2))
|
||||
for line in doc.split('\n'):
|
||||
print(textwrap.fill(line, 80,
|
||||
initial_indent=self._indent*3, subsequent_indent=self._indent*6))
|
||||
print(
|
||||
textwrap.fill(
|
||||
"{}:".format(m),
|
||||
80,
|
||||
initial_indent=self._indent * 2,
|
||||
subsequent_indent=self._indent * 2,
|
||||
)
|
||||
)
|
||||
for line in doc.split("\n"):
|
||||
print(
|
||||
textwrap.fill(
|
||||
line,
|
||||
80,
|
||||
initial_indent=self._indent * 3,
|
||||
subsequent_indent=self._indent * 6,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
log.warning(e)
|
||||
|
||||
|
||||
class Config(util.store.Store):
|
||||
def __init__(self, args):
|
||||
super(Config, self).__init__()
|
||||
|
||||
parser = argparse.ArgumentParser(description='bumblebee-status is a modular, theme-able status line generator for the i3 window manager. https://github.com/tobi-wan-kenobi/bumblebee-status/wiki')
|
||||
parser.add_argument('-m', '--modules', nargs='+', action='append', default=[],
|
||||
help=MODULE_HELP)
|
||||
parser.add_argument('-p', '--parameters', nargs='+', action='append', default=[],
|
||||
help=PARAMETER_HELP)
|
||||
parser.add_argument('-t', '--theme', default='default', help=THEME_HELP)
|
||||
parser.add_argument('-i', '--iconset', default='auto',
|
||||
help='Specify the name of an iconset to use (overrides theme default)')
|
||||
parser.add_argument('-a', '--autohide', nargs='+', default=[],
|
||||
help='Specify a list of modules to hide when not in warning/error state')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='Add debug fields to i3 output')
|
||||
parser.add_argument('-f', '--logfile', help='destination for the debug log file, if -d|--debug is specified; defaults to stderr')
|
||||
parser.add_argument('-r', '--right-to-left', action='store_true', help='Draw widgets from right to left, rather than left to right (which is the default)')
|
||||
parser.add_argument('-l', '--list', choices=['modules', 'themes', 'modules-markdown'], help='Display a list of available themes or available modules, along with their parameters',
|
||||
action=print_usage)
|
||||
parser = argparse.ArgumentParser(
|
||||
description="bumblebee-status is a modular, theme-able status line generator for the i3 window manager. https://github.com/tobi-wan-kenobi/bumblebee-status/wiki"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m", "--modules", nargs="+", action="append", default=[], help=MODULE_HELP
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--parameters",
|
||||
nargs="+",
|
||||
action="append",
|
||||
default=[],
|
||||
help=PARAMETER_HELP,
|
||||
)
|
||||
parser.add_argument("-t", "--theme", default="default", help=THEME_HELP)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--iconset",
|
||||
default="auto",
|
||||
help="Specify the name of an iconset to use (overrides theme default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--autohide",
|
||||
nargs="+",
|
||||
default=[],
|
||||
help="Specify a list of modules to hide when not in warning/error state",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--debug", action="store_true", help="Add debug fields to i3 output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--logfile",
|
||||
help="destination for the debug log file, if -d|--debug is specified; defaults to stderr",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--right-to-left",
|
||||
action="store_true",
|
||||
help="Draw widgets from right to left, rather than left to right (which is the default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--list",
|
||||
choices=["modules", "themes", "modules-markdown"],
|
||||
help="Display a list of available themes or available modules, along with their parameters",
|
||||
action=print_usage,
|
||||
)
|
||||
|
||||
self.__args = parser.parse_args(args)
|
||||
|
||||
for cfg in [ '~/.bumblebee-status.conf', '~/.config/bumblebee-status.conf', '~/.config/bumblebee-status/config' ]:
|
||||
for cfg in [
|
||||
"~/.bumblebee-status.conf",
|
||||
"~/.config/bumblebee-status.conf",
|
||||
"~/.config/bumblebee-status/config",
|
||||
]:
|
||||
cfg = os.path.expanduser(cfg)
|
||||
self.load_config(cfg)
|
||||
|
||||
parameters = [ item for sub in self.__args.parameters for item in sub ]
|
||||
parameters = [item for sub in self.__args.parameters for item in sub]
|
||||
for param in parameters:
|
||||
if not '=' in param:
|
||||
log.error('missing value for parameter "{}" - ignoring this parameter'.format(param))
|
||||
if not "=" in param:
|
||||
log.error(
|
||||
'missing value for parameter "{}" - ignoring this parameter'.format(
|
||||
param
|
||||
)
|
||||
)
|
||||
continue
|
||||
key, value = param.split('=', 1)
|
||||
key, value = param.split("=", 1)
|
||||
self.set(key, value)
|
||||
|
||||
def load_config(self, filename):
|
||||
if os.path.exists(filename):
|
||||
log.info('loading {}'.format(filename))
|
||||
log.info("loading {}".format(filename))
|
||||
tmp = RawConfigParser()
|
||||
tmp.read(filename)
|
||||
|
||||
if tmp.has_section('module-parameters'):
|
||||
for key, value in tmp.items('module-parameters'):
|
||||
if tmp.has_section("module-parameters"):
|
||||
for key, value in tmp.items("module-parameters"):
|
||||
self.set(key, value)
|
||||
|
||||
def modules(self):
|
||||
return [item for sub in self.__args.modules for item in sub]
|
||||
|
||||
def interval(self, default=1):
|
||||
return util.format.seconds(self.get('interval', default))
|
||||
return util.format.seconds(self.get("interval", default))
|
||||
|
||||
def debug(self):
|
||||
return self.__args.debug
|
||||
|
@ -164,4 +226,5 @@ class Config(util.store.Store):
|
|||
def autohide(self, name):
|
||||
return name in self.__args.autohide
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
|
@ -1,41 +1,49 @@
|
|||
import util.format
|
||||
|
||||
|
||||
def never(init):
|
||||
def call_init(obj, *args, **kwargs):
|
||||
init(obj, *args, **kwargs)
|
||||
if obj.parameter('interval') is None:
|
||||
obj.set('interval', 'never')
|
||||
if obj.parameter("interval") is None:
|
||||
obj.set("interval", "never")
|
||||
|
||||
return call_init
|
||||
|
||||
|
||||
def every(hours=0, minutes=0, seconds=0):
|
||||
def decorator_init(init):
|
||||
def call_init(obj, *args, **kwargs):
|
||||
init(obj, *args, **kwargs)
|
||||
if obj.parameter('interval') is None:
|
||||
obj.set('interval', hours*3600 + minutes*60 + seconds)
|
||||
if obj.parameter("interval") is None:
|
||||
obj.set("interval", hours * 3600 + minutes * 60 + seconds)
|
||||
|
||||
return call_init
|
||||
|
||||
return decorator_init
|
||||
|
||||
|
||||
def scrollable(func):
|
||||
def wrapper(module, widget):
|
||||
text = func(module, widget)
|
||||
if not text:
|
||||
return text
|
||||
width = widget.get('theme.width', util.format.asint(module.parameter('width', 30)))
|
||||
if util.format.asbool(module.parameter('scrolling.makewide', True)):
|
||||
widget.set('theme.minwidth', 'A'*width)
|
||||
width = widget.get(
|
||||
"theme.width", util.format.asint(module.parameter("width", 30))
|
||||
)
|
||||
if util.format.asbool(module.parameter("scrolling.makewide", True)):
|
||||
widget.set("theme.minwidth", "A" * width)
|
||||
if width < 0 or len(text) <= width:
|
||||
return text
|
||||
|
||||
start = widget.get('scrolling.start', 0)
|
||||
bounce = util.format.asbool(module.parameter('scrolling.bounce', True))
|
||||
scroll_speed = util.format.asint(module.parameter('scrolling.speed', 1))
|
||||
direction = widget.get('scrolling.direction', 'right')
|
||||
start = widget.get("scrolling.start", 0)
|
||||
bounce = util.format.asbool(module.parameter("scrolling.bounce", True))
|
||||
scroll_speed = util.format.asint(module.parameter("scrolling.speed", 1))
|
||||
direction = widget.get("scrolling.direction", "right")
|
||||
|
||||
if direction == 'left':
|
||||
if direction == "left":
|
||||
scroll_speed = -scroll_speed
|
||||
if start + scroll_speed <= 0: # bounce back
|
||||
widget.set('scrolling.direction', 'right')
|
||||
if start + scroll_speed <= 0: # bounce back
|
||||
widget.set("scrolling.direction", "right")
|
||||
|
||||
next_start = start + scroll_speed
|
||||
if next_start + width > len(text):
|
||||
|
@ -43,11 +51,13 @@ def scrollable(func):
|
|||
next_start = 0
|
||||
else:
|
||||
next_start = start - scroll_speed
|
||||
widget.set('scrolling.direction', 'left')
|
||||
widget.set("scrolling.direction", "left")
|
||||
|
||||
widget.set('scrolling.start', next_start)
|
||||
widget.set("scrolling.start", next_start)
|
||||
|
||||
return text[start : start + width]
|
||||
|
||||
return text[start:start+width]
|
||||
return wrapper
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
__callbacks = {}
|
||||
|
||||
|
||||
def register(event, callback, *args, **kwargs):
|
||||
cb = callback
|
||||
if len(args) + len(kwargs) > 0:
|
||||
|
@ -8,12 +8,15 @@ def register(event, callback, *args, **kwargs):
|
|||
|
||||
__callbacks.setdefault(event, []).append(cb)
|
||||
|
||||
|
||||
def clear():
|
||||
__callbacks.clear()
|
||||
|
||||
|
||||
def trigger(event, *args, **kwargs):
|
||||
cb = __callbacks.get(event, [])
|
||||
if len(cb) == 0: return False
|
||||
if len(cb) == 0:
|
||||
return False
|
||||
|
||||
for callback in cb:
|
||||
if len(args) + len(kwargs) == 0:
|
||||
|
@ -22,4 +25,5 @@ def trigger(event, *args, **kwargs):
|
|||
callback(*args, **kwargs)
|
||||
return True
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
|
@ -11,45 +11,59 @@ RIGHT_MOUSE = 3
|
|||
WHEEL_UP = 4
|
||||
WHEEL_DOWN = 5
|
||||
|
||||
|
||||
def button_name(button):
|
||||
if button == LEFT_MOUSE: return 'left-mouse'
|
||||
if button == RIGHT_MOUSE: return 'right-mouse'
|
||||
if button == MIDDLE_MOUSE: return 'middle-mouse'
|
||||
if button == WHEEL_UP: return 'wheel-up'
|
||||
if button == WHEEL_DOWN: return 'wheel-down'
|
||||
return 'n/a'
|
||||
if button == LEFT_MOUSE:
|
||||
return "left-mouse"
|
||||
if button == RIGHT_MOUSE:
|
||||
return "right-mouse"
|
||||
if button == MIDDLE_MOUSE:
|
||||
return "middle-mouse"
|
||||
if button == WHEEL_UP:
|
||||
return "wheel-up"
|
||||
if button == WHEEL_DOWN:
|
||||
return "wheel-down"
|
||||
return "n/a"
|
||||
|
||||
|
||||
class Object(object):
|
||||
def __init__(self):
|
||||
super(Object, self).__init__()
|
||||
self.id = str(uuid.uuid4())
|
||||
|
||||
|
||||
def __event_id(obj_id, button):
|
||||
return '{}::{}'.format(obj_id, button_name(button))
|
||||
return "{}::{}".format(obj_id, button_name(button))
|
||||
|
||||
|
||||
def __execute(cmd):
|
||||
try:
|
||||
util.cli.execute(cmd, wait=False)
|
||||
except Exception as e:
|
||||
logging.error('failed to invoke callback: {}'.format(e))
|
||||
logging.error("failed to invoke callback: {}".format(e))
|
||||
|
||||
|
||||
def register(obj, button=None, cmd=None):
|
||||
event_id = __event_id(obj.id if obj is not None else '', button)
|
||||
logging.debug('registering callback {}'.format(event_id))
|
||||
event_id = __event_id(obj.id if obj is not None else "", button)
|
||||
logging.debug("registering callback {}".format(event_id))
|
||||
if callable(cmd):
|
||||
core.event.register(event_id, cmd)
|
||||
else:
|
||||
core.event.register(event_id, lambda _: __execute(cmd))
|
||||
|
||||
|
||||
def trigger(event):
|
||||
if not 'button' in event: return
|
||||
if not "button" in event:
|
||||
return
|
||||
|
||||
triggered = False
|
||||
for field in ['instance', 'name']:
|
||||
if not field in event: continue
|
||||
if core.event.trigger(__event_id(event[field], event['button']), event):
|
||||
for field in ["instance", "name"]:
|
||||
if not field in event:
|
||||
continue
|
||||
if core.event.trigger(__event_id(event[field], event["button"]), event):
|
||||
triggered = True
|
||||
if not triggered:
|
||||
core.event.trigger(__event_id('', event['button']), event)
|
||||
core.event.trigger(__event_id("", event["button"]), event)
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
|
@ -7,35 +7,41 @@ import core.widget
|
|||
import core.decorators
|
||||
|
||||
try:
|
||||
error = ModuleNotFoundError('')
|
||||
error = ModuleNotFoundError("")
|
||||
except Exception as e:
|
||||
ModuleNotFoundError = Exception
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load(module_name, config=core.config.Config([]), theme=None):
|
||||
error = None
|
||||
module_short, alias = (module_name.split(':') + [module_name])[0:2]
|
||||
config.set('__alias__', alias)
|
||||
for namespace in [ 'core', 'contrib' ]:
|
||||
module_short, alias = (module_name.split(":") + [module_name])[0:2]
|
||||
config.set("__alias__", alias)
|
||||
for namespace in ["core", "contrib"]:
|
||||
try:
|
||||
mod = importlib.import_module('modules.{}.{}'.format(namespace, module_short))
|
||||
log.debug('importing {} from {}.{}'.format(module_short, namespace, module_short))
|
||||
return getattr(mod, 'Module')(config, theme)
|
||||
mod = importlib.import_module(
|
||||
"modules.{}.{}".format(namespace, module_short)
|
||||
)
|
||||
log.debug(
|
||||
"importing {} from {}.{}".format(module_short, namespace, module_short)
|
||||
)
|
||||
return getattr(mod, "Module")(config, theme)
|
||||
except ImportError as e:
|
||||
error = e
|
||||
log.fatal('failed to import {}: {}'.format(module_name, error))
|
||||
error = e
|
||||
log.fatal("failed to import {}: {}".format(module_name, error))
|
||||
return Error(config=config, module=module_name, error=error)
|
||||
|
||||
|
||||
class Module(core.input.Object):
|
||||
def __init__(self, config=core.config.Config([]), theme=None, widgets=[]):
|
||||
super().__init__()
|
||||
self.__config = config
|
||||
self.__widgets = widgets if isinstance(widgets, list) else [ widgets ]
|
||||
self.__widgets = widgets if isinstance(widgets, list) else [widgets]
|
||||
|
||||
self.module_name = self.__module__.split('.')[-1]
|
||||
self.module_name = self.__module__.split(".")[-1]
|
||||
self.name = self.module_name
|
||||
self.alias = self.__config.get('__alias__', None)
|
||||
self.alias = self.__config.get("__alias__", None)
|
||||
self.id = self.alias if self.alias else self.name
|
||||
self.next_update = None
|
||||
|
||||
|
@ -50,13 +56,13 @@ class Module(core.input.Object):
|
|||
def parameter(self, key, default=None):
|
||||
value = default
|
||||
|
||||
for prefix in [ self.name, self.module_name, self.alias ]:
|
||||
value = self.__config.get('{}.{}'.format(prefix, key), value)
|
||||
for prefix in [self.name, self.module_name, self.alias]:
|
||||
value = self.__config.get("{}.{}".format(prefix, key), value)
|
||||
# TODO retrieve from config file
|
||||
return value
|
||||
|
||||
def set(self, key, value):
|
||||
self.__config.set('{}.{}'.format(self.name, key), value)
|
||||
self.__config.set("{}.{}".format(self.name, key), value)
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
@ -65,8 +71,8 @@ class Module(core.input.Object):
|
|||
try:
|
||||
self.update()
|
||||
except Exception as e:
|
||||
self.set('interval', 1)
|
||||
module = Error(config=self.__config, module='error', error=str(e))
|
||||
self.set("interval", 1)
|
||||
module = Error(config=self.__config, module="error", error=str(e))
|
||||
self.__widgets = [module.widget()]
|
||||
self.update = module.update
|
||||
|
||||
|
@ -75,28 +81,31 @@ class Module(core.input.Object):
|
|||
self.__widgets = widgets
|
||||
return self.__widgets
|
||||
|
||||
def add_widget(self, full_text='', name=None):
|
||||
def add_widget(self, full_text="", name=None):
|
||||
widget = core.widget.Widget(full_text=full_text, name=name, module=self)
|
||||
self.widgets().append(widget)
|
||||
return widget
|
||||
|
||||
def widget(self, name=None):
|
||||
if not name: return self.widgets()[0]
|
||||
if not name:
|
||||
return self.widgets()[0]
|
||||
|
||||
for w in self.widgets():
|
||||
if w.name == name: return w
|
||||
if w.name == name:
|
||||
return w
|
||||
return None
|
||||
|
||||
def state(self, widget):
|
||||
return []
|
||||
|
||||
def threshold_state(self, value, warn, crit):
|
||||
if value > float(self.parameter('critical', crit)):
|
||||
return 'critical'
|
||||
if value > float(self.parameter('warning', warn)):
|
||||
return 'warning'
|
||||
if value > float(self.parameter("critical", crit)):
|
||||
return "critical"
|
||||
if value > float(self.parameter("warning", warn)):
|
||||
return "warning"
|
||||
return None
|
||||
|
||||
|
||||
class Error(Module):
|
||||
def __init__(self, module, error, config=core.config.Config([]), theme=None):
|
||||
super().__init__(config, theme, core.widget.Widget(self.full_text))
|
||||
|
@ -104,9 +113,10 @@ class Error(Module):
|
|||
self.__error = error
|
||||
|
||||
def full_text(self, widget):
|
||||
return '{}: {}'.format(self.__module, self.__error)
|
||||
return "{}: {}".format(self.__module, self.__error)
|
||||
|
||||
def state(self, widget):
|
||||
return ['critical']
|
||||
return ["critical"]
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
172
core/output.py
172
core/output.py
|
@ -7,15 +7,17 @@ import core.event
|
|||
|
||||
import util.format
|
||||
|
||||
|
||||
def dump_json(obj):
|
||||
return obj.dict()
|
||||
|
||||
|
||||
def assign(src, dst, key, src_key=None, default=None):
|
||||
if not src_key:
|
||||
if key.startswith('_'):
|
||||
if key.startswith("_"):
|
||||
src_key = key
|
||||
else:
|
||||
src_key = key.replace('_', '-') # automagically replace _ with -
|
||||
src_key = key.replace("_", "-") # automagically replace _ with -
|
||||
|
||||
for k in src_key if isinstance(src_key, list) else [src_key]:
|
||||
if k in src:
|
||||
|
@ -24,12 +26,23 @@ def assign(src, dst, key, src_key=None, default=None):
|
|||
if default is not None:
|
||||
dst[key] = default
|
||||
|
||||
|
||||
class block(object):
|
||||
__COMMON_THEME_FIELDS = [
|
||||
'separator', 'separator-block-width', 'default-separators',
|
||||
'border-top', 'border-left', 'border-right', 'border-bottom',
|
||||
'fg', 'bg', 'padding', 'prefix', 'suffix'
|
||||
"separator",
|
||||
"separator-block-width",
|
||||
"default-separators",
|
||||
"border-top",
|
||||
"border-left",
|
||||
"border-right",
|
||||
"border-bottom",
|
||||
"fg",
|
||||
"bg",
|
||||
"padding",
|
||||
"prefix",
|
||||
"suffix",
|
||||
]
|
||||
|
||||
def __init__(self, theme, module, widget):
|
||||
self.__attributes = {}
|
||||
for key in self.__COMMON_THEME_FIELDS:
|
||||
|
@ -37,72 +50,85 @@ class block(object):
|
|||
if tmp is not None:
|
||||
self.__attributes[key] = tmp
|
||||
|
||||
self.__attributes['name'] = module.id
|
||||
self.__attributes['instance'] = widget.id
|
||||
self.__attributes['prev-bg'] = theme.get('bg', 'previous')
|
||||
self.__attributes["name"] = module.id
|
||||
self.__attributes["instance"] = widget.id
|
||||
self.__attributes["prev-bg"] = theme.get("bg", "previous")
|
||||
|
||||
def set(self, key, value):
|
||||
self.__attributes[key] = value
|
||||
|
||||
def is_pango(self, attr):
|
||||
if isinstance(attr, dict) and 'pango' in attr:
|
||||
if isinstance(attr, dict) and "pango" in attr:
|
||||
return True
|
||||
return False
|
||||
|
||||
def pangoize(self, text):
|
||||
if not self.is_pango(text):
|
||||
return text
|
||||
self.__attributes['markup'] = 'pango'
|
||||
attr = dict(text['pango'])
|
||||
text = attr.get('full_text', '')
|
||||
if 'full_text' in attr:
|
||||
del attr['full_text']
|
||||
result = '<span'
|
||||
self.__attributes["markup"] = "pango"
|
||||
attr = dict(text["pango"])
|
||||
text = attr.get("full_text", "")
|
||||
if "full_text" in attr:
|
||||
del attr["full_text"]
|
||||
result = "<span"
|
||||
for key, value in attr.items():
|
||||
result = '{} {}="{}"'.format(result, key, value)
|
||||
result = '{}>{}</span>'.format(result, text)
|
||||
result = "{}>{}</span>".format(result, text)
|
||||
return result
|
||||
|
||||
def dict(self):
|
||||
result = {}
|
||||
|
||||
assign(self.__attributes, result, 'full_text', ['full_text', 'separator'])
|
||||
assign(self.__attributes, result, 'separator', 'default-separators')
|
||||
assign(self.__attributes, result, "full_text", ["full_text", "separator"])
|
||||
assign(self.__attributes, result, "separator", "default-separators")
|
||||
|
||||
if '_decorator' in self.__attributes:
|
||||
assign(self.__attributes, result, 'color', 'bg')
|
||||
assign(self.__attributes, result, 'background', 'prev-bg')
|
||||
result['_decorator'] = True
|
||||
if "_decorator" in self.__attributes:
|
||||
assign(self.__attributes, result, "color", "bg")
|
||||
assign(self.__attributes, result, "background", "prev-bg")
|
||||
result["_decorator"] = True
|
||||
else:
|
||||
assign(self.__attributes, result, 'color', 'fg')
|
||||
assign(self.__attributes, result, 'background', 'bg')
|
||||
assign(self.__attributes, result, "color", "fg")
|
||||
assign(self.__attributes, result, "background", "bg")
|
||||
|
||||
if 'full_text' in self.__attributes:
|
||||
result['full_text'] = self.pangoize(result['full_text'])
|
||||
result['full_text'] = self.__format(self.__attributes['full_text'])
|
||||
if "full_text" in self.__attributes:
|
||||
result["full_text"] = self.pangoize(result["full_text"])
|
||||
result["full_text"] = self.__format(self.__attributes["full_text"])
|
||||
|
||||
for k in [
|
||||
'name', 'instance', 'separator_block_width', 'border', 'border_top',
|
||||
'border_bottom', 'border_left', 'border_right', 'markup',
|
||||
'_raw', '_suffix', '_prefix', 'min_width'
|
||||
"name",
|
||||
"instance",
|
||||
"separator_block_width",
|
||||
"border",
|
||||
"border_top",
|
||||
"border_bottom",
|
||||
"border_left",
|
||||
"border_right",
|
||||
"markup",
|
||||
"_raw",
|
||||
"_suffix",
|
||||
"_prefix",
|
||||
"min_width",
|
||||
]:
|
||||
assign(self.__attributes, result, k)
|
||||
|
||||
return result
|
||||
|
||||
def __pad(self, text):
|
||||
padding = self.__attributes.get('padding', '')
|
||||
if not text: return padding
|
||||
return '{}{}{}'.format(padding, text, padding)
|
||||
padding = self.__attributes.get("padding", "")
|
||||
if not text:
|
||||
return padding
|
||||
return "{}{}{}".format(padding, text, padding)
|
||||
|
||||
def __format(self, text):
|
||||
if text is None: return None
|
||||
prefix = self.__pad(self.pangoize(self.__attributes.get('prefix')))
|
||||
suffix = self.__pad(self.pangoize(self.__attributes.get('suffix')))
|
||||
self.set('_prefix', prefix)
|
||||
self.set('_suffix', suffix)
|
||||
self.set('_raw', text)
|
||||
return '{}{}{}'.format(prefix, text, suffix)
|
||||
if text is None:
|
||||
return None
|
||||
prefix = self.__pad(self.pangoize(self.__attributes.get("prefix")))
|
||||
suffix = self.__pad(self.pangoize(self.__attributes.get("suffix")))
|
||||
self.set("_prefix", prefix)
|
||||
self.set("_suffix", suffix)
|
||||
self.set("_raw", text)
|
||||
return "{}{}{}".format(prefix, text, suffix)
|
||||
|
||||
|
||||
class i3(object):
|
||||
def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])):
|
||||
|
@ -110,10 +136,10 @@ class i3(object):
|
|||
self.__content = {}
|
||||
self.__theme = theme
|
||||
self.__config = config
|
||||
core.event.register('update', self.update)
|
||||
core.event.register('start', self.draw, 'start')
|
||||
core.event.register('draw', self.draw, 'statusline')
|
||||
core.event.register('stop', self.draw, 'stop')
|
||||
core.event.register("update", self.update)
|
||||
core.event.register("start", self.draw, "start")
|
||||
core.event.register("draw", self.draw, "statusline")
|
||||
core.event.register("stop", self.draw, "stop")
|
||||
|
||||
def theme(self, new_theme=None):
|
||||
if new_theme:
|
||||
|
@ -123,63 +149,65 @@ class i3(object):
|
|||
def modules(self, modules=None):
|
||||
if not modules:
|
||||
return self.__modules
|
||||
self.__modules = modules if isinstance(modules, list) else [ modules ]
|
||||
self.__modules = modules if isinstance(modules, list) else [modules]
|
||||
|
||||
def draw(self, what, args=None):
|
||||
cb = getattr(self, what)
|
||||
data = cb(args) if args else cb()
|
||||
if 'blocks' in data:
|
||||
sys.stdout.write(json.dumps(data['blocks'], default=dump_json))
|
||||
if 'suffix' in data:
|
||||
sys.stdout.write(data['suffix'])
|
||||
sys.stdout.write('\n')
|
||||
if "blocks" in data:
|
||||
sys.stdout.write(json.dumps(data["blocks"], default=dump_json))
|
||||
if "suffix" in data:
|
||||
sys.stdout.write(data["suffix"])
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
def start(self):
|
||||
return {
|
||||
'blocks': { 'version': 1, 'click_events': True },
|
||||
'suffix': '\n[',
|
||||
"blocks": {"version": 1, "click_events": True},
|
||||
"suffix": "\n[",
|
||||
}
|
||||
|
||||
def stop(self):
|
||||
return { 'suffix': '\n]' }
|
||||
return {"suffix": "\n]"}
|
||||
|
||||
def __separator_block(self, module, widget):
|
||||
if not self.__theme.get('separator'):
|
||||
if not self.__theme.get("separator"):
|
||||
return []
|
||||
blk = block(self.__theme, module, widget)
|
||||
blk.set('_decorator', True)
|
||||
blk.set("_decorator", True)
|
||||
return [blk]
|
||||
|
||||
def __content_block(self, module, widget):
|
||||
blk = block(self.__theme, module, widget)
|
||||
minwidth = widget.theme('minwidth')
|
||||
minwidth = widget.theme("minwidth")
|
||||
if minwidth is not None:
|
||||
try:
|
||||
blk.set('min-width', '-'*int(minwidth))
|
||||
blk.set("min-width", "-" * int(minwidth))
|
||||
except:
|
||||
blk.set('min-width', minwidth)
|
||||
blk.set('full_text', self.__content[widget])
|
||||
if widget.get('pango', False):
|
||||
blk.set('markup', 'pango')
|
||||
blk.set("min-width", minwidth)
|
||||
blk.set("full_text", self.__content[widget])
|
||||
if widget.get("pango", False):
|
||||
blk.set("markup", "pango")
|
||||
if self.__config.debug():
|
||||
state = module.state(widget)
|
||||
if isinstance(state, list):
|
||||
state = ', '.join(state)
|
||||
blk.set('__state', state)
|
||||
state = ", ".join(state)
|
||||
blk.set("__state", state)
|
||||
return blk
|
||||
|
||||
def blocks(self, module):
|
||||
blocks = []
|
||||
for widget in module.widgets():
|
||||
if widget.module and self.__config.autohide(widget.module.name):
|
||||
if not any(state in widget.state() for state in [ 'warning', 'critical']):
|
||||
if not any(
|
||||
state in widget.state() for state in ["warning", "critical"]
|
||||
):
|
||||
continue
|
||||
if module.hidden():
|
||||
continue
|
||||
blocks.extend(self.__separator_block(module, widget))
|
||||
blocks.append(self.__content_block(module, widget))
|
||||
core.event.trigger('next-widget')
|
||||
core.event.trigger("next-widget")
|
||||
return blocks
|
||||
|
||||
# TODO: only updates full text, not the state!?
|
||||
|
@ -189,14 +217,16 @@ class i3(object):
|
|||
if affected_modules and not module.id in affected_modules:
|
||||
continue
|
||||
if not affected_modules and module.next_update:
|
||||
if module.parameter('interval', '') == 'never':
|
||||
if module.parameter("interval", "") == "never":
|
||||
continue
|
||||
if now < module.next_update:
|
||||
continue
|
||||
if not redraw_only:
|
||||
module.update_wrapper()
|
||||
if module.parameter('interval', '') != 'never':
|
||||
module.next_update = now + util.format.seconds(module.parameter('interval', self.__config.interval()))
|
||||
if module.parameter("interval", "") != "never":
|
||||
module.next_update = now + util.format.seconds(
|
||||
module.parameter("interval", self.__config.interval())
|
||||
)
|
||||
for widget in module.widgets():
|
||||
self.__content[widget] = widget.full_text()
|
||||
|
||||
|
@ -204,12 +234,10 @@ class i3(object):
|
|||
blocks = []
|
||||
for module in self.__modules:
|
||||
blocks.extend(self.blocks(module))
|
||||
return {
|
||||
'blocks': blocks,
|
||||
'suffix': ','
|
||||
}
|
||||
return {"blocks": blocks, "suffix": ","}
|
||||
|
||||
def wait(self, interval):
|
||||
time.sleep(interval)
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
|
@ -10,24 +10,26 @@ import util.algorithm
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
THEME_BASE_DIR=os.path.dirname(os.path.realpath(__file__))
|
||||
PATHS=[
|
||||
'.',
|
||||
os.path.join(THEME_BASE_DIR, '../themes'),
|
||||
os.path.expanduser('~/.config/bumblebee-status/themes'),
|
||||
THEME_BASE_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
PATHS = [
|
||||
".",
|
||||
os.path.join(THEME_BASE_DIR, "../themes"),
|
||||
os.path.expanduser("~/.config/bumblebee-status/themes"),
|
||||
]
|
||||
|
||||
|
||||
def themes():
|
||||
themes_dict = {}
|
||||
|
||||
for path in PATHS:
|
||||
for filename in glob.iglob('{}/*.json'.format(path)):
|
||||
if 'test' not in filename:
|
||||
themes_dict[os.path.basename(filename).replace('.json', '')] = 1
|
||||
for filename in glob.iglob("{}/*.json".format(path)):
|
||||
if "test" not in filename:
|
||||
themes_dict[os.path.basename(filename).replace(".json", "")] = 1
|
||||
result = list(themes_dict.keys())
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
|
||||
def merge_replace(value, new_value, key):
|
||||
if not isinstance(value, dict):
|
||||
return new_value
|
||||
|
@ -35,12 +37,13 @@ def merge_replace(value, new_value, key):
|
|||
util.algorithm.merge(value, new_value)
|
||||
return value
|
||||
# right now, merging needs explicit pango support :(
|
||||
if 'pango' in value:
|
||||
value['pango']['full_text'] = new_value
|
||||
if "pango" in value:
|
||||
value["pango"]["full_text"] = new_value
|
||||
return value
|
||||
|
||||
|
||||
class Theme(object):
|
||||
def __init__(self, name='default', iconset='auto', raw_data=None):
|
||||
def __init__(self, name="default", iconset="auto", raw_data=None):
|
||||
self.name = name
|
||||
self.__widget_count = 0
|
||||
self.__previous = {}
|
||||
|
@ -48,15 +51,15 @@ class Theme(object):
|
|||
self.__keywords = {}
|
||||
self.__value_idx = {}
|
||||
self.__data = raw_data if raw_data else self.load(name)
|
||||
for icons in self.__data.get('icons', []):
|
||||
util.algorithm.merge(self.__data, self.load(icons, 'icons'))
|
||||
if iconset != 'auto':
|
||||
util.algorithm.merge(self.__data, self.load(iconset, 'icons'))
|
||||
for colors in self.__data.get('colors', []):
|
||||
for icons in self.__data.get("icons", []):
|
||||
util.algorithm.merge(self.__data, self.load(icons, "icons"))
|
||||
if iconset != "auto":
|
||||
util.algorithm.merge(self.__data, self.load(iconset, "icons"))
|
||||
for colors in self.__data.get("colors", []):
|
||||
util.algorithm.merge(self.__keywords, self.load_keywords(colors))
|
||||
|
||||
core.event.register('draw', self.__start)
|
||||
core.event.register('next-widget', self.__next_widget)
|
||||
core.event.register("draw", self.__start)
|
||||
core.event.register("next-widget", self.__next_widget)
|
||||
|
||||
def keywords(self):
|
||||
return self.__keywords
|
||||
|
@ -64,17 +67,20 @@ class Theme(object):
|
|||
def color(self, name, default=None):
|
||||
return self.keywords().get(name, default)
|
||||
|
||||
def load(self, name, subdir=''):
|
||||
if isinstance(name, dict): return name # support plain data
|
||||
def load(self, name, subdir=""):
|
||||
if isinstance(name, dict):
|
||||
return name # support plain data
|
||||
for path in PATHS:
|
||||
theme_file = os.path.join(path, subdir, '{}.json'.format(name))
|
||||
theme_file = os.path.join(path, subdir, "{}.json".format(name))
|
||||
result = self.__load_json(theme_file)
|
||||
if result != {}: return result
|
||||
raise RuntimeError('unable to find theme {}'.format(name))
|
||||
if result != {}:
|
||||
return result
|
||||
raise RuntimeError("unable to find theme {}".format(name))
|
||||
|
||||
def __load_json(self, filename):
|
||||
filename = os.path.expanduser(filename)
|
||||
if not os.path.isfile(filename): return {}
|
||||
if not os.path.isfile(filename):
|
||||
return {}
|
||||
with io.open(filename) as data:
|
||||
return json.load(data)
|
||||
|
||||
|
@ -82,15 +88,15 @@ class Theme(object):
|
|||
try:
|
||||
if isinstance(name, dict):
|
||||
return name
|
||||
if name.lower() == 'wal':
|
||||
wal = self.__load_json('~/.cache/wal/colors.json')
|
||||
if name.lower() == "wal":
|
||||
wal = self.__load_json("~/.cache/wal/colors.json")
|
||||
result = {}
|
||||
for field in ['special', 'colors']:
|
||||
for field in ["special", "colors"]:
|
||||
for key in wal.get(field, {}):
|
||||
result[key] = wal[field][key]
|
||||
return result
|
||||
except Exception as e:
|
||||
log.error('failed to load colors: {}', e)
|
||||
log.error("failed to load colors: {}", e)
|
||||
|
||||
def __start(self):
|
||||
self.__widget_count = 0
|
||||
|
@ -107,14 +113,14 @@ class Theme(object):
|
|||
|
||||
def get(self, key, widget=None, default=None):
|
||||
if not widget:
|
||||
widget = core.widget.Widget('')
|
||||
widget = core.widget.Widget("")
|
||||
# special handling
|
||||
if widget == 'previous':
|
||||
if widget == "previous":
|
||||
return self.__previous.get(key, None)
|
||||
|
||||
value = default
|
||||
|
||||
for option in ['defaults', 'cycle']:
|
||||
for option in ["defaults", "cycle"]:
|
||||
if option in self.__data:
|
||||
tmp = self.__data[option]
|
||||
if isinstance(tmp, list):
|
||||
|
@ -127,7 +133,9 @@ class Theme(object):
|
|||
value = merge_replace(value, self.__data.get(key, value), key)
|
||||
|
||||
if widget.module:
|
||||
value = merge_replace(value, self.get(widget.module.name, None, {}).get(key, value), key)
|
||||
value = merge_replace(
|
||||
value, self.get(widget.module.name, None, {}).get(key, value), key
|
||||
)
|
||||
|
||||
if not key in widget.state():
|
||||
for state in widget.state():
|
||||
|
@ -138,11 +146,12 @@ class Theme(object):
|
|||
value = self.__keywords.get(value, value)
|
||||
|
||||
if isinstance(value, list):
|
||||
idx = self.__value_idx.get('{}::{}'.format(widget.id, key), 0) % len(value)
|
||||
self.__value_idx['{}::{}'.format(widget.id, key)] = idx
|
||||
idx = self.__value_idx.get("{}::{}".format(widget.id, key), 0) % len(value)
|
||||
self.__value_idx["{}::{}".format(widget.id, key)] = idx
|
||||
widget.set(key, idx)
|
||||
value = value[idx]
|
||||
self.__current[key] = value
|
||||
return value
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
|
@ -4,8 +4,9 @@ import core.decorators
|
|||
import util.store
|
||||
import util.format
|
||||
|
||||
|
||||
class Widget(util.store.Store, core.input.Object):
|
||||
def __init__(self, full_text='', name=None, module=None):
|
||||
def __init__(self, full_text="", name=None, module=None):
|
||||
super(Widget, self).__init__()
|
||||
self.__full_text = full_text
|
||||
self.module = module
|
||||
|
@ -19,25 +20,27 @@ class Widget(util.store.Store, core.input.Object):
|
|||
def module(self, module):
|
||||
self.__module = module
|
||||
|
||||
if self.index() < 0: return
|
||||
if self.index() < 0:
|
||||
return
|
||||
|
||||
if module:
|
||||
custom_ids = util.format.aslist(module.parameter('id'))
|
||||
custom_ids = util.format.aslist(module.parameter("id"))
|
||||
if len(custom_ids) > self.index():
|
||||
self.id = custom_ids[self.index()]
|
||||
|
||||
def index(self):
|
||||
if not self.module: return 0
|
||||
if not self.module:
|
||||
return 0
|
||||
|
||||
idx = 0
|
||||
for w in self.module.widgets():
|
||||
if w.id == self.id:
|
||||
return idx
|
||||
idx = idx + 1
|
||||
return -1 # not found
|
||||
return -1 # not found
|
||||
|
||||
def theme(self, attribute):
|
||||
attr = 'theme.{}'.format(attribute)
|
||||
attr = "theme.{}".format(attribute)
|
||||
if self.module:
|
||||
param = util.format.aslist(self.module.parameter(attr))
|
||||
if param and len(param) > self.index():
|
||||
|
@ -54,12 +57,13 @@ class Widget(util.store.Store, core.input.Object):
|
|||
|
||||
def state(self):
|
||||
rv = []
|
||||
if self.get('state', None):
|
||||
tmp = self.get('state')
|
||||
if self.get("state", None):
|
||||
tmp = self.get("state")
|
||||
rv = tmp[:] if isinstance(tmp, list) else [tmp]
|
||||
if self.module:
|
||||
tmp = self.module.state(self)
|
||||
rv.extend(tmp if isinstance(tmp, list) else [tmp])
|
||||
return rv
|
||||
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue