[all] Major refactoring

Introduce an "Engine" class to abstract some of the details of loading
modules, registering events, etc.
This commit is contained in:
Tobias Witek 2016-11-04 21:03:12 +01:00
parent a6bdd0557c
commit d62258c89d
5 changed files with 135 additions and 116 deletions

View file

@ -1,114 +1,20 @@
#!/usr/bin/env python
import os
import sys
import time
import glob
import pkgutil
import argparse
import textwrap
import threading
import importlib
import bumblebee.theme
import bumblebee.modules
import bumblebee.engine
import bumblebee.outputs.i3
def print_module_list():
print "available modules:"
path = os.path.dirname(bumblebee.modules.__file__)
for mod in [ name for _, name, _ in pkgutil.iter_modules([path])]:
m = importlib.import_module("bumblebee.modules.{}".format(mod))
desc = "n/a" if not hasattr(m, "description") else getattr(m, "description")()
usage = "n/a" if not hasattr(m, "usage") else getattr(m, "usage")()
notes = "n/a" if not hasattr(m, "notes") else getattr(m, "notes")()
print " {}: ".format(mod)
print textwrap.fill("Description: {}".format(desc),
80, initial_indent=" ", subsequent_indent=" ")
print textwrap.fill("Usage : {}".format(usage),
80, initial_indent=" ", subsequent_indent=" ")
print textwrap.fill("Notes : {}".format(notes),
80, initial_indent=" ", subsequent_indent=" ")
print ""
def print_theme_list():
d = bumblebee.theme.getpath()
print "available themes:"
print textwrap.fill(", ".join(
[ os.path.basename(f).replace(".json", "") for f in glob.iglob("{}/*.json".format(d)) ]),
80, initial_indent = " ", subsequent_indent = " "
)
def init_argument_parser():
parser = argparse.ArgumentParser(description="display system data in the i3bar")
parser.add_argument("-m", "--modules", nargs="+", help="List of modules to load. The order of the list determines their order in the i3bar (from left to right)", default=[])
parser.add_argument("-e", "--events", nargs="+", help="List of click events that should be handled. Format is: <module name><splitter, see -s><button ID><splitter><command to execute>", default=[])
parser.add_argument("-l", "--list", action="store_true", help="List all available modules and themes")
parser.add_argument("-t", "--theme", help="Specify which theme to use for drawing the modules")
parser.add_argument("-i", "--interval", help="Specify the update interval", default=1, type=int)
parser.add_argument("-s", "--split", help="Specify string to use for splitting modules and their arguments", default="::")
return parser
def getmodule(args, output, modulespec):
s = args.split
module_name = modulespec if not s in modulespec else modulespec.split(s)[0]
module_args = None if not s in modulespec else modulespec.split(s)[1:]
module = importlib.import_module("bumblebee.modules.{}".format(module_name))
return getattr(module, "Module")(output, module_args)
def register_event(args, output, event):
ev = event.split(args.split)
if len(ev) < 3:
sys.stderr.write("invalid format for click event, expect 3 parameters")
return
output.add_callback(
module=ev[0],
button=int(ev[1]),
cmd=ev[2],
)
def main():
parser = init_argument_parser()
args = bumblebee.engine.Arguments()
if len(sys.argv) == 1:
parser.print_help()
parser.exit()
theme = bumblebee.theme.Theme(args.args())
output = bumblebee.outputs.i3.Output(args.args())
args = parser.parse_args()
engine = bumblebee.engine.Engine(args.args(), theme, output)
engine.load_modules()
engine.register_events()
if args.list:
print_module_list()
print_theme_list()
sys.exit(0)
refresh = threading.Condition()
theme = bumblebee.theme.Theme(args.theme) if args.theme else bumblebee.theme.Theme()
output = bumblebee.outputs.i3.i3bar(refresh, theme)
modules = []
for m in args.modules:
modules.append(getmodule(args, output, m))
for e in args.events:
register_event(args, output, e)
print output.start()
sys.stdout.flush()
refresh.acquire()
while True:
theme.reset()
for m in modules:
output.add(m)
theme.next()
print output.get()
sys.stdout.flush()
refresh.wait(args.interval)
print output.stop()
engine.run()
if __name__ == "__main__":
main()

118
bumblebee/engine.py Normal file
View file

