From 9e892ba587d9e45a94114a7da5d93147368a102a Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr> Date: Tue, 21 Mar 2023 17:29:42 +0100 Subject: [PATCH] network: Add edge feature and delete node feature. --- src/view/NetworkWindow.py | 8 +- src/view/network/GraphWidget.py | 222 ++++++++++++++++++++------------ 2 files changed, 146 insertions(+), 84 deletions(-) diff --git a/src/view/NetworkWindow.py b/src/view/NetworkWindow.py index 5f2ab32a..992ac5eb 100644 --- a/src/view/NetworkWindow.py +++ b/src/view/NetworkWindow.py @@ -191,13 +191,13 @@ class NetworkWindow(ASubWindow): def clicked_add(self): if self.get_push_button_checkable("pushButton_add"): self.set_push_button_checkable("pushButton_del", False) - self.graph_widget.state = "add" + self.graph_widget.state("add") else: - self.graph_widget.state = "move" + self.graph_widget.state("move") def clicked_del(self): if self.get_push_button_checkable("pushButton_del"): self.set_push_button_checkable("pushButton_add", False) - self.graph_widget.state = "del" + self.graph_widget.state("del") else: - self.graph_widget.state = "move" + self.graph_widget.state("move") diff --git a/src/view/network/GraphWidget.py b/src/view/network/GraphWidget.py index 6bdb759d..d9cf5303 100644 --- a/src/view/network/GraphWidget.py +++ b/src/view/network/GraphWidget.py @@ -92,6 +92,8 @@ class NodeItem(QGraphicsItem): color = Qt.yellow if self.graph.selectedNode() == self: color = Qt.red + if self.graph.selectedNewEdgeSrcNode() == self: + color = Qt.darkRed painter.setBrush(QBrush(color)) painter.drawEllipse(-10, -10, 20, 20) @@ -101,26 +103,17 @@ class NodeItem(QGraphicsItem): self.graph.itemMoved() self.graph.nodeChangePosition(value, self) + self.graph.update_edges(self) return super(NodeItem, self).itemChange(change, value) def mousePressEvent(self, event): # Switch selected node - previous_node = self.graph.selectedNode() - self.graph.setSelectedNode(self) - - # Update previous node - if previous_node: - previous_node.update() + if not self.graph.selectedNewEdgeSrcNode(): + self.graph.setSelectedNode(self) self.update() super(NodeItem, self).mousePressEvent(event) - # def mouseMoveEvent(self, event): - # self.node.setPos(event.pos().x(), event.pos().y()) - - # self.update() - # super(NodeItem, self).mousePressEvent(event) - def mouseReleaseEvent(self, event): self.update() super(NodeItem, self).mouseReleaseEvent(event) @@ -128,79 +121,62 @@ class NodeItem(QGraphicsItem): class EdgeItem(QGraphicsItem): Type = QGraphicsItem.UserType + 2 - def __init__(self, edge): + def __init__(self, src_node_item, dest_node_item, edge, graph_widget): super(EdgeItem, self).__init__() - self.arrowSize = 10.0 - self.sourcePoint = QPointF() - self.destPoint = QPointF() + self.src_node = src_node_item + self.dest_node = dest_node_item + self.edge = edge + + self.graph = graph_widget + + self.src_pos = src_node_item.pos() + self.dest_pos = dest_node_item.pos() self.setAcceptedMouseButtons(Qt.NoButton) - self.source = edge.node1 - self.dest = edge.node2 - self.edge = edge self.adjust() def type(self): return Edge.Type def sourceNode(self): - return self.source + return self.src_node.node - def setSourceNode(self, node): - self.source = node + def setSourceNode(self, node_item): + self.src_node = node_item self.adjust() def destNode(self): return self.dest - def setDestNode(self, node): - self.dest = node + def setDestNode(self, node_item): + self.dest_node = node_item self.adjust() def adjust(self): - if not self.source or not self.dest: - return - line = QLineF( - self.mapFromItem(self.source, 0, 0), - self.mapFromItem(self.dest, 0, 0) + self.mapFromItem(self.src_node, 0, 0), + self.mapFromItem(self.dest_node, 0, 0) ) length = line.length() self.prepareGeometryChange() - if length > 20.0: - edgeOffset = QPointF((line.dx() * 10) / length, - (line.dy() * 10) / length) - - self.sourcePoint = line.p1() + edgeOffset - self.destPoint = line.p2() - edgeOffset - else: - self.sourcePoint = line.p1() - self.destPoint = line.p1() - def boundingRect(self): - if not self.source or not self.dest: - return QRectF() - penWidth = 1.0 - extra = (penWidth + self.arrowSize) / 2.0 + extra = (penWidth + 5) / 2.0 return QRectF( - self.sourcePoint, + self.src_pos, QSizeF( - self.destPoint.x() - self.sourcePoint.x(), - self.destPoint.y() - self.sourcePoint.y() + self.dest_pos.x() - self.src_pos.x(), + self.dest_pos.y() - self.src_pos.y() ) ).normalized().adjusted(-extra, -extra, extra, extra) def paint(self, painter, option, widget): - if not self.source or not self.dest: - return - # Draw the line itself. - line = QLineF(self.sourcePoint, self.destPoint) + line = QLineF(self.src_node.pos(), self.dest_node.pos()) if line.length() == 0.0: return @@ -210,22 +186,22 @@ class EdgeItem(QGraphicsItem): painter.drawLine(line) # Draw the arrows if there's enough room. - angle = math.acos(line.dx() / line.length()) - if line.dy() >= 0: - angle = Edge.TwoPi - angle - - sourceArrowP1 = self.sourcePoint + QPointF(math.sin(angle + Edge.Pi / 3) * self.arrowSize, - math.cos(angle + Edge.Pi / 3) * self.arrowSize) - sourceArrowP2 = self.sourcePoint + QPointF(math.sin(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize, - math.cos(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize); - destArrowP1 = self.destPoint + QPointF(math.sin(angle - Edge.Pi / 3) * self.arrowSize, - math.cos(angle - Edge.Pi / 3) * self.arrowSize) - destArrowP2 = self.destPoint + QPointF(math.sin(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize, - math.cos(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize) - - painter.setBrush(Qt.black) - painter.drawPolygon(QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2])) - painter.drawPolygon(QPolygonF([line.p2(), destArrowP1, destArrowP2])) + # angle = math.acos(line.dx() / line.length()) + # if line.dy() >= 0: + # angle = Edge.TwoPi - angle + + # sourceArrowP1 = self.sourcePoint + QPointF(math.sin(angle + Edge.Pi / 3) * self.arrowSize, + # math.cos(angle + Edge.Pi / 3) * self.arrowSize) + # sourceArrowP2 = self.sourcePoint + QPointF(math.sin(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize, + # math.cos(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize); + # destArrowP1 = self.destPoint + QPointF(math.sin(angle - Edge.Pi / 3) * self.arrowSize, + # math.cos(angle - Edge.Pi / 3) * self.arrowSize) + # destArrowP2 = self.destPoint + QPointF(math.sin(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize, + # math.cos(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize) + + # painter.setBrush(Qt.black) + # painter.drawPolygon(QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2])) + # painter.drawPolygon(QPolygonF([line.p2(), destArrowP1, destArrowP2])) class GraphWidget(QGraphicsView): changeEdge = pyqtSignal(object) @@ -236,9 +212,13 @@ class GraphWidget(QGraphicsView): self.timerId = 0 self.parent = parent - self.state = "move" + self._state = "move" self._selectedNode = None + self._selectedNewEdgeSrcNode = None + + self.node_items = [] + self.edge_items = [] scene = QGraphicsScene(self) scene.setItemIndexMethod(QGraphicsScene.NoIndex) @@ -259,26 +239,87 @@ class GraphWidget(QGraphicsView): self.create_items() def create_items(self): + self.node_items = [] + self.edge_items = [] + for node in self.graph.nodes(): - self.scene().addItem(NodeItem(node, self)) + inode = NodeItem(node, self) + self.scene().addItem(inode) + self.node_items.append(inode) + + for edge in self.graph.edges(): + n1 = list( + filter( + lambda n: n.node.name == edge.node1.name, + self.node_items + ) + ) + + n2 = list( + filter( + lambda n: n.node.name == edge.node2.name, + self.node_items + ) + ) - # for edge in self.graph.edges(): - # scene.addItem(EdgeItem(edge)) + iedge = EdgeItem(n1[0], n2[0], edge, self) + self.scene().addItem(iedge) + self.edge_items.append(iedge) # self.mark = LandMark(1000, 1000, self) # self.scene().addItem(self.mark) + def state(self, status): + self._state = status + def add_node(self, pos): node = self.graph.add_node(pos.x(), pos.y()) - self.scene().addItem(NodeItem(node, self)) + inode = NodeItem(node, self) + self.scene().addItem(inode) + self.node_items.append(inode) + self.changeNode.emit(self.sender()) def del_node(self, node): - self.scene().clear() + edges = list( + filter( + lambda ie: ie.edge.node1 == node or ie.edge.node2 == node, + self.edge_items + ) + ) + + for edge in edges: + self.graph.remove_edge(edge.edge.name) + self.graph.remove_node(node.name) + self.scene().clear() self.create_items() self.changeNode.emit(self.sender()) + self.changeEdge.emit(self.sender()) + + def add_edge(self, node1, node2): + if node1 == node2: + return + edge = self.graph.add_edge(node1.node, node2.node) + iedge = EdgeItem(node1, node2, edge, self) + self.scene().addItem(iedge) + self.edge_items.append(iedge) + + self.setSelectedNode(None) + self.setSelectedNewEdgeSrcNode(None) + self.changeEdge.emit(self.sender()) + + def update_edges(self, node): + edges = list( + filter( + lambda ie: ie.edge.node1 == node.node or ie.edge.node2 == node.node, + self.edge_items + ) + ) + + for edge in edges: + edge.update() def itemMoved(self): if not self.timerId: @@ -291,7 +332,25 @@ class GraphWidget(QGraphicsView): return self._selectedNode def setSelectedNode(self, node): + previous_node = self._selectedNode self._selectedNode = node + if node: + self.setSelectedNewEdgeSrcNode(None) + + if previous_node: + previous_node.update() + + def selectedNewEdgeSrcNode(self): + return self._selectedNewEdgeSrcNode + + def setSelectedNewEdgeSrcNode(self, node): + previous_node = self._selectedNewEdgeSrcNode + self._selectedNewEdgeSrcNode = node + if node: + self.setSelectedNode(None) + + if previous_node: + previous_node.update() def keyPressEvent(self, event): key = event.key() @@ -340,20 +399,23 @@ class GraphWidget(QGraphicsView): def mousePressEvent(self, event): pos = self.mapToScene(event.pos()) - if self.state == "move": - self.mouse_origin_x = pos.x - self.mouse_origin_y = pos.y + if self._state == "move": + self.mouse_origin_x = pos.x() + self.mouse_origin_y = pos.y() - elif self.state == "add": + elif self._state == "add": items = self.items(event.pos()) if not items: self.add_node(pos) else: - print(f"TODO add edge {items}") + nodes = list(filter(lambda i: type(i) == NodeItem, items)) + if self.selectedNewEdgeSrcNode() is None: + self.setSelectedNewEdgeSrcNode(nodes[0]) + else: + self.add_edge(self.selectedNewEdgeSrcNode(), nodes[0]) - elif self.state == "del": + elif self._state == "del": items = self.items(event.pos()) - print(f"del: {items}") if len(items) > 0: nodes = list(filter(lambda i: type(i) == NodeItem, items)) if len(nodes) > 0: @@ -367,6 +429,6 @@ class GraphWidget(QGraphicsView): super(GraphWidget, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): - if self.state == "move": + if self._state == "move": self.update() super(GraphWidget, self).mouseMoveEvent(event) -- GitLab