# Examples

## Minimal example

Simplest example rendering:

    [-] item 1
            sub item 1
            sub item 2
        item 2

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    import urwid
    import urwidtrees

    tree_widget = urwidtrees.widgets.TreeBox(
        urwidtrees.decoration.CollapsibleIndentedTree(
            urwidtrees.tree.SimpleTree([
                (urwid.SelectableIcon('item 1'), (
                    (urwid.SelectableIcon('sub item 1'), None),
                    (urwid.SelectableIcon('sub item 2'), None),
                )),
                (urwid.SelectableIcon('item 2'), None),
            ])
        )
    )

    urwid.MainLoop(tree_widget).run()

## Basic use

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    #!/usr/bin/python
    # Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
    # This file is released under the GNU GPL, version 3 or a later revision.

    import urwid
    from urwidtrees.tree import SimpleTree
    from urwidtrees.widgets import TreeBox

    # define some colours
    palette = [
        ('body', 'black', 'light gray'),
        ('focus', 'light gray', 'dark blue', 'standout'),
        ('bars', 'dark blue', 'light gray', ''),
        ('arrowtip', 'light blue', 'light gray', ''),
        ('connectors', 'light red', 'light gray', ''),
    ]

    # We use selectable Text widgets for our example..

    class FocusableText(urwid.WidgetWrap):
        """Selectable Text used for nodes in our example"""
        def __init__(self, txt):
            t = urwid.Text(txt)
            w = urwid.AttrMap(t, 'body', 'focus')
            urwid.WidgetWrap.__init__(self, w)

        def selectable(self):
            return True

        def keypress(self, size, key):
            return key

    # define a test tree in the format accepted by SimpleTree. Essentially, a
    # tree is given as (nodewidget, [list, of, subtrees]). SimpleTree accepts
    # lists of such trees.

    def construct_example_simpletree_structure(selectable_nodes=True, children=3):

        Text = FocusableText if selectable_nodes else urwid.Text

        # define root node
        tree = (Text('ROOT'), [])

        # define some children
        c = g = gg = 0  # counter
        for i in range(children):
            subtree = (Text('Child %d' % c), [])
            # and grandchildren..
            for j in range(children):
                subsubtree = (Text('Grandchild %d' % g), [])
                for k in range(children):
                    leaf = (Text('Grand Grandchild %d' % gg), None)
                    subsubtree[1].append(leaf)
                    gg += 1  # inc grand-grandchild counter
                subtree[1].append(subsubtree)
                g += 1  # inc grandchild counter
            tree[1].append(subtree)
            c += 1
        return tree

    def construct_example_tree(selectable_nodes=True, children=2):
        # define a list of tree structures to be passed on to SimpleTree
        forrest = [construct_example_simpletree_structure(selectable_nodes,
                                                          children)]

        # stick out test tree into a SimpleTree and return
        return SimpleTree(forrest)

    def unhandled_input(k):
        #exit on q
        if k in ['q', 'Q']: raise urwid.ExitMainLoop()

    if __name__ == "__main__":
        # get example tree
        stree = construct_example_tree()

        # put the tree into a treebox
        treebox = TreeBox(stree)

        # add some decoration
        rootwidget = urwid.AttrMap(treebox, 'body')
        #add a text footer
        footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
        #enclose all in a frame
        urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run()  # go

## Decoration

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/usr/bin/python
    # Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
    # This file is released under the GNU GPL, version 3 or a later revision.

    from example1 import construct_example_tree, palette, unhandled_input # example data
    from urwidtrees.decoration import ArrowTree  # for Decoration
    from urwidtrees.widgets import TreeBox
    import urwid

    if __name__ == "__main__":
        # get example tree
        stree = construct_example_tree()
        # Here, we add some decoration by wrapping the tree using ArrowTree.
        atree = ArrowTree(stree,
                          # customize at will..
                          # arrow_hbar_char=u'\u2550',
                          # arrow_vbar_char=u'\u2551',
                          # arrow_tip_char=u'\u25B7',
                          # arrow_connector_tchar=u'\u2560',
                          # arrow_connector_lchar=u'\u255A',
                          )

        # put the into a treebox
        treebox = TreeBox(atree)
        rootwidget = urwid.AttrMap(treebox, 'body')
        #add a text footer
        footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
        #enclose in a frame
        urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run()  # go