@ -0,0 +1,118 @@
import os
import sys
import glob
import pkgutil
import textwrap
import argparse
import threading
import importlib
import bumblebee.theme
class Arguments:
def __init__(self):
parser = self.create_parser()
if len(sys.argv) == 1:
parser.print_help()
parser.exit()
self._args = parser.parse_args()
if self._args.list:
self.print_module_list()
self.print_theme_list()
parser.exit()
def args(self):
return self._args
def create_parser(self):
parser = argparse.ArgumentParser(description="display system data in the i3bar")
parser.add_argument("-m", "--modules", nargs="+", help="List of modules to load. The order of the list determines their order in the i3bar (from left to right)", default=[])
parser.add_argument("-e", "--events", nargs="+", help="List of click events that should be handled. Format is: <module name><splitter, see -s><button ID><splitter><command to execute>", default=[])
parser.add_argument("-l", "--list", action="store_true", help="List all available modules and themes")
parser.add_argument("-t", "--theme", help="Specify which theme to use for drawing the modules", default="default")
parser.add_argument("-i", "--interval", help="Specify the update interval", default=1, type=int)
parser.add_argument("-s", "--split", help="Specify string to use for splitting modules and their arguments", default="::")
return parser
def print_theme_list(self):
d = bumblebee.theme.getpath()
print "available themes:"
print textwrap.fill(", ".join(
[ os.path.basename(f).replace(".json", "") for f in glob.iglob("{}/*.json".format(d)) ]),
80, initial_indent = " ", subsequent_indent = " "
)
def print_module_list(self):
print "available modules:"
path = os.path.dirname(bumblebee.modules.__file__)
for mod in [ name for _, name, _ in pkgutil.iter_modules([path])]:
m = importlib.import_module("bumblebee.modules.{}".format(mod))
desc = "n/a" if not hasattr(m, "description") else getattr(m, "description")()
usage = "n/a" if not hasattr(m, "usage") else getattr(m, "usage")()
notes = "n/a" if not hasattr(m, "notes") else getattr(m, "notes")()
print " {}: ".format(mod)
print textwrap.fill("Description: {}".format(desc),
80, initial_indent=" ", subsequent_indent=" ")
print textwrap.fill("Usage : {}".format(usage),
80, initial_indent=" ", subsequent_indent=" ")
print textwrap.fill("Notes : {}".format(notes),
80, initial_indent=" ", subsequent_indent=" ")
print ""
class Engine:
def __init__(self, args, theme, output):
self._modules = []
self._output = output
self._args = args
self._theme = theme
def load_module(self, modulespec):
name = modulespec.split(self._args.split)[0]
args = None if name == modulespec else modulespec.split(self._args.split)[1:]
module = importlib.import_module("bumblebee.modules.{}".format(name))
return getattr(module, "Module")(self._output, args)
def load_modules(self):
for m in self._args.modules:
self._modules.append(self.load_module(m))
def register_event(self, eventspec):
event = eventspec.split(self._args.split)
if len(event) < 3:
raise Exception("invalid click event format, expected 3 parameters")
self._output.add_callback(
module = event[0],
button = int(event[1]),
cmd = event[2],
)
def register_events(self):
for e in self._args.events:
self.register_event(e)
pass
def run(self):
print self._output.start()
sys.stdout.flush()
refresh = threading.Condition()
refresh.acquire()
while True:
# improve this
self._theme.reset()
for m in self._modules:
self._output.add(m, self._theme)
self._theme.next()
print self._output.get()
sys.stdout.flush()
refresh.wait(self._args.interval)
print self._output.stop()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,11 +1,10 @@
class Output(object):
def __init__(self, refresh, theme):
self._theme = theme
self._refresh = refresh
def __init__(self, args):
self._callbacks = {}
def redraw(self):
pass
self._refresh.acquire()
self._refresh.notify()
self._refresh.release()
@ -30,13 +29,10 @@ class Output(object):
), None)
return cb
def theme(self):
return self._theme
def start(self):
pass
def add(self, obj):
def add(self, obj, theme):
pass
def get(self):

View file

@ -27,9 +27,9 @@ def read_input(output):
subprocess.Popen(shlex.split(cb), stdout=DEVNULL, stderr=DEVNULL)
output.redraw()
class i3bar(bumblebee.output.Output):
def __init__(self, refresh, theme):
super(i3bar, self).__init__(refresh, theme)
class Output(bumblebee.output.Output):
def __init__(self, args):
super(Output, self).__init__(args)
self._data = []
self.add_callback("i3-msg workspace prev_on_output", 4)
@ -41,8 +41,7 @@ class i3bar(bumblebee.output.Output):
def start(self):
return json.dumps({ "version": 1, "click_events": True }) + "["
def add(self, obj):
theme = self.theme()
def add(self, obj, theme):
while True:
d = obj.data()

View file

@ -7,10 +7,10 @@ def getpath():
class Theme:
_cycle_index = 0
_cycle = None
def __init__(self, name="default"):
def __init__(self, args):
self._data = None
path = os.path.dirname(os.path.realpath(__file__))
with open("{}/{}.json".format(getpath(), name)) as f:
with open("{}/{}.json".format(getpath(), args.theme)) as f:
self._data = json.load(f)
self._defaults = self._data.get("defaults", {})
self._cycle = self._defaults.get("cycle", [])