dot_to_qt.py
Go to the documentation of this file.
00001 """
00002 Software License Agreement (BSD License)
00003 
00004 Copyright (c) 2008, Willow Garage, Inc.
00005 All rights reserved.
00006 
00007 Redistribution and use in source and binary forms, with or without
00008 modification, are permitted provided that the following conditions
00009 are met:
00010 
00011 *  Redistributions of source code must retain the above copyright
00012    notice, this list of conditions and the following disclaimer.
00013 *  Redistributions in binary form must reproduce the above
00014    copyright notice, this list of conditions and the following
00015    disclaimer in the documentation and/or other materials provided
00016    with the distribution.
00017 *  Neither the name of Willow Garage, Inc. nor the names of its
00018    contributors may be used to endorse or promote products derived
00019    from this software without specific prior written permission.
00020 
00021 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 POSSIBILITY OF SUCH DAMAGE
00033 
00034 Note: This is modified version by Cogniteam
00035 """
00036 
00037 from python_qt_binding.QtCore import QPointF, QRectF
00038 from python_qt_binding.QtGui import QColor
00039 from pydot import *
00040 
00041 POINTS_PER_INCH = 72
00042 
00043 
00044 # hack required by pydot
00045 def get_unquoted(item, name):
00046     value = item.get(name)
00047     if value is None:
00048         return None
00049     try:
00050         return value.strip('"\n"')
00051     except AttributeError:
00052         # not part of the string family
00053         return value
00054 
00055 
00056 # hack required to show properly long labels of custom shapes
00057 def clean_line_separator(attribute):
00058     return attribute.replace('\\', '')
00059 
00060 # approximately, for workarounds (TODO: get this from dotfile somehow)
00061 LABEL_HEIGHT = 30
00062 
00063 
00064 # Class generating Qt Elements from doctcode
00065 class DotToQtGenerator():
00066 
00067     def __init__(self, factory):
00068         self._factory = factory
00069 
00070     def get_node_item_for_node(self, node):
00071         """
00072         returns a pyqt NodeItem object, or None in case of error or invisible style
00073         """
00074         # let pydot imitate pygraphviz api
00075         attr = {}
00076         for name in node.get_attributes().iterkeys():
00077             value = get_unquoted(node, name)
00078             attr[name] = value
00079         obj_dic = node.__getattribute__("obj_dict")
00080         for name in obj_dic:
00081             if name not in ['attributes', 'parent_graph'] and obj_dic[name] is not None:
00082                 attr[name] = get_unquoted(obj_dic, name)
00083         node.attr = attr
00084 
00085         if 'style' in node.attr:
00086             if node.attr['style'] == 'invis':
00087                 return None
00088 
00089         color = QColor(node.attr['color']) if 'color' in node.attr else None
00090         name = None
00091         if 'label' in node.attr:
00092             name = node.attr['label']
00093         elif 'name' in node.attr:
00094             name = node.attr['name']
00095         else:
00096             print("Error, no label defined for node with attr: %s" % node.attr)
00097             return None
00098 
00099         if name is None:
00100             # happens on Lucid pygraphviz version
00101             print("Error, label is None for node %s, pygraphviz version may be too old." % node)
00102         else:
00103             name = name.decode('string_escape')
00104 
00105         # decrease rect by one so that edges do not reach inside
00106         bb_width = len(name) / 5
00107         if 'width' in node.attr:
00108             bb_width = node.attr['width']
00109 
00110         bb_height = 1.0
00111         if 'width' in node.attr:
00112             bb_height = node.attr['height']
00113 
00114         pos = (0, 0)
00115         if 'pos' in node.attr:
00116             pos = node.attr['pos'].split(',')
00117 
00118         bounding_box = QRectF(0, 0, POINTS_PER_INCH * float(bb_width) - 1.0, POINTS_PER_INCH * float(bb_height) - 1.0)
00119 
00120         bounding_box.moveCenter(QPointF(float(pos[0]), -float(pos[1])))
00121 
00122         pen_width = self._factory.pen_width()
00123         if 'penwidth' in node.attr:
00124             pen_width = float(node.attr['penwidth'])
00125 
00126         url = node.attr['URL'] if 'URL' in node.attr else 'N/A'
00127         node_item = self._factory.create_node(bounding_box=bounding_box,
00128                                               shape=node.attr.get('shape', 'box'),
00129                                               label=name,
00130                                               url=url,
00131                                               penwidth=pen_width)
00132         return node_item
00133 
00134     def add_edge_item_for_edge(self, edge, nodes, edges):
00135         """
00136         adds EdgeItem by data in edge to edges
00137         :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00138         """
00139         # let pydot imitate pygraphviz api
00140         attr = {}
00141         for name in edge.get_attributes().iterkeys():
00142             value = get_unquoted(edge, name)
00143             attr[name] = value
00144         edge.attr = attr
00145 
00146         # cleans '\\\n' from positions, hack
00147         edge.attr['pos'] = clean_line_separator(edge.attr['pos'])
00148 
00149         if 'style' in edge.attr:
00150             if edge.attr['style'] == 'invis':
00151                 return
00152 
00153         label = edge.attr.get('label', None)
00154         label_pos = edge.attr.get('lp', None)
00155         label_center = None
00156         if label_pos is not None:
00157             label_pos = label_pos.split(',')
00158             label_center = QPointF(float(label_pos[0]), -float(label_pos[1]))
00159 
00160         # try pydot, fallback for pygraphviz
00161         source_node = edge.get_source() if hasattr(edge, 'get_source') else edge[0]
00162 
00163         destination_node = str(edge.get_destination() if hasattr(edge, 'get_destination') else edge[1])
00164 
00165         # create edge with from-node and to-node
00166         edge_pos = (0, 0)
00167         if 'pos' in edge.attr:
00168             edge_pos = edge.attr['pos']
00169         if label is not None:
00170             label = label.decode('string_escape')
00171 
00172         edge_item = self._factory.create_edge(spline=edge_pos,
00173                                               label=label,
00174                                               label_center=label_center,
00175                                               from_node=nodes[source_node],
00176                                               to_node=nodes[destination_node])
00177 
00178         if self._factory.same_label_siblings():
00179             if label is None:
00180                 # for sibling detection
00181                 label = "%s_%s" % (source_node, destination_node)
00182             # symmetrically add all sibling edges with same label
00183             if label in edges:
00184                 for sibling in edges[label]:
00185                     edge_item.add_sibling_edge(sibling)
00186                     sibling.add_sibling_edge(edge_item)
00187 
00188         if label not in edges:
00189             edges[label] = []
00190         edges[label].append(edge_item)
00191 
00192     def get_graph_edges(self, graph, nodes, edges):
00193         for subgraph in graph.get_subgraph_list():
00194             self.get_graph_edges(subgraph, nodes, edges)
00195             for edge in subgraph.get_edge_list():
00196                 self.add_edge_item_for_edge(edge, nodes, edges)
00197 
00198         for edge in graph.get_edge_list():
00199             self.add_edge_item_for_edge(edge, nodes, edges)
00200 
00201     def get_cluster_node(self, node):
00202         # let pydot imitate pygraphviz api
00203         attr = {}
00204         for name in node.get_attributes().iterkeys():
00205             value = get_unquoted(node, name)
00206             attr[name] = value
00207         obj_dic = node.__getattribute__("obj_dict")
00208         for name in obj_dic:
00209             if name not in ['nodes', 'attributes', 'parent_graph'] and obj_dic[name] is not None:
00210                 attr[name] = get_unquoted(obj_dic, name)
00211             elif name == 'nodes':
00212                 for key in obj_dic['nodes']['graph'][0]['attributes']:
00213                     attr[key] = get_unquoted(obj_dic['nodes']['graph'][0]['attributes'], key)
00214         node.attr = attr
00215 
00216         name = None
00217         if 'label' in node.attr:
00218             name = node.attr['label']
00219         elif 'name' in node.attr:
00220             name = node.attr['name']
00221         else:
00222             print("Error, no label defined for node with attr: %s" % node.attr)
00223             return None
00224         if name is None:
00225             # happens on Lucid pygraphviz version
00226             print("Error, label is None for node %s, pygraphviz version may be too old." % node)
00227 
00228         bb_width = node.attr['width']
00229         bb_height = node.attr['height']
00230 
00231         bounding_box = QRectF(0, 0, POINTS_PER_INCH * float(bb_width) - 1.0, POINTS_PER_INCH * float(bb_height) - 1.0)
00232         # print bounding_box
00233         pos = (0, 0)
00234         if 'pos' in node.attr:
00235             pos = node.attr['pos'].split(',')
00236         bounding_box.moveCenter(QPointF(float(pos[0]), -float(pos[1])))
00237 
00238         label_pos = QPointF(bounding_box.center().x(), bounding_box.top() + LABEL_HEIGHT / 2)
00239 
00240         color = QColor(node.attr['color']) if 'color' in node.attr else None
00241 
00242         url = node.attr['URL'] if 'URL' in node.attr else 'N/A'
00243         label = node.attr['label'] if 'label' in node.attr else name
00244 
00245         graph_node_item = self._factory.create_node(bounding_box=bounding_box,
00246                                                     shape='box',
00247                                                     label=label,
00248                                                     label_pos=label_pos,
00249                                                     url=url,
00250                                                     cluster=True)
00251 
00252         return graph_node_item
00253 
00254     def get_subgraph_nodes(self, graph, nodes):
00255         # let pydot imitate pygraphviz api
00256         attr = {}
00257         for name in graph.get_attributes().iterkeys():
00258             value = get_unquoted(graph, name)
00259             attr[name] = value
00260         obj_dic = graph.__getattribute__("obj_dict")
00261         for name in obj_dic:
00262             if name not in ['nodes', 'attributes', 'parent_graph'] and obj_dic[name] is not None:
00263                 attr[name] = get_unquoted(obj_dic, name)
00264             elif name == 'nodes':
00265                 for key in obj_dic['nodes']['graph'][0]['attributes']:
00266                     attr[key] = get_unquoted(obj_dic['nodes']['graph'][0]['attributes'], key)
00267         graph.attr = attr
00268 
00269         bb = graph.attr['bb'].strip('"').split(',')
00270         if len(bb) < 4:
00271             # bounding box is empty
00272             return None
00273         bounding_box = QRectF(0, 0, float(bb[2]) - float(bb[0]), float(bb[3]) - float(bb[1]))
00274         if 'lp' in graph.attr:
00275             label_pos = graph.attr['lp'].strip('"').split(',')
00276         else:
00277             label_pos = (float(bb[0]) + (float(bb[2]) - float(bb[0])) / 2,
00278                          float(bb[1]) + (float(bb[3]) - float(bb[1])) - LABEL_HEIGHT / 2)
00279         bounding_box.moveCenter(QPointF(float(bb[0]) + (float(bb[2]) - float(bb[0])) / 2,
00280                                         -float(bb[1]) - (float(bb[3]) - float(bb[1])) / 2))
00281         name = graph.attr.get('label', '')
00282 
00283         color = QColor(graph.attr['color']) if 'color' in graph.attr else None
00284 
00285         url = graph.attr['URL'] if 'URL' in graph.attr else 'N/A'
00286 
00287         graph_node_item = self._factory.create_node(bounding_box=bounding_box,
00288                                                     shape='box',
00289                                                     label=name,
00290                                                     label_pos=QPointF(float(label_pos[0]), -float(label_pos[1])),
00291                                                     url=url,
00292                                                     cluster=True)
00293 
00294         for subgraph in graph.get_subgraph_list():
00295             subgraph_node_item = self.get_subgraph_nodes(subgraph, nodes)
00296             # skip subgraphs with empty bounding boxes
00297             if subgraph_node_item is None:
00298                 continue
00299 
00300             nodes[subgraph.get_name()] = subgraph_node_item
00301             for node in subgraph.get_node_list():
00302                 # hack required by pydot
00303                 if node.get_name() in ('graph', 'node', 'empty'):
00304                     continue
00305                 nodes[node.get_name()] = self.get_node_item_for_node(node)
00306         for node in graph.get_node_list():
00307             # hack required by pydot
00308             if node.get_name() in ('graph', 'node',  'empty'):
00309                 continue
00310             nodes[node.get_name()] = self.get_node_item_for_node(node)
00311 
00312         return graph_node_item
00313 
00314     def get_graph_nodes(self, graph, nodes):
00315 
00316         for subgraph in graph.get_subgraph_list():
00317             subgraph_node_item = self.get_subgraph_nodes(subgraph, nodes)
00318             # skip subgraphs with empty bounding boxes
00319             if subgraph_node_item is None:
00320                 continue
00321 
00322             nodes[subgraph.get_name()] = subgraph_node_item
00323             for node in subgraph.get_node_list():
00324                 # hack required by pydot
00325                 if node.get_name() in ('graph', 'node', 'empty'):
00326                     continue
00327                 nodes[node.get_name()] = self.get_node_item_for_node(node)
00328         for node in graph.get_node_list():
00329             # hack required by pydot
00330             if node.get_name() in ('graph', 'node', 'empty'):
00331                 continue
00332             nodes[node.get_name()] = self.get_node_item_for_node(node)
00333 
00334     def dotcode_to_qt_items(self, dotcode):
00335         """
00336         takes dotcode, runs layout, and creates qt items based on the dot layout.
00337         returns two dicts, one mapping node names to Node_Item, one mapping edge names to lists of Edge_Item
00338         :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00339         """
00340         # layout graph
00341         if dotcode is None:
00342             return {}, {}
00343 
00344         graph = graph_from_dot_data(dotcode.encode("ascii", "ignore"))
00345 
00346         nodes = {}
00347         edges = {}
00348 
00349         self.get_graph_nodes(graph, nodes)
00350         self.get_graph_edges(graph, nodes, edges)
00351 
00352         return nodes, edges


rqt_decision_graph
Author(s):
autogenerated on Wed Aug 26 2015 11:16:47