## Collapsible subtrees

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #!/usr/bin/python
    # Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
    # This file is released under the GNU GPL, version 3 or a later revision.

    from example1 import construct_example_tree, palette, unhandled_input # example data
    from urwidtrees.decoration import CollapsibleIndentedTree  # for Decoration
    from urwidtrees.widgets import TreeBox
    import urwid

    if __name__ == "__main__":
        # get some SimpleTree
        stree = construct_example_tree()

        # Use (subclasses of) the wrapper decoration.CollapsibleTree to construct a
        # tree where collapsible subtrees. Apart from the original tree, these take
        # a callable `is_collapsed` that defines initial collapsed-status if a
        # given position.

        # We want all grandchildren collapsed initially
        if_grandchild = lambda pos: stree.depth(pos) > 1

        # We use CollapsibleIndentedTree around the original example tree.
        # This uses Indentation to indicate the tree structure and squeezes in
        # text-icons to indicate the collapsed status.
        # Also try CollapsibleTree or CollapsibleArrowTree..
        tree = CollapsibleIndentedTree(stree,
                                       is_collapsed=if_grandchild,
                                       icon_focussed_att='focus',
                                       # indent=6,
                                       # childbar_offset=1,
                                       # icon_frame_left_char=None,
                                       # icon_frame_right_char=None,
                                       # icon_expanded_char='-',
                                       # icon_collapsed_char='+',
                                       )

        # put the tree into a treebox
        treebox = TreeBox(tree)
        rootwidget = urwid.AttrMap(treebox, 'body')
        #add a text footer
        footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
        #enclose all in a frame
        urwid.MainLoop(urwid.Frame(rootwidget, footer=footer), palette, unhandled_input = unhandled_input).run() # go

## Custom Trees: Walking the filesystem

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    #!/usr/bin/python
    # Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
    # This file is released under the GNU GPL, version 3 or a later revision.

    import urwid
    import os
    from example1 import palette, unhandled_input  # example data
    from urwidtrees.widgets import TreeBox
    from urwidtrees.tree import Tree
    from urwidtrees.decoration import CollapsibleArrowTree

    # define selectable urwid.Text widgets to display paths
    class FocusableText(urwid.WidgetWrap):
        """Widget to display paths lines"""
        def __init__(self, txt):
            t = urwid.Text(txt)
            w = urwid.AttrMap(t, 'body', 'focus')
            urwid.WidgetWrap.__init__(self, w)

        def selectable(self):
            return True

        def keypress(self, size, key):
            return key

    # define Tree that can walk your filesystem

    class DirectoryTree(Tree):
        """
        A custom Tree representing our filesystem structure.
        This implementation is rather inefficient: basically every position-lookup
        will call `os.listdir`.. This makes navigation in the tree quite slow.
        In real life you'd want to do some caching.

        As positions we use absolute path strings.
        """
        # determine dir separator and form of root node
        pathsep = os.path.sep
        drive, _ = os.path.splitdrive(pathsep)

        # define root node This is part of the Tree API!
        root = drive + pathsep

        def __getitem__(self, pos):
            return FocusableText(pos)

        # generic helper
        def _list_dir(self, path):
            """returns absolute paths for all entries in a directory"""
            try:
                elements = [os.path.join(
                    path, x) for x in os.listdir(path) if os.path.isdir(path)]
                elements.sort()
            except OSError:
                elements = None
            return elements

        def _get_siblings(self, pos):
            """lists the parent directory of pos """
            parent = self.parent_position(pos)
            siblings = [pos]
            if parent is not None:
                siblings = self._list_dir(parent)
            return siblings

        # Tree API
        def parent_position(self, pos):
            parent = None
            if pos != '/':
                parent = os.path.split(pos)[0]
            return parent

        def first_child_position(self, pos):
            candidate = None
            if os.path.isdir(pos):
                children = self._list_dir(pos)
                if children:
                    candidate = children[0]
            return candidate

        def last_child_position(self, pos):
            candidate = None
            if os.path.isdir(pos):
                children = self._list_dir(pos)
                if children:
                    candidate = children[-1]
            return candidate

        def next_sibling_position(self, pos):
            candidate = None
            siblings = self._get_siblings(pos)
            myindex = siblings.index(pos)
            if myindex + 1 < len(siblings):  # pos is not the last entry
                candidate = siblings[myindex + 1]
            return candidate

        def prev_sibling_position(self, pos):
            candidate = None
            siblings = self._get_siblings(pos)
            myindex = siblings.index(pos)
            if myindex > 0:  # pos is not the first entry
                candidate = siblings[myindex - 1]
            return candidate

    if __name__ == "__main__":
        cwd = os.getcwd()  # get current working directory
        dtree = DirectoryTree()  # get a directory walker

        # Use CollapsibleArrowTree for decoration.
        # define initial collapse:
        as_deep_as_cwd = lambda pos: dtree.depth(pos) >= dtree.depth(cwd)

        # We hide the usual arrow tip and use a customized collapse-icon.
        decorated_tree = CollapsibleArrowTree(dtree,
                                              is_collapsed=as_deep_as_cwd,
                                              arrow_tip_char=None,
                                              icon_frame_left_char=None,
                                              icon_frame_right_char=None,
                                              icon_collapsed_char=u'\u25B6',
                                              icon_expanded_char=u'\u25B7',)

        # stick it into a TreeBox and use 'body' color attribute for gaps
        tb = TreeBox(decorated_tree, focus=cwd)
        root_widget = urwid.AttrMap(tb, 'body')
        #add a text footer
        footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
        #enclose all in a frame
        urwid.MainLoop(urwid.Frame(root_widget, footer=footer), palette, unhandled_input = unhandled_input).run() # go

