Deleting all childs of a node

79 views
Skip to first unread message

Fidel Pérez

unread,
May 12, 2013, 1:52:39 PM5/12/13
to leo-e...@googlegroups.com
Hi:
I have been reading on the documentation (both Leo and group) about deleting childs in a node, and actually testing it myself and I cant manage it to work since when a node is deleted the reference to it changes, so the loop will go crazy (on one of the tests it acutally deleted all the nodes in my leo file lol)
Did someone solve this problem already? Which would be the code to delete the following nodes?

Code I tried:

for idx, n in enumerate(MasterNode.subtree()):
    c.deleteOutline(n)

As previously said, I'm adding all those to an easy access list (which hopefully will be a part of the quickstart guide) so new users will directly know how to apply those.
Thanks!

Fidel Pérez

unread,
May 12, 2013, 3:45:50 PM5/12/13
to leo-e...@googlegroups.com
I just realized I will also need to go on with loops through the tree before and after some of the nodes deletion (I am preparing the script to transform an imported html mindmap into Leo).
Could someone please point me towards any script/code which already loops through items on a tree even after deleting some of them or adding new ones?

Terry Brown

unread,
May 12, 2013, 10:01:51 PM5/12/13
to leo-e...@googlegroups.com
On Sun, 12 May 2013 12:45:50 -0700 (PDT)
Fidel Pérez <fidel...@gmail.com> wrote:

> I just realized I will also need to go on with loops through the tree
> before and after some of the nodes deletion (I am preparing the script to
> transform an imported html mindmap into Leo).
> Could someone please point me towards any script/code which already loops
> through items on a tree even after deleting some of them or adding new ones?

Typically you try to avoid iterating over changing trees. For example
you sometimes collect a list of all the things that need deleting, and
then just iterate through that list, deleting things, after you've
iterated the tree. The exact form of the solution depends a bit on
what you're trying to do.

I imported stuff from FreeMind, but so long ago I can't find any of the
pieces. I did just find
.../contrib/Projects/jyleo/plugins/freemind
.../contrib/Projects/jyleo/plugins/FreeLeoMind.java

but those are in the old experimental jython Leo, it seems. Possibly
not importers but interface hacks or something.

Cheers -Terry

> On Sunday, May 12, 2013 7:52:39 PM UTC+2, Fidel Pérez wrote:
> >
> > Hi:
> > I have been reading on the documentation (both Leo and group) about
> > deleting childs in a node, and actually testing it myself and I cant manage
> > it to work since when a node is deleted the reference to it changes, so the
> > loop will go crazy (on one of the tests it acutally deleted all the nodes
> > in my leo file lol)
> > Did someone solve this problem already? Which would be the code to delete
> > the following nodes?
> >
> > *Code I tried:*
> > *
> > *

Terry Brown

unread,
May 12, 2013, 2:18:16 PM5/12/13
to leo-e...@googlegroups.com
On Sun, 12 May 2013 10:52:39 -0700 (PDT)
Fidel Pérez <fidel...@gmail.com> wrote:

> Hi:
> I have been reading on the documentation (both Leo and group) about
> deleting childs in a node, and actually testing it myself and I cant manage

I know deleting nodes was discussed at length when

.../leo/plugins/leoPluginsRef.leo#Plugins--> Qt only plugins-->@file
contextmenu.py-->deletenodes_rclick

was written. I thought the discussion resulted in a core method to
deal with this, but perhaps not. deletenodes_rclick() works even when
multiple nodes are selected, which is an unusual case.

You're probably running in to trouble because your code deletes the
first child of Masternode before moving on to that *childs* children,
which are already gone - deleteOutline is recursive itself.

What happens if you just use MasterNode.children()

?

Cheers -Terry

> it to work since when a node is deleted the reference to it changes, so the
> loop will go crazy (on one of the tests it acutally deleted all the nodes
> in my leo file lol)
> Did someone solve this problem already? Which would be the code to delete
> the following nodes?
>
> *Code I tried:*
> *
> *
>

Fidel Pérez

