From 3d82b7be93ec513c3095efb210bf556df9757618 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Fri, 24 Mar 2023 13:28:01 +0100
Subject: [PATCH] network: New file for custom table model.

---
 src/view/NetworkWindow.py      | 166 ++-------------------------------
 src/view/network/TableModel.py | 166 +++++++++++++++++++++++++++++++++
 2 files changed, 176 insertions(+), 156 deletions(-)
 create mode 100644 src/view/network/TableModel.py

diff --git a/src/view/NetworkWindow.py b/src/view/NetworkWindow.py
index d165e6ef..4cf74d00 100644
--- a/src/view/NetworkWindow.py
+++ b/src/view/NetworkWindow.py
@@ -5,6 +5,9 @@ from model.network.Edge import Edge
 from model.network.Graph import Graph
 from view.ASubWindow import ASubWindow
 from view.network.GraphWidget import GraphWidget
+from view.network.TableModel import (
+    GraphTableModel, ComboBoxDelegate, TrueFalseComboBoxDelegate,
+)
 
 from PyQt5.QtCore import (
     Qt, QRect, QVariant, QAbstractTableModel, pyqtSlot, pyqtSignal,
@@ -17,154 +20,6 @@ from PyQt5.QtWidgets import (
     QApplication,
 )
 
-class ComboBoxDelegate(QItemDelegate):
-    def __init__(self, graph=None, parent=None):
-        super(ComboBoxDelegate, self).__init__(parent)
-        self.graph = graph
-
-    def createEditor(self, parent, option, index):
-        self.editor = QComboBox(parent)
-        self.editor.addItems(self.graph.nodes_names())
-        return self.editor
-
-    def setEditorData(self, editor, index):
-        value = index.data(Qt.DisplayRole)
-        self.editor.currentTextChanged.connect(self.currentItemChanged)
-
-    def setModelData(self, editor, model, index):
-        text = str(editor.currentText())
-        model.setData(index, text)
-        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 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"):
-        super(QAbstractTableModel, self).__init__()
-        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):
-        options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
-
-        if self.headers[index.column()] != "type":
-            options |= Qt.ItemIsEditable
-
-        return options
-
-    def rowCount(self, parent):
-        return len(self.rows)
-
-    def columnCount(self, parent):
-        return len(self.headers)
-
-    def data(self, index, role):
-        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
-
-        return QVariant()
-
-    @pyqtSlot()
-    def setData(self, index, value, role=Qt.EditRole):
-        if index.isValid():
-            if role == Qt.EditRole:
-                if (self.headers[index.column()] == "node1" or
-                    self.headers[index.column()] == "node2"):
-                    node = self.graph.node(value)
-                    self.rows[index.row()][self.headers[index.column()]] = node
-                else:
-                    self.rows[index.row()][self.headers[index.column()]] = value
-
-                self.dataChanged.emit(index, index, [Qt.DisplayRole])
-                self.layoutChanged.emit()
-                return True
-
-            self.dataChanged.emit(index, index)
-        else:
-            return False
-
-    def update(self):
-        if self._type == "nodes":
-            self.rows = self.graph.nodes()
-        elif self._type == "edges":
-            self.rows = self.graph.edges()
-
-        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)
@@ -177,16 +32,10 @@ class NetworkWindow(ASubWindow):
         self.graph_widget = GraphWidget(self.graph, parent=self)
         self.graph_layout = self.find(QHBoxLayout, "horizontalLayout_graph")
         self.graph_layout.addWidget(self.graph_widget)
-        # self.zoom_slider = QSlider(Qt.Orientation.Vertical, parent=self)
-        # self.zoom_slider.setMinimum(0)
-        # self.zoom_slider.setMaximum(99)
-        # self.zoom_slider.setValue(50)
-        # self.graph_layout.addWidget(self.zoom_slider)
-        # self.zoom_slider.valueChanged.connect(self.graph_widget.scaleViewSlider)
 
         # Nodes table
 
-        self.nodes_model = TableModel(
+        self.nodes_model = GraphTableModel(
             headers = ["name", "type"],
             graph = self.graph,
             rows_type="nodes",
@@ -197,7 +46,7 @@ class NetworkWindow(ASubWindow):
 
         # Edges table
 
-        self.reachs_model = TableModel(
+        self.reachs_model = GraphTableModel(
             headers = ["name", "enable", "node1", "node2"],
             graph = self.graph,
             rows_type="edges"
@@ -257,6 +106,11 @@ class NetworkWindow(ASubWindow):
             self.graph_widget.reset_selection
 
     def reverse_edge(self):
+        """Reverse edge direction of selected table rows
+
+        Returns:
+            Nothing
+        """
         indices = self.find(QTableView, "tableView_reachs").selectionModel().selectedIndexes()
         indexes = {}
 
diff --git a/src/view/network/TableModel.py b/src/view/network/TableModel.py
new file mode 100644
index 00000000..0b8534c2
--- /dev/null
+++ b/src/view/network/TableModel.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+
+from model.network.Node import Node
+from model.network.Edge import Edge
+from model.network.Graph import Graph
+from view.ASubWindow import ASubWindow
+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, QCheckBox, QStyledItemDelegate, QStyleOptionButton, QStyle,
+    QApplication,
+)
+
+class ComboBoxDelegate(QItemDelegate):
+    def __init__(self, graph=None, parent=None):
+        super(ComboBoxDelegate, self).__init__(parent)
+        self.graph = graph
+
+    def createEditor(self, parent, option, index):
+        self.editor = QComboBox(parent)
+        self.editor.addItems(self.graph.nodes_names())
+        return self.editor
+
+    def setEditorData(self, editor, index):
+        value = index.data(Qt.DisplayRole)
+        self.editor.currentTextChanged.connect(self.currentItemChanged)
+
+    def setModelData(self, editor, model, index):
+        text = str(editor.currentText())
+        model.setData(index, text)
+        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 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 GraphTableModel(QAbstractTableModel):
+    def __init__(self, headers=[], graph=None, rows_type="nodes"):
+        super(QAbstractTableModel, self).__init__()
+        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):
+        options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
+
+        if self.headers[index.column()] != "type":
+            options |= Qt.ItemIsEditable
+
+        return options
+
+    def rowCount(self, parent):
+        return len(self.rows)
+
+    def columnCount(self, parent):
+        return len(self.headers)
+
+    def data(self, index, role):
+        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
+
+        return QVariant()
+
+    @pyqtSlot()
+    def setData(self, index, value, role=Qt.EditRole):
+        if index.isValid():
+            if role == Qt.EditRole:
+                if (self.headers[index.column()] == "node1" or
+                    self.headers[index.column()] == "node2"):
+                    node = self.graph.node(value)
+                    self.rows[index.row()][self.headers[index.column()]] = node
+                else:
+                    self.rows[index.row()][self.headers[index.column()]] = value
+
+                self.dataChanged.emit(index, index, [Qt.DisplayRole])
+                self.layoutChanged.emit()
+                return True
+
+            self.dataChanged.emit(index, index)
+        else:
+            return False
+
+    def update(self):
+        if self._type == "nodes":
+            self.rows = self.graph.nodes()
+        elif self._type == "edges":
+            self.rows = self.graph.edges()
+
+        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()
-- 
GitLab