Skip to content

Instantly share code, notes, and snippets.

@SegFaultAX
Last active August 22, 2021 02:40
Show Gist options
  • Save SegFaultAX/2e8007d01bd7141390d8 to your computer and use it in GitHub Desktop.
Save SegFaultAX/2e8007d01bd7141390d8 to your computer and use it in GitHub Desktop.
Draw nested ascii tables in Python
from StringIO import StringIO
from operator import attrgetter
import textwrap
class Node(object):
def __init__(self, val=None, *children):
self.val = val
self.children = list(children)
test1 = Node("foo")
test2 = Node("foo", Node("bar"))
test3 = Node("foo", Node("spam", Node("spam1"), Node("spam2")),
Node("eggs", Node("eggs1"), Node("eggs2")))
test4 = Node("", Node("", Node("", Node("", Node("", Node(("suck it", "you", "bitch")))))))
def draw_nested(tree, valfn=attrgetter("val"), childfn=attrgetter("children"), maxwidth=0):
def _draw_table(root, prefix, acc):
val, children = valfn(root), childfn(root)
acc.append((prefix+".", "", "-", False))
if isinstance(val, (list, tuple)):
for val in val:
acc.append((prefix + "| ", val, " ", True))
elif val:
acc.append((prefix+ "| ", val, " ", True))
for child in children:
_draw_table(child, prefix + "| ", acc)
acc.append((prefix+"'", "", "-", False))
return acc
table = _draw_table(tree, "", [])
buff = StringIO()
if maxwidth <= 0:
maxwidth = max(2 * len(prefix) + len(text) for prefix, text, _, _ in table)
for prefix, text, fill, is_content in table:
if is_content:
contentwidth = maxwidth - 2 * len(prefix)
lines = textwrap.wrap(text, width=contentwidth)
else:
lines = [text]
for line in lines:
fillwidth = maxwidth - 2 * len(prefix) - len(line)
buff.write("{}{}{}{}\n".format(
prefix, line.encode("utf-8"), fill * fillwidth, prefix[::-1]))
return buff.getvalue()
lines = draw_nested(test4)
print lines
.---------------.
| foo |
| .-----------. |
| | spam | |
| | .-------. | |
| | | spam1 | | |
| | '-------' | |
| | .-------. | |
| | | spam2 | | |
| | '-------' | |
| '-----------' |
| .-----------. |
| | eggs | |
| | .-------. | |
| | | eggs1 | | |
| | '-------' | |
| | .-------. | |
| | | eggs2 | | |
| | '-------' | |
| '-----------' |
'---------------'
Reddit comment example:
.-------------------------------------------.
| .---------------------------------------. |
| | ingolemo (2/0) | |
| | Looks to me like it would be better | |
| | to split at the commas and then strip | |
| | off the whitespace. Either way, this | |
| | would be best accomplished with a | |
| | list comprehesion; for line in | |
| | gradefile: linelist = | |
| | [field.strip() for field in | |
| | line.split(',')] # do | |
| | something with linelist | |
| | .-----------------------------------. | |
| | | Blackshirt12 (1/0) | | |
| | | So what is a field in this | | |
| | | context? This worked for me, but | | |
| | | I don't understand why. I haven't | | |
| | | used this method before. | | |
| | | .-------------------------------. | | |
| | | | ingolemo (1/0) | | | |
| | | | It's just the name that I | | | |
| | | | gave to the variable that | | | |
| | | | temporarily holds each of the | | | |
| | | | sub-items in | | | |
| | | | `line.split(',')`. it's | | | |
| | | | analogous to `line` on the | | | |
| | | | line above it. | | | |
| | | | .---------------------------. | | | |
| | | | | zahlman (1/0) | | | | |
| | | | | I don't know why you were | | | | |
| | | | | downvoted. This is | | | | |
| | | | | exactly how I'd do it and | | | | |
| | | | | it's perfectly idiomatic | | | | |
| | | | | and correct. | | | | |
| | | | '---------------------------' | | | |
| | | '-------------------------------' | | |
| | '-----------------------------------' | |
| '---------------------------------------' |
| .---------------------------------------. |
| | ahnakel (1/0) | |
| | Do you just need to remove the last | |
| | character? linelist[:-1] should | |
| | reference all elements in a list | |
| | before the last one. | |
| '---------------------------------------' |
| .---------------------------------------. |
| | pkkid (1/0) | |
| | Assuming `gradefile` is a list of 4 | |
| | strings as you suggest in your | |
| | description, something like the | |
| | following should work: # Remove | |
| | trailing commas gradefile = | |
| | [x.rstrip(",") for x in gradefile] | |
| | # Convert one of them to a float | |
| | gradefile[3] = float(gradefile[3]) | |
| '---------------------------------------' |
'-------------------------------------------'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment