Home | History | Annotate | Download | only in drawing
      1 """
      2 *****
      3 Pydot
      4 *****
      5 
      6 Import and export NetworkX graphs in Graphviz dot format using pydot.
      7 
      8 Either this module or nx_pygraphviz can be used to interface with graphviz.
      9 
     10 See Also
     11 --------
     12 Pydot: http://code.google.com/p/pydot/
     13 Graphviz:          http://www.research.att.com/sw/tools/graphviz/
     14 DOT Language:  http://www.graphviz.org/doc/info/lang.html
     15 """
     16 #    Copyright (C) 2004-2013 by
     17 #    Aric Hagberg <hagberg (at] lanl.gov>
     18 #    Dan Schult <dschult (at] colgate.edu>
     19 #    Pieter Swart <swart (at] lanl.gov>
     20 #    All rights reserved.
     21 #    BSD license.
     22 from networkx.utils import open_file, make_str
     23 import networkx as nx
     24 __author__ = """Aric Hagberg (aric.hagberg (at] gmail.com)"""
     25 __all__ = ['write_dot', 'read_dot', 'graphviz_layout', 'pydot_layout',
     26            'to_pydot', 'from_pydot']
     27 
     28 @open_file(1,mode='w')
     29 def write_dot(G,path):
     30     """Write NetworkX graph G to Graphviz dot format on path.
     31 
     32     Path can be a string or a file handle.
     33     """
     34     try:
     35         import pydot
     36     except ImportError:
     37         raise ImportError("write_dot() requires pydot",
     38                           "http://code.google.com/p/pydot/")
     39     P=to_pydot(G)
     40     path.write(P.to_string())
     41     return
     42 
     43 @open_file(0,mode='r')
     44 def read_dot(path):
     45     """Return a NetworkX MultiGraph or MultiDiGraph from a dot file on path.
     46 
     47     Parameters
     48     ----------
     49     path : filename or file handle
     50 
     51     Returns
     52     -------
     53     G : NetworkX multigraph
     54         A MultiGraph or MultiDiGraph.
     55 
     56     Notes
     57     -----
     58     Use G=nx.Graph(nx.read_dot(path)) to return a Graph instead of a MultiGraph.
     59     """
     60     try:
     61         import pydot
     62     except ImportError:
     63         raise ImportError("read_dot() requires pydot",
     64                           "http://code.google.com/p/pydot/")
     65 
     66     data=path.read()
     67     P=pydot.graph_from_dot_data(data)
     68     return from_pydot(P)
     69 
     70 def from_pydot(P):
     71     """Return a NetworkX graph from a Pydot graph.
     72 
     73     Parameters
     74     ----------
     75     P : Pydot graph
     76       A graph created with Pydot
     77 
     78     Returns
     79     -------
     80     G : NetworkX multigraph
     81         A MultiGraph or MultiDiGraph.
     82 
     83     Examples
     84     --------
     85     >>> K5=nx.complete_graph(5)
     86     >>> A=nx.to_pydot(K5)
     87     >>> G=nx.from_pydot(A) # return MultiGraph
     88     >>> G=nx.Graph(nx.from_pydot(A)) # make a Graph instead of MultiGraph
     89 
     90     """
     91     if P.get_strict(None): # pydot bug: get_strict() shouldn't take argument
     92         multiedges=False
     93     else:
     94         multiedges=True
     95 
     96     if P.get_type()=='graph': # undirected
     97         if multiedges:
     98             create_using=nx.MultiGraph()
     99         else:
    100             create_using=nx.Graph()
    101     else:
    102         if multiedges:
    103             create_using=nx.MultiDiGraph()
    104         else:
    105             create_using=nx.DiGraph()
    106 
    107     # assign defaults
    108     N=nx.empty_graph(0,create_using)
    109     N.name=P.get_name()
    110 
    111     # add nodes, attributes to N.node_attr
    112     for p in P.get_node_list():
    113         n=p.get_name().strip('"')
    114         if n in ('node','graph','edge'):
    115             continue
    116         N.add_node(n,**p.get_attributes())
    117 
    118     # add edges
    119     for e in P.get_edge_list():
    120         u=e.get_source().strip('"')
    121         v=e.get_destination().strip('"')
    122         attr=e.get_attributes()
    123         N.add_edge(u,v,**attr)
    124 
    125     # add default attributes for graph, nodes, edges
    126     N.graph['graph']=P.get_attributes()
    127     try:
    128         N.graph['node']=P.get_node_defaults()[0]
    129     except:# IndexError,TypeError:
    130         N.graph['node']={}
    131     try:
    132         N.graph['edge']=P.get_edge_defaults()[0]
    133     except:# IndexError,TypeError:
    134         N.graph['edge']={}
    135     return N
    136 
    137 def to_pydot(N, strict=True):
    138     """Return a pydot graph from a NetworkX graph N.
    139 
    140     Parameters
    141     ----------
    142     N : NetworkX graph
    143       A graph created with NetworkX
    144 
    145     Examples
    146     --------
    147     >>> K5=nx.complete_graph(5)
    148     >>> P=nx.to_pydot(K5)
    149 
    150     Notes
    151     -----
    152 
    153     """
    154     try:
    155         import pydot
    156     except ImportError:
    157         raise ImportError('to_pydot() requires pydot: '
    158                           'http://code.google.com/p/pydot/')
    159 
    160     # set Graphviz graph type
    161     if N.is_directed():
    162         graph_type='digraph'
    163     else:
    164         graph_type='graph'
    165     strict=N.number_of_selfloops()==0 and not N.is_multigraph()
    166 
    167     name = N.graph.get('name')
    168     graph_defaults=N.graph.get('graph',{})
    169     if name is None:
    170         P = pydot.Dot(graph_type=graph_type,strict=strict,**graph_defaults)
    171     else:
    172         P = pydot.Dot('"%s"'%name,graph_type=graph_type,strict=strict,
    173                       **graph_defaults)
    174     try:
    175         P.set_node_defaults(**N.graph['node'])
    176     except KeyError:
    177         pass
    178     try:
    179         P.set_edge_defaults(**N.graph['edge'])
    180     except KeyError:
    181         pass
    182 
    183     for n,nodedata in N.nodes_iter(data=True):
    184         str_nodedata=dict((k,make_str(v)) for k,v in nodedata.items())
    185         p=pydot.Node(make_str(n),**str_nodedata)
    186         P.add_node(p)
    187 
    188     if N.is_multigraph():
    189         for u,v,key,edgedata in N.edges_iter(data=True,keys=True):
    190             str_edgedata=dict((k,make_str(v)) for k,v in edgedata.items())
    191             edge=pydot.Edge(make_str(u),make_str(v),key=make_str(key),**str_edgedata)
    192             P.add_edge(edge)
    193 
    194     else:
    195         for u,v,edgedata in N.edges_iter(data=True):
    196             str_edgedata=dict((k,make_str(v)) for k,v in edgedata.items())
    197             edge=pydot.Edge(make_str(u),make_str(v),**str_edgedata)
    198             P.add_edge(edge)
    199     return P
    200 
    201 
    202 def pydot_from_networkx(N):
    203     """Create a Pydot graph from a NetworkX graph."""
    204     from warnings import warn
    205     warn('pydot_from_networkx is replaced by to_pydot', DeprecationWarning)
    206     return to_pydot(N)
    207 
    208 def networkx_from_pydot(D, create_using=None):
    209     """Create a NetworkX graph from a Pydot graph."""
    210     from warnings import warn
    211     warn('networkx_from_pydot is replaced by from_pydot',
    212          DeprecationWarning)
    213     return from_pydot(D)
    214 
    215 def graphviz_layout(G,prog='neato',root=None, **kwds):
    216     """Create node positions using Pydot and Graphviz.
    217 
    218     Returns a dictionary of positions keyed by node.
    219 
    220     Examples
    221     --------
    222     >>> G=nx.complete_graph(4)
    223     >>> pos=nx.graphviz_layout(G)
    224     >>> pos=nx.graphviz_layout(G,prog='dot')
    225 
    226     Notes
    227     -----
    228     This is a wrapper for pydot_layout.
    229     """
    230     return pydot_layout(G=G,prog=prog,root=root,**kwds)
    231 
    232 
    233 def pydot_layout(G,prog='neato',root=None, **kwds):
    234     """Create node positions using Pydot and Graphviz.
    235 
    236     Returns a dictionary of positions keyed by node.
    237 
    238     Examples
    239     --------
    240     >>> G=nx.complete_graph(4)
    241     >>> pos=nx.pydot_layout(G)
    242     >>> pos=nx.pydot_layout(G,prog='dot')
    243     """
    244     try:
    245         import pydot
    246     except ImportError:
    247         raise ImportError('pydot_layout() requires pydot ',
    248                           'http://code.google.com/p/pydot/')
    249 
    250     P=to_pydot(G)
    251     if root is not None :
    252         P.set("root",make_str(root))
    253 
    254     D=P.create_dot(prog=prog)
    255 
    256     if D=="":  # no data returned
    257         print("Graphviz layout with %s failed"%(prog))
    258         print()
    259         print("To debug what happened try:")
    260         print("P=pydot_from_networkx(G)")
    261         print("P.write_dot(\"file.dot\")")
    262         print("And then run %s on file.dot"%(prog))
    263         return
    264 
    265     Q=pydot.graph_from_dot_data(D)
    266 
    267     node_pos={}
    268     for n in G.nodes():
    269         pydot_node = pydot.Node(make_str(n)).get_name().encode('utf-8')
    270         node=Q.get_node(pydot_node)
    271 
    272         if isinstance(node,list):
    273             node=node[0]
    274         pos=node.get_pos()[1:-1] # strip leading and trailing double quotes
    275         if pos != None:
    276             xx,yy=pos.split(",")
    277             node_pos[n]=(float(xx),float(yy))
    278     return node_pos
    279 
    280 # fixture for nose tests
    281 def setup_module(module):
    282     from nose import SkipTest
    283     try:
    284         import pydot
    285         import dot_parser
    286     except:
    287         raise SkipTest("pydot not available")
    288