-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added recursive subgraph parsing, box3d shape, graphics items now immediately parented #87
Changes from 2 commits
1223742
b39e9b3
6fdf57d
885ed74
80a6c3a
c01a78d
8180a48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from python_qt_binding.QtCore import QRectF | ||
from python_qt_binding.QtGui import QColor | ||
from python_qt_binding.QtWidgets import QAbstractGraphicsShapeItem | ||
|
||
class QGraphicsBox3dItem(QAbstractGraphicsShapeItem): | ||
def __init__(self, bounding_box): | ||
super(QGraphicsBox3dItem, self).__init__() | ||
self._bounding_box = bounding_box | ||
|
||
def boundingRect(self): | ||
return self._bounding_box | ||
|
||
def paint(self, painter, option, widget): | ||
# Main rectangle | ||
rectangle = QRectF(self._bounding_box.topLeft().x(), | ||
self._bounding_box.topLeft().y() + self._bounding_box.height() * 0.1, | ||
self._bounding_box.width() - self._bounding_box.height() * 0.1, | ||
self._bounding_box.height() - self._bounding_box.height() * 0.1); | ||
painter.drawRect(rectangle) | ||
# Top line | ||
painter.drawLine(rectangle.topLeft().x() + self._bounding_box.height() * 0.1, | ||
self._bounding_box.topLeft().y(), | ||
self._bounding_box.topRight().x(), | ||
self._bounding_box.topRight().y()) | ||
# Top left corner | ||
painter.drawLine(rectangle.topLeft().x() + self._bounding_box.height() * 0.1, | ||
self._bounding_box.topLeft().y(), | ||
self._bounding_box.topLeft().x() + 1, | ||
rectangle.topLeft().y()) | ||
# Top right corner | ||
painter.drawLine(self._bounding_box.topRight().x(), | ||
self._bounding_box.topRight().y(), | ||
rectangle.topRight().x(), | ||
rectangle.topRight().y()); | ||
# Bottom right corner | ||
painter.drawLine(rectangle.bottomRight().x() + 1, | ||
rectangle.bottomRight().y() - 1, | ||
self._bounding_box.bottomRight().x(), | ||
rectangle.bottomRight().y() - self._bounding_box.height() * 0.1); | ||
# Right line | ||
painter.drawLine(self._bounding_box.topRight().x(), | ||
self._bounding_box.topRight().y(), | ||
self._bounding_box.topRight().x(), | ||
self._bounding_box.bottomRight().y() - self._bounding_box.height() * 0.1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use four space indentation and a newline at the end of the file to match the code style of the existing code. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,7 +65,7 @@ class DotToQtGenerator(): | |
def __init__(self): | ||
pass | ||
|
||
def getNodeItemForSubgraph(self, subgraph, highlight_level): | ||
def getNodeItemForSubgraph(self, subgraph, highlight_level, scene=None): | ||
# let pydot imitate pygraphviz api | ||
attr = {} | ||
for name in subgraph.get_attributes().keys(): | ||
|
@@ -101,6 +101,7 @@ def getNodeItemForSubgraph(self, subgraph, highlight_level): | |
label=name, | ||
shape='box', | ||
color=color, | ||
parent=scene.activePanel() if scene is not None else None, | ||
label_pos=QPointF(float(label_pos[0]), -float(label_pos[1]))) | ||
bounding_box = QRectF(bounding_box) | ||
# With clusters we have the problem that mouse hovers cannot | ||
|
@@ -109,9 +110,12 @@ def getNodeItemForSubgraph(self, subgraph, highlight_level): | |
# border region would be even better (multiple RectF) | ||
bounding_box.setHeight(LABEL_HEIGHT) | ||
subgraph_nodeitem.set_hovershape(bounding_box) | ||
|
||
if scene is not None: | ||
scene.addItem(subgraph_nodeitem) | ||
return subgraph_nodeitem | ||
|
||
def getNodeItemForNode(self, node, highlight_level): | ||
def getNodeItemForNode(self, node, highlight_level, scene=None): | ||
""" | ||
returns a pyqt NodeItem object, or None in case of error or invisible style | ||
""" | ||
|
@@ -149,13 +153,15 @@ def getNodeItemForNode(self, node, highlight_level): | |
label=name, | ||
shape=node.attr.get('shape', 'ellipse'), | ||
color=color, | ||
tooltip=node.attr.get('tooltip') | ||
#parent=None, | ||
tooltip=node.attr.get('tooltip'), | ||
parent=scene.activePanel() if scene is not None else None | ||
#label_pos=None | ||
) | ||
if scene is not None: | ||
scene.addItem(node_item) | ||
return node_item | ||
|
||
def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=False): | ||
def addEdgeItem(self, edge, nodes, edges, highlight_level, scene=None, same_label_siblings=False): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add the new keyword argument Same below for |
||
""" | ||
adds EdgeItem by data in edge to edges | ||
:param same_label_siblings: if true, edges with same label will be considered siblings (collective highlighting) | ||
|
@@ -205,6 +211,7 @@ def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=F | |
from_node=nodes[source_node], | ||
to_node=nodes[destination_node], | ||
penwidth=penwidth, | ||
parent=scene.activePanel() if scene is not None else None, | ||
edge_color=color, | ||
style=style) | ||
|
||
|
@@ -225,8 +232,10 @@ def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=F | |
if edge_name not in edges: | ||
edges[edge_name] = [] | ||
edges[edge_name].append(edge_item) | ||
if scene is not None: | ||
edge_item.add_to_scene(scene) | ||
|
||
def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=False): | ||
def dotcode_to_qt_items(self, dotcode, highlight_level, scene=None, same_label_siblings=False): | ||
""" | ||
takes dotcode, runs layout, and creates qt items based on the dot layout. | ||
returns two dicts, one mapping node names to Node_Item, one mapping edge names to lists of Edge_Item | ||
|
@@ -242,20 +251,21 @@ def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=Fals | |
#graph = pygraphviz.AGraph(string=self._current_dotcode, strict=False, directed=True) | ||
#graph.layout(prog='dot') | ||
|
||
nodes = self.parse_nodes(graph, highlight_level) | ||
edges = self.parse_edges(graph, nodes, highlight_level, same_label_siblings) | ||
nodes = self.parse_nodes(graph, highlight_level, scene) | ||
edges = self.parse_edges(graph, nodes, highlight_level, scene, same_label_siblings) | ||
return nodes, edges | ||
|
||
def parse_nodes(self, graph, highlight_level): | ||
def parse_nodes(self, graph, highlight_level, scene): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the new argument be an optional keyword argument (for the same compatibility reason mentioned above). All callers should pass the keyword argument by explicitly passing the keyword. Same for |
||
"""Recursively searches all nodes inside the graph and all subgraphs.""" | ||
# let pydot imitate pygraphviz api | ||
graph.nodes_iter = graph.get_node_list | ||
graph.subgraphs_iter = graph.get_subgraph_list | ||
|
||
nodes = {} | ||
edges = {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assigned but never used? |
||
for subgraph in graph.subgraphs_iter(): | ||
subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level) | ||
nodes.update(self.parse_nodes(subgraph, highlight_level)) | ||
subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level, scene=scene) | ||
nodes.update(self.parse_nodes(subgraph, highlight_level, scene)) | ||
# skip subgraphs with empty bounding boxes | ||
if subgraph_nodeitem is None: | ||
continue | ||
|
@@ -266,15 +276,15 @@ def parse_nodes(self, graph, highlight_level): | |
# hack required by pydot | ||
if node.get_name() in ('graph', 'node', 'empty'): | ||
continue | ||
nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level) | ||
nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level, scene=scene) | ||
for node in graph.nodes_iter(): | ||
# hack required by pydot | ||
if node.get_name() in ('graph', 'node', 'empty'): | ||
continue | ||
nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level) | ||
nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level, scene=scene) | ||
return nodes | ||
|
||
def parse_edges(self, graph, nodes, highlight_level, same_label_siblings): | ||
def parse_edges(self, graph, nodes, highlight_level, scene, same_label_siblings): | ||
"""Recursively searches all edges inside the graph and all subgraphs.""" | ||
# let pydot imitate pygraphviz api | ||
graph.subgraphs_iter = graph.get_subgraph_list | ||
|
@@ -283,15 +293,17 @@ def parse_edges(self, graph, nodes, highlight_level, same_label_siblings): | |
edges = {} | ||
for subgraph in graph.subgraphs_iter(): | ||
subgraph.edges_iter = subgraph.get_edge_list | ||
edges.update(self.parse_edges(subgraph, nodes, highlight_level, same_label_siblings)) | ||
edges.update(self.parse_edges(subgraph, nodes, highlight_level, scene, same_label_siblings)) | ||
for edge in subgraph.edges_iter(): | ||
self.addEdgeItem(edge, nodes, edges, | ||
highlight_level=highlight_level, | ||
same_label_siblings=same_label_siblings) | ||
same_label_siblings=same_label_siblings, | ||
scene=scene) | ||
|
||
for edge in graph.edges_iter(): | ||
self.addEdgeItem(edge, nodes, edges, | ||
highlight_level=highlight_level, | ||
same_label_siblings=same_label_siblings) | ||
same_label_siblings=same_label_siblings, | ||
scene=scene) | ||
|
||
return edges |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,7 @@ def __init__(self, highlight_level, spline, label_center, label, from_node, to_n | |
self._arrow.setAcceptHoverEvents(True) | ||
|
||
self._path = QGraphicsPathItem() | ||
self._path = QGraphicsPathItem(parent) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the line before be removed? |
||
self._path.setPath(path) | ||
self.addToGroup(self._path) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ | |
from python_qt_binding.QtGui import QBrush, QPainterPath, QPen | ||
from python_qt_binding.QtWidgets import QGraphicsEllipseItem, QGraphicsRectItem, QGraphicsSimpleTextItem | ||
|
||
from .dot_shapes import QGraphicsBox3dItem | ||
from .graph_item import GraphItem | ||
|
||
|
||
|
@@ -51,10 +52,7 @@ def __init__(self, highlight_level, bounding_box, label, shape, color=None, pare | |
self._incoming_edges = set() | ||
self._outgoing_edges = set() | ||
|
||
if shape == 'box': | ||
self._graphics_item = QGraphicsRectItem(bounding_box) | ||
else: | ||
self._graphics_item = QGraphicsEllipseItem(bounding_box) | ||
self.parse_shape(shape, bounding_box) | ||
self.addToGroup(self._graphics_item) | ||
|
||
self._label = QGraphicsSimpleTextItem(label) | ||
|
@@ -75,6 +73,18 @@ def __init__(self, highlight_level, bounding_box, label, shape, color=None, pare | |
|
||
self.hovershape = None | ||
|
||
def parse_shape(self, shape, bounding_box): | ||
if shape == 'box' or shape == 'rect' or shape == 'rectangle': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead: Similar below. |
||
self._graphics_item = QGraphicsRectItem(bounding_box) | ||
elif shape == 'ellipse' or shape == 'oval' or shape == 'circle': | ||
self._graphics_item = QGraphicsEllipseItem(bounding_box) | ||
elif shape == 'box3d': | ||
self._graphics_item = QGraphicsBox3dItem(bounding_box) | ||
else: | ||
#raise ValueError("Invalid shape '"+shape+"'") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove the commented code line. |
||
print("Invalid shape '"+shape+"', defaulting to ellipse") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also print the error message to
This will require the following at the top of the file:
|
||
self._graphics_item = QGraphicsEllipseItem(bounding_box) | ||
|
||
def set_hovershape(self, newhovershape): | ||
self.hovershape = newhovershape | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import not used.