Created
September 22, 2017 22:08
-
-
Save geier/1b6920041d3518966fc7f8cb2b55b1d6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# Copyright (C) 2013 Patrick Totzke <[email protected]> | |
# 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", | |
"[email protected]", | |
"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