From 3617fde986cdc9fc24119888c4c1d297719b13d2 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Tue, 9 May 2023 13:36:44 +0200
Subject: [PATCH] BC: Add multiple tab for liquid, solid and suspenssion.

---
 .../BoundaryCondition/BoundaryCondition.py    |   4 +
 .../BoundaryConditionList.py                  |  60 +++++++---
 .../BoundaryConditionTypes.py                 |  16 +++
 src/View/BoundaryCondition/BCUndoCommand.py   | 106 ++++++++++--------
 .../BoundaryConditionWindow.py                |  93 +++++++++------
 src/View/BoundaryCondition/Table.py           |  48 +++++---
 src/View/ui/BoundaryConditions.ui             |   6 +-
 7 files changed, 213 insertions(+), 120 deletions(-)

diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py
index d5486eaa..5bd26189 100644
--- a/src/Model/BoundaryCondition/BoundaryCondition.py
+++ b/src/Model/BoundaryCondition/BoundaryCondition.py
@@ -18,6 +18,10 @@ class BoundaryCondition(object):
     def __len__(self):
         return len(self._data)
 
+    @classmethod
+    def compatibility(cls):
+        return ["liquid", "solid", "suspenssion"]
+
     @property
     def name(self):
         return self._name
diff --git a/src/Model/BoundaryCondition/BoundaryConditionList.py b/src/Model/BoundaryCondition/BoundaryConditionList.py
index a44629d4..4b14d924 100644
--- a/src/Model/BoundaryCondition/BoundaryConditionList.py
+++ b/src/Model/BoundaryCondition/BoundaryConditionList.py
@@ -11,56 +11,82 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
     TimeOverZ, TimeOverDebit, ZOverDebit
 )
 
-class BoundaryConditionList(list):
+class BoundaryConditionList(object):
     def __init__(self):
         super(BoundaryConditionList, self).__init__()
 
-    def new(self, index):
+        self._tabs = {
+            "liquid" : [],
+            "solid" : [],
+            "suspenssion" : []
+        }
+
+    def len(self, lst):
+        return len(self._tabs[lst])
+
+    def get_tab(self, lst):
+        return self._tabs[lst].copy()
+
+    def get(self, lst, row):
+        return self._tabs[lst][row]
+
+    def set(self, lst, row, new):
+        self._tabs[lst][row] = new
+
+    def new(self, lst, index):
         n = NotDefined()
-        self.insert(index, n)
+        self._tabs[lst].insert(index, n)
         return n
 
-    def delete(self, bcs):
+    def insert(self, lst, index, new):
+        self._tabs[lst].insert(index, new)
+
+    def delete(self, lst, bcs):
         for bc in bcs:
-            self.remove(bc)
+            self._tabs[lst].remove(bc)
 
-    def delete_i(self, indexes):
+    def delete_i(self, lst, indexes):
         bcs = list(
             map(
                 lambda x: x[1],
                 filter(
                     lambda x: x[0] in indexes,
-                    enumerate(self)
+                    enumerate(self._tabs[lst])
                 )
             )
         )
-        self.delete(bcs)
+        self.delete(lst, bcs)
+
+    def sort(self, lst, reverse=False, key=None):
+        self._tabs[lst].sort(reverse=reverse, key=key)
 
-    def move_up(self, index):
-        if index < len(self):
+    def move_up(self, lst, index):
+        if index < len(self._tabs[lst]):
             next = index - 1
 
-            self[index], self[next] = self[next], self[index]
+            l = self._tabs[lst]
+            l[index], l[next] = l[next], l[index]
 
-    def move_down(self, index):
+    def move_down(self, lst, index):
         if index >= 0:
             prev = index + 1
 
-            self[index], self[prev] = self[prev], self[index]
+            l = self._tabs[lst]
+            l[index], l[prev] = l[prev], l[index]
 
     def __copy__(self):
         new = BoundaryConditionList()
 
