Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

pretty printing graphs

0 views
Skip to first unread message

John Hunter

unread,
Jul 28, 2003, 1:13:10 PM7/28/03
to

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

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

Scherer, Bill

unread,
Jul 28, 2003, 1:27:43 PM7/28/03
to
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

--
Bill.Scherer at Verizon Wireless
RHCE 807101044903581


Michele Simionato

unread,
Jul 28, 2003, 7:30:03 PM7/28/03
to
"Scherer, Bill" <Bill.S...@verizonwireless.com> wrote in message news:<mailman.105941338...@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 ;):


"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

Bengt Richter

unread,
Jul 28, 2003, 10:07:14 PM7/28/03
to
On Mon, 28 Jul 2003 12:13:10 -0500, John Hunter <jdhu...@ace.bsd.uchicago.edu> 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
>

The outline format is not good? I.e.,

C
C1
C1a
C1b
C2
C2a
C2b

An outline would be simple to print recursively.
[... snip code etc ...]


>
>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
>
>
>

How about (I added a name in the first node line for debugging, and boxing):
(code follows output). Not tested much ;-)

[19:04] C:\pywk\clp>python pphunter.py
+----------+
| n0|


|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| | n2|


|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|

+----------+ +----------+

+----------+ +----------+ +----------+ +----------+
| n1a| | n1b| | n2a| | n2b|


|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|

+----------+ +----------+ +----------+ +----------+


====< pphunter.py >==============================================


from __future__ import division, generators

def enumerate(seq):
"Waiting for python 2.3"
for i in range(len(seq)):
yield i, seq[i]

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
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():
n0 = Node(TextBox("""n0


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(TextBox("""n1


1 2 3 0
1 2 3 4
1 2 1 5
1 2 1 1
----------
1 1 0 0
"""))

n2 = Node(TextBox("""n2


4 3 2 2
4 3 2 7
2 3 2 3
2 3 2 9
----------
0 1 1 0
"""))

n1a = Node(TextBox("""n1a


1 2 1 5
1 2 1 1
----------
1 1 1 0
"""))

n1b = Node(TextBox("""n1b


1 2 3 0
1 2 3 4
----------
1 1 1 0
"""))

n2a = Node(TextBox("""n2a


2 3 2 3
2 3 2 9
----------
1 1 1 0
"""))

n2b = Node(TextBox("""n2b


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

if __name__ == '__main__': test()
=================================================================

Regards,
Bengt Richter

Tim Churches

unread,
Jul 28, 2003, 9:39:21 PM7/28/03
to

Some graphs of hidden Markov models, also drawn by dot (GraphViz) code
generated by a very similar Python program can be found in the figures (scroll
down a bit, on the left) for the paper at
http://www.biomedcentral.com/1472-6947/2/9

Tim C

Bengt Richter

unread,
Jul 29, 2003, 12:27:21 AM7/29/03
to

Decided to try the tree print I did for John Hunter in pphunter.py on your class tree:
explore is just a quick-n-dirty to make the tree.

====< mrog.py >=====================


# 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

import pphunter
def explore(cls, tree):
node = pphunter.Node(pphunter.TextBox(cls.__name__))
tree.children.append(node)
for b in cls.__bases__: explore(b, node)

root = pphunter.Node(pphunter.TextBox('root'))
explore(A, root)
print root.children[0].showInPage(20, 60)
====================================

Result:

[21:26] C:\pywk\clp>python mrog.py
+-+
|A|
+-+

+-+ +-+
|B| |C|
+-+ +-+

+-+ +-+ +-+ +-+ +-+
|E| |D| |F| |D| |G|
+-+ +-+ +-+ +-+ +-+

+------+ +------+ +------+ +------+ +------+
|object| |object| |object| |object| |object|
+------+ +------+ +------+ +------+ +------+

I guess I will have to do connector lines ;-)

BTW, I found a bug in the showInPage method of pphunter.Node. It should be

--
def showInPage(self, pageHeight=6*11, pageWidth=78):
return '\n'.join(self.boxlines(pageHeight, pageWidth))
--
(I capitalized pageHeight and page Width) . Boo :-(
(see 'pretty printing graphs' thread).

Regards,
Bengt Richter

Bengt Richter

unread,
Jul 29, 2003, 12:30:02 AM7/29/03
to
On 29 Jul 2003 02:07:14 GMT, bo...@oz.net (Bengt Richter) wrote:
[...]
>====< pphunter.py >==============================================
[...]
>class Node:
[...]

>
> def showInPage(self, pageHeight=6*11, pageWidth=78):
> return '\n'.join(self.boxlines(PageHeight, PageWidth))
>
should be
--
def showInPage(self, pageHeight=6*11, pageWidth=78):
return '\n'.join(self.boxlines(pageHeight, pageWidth))
--
[...]

Sorry.

Regards,
Bengt Richter

Bengt Richter

unread,
Jul 29, 2003, 1:48:49 AM7/29/03
to
On 28 Jul 2003 16:30:03 -0700, mi...@pitt.edu (Michele Simionato) wrote:

>"Scherer, Bill" <Bill.S...@verizonwireless.com> wrote in message news:<mailman.105941338...@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:

# 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

Michele Simionato

unread,
Jul 29, 2003, 10:00:36 AM7/29/03
to
Here is an enhanced version that also draws the metaclass instantiation
relationship:

import os

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

def dotMRO(cls):
"Generate 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([

' edge [style=solid]; %s -> %s %s;' %


(b.__name__,c.__name__,label(i,n))
for i,b in enumerate(c.__bases__)])

