# 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()