Skip to content

Instantly share code, notes, and snippets.

@geier
Created September 22, 2017 22:08
Show Gist options
  • Save geier/1b6920041d3518966fc7f8cb2b55b1d6 to your computer and use it in GitHub Desktop.
Save geier/1b6920041d3518966fc7f8cb2b55b1d6 to your computer and use it in GitHub Desktop.
#!/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.
#
# adapted from example1.py and example3.collapse.py from the urwidtrees examples
from urwidtrees.tree import SimpleTree
from urwidtrees.decoration import CollapseIconMixin, DecoratedTree
from urwidtrees.widgets import TreeBox
import urwid
import re
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', ''),
]
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
@property
def text(self):
return self._wrapped_widget._original_widget.text
mail = [
"No problem. 6pm it is then. --Jim",
"",
"At 10.01am Wednesday, Danny wrote:",
"> Whoa! I need to email a report at 5:30.",
"> Could you push it back an hour? --Danny",
">",
"> At 9.40am Wednesday, Jim wrote:",
">",
">> I'm going to suspend the mail service for approx. thirty",
">> minutes tonight, starting at 5pm. --Jim",
">>",
">",
"> On the 16th, Alice wrote:",
">> Hello,",
">> how are you?",
">> I'm good.",
">> Bye",
">",
"",
"-- ",
"Jim Doe",
"Chief of Nothing",
"Nothing Inc.",
"",
"",
"",
"------------------------------------------------------",
"something mailing list",
"list@mail.list",
"http://mail.list.org",
]
# (?: ) is a non-capturing group
reg = re.compile(r'^((?:> ?)+)?(.*)')
def get_reply_levels(lines):
for num, line in enumerate(lines):
groups = reg.match(line).groups()
indendation = groups[0] or ''
content = groups[1] or ''
level = indendation.count('>')
yield level
def build_tree():
levels = list(get_reply_levels(mail))
oldlevel = last = 0
subtrees = []
for num, level in enumerate(levels):
while level > oldlevel:
new = FocusableText('\n'.join(mail[last:num]))
subtrees.append(new)
last = num
oldlevel += 1
new = FocusableText('\n'.join(mail[last:num]))
subtrees.append(new)
last = None
for subtree in subtrees[::-1]:
new = [(subtree, last)]
last = new
return SimpleTree(last)
class CollapsibleIconTree(CollapseIconMixin, DecoratedTree):
"""
Indent collapsible tree nodes according to their depth in the tree and
display icons indicating collapse-status in the gaps.
"""
def __init__(self, walker, icon_offset=1, **kwargs):
"""
:param walker: tree of widgets to be displayed
:type walker: Tree
:param indent: indentation width
:type indent: int
:param icon_offset: distance from icon to the eginning of the tree
node.
:type icon_offset: int
"""
self._icon_offset = icon_offset
DecoratedTree.__init__(self, walker)
CollapseIconMixin.__init__(self, **kwargs)
def decorate(self, pos, widget, is_first=True):
iwidth, icon = self._construct_collapse_icon(pos)
cols = []
void = urwid.SolidFill(' ')
line = None
# add icon only for non-leafs
is_leaf = self._tree.is_leaf(pos)
if not is_leaf:
if icon is not None:
# space to the left
cols.append((1, urwid.SolidFill(' ')))
# icon
icon_pile = urwid.Pile([void, ('pack', icon)])
cols.append((iwidth, icon_pile))
cols.append((self._icon_offset, urwid.SolidFill(' ')))
else: # otherwise just add another spacer
cols.append((3, urwid.SolidFill(' ')))
cols.append(widget) # original widget ]
# construct a Columns, defining all spacer as Box widgets
line = urwid.Columns(cols, box_columns=range(len(cols))[:-1])
return line
def unhandled_input(key):
if isinstance(key, str) and key.lower() == 'q':
raise urwid.ExitMainLoop()
if __name__ == "__main__":
tree = CollapsibleIconTree(
build_tree(),
is_collapsed=lambda _: True,
icon_focussed_att='focus',
icon_frame_left_char=None,
icon_frame_right_char=None,
icon_expanded_char='▼',
icon_collapsed_char='▶',
)
treebox = TreeBox(tree)
rootwidget = urwid.AttrMap(treebox, 'body')
footer = urwid.AttrMap(urwid.Text('Q to quit'), 'focus')
urwid.MainLoop(
urwid.Frame(rootwidget, footer=footer),
palette,
unhandled_input=unhandled_input,
).run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment