digraph.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2015 Airbus
00004 # Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
00005 #
00006 # Licensed under the Apache License, Version 2.0 (the "License");
00007 # you may not use this file except in compliance with the License.
00008 # You may obtain a copy of the License at
00009 #
00010 #   http://www.apache.org/licenses/LICENSE-2.0
00011 #
00012 # Unless required by applicable law or agreed to in writing, software
00013 # distributed under the License is distributed on an "AS IS" BASIS,
00014 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 # See the License for the specific language governing permissions and
00016 # limitations under the License.
00017 
00018 
00019 import subprocess
00020 from xml.etree import ElementTree as ET
00021 from airbus_docgen.common.color import RgbColor
00022 from airbus_docgen.digraph.shape import SHAPE
00023 from airbus_docgen.digraph.arrow import ARROWHEAD
00024 from airbus_docgen.digraph.spline import SPLINE
00025 from airbus_docgen.digraph.style import STYLE
00026 from airbus_docgen.digraph.text import ALIGN
00027 
00028 from airbus_docgen.common import html
00029 
00030 def indent(elem, level=0):
00031     
00032     i = "\n" + level*"  "
00033     if len(elem):
00034         if not elem.text or not elem.text.strip():
00035             elem.text = i + "  "
00036         if not elem.tail or not elem.tail.strip():
00037             elem.tail = i
00038         for elem in elem:
00039             indent(elem, level+1)
00040         if not elem.tail or not elem.tail.strip():
00041             elem.tail = i
00042     else:
00043         if level and (not elem.tail or not elem.tail.strip()):
00044             elem.tail = i
00045 
00046 class TD(ET.Element):
00047     
00048     ALIGN="ALIGN"
00049     BALIGN="BALIGN"
00050     BGCOLOR="BGCOLOR"
00051     BORDER="BORDER"
00052     CELLPADDING="CELLPADDING"
00053     CELLSPACING="CELLSPACING"
00054     COLOR="COLOR"
00055     COLSPAN="COLSPAN"
00056     FIXEDSIZE="FIXEDSIZE"
00057     GRADIENTANGLE="GRADIENTANGLE"
00058     HEIGHT="HEIGHT"
00059     HREF="HREF"
00060     ID="ID"
00061     PORT="PORT"
00062     ROWSPAN="ROWSPAN"
00063     SIDES="SIDES"
00064     STYLE="STYLE"
00065     TARGET="TARGET"
00066     TITLE="TITLE"
00067     TOOLTIP="TOOLTIP"
00068     VALIGN="VALIGN"
00069     WIDTH="WIDTH"
00070     
00071     __ATTRIB_KEYS__ = [ALIGN,BALIGN,BGCOLOR,BORDER,CELLPADDING,CELLSPACING,
00072                        COLOR,COLSPAN,FIXEDSIZE,GRADIENTANGLE,HEIGHT,HREF,
00073                        ID,PORT,ROWSPAN,SIDES,STYLE,TARGET,TITLE,TOOLTIP,
00074                        VALIGN, WIDTH]
00075     
00076     def __init__(self):
00077         ET.Element.__init__(self, tag=html.Tables.td)
00078         
00079     def setAttrib(self, key, value):
00080         if key in self.__ATTRIB_KEYS__:
00081             self.set(key, str(value).replace("\"", ""))
00082         else:
00083             raise Exception("Invalid key attribute from '%s' !"%str(key))
00084         
00085     def setText(self, text):
00086         self.text = text
00087         
00088     def __str__(self):
00089         return ET.tostring(self, encoding="us-ascii", method="xml")
00090 
00091 class TR(ET.Element):
00092     
00093     def __init__(self, td=None):
00094         ET.Element.__init__(self, tag=html.Tables.tr)# "TR")
00095         
00096         if td is not None:
00097             self.addTD(td)
00098     
00099     def addTD(self, td):
00100         self.append(td)
00101         
00102     def addTDs(self, tds):
00103         for td in tds:
00104             self.addTD(td)
00105         
00106     def __str__(self):
00107         return ET.tostring(self, encoding="us-ascii", method="xml")
00108 
00109 class TABLE(ET.Element):
00110     
00111     ALIGN="ALIGN"
00112     BGCOLOR="BGCOLOR"
00113     BORDER="BORDER"
00114     CELLBORDER="CELLBORDER"
00115     CELLPADDING="CELLPADDING"
00116     CELLSPACING="CELLSPACING"
00117     COLOR="COLOR"
00118     COLUMNS="COLUMNS"
00119     FIXEDSIZE="FIXEDSIZE"
00120     GRADIENTANGLE="GRADIENTANGLE"
00121     HEIGHT="HEIGHT"
00122     HREF="HREF"
00123     ID="ID"
00124     PORT="PORT"
00125     ROWS="ROWS"
00126     SIDES="SIDES"
00127     STYLE="STYLE"
00128     TARGET="TARGET"
00129     TITLE="TITLE"
00130     TOOLTIP="TOOLTIP"
00131     VALIGN="VALIGN"
00132     WIDTH="WIDTH"
00133     
00134     __ATTRIB_KEYS__ = [ALIGN, BGCOLOR, BORDER, CELLBORDER, CELLPADDING,
00135                        CELLSPACING, COLOR, COLUMNS, FIXEDSIZE,
00136                        GRADIENTANGLE,  HEIGHT, HREF, ID, PORT, ROWS, SIDES,
00137                        STYLE, TARGET, TITLE, TOOLTIP, VALIGN, WIDTH]
00138     
00139     def __init__(self):
00140         ET.Element.__init__(self, tag=html.Tables.table)#"TABLE")
00141         
00142     def setAttrib(self, key, value):
00143         if key in self.__ATTRIB_KEYS__:
00144             self.set(key, str(value).replace("\"", ""))
00145         else:
00146             raise Exception("Invalid key attribute from '%s' !"%str(key))
00147     
00148     def addTR(self, tr):
00149         self.append(tr)
00150         
00151     def addTRs(self, tr_list):
00152         for tr in tr_list:
00153             self.addTR(tr)
00154         
00155     def __str__(self):
00156         indent(self)
00157         return ET.tostring(self, encoding="us-ascii", method="xml")
00158         
00159 class NODE:
00160     
00161     MARGIN    = "margin" 
00162     FONTCOLOR = "fontcolor" 
00163     FONTSIZE  = "fontsize" 
00164     WIDTH     = "width" 
00165     SHAPE     = "shape" 
00166     STYLE     = "style"
00167     LABEL     = "label"
00168     DIR       = "dir"
00169     COLOR     = 'color'
00170     
00171     def __init__(self, node_name=""):
00172         self._node_name = NODE.formatNodeName(node_name)
00173         self._attribs = ""
00174         
00175     @staticmethod
00176     def formatNodeName(name):
00177         return name.replace('.', '_').replace('-','_').replace('/','_')
00178         
00179     def getName(self):
00180         return self._node_name
00181         
00182     def setAttrib(self, key, value):
00183         self._attribs += ' %s=%s'%(key, str(value))
00184     
00185     def setHtml(self, html):
00186         self._attribs += ' label=<%s>'%str(html)
00187         
00188     def setLabel(self, label):
00189         self._attribs += ' label="%s"'%str(label)
00190     
00191     def __str__(self):
00192         return "%s [%s];\n"%(self._node_name, self._attribs)
00193     
00194 class Edge:
00195     
00196     DIR = "dir"
00197     
00198     def __init__(self, key=None, value=None):
00199         self._attrib = "["
00200         
00201         if key is not None and value is not None:
00202             self.setAttrib(key, value)
00203     
00204     def setAttrib(self, key, value):
00205         self._attrib += "%s=%s "%(key, str(value))
00206         
00207         return self
00208         
00209     def __str__(self):
00210         return "%s];\n"%self._attrib
00211 
00212 class Digraph:
00213     
00214     ROOT = 'root'
00215     DIGRAPH  = "digraph"
00216     SUBGRAPH = "subgraph"
00217     STYLE = "style"
00218     COLOR = "color"
00219     RANKDIR = "rankdir"
00220     SPLINE = "splines"
00221     NODESEP = 'nodesep'
00222     
00223     def __init__(self, graph_name, graph_type=DIGRAPH):
00224         self._graph_name = graph_name
00225         self._graph_type = graph_type
00226         self._attribs = ""
00227         self._node_attribs = ""
00228         self._root_node = ""
00229         self._nodes = ""
00230         self._connections = ""
00231         self._dot_src_dir = None
00232         
00233     def getType(self):
00234         return self._graph_type
00235         
00236     def getName(self):
00237         return self._graph_name
00238         
00239     def setAttrib(self, key, value):
00240         self._attribs += '%s=%s;\n'%(key,str(value))
00241         
00242     def setLabel(self, label):
00243         self._attribs += 'label="%s";\n'%label
00244     
00245     def addNode(self, node):
00246         if isinstance(node, NODE):
00247             self._nodes += str(node)
00248         else:
00249             raise Exception("Bad class instance from NODE !")
00250         
00251     def addRootNode(self,node):
00252         self._root_node = node.getName()
00253         self.addNode(node)
00254         
00255     def getRootNode(self):
00256         return self._root_node
00257         
00258     def addSubGraph(self, graph):
00259         if graph.getType() == self.SUBGRAPH:
00260             self._nodes += str(graph)
00261         else:
00262             raise Exception("Bad class instance from SubGraph !")
00263     
00264     def _resolve_(self, obj):
00265         
00266         obj_str = ""
00267         
00268         if isinstance(obj, NODE) or isinstance(obj, Digraph):
00269             obj_str = obj.getName()
00270         elif isinstance(obj, dict):
00271             k = obj.keys()[0]
00272             v = obj.values()[0]
00273             obj_str = "%s:%s"%(str(k.getName()),v)
00274         else:
00275             obj_str = obj
00276             
00277         return obj_str
00278         
00279     def connect(self, node1, node2, edge_attrib=None):
00280         
00281         if node1 is None or node2 is None:
00282             return
00283         
00284         if edge_attrib is not None:
00285             self._connections += "%s -> %s %s"%(self._resolve_(node1),
00286                                                 self._resolve_(node2),
00287                                                 str(edge_attrib))
00288         else:
00289             self._connections +="%s -> %s;\n"%(self._resolve_(node1),
00290                                                self._resolve_(node2))
00291         
00292     def connectRank(self, key, nodes=[], edge=None):
00293         
00294         if nodes is None:
00295             return
00296         
00297         if len(nodes) >= 2:
00298             
00299             self._connections += "{rank=%s;"%key
00300             
00301             begin = " "
00302             for i in range(1, len(nodes)):
00303                 self._connections += "%s%s"%(begin,self._resolve_(nodes[i-1]))
00304                 self._connections += " -> %s"%self._resolve_(nodes[i])
00305                 begin = " -> "
00306             
00307             self._connections += ";}\n"
00308         
00309     def __str__(self):
00310         
00311         if self._node_attribs != "":
00312             self._node_attribs = "node [%s]\n"%self._node_attribs
00313         
00314         graph_str = "%s %s {\n%s%s%s%s}\n"%(
00315                                 self._graph_type,
00316                                 self._graph_name,
00317                                 self._attribs,
00318                                 self._node_attribs,
00319                                 self._nodes,
00320                                 self._connections)
00321         
00322         return graph_str
00323     
00324     def saveDot(self, output):
00325         
00326         if '.dot' not in output:
00327             output+=".dot"
00328             
00329         self._dot_src_dir = output
00330         
00331         with open(self._dot_src_dir,'w') as f_dot:
00332             f_dot.write(self.__str__())
00333         
00334     
00335     def dotToPng(self, output, rm_dot=False):
00336         
00337         if '.png' not in output:
00338             output+=".png"
00339         
00340         if self._dot_src_dir is None:
00341             raise Exception("You must save dot before converting to png file !")
00342         
00343         cmd=["dot", "-Tpng", self._dot_src_dir, "-o", output]
00344         subprocess.Popen(cmd)
00345         
00346     def dotToPs(self, output, rm_dot=False):
00347         
00348         if '.ps' not in output:
00349             output+=".ps"
00350         
00351         if self._dot_src_dir is None:
00352             raise Exception("You must save dot before converting to ps file !")
00353         
00354         cmd=["dot", "-Tps", self._dot_src_dir, "-o", output]
00355         subprocess.Popen(cmd)
00356         
00357 
00358 def digraph_test():
00359     
00360     digraph = Digraph("unittest")
00361     digraph.setAttrib('rankdir','LR')
00362     
00363     head = NODE("node")
00364     head.setAttrib(NODE.SHAPE, SHAPE.Plaintext)
00365     digraph.addNode(head)
00366     
00367     node1 = NODE("node_test")
00368     node1.setAttrib(NODE.SHAPE, SHAPE.Plaintext)
00369     node1.setAttrib(NODE.FONTCOLOR, RgbColor.Red)
00370     
00371     table = TABLE()
00372     table.setAttrib(TABLE.BORDER, 0)
00373     table.setAttrib(TABLE.CELLBORDER, 1)
00374     table.setAttrib(TABLE.CELLSPACING, 0)
00375     
00376     tr = TR()
00377     
00378     td = TD()
00379     td.setAttrib(TD.PORT, "input")
00380     td.setText("test")
00381     
00382     td2 = TD()
00383     td2.setAttrib(TD.PORT, "output")
00384     td2.setAttrib(TD.COLOR, RgbColor.Blue)
00385     td2.setText("test2")
00386     
00387     tr.addTD(td)
00388     tr.addTD(td2)
00389     
00390     table.addTR(tr)
00391     
00392     node1.setHtml(table)
00393     
00394     node2 = NODE("node_test_2")
00395     #{
00396     table = TABLE()
00397     table.setAttrib(TABLE.BORDER, 0)
00398     table.setAttrib(TABLE.CELLBORDER, 10)
00399     
00400     tr = TR()
00401     td = TD()
00402     td.setAttrib(TD.PORT, "input")
00403     td.setText("test")
00404     td2 = TD()
00405     td2.setAttrib(TD.PORT, "output")
00406     td2.setAttrib(TD.COLOR, RgbColor.Red)
00407     td2.setText("test3")
00408     
00409     tr.addTD(td)
00410     tr.addTD(td2)
00411     
00412     table.addTR(tr)
00413     
00414     node2.setHtml(table)
00415     
00416     node3 = NODE("test11")
00417     node3.setLabel("Node for testing none html label !")
00418     node3.setAttrib(NODE.SHAPE, SHAPE.Ellipse)
00419     
00420     digraph.addNode(node1)
00421     digraph.addNode(node2)
00422     digraph.addNode(node3)
00423     
00424 #     digraph.connect({node1:"output"}, {node2:"input"})
00425 #     digraph.connect({node2:"output"}, {node1:"input"})
00426 #     digraph.connect(node3, node1)
00427     digraph.connectRank("same", [node1, node2, node3])
00428     
00429     print str(digraph)
00430     
00431     digraph.saveDot("/tmp/digraph_test.dot")
00432     digraph.dotToPng("/tmp/digraph_test.png")
00433 
00434 def getSubGraph(name):
00435     
00436     subgraph = Digraph(name, Digraph.SUBGRAPH)
00437     subgraph.setLabel(name)
00438     subgraph.setAttrib(Digraph.STYLE, "filled")
00439     subgraph.setAttrib(Digraph.COLOR, RgbColor.Cyan)
00440     
00441     node1 = NODE("node1_%s"%name)
00442     node1.setAttrib(NODE.SHAPE, SHAPE.Diamond)
00443     subgraph.addNode(node1)
00444     
00445     node2 = NODE("node2_%s"%name)
00446     node2.setAttrib(NODE.SHAPE, SHAPE.House)
00447     subgraph.addNode(node2)
00448     
00449     subgraph.connect(node1, node2)
00450     
00451     return subgraph
00452     
00453 if __name__ == '__main__':
00454     
00455     digraph_test()
00456     
00457 #     graph = Digraph("root_graph")
00458 #     graph.setLabel("Root Graph")
00459 #     
00460 #     root = NODE("root")
00461 #     root.setLabel("Root into root_graph !")
00462 #     root.setAttrib(NODE.SHAPE, SHAPE.Doubleoctagon)
00463 #     root.setAttrib(NODE.FONTCOLOR, RGB.Red)
00464 #     graph.addNode(root)
00465 #     
00466 #     sub1 = getSubGraph("sub1")
00467 #     sub2 = getSubGraph("sub2")
00468 #     
00469 #     graph.addSubGraph(sub1)
00470 #     graph.addSubGraph(sub2)
00471 #     
00472 #     graph.connect(root , "node1_sub1")
00473 #     graph.connect(root , "node1_sub2")
00474 #     
00475 #     print str(graph)
00476 #     
00477 #     graph.saveDot("/tmp/subgraph_test.dot")
00478 #     graph.dotToPng("/tmp/subgraph_test.png")


airbus_docgen
Author(s): Matignon Martin
autogenerated on Thu Jun 6 2019 17:59:08