unread,
May 13, 2013, 2:52:20 AM5/13/13
to leo-e...@googlegroups.com
Thank you very much for your answers. Yes, I am importing from freemind but enjoying the experience as a way to learn Leo's working style so I would like to still make the full script myself.
The problem is importing through html generates a lot of nodes called "li" and other things, which I want to remove and leave only with the information I want (just node names and bodies)
So I have to both loop through the nodes and keep deleting some of them, renamig some of them, sometimes adding some of them probably.
Marking them and deleting all of them together would be a solution, but I still need to create nodes with iterations, which remains unsolved through marking nodes.
I will actually look at the way html files are imported since they create a lot of nested nodes and I guess some of them are created after the structure exists so hopefully there are recursive loops in there.

I already tried (in several ways) to go to the "childest" child and delete it, then go up, delete, etc, but after the first "childest child" deletion, when trying to delete its brothers with the iteration, Leo wont work as I would expect. (We can discard the problem you suggested)

Cheers,
Fidel.

Fidel Pérez

unread,
May 13, 2013, 2:59:38 AM5/13/13
to leo-e...@googlegroups.com
Well thinking further, the only think I would *really* need is to find a way to keep a reference to nodes which lasts through changes in the trees, and if not possible for all of the nodes, at least for two of them, the node which begins the iteration, and the child currently in the loop.


On Sunday, May 12, 2013 7:52:39 PM UTC+2, Fidel Pérez wrote:

Edward K. Ream

unread,
May 13, 2013, 9:16:02 AM5/13/13
to leo-editor
On Mon, May 13, 2013 at 1:59 AM, Fidel Pérez <fidel...@gmail.com> wrote:
Well thinking further, the only think I would *really* need is to find a way to keep a reference to nodes which lasts through changes in the trees, and if not possible for all of the nodes, at least for two of them, the node which begins the iteration, and the child currently in the loop.

The difficulty is that references to positions break when the tree changes.  References to vnodes are stable, but usually considerably harder to use.  It's not an easy problem.  

Have you tried p.deletePositionsInList?  This method is supposed to handle the complications properly.  Create the entire list of positions first.

Edward

Terry Brown

unread,
May 13, 2013, 9:48:04 AM5/13/13
to leo-e...@googlegroups.com
On Sun, 12 May 2013 23:52:20 -0700 (PDT)
Fidel Pérez <fidel...@gmail.com> wrote:

> I already tried (in several ways) to go to the "childest" child and delete
> it, then go up, delete, etc, but after the first "childest child" deletion,
> when trying to delete its brothers with the iteration, Leo wont work as I
> would expect. (We can discard the problem you suggested)

It's worth understanding that problem though. So if A has three
children

A
B1
B2
B3

Then the position class has an address for each node, B1 is the first
child of A, B2 the second, etc. So even though B1 is a "childest"
node (aka leaf node :-) deleting it still interferes with the position
class's address for B2, B2 becomes the first rather than the second.
So you'd need to delete the leaf nodes from last to first, something
you see quite often in list deletion in Python.

I can't remember how I imported FreeMind way back when... hah, just
found it, I used XSLT, the XML to XML translator.

If I was doing it today, I'd use the Python lxml XML parsing library to
iterate over the FreeMind XML file, and build the Leo tree from that
iteration.

It sounds like you've exported from FreeMind to HTML then imported the
HTML into Leo and are then working on tidying up the result. That is a
reasonable approach, but you might find parsing the FreeMind XML
directly was cleaner and gave more of the original data. If you think
you'll ever want to deal with XML in Python the lxml library is worth
knowing.

Cheers -Terry

Fidel Pérez

unread,
May 13, 2013, 11:45:59 AM5/13/13
to leo-e...@googlegroups.com
Thank you both for the answers and clarifications (Terry). Everything was very useful.

Edward: Yes, im trying the following code without success:

thelist=p.subtree()
for p in thelist:
    g.es(p)
p.deletePositionsInList(thelist)
c.redraw()

Also tried several variations but cant get it to work (the list on the log will be correct and be the list in the nodes, but the command deletepositionsinlist just wont do anything). My only guess is that it needs just the position not the full node description, so I am reading on which command isolates that.
Any further suggestions from there?
Thanks.

Fidel Pérez

unread,
May 13, 2013, 11:51:21 AM5/13/13
to leo-e...@googlegroups.com
As per learning xml parser in python, I appreciate the suggestion and will do that, but right now Im just too bussy just learning Leo :)
Im doing this also as an exercice to learn Leo functionalities and familiarize myself with it, and on the way there im also making a guide I would like to have had when noob =)
Basically it will allow new users to create programs and functions just by dropping branches of a "programming tree" into their structure. Cant wait for you guys to check that out, I think you will like that. One month of work more or less =)

