Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Re: pretty printing graphs

Reply
Thread Tools

Re: pretty printing graphs

 
 
Scherer, Bill
Guest
Posts: n/a
 
      07-28-2003
On Mon, 28 Jul 2003, John Hunter wrote:

>
> I have a tree structure (directed acyclic graph), where each node can
> represent itself as a multi-line string that is not very wide (eg, 10
> chars per line). I would like to print the graph like
>
> C
> C1 C2
> C1a C1b C2a C2b


[P&M]

John -

Do you strictly require plain text output? If not, look into
graphiz. It's fairly easy to generate dot files from python and
then generate output in postscript or in a bitmap format (jpg,
png, etc) using the graphiz tools.

graphiz can be had from http://www.graphviz.org

There is a python interface to graphviz available from
http://www.cs.brown.edu/~er/software , but I have not used it.

graphviz's output is customizable to some extant, and looks
rather nice.

HTH,

Bill

>
> Where C, C1, etc.. are the multiline string blocks referred to above.
> Does anybody know of a tool that can do this. Here is an example,
> somewhat long because I have to create the multiline strings.
>
> from __future__ import division, generators
>
> def enumerate(seq):
> "Waiting for python 2.3"
> for i in range(len(seq)):
> yield i, seq[i]
>
> class O:
>
> def __init__(self, text):
> self.text = text
> def __str__(self):
> return self.text
>
> class Node:
> def __init__(self, o):
> self.node = o
> self.children = []
>
> def __str__(self):
> s = ''
> #s += str(self.node) + '\n\n'
> if len(self.children)==0: return s
> childStrs = [str(child.node) for child in self.children]
>
> lines = [[] for tmp in childStrs]
> for lineNum, t in enumerate(childStrs):
> lines[lineNum].extend(t.split('\n'))
>
> maxLines = max([len(l) for l in lines])
> sep = ' '
> for lineNum in range(maxLines):
> row = ''
> for childNum in range(len(childStrs)):
> row += lines[childNum][lineNum] + sep
> s += row + '\n'
> s += '\n\n'
> for l in self.children:
> s += str(l)
> return s
>
>
>
> n0 = Node(O("""1 2 3 0
> 1 2 3 4
> 1 2 1 5
> 1 2 1 1
> 4 3 2 2
> 4 3 2 7
> 2 3 2 3
> 2 3 2 9"""))
>
>
> n1 = Node(O("""1 2 3 0
> 1 2 3 4
> 1 2 1 5
> 1 2 1 1
> ----------
> 1 1 0 0"""))
>
> n2 = Node(O("""4 3 2 2
> 4 3 2 7
> 2 3 2 3
> 2 3 2 9
> ----------
> 0 1 1 0"""))
>
>
>
> n1a = Node(O("""1 2 1 5
> 1 2 1 1
> ----------
> 1 1 1 0"""))
>
> n1b = Node(O("""1 2 3 0
> 1 2 3 4
> ----------
> 1 1 1 0"""))
>
> n2a = Node(O("""2 3 2 3
> 2 3 2 9
> ----------
> 1 1 1 0"""))
>
> n2b = Node(O("""4 3 2 2
> 4 3 2 7
> ----------
> 1 1 1 0"""))
>
>
> n0.children.extend([n1, n2])
> n1.children.extend([n1a, n1b])
> n2.children.extend([n2a, n2b])
> print n0
>
> Which prints:
>
> 1 2 3 0 4 3 2 2
> 1 2 3 4 4 3 2 7
> 1 2 1 5 2 3 2 3
> 1 2 1 1 2 3 2 9
> ---------- ----------
> 1 1 0 0 0 1 1 0
>
>
> 1 2 1 5 1 2 3 0
> 1 2 1 1 1 2 3 4
> ---------- ----------
> 1 1 1 0 1 1 1 0
>
>
> 2 3 2 3 4 3 2 2
> 2 3 2 9 4 3 2 7
> ---------- ----------
> 1 1 1 0 1 1 1 0
>
>
> This does part of the work, printing the child nodes on the same rows,
> but doesn't the hierarchical part very well. What I would like is
> something like this:
>
> 1 2 3 0
> 1 2 3 4
> 1 2 1 5
> 1 2 1 1
> 4 3 2 2
> 4 3 2 7
> 2 3 2 3
> 2 3 2 9
>
> 1 2 3 0 4 3 2 2
> 1 2 3 4 4 3 2 7
> 1 2 1 5 2 3 2 3
> 1 2 1 1 2 3 2 9
> ---------- ----------
> 1 1 0 0 0 1 1 0
>
>
> 1 2 1 5 1 2 3 0 2 3 2 3 4 3 2 2
> 1 2 1 1 1 2 3 4 2 3 2 9 4 3 2 7
> ---------- ---------- ---------- ----------
> 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0
>
>
> Thanks,
> John Hunter
>
>


