> > > Imo, #1068 <https://github.com/leo-editor/leo-editor/issues/1068> is > likely the best way to do this. >
I would not be so sure about #1068 being the best way. But it certainly deserves further exploration. The idea of finding the difference and applying only small set of operations to transform the tree is very much like the way most popular front end javascripts frameworks (like React and Vue) deal with the dom updates. So, if they can use this for speed, then it might be the way for Leo too. However, there might be a better way. The proposed idea (in #1068), assumes Leo is using QTreeWidget for displaying the outline. Now, both Leo and QTreeWidget keep their data representation of the outline on their own. And if something changes in the Leo, problem arises with the keeping QTreeWidget's data in sync. If OTOH, we use QGraphicsScene for displaying the outline, it would contain only a handful of QGraphicItems (as many as there are visible nodes on the screen, i.e. at most 50). QTreeWidget in the worse case contains all nodes. Calculating diffs can be expensive too if the number of nodes is large. But if the lists are never longer than 50 items, it will be fast enough. The script I wrote above shows that it is possible to pass through the large outline (more than 8000 nodes) in less than 10ms. Adding a filter to skip hidden nodes may add some time to this but I don't expect more than 5ms. ... a few hours later I have just looked in the p.isExpanded and c.shouldBeExpanded methods to see how it is achieved separate expanded state for clones. There is a better way. Currently each v instance keeps track of its expanded positions in the v.expandedPositions. As we know, positions can become invalid and lots of these lists become also invalidated. There is a better way. Let's imagine a list of gnxes in the outline order. To distinguish between clones, it would be enough to take in account the number of the previous occurrences of same gnx in the outline list. When we encounter gnx for the first time its number is 1. Every next occurrence of this same gnx will have this number increased by 1. Then we can keep a set of pairs (gnx, occurrence_number) in c.expandedNodes. So, to check if some cloned vnode is expanded or not, it would be enough to check if c.expandedPositions contains (gnx, occ_num) for that particular occurrence. Here is the function that would generate list of nodes that should be displayed: import timeit from collections import Counter def get_exp(): cnt = Counter() exp = set() exp.add((1, c.hiddenRootNode.fileIndex)) # root node is always expanded for p in c.all_positions_iter(): gnx = p.v.fileIndex cnt[gnx] += 1 if p.isExpanded(): exp.add((cnt[gnx], gnx)) return exp expanded = get_exp() # lets cache expanded state def f(): cnt = Counter() def it(root, lev): gnx = root.fileIndex cnt[gnx] += 1 yield root, lev if (cnt[gnx], gnx): # or True (to fully expand outline) for v in root.children: yield from it(v, lev+1) return list(it(c.hiddenRootNode, 0)) def report(fun, n=100): t = timeit.timeit(fun, number=n)/n * 1000 g.es('function %s took %.1fms'%(fun.__name__, t)) report(f, 10) # gives 0.3ms for LeoPyRef (not fully expanded) For the same outline, FastRedraw.flatten_outline(c) gives 1.5ms. When fully expanded function f takes 17.7ms, and FastRedraw.flatten_outline(c) takes 88.4ms. I think that allowing clone instances to have separate expanded state is not much of a feature. Most likely the need for such a feature is caused by the drawing code being too slow. If the drawing code was fast enough, I doubt that this feature would ever existed. So, I would vote to drop this feature and have all clones expand/collapse simultaneousely. That would enable even faster redraws. With the list of nodes to display generated in less than 18ms, adjusting all of the (<50) visible QGraphicsItems in the QGraphicsScene should not take much more time. So, complete redraw might take less than 50ms in the worse case scenario. and about 20ms for the normal cases. I would welcome your help with this. I promise not to touch anything you > do until you declare your work finished. > I really would like to help, but I believe the best way to help is to just share my ideas and let you do the coding. If you need some clarification or more detailed explanation of my ideas, ask away and I would be glad to answer ASAP. Vitalije PS: I have recently writen in cython a module for calculating diffs and encoding them using fossil source. The author of sqlite and fossil D. Richard Hipp wrote in C this few functions in a way that they can be used as a standalone functions. It was really easy to make a Python extension module of this C code using cython. The main reason for me doing this was to enable Leo to keep track of changes in the outline. I wrote a plugin a while ago using SequenceMatcher but I have never made any real usage of it. On one of the currently open issues there is a label `waiting`. I was surprised to find out that it is waiting for me to publish this plugin and I totally have forgotten about it. So, while looking again on this plugin I tried to use fossil super fast and super efficient code that calculates diffs very fast and encodes them in very compressed (but ascii friendly) form. This has a potential to make unlimited undo history very efficiently, very fast and very reliable. -- You received this message because you are subscribed to the Google Groups "leo-editor" group. To unsubscribe from this group and stop receiving emails from it, send an email to leo-editor+unsubscr...@googlegroups.com. To post to this group, send email to leo-editor@googlegroups.com. Visit this group at https://groups.google.com/group/leo-editor. For more options, visit https://groups.google.com/d/optout.