-        for bc in self:
-            new.append(bc)
+        for l in self._tabs:
+            new.tabs[l] = self._tabs[l].copy()
 
         return new
 
     def __deepcopy__(self):
         new = BoundaryConditionList()
 
-        for bc in self:
-            new.append(deepcopy(bc))
+        for l in self._tabs:
+            new.tabs[l] = self._tabs[l].deepcopy()
 
         return new
 
diff --git a/src/Model/BoundaryCondition/BoundaryConditionTypes.py b/src/Model/BoundaryCondition/BoundaryConditionTypes.py
index 327bc3a5..85113389 100644
--- a/src/Model/BoundaryCondition/BoundaryConditionTypes.py
+++ b/src/Model/BoundaryCondition/BoundaryConditionTypes.py
@@ -19,6 +19,10 @@ class PonctualContribution(BoundaryCondition):
         self._type = "PC"
         self._header = ["time", "debit"]
 
+    @classmethod
+    def compatibility(cls):
+        return ["liquid"]
+
 class TimeOverZ(BoundaryCondition):
     def __init__(self, name:str = ""):
         super(TimeOverZ, self).__init__(name=name)
@@ -26,6 +30,10 @@ class TimeOverZ(BoundaryCondition):
         self._type = "TZ"
         self._header = ["time", "z"]
 
+    @classmethod
+    def compatibility(cls):
+        return ["liquid"]
+
 class TimeOverDebit(BoundaryCondition):
     def __init__(self, name:str = ""):
         super(TimeOverDebit, self).__init__(name=name)
@@ -33,6 +41,10 @@ class TimeOverDebit(BoundaryCondition):
         self._type = "TD"
         self._header = ["time", "debit"]
 
+    @classmethod
+    def compatibility(cls):
+        return ["liquid"]
+
 class ZOverDebit(BoundaryCondition):
     def __init__(self, name:str = ""):
         super(ZOverDebit, self).__init__(name=name)
@@ -41,6 +53,10 @@ class ZOverDebit(BoundaryCondition):
         self._header = ["z", "debit"]
         self._types = [float, float]
 
+    @classmethod
+    def compatibility(cls):
+        return ["liquid"]
+
     @property
     def _default_0(self):
         return 0.0
diff --git a/src/View/BoundaryCondition/BCUndoCommand.py b/src/View/BoundaryCondition/BCUndoCommand.py
index 3cd3ec9f..7b1c7f5e 100644
--- a/src/View/BoundaryCondition/BCUndoCommand.py
+++ b/src/View/BoundaryCondition/BCUndoCommand.py
@@ -11,105 +11,114 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
 from Model.BoundaryCondition.BoundaryConditionList import BoundaryConditionList
 
 class SetNameCommand(QUndoCommand):
-    def __init__(self, lst, index, new_value):
+    def __init__(self, bcs, tab, index, new_value):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._index = index
-        self._old = self._lst[self._index].name
+        self._old = self._bcs.get(self._tab, self._index).name
         self._new = new_value
 
     def undo(self):
-        self._lst[self._index].name = self._old
+        self._bcs.get(self._tab, self._index).name = self._old
 
     def redo(self):
-        self._lst[self._index].name = self._new
+        self._bcs.get(self._tab, self._index).name = self._new
 
 class SetNodeCommand(QUndoCommand):
-    def __init__(self, lst, index, node):
+    def __init__(self, bcs, tab, index, node):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._index = index
-        self._old = self._lst[index].node
+        self._old = self._bcs.get(self._tab, self._index).node
         self._new = node
 
     def undo(self):
-        self._lst[self._index].node = self._old
+        self._bcs.get(self._tab, self._index).node = self._old
 
     def redo(self):
-        self._lst[self._index].node = self._new
+        self._bcs.get(self._tab, self._index).node = self._new
 
 class SetTypeCommand(QUndoCommand):
-    def __init__(self, lst, index, _type):
+    def __init__(self, bcs, tab, index, _type):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._index = index
         self._type = _type
-        self._old = self._lst[index]
-        self._new = self._lst[index].convert(self._type)
+        self._old = self._bcs.get(self._tab, self._index)
+        self._new = self._bcs.get(self._tab, self._index)\
+                             .convert(self._type)
 
     def undo(self):
-        self._lst[self._index] = self._old
+        self._bcs.set(self._tab, self._index, self._old)
 
     def redo(self):
-        self._lst[self._index] = self._new
+        self._bcs.set(self._tab, self._index, self._new)
 
 class AddCommand(QUndoCommand):
-    def __init__(self, lst, index):
+    def __init__(self, bcs, tab, index):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._index = index
         self._new = None
 
     def undo(self):
-        self._lst.delete_i([self._index])
+        self._bcs.delete_i(self._tab, [self._index])
 
     def redo(self):
         if self._new is None:
-            self._new = self._lst.new(self._index)
+            self._new = self._bcs.new(self._tab, self._index)
         else:
-            self._lst.insert(self._index, self._new)
+            self._bcs.insert(self._tab, self._index, self._new)
 
 class DelCommand(QUndoCommand):
-    def __init__(self, lst, rows):
+    def __init__(self, bcs, tab, rows):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._rows = rows
 
         self._bc = []
         for row in rows:
-            self._bc.append((row, self._lst[row]))
+            self._bc.append((row, self._bcs.get(self._tab, row)))
         self._bc.sort()
 
     def undo(self):
         for row, el in self._bc:
-            self._lst.insert(row, el)
+            self._bcs.insert(self._tab, row, el)
 
     def redo(self):
-        self._lst.delete_i(self._rows)
+        self._bcs.delete_i(self._tab, self._rows)
 
 class SortCommand(QUndoCommand):
-    def __init__(self, lst, _reverse):
+    def __init__(self, bcs, tab, _reverse):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._reverse = _reverse
 
-        self._old = self._lst.copy()
+        self._old = self._bcs.get_tab(self._tab)
         self._indexes = None
 
     def undo(self):
-        ll = self._lst.copy()
-        self._lst.sort(
+        ll = self._bcs.get_tab(self._tab)
+        self._bcs.sort(
+            self._tab,
             key=lambda x: self._indexes[ll.index(x)]
         )
 
     def redo(self):
-        self._lst.sort(
+        self._bcs.sort(
+            self._tab,
             reverse=self._reverse,
             key=lambda x: x.name
         )
@@ -117,62 +126,65 @@ class SortCommand(QUndoCommand):
             self._indexes = list(
                 map(
                     lambda p: self._old.index(p),
-                    self._lst
+                    self._bcs.get_tab(self._tab)
                 )
             )
             self._old = None
 
 
 class MoveCommand(QUndoCommand):
-    def __init__(self, lst, up, i):
+    def __init__(self, bcs, tab, up, i):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._up = up == "up"
         self._i = i
 
     def undo(self):
         if self._up:
-            self._lst.move_up(self._i)
+            self._bcs.move_up(self._tab, self._i)
         else:
-            self._lst.move_down(self._i)
+            self._bcs.move_down(self._tab, self._i)
 
     def redo(self):
         if self._up:
-            self._lst.move_up(self._i)
+            self._bcs.move_up(self._tab, self._i)
         else:
-            self._lst.move_down(self._i)
+            self._bcs.move_down(self._tab, self._i)
 
 
 class PasteCommand(QUndoCommand):
-    def __init__(self, lst, row, bc):
+    def __init__(self, bcs, tab, row, bc):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._row = row
         self._bc = deepcopy(bc)
         self._bc.reverse()
 
     def undo(self):
-        self._lst.delete(self._bc)
+        self._bcs.delete(self._tab, self._bc)
 
     def redo(self):
         for bc in self._bc:
-            self._lst.insert(self._row, bc)
+            self._bcs.insert(self._tab, self._row, bc)
 
 
 class DuplicateCommand(QUndoCommand):
-    def __init__(self, lst, rows, bc):
+    def __init__(self, bcs, tab, rows, bc):
         QUndoCommand.__init__(self)
 
-        self._lst = lst
+        self._bcs = bcs
+        self._tab = tab
         self._rows = rows
         self._bc = deepcopy(bc)
         self._bc.reverse()
 
     def undo(self):
-        self._lst.delete(self._bc)
+        self._bcs.delete(self._tab, self._bc)
 
     def redo(self):
         for bc in self._bcs:
-            self._lst.insert(self._rows[0], bc)
+            self._bcs.insert(self._tab, self._rows[0], bc)
diff --git a/src/View/BoundaryCondition/BoundaryConditionWindow.py b/src/View/BoundaryCondition/BoundaryConditionWindow.py
index 38b9e6b1..c14ef005 100644
--- a/src/View/BoundaryCondition/BoundaryConditionWindow.py
+++ b/src/View/BoundaryCondition/BoundaryConditionWindow.py
@@ -19,7 +19,7 @@ from PyQt5.QtWidgets import (
     QDialogButtonBox, QPushButton, QLineEdit,
     QFileDialog, QTableView, QAbstractItemView,
     QUndoStack, QShortcut, QAction, QItemDelegate,
-    QComboBox, QVBoxLayout, QHeaderView
+    QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
 )
 
 from View.BoundaryCondition.BCUndoCommand import (
@@ -69,32 +69,40 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
         self.paste_sc = QShortcut(QKeySequence.Paste, self)
 
     def setup_table(self):
-        table = self.find(QTableView, "tableView_liquid")
-        self._table = TableModel(
-            data = self._study.river,
-            undo = self._undo_stack
-        )
-        table.setModel(self._table)
+        self._table = {}
+
+        for t in ["liquid", "solid", "suspenssion"]:
+            table = self.find(QTableView, f"tableView_{t}")
+            self._table[t] = TableModel(
+                data = self._study.river,
+                undo = self._undo_stack,
+                tab = t,
+            )
+            table.setModel(self._table[t])
 
-        self._delegate_type = ComboBoxDelegate(
-            data = self._study.river,
-            mode = "type"
-        )
-        self._delegate_node = ComboBoxDelegate(
-            data = self._study.river,
-            mode = "node"
-        )
+            self._delegate_type = ComboBoxDelegate(
+                data = self._study.river,
+                mode = "type",
+                tab = t,
+                parent=self
+            )
+            self._delegate_node = ComboBoxDelegate(
+                data = self._study.river,
+                mode = "node",
+                tab = t,
+                parent=self
+            )
 
-        table.setItemDelegateForColumn(
-            1, self._delegate_type
-        )
-        table.setItemDelegateForColumn(
-            2, self._delegate_node
-        )
+            table.setItemDelegateForColumn(
+                1, self._delegate_type
+            )
+            table.setItemDelegateForColumn(
+                2, self._delegate_node
+            )
 
-        table.setSelectionBehavior(QAbstractItemView.SelectRows)
-        table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
-        table.setAlternatingRowColors(True)
+            table.setSelectionBehavior(QAbstractItemView.SelectRows)
+            table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+            table.setAlternatingRowColors(True)
 
     def setup_graph(self):
         self.graph_widget = GraphWidget(
@@ -117,15 +125,22 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
         self.copy_sc.activated.connect(self.copy)
         self.paste_sc.activated.connect(self.paste)
 
+    def current_tab(self):
+        return self.find(QTabWidget, "tabWidget")\
+                   .currentWidget()\
+                   .objectName()\
+                   .replace("tab_", "")
 
     def index_selected_row(self):
-        table = self.find(QTableView, "tableView_liquid")
+        tab = self.current_tab()
+        table = self.find(QTableView, f"tableView_{tab}")
         return table.selectionModel()\
                     .selectedRows()[0]\
                     .row()
 
     def index_selected_rows(self):
-        table = self.find(QTableView, "tableView_liquid")
+        tab = self.current_tab()
+        table = self.find(QTableView, f"tableView_{tab}")
         return list(
             # Delete duplicate
             set(
@@ -137,30 +152,34 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
         )
 
     def add(self):
+        tab = self.current_tab()
         rows = self.index_selected_rows()
-        if len(self._lst) == 0 or len(rows) == 0:
-            self._table.add(0)
+        if self._lst.len(tab) == 0 or len(rows) == 0:
+            self._table[tab].add(0)
         else:
-            self._table.add(rows[0])
+            self._table[tab].add(rows[0])
 
     def delete(self):
+        tab = self.current_tab()
         rows = self.index_selected_rows()
         if len(rows) == 0:
             return
 
-        self._table.delete(rows)
+        self._table[tab].delete(rows)
 
     def sort(self):
-        self._table.sort(False)
+        tab = self.current_tab()
+        self._table[tab].sort(False)
 
     def move_up(self):
+        tab = self.current_tab()
         row = self.index_selected_row()
-        self._table.move_up(row)
+        self._table[tab].move_up(row)
 
     def move_down(self):
+        tab = self.current_tab()
         row = self.index_selected_row()
-        self._table.move_down(row)
-
+        self._table[tab].move_down(row)
 
     def copy(self):
         print("TODO")
@@ -169,10 +188,12 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
         print("TODO")
 
     def undo(self):
-        self._table.undo()
+        tab = self.current_tab()
+        self._table[tab].undo()
 
     def redo(self):
-        self._table.redo()
+        tab = self.current_tab()
+        self._table[tab].redo()
 
     def edit(self):
         rows = self.index_selected_rows()
diff --git a/src/View/BoundaryCondition/Table.py b/src/View/BoundaryCondition/Table.py
index f7bc377a..019f4642 100644
--- a/src/View/BoundaryCondition/Table.py
+++ b/src/View/BoundaryCondition/Table.py
@@ -30,17 +30,30 @@ from View.BoundaryCondition.translate import *
 _translate = QCoreApplication.translate
 
 class ComboBoxDelegate(QItemDelegate):
-    def __init__(self, data=None, mode="type", parent=None):
+    def __init__(self, data=None, mode="type", tab="", parent=None):
         super(ComboBoxDelegate, self).__init__(parent)
 
         self._data = data
         self._mode = mode
+        self._tab = tab
 
     def createEditor(self, parent, option, index):
         self.editor = QComboBox(parent)
 
         if self._mode == "type":
-            self.editor.addItems([long_types[k] for k in BC_types.keys()])
+            lst = list(
+                map(
+                    lambda k: long_types[k],
+                    filter(
+                        lambda k: self._tab in BC_types[k].compatibility(),
+                        BC_types.keys()
+                    )
+                )
+            )
+            print(lst)
+            self.editor.addItems(
+                lst
+            )
         else:
             self.editor.addItems(
                 [_translate("BoundaryCondition", "Not associate")] +
@@ -72,12 +85,13 @@ class ComboBoxDelegate(QItemDelegate):
 
 
 class TableModel(QAbstractTableModel):
-    def __init__(self, data=None, undo=None):
+    def __init__(self, data=None, undo=None, tab=""):
         super(QAbstractTableModel, self).__init__()
         self._headers = list(table_headers.keys())
         self._data = data
         self._undo = undo
-        self._lst = self._data.boundary_condition
+        self._tab = tab
+        self._bcs = self._data.boundary_condition
 
     def flags(self, index):
         options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
@@ -86,7 +100,7 @@ class TableModel(QAbstractTableModel):
         return options
 
     def rowCount(self, parent):
-        return len(self._lst)
+        return self._bcs.len(self._tab)
 
     def columnCount(self, parent):
         return len(self._headers)
@@ -99,12 +113,12 @@ class TableModel(QAbstractTableModel):
         column = index.column()
 
         if self._headers[column] == "name":
-            return self._lst[row].name
+            return self._bcs.get(self._tab, row).name
         elif self._headers[column] == "type":
-            t = self._lst[row].bctype
+            t = self._bcs.get(self._tab, row).bctype
             return long_types[t]
         elif self._headers[column] == "node":
-            n = self._lst[row].node
+            n = self._bcs.get(self._tab, row).node
             if n is None:
                 return _translate("BoundaryCondition", "Not associate")
             return n.name
@@ -127,20 +141,20 @@ class TableModel(QAbstractTableModel):
         if self._headers[column] == "name":
             self._undo.push(
                 SetNameCommand(
-                    self._lst, row, value
+                    self._bcs, self._tab,row, value
                 )
             )
         elif self._headers[column] == "type":
             key = next(k for k, v in long_types.items() if v == value)
             self._undo.push(
                 SetTypeCommand(
-                    self._lst, row, BC_types[key]
+                    self._bcs, self._tab,row, BC_types[key]
                 )
             )
         elif self._headers[column] == "node":
             self._undo.push(
                 SetNodeCommand(
-                    self._lst, row, self._data.node(value)
+                    self._bcs, self._tab,row, self._data.node(value)
                 )
             )
 
@@ -152,7 +166,7 @@ class TableModel(QAbstractTableModel):
 
         self._undo.push(
             AddCommand(
-                self._lst, row
+                self._bcs, self._tab,row
             )
         )
 
@@ -164,7 +178,7 @@ class TableModel(QAbstractTableModel):
 
         self._undo.push(
             DelCommand(
-                self._lst, rows
+                self._bcs, self._tab,rows
             )
         )
 
@@ -176,7 +190,7 @@ class TableModel(QAbstractTableModel):
 
         self._undo.push(
             SortCommand(
-                self._lst, False
+                self._bcs, self._tab,False
             )
         )
 
@@ -193,7 +207,7 @@ class TableModel(QAbstractTableModel):
 
         self._undo_stack.push(
             MoveCommand(
-                self._lst, "up", row
+                self._bcs, self._tab,"up", row
             )
         )
 
@@ -201,7 +215,7 @@ class TableModel(QAbstractTableModel):
         self.layoutChanged.emit()
 
     def move_down(self, index, parent=QModelIndex()):
-        if row > len(self._lst):
+        if row > len(self._bcs):
             return
 
         target = row
@@ -210,7 +224,7 @@ class TableModel(QAbstractTableModel):
 
         self._undo_stack.push(
             MoveCommand(
-                self._lst, "down", row
+                self._bcs, self._tab,"down", row
             )
         )
 
diff --git a/src/View/ui/BoundaryConditions.ui b/src/View/ui/BoundaryConditions.ui
index 602a1887..3097fd63 100644
--- a/src/View/ui/BoundaryConditions.ui
+++ b/src/View/ui/BoundaryConditions.ui
@@ -33,7 +33,7 @@
        <property name="currentIndex">
         <number>0</number>
        </property>
-       <widget class="QWidget" name="tab">
+       <widget class="QWidget" name="tab_liquid">
         <attribute name="title">
          <string>Liquid</string>
         </attribute>
@@ -43,7 +43,7 @@
          </item>
         </layout>
        </widget>
-       <widget class="QWidget" name="tab_2">
+       <widget class="QWidget" name="tab_solid">
         <attribute name="title">
          <string>Solid</string>
         </attribute>
@@ -53,7 +53,7 @@
          </item>
         </layout>
        </widget>
-       <widget class="QWidget" name="tab_3">
+       <widget class="QWidget" name="tab_suspenssion">
         <attribute name="title">
          <string>Suspenssion</string>
         </attribute>
-- 
GitLab