pretty printing graphs
Bengt Richter
bokr at oz.net
Tue Jul 29 01:48:49 EDT 2003
On 28 Jul 2003 16:30:03 -0700, mis6 at pitt.edu (Michele Simionato) wrote:
>"Scherer, Bill" <Bill.Scherer at verizonwireless.com> wrote in message news:<mailman.1059413382.3564.python-list at python.org>...
>
>Hey "dot" is great! I didn't know about it before readin your post.
>
>In a very short time I came out with the following recipe to draw
>Python inheritance hierarchies (which I post since it is pretty
>short and useful ;):
>
Well, here's the version with connectors:
====< pptree.py >===============================================
# pptree.py v 0.01 -- 20030728 22:20:17 bokr
class TextBox:
def __init__(self, text):
self.text = text
lines = text.splitlines()
self.bb = len(lines)+2, max(map(len, lines))+2 # rows,cols bounding box
def __str__(self):
return self.text
class Node:
PageHeight = 6*11; PageWidth = 78
def __repr__(self): return '<Node w/ text %r ...>'%self.textBox.text.splitlines()[0]
def treebb(self): # tree bb incl this node
childMaxHeight, childTotWidth = 0, 0
for child in self.children:
h, w = child.treebb()
childMaxHeight = max(childMaxHeight, h)
childTotWidth += w
ret = childMaxHeight+self.textBox.bb[0], max(childTotWidth, self.textBox.bb[1])
return ret
def __init__(self, textBox):
self.textBox = textBox
self.children = []
def boxlines(node, boxHeight, boxWidth):
oh, ow = node.textBox.bb # this node top text box bb
th, tw = node.treebb() # minimal child tree bb incl text box at top
render = ['']*boxHeight
ofmt = '|%%%ds|'% (ow-2)
render[0] = ('+'+'-'*(ow-2)+'+').center(boxWidth)
iLine=1
for line in node.textBox.text.splitlines():
render[iLine] = (ofmt%line).center(boxWidth)
iLine += 1
render[iLine] = render[0]
iLine += 1
if node.children:
availSepSpaces = boxWidth - tw
nch = len(node.children)
sep = nch>1 and availSepSpaces//nch or 0
childBoxes = []
for child in node.children:
chh, chw = child.treebb()
childBoxes.append(child.boxlines(boxHeight-oh-1, sep and chw+sep or boxWidth))
cbhs = map(len, childBoxes); assert max(cbhs)==min(cbhs) # all child boxes same ht
# do connector line (with wasteful repetition)
conn = ''.join(['+'.center(sep and child.treebb()[1]+sep or boxWidth)
for child in node.children])
conn = conn.center(boxWidth)
first = conn.find('+'); last = conn.rfind('+')
conn = conn[:first] + conn[first:last].replace(' ','-') + conn[last:]
center = '+'.center(boxWidth).find('+') # whatever the alg is
conn = list(conn); conn[center]='|'; conn = ''.join(conn)
render[iLine] = conn
for iChildline in xrange(cbhs[0]):
iLine += 1
render[iLine] = ''.join(
[childBox[iChildline] for childBox in childBoxes]
).center(boxWidth)
for iLine in range(boxHeight):
if not render[iLine]: render[iLine] = ' '*boxWidth
return render
def __str__(self):
return '\n'.join(self.boxlines(self.PageHeight, self.PageWidth))
def showInPage(self, pageHeight=6*11, pageWidth=78):
return '\n'.join(self.boxlines(pageHeight, pageWidth))
def test(height,width): # dimensions of chart
# Example hierarchy
O = object
class F(O): pass
class E(O): pass
class D(O): pass
class G(O): pass
class C(F,D,G): pass
class B(E,D): pass
class A(B,C): pass
def explore(cls, tree):
node = Node(TextBox(cls.__name__))
tree.children.append(node)
for b in cls.__bases__: explore(b, node)
root = Node(TextBox('root'))
explore(A, root)
print
print root.children[0].showInPage(height, width)
if __name__ == '__main__':
import sys; args = sys.argv[1:]
height = args and int(args.pop(0)) or 20
width = args and int(args.pop(0)) or 60
test(height,width)
================================================================
Results: first 50 wide, then 90
[22:54] C:\pywk\clp>pptree.py 20 50
+-+
|A|
+-+
+-------------|----------+
+-+ +-+
|B| |C|
+-+ +-+
+----|----+ +--------|--------+
+-+ +-+ +-+ +-+ +-+
|E| |D| |F| |D| |G|
+-+ +-+ +-+ +-+ +-+
| | | | |
+------+ +------+ +------+ +------+ +------+
|object| |object| |object| |object| |object|
+------+ +------+ +------+ +------+ +------+
[22:54] C:\pywk\clp>pptree.py 20 90
+-+
|A|
+-+
+-----------------------|--------------------+
+-+ +-+
|B| |C|
+-+ +-+
+---------|---------+ +---------------|---------------+
+-+ +-+ +-+ +-+ +-+
|E| |D| |F| |D| |G|
+-+ +-+ +-+ +-+ +-+
| | | | |
+------+ +------+ +------+ +------+ +------+
|object| |object| |object| |object| |object|
+------+ +------+ +------+ +------+ +------+
The code is pretty hacky, but I wanted to show the art ;-)
BTW, TextBox can accept a multiline string, and finds the bounding box (without trimming blanks).
Regards,
Bengt Richter
More information about the Python-list
mailing list