From 2c0b265a2be6305feaeb7a9b389239154612ad31 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr> Date: Tue, 21 Mar 2023 16:09:47 +0100 Subject: [PATCH] network: Add nodes features. --- src/model/network/Graph.py | 8 +- src/model/network/Node.py | 4 + src/view/NetworkWindow.py | 33 +++++--- src/view/network/GraphWidget.py | 146 +++++++++++++++++++++++++++----- 4 files changed, 153 insertions(+), 38 deletions(-) diff --git a/src/model/network/Graph.py b/src/model/network/Graph.py index 78ea877c..c727c8e9 100644 --- a/src/model/network/Graph.py +++ b/src/model/network/Graph.py @@ -19,13 +19,13 @@ class Graph(object): return f"Graph {{nodes: {self._nodes}, edges: {self._edges}}}" def nodes(self): - return self._nodes.copy() + return self._nodes def nodes_names(self): return list(map(lambda n: n.name, self._nodes)) def edges(self): - return self._edges.copy() + return self._edges def nodes_counts(self): return len(self._nodes) @@ -67,7 +67,7 @@ class Graph(object): return edge def remove_node(self, node_name:str): - self._nodes = filter(lambda n: n.name != node_name, self._nodes) + self._nodes = list(filter(lambda n: n.name != node_name, self._nodes)) def remove_edge(self, edge_name:str): - self._edges = filter(lambda e: e.name != edge_name, self._edges) + self._edges = list(filter(lambda e: e.name != edge_name, self._edges)) diff --git a/src/model/network/Node.py b/src/model/network/Node.py index 46ed203f..73e1a7b0 100644 --- a/src/model/network/Node.py +++ b/src/model/network/Node.py @@ -31,3 +31,7 @@ class Node(object): self.name = value elif name == "id": self.id = value + + def setPos(self, x, y): + self.pos.x = x + self.pos.y = y diff --git a/src/view/NetworkWindow.py b/src/view/NetworkWindow.py index a9065028..5f2ab32a 100644 --- a/src/view/NetworkWindow.py +++ b/src/view/NetworkWindow.py @@ -63,11 +63,16 @@ class ComboBoxDelegate(QItemDelegate): class TableModel(QAbstractTableModel): - def __init__(self, headers=[], rows=[], graph=None): + def __init__(self, headers=[], graph=None, rows_type="nodes"): super(QAbstractTableModel, self).__init__() - self.rows = rows self.headers = headers self.graph = graph + self._type = rows_type + + if self._type == "nodes": + self.rows = graph.nodes() + elif self._type == "edges": + self.rows = graph.edges() def flags(self, index): return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable @@ -113,9 +118,12 @@ class TableModel(QAbstractTableModel): return False def update(self): - print("update") - self.layoutChanged.emit() + if self._type == "nodes": + self.rows = self.graph.nodes() + elif self._type == "edges": + self.rows = self.graph.edges() + self.layoutChanged.emit() class NetworkWindow(ASubWindow): def __init__(self, title="Network", parent=None): @@ -123,9 +131,6 @@ class NetworkWindow(ASubWindow): self.ui.setWindowTitle(title) self.graph = Graph() - n1 = self.graph.add_node() - n2 = self.graph.add_node(50.0,50.0) - e1 = self.graph.add_edge(n1,n2) # Graph Widget @@ -143,8 +148,8 @@ class NetworkWindow(ASubWindow): self.nodes_model = TableModel( headers = ["name", "id", "pos"], - rows = self.graph.nodes(), graph = self.graph, + rows_type="nodes", ) self.delegate_line = LineEditDelegate(parent=self) table = self.find(QTableView, "tableView_nodes") @@ -156,8 +161,8 @@ class NetworkWindow(ASubWindow): self.reachs_model = TableModel( headers = ["name", "node1", "node2"], - rows = self.graph.edges(), graph = self.graph, + rows_type="edges" ) self.delegate_combobox = ComboBoxDelegate( graph = self.graph, @@ -173,20 +178,26 @@ class NetworkWindow(ASubWindow): self.nodes_model.dataChanged.connect(self.reachs_model.update) self.reachs_model.dataChanged.connect(self.nodes_model.update) + self.graph_widget.changeEdge.connect(self.reachs_model.update) + self.graph_widget.changeNode.connect(self.nodes_model.update) self.find(QPushButton, "pushButton_add").clicked.connect( self.clicked_add ) - self.find(QPushButton, "pushButton_del").clicked.connect( self.clicked_del ) - 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" + else: + 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" + else: + self.graph_widget.state = "move" diff --git a/src/view/network/GraphWidget.py b/src/view/network/GraphWidget.py index a7684982..6bdb759d 100644 --- a/src/view/network/GraphWidget.py +++ b/src/view/network/GraphWidget.py @@ -8,7 +8,7 @@ from model.network.Graph import Graph from PyQt5.QtCore import ( qAbs, QLineF, QPointF, QPoint, qrand, QRectF, QSizeF, qsrand, - Qt, QTime + Qt, QTime, pyqtSlot, pyqtSignal, ) from PyQt5.QtGui import ( QBrush, QColor, QLinearGradient, QPainter, @@ -20,6 +20,36 @@ from PyQt5.QtWidgets import ( QGraphicsView, QStyle ) +class LandMark(QGraphicsItem): + def __init__(self, width, height, parent=None): + super(LandMark, self).__init__() + + self.width = width + self.height = height + + self.center = QPoint(width / 2, height / 2) + self.parent = parent + + def paint(self, painter, option, widget): + painter.setPen(Qt.NoPen) + painter.setPen(QPen(Qt.black, 0)) + + painter.drawLine( + -10 + self.center.x(), 0 + self.center.y(), + 10 + self.center.x(), 0 + self.center.y() + ) + painter.drawLine( + 0 + self.center.x(), -10 + self.center.y(), + 0 + self.center.x(), 10 + self.center.y() + ) + + def boundingRect(self): + return QRectF( + -10 + self.center.x(), -10 + self.center.y(), + 10 + self.center.x(), 10 + self.center.y() + ) + + class NodeItem(QGraphicsItem): Type = QGraphicsItem.UserType + 1 @@ -58,33 +88,39 @@ class NodeItem(QGraphicsItem): def paint(self, painter, option, widget): painter.setPen(Qt.NoPen) - # painter.setBrush(Qt.darkGray) - # painter.drawEllipse(-7, -7, 20, 20) - - gradient = QRadialGradient(-3, -3, 10) - if option.state & QStyle.State_Sunken: - gradient.setCenter(3, 3) - gradient.setFocalPoint(3, 3) - gradient.setColorAt(1, QColor(Qt.yellow).lighter(120)) - gradient.setColorAt(0, QColor(Qt.darkYellow).lighter(120)) - else: - gradient.setColorAt(0, Qt.yellow) - gradient.setColorAt(1, Qt.darkYellow) - painter.setBrush(QBrush(gradient)) - # painter.setPen(QPen(Qt.black, 0)) + color = Qt.yellow + if self.graph.selectedNode() == self: + color = Qt.red + + painter.setBrush(QBrush(color)) painter.drawEllipse(-10, -10, 20, 20) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionHasChanged: self.graph.itemMoved() + self.graph.nodeChangePosition(value, 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() + 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) @@ -191,17 +227,22 @@ class EdgeItem(QGraphicsItem): painter.drawPolygon(QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2])) painter.drawPolygon(QPolygonF([line.p2(), destArrowP1, destArrowP2])) - class GraphWidget(QGraphicsView): + changeEdge = pyqtSignal(object) + changeNode = pyqtSignal(object) + def __init__(self, graph, parent=None): super(GraphWidget, self).__init__(parent=parent) self.timerId = 0 self.parent = parent + self.state = "move" + + self._selectedNode = None scene = QGraphicsScene(self) scene.setItemIndexMethod(QGraphicsScene.NoIndex) - scene.setSceneRect(0, 0, 10000, 10000) + scene.setSceneRect(0, 0, 1000, 1000) self.setScene(scene) self.setCacheMode(QGraphicsView.CacheBackground) self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) @@ -209,23 +250,49 @@ class GraphWidget(QGraphicsView): self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorViewCenter) - # scene.addItem(LandMark()) - self.graph = graph + + self.scale(1, 1) + self.previousScale = 1 + self.setMinimumSize(400, 400) + + self.create_items() + + def create_items(self): for node in self.graph.nodes(): - scene.addItem(NodeItem(node, self)) + self.scene().addItem(NodeItem(node, self)) # for edge in self.graph.edges(): # scene.addItem(EdgeItem(edge)) - self.scale(1, 1) - self.previousScale = 1 - self.setMinimumSize(400, 400) + # self.mark = LandMark(1000, 1000, self) + # self.scene().addItem(self.mark) + + def add_node(self, pos): + node = self.graph.add_node(pos.x(), pos.y()) + self.scene().addItem(NodeItem(node, self)) + self.changeNode.emit(self.sender()) + + def del_node(self, node): + self.scene().clear() + self.graph.remove_node(node.name) + + self.create_items() + self.changeNode.emit(self.sender()) def itemMoved(self): if not self.timerId: self.timerId = self.startTimer(1000 / 25) + def nodeChangePosition(self, pos, node): + node.node.setPos(pos.x(), pos.y()) + + def selectedNode(self): + return self._selectedNode + + def setSelectedNode(self, node): + self._selectedNode = node + def keyPressEvent(self, event): key = event.key() @@ -270,3 +337,36 @@ class GraphWidget(QGraphicsView): # print(f"{scaleFactor} : {factor}") self.scale(scaleFactor, scaleFactor) + + 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 + + elif self.state == "add": + items = self.items(event.pos()) + if not items: + self.add_node(pos) + else: + print(f"TODO add edge {items}") + + 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: + self.del_node(nodes[0].node) + + self.update() + super(GraphWidget, self).mousePressEvent(event) + + def mouseReleaseEvent(self, event): + self.update() + super(GraphWidget, self).mouseReleaseEvent(event) + + def mouseMoveEvent(self, event): + if self.state == "move": + self.update() + super(GraphWidget, self).mouseMoveEvent(event) -- GitLab