[core] Widget creation/update overhaul

Until now, widgets were re-created during each iteration. For multiple,
reasons, using static widget objects is much easier, so instead of
creating new widgets continuously, modules now create the widgets during
instantiation and get the list of widgets passed as parameter whenever
an update occurs. During the update, they can still manipulate the
widget list by removing and adding elements as needed.

Advantages:
* Less memory fragmentation (fewer (de)allocations)
* Easier event management (widgets now have static IDs)
* Easier module code (widget contents can simply be the result of a
  callback)

see #23
This commit is contained in:
Tobi-wan Kenobi 2016-12-08 08:44:54 +01:00
parent 60ae92c8e3
commit f645203579
9 changed files with 65 additions and 17 deletions

View file

@ -12,8 +12,14 @@ class Module(object):
(e.g. CPU utilization, disk usage, etc.) derive from
this base class.
"""
def __init__(self, engine):
pass
def __init__(self, engine, widgets):
self._widgets = []
if widgets:
self._widgets = widgets if isinstance(widgets, list) else [widgets]
def widgets(self):
"""Return the widgets to draw for this module"""
return self._widgets
class Engine(object):
"""Engine for driving the application
@ -53,11 +59,9 @@ class Engine(object):
"""Start the event loop"""
self._output.start()
while self.running():
widgets = []
for module in self._modules:
module_widgets = module.widgets()
widgets += module_widgets if isinstance(module_widgets, list) else [module_widgets]
self._output.draw(widgets=widgets, engine=self)
module.update(module.widgets())
self._output.draw(widgets=module.widgets(), engine=self)
self._output.flush()
if self.running():
time.sleep(1)

View file

@ -7,12 +7,15 @@ import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine):
super(Module, self).__init__(engine)
super(Module, self).__init__(engine,
bumblebee.output.Widget(full_text=self.utilization)
)
self._utilization = psutil.cpu_percent(percpu=False)
def widgets(self):
self._utilization = psutil.cpu_percent(percpu=False)
def utilization(self):
return "{:05.02f}%".format(self._utilization)
return bumblebee.output.Widget(full_text="{:05.02f}%".format(self._utilization))
def update(self, widgets):
self._utilization = psutil.cpu_percent(percpu=False)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -6,9 +6,11 @@ import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine):
super(Module, self).__init__(engine)
super(Module, self).__init__(engine,
bumblebee.output.Widget(full_text="test")
)
def widgets(self):
return bumblebee.output.Widget(full_text="test")
def update(self, widgets):
pass
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,7 +12,10 @@ class Widget(object):
def full_text(self):
"""Retrieve the full text to display in the widget"""
return self._full_text
if callable(self._full_text):
return self._full_text()
else:
return self._full_text
class I3BarOutput(object):
"""Manage output according to the i3bar protocol"""

View file

@ -1,6 +1,7 @@
"""Theme support"""
class Theme(object):
"""Represents a collection of icons and colors"""
pass
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,3 +1,3 @@
#!/bin/sh
find . -name "*.py"|xargs pylint --disable=R0903,R0201
find . -name "*.py"|xargs pylint --disable=R0903,R0201,C0330

View file

@ -10,7 +10,14 @@ class TestCPUModule(unittest.TestCase):
self.module = Module(None)
def test_widgets(self):
widget = self.module.widgets()
assertWidgetAttributes(self, widget)
widgets = self.module.widgets()
for widget in widgets:
assertWidgetAttributes(self, widget)
def test_update(self):
widgets = self.module.widgets()
self.module.update(widgets)
self.test_widgets()
self.assertEquals(widgets, self.module.widgets())
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

25
tests/test_module.py Normal file
View file

@ -0,0 +1,25 @@
# pylint: disable=C0103,C0111,W0703
import unittest
from bumblebee.engine import Module
from tests.util import MockWidget
class TestModule(unittest.TestCase):
def setUp(self):
self.widget = MockWidget("foo")
self.moduleWithoutWidgets = Module(engine=None, widgets=None)
self.moduleWithOneWidget = Module(engine=None, widgets=self.widget)
self.moduleWithMultipleWidgets = Module(engine=None,
widgets=[self.widget, self.widget, self.widget]
)
def test_empty_widgets(self):
self.assertEquals(self.moduleWithoutWidgets.widgets(), [])
def test_single_widget(self):
self.assertEquals(self.moduleWithOneWidget.widgets(), [self.widget])
def test_multiple_widgets(self):
for widget in self.moduleWithMultipleWidgets.widgets():
self.assertEquals(widget, self.widget)

View file

@ -23,6 +23,9 @@ class MockWidget(object):
def __init__(self, text):
self._text = text
def update(self, widgets):
pass
def full_text(self):
return self._text