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