4. In the first draft of this post, I said that detecting simple patterns on the line-oriented diffs will tell the redraw code what Leo did. But, as I write this I see (huge aha) that the gui redraw code doesn't care what Leo did! It only cares about minimizing the number of tree widgets that have to be inserted, deleted or moved. But that's precisely what diff tells us!
This was the approach I took to made several demos while we were discussing the new data model for Leo.
This idea worked perfectly but not with the current Leo data model.
The main challenge is to have the list you have described above (list of pairs (level, gnx)), always in sync with the Leo's vnode graph without recreating the list every time. Recreating the list takes almost the same time as redrawing the whole tree from scratch.
app.make_redraw_dict: 5 direct children 0.006 sec.children = [
self.make_dict_for_position(child)
for child in p.children()
if level == 0 or child.isVisible(c)
]if p.level() == 0 or p.v.isExpanded():
children = [
self.make_dict_for_position(child)
for child in p.children()
]
else:
children = []app.make_redraw_dict: 5 direct children 0.0004 sec.The only way to really exploit this idea of small redraws is to force Leo to keep this list always in sync. In other words, this list becomes part of Leo data model.
Happily, this can be improved, because it is already known that p is visible!if p.level() == 0 or p.v.isExpanded():
children = [
self.make_dict_for_position(child)
for child in p.children()
]
else:
children = []
if p.v.isExpanded():
children = [
self.make_dict_for_position(child)
for child in p.children()
]
else:
children = []So a snapshot might be a simple linear list of all visible positions: like this:level:gnxlevel:gnx...
I don't know what is redraw_dict and where did it come from?
In this thread you described a list of pairs (level, gnx).
I know that computing this list on a large outline like leoPyRef.leo takes much more time than 4ms.
If you skip invisible members of the outline when re-creating this list, you end up with the algorithm Leo currently uses. I don's see the "A-HA" here.
That algorithm have the same issues when dealing with big fully expanded outlines.
OTOH, having this list ready enables much more efficient redraws (what I understood was this thread's 'A-HA' all about).
app.make_redraw_dict: 5 direct children 0.00011 secThe Aha is that the redraw code doesn't have to redraw all the visible nodes.
...the PY side will pass a redraw instruction list to the JS side. By definition, this instruction list specifies the minimum number of gui operations needed to update the screen.
Important: it's not clear that the flx.TreeWidget or flx.TreeItem classes can actually take advantage of [the redraw instruction list].
> This may be true now, but perhaps Almar will be open to adding new
> methods to the TreeWidget or TreeItem classes. Assuming that's
> possible :-) And assuming a real performance gain would result.
You can just subclass them?
I'm going on about [the speed of of app.make_redraw_dict] because this is a crucial figure of merit, which depends on the number of visible nodes.
ap = self.p_to_ap(p)
if 1:
# For development of make_redraw_instruction_list
#
# 1. Measure time for a full redraw.
for p2 in c.all_positions(copy=False):
p2.expand()
t1 = time.clock()
d = self.make_redraw_dict(p)
redraw_instruction_list = self.make_redraw_instruction_list(d)
# Does nothing, at present
t2 = time.clock()
#
# 2. Do a normal redraw, so the flexx tree doesn't choke.
for p2 in c.all_positions(copy=False):
p2.contract()
c.expandAllAncestors(p)
# Does not do a redraw.
else:
w.tree.redraw_or_repopulate(d)
w.tree.select_ap(ap)
d = self.make_redraw_dict(p)
w.tree.redraw_with_dict(d)app.make_redraw_dict: 5 direct children 0.057 sec.
app.make_redraw_dict: 5 direct children 0.000 sec.app.make_redraw_instruction_list should be roughly as fast as app.make_redraw_dict. It's next.
def flatten_outline (self):
'''Return a flat list of strings "level:gnx"
for all *visible* positions.'''
c, aList = self.c, []
for p in c.rootPosition().self_and_siblings():
self.extend_flattened_outline(aList, p)
return aList
def extend_flattened_outline(self, aList, p):
aList.append('%s:%s' % (p.level(), p.gnx))
if p.isExpanded():
for child in p.children():
self.extend_redraw_list(aList, child)app.make_redraw_dict: 5 direct children 0.058 sec.
app.flatten_outline: 2813 entries 0.016 sec.app.make_redraw_dict: 5 direct children 0.058 sec.
app.flatten_outline: 2813 entries 0.016 sec.
On to diffing!