--
Bill.Scherer at Verizon Wireless
RHCE 807101044903581


 
Reply With Quote
 
 
 
 
Michele Simionato
Guest
Posts: n/a
 
      07-28-2003
"Scherer, Bill" <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)>...

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 :


"How to draw inheritance hierarchies via dot"

import os

def label(i,n):
if n>1: return '[label="%s"]' % (i+1)
return ""

def dotMRO(cls):
"Generates the dot code for the MRO directed graph"
yield "digraph MRO_of_%s{\n" % cls.__name__
for c in cls.__mro__:
n=len(c.__bases__)
yield ' '.join([' %s -> %s %s;' % (b.__name__,c.__name__,label(i,n))
for i,b in enumerate(c.__bases__)])
yield '}'

# 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

# creates the graph
dotcode='\n'.join(dotMRO(A)); print dotcode
os.system('echo "%s" | dot -Tps > prova.ps' % dotcode)
os.system('gv prova.ps&')

Assuming you have "dot" and the standard Unix tools installed this
will generate a very nice diagram. I am open to suggestion to improve it,
since this is may first trial with "dot". I am quite impressed by the
easy of use.

Very nice!


Michele
 
Reply With Quote
 
 
 
 
Bengt Richter
Guest
Posts: n/a
 
      07-29-2003
On 28 Jul 2003 16:30:03 -0700, http://www.velocityreviews.com/forums/(E-Mail Removed) (Michele Simionato) wrote:

>"Scherer, Bill" <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)>...
>
>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=7:
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
 
Reply With Quote
 
Michele Simionato
Guest
Posts: n/a
 
      07-29-2003
(E-Mail Removed) (Bengt Richter) wrote in message news:<bg51s1$qp2$0@216.39.172.122>...
> Well, here's the version with connectors:
> <snip>


Thanks Bengt,

this give me some idea. I was writing something similar three or four
days ago, but I was stuck with the connection lines. I had the very
bad idea of drawing them as dots, but this is the unhappy result in
a similar hierarchy:


+------+
|object|
+------+..
. . ...
. . ...
. . ...
+------+ +------+ +------+
|ClassD| |ClassE| |ClassF|
+------+.. .+------+ +------+
. ..... .
. ... ... .
. ... ... .
+------+ +------+
|ClassB| |ClassC|
+------+ .....+------+
. .....
. .....
. .....
+------+..
|ClassA|
+------+


I think I will copy your connection lines idea, if not the code


Michele
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: One more Pretty Face - Pretty face9.jpg [1/1] Harvey Digital Photography 7 11-04-2004 09:53 AM
Pretty Printing Craig Duffield Java 0 02-26-2004 10:26 PM
Re: pretty printing graphs Bengt Richter Python 4 07-29-2003 06:52 PM
Re: Pretty Please Peer2Peer need help Pretty Please John Haithwaite @ Blue Case Solutions Computer Support 0 07-06-2003 10:20 PM
Re: Pretty Please Peer2Peer need help Pretty Please Ralph Wade Phillips Computer Support 0 07-06-2003 09:07 PM



Advertisments