When I wrote Facts and myths about Python names and values, I included figures drawn with Graphviz. I made a light wrapper to help, but the figures were mostly just Graphviz. For example, this code:

g = CogGraphviz()
g.codelabel("nums = [1, 2, 3]")
g.dot("""
    nums [NAMEATTRS]
    list1 [shape=record, label="{<0>|<1>|<2>}"]
    nums -> list1:0
    subgraph nums {
        rankdir=LR;
        1 [INTATTRS]
        2 [INTATTRS]
        3 [INTATTRS]
        }
    list1:0 -> 1
    list1:1 -> 2
    list1:2 -> 3
    """)
g.img(alt="nums refers to a list, which refers to ints")

produced this figure:

A list of three numbers

The beauty of Graphviz is that you describe the topology of your graph, and it lays it out for you. The problem is, if you care about the layout, it is very difficult to control. Why are the 1, 2, 3 where they are? How do I move them? I couldn't figure it out. And I wanted to have new shapes, and control where the lines joined, etc.

So I wrote a library called Cupid to help me make the figures the way I wanted them. Cupid is an API that generates SVG figures. Half of it is generic "place this rectangle here" kinds of SVG operations, and half is very specific to the names-and-values diagrams I wanted to create.

Now to make that same figure, I use this code:

fig = start_figure(title="nums refers to a list, which refers to ints")
fig.top_code("nums = [1, 2, 3]")
n_nums = fig.auto_name("nums")
l_nums = fig.list(length=3, pos=fig.val_for_name(n_nums))
fig.reference(n_nums, l_nums[0])
y_ints = fig.y+10
for i in range(3):
    i_num = fig.int(top=(l_nums[1].cx+(i-1)*35, y_ints), text=str(i+1))
    fig.connect(
        l_nums[i].center, 90, i_num.north, 90, 
        start_marker=fig.DOT, class_="arrow"
    )
finish_figure(fig)

nums refers to a list, which refers to intsnums123nums = [1, 2, 3]

The new code is more complicated than the old code, but I can predict what it will do, and if I want it to do something new, I can extend Cupid.

Cupid isn't the handiest thing, but I can make it do what I want. This is my way with this site: I want it the way I want it, I enjoy writing the tools that let me make it that way, and I don't mind writing code to produce figures when other people would use a mouse.

tagged: , » 6 reactions

Comments

[gravatar]
Paul Moore 10:38 PM on 26 Jan 2014

Are you aware of dpic (https://ece.uwaterloo.ca/~aplevich/dpic/) which takes the pic language (from troff) as input, and produces a variety of outputs, including svg?

[gravatar]
Ned Batchelder 11:37 PM on 26 Jan 2014

@Paul, I was not aware of it. It looks incredibly powerful! I will remember it for when I outgrow Cupid. :)

[gravatar]
Carlos Andrés Rocha 1:25 AM on 27 Jan 2014

Nice! I wonder where the name came from? Is it because of the arrows?

TikZ is a similar package that I have used in the past. It is based on LaTex. There are some really nice examples out there:
http://www.texample.net/tikz/examples/tcp-state-machine/

[gravatar]
Lawrence 7:38 PM on 27 Jan 2014

If you don't want to write a svg generator then look at SvgWrite
http://packages.python.org/svgwrite/ You would still have to create the specifics on how you want the figures but the python to svg is already written and tested. More example svgwrite python programs and svg are at
https://docs.google.com/folder/d/0BwFQiTKfux0qY1Y2d1hRdndtSEk/edit

[gravatar]
Ned Batchelder 10:13 PM on 27 Jan 2014

@Lawrence: I neglected to mention it, but Cupid uses svgwrite!

[gravatar]
ridesthebus 2:31 AM on 29 Jan 2014

Have you tried diagrams[1]?

[1]: http://projects.haskell.org/diagrams/gallery.html

Add a comment:

name
email
Ignore this:
not displayed and no spam.
Leave this empty:
www
not searched.
 
Name and either email or www are required.
Don't put anything here:
Leave this empty:
URLs auto-link and some tags are allowed: <a><b><i><p><br><pre>.