Examples

Minimal example

Simplest example rendering:

[-] item 1
        sub item 1
        sub item 2
    item 2
 1#!/usr/bin/python
 2# Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
 3# This file is released under the GNU GPL, version 3 or a later revision.
 4
 5import urwid
 6import urwidtrees
 7
 8
 9tree_widget = urwidtrees.widgets.TreeBox(
10    urwidtrees.decoration.CollapsibleIndentedTree(
11        urwidtrees.tree.SimpleTree([
12            (urwid.SelectableIcon('item 1'), (
13                (urwid.SelectableIcon('sub item 1'), None),
14                (urwid.SelectableIcon('sub item 2'), None),
15            )),
16            (urwid.SelectableIcon('item 2'), None),
17        ])
18    )
19)
20
21urwid.MainLoop(tree_widget).run()

Basic use

 1#!/usr/bin/python
 2# Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
 3# This file is released under the GNU GPL, version 3 or a later revision.
 4
 5import urwid
 6from urwidtrees.tree import SimpleTree
 7from urwidtrees.widgets import TreeBox
 8
 9
10# define some colours
11palette = [
12    ('body', 'black', 'light gray'),
13    ('focus', 'light gray', 'dark blue', 'standout'),
14    ('bars', 'dark blue', 'light gray', ''),
15    ('arrowtip', 'light blue', 'light gray', ''),
16    ('connectors', 'light red', 'light gray', ''),
17]
18
19# We use selectable Text widgets for our example..
20
21
22class FocusableText(urwid.WidgetWrap):
23    """Selectable Text used for nodes in our example"""
24    def __init__(self, txt):
25        t = urwid.Text(txt)
26        w = urwid.AttrMap(t, 'body', 'focus')
27        urwid.WidgetWrap.__init__(self, w)
28
29    def selectable(self):
30        return True
31
32    def keypress(self, size, key):
33        return key
34
35# define a test tree in the format accepted by SimpleTree. Essentially, a
36# tree is given as (nodewidget, [list, of, subtrees]). SimpleTree accepts
37# lists of such trees.
38
39
40def construct_example_simpletree_structure(selectable_nodes=True, children=3):
41
42    Text = FocusableText if selectable_nodes else urwid.Text
43
44    # define root node
45    tree = (Text('ROOT'), [])
46
47    # define some children
48    c = g = gg = 0  # counter
49    for i in range(children):
50        subtree = (Text('Child {0:d}'.format(c)), [])
51        # and grandchildren..
52        for j in range(children):
53            subsubtree = (Text('Grandchild {0:d}'.format(g)), [])
54            for k in range(children):
55                leaf = (Text('Grand Grandchild {0:d}'.format(gg)), None)
56                subsubtree[1].append(leaf)
57                gg += 1  # inc grand-grandchild counter
58            subtree[1].append(subsubtree)
59            g += 1  # inc grandchild counter
60        tree[1].append(subtree)
61        c += 1
62    return tree
63
64
65def construct_example_tree(selectable_nodes=True, children=2):
66    # define a list of tree structures to be passed on to SimpleTree
67    forrest = [construct_example_simpletree_structure(selectable_nodes,
68                                                      children)]
69
70    # stick out test tree into a SimpleTree and return
71    return SimpleTree(forrest)
72
73def unhandled_input(k):
74    #exit on q
75    if k in ['q', 'Q']: raise urwid.ExitMainLoop()
76
77if __name__ == "__main__":
78    # get example tree
79    stree = construct_example_tree()
80
81    # put the tree into a treebox
82    treebox = TreeBox(stree)
83
84    # add some decoration
85    rootwidget = urwid.AttrMap(treebox, 'body')
86    #add a text footer
87    footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
88    #enclose all in a frame
89    urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run()  # go

Decoration

 1#!/usr/bin/python
 2# Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
 3# This file is released under the GNU GPL, version 3 or a later revision.
 4
 5from example1 import construct_example_tree, palette, unhandled_input # example data
 6from urwidtrees.decoration import ArrowTree  # for Decoration
 7from urwidtrees.widgets import TreeBox
 8import urwid
 9
10if __name__ == "__main__":
11    # get example tree
12    stree = construct_example_tree()
13    # Here, we add some decoration by wrapping the tree using ArrowTree.
14    atree = ArrowTree(stree,
15                      # customize at will..
16                      # arrow_hbar_char=u'\u2550',
17                      # arrow_vbar_char=u'\u2551',
18                      # arrow_tip_char=u'\u25B7',
19                      # arrow_connector_tchar=u'\u2560',
20                      # arrow_connector_lchar=u'\u255A',
21                      )
22
23    # put the into a treebox
24    treebox = TreeBox(atree)
25    rootwidget = urwid.AttrMap(treebox, 'body')
26    #add a text footer
27    footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
28    #enclose in a frame
29    urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run()  # go

Collapsible subtrees

 1#!/usr/bin/python
 2# Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
 3# This file is released under the GNU GPL, version 3 or a later revision.
 4
 5from example1 import construct_example_tree, palette, unhandled_input # example data
 6from urwidtrees.decoration import CollapsibleIndentedTree  # for Decoration
 7from urwidtrees.widgets import TreeBox
 8import urwid
 9
