Commit 772372e9 authored by Bram Schoenmakers's avatar Bram Schoenmakers

Cache TodoWidgets to improve performance

Having hundreds of todo items slows down the column UI quite a lot when doing
an update. An update basically throws away all widgets and creates them from
scratch. That is quite a waste when only one item has changed.

Instead, keep all widgets in a cache, retrievable by the todo's raw text. When
the raw text changes, a new widget is constructed.
parent 86eb39da
......@@ -93,7 +93,8 @@ class TodoListWidget(urwid.LineBox):
del self.todolist[:]
for todo in self.view.todos:
todowidget = TodoWidget(todo, self.view.todolist.number(todo))
todowidget = TodoWidget.create(todo)
todowidget.number = self.view.todolist.number(todo)
self.todolist.append(todowidget)
self.todolist.append(urwid.Divider('-'))
......@@ -105,6 +106,10 @@ class TodoListWidget(urwid.LineBox):
# -2 for the same reason as in self._scroll_to_bottom()
self.todolist.set_focus(len(self.todolist) - 2)
# after the update there might be old stuff left in the widget cache,
# clean it
TodoWidget.clean_cache(self.view.todolist)
def _scroll_to_top(self, p_size):
self.listbox.set_focus(0)
......
......@@ -53,7 +53,7 @@ def _markup(p_todo, p_focus):
class TodoWidget(urwid.WidgetWrap):
def __init__(self, p_todo, p_number):
def __init__(self, p_todo):
# clients use this to associate this widget with the given todo item
self.todo = p_todo
......@@ -83,21 +83,21 @@ class TodoWidget(urwid.WidgetWrap):
else:
txt_markup.append(substring)
id_widget = urwid.Text(str(p_number), align='right')
self.id_widget = urwid.Text('', align='right')
priority_widget = urwid.Text(priority_text)
self.text_widget = urwid.Text(txt_markup)
progress = to_urwid_color(progress_color(p_todo)) if config().colors() else PaletteItem.DEFAULT
progress_bar = urwid.AttrMap(
self.progress_bar = urwid.AttrMap(
urwid.SolidFill(' '),
urwid.AttrSpec(PaletteItem.DEFAULT, progress, 256),
urwid.AttrSpec(PaletteItem.DEFAULT, progress, 256),
{},
)
self.update_progress()
self.columns = urwid.Columns(
[
(1, progress_bar),
(4, id_widget),
(1, self.progress_bar),
(4, self.id_widget),
(3, priority_widget),
('weight', 1, self.text_widget),
],
......@@ -124,6 +124,21 @@ class TodoWidget(urwid.WidgetWrap):
# make sure that ListBox will highlight this widget
return True
@property
def number(self):
pass
@number.setter
def number(self, p_number):
self.id_widget.set_text(str(p_number))
def update_progress(self):
color = to_urwid_color(progress_color(self.todo)) if config().colors() else PaletteItem.DEFAULT
self.progress_bar.set_attr_map(
{None: urwid.AttrSpec(PaletteItem.DEFAULT, color, 256)}
)
def mark(self):
attr_map = {
None: PaletteItem.MARKED,
......@@ -136,3 +151,49 @@ class TodoWidget(urwid.WidgetWrap):
def unmark(self):
self.widget.set_attr_map(_markup(self.todo, False))
cache = {}
@classmethod
def create(p_class, p_todo):
"""
Creates a TodoWidget instance for the given todo. Widgets are
cached, the same object is returned for the same todo item.
"""
def parent_progress_may_have_changed(p_todo):
"""
Returns True when a todo's progress should be updated because it is
dependent on the parent's progress.
"""
return p_todo.has_tag('p') and not p_todo.has_tag('due')
source = p_todo.source()
if source in p_class.cache:
widget = p_class.cache[source]
if parent_progress_may_have_changed(p_todo):
widget.update_progress()
else:
widget = p_class(p_todo)
p_class.cache[source] = widget
return widget
cache_clean_counter = 0
@classmethod
def clean_cache(p_class, p_todolist):
""" Cleans the widget cache once every 500 column updates. """
if p_class.cache_clean_counter % 500 != 0:
# wait
p_class.cache_clean_counter += 1
return
sources_in_list = set([todo.source() for todo in p_todolist])
sources_in_cache = set(p_class.cache.keys())
for stale in sources_in_cache - sources_in_list:
del p_class.cache[stale]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment