4

Does anyone know how to add an edge between two subgraphs (clusters) in pydot?

callgraph = pydot.Dot(graph_type='digraph',fontname="Verdana")
cluster_foo=pydot.Cluster('foo',label='foo')
cluster_foo.add_node(pydot.Node('foo_method_1',label='method_1'))
callgraph.add_subgraph(cluster_foo)

cluster_bar=pydot.Cluster('bar',label='Component1')
cluster_bar.add_node(pydot.Node('bar_method_a'))
callgraph.add_subgraph(cluster_bar)

I tried:

callgraph.add_edge(pydot.Edge("foo","bar"))

but doesn't work. It just creates two more nodes labeled "foo" and "bar" in the initial graph and puts an edge between them!

Can anyone help, please?

TylerH
  • 20,799
  • 66
  • 75
  • 101
user880219
  • 41
  • 1
  • 3

1 Answers1

6
  • Graphviz demands that an edge be between nodes in the 2 clusters.
  • Add graph parameter compound='true'.
  • Use edge parameters lhead= and ltail=.

So your code would become:

callgraph = pydot.Dot(graph_type='digraph', fontname="Verdana", compound='true')


cluster_foo=pydot.Cluster('foo',label='foo')
callgraph.add_subgraph(cluster_foo)

node_foo = pydot.Node('foo_method_1',label='method_1')
cluster_foo.add_node(node_foo)


cluster_bar=pydot.Cluster('bar',label='Component1')
callgraph.add_subgraph(cluster_bar)

node_bar = pydot.Node('bar_method_a')
cluster_bar.add_node(node_bar)


callgraph.add_edge(pydot.Edge(node_foo, node_bar, ltail=cluster_foo.get_name(), lhead=cluster_bar.get_name()))
Nanno Langstraat
  • 1,335
  • 10
  • 8
  • The `Edge()` call seems to be slightly incorrect: It takes the edge names `foo_method_1`, `bar_method_a`, rather than the node instances. Your version will lead to unhandled runtime errors in pydot when it tries to convert to dot files. – Michel Müller Jun 01 '15 at 08:34
  • 1
    It turns out that pydot is slightly under-documented on this subject. You're right, the `pydot.Edge()` docstring describes it as taking plain node name strings, and does not mention `pydot.Node` objects at all. However, the actual `Edge` class does contain code to accept `Node` objects as an alternative to plain strings. The relevant code in pydot.py that handles them is: `if isinstance(src, Node): src = src.get_name() if isinstance(dst, Node): dst = dst.get_name()` So, the answer code above does function after all. (It's been tested back then, and again just now just to make sure.) – Nanno Langstraat Jun 02 '15 at 15:26
  • In general I am very strict about not going beyond the promises made by an API's documentation, so: thank you for noticing this and speaking up! That said, in this specific case I am pretty sure that it's only a missing bit of documentation, and that pydot's support of `Edge(Node(...))` is an officially intended stable feature. For example, otherwise `Edge()` and `Graph.add_node()` would have confusing conflicting call conventions. And both of the 2 top Google hits for "pydot example" actually use this feature of pydot. – Nanno Langstraat Jun 02 '15 at 16:00
  • In my case it wasn't supported, i.e. it gave a runtime exception as I wrote - this may be related to the version of pydot I'm using: 1.0.28. Which one do you have (`pip freeze | grep pydot`)? – Michel Müller Jun 03 '15 at 00:51
  • Back in 2011 I likely tested with 1.0.2 or 1.0.4. Just now I tested with Ubuntu 15.04's `python-pydot` package, which is 1.0.28. According to Github, the feature was introduced in 1.0.2: https://github.com/erocarrera/pydot/commit/842173cab55b57b0dbb753766edfa6c9d6842e87 . When I test it with pip: `virtualenv test-pydot-B; source test-pydot-B/bin/activate; pip install pydot; pip freeze` I get `pydot==1.0.2`. The feature also works when the package is installed via that route. – Nanno Langstraat Jun 03 '15 at 09:31
  • When I force pip to install 1.0.28 by following: http://stackoverflow.com/questions/22868777/why-wont-pip-install-the-current-version-of-a-package (on my throwaway computer, for security), the `Edge(Node(...), ...)` feature works there too. However, that SO question indicates that the packaging/distribution of pydot may currently be not quite optimal. PyPI also still links to code.google.com, which is obsolete (redirects to Github). Can you perhaps double-check that the actual contents of the pydot.py file on your computer matches with a copy of the 1.0.28 code that you download independently? – Nanno Langstraat Jun 03 '15 at 09:51
  • Ok I've tested again and I can't reproduce it anymore. I think it must have been related to some other bug I was fixing, but I didn't make enough commits to find the state where it broke. I remember that I got an exception message from within pydot that was along the lines of a type error (string expected, instance found) for the node argument(s). But when I pass nodes with the examples I have now it works fine - sorry. – Michel Müller Jun 03 '15 at 13:07
  • No problem; I've submitted a small documentation update to Ero Carrera and also to davidvilla's fork, so hopefully we all come out ahead in the end. (My only worry is that Pydot shows multiple signs of not being maintained any longer, and seems to have fragmented into at least 3 forks with significant changes. But we'll see.) – Nanno Langstraat Jun 16 '15 at 14:41