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
00034 import pyparsing
00035 pyparsing._noncomma = "".join([c for c in pyparsing.printables if c != ","])
00036 import pydot
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
00086 return None
00087 bounding_box = QRectF(0, 0, float(bb[2]) - float(bb[0]), float(bb[3]) - float(bb[1]))
00088 if 'lp' in subgraph.attr:
00089 label_pos = subgraph.attr['lp'].strip('"').split(',')
00090 else:
00091 label_pos = (float(bb[0]) + (float(bb[2]) - float(bb[0])) / 2, float(bb[1]) + (float(bb[3]) - float(bb[1])) - LABEL_HEIGHT / 2)
00092 bounding_box.moveCenter(QPointF(float(bb[0]) + (float(bb[2]) - float(bb[0])) / 2, -float(bb[1]) - (float(bb[3]) - float(bb[1])) / 2))
00093 name = subgraph.attr.get('label', '')
00094 color = QColor(subgraph.attr['color']) if 'color' in subgraph.attr else None
00095 subgraph_nodeitem = NodeItem(highlight_level,
00096 bounding_box,
00097 label=name,
00098 shape='box',
00099 color=color,
00100 label_pos=QPointF(float(label_pos[0]), -float(label_pos[1])))
00101 bounding_box = QRectF(bounding_box)
00102
00103
00104
00105
00106 bounding_box.setHeight(LABEL_HEIGHT)
00107 subgraph_nodeitem.set_hovershape(bounding_box)
00108 return subgraph_nodeitem
00109
00110 def getNodeItemForNode(self, node, highlight_level):
00111 """
00112 returns a pyqt NodeItem object, or None in case of error or invisible style
00113 """
00114
00115 attr = {}
00116 for name in node.get_attributes().iterkeys():
00117 value = get_unquoted(node, name)
00118 attr[name] = value
00119 obj_dic = node.__getattribute__("obj_dict")
00120 for name in obj_dic:
00121 if name not in ['attributes', 'parent_graph'] and obj_dic[name] is not None:
00122 attr[name] = get_unquoted(obj_dic, name)
00123 node.attr = attr
00124
00125 if 'style' in node.attr:
00126 if node.attr['style'] == 'invis':
00127 return None
00128
00129 color = QColor(node.attr['color']) if 'color' in node.attr else None
00130 name = None
00131 if 'label' in node.attr:
00132 name = node.attr['label']
00133 elif 'name' in node.attr:
00134 name = node.attr['name']
00135 else:
00136 print("Error, no label defined for node with attr: %s" % node.attr)
00137 return None
00138 if name is None:
00139
00140 print("Error, label is None for node %s, pygraphviz version may be too old." % node)
00141 else:
00142 name = name.decode('string_escape')
00143
00144
00145 bb_width = len(name) / 5
00146 if 'width' in node.attr:
00147 bb_width = node.attr['width']
00148 bb_height = 1.0
00149 if 'width' in node.attr:
00150 bb_height = node.attr['height']
00151 bounding_box = QRectF(0, 0, POINTS_PER_INCH * float(bb_width) - 1.0, POINTS_PER_INCH * float(bb_height) - 1.0)
00152 pos = (0, 0)
00153 if 'pos' in node.attr:
00154 pos = node.attr['pos'].split(',')
00155 bounding_box.moveCenter(QPointF(float(pos[0]), -float(pos[1])))
00156
00157 node_item = NodeItem(highlight_level=highlight_level,
00158 bounding_box=bounding_box,
00159 label=name,
00160 shape=node.attr.get('shape', 'ellipse'),
00161 color=color,
00162
00163
00164 )
00165
00166 return node_item
00167
00168 def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=False):
00169 """
00170 adds EdgeItem by data in edge to edges
00171 :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00172 """
00173
00174 attr = {}
00175 for name in edge.get_attributes().iterkeys():
00176 value = get_unquoted(edge, name)
00177 attr[name] = value
00178 edge.attr = attr
00179
00180 if 'style' in edge.attr:
00181 if edge.attr['style'] == 'invis':
00182 return
00183
00184 label = edge.attr.get('label', None)
00185 label_pos = edge.attr.get('lp', None)
00186 label_center = None
00187 if label_pos is not None:
00188 label_pos = label_pos.split(',')
00189 label_center = QPointF(float(label_pos[0]), -float(label_pos[1]))
00190
00191
00192 source_node = edge.get_source() if hasattr(edge, 'get_source') else edge[0]
00193 destination_node = edge.get_destination() if hasattr(edge, 'get_destination') else edge[1]
00194
00195
00196 edge_pos = (0, 0)
00197 if 'pos' in edge.attr:
00198 edge_pos = edge.attr['pos']
00199 if label is not None:
00200 label = label.decode('string_escape')
00201
00202 color = None
00203 if 'colorR' in edge.attr and 'colorG' in edge.attr and 'colorB' in edge.attr:
00204 r = edge.attr['colorR']
00205 g = edge.attr['colorG']
00206 b = edge.attr['colorB']
00207 color = QColor(float(r), float(g), float(b))
00208
00209 edge_item = EdgeItem(highlight_level=highlight_level,
00210 spline=edge_pos,
00211 label_center=label_center,
00212 label=label,
00213 from_node=nodes[source_node],
00214 to_node=nodes[destination_node],
00215 penwidth=int(edge.attr['penwidth']),
00216 edge_color=color)
00217
00218 if same_label_siblings:
00219 if label is None:
00220
00221 label = "%s_%s" % (source_node, destination_node)
00222
00223 if label in edges:
00224 for sibling in edges[label]:
00225 edge_item.add_sibling_edge(sibling)
00226 sibling.add_sibling_edge(edge_item)
00227
00228 if label not in edges:
00229 edges[label] = []
00230 edges[label].append(edge_item)
00231
00232 def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=False):
00233 """
00234 takes dotcode, runs layout, and creates qt items based on the dot layout.
00235 returns two dicts, one mapping node names to Node_Item, one mapping edge names to lists of Edge_Item
00236 :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00237 """
00238
00239 if dotcode is None:
00240 return {}, {}
00241 graph = pydot.graph_from_dot_data(dotcode.encode("ascii", "ignore"))
00242
00243
00244
00245
00246
00247 graph.nodes_iter = graph.get_node_list
00248 graph.edges_iter = graph.get_edge_list
00249
00250 graph.subgraphs_iter = graph.get_subgraph_list
00251
00252 nodes = {}
00253 for subgraph in graph.subgraphs_iter():
00254 subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level)
00255
00256 if subgraph_nodeitem is None:
00257 continue
00258
00259 subgraph.nodes_iter = subgraph.get_node_list
00260 nodes[subgraph.get_name()] = subgraph_nodeitem
00261 for node in subgraph.nodes_iter():
00262
00263 if node.get_name() in ('graph', 'node', 'empty'):
00264 continue
00265 nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level)
00266 for node in graph.nodes_iter():
00267
00268 if node.get_name() in ('graph', 'node', 'empty'):
00269 continue
00270 nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level)
00271
00272 edges = {}
00273
00274 for subgraph in graph.subgraphs_iter():
00275 subgraph.edges_iter = subgraph.get_edge_list
00276 for edge in subgraph.edges_iter():
00277 self.addEdgeItem(edge, nodes, edges,
00278 highlight_level=highlight_level,
00279 same_label_siblings=same_label_siblings)
00280
00281 for edge in graph.edges_iter():
00282 self.addEdgeItem(edge, nodes, edges,
00283 highlight_level=highlight_level,
00284 same_label_siblings=same_label_siblings)
00285
00286 return nodes, edges