On Monday, May 13, 2013 3:48:04 PM UTC+2, Terry wrote:

Fidel Pérez

unread,
May 14, 2013, 8:00:41 AM5/14/13
to leo-e...@googlegroups.com
I got something which does the trick:

for x in range (0,totalnumberofnodes+1):
    c.executeMinibufferCommand('goto-next-node')
    c.executeMinibufferCommand('delete-node')




On Sunday, May 12, 2013 7:52:39 PM UTC+2, Fidel Pérez wrote:

Edward K. Ream

unread,
May 14, 2013, 9:41:56 AM5/14/13
to leo-editor
On Mon, May 13, 2013 at 10:45 AM, Fidel Pérez <fidel...@gmail.com> wrote:
Thank you both for the answers and clarifications (Terry). Everything was very useful.

Edward: Yes, im trying the following code without success:

thelist=p.subtree()
for p in thelist:
    g.es(p)
p.deletePositionsInList(thelist)
c.redraw()

This won't work.  At minimum, you would have to have:

    thelist = [z.copy() for z in p.subtree()]

This makes a copy of each position returned by the p.subtree iterator.  This ensures that the positions don't move as the iterator progresses.

Edward

Edward K. Ream

unread,
May 14, 2013, 9:44:17 AM5/14/13
to leo-editor
On Tue, May 14, 2013 at 7:00 AM, Fidel Pérez <fidel...@gmail.com> wrote:
I got something which does the trick:

for x in range (0,totalnumberofnodes+1):
    c.executeMinibufferCommand('goto-next-node')
    c.executeMinibufferCommand('delete-node')

I'm glad this works for you.  I'd like to point out, however, that it's not a general solution to the problem you posed.  What we want is something that works for any list of (copies of) positions, randomly distributed throughout a tree.  I have doubts that p.deletePositionsInList(aList) works ;-)  I'll investigate...

Edward

Fidel Pérez

unread,
May 14, 2013, 12:19:44 PM5/14/13
to leo-e...@googlegroups.com
What references do we have as constants now?
If a parent has childs that change, but he wont, can we reference back to him during an iteration through its child nodes?
In that case we could just use that function to store the relative position from the nodes-wanted-to-be-deleted to that non-changing-parent and then "go down-erase" as I did..

I currently need to be able to access/refer that non-changing-parent for an iteration I need to perform (IE Restart the iteration after changes have been made to its childs)...so, does that function exist?

Thanks!

Edward K. Ream

unread,
May 15, 2013, 12:25:48 PM5/15/13
to leo-editor
On Tue, May 14, 2013 at 11:19 AM, Fidel Pérez <fidel...@gmail.com> wrote:
What references do we have as constants now?

Good question.  At present, I don't know and don't remember.  I'm going to study all off this asap.  Maybe later today.

Edward

Fidel Pérez

unread,
May 16, 2013, 3:36:54 AM5/16/13
to leo-e...@googlegroups.com
In my opinion (Leo-Ignorant one) it should be easy to just refer all the nodes to their absolute position on the tree.
So several functions can be made to copy paste and move several nodes according to their absolute position, counting all the nodes within the tree.

For instance:

A (1)
    B (2)
    C (3)
       D (4)
E (5)
   F (6)
      G (7)
   H (8)
      I (9)

Given that we can dynamically get the number of parents any node has (therefore, its indent), then we have all the references we need to do those processes. I know it will require maybe too much iterations, and it wont be as efficient as it probably is now, but it will allow any iteration or moving through nodes...

And for loops through nodes it will be more intuitive... If im looping thourgh node E and childs, and I delete G, I will both intuitively and easily know that refference 7 is now H, or if I insert a child into G, I will also easily know that H is my refference 9 (new child being 8), and I will be able to go on with any loop without having to restart it, because my Loop initiator will allways be E.

Edward K. Ream

unread,
May 17, 2013, 8:12:02 AM5/17/13
to leo-editor
On Thu, May 16, 2013 at 2:36 AM, Fidel Pérez <fidel...@gmail.com> wrote:
In my opinion (Leo-Ignorant one) it should be easy to just refer all the nodes to their absolute position on the tree.

