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