10if __name__ == "__main__":
11    # get some SimpleTree
12    stree = construct_example_tree()
13
14    # Use (subclasses of) the wrapper decoration.CollapsibleTree to construct a
15    # tree where collapsible subtrees. Apart from the original tree, these take
16    # a callable `is_collapsed` that defines initial collapsed-status if a
17    # given position.
18
19    # We want all grandchildren collapsed initially
20    if_grandchild = lambda pos: stree.depth(pos) > 1
21
22    # We use CollapsibleIndentedTree around the original example tree.
23    # This uses Indentation to indicate the tree structure and squeezes in
24    # text-icons to indicate the collapsed status.
25    # Also try CollapsibleTree or CollapsibleArrowTree..
26    tree = CollapsibleIndentedTree(stree,
27                                   is_collapsed=if_grandchild,
28                                   icon_focussed_att='focus',
29                                   # indent=6,
30                                   # childbar_offset=1,
31                                   # icon_frame_left_char=None,
32                                   # icon_frame_right_char=None,
33                                   # icon_expanded_char='-',
34                                   # icon_collapsed_char='+',
35                                   )
36
37    # put the tree into a treebox
38    treebox = TreeBox(tree)
39    rootwidget = urwid.AttrMap(treebox, 'body')
40    #add a text footer
41    footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
42    #enclose all in a frame
43    urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run() # go
44

Custom Trees: Walking the filesystem

  1#!/usr/bin/python
  2# Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
  3# This file is released under the GNU GPL, version 3 or a later revision.
  4
  5import urwid
  6import os
  7from example1 import palette, unhandled_input  # example data
  8from urwidtrees.widgets import TreeBox
  9from urwidtrees.tree import Tree
 10from urwidtrees.decoration import CollapsibleArrowTree
 11
 12
 13# define selectable urwid.Text widgets to display paths
 14class FocusableText(urwid.WidgetWrap):
 15    """Widget to display paths lines"""
 16    def __init__(self, txt):
 17        t = urwid.Text(txt)
 18        w = urwid.AttrMap(t, 'body', 'focus')
 19        urwid.WidgetWrap.__init__(self, w)
 20
 21    def selectable(self):
 22        return True
 23
 24    def keypress(self, size, key):
 25        return key
 26
 27# define Tree that can walk your filesystem
 28
 29
 30class DirectoryTree(Tree):
 31    """
 32    A custom Tree representing our filesystem structure.
 33    This implementation is rather inefficient: basically every position-lookup
 34    will call `os.listdir`.. This makes navigation in the tree quite slow.
 35    In real life you'd want to do some caching.
 36
 37    As positions we use absolute path strings.
 38    """
 39    # determine dir separator and form of root node
 40    pathsep = os.path.sep
 41    drive, _ = os.path.splitdrive(pathsep)
 42
 43    # define root node This is part of the Tree API!
 44    root = drive + pathsep
 45
 46    def __getitem__(self, pos):
 47        return FocusableText(pos)
 48
 49    # generic helper
 50    def _list_dir(self, path):
 51        """returns absolute paths for all entries in a directory"""
 52        try:
 53            elements = [
 54                os.path.join(path, x) for x in os.listdir(path)
 55            ] if os.path.isdir(path) else []
 56            elements.sort()
 57        except OSError:
 58            elements = None
 59        return elements
 60
 61    def _get_siblings(self, pos):
 62        """lists the parent directory of pos """
 63        parent = self.parent_position(pos)
 64        siblings = [pos]
 65        if parent is not None:
 66            siblings = self._list_dir(parent)
 67        return siblings
 68
 69    # Tree API
 70    def parent_position(self, pos):
 71        parent = None
 72        if pos != '/':
 73            parent = os.path.split(pos)[0]
 74        return parent
 75
 76    def first_child_position(self, pos):
 77        candidate = None
 78        if os.path.isdir(pos):
 79            children = self._list_dir(pos)
 80            if children:
 81                candidate = children[0]
 82        return candidate
 83
 84    def last_child_position(self, pos):
 85        candidate = None
 86        if os.path.isdir(pos):
 87            children = self._list_dir(pos)
 88            if children:
 89                candidate = children[-1]
 90        return candidate
 91
 92    def next_sibling_position(self, pos):
 93        candidate = None
 94        siblings = self._get_siblings(pos)
 95        myindex = siblings.index(pos)
 96        if myindex + 1 < len(siblings):  # pos is not the last entry
 97            candidate = siblings[myindex + 1]
 98        return candidate
 99
