c0fec0de / anytree

Python tree data library
Apache License 2.0
937 stars 130 forks source link

A way to create iterating labels when rendering tree #186

Open Vathys opened 2 years ago

Vathys commented 2 years ago

Is there a way to be able to create iterate inside a RenderTree? For example, I want to print a tree labeled 1, 2, 3,... before each child. I want to do this to be able to print a selectable menu commandline menu, but not necessarily changing the names of each child.

eckp commented 2 years ago

With RenderTree, you can use the .by_attr() method to customise the representation of each node. To generate a numbering for the children, you could create a function that looks up the current node's index in its parent's tuple of children:

from anytree import Node, RenderTree

root = Node('root')
ch1 = Node('first child', parent=root)
ch2 = Node('second child', parent=root)
ch3 = Node('third child', parent=root)
ch2ch1 = Node('first child of second child', parent=ch2)
ch2ch2 = Node('second child of second child', parent=ch2)
ch2ch3 = Node('third child of second child', parent=ch2)

get_index = lambda n: n.parent.children.index(n)
print(RenderTree(root).by_attr(lambda n: f'{get_index(n)+1}. {n.name}'  if not n.is_root else n.name))

prints

root
├── 1. first child
├── 2. second child
│   ├── 1. first child of second child
│   ├── 2. second child of second child
│   └── 3. third child of second child
└── 3. third child

However if you want unique increasing numbers for each option to select from, regardless of the level, consider using a generator: (courtesy of https://stackoverflow.com/q/9604516)

import itertools

c=itertools.count()
print(RenderTree(root).by_attr(lambda n: f'{next(c)+1}. {n.name}'  if not n.is_root else n.name))

prints

root
├── 1. first child
├── 2. second child
│   ├── 3. first child of second child
│   ├── 4. second child of second child
│   └── 5. third child of second child
└── 6. third child
eckp commented 9 months ago

@c0fec0de question answered, issue can be closed.