00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 import pydot
00034
00035 import roslib
00036 roslib.load_manifest('qt_dotgraph')
00037
00038 from python_qt_binding.QtCore import QPointF, QRectF
00039 from python_qt_binding.QtGui import QColor
00040
00041 from .edge_item import EdgeItem
00042 from .node_item import NodeItem
00043
00044 POINTS_PER_INCH = 72
00045
00046
00047
00048 def get_unquoted(item, name):
00049 value = item.get(name)
00050 if value is None:
00051 return None
00052 try:
00053 return value.strip('"\n"')
00054 except AttributeError:
00055
00056 return value
00057
00058
00059 LABEL_HEIGHT = 30
00060
00061
00062
00063 class DotToQtGenerator():
00064
00065 def __init__(self):
00066 pass
00067
00068 def getNodeItemForSubgraph(self, subgraph, highlight_level):
00069
00070 attr = {}
00071 for name in subgraph.get_attributes().iterkeys():
00072 value = get_unquoted(subgraph, name)
00073 attr[name] = value
00074 obj_dic = subgraph.__getattribute__("obj_dict")
00075 for name in obj_dic:
00076 if name not in ['nodes', 'attributes', 'parent_graph'] and obj_dic[name] is not None:
00077 attr[name] = get_unquoted(obj_dic, name)
00078 elif name == 'nodes':
00079 for key in obj_dic['nodes']['graph'][0]['attributes']:
00080 attr[key] = get_unquoted(obj_dic['nodes']['graph'][0]['attributes'], key)
00081 subgraph.attr = attr
00082
00083 bb = subgraph.attr['bb'].strip('"').split(',')
00084 if len(bb) < 4:
00085 raise ValueError('bounding box has too few elements %s' % subgraph.attr)
00086 bounding_box = QRectF(0, 0, float(bb[2]) - float(bb[0]), float(bb[3]) - float(bb[1]))
00087 if 'lp' in subgraph.attr:
00088 label_pos = subgraph.attr['lp'].strip('"').split(',')
00089 else:
00090 label_pos = (float(bb[0]) + (float(bb[2]) - float(bb[0])) / 2, float(bb[1]) + (float(bb[3]) - float(bb[1])) - LABEL_HEIGHT / 2)
00091 bounding_box.moveCenter(QPointF(float(bb[0]) + (float(bb[2]) - float(bb[0])) / 2, -float(bb[1]) - (float(bb[3]) - float(bb[1])) / 2))
00092 name = subgraph.attr['label']
00093 color = QColor(subgraph.attr['color']) if 'color' in subgraph.attr else None
00094 subgraph_nodeitem = NodeItem(highlight_level,
00095 bounding_box,
00096 label=name,
00097 shape='box',
00098 color=color,
00099 label_pos=QPointF(float(label_pos[0]), -float(label_pos[1])))
00100 bounding_box = QRectF(bounding_box)
00101
00102
00103
00104
00105 bounding_box.setHeight(LABEL_HEIGHT)
00106 subgraph_nodeitem.set_hovershape(bounding_box)
00107 return subgraph_nodeitem
00108
00109 def getNodeItemForNode(self, node, highlight_level):
00110 """
00111 returns a pyqt NodeItem object, or None in case of error or invisible style
00112 """
00113
00114 attr = {}
00115 for name in node.get_attributes().iterkeys():
00116 value = get_unquoted(node, name)
00117 attr[name] = value
00118 obj_dic = node.__getattribute__("obj_dict")
00119 for name in obj_dic:
00120 if name not in ['attributes', 'parent_graph'] and obj_dic[name] is not None:
00121 attr[name] = get_unquoted(obj_dic, name)
00122 node.attr = attr
00123
00124 if 'style' in node.attr:
00125 if node.attr['style'] == 'invis':
00126 return None
00127
00128 color = QColor(node.attr['color']) if 'color' in node.attr else None
00129 name = None
00130 if 'label' in node.attr:
00131 name = node.attr['label']
00132 elif 'name' in node.attr:
00133 name = node.attr['name']
00134 else:
00135 print("Error, no label defined for node with attr: %s" % node.attr)
00136 return None
00137 if name is None:
00138
00139 print("Error, label is None for node %s, pygraphviz version may be too old." % node)
00140 else:
00141 name = name.decode('string_escape')
00142
00143
00144 bb_width = len(name) / 5
00145 if 'width' in node.attr:
00146 bb_width = node.attr['width']
00147 bb_height = 1.0
00148 if 'width' in node.attr:
00149 bb_height = node.attr['height']
00150 bounding_box = QRectF(0, 0, POINTS_PER_INCH * float(bb_width) - 1.0, POINTS_PER_INCH * float(bb_height) - 1.0)
00151 pos = (0, 0)
00152 if 'pos' in node.attr:
00153 pos = node.attr['pos'].split(',')
00154 bounding_box.moveCenter(QPointF(float(pos[0]), -float(pos[1])))
00155
00156 node_item = NodeItem(highlight_level=highlight_level,
00157 bounding_box=bounding_box,
00158 label=name,
00159 shape=node.attr.get('shape', 'ellipse'),
00160 color=color,
00161
00162
00163 )
00164
00165 return node_item
00166
00167 def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=False):
00168 """
00169 adds EdgeItem by data in edge to edges
00170 :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00171 """
00172
00173 attr = {}
00174 for name in edge.get_attributes().iterkeys():
00175 value = get_unquoted(edge, name)
00176 attr[name] = value
00177 edge.attr = attr
00178
00179 if 'style' in edge.attr:
00180 if edge.attr['style'] == 'invis':
00181 return
00182
00183 label = edge.attr.get('label', None)
00184 label_pos = edge.attr.get('lp', None)
00185 label_center = None
00186 if label_pos is not None:
00187 label_pos = label_pos.split(',')
00188 label_center = QPointF(float(label_pos[0]), -float(label_pos[1]))
00189
00190
00191 source_node = edge.get_source() if hasattr(edge, 'get_source') else edge[0]
00192 destination_node = edge.get_destination() if hasattr(edge, 'get_destination') else edge[1]
00193
00194
00195 edge_pos = (0, 0)
00196 if 'pos' in edge.attr:
00197 edge_pos = edge.attr['pos']
00198 if label is not None:
00199 label = label.decode('string_escape')
00200 edge_item = EdgeItem(highlight_level=highlight_level,
00201 spline=edge_pos,
00202 label_center=label_center,
00203 label=label,
00204 from_node=nodes[source_node],
00205 to_node=nodes[destination_node])
00206
00207 if same_label_siblings:
00208 if label is None:
00209
00210 label = "%s_%s" % (source_node, destination_node)
00211
00212 if label in edges:
00213 for sibling in edges[label]:
00214 edge_item.add_sibling_edge(sibling)
00215 sibling.add_sibling_edge(edge_item)
00216
00217 if label not in edges:
00218 edges[label] = []
00219 edges[label].append(edge_item)
00220
00221 def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=False):
00222 """
00223 takes dotcode, runs layout, and creates qt items based on the dot layout.
00224 returns two dicts, one mapping node names to Node_Item, one mapping edge names to lists of Edge_Item
00225 :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00226 """
00227
00228 if dotcode is None:
00229 return {}, {}
00230 graph = pydot.graph_from_dot_data(dotcode.encode("ascii", "ignore"))
00231
00232
00233
00234
00235
00236 graph.nodes_iter = graph.get_node_list
00237 graph.edges_iter = graph.get_edge_list
00238
00239 graph.subgraphs_iter = graph.get_subgraph_list
00240
00241 nodes = {}
00242 for subgraph in graph.subgraphs_iter():
00243 subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level)
00244
00245 subgraph.nodes_iter = subgraph.get_node_list
00246 nodes[subgraph.get_name()] = subgraph_nodeitem
00247 for node in subgraph.nodes_iter():
00248
00249 if node.get_name() in ('graph', 'node', 'empty'):
00250 continue
00251 nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level)
00252 for node in graph.nodes_iter():
00253
00254 if node.get_name() in ('graph', 'node', 'empty'):
00255 continue
00256 nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level)
00257
00258 edges = {}
00259
00260 for subgraph in graph.subgraphs_iter():
00261 subgraph.edges_iter = subgraph.get_edge_list
00262 for edge in subgraph.edges_iter():
00263 self.addEdgeItem(edge, nodes, edges,
00264 highlight_level=highlight_level,
00265 same_label_siblings=same_label_siblings)
00266
00267 for edge in graph.edges_iter():
00268 self.addEdgeItem(edge, nodes, edges,
00269 highlight_level=highlight_level,
00270 same_label_siblings=same_label_siblings)
00271
00272 return nodes, edges