dot_processor.py
Go to the documentation of this file.
00001 """
00002 Copyright (c) 2013, Cogniteam
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007 
00008 *   Redistributions of source code must retain the above copyright
00009     notice, this list of conditions and the following disclaimer.
00010 
00011 *   Redistributions in binary form must reproduce the above copyright
00012     notice, this list of conditions and the following disclaimer in the
00013     documentation and/or other materials provided with the distribution.
00014 
00015 *   Neither the name of the Cogniteam nor the
00016     names of its contributors may be used to endorse or promote products
00017     derived from this software without specific prior written permission.
00018 
00019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
00023 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 """
00030 
00031 from __future__ import division
00032 from python_qt_binding.QtGui import QGraphicsTextItem
00033 from pydot import *
00034 
00035 POINTS_PER_INCH = 72
00036 
00037 
00038 class DotProcessor(object):
00039     def __init__(self, dot_to_qt):
00040         super(DotProcessor, self).__init__()
00041 
00042         self._dot_to_qt = dot_to_qt
00043 
00044     def process(self, dot_data):
00045         graphs = []
00046         graphs_nodes_and_edges = {}
00047         cluster_nodes = []
00048 
00049         self._map_dot_graph(dot_data, graphs, graphs_nodes_and_edges, cluster_nodes)
00050 
00051         return self._get_qt_elements(graphs, graphs_nodes_and_edges, cluster_nodes)
00052 
00053     def _is_cluster(self, node_name, cluster_nodes):
00054         return next((True for node in cluster_nodes if node.get_name() == node_name), False)
00055 
00056     def _find_parent_graph_node(self, graph_name, graphs_nodes_and_edges):
00057         for graph in graphs_nodes_and_edges:
00058             graph_nodes = graphs_nodes_and_edges[graph][0]
00059 
00060             graph_node = next((node for node in graph_nodes if node.get_name() == graph_name), None)
00061 
00062             if graph_node is not None:
00063                 return graph_node
00064 
00065         return None
00066 
00067     def _extract_positon(self, graph_object):
00068         return [float(value) for value in graph_object.get_pos().strip('\"').split(',')]
00069 
00070     def _extract_node_size(self, node):
00071         width = node.get_width().strip('\"')
00072         height = node.get_height().strip('\"')
00073 
00074         return float(width), float(height)
00075 
00076     def _align_edge_positions(self, edge, horizontal_offset, vertical_offset):
00077         position = edge.get_pos()[3:].strip('\"').split(' ')
00078 
00079         result = '\"e,'
00080         for pair in position:
00081             x, y = [float(value) for value in pair.replace('\\', '').split(',')]
00082             result += '{0},{1} '.format(horizontal_offset + x, vertical_offset + y)
00083         result += '\"'
00084 
00085         edge.set_pos(result)
00086 
00087     def _align_edge_label(self, edge, horizontal_offset, vertical_offset):
00088         label_pos_x, label_pos_y = [float(value) for value in edge.get_lp().strip('\"').split(',')]
00089 
00090         edge.set_lp('\"{0},{1}\"'.format(horizontal_offset + label_pos_x, vertical_offset + label_pos_y))
00091 
00092     def _align_edge_to_parent(self, edge, horizontal_offset, vertical_offset):
00093         self._align_edge_positions(edge, horizontal_offset, vertical_offset)
00094 
00095         if edge.get_label() is not None:
00096             self._align_edge_label(edge, horizontal_offset, vertical_offset)
00097 
00098     def _align_node_to_parent(self, node, horizontal_offset, vertical_offset):
00099 
00100         x, y = self._extract_positon(node)
00101         node.set_pos('\"{0},{1}\"'.format(horizontal_offset + x, vertical_offset + y))
00102 
00103     def _get_offsets(self, target_node):
00104         parent_x, parent_y = self._extract_positon(target_node)
00105         parent_width, parent_height = self._extract_node_size(target_node)
00106 
00107         # magic number detected
00108         horizontal_offset = parent_x - parent_width * POINTS_PER_INCH / 2 + 8
00109         vertical_offset = parent_y - parent_height * POINTS_PER_INCH / 2 + 8
00110 
00111         return horizontal_offset, vertical_offset
00112 
00113     def _get_qt_nodes(self, graphs, graphs_nodes_and_edges, cluster_nodes, nodes):
00114         for graph in graphs:
00115             if self._is_cluster(graph, cluster_nodes):
00116                 parent_node = self._find_parent_graph_node(graph, graphs_nodes_and_edges)
00117                 horizontal_offset, vertical_offset = self._get_offsets(parent_node)
00118 
00119                 cluster_node_nodes = graphs_nodes_and_edges[graph][0]
00120 
00121                 for node in cluster_node_nodes:
00122                     self._align_node_to_parent(node, horizontal_offset, vertical_offset)
00123 
00124                     if self._is_cluster(node.get_name(), cluster_nodes):
00125                         nodes[node.get_name()] = self._dot_to_qt.get_cluster_node(node)
00126                     else:
00127                         nodes[node.get_name()] = self._dot_to_qt.get_node_item_for_node(node)
00128             else:
00129                 graph_nodes = graphs_nodes_and_edges[graph][0]
00130 
00131                 for node in graph_nodes:
00132                     if self._is_cluster(node.get_name(), cluster_nodes):
00133                         nodes[node.get_name()] = self._dot_to_qt.get_cluster_node(node)
00134                     else:
00135                         nodes[node.get_name()] = self._dot_to_qt.get_node_item_for_node(node)
00136 
00137     def _get_qt_edges(self, graphs, graphs_nodes_and_edges, cluster_nodes, nodes, edges):
00138         for graph in graphs:
00139             if self._is_cluster(graph, cluster_nodes):
00140                 parent_node = self._find_parent_graph_node(graph, graphs_nodes_and_edges)
00141                 horizontal_offset, vertical_offset = self._get_offsets(parent_node)
00142 
00143                 cluster_node_edges = graphs_nodes_and_edges[graph][1]
00144 
00145                 for edge in cluster_node_edges:
00146                     self._align_edge_to_parent(edge, horizontal_offset, vertical_offset)
00147                     self._dot_to_qt.add_edge_item_for_edge(edge, nodes, edges)
00148             else:
00149                 cluster_node_edges = graphs_nodes_and_edges[graph][1]
00150                 for edge in cluster_node_edges:
00151                     self._dot_to_qt.add_edge_item_for_edge(edge, nodes, edges)
00152 
00153     def _get_qt_elements(self, graphs, graphs_nodes_and_edges, cluster_nodes):
00154         nodes, edges = {}, {}
00155 
00156         self._get_qt_nodes(graphs, graphs_nodes_and_edges, cluster_nodes, nodes)
00157         self._get_qt_edges(graphs, graphs_nodes_and_edges, cluster_nodes, nodes, edges)
00158 
00159         return nodes, edges
00160 
00161     def _map_dot_graph(self, dot_data, graphs, graphs_nodes_and_edges, cluster_nodes):
00162 
00163         graph = graph_from_dot_data(dot_data)
00164         graphs.append(graph.get_name())
00165 
00166         for sub_graph in graph.get_subgraph_list():
00167             if not sub_graph.get_name():
00168                 continue
00169 
00170             digraph, digraph_dot, self_edge = self._create_digraph(sub_graph)
00171 
00172             if self_edge:
00173                 graph.add_edge(self_edge)
00174 
00175             bounding_box = self._map_dot_graph(digraph_dot, graphs, graphs_nodes_and_edges, cluster_nodes)
00176             width, height = self._calculate_size(bounding_box)
00177 
00178             width += 0.2
00179             height += 0.2
00180 
00181             node = self._create_fixed_size_node(digraph.get_name(), digraph.get_label(), str(width), str(height),
00182                                                 digraph.get_URL())
00183 
00184             cluster_nodes.append(node)
00185             graph.add_node(node)
00186 
00187         if self._has_html_nodes(graph):
00188             graph = self._adjust_html_nodes_size(graph)
00189 
00190         processed_graph, processed_graph_dot, _ = self._create_digraph(graph,
00191                                                                        sub_graphs=False,
00192                                                                        translate_edge=True)
00193 
00194         self._map_nodes_and_edges_to_graph(graphs_nodes_and_edges,
00195                                            processed_graph,
00196                                            processed_graph.get_node_list(),
00197                                            processed_graph.get_edge_list())
00198 
00199         return processed_graph.get_bb()
00200 
00201     def _map_nodes_and_edges_to_graph(self, graphs_nodes_and_edges, graph, nodes, edges, include_nameless=True):
00202         if graph.get_name() not in graphs_nodes_and_edges:
00203             graphs_nodes_and_edges[graph.get_name()] = ([], [])
00204 
00205         graph_name = graph.get_name()
00206         graph_nodes = graphs_nodes_and_edges[graph_name][0]
00207         graph_edges = graphs_nodes_and_edges[graph_name][1]
00208 
00209         for node in nodes:
00210             if node.get_name() in ('graph', 'node', 'empty'):
00211                 continue
00212 
00213             graph_nodes.append(node)
00214 
00215         for edge in edges:
00216             graph_edges.append(edge)
00217 
00218         if not include_nameless:
00219             return
00220 
00221         for sub_graph in graph.get_subgraph_list():
00222             if sub_graph.get_name():
00223                 continue
00224 
00225             self._map_nodes_and_edges_to_graph(graphs_nodes_and_edges,
00226                                                graph,
00227                                                sub_graph.get_node_list(),
00228                                                sub_graph.get_edge_list(),
00229                                                include_nameless=False)
00230 
00231     def _calculate_size(self, bounding_box):
00232         return [float(value) / POINTS_PER_INCH for value in bounding_box.strip('\"').split(',')[-2:]]
00233 
00234     def _create_fixed_size_node(self, name, label, width, height, url):
00235         return Node(name=name, label=label, shape='box', width=width, height=height, fixedsize='true', URL=url)
00236 
00237     def _is_html_node(self, node):
00238         if node.get_name() in ('graph', 'node', 'empty'):
00239             return False
00240 
00241         label = node.get_label()
00242 
00243         if not label:
00244             return False
00245 
00246         label = label.strip('\"')
00247 
00248         if label.startswith('<') and label.endswith('>'):
00249             return True
00250 
00251         return True
00252 
00253     def _has_html_nodes(self, graph):
00254         for node in graph.get_node_list():
00255             if self._is_html_node(node):
00256                 return True
00257 
00258         for sub_graph in graph.get_subgraph_list():
00259             if self._is_html_node(sub_graph):
00260                 return True
00261 
00262         return False
00263 
00264     def _adjust_html_node_size(self, node):
00265         if node.get_name() in ('graph', 'node', 'empty'):
00266             return node
00267 
00268         label = node.get_label()
00269 
00270         if not label:
00271             return node
00272 
00273         label = label.strip('\"')
00274 
00275         if not label.startswith('<') and not label.endswith('>'):
00276             return node
00277 
00278         text = QGraphicsTextItem()
00279         text.setHtml(label)
00280 
00281         bounding_box = text.boundingRect()
00282         width = bounding_box.width() / POINTS_PER_INCH
00283         height = bounding_box.height() / POINTS_PER_INCH
00284 
00285         url = node.get_attributes().get('URL', None)
00286 
00287         return self._create_fixed_size_node(node.get_name(), label, str(width), str(height), url)
00288 
00289     def _adjust_html_nodes_size(self, graph):
00290         digraph = Dot(graph_name=graph.get_name(), graph_type='digraph')
00291 
00292         for node in graph.get_node_list():
00293             digraph.add_node(self._adjust_html_node_size(node))
00294 
00295         for edge in graph.get_edge_list():
00296             digraph.add_edge(edge)
00297 
00298         for sub_graph in graph.get_subgraph_list():
00299             if sub_graph.get_name() == '':
00300                 fixed_sub_graph = Subgraph()
00301                 for node in sub_graph.get_node_list():
00302                     fixed_sub_graph.add_node(self._adjust_html_node_size(node))
00303 
00304                 digraph.add_subgraph(fixed_sub_graph)
00305             else:
00306                 digraph.add_subgraph(sub_graph)
00307 
00308         return graph_from_dot_data(digraph.create_dot())
00309 
00310     def _translate_edge_source(self, edge):
00311         source = edge.get_ltail()
00312 
00313         if not source:
00314             source = edge.get_source()
00315 
00316         return source
00317 
00318     def _translate_edge_destination(self, edge):
00319         destination = edge.get_lhead()
00320 
00321         if not destination:
00322             destination = edge.get_destination()
00323 
00324         return destination
00325 
00326     def _is_parent_graph_node(self, source, destination, parent_name):
00327         return source == destination and destination == parent_name
00328 
00329     def _create_digraph(self, graph, sub_graphs=True, translate_edge=False):
00330         parent_edge = None
00331         digraph = Dot(graph_name=graph.get_name(), graph_type='digraph')
00332         digraph.set_node_defaults(shape='box')
00333         label = None
00334         url = None
00335         for node in graph.get_node_list():
00336             if node.get_name() in ('node', 'empty'):
00337                 continue
00338             elif node.get_name() == 'graph':
00339                 label = node.get_attributes().get('label', ' ')
00340                 url = node.get_attributes().get('URL', None)
00341             else:
00342                 digraph.add_node(node)
00343 
00344         for edge in graph.get_edge_list():
00345             source = self._translate_edge_source(edge)
00346             destination = self._translate_edge_destination(edge)
00347             edge_label = edge.get_label()
00348 
00349             if translate_edge:
00350                 if self._is_parent_graph_node(source, destination, graph.get_name()):
00351                     continue
00352 
00353                 translated_edge = Edge(source, destination)
00354                 if edge_label:
00355                     translated_edge.set_label(edge_label)
00356 
00357                 digraph.add_edge(translated_edge)
00358 
00359             else:
00360                 if self._is_parent_graph_node(source, destination, graph.get_name()):
00361                     parent_edge = Edge(source, destination)
00362                     if edge_label:
00363                         parent_edge.set_label(edge_label)
00364                     continue
00365                 digraph.add_edge(edge)
00366 
00367         for sub_graph in graph.get_subgraph_list():
00368             if sub_graphs or sub_graph.get_name() == '':
00369                 digraph.add_subgraph(sub_graph)
00370 
00371         if label:
00372             digraph.set_label(label)
00373             digraph.set_labelloc('t')
00374 
00375         if url:
00376             digraph.set_URL(url)
00377 
00378         digraph_dot = digraph.create_dot()
00379 
00380         return graph_from_dot_data(digraph_dot), digraph_dot, parent_edge
00381 


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