That would be possible only if clones did not exist.  The present (and final) scheme is the third or fourth major revision to how Leo represents clones.  Every node (vnode) exists only once, no matter how many clones there are.  Positions are the glue that makes it possible to show nodes multiple (arbitrarily many) times in the expansion of an outline.

I'll discuss the issues involved in deleting nodes in another post in this thread.

Edward

Fidel Pérez

unread,
May 17, 2013, 8:55:26 AM5/17/13
to leo-e...@googlegroups.com
Im sorry but I still don't understand it:

Say we have a tree of nodes.
For being able to represent that list of nodes into a tree we must have first assigned a position for each node into the tree, therefore, that position is a piece of information we have out there.

Given we have the information on how to put each node (also cloned ones) one after another, the deleting process would consist only on reading that information backwards, then deleting through that reference.

For being able to do so, we might have to create a temporary list which assigns positions backwards to VNode references, just when we are generating the tree out of the vnode / tree information, and then operate with them.

Then for instance a delete operation would be to remove that position from the VNode positions lists, if it happens to be a clone (it has several positions) it will be erased one of its positions, if its a single node, it will be deleted too.

A move operation (or move several) would mean to change the position of the selected vnodes and the ones in-between them, and then regenerating the tree on the new information.

Which is the part I am assuming wrong?

Edward K. Ream

unread,
May 17, 2013, 10:01:49 AM5/17/13
to leo-editor
On Wed, May 15, 2013 at 11:25 AM, Edward K. Ream <edre...@gmail.com> wrote:
On Tue, May 14, 2013 at 11:19 AM, Fidel Pérez <fidel...@gmail.com> wrote:
What references do we have as constants now?

Good question.  At present, I don't know and don't remember.  I'm going to study all off this asap.

p.deletePositionsInList may well have problems.  The only way to know for sure is to develop a suite of unit tests.  They don't exist at present.

Leo's docs don't say enough about positions and the position class.  This is unfortunate, both for typical users and potential maintainers.  I'll start work on a full and *accurate* description of positions next.  This will require some explanatory scripts to be sure I have the details correct myself.

In short, some programming work is needed, both for testing and explanation.  I'll do that next.

Edward

Fidel Pérez

unread,
May 17, 2013, 10:03:45 AM5/17/13
to leo-e...@googlegroups.com
Ok, thanks for that Edward, will be looking forward your conclusions.

Edward K. Ream

unread,
May 17, 2013, 10:11:03 AM5/17/13
to leo-editor
On Fri, May 17, 2013 at 7:55 AM, Fidel Pérez <fidel...@gmail.com> wrote:
Im sorry but I still don't understand it:

As I just wrote, that's perfectly understandable.  There are big holes in the documentation that I'm going to fix next.

Say we have a tree of nodes.
For being able to represent that list of nodes into a tree we must have first assigned a position for each node into the tree, therefore, that position is a piece of information we have out there.

A position isn't something we have "out there".  Positions are typically created for a specific, limited purposes.  Positions *may* (or may not) become invalid when the structure of the outline changes.  The details are too complicated to discuss here.  After I explain positions more fully, you should be able to work out the details yourself.

Given we have the information on how to put each node (also cloned ones) one after another, the deleting process would consist only on reading that information backwards, then deleting through that reference.

Actually, deleting nodes in reverse order is a reasonable idea.  It *may* help to ensure that positions remain valid as nodes are deleted.  But because of clones it's not clear that even this suffices.

Which is the part I am assuming wrong?

I'm not sure that you are missing anything, but this situation is so complex that only careful testing will ensure that supposed "proofs" of the validity of  p.deletePositionsInList are correct.

Let's hold off on further discussion just now while I create scripts that will clarify matters.

Edward

Edward K. Ream

unread,
May 17, 2013, 12:39:37 PM5/17/13
to leo-editor
On Fri, May 17, 2013 at 9:11 AM, Edward K. Ream <edre...@gmail.com> wrote:

> Let's hold off on further discussion just now while I create scripts that will clarify matters.

Consider the following outline, dumped using export-headlines:

+ root
    + a
        - b
        - c
        - b
    + a
        - b
        - c
        - b

**Important**: all nodes with the same names are clones of each other.

