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 node.attr.get('style') == 'invis':
00130 return None
00131
00132 color = QColor(node.attr['color']) if 'color' in node.attr else None
00133
00134 name = node.attr.get('label', node.attr.get('name'))
00135 if name is None:
00136 print("Error, no label defined for node with attr: %s" % node.attr)
00137 return None
00138 name = name.decode('string_escape')
00139
00140
00141 bb_width = node.attr.get('width', len(name) / 5)
00142 bb_height = node.attr.get('height', 1.0)
00143 bounding_box = QRectF(0, 0, POINTS_PER_INCH * float(bb_width) - 1.0, POINTS_PER_INCH * float(bb_height) - 1.0)
00144 pos = node.attr.get('pos', '0,0').split(',')
00145 bounding_box.moveCenter(QPointF(float(pos[0]), -float(pos[1])))
00146
00147 node_item = NodeItem(highlight_level=highlight_level,
00148 bounding_box=bounding_box,
00149 label=name,
00150 shape=node.attr.get('shape', 'ellipse'),
00151 color=color,
00152 tooltip=node.attr.get('tooltip')
00153
00154
00155 )
00156 return node_item
00157
00158 def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=False):
00159 """
00160 adds EdgeItem by data in edge to edges
00161 :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00162 """
00163
00164 attr = {}
00165 for name in edge.get_attributes().iterkeys():
00166 value = get_unquoted(edge, name)
00167 attr[name] = value
00168 edge.attr = attr
00169
00170 style = edge.attr.get('style')
00171 if style == 'invis':
00172 return
00173
00174 label = edge.attr.get('label', None)
00175 label_pos = edge.attr.get('lp', None)
00176 label_center = None
00177 if label_pos is not None:
00178 label_pos = label_pos.split(',')
00179 label_center = QPointF(float(label_pos[0]), -float(label_pos[1]))
00180
00181
00182 source_node = edge.get_source() if hasattr(edge, 'get_source') else edge[0]
00183 destination_node = edge.get_destination() if hasattr(edge, 'get_destination') else edge[1]
00184
00185
00186 edge_pos = edge.attr.get('pos')
00187 if edge_pos is None:
00188 return
00189 if label is not None:
00190 label = label.decode('string_escape')
00191
00192 penwidth = int(edge.attr.get('penwidth', 1))
00193
00194 color = None
00195 if 'colorR' in edge.attr and 'colorG' in edge.attr and 'colorB' in edge.attr:
00196 r = edge.attr['colorR']
00197 g = edge.attr['colorG']
00198 b = edge.attr['colorB']
00199 color = QColor(float(r), float(g), float(b))
00200
00201 edge_item = EdgeItem(highlight_level=highlight_level,
00202 spline=edge_pos,
00203 label_center=label_center,
00204 label=label,
00205 from_node=nodes[source_node],
00206 to_node=nodes[destination_node],
00207 penwidth=penwidth,
00208 edge_color=color,
00209 style=style)
00210
00211 if same_label_siblings:
00212 if label is None:
00213
00214 label = "%s_%s" % (source_node, destination_node)
00215
00216 if label in edges:
00217 for sibling in edges[label]:
00218 edge_item.add_sibling_edge(sibling)
00219 sibling.add_sibling_edge(edge_item)
00220
00221 if label not in edges:
00222 edges[label] = []
00223 edges[label].append(edge_item)
00224
00225 def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=False):
00226 """
00227 takes dotcode, runs layout, and creates qt items based on the dot layout.
00228 returns two dicts, one mapping node names to Node_Item, one mapping edge names to lists of Edge_Item
00229 :param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting)
00230 """
00231
00232 if dotcode is None:
00233 return {}, {}
00234 graph = pydot.graph_from_dot_data(dotcode.encode("ascii", "ignore"))
00235 if isinstance(graph, list):
00236 graph = graph[0]
00237
00238
00239
00240
00241
00242 graph.nodes_iter = graph.get_node_list
00243 graph.edges_iter = graph.get_edge_list
00244
00245 graph.subgraphs_iter = graph.get_subgraph_list
00246
00247 nodes = {}
00248 for subgraph in graph.subgraphs_iter():
00249 subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level)
00250
00251 if subgraph_nodeitem is None:
00252 continue
00253
00254 subgraph.nodes_iter = subgraph.get_node_list
00255 nodes[subgraph.get_name()] = subgraph_nodeitem
00256 for node in subgraph.nodes_iter():
00257
00258 if node.get_name() in ('graph', 'node', 'empty'):
00259 continue
00260 nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level)
00261 for node in graph.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
00267 edges = {}
00268
00269 for subgraph in graph.subgraphs_iter():
00270 subgraph.edges_iter = subgraph.get_edge_list
00271 for edge in subgraph.edges_iter():
00272 self.addEdgeItem(edge, nodes, edges,
00273 highlight_level=highlight_level,
00274 same_label_siblings=same_label_siblings)
00275
00276 for edge in graph.edges_iter():
00277 self.addEdgeItem(edge, nodes, edges,
00278 highlight_level=highlight_level,
00279 same_label_siblings=same_label_siblings)
00280
00281 return nodes, edges