[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 #!/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.theme
import bumblebee.modules import bumblebee.engine
import bumblebee.outputs.i3 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(): def main():
parser = init_argument_parser() args = bumblebee.engine.Arguments()
if len(sys.argv) == 1: theme = bumblebee.theme.Theme(args.args())
parser.print_help() output = bumblebee.outputs.i3.Output(args.args())
parser.exit()
args = parser.parse_args() engine = bumblebee.engine.Engine(args.args(), theme, output)
engine.load_modules()
engine.register_events()
if args.list: engine.run()
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()
if __name__ == "__main__": if __name__ == "__main__":
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): class Output(object):
def __init__(self, refresh, theme): def __init__(self, args):
self._theme = theme
self._refresh = refresh
self._callbacks = {} self._callbacks = {}
def redraw(self): def redraw(self):
pass
self._refresh.acquire() self._refresh.acquire()
self._refresh.notify() self._refresh.notify()
self._refresh.release() self._refresh.release()
@ -30,13 +29,10 @@ class Output(object):
), None) ), None)
return cb return cb
def theme(self):
return self._theme
def start(self): def start(self):
pass pass
def add(self, obj): def add(self, obj, theme):
pass pass
def get(self): def get(self):

View file

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

View file

@ -7,10 +7,10 @@ def getpath():
class Theme: class Theme:
_cycle_index = 0 _cycle_index = 0
_cycle = None _cycle = None
def __init__(self, name="default"): def __init__(self, args):
self._data = None self._data = None
path = os.path.dirname(os.path.realpath(__file__)) 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._data = json.load(f)
self._defaults = self._data.get("defaults", {}) self._defaults = self._data.get("defaults", {})
self._cycle = self._defaults.get("cycle", []) self._cycle = self._defaults.get("cycle", [])