From 874f1a0bd6d6d7d62c1226cc9244e633e4ac0f3d Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr> Date: Wed, 22 Mar 2023 16:26:54 +0100 Subject: [PATCH] network: Some minor change. --- src/model/network/Edge.py | 2 +- src/model/network/Graph.py | 21 ++++++- src/view/NetworkWindow.py | 107 ++++++++++++++++++++++++-------- src/view/network/GraphWidget.py | 9 +++ src/view/ui/Network.ui | 19 ++++++ 5 files changed, 129 insertions(+), 29 deletions(-) diff --git a/src/model/network/Edge.py b/src/model/network/Edge.py index 220003ef..1bb8cbb0 100644 --- a/src/model/network/Edge.py +++ b/src/model/network/Edge.py @@ -7,7 +7,7 @@ class Edge(object): super(Edge, self).__init__() self.id = id - self.name = name + self.name = name if name != "" else f"{node1.name} -> {node2.name}" self.node1 = node1 self.node2 = node2 diff --git a/src/model/network/Graph.py b/src/model/network/Graph.py index 02a119ea..ee0ac026 100644 --- a/src/model/network/Graph.py +++ b/src/model/network/Graph.py @@ -61,7 +61,7 @@ class Graph(object): return node def add_edge(self, n1:Node, n2:Node): - edge = Edge(self._edges_ids, f"Edge {self._edges_ids}", n1, n2) + edge = Edge(self._edges_ids, "", n1, n2) self._edges.append(edge) self._edges_ids += 1 return edge @@ -74,14 +74,29 @@ class Graph(object): def is_upstream_node(self, node): return reduce( - lambda acc, e: (acc and (e.node2 != node)), + lambda acc, e: (acc and (e.node2 != node or not e.enable)), self._edges, True ) def is_downstream_node(self, node): return reduce( - lambda acc, e: (acc and (e.node1 != node)), + lambda acc, e: (acc and (e.node1 != node or not e.enable)), self._edges, True ) + + def is_enable_node(self, node): + return reduce( + lambda acc, e: ( + acc or ( + (e.node1 == node or e.node2 == node) + and e.enable + ) + ), + self._edges, + False + ) + + def is_enable_edge(self, edge): + return edge.enable diff --git a/src/view/NetworkWindow.py b/src/view/NetworkWindow.py index 72c61a0b..d165e6ef 100644 --- a/src/view/NetworkWindow.py +++ b/src/view/NetworkWindow.py @@ -8,29 +8,15 @@ from view.network.GraphWidget import GraphWidget from PyQt5.QtCore import ( Qt, QRect, QVariant, QAbstractTableModel, pyqtSlot, pyqtSignal, + QEvent, ) from PyQt5.QtWidgets import ( QTableView, QItemDelegate, QComboBox, QLineEdit, QHBoxLayout, QSlider, - QPushButton, + QPushButton, QCheckBox, QStyledItemDelegate, QStyleOptionButton, QStyle, + QApplication, ) -class LineEditDelegate(QItemDelegate): - on_focus_out = pyqtSignal(object) - - def __init__(self, *args, **kwargs): - super(LineEditDelegate, self).__init__(*args, **kwargs) - self.line_edit = None - - def createEditor(self, parent, option, index): - self.line_edit = QLineEdit(parent=parent) - self.line_edit.destroyed.connect(lambda: self._line_edit_left(index=index)) - return self.line_edit - - def _line_edit_left(self, index): - self.on_focus_out.emit(index) - return - class ComboBoxDelegate(QItemDelegate): def __init__(self, graph=None, parent=None): super(ComboBoxDelegate, self).__init__(parent) @@ -61,6 +47,34 @@ class ComboBoxDelegate(QItemDelegate): def currentItemChanged(self): self.commitData.emit(self.sender()) +class TrueFalseComboBoxDelegate(QItemDelegate): + def __init__(self, parent=None): + super(TrueFalseComboBoxDelegate, self).__init__(parent) + + def createEditor(self, parent, option, index): + self.editor = QComboBox(parent) + self.editor.addItems(["true", "false"]) + return self.editor + + def setEditorData(self, editor, index): + value = str(index.data(Qt.DisplayRole)) + self.editor.currentTextChanged.connect(self.currentItemChanged) + + def setModelData(self, editor, model, index): + value = str(editor.currentText()) == "true" + model.setData(index, value) + editor.close() + editor.deleteLater() + + def updateEditorGeometry(self, editor, option, index): + r = QRect(option.rect) + if self.editor.windowFlags() & Qt.Popup and editor.parent() is not None: + r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft())) + editor.setGeometry(r) + + @pyqtSlot() + def currentItemChanged(self): + self.commitData.emit(self.sender()) class TableModel(QAbstractTableModel): def __init__(self, headers=[], graph=None, rows_type="nodes"): @@ -75,7 +89,12 @@ class TableModel(QAbstractTableModel): self.rows = graph.edges() def flags(self, index): - return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable + options = Qt.ItemIsEnabled | Qt.ItemIsSelectable + + if self.headers[index.column()] != "type": + options |= Qt.ItemIsEditable + + return options def rowCount(self, parent): return len(self.rows) @@ -87,14 +106,27 @@ class TableModel(QAbstractTableModel): if role != Qt.ItemDataRole.DisplayRole: return QVariant() + if self.headers[index.column()] == "type": + node = self.rows[index.row()] + ret = "internal" + + if not self.graph.is_enable_node(node): + ret = "disable" + elif self.graph.is_upstream_node(node): + ret = "upstream" + elif self.graph.is_downstream_node(node): + ret = "downstream" + + return ret + return self.rows[index.row()][self.headers[index.column()]] def headerData(self, section, orientation, role): if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal: return self.headers[section].capitalize() - if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Vertical: - return section + # if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Vertical: + # return section return QVariant() @@ -125,6 +157,14 @@ class TableModel(QAbstractTableModel): self.layoutChanged.emit() + def reverse_edge(self, index): + if self._type == "edges": + tmp = self.rows[index.row()].node1 + self.rows[index.row()].node1 = self.rows[index.row()].node2 + self.rows[index.row()].node2 = tmp + self.dataChanged.emit(index, index, [Qt.DisplayRole]) + self.layoutChanged.emit() + class NetworkWindow(ASubWindow): def __init__(self, title="Network", parent=None): super(NetworkWindow, self).__init__(name=title, ui="Network", parent=parent) @@ -147,20 +187,18 @@ class NetworkWindow(ASubWindow): # Nodes table self.nodes_model = TableModel( - headers = ["name", "id", "pos"], + headers = ["name", "type"], graph = self.graph, rows_type="nodes", ) - self.delegate_line = LineEditDelegate(parent=self) table = self.find(QTableView, "tableView_nodes") table.setModel(self.nodes_model) - table.setItemDelegate(self.delegate_line) #table.resizeColumnsToContents() # Edges table self.reachs_model = TableModel( - headers = ["name", "node1", "node2"], + headers = ["name", "enable", "node1", "node2"], graph = self.graph, rows_type="edges" ) @@ -168,16 +206,22 @@ class NetworkWindow(ASubWindow): graph = self.graph, parent = self, ) + self.delegate_true_false_combobox = TrueFalseComboBoxDelegate( + parent = self, + ) + table = self.find(QTableView, "tableView_reachs") table.setModel(self.reachs_model) - table.setItemDelegateForColumn(1, self.delegate_combobox) + table.setItemDelegateForColumn(1, self.delegate_true_false_combobox) table.setItemDelegateForColumn(2, self.delegate_combobox) + table.setItemDelegateForColumn(3, self.delegate_combobox) #table.resizeColumnsToContents() # Connection self.nodes_model.dataChanged.connect(self.reachs_model.update) self.nodes_model.dataChanged.connect(self.graph_widget.rename_nodes) + self.reachs_model.dataChanged.connect(self.graph_widget.display_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) @@ -188,6 +232,9 @@ class NetworkWindow(ASubWindow): self.find(QPushButton, "pushButton_del").clicked.connect( self.clicked_del ) + self.find(QPushButton, "pushButton_reverse").clicked.connect( + self.reverse_edge + ) def clicked_add(self): if self.get_push_button_checkable("pushButton_add"): @@ -208,3 +255,13 @@ class NetworkWindow(ASubWindow): if key == Qt.Key_Escape: self.graph_widget.reset_selection + + def reverse_edge(self): + indices = self.find(QTableView, "tableView_reachs").selectionModel().selectedIndexes() + indexes = {} + + for index in sorted(indices): + indexes[index.row()] = index + + for row in indexes: + self.reachs_model.reverse_edge(indexes[row]) diff --git a/src/view/network/GraphWidget.py b/src/view/network/GraphWidget.py index cfca8bdc..b796abc8 100644 --- a/src/view/network/GraphWidget.py +++ b/src/view/network/GraphWidget.py @@ -94,6 +94,8 @@ class NodeItem(QGraphicsItem): color = Qt.darkRed elif self.graph.selectedItem() == self: color = Qt.red + elif not self.graph.graph.is_enable_node(self.node): + color = Qt.darkGray elif self.graph.graph.is_upstream_node(self.node): color = Qt.blue elif self.graph.graph.is_downstream_node(self.node): @@ -184,6 +186,9 @@ class EdgeItem(QGraphicsItem): color = Qt.blue if self.graph.selectedItem() == self: color = Qt.red + elif not self.graph.graph.is_enable_edge(self.edge): + color = Qt.darkGray + painter.setPen(QPen(color, 3, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) @@ -380,6 +385,10 @@ class GraphWidget(QGraphicsView): if type(i) == NodeItem: self.texts[i].rename() + def display_update(self): + self.scene().clear() + self.create_items() + def add_edge(self, node1, node2): if node1 == node2: return diff --git a/src/view/ui/Network.ui b/src/view/ui/Network.ui index b2ba931a..202fc2fa 100644 --- a/src/view/ui/Network.ui +++ b/src/view/ui/Network.ui @@ -26,6 +26,12 @@ <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QPushButton" name="pushButton_add"> + <property name="maximumSize"> + <size> + <width>40</width> + <height>16777215</height> + </size> + </property> <property name="text"> <string/> </property> @@ -49,6 +55,12 @@ </item> <item> <widget class="QPushButton" name="pushButton_del"> + <property name="maximumSize"> + <size> + <width>40</width> + <height>16777215</height> + </size> + </property> <property name="text"> <string/> </property> @@ -118,6 +130,13 @@ </property> </widget> </item> + <item> + <widget class="QPushButton" name="pushButton_reverse"> + <property name="text"> + <string>Reverse</string> + </property> + </widget> + </item> <item> <spacer name="horizontalSpacer_5"> <property name="orientation"> -- GitLab