Here is a script that will traverse this outline, dumping each position as it goes::

    root = g.findNodeAnywhere(c,'root')
    line = 0
    for p in root.self_and_subtree():
        line += 1
        print('%s: %4s @ %d p._childIndex: %d p.stack: %s' % (
            line,
            p.h,
            id(p.v),
            p._childIndex,
            '[%s]' % ', '.join(['(v:%s @ %s, %s)' % (v.h,id(v),i) for v,i in p.stack]),
        ))

The output of this script is as follows, with newlines added for clarity so as to put the stack on a different line::

1: root @ 173840816 p._childIndex: 0 p.stack: []
2:    a @ 173840880 p._childIndex: 0 p.stack: [
    (v:root @ 173840816, 0)]
3:    b @ 172686704 p._childIndex: 0 p.stack: [
    (v:root @ 173840816, 0), (v:a @ 173840880, 0)]
4:    c @ 172702288 p._childIndex: 1 p.stack: [
    (v:root @ 173840816, 0), (v:a @ 173840880, 0)]
5:    b @ 172686704 p._childIndex: 2 p.stack: [
    (v:root @ 173840816, 0), (v:a @ 173840880, 0)]
6:    a @ 173840880 p._childIndex: 1 p.stack: [
    (v:root @ 173840816, 0)]
7:    b @ 172686704 p._childIndex: 0 p.stack: [
    (v:root @ 173840816, 0), (v:a @ 173840880, 1)]
8:    c @ 172702288 p._childIndex: 1 p.stack: [
    (v:root @ 173840816, 0), (v:a @ 173840880, 1)]
9:    b @ 172686704 p._childIndex: 2 p.stack: [
    (v:root @ 173840816, 0), (v:a @ 173840880, 1)]

This is worth careful study, especially for implementers.  The highlights::

A.  There is only one copy of each vnode, as shown by the vnode addresses that follow the @ signs.

B. The same *vnode* may appear in two different childIndex places in a parent node.  For example, the p._childIndex field for node b is 0 in line 7, and 2 in line 8.

C. *Positions* may visit the same *vnode* arbitrarily many times.  In this example, vnode b is visited 4 times, in lines 3, 5, 7 and 9.  Note that all these positions are *different*, because of different child indices in the stack, or different p._childIndex fields.

Please feel free to ask questions about the output.

That's all for now.  I'll say more later when I have time.  This is all preliminary work for the real task of testing algorithms that will delete nodes.

Edward

P.S.  As was researching this topic, I noticed two different ways of archiving positions, p.key and p.archivedPosition.  The differences between these not concern us now.  The point is that *any* form of archived position will become invalid when the outline changes.

EKR

SegundoBob

unread,
May 17, 2013, 3:42:29 PM5/17/13
to leo-editor
This is the scheme I use:

def delAllChildren(posx):
while True:
pos2 = posx.getLastChild()
if pos2:
pos2.doDelete()
else:
break

https://github.com/SegundoBob/leo_misc/blob/master/delAllChildren.leo
is a trivial .leo file that can be used to play with this function.

Edward K. Ream

unread,
May 20, 2013, 3:33:31 PM5/20/13
to leo-editor
On Fri, May 17, 2013 at 2:42 PM, SegundoBob <bhos...@ieee.org> wrote:
This is the scheme I use:

def delAllChildren(posx):
    while True:
        pos2 = posx.getLastChild()
        if pos2:
            pos2.doDelete()
        else:
            break

This looks like it should work well, because it deletes children in reverse order, which is a standard trick when working with positions.

Again, however, I should point out that p.deletePositionsInList is attempting to solve a more genera;, and much harder, problem.

Clearly, this is a problem of interest to many people.  Solving it, and *proving* that it is solved, is high on my list of to-do items.

Edward

Fidel Pérez

unread,
Jun 16, 2013, 4:11:23 AM6/16/13
to leo-e...@googlegroups.com
I think I found a good solution. I was trying to use deleteallitemsinlist but could not get it to work (it would erase only the first node on the list, well i just couldnot).
So now Im using this one which works perfectly:

First make a list (Nodetodeletelist) with the list of nodes gnx's, then

FoundOne=1

Keeptesting=1

while FoundOne==1:

    Keeptesting=0

    for ChildsToErase in c.p.subtree():

        if ChildsToErase.gnx in NodesToDeleteList:

        ChildsToErase.doDelete()

        Keeptesting=1

    if Keeptesting==0:

        FoundOne=0

Reply all
Reply to author
Forward
0 new messages