if type(c) is not type: yield \
' edge [style=dashed]; %s -> %s;' % (type(c).__name__,c.__name__)
yield '}'

class M(type): pass
O=object


class F(O): pass
class E(O): pass
class D(O): pass

class G(O): __metaclass__=M


class C(F,D,G): pass
class B(E,D): pass
class A(B,C): pass

dotcode='\n'.join(dotMRO(A)); print dotcode


os.system('echo "%s" | dot -Tps > prova.ps' % dotcode)
os.system('gv prova.ps&')

See the graph at

http://www.phyast.pitt.edu/~micheles/prova.ps

Here I had a problem due to my lack of knowledge of "dot":
class C inherits from F,D and G (in this order, see the labels
1,2,3). However, "dot" draws D before F and, without looking
at the labels, one would think that D comes before F, which
is not the case. IOW, the line FC should cross the line DC,
and the F-arrow should be on the left of the D-arrow.
Is there any way of fixing that?

TIA,


Michele

John Hunter

unread,
Jul 29, 2003, 9:53:27 AM7/29/03
to
>>>>> "Bengt" == Bengt Richter <bo...@oz.net> writes:

Bengt> How about (I added a name in the first node line for
Bengt> debugging, and boxing): (code follows output). Not tested
Bengt> much ;-)

Thanks Bengt - that looks great. You really should be on the payroll.

I won't have time until tonight to wrap my head around your code, but
I think I'll add a to_dot method to the Node class which generates dot
output, so I can use your ascii output for day-to-day stuff, and then
go to dot for publication quality.

Thanks all for the suggestions,
JDH

Michele Simionato