## Nesting Trees

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    #!/usr/bin/python
    # Copyright (C) 2013  Patrick Totzke <patricktotzke@gmail.com>
    # This file is released under the GNU GPL, version 3 or a later revision.

    from example1 import palette, construct_example_tree  # example data
    from example1 import FocusableText, unhandled_input  # Selectable Text used for nodes
    from urwidtrees.widgets import TreeBox
    from urwidtrees.tree import SimpleTree
    from urwidtrees.nested import NestedTree
    from urwidtrees.decoration import ArrowTree, CollapsibleArrowTree  # decoration
    import urwid
    import logging

    if __name__ == "__main__":
        #logging.basicConfig(filename='example.log',level=logging.DEBUG)
        # Take some Arrow decorated Tree that we later stick inside another tree.
        innertree = ArrowTree(construct_example_tree())
        # Some collapsible, arrow decorated tree with extra indent
        anotherinnertree = CollapsibleArrowTree(construct_example_tree(),
                                                indent=10)

        # A SimpleTree, that contains the two above
        middletree = SimpleTree(
            [
                (FocusableText('Middle ROOT'),
                 [
                     (FocusableText('Mid Child One'), None),
                     (FocusableText('Mid Child Two'), None),
                     (innertree, None),
                     (FocusableText('Mid Child Three'),
                      [
                          (FocusableText('Mid Grandchild One'), None),
                          (FocusableText('Mid Grandchild Two'), None),
                      ]
                      ),
                     (anotherinnertree,
                      # middletree defines a childnode here. This is usually
                      # covered by the tree 'anotherinnertree', unless the
                      # interepreting NestedTree's constructor gets parameter
                      # interpret_covered=True..
                      [
                          (FocusableText('XXX I\'m invisible!'), None),

                      ]),
                 ]
                 )
            ]
        )  # end SimpleTree constructor for middletree
        # use customized arrow decoration for middle tree
        middletree = ArrowTree(middletree,
                               arrow_hbar_char=u'\u2550',
                               arrow_vbar_char=u'\u2551',
                               arrow_tip_char=u'\u25B7',
                               arrow_connector_tchar=u'\u2560',
                               arrow_connector_lchar=u'\u255A')

        # define outmost tree
        outertree = SimpleTree(
            [
                (FocusableText('Outer ROOT'),
                 [
                     (FocusableText('Child One'), None),
                     (middletree, None),
                     (FocusableText('last outer child'), None),
                 ]
                 )
            ]
        )  # end SimpleTree constructor

        # add some Arrow decoration
        outertree = ArrowTree(outertree)
        # wrap the whole thing into a Nested Tree
        outertree = NestedTree(outertree,
                               # show covered nodes like  XXX
                               interpret_covered=False
                               )

        # put it into a treebox and run
        treebox = TreeBox(outertree)
        rootwidget = urwid.AttrMap(treebox, 'body')
        #add a text footer
        footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
        #enclose all in a frame
        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

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import subprocess
    import urwid
    import urwidtrees

    root_node = [urwid.Text('root'), None]
    tree_widget = urwidtrees.widgets.TreeBox(
        urwidtrees.decoration.ArrowTree(
            urwidtrees.tree.SimpleTree([root_node])
        )
    )

    def exit_on_q(key):
        if key in ['q', 'Q']:
            raise urwid.ExitMainLoop()

    loop = urwid.MainLoop(tree_widget,
        unhandled_input=exit_on_q)

    def on_stdout(data):
        if not root_node[1]:
            root_node[1] = []
        root_node[1].append((urwid.Text(data), None))
        tree_widget.refresh()

    proc = subprocess.Popen(
        ['ping', '127.0.0.1'],
        stdout=loop.watch_pipe(on_stdout),
        close_fds=True)

    loop.run()
    proc.kill()