100    def prev_sibling_position(self, pos):
101        candidate = None
102        siblings = self._get_siblings(pos)
103        myindex = siblings.index(pos)
104        if myindex > 0:  # pos is not the first entry
105            candidate = siblings[myindex - 1]
106        return candidate
107
108
109if __name__ == "__main__":
110    cwd = os.getcwd()  # get current working directory
111    dtree = DirectoryTree()  # get a directory walker
112
113    # Use CollapsibleArrowTree for decoration.
114    # define initial collapse:
115    as_deep_as_cwd = lambda pos: dtree.depth(pos) >= dtree.depth(cwd)
116
117    # We hide the usual arrow tip and use a customized collapse-icon.
118    decorated_tree = CollapsibleArrowTree(dtree,
119                                          is_collapsed=as_deep_as_cwd,
120                                          arrow_tip_char=None,
121                                          icon_frame_left_char=None,
122                                          icon_frame_right_char=None,
123                                          icon_collapsed_char=u'\u25B6',
124                                          icon_expanded_char=u'\u25B7',)
125
126    # stick it into a TreeBox and use 'body' color attribute for gaps
127    tb = TreeBox(decorated_tree, focus=cwd)
128    root_widget = urwid.AttrMap(tb, 'body')
129    #add a text footer
130    footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
131    #enclose all in a frame
132    urwid.MainLoop(urwid.Frame(root_widget, footer=footer), palette, unhandled_input = unhandled_input).run() # go

Nesting Trees

 1#!/usr/bin/python
 2# Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
 3# This file is released under the GNU GPL, version 3 or a later revision.
 4
 5from example1 import palette, construct_example_tree  # example data
 6from example1 import FocusableText, unhandled_input  # Selectable Text used for nodes
 7from urwidtrees.widgets import TreeBox
 8from urwidtrees.tree import SimpleTree
 9from urwidtrees.nested import NestedTree
10from urwidtrees.decoration import ArrowTree, CollapsibleArrowTree  # decoration
11import urwid
12import logging
13
14if __name__ == "__main__":
15    #logging.basicConfig(filename='example.log',level=logging.DEBUG)
16    # Take some Arrow decorated Tree that we later stick inside another tree.
17    innertree = ArrowTree(construct_example_tree())
18    # Some collapsible, arrow decorated tree with extra indent
19    anotherinnertree = CollapsibleArrowTree(construct_example_tree(),
20                                            indent=10)
21
22    # A SimpleTree, that contains the two above
23    middletree = SimpleTree(
24        [
25            (FocusableText('Middle ROOT'),
26             [
27                 (FocusableText('Mid Child One'), None),
28                 (FocusableText('Mid Child Two'), None),
29                 (innertree, None),
30                 (FocusableText('Mid Child Three'),
31                  [
32                      (FocusableText('Mid Grandchild One'), None),
33                      (FocusableText('Mid Grandchild Two'), None),
34                  ]
35                  ),
36                 (anotherinnertree,
37                  # middletree defines a childnode here. This is usually
38                  # covered by the tree 'anotherinnertree', unless the
39                  # interepreting NestedTree's constructor gets parameter
40                  # interpret_covered=True..
41                  [
42                      (FocusableText('XXX I\'m invisible!'), None),
43
44                  ]),
45             ]
46             )
47        ]
48    )  # end SimpleTree constructor for middletree
49    # use customized arrow decoration for middle tree
50    middletree = ArrowTree(middletree,
51                           arrow_hbar_char=u'\u2550',
52                           arrow_vbar_char=u'\u2551',
53                           arrow_tip_char=u'\u25B7',
54                           arrow_connector_tchar=u'\u2560',
55                           arrow_connector_lchar=u'\u255A')
56
57    # define outmost tree
58    outertree = SimpleTree(
59        [
60            (FocusableText('Outer ROOT'),
61             [
62                 (FocusableText('Child One'), None),
63                 (middletree, None),
64                 (FocusableText('last outer child'), None),
65             ]
66             )
67        ]
68    )  # end SimpleTree constructor
69
70    # add some Arrow decoration
71    outertree = ArrowTree(outertree)
72    # wrap the whole thing into a Nested Tree
73    outertree = NestedTree(outertree,
74                           # show covered nodes like  XXX
75                           interpret_covered=False
76                           )
77
78    # put it into a treebox and run
79    treebox = TreeBox(outertree)
80    rootwidget = urwid.AttrMap(treebox, 'body')
81    #add a text footer
82    footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
83    #enclose all in a frame
84    urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run() # go

Dynamic List

Update the tree after it's initially build.

Shows something like:

root
├─➤PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
│  64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.039 ms
│
├─➤64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.053 ms
│
└─➤64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.064 ms
 1import subprocess
 2import urwid
 3import urwidtrees
 4
 5root_node = [urwid.Text('root'), None]
 6tree_widget = urwidtrees.widgets.TreeBox(
 7    urwidtrees.decoration.ArrowTree(
 8        urwidtrees.tree.SimpleTree([root_node])
 9    )
10)
11
12def exit_on_q(key):
13    if key in ['q', 'Q']:
14        raise urwid.ExitMainLoop()
15
16loop = urwid.MainLoop(tree_widget, 
17    unhandled_input=exit_on_q)
18
19
20def on_stdout(data):
21    if not root_node[1]:
22        root_node[1] = []
23    root_node[1].append((urwid.Text(data), None))
24    tree_widget.refresh()
25
26
27proc = subprocess.Popen(
28    ['ping', '127.0.0.1'],
29    stdout=loop.watch_pipe(on_stdout),
30    close_fds=True)
31
32loop.run()
33proc.kill()