unread,
Jul 29, 2003, 10:11:33 AM7/29/03
to
bo...@oz.net (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

John Hunter

unread,
Jul 29, 2003, 11:31:51 AM7/29/03
to
>>>>> "Bengt" == Bengt Richter <bo...@oz.net> writes:

Bengt> should be -- def showInPage(self, pageHeight=6*11,
Bengt> pageWidth=78): return '\n'.join(self.boxlines(pageHeight,
Bengt> pageWidth)) -- [...]

I think I may have discovered another bug. In the longish example below,
the children of n2 are n20 and n21

n2.children.extend([n20, n21])

These children are the branch:

|------------------+
+-------+ +-------+
|3 4 5 6| |6 4 5 6|
|3 4 5 6| |-------|
|-------| |1 1 1 1|
|1 1 1 1| +-------+
+-------+

However, if you run your pprint on this example, they appear below the
n4 branch.

Haven't had a chance to grok the code yet -- I just came across this
bug when using your code on a test case for a projective clustering
neural network algorithm I'm implementing. The example is from Cao
and Wu, Neural Networks 15(2002) 105-120.

http://www.sciencedirect.com/science?_ob=ArticleURL&_udi=B6T08-43T2MC4-1&_user=5745&_handle=W-WA-A-A-AD-MsSAYZA-UUW-AUCEZCZEBD-AZZEEDDUV-AD-U&_fmt=full&_coverDate=01%2F31%2F2002&_rdoc=10&_orig=browse&_srch=%23toc%234856%232002%23999849998%23290257!&_cdi=4856&view=c&_acct=C000001358&_version=1&_urlVersion=0&_userid=5745&md5=61f59ff40e082d56154538b436b0010e


def test3():
n = Node(TextBox("""1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
6 7 8 9
1 2 3 4
3 4 5 6
2 3 4 5
6 4 5 6
4 2 3 1
6 7 1 2
4 5 6 7
-------
0 0 0 0
"""))

n0 = Node(TextBox("""1 2 3 4
1 2 3 4
4 2 3 1


-------
0 1 1 0
"""))

n1 = Node(TextBox("""2 3 4 5
2 3 4 5
-------
1 1 1 1
"""))

n2 = Node(TextBox("""3 4 5 6
3 4 5 6
6 4 5 6
-------
0 1 1 1
"""))

n3 = Node(TextBox("""4 5 6 7
4 5 6 7
-------
1 1 1 1
"""))

n4 = Node(TextBox("""6 7 8 9
6 7 1 2


-------
1 1 0 0
"""))

n.children.extend([n0, n1, n2, n3, n4])

n00 = Node(TextBox("""1 2 3 4
1 2 3 4
-------
1 1 1 1
"""))

n01 = Node(TextBox("""4 2 3 1
-------
1 1 1 1
"""))

n0.children.extend([n00, n01])

n20 = Node(TextBox("""3 4 5 6
3 4 5 6
-------
1 1 1 1
"""))

n21 = Node(TextBox("""6 4 5 6
-------
1 1 1 1
"""))

n2.children.extend([n20, n21])

n40 = Node(TextBox("""6 7 8 9
-------
1 1 1 1
"""))

n41 = Node(TextBox("""6 7 1 2
-------
1 1 1 1
"""))

n4.children.extend([n40, n41])

print n

Bengt Richter

unread,
Jul 29, 2003, 2:04:29 PM7/29/03
to
On Tue, 29 Jul 2003 08:53:27 -0500, John Hunter <jdhu...@ace.bsd.uchicago.edu> wrote:

>>>>>> "Bengt" == Bengt Richter <bo...@oz.net> writes:
>
> Bengt> How about (I added a name in the first node line for
> Bengt> debugging, and boxing): (code follows output). Not tested
> Bengt> much ;-)
>
>Thanks Bengt - that looks great. You really should be on the payroll.
>

Email me for particulars on how to send money ;-))

>I won't have time until tonight to wrap my head around your code, but
>I think I'll add a to_dot method to the Node class which generates dot
>output, so I can use your ascii output for day-to-day stuff, and then
>go to dot for publication quality.

If you have an interactive window with tty font with box characters, it could
look pretty nice for "day-to-day", I think (though requiring a little more logic
for choosing boxing and connection line characters).


>
>Thanks all for the suggestions,

You're welcome. Be aware that I just copied your original post and
vimmed at it until something emerged. It's pretty brute force, recomputing
a lot, etc etc. And not very well factored. OTOH, it seems to work, within its limits ;-)
It could use some sanity checks etc. though.

BTW, also in this thread there is a version with connector lines
called pptree.py, in case that is of interest.

I think to make a real tool, I would design differently. Off hand IWT using a random access
2-dimensional mutable character array as a page "canvas" to paint on would eliminate
some of the constraints on the layout that are inherent in the way I did it in pptree.
(I.e., hierarchical box model where any box may be contain a centered smaller
box connected to one row of boxes underneath it, and any of those may similarly either
be a single box or contain a smaller top box with its row of children, etc., so each box
is only a matter of one top single box and one row of child boxes, whatever the content of
the child boxes. Then its just a matter of dealing with the different sizes.)

Also more abstract element definitions and drawing primitives could allow for subclassing
for different output media like ascii, tty/boxchars, tkinter, postscript, etc.

I'm curious as to what your actual text blocks represent...

Regards,
Bengt Richter

Bengt Richter

unread,
Jul 29, 2003, 2:52:29 PM7/29/03
to
On Tue, 29 Jul 2003 10:31:51 -0500, John Hunter <jdhu...@ace.bsd.uchicago.edu> wrote:

