Re: pretty printing graphs

Discussion in 'Python' started by Scherer, Bill, Jul 28, 2003.

  1. 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
    >
    > 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
     
    Scherer, Bill, Jul 28, 2003
    #1
    1. Advertising

  2. "Scherer, Bill" <> wrote in message news:<>...

    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
     
    Michele Simionato, Jul 29, 2003
    #2
    1. Advertising

  3. On 28 Jul 2003 16:30:03 -0700, (Michele Simionato) wrote:

    >"Scherer, Bill" <> wrote in message news:<>...
    >
    >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
    ='|'; 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​
     
    Bengt Richter, Jul 29, 2003
    #3
  4. (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
     
    Michele Simionato, Jul 29, 2003
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Craig Duffield

    Pretty Printing

    Craig Duffield, Feb 26, 2004, in forum: Java
    Replies:
    0
    Views:
    372
    Craig Duffield
    Feb 26, 2004
  2. Christian Ashby

    Pretty Printing / Code formatting for JSPs

    Christian Ashby, Aug 20, 2004, in forum: Java
    Replies:
    0
    Views:
    371
    Christian Ashby
    Aug 20, 2004
  3. Andy Fish

    MXXMLWriter for pretty printing

    Andy Fish, Oct 28, 2003, in forum: XML
    Replies:
    0
    Views:
    1,810
    Andy Fish
    Oct 28, 2003
  4. Bengt Richter

    Re: pretty printing graphs

    Bengt Richter, Jul 29, 2003, in forum: Python
    Replies:
    4
    Views:
    351
    Bengt Richter
    Jul 29, 2003
  5. David
    Replies:
    5
    Views:
    319
    bsneddon
    Jul 8, 2007
Loading...

Share This Page