>>>>>> "Bengt" == Bengt Richter <bo...@oz.net> writes:
>
> Bengt> should be -- def showInPage(self, pageHeight=6*11,
> Bengt> pageWidth=78): return '\n'.join(self.boxlines(pageHeight,
> Bengt> pageWidth)) -- [...]
>
>I think I may have discovered another bug. In the longish example below,
>the children of n2 are n20 and n21
>
> n2.children.extend([n20, n21])
>
>These children are the branch:
>
> |------------------+
> +-------+ +-------+
> |3 4 5 6| |6 4 5 6|
> |3 4 5 6| |-------|
> |-------| |1 1 1 1|
> |1 1 1 1| +-------+
> +-------+
>
>However, if you run your pprint on this example, they appear below the
>n4 branch.
>

I suspect the default "page" width of 78 is insufficient. I modded your
data to include a node names (e.g., see first two below)
e.g.,


>Haven't had a chance to grok the code yet -- I just came across this
>bug when using your code on a test case for a projective clustering
>neural network algorithm I'm implementing. The example is from Cao
>and Wu, Neural Networks 15(2002) 105-120.
>
> http://www.sciencedirect.com/science?_ob=ArticleURL&_udi=B6T08-43T2MC4-1&_user=5745&_handle=W-WA-A-A-AD-MsSAYZA-UUW-AUCEZCZEBD-AZZEEDDUV-AD-U&_fmt=full&_coverDate=01%2F31%2F2002&_rdoc=10&_orig=browse&_srch=%23toc%234856%232002%23999849998%23290257!&_cdi=4856&view=c&_acct=C000001358&_version=1&_urlVersion=0&_userid=5745&md5=61f59ff40e082d56154538b436b0010e
>
>
>def test3():

#> n = Node(TextBox("""1 2 3 4
n = Node(TextBox("""n


1 2 3 4
>2 3 4 5
>3 4 5 6
>4 5 6 7
>6 7 8 9
>1 2 3 4
>3 4 5 6
>2 3 4 5
>6 4 5 6
>4 2 3 1
>6 7 1 2
>4 5 6 7
>-------
>0 0 0 0
>"""))
>

#> n0 = Node(TextBox("""1 2 3 4
n0 = Node(TextBox("""n0


1 2 3 4
>1 2 3 4
>4 2 3 1
>-------
>0 1 1 0
>"""))
>

[...]


> n4.children.extend([n40, n41])
>
> print n

To specify a page wide enough here, try

print n.showInPage(35, 90)

With the name-modded data, I got

[11:56] C:\pywk\clp>pptree_t3.py 35 90

+-------+
| n|


|1 2 3 4|
|2 3 4 5|
|3 4 5 6|
|4 5 6 7|
|6 7 8 9|
|1 2 3 4|
|3 4 5 6|
|2 3 4 5|
|6 4 5 6|
|4 2 3 1|
|6 7 1 2|
|4 5 6 7|
|-------|
|0 0 0 0|

+-------+
+---------------+----------------|---------------+----------------+
+-------+ +-------+ +-------+ +-------+ +-------+
| n0| | n1| | n2| | n3| | n4|


|1 2 3 4| |2 3 4 5| |3 4 5 6| |4 5 6 7| |6 7 8 9|

|1 2 3 4| |2 3 4 5| |3 4 5 6| |4 5 6 7| |6 7 1 2|
|4 2 3 1| |-------| |6 4 5 6| |-------| |-------|
|-------| |1 1 1 1| |-------| |1 1 1 1| |1 1 0 0|
|0 1 1 0| +-------+ |0 1 1 1| +-------+ +-------+
+-------+ +-------+ +----|----+
+----|----+ +----|----+ +-------+ +-------+
+-------+ +-------+ +-------+ +-------+ | n40| | n41|
| n00| | n01| | n20| | n21| |6 7 8 9| |6 7 1 2|
|1 2 3 4| |4 2 3 1| |3 4 5 6| |6 4 5 6| |-------| |-------|
|1 2 3 4| |-------| |3 4 5 6| |-------| |1 1 1 1| |1 1 1 1|
|-------| |1 1 1 1| |-------| |1 1 1 1| +-------+ +-------+
|1 1 1 1| +-------+ |1 1 1 1| +-------+
+-------+ +-------+

I guess it would be good to make it self-expanding if the default page is too small, or raise
an informative exception.

Regards,
Bengt Richter

0 new messages