DIF Model

adists_num
Youcef AOUAD 2024-06-24 11:23:09 +02:00
parent 95a0dabe68
commit de72e2e96b
11 changed files with 1377 additions and 1 deletions

View File

@ -0,0 +1,258 @@
# DIFAdisTS.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import trace, timer, old_pamhyr_date_to_timestamp
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.DIFAdisTS.DIFAdisTSSpec import DIFAdisTSSpec
logger = logging.getLogger()
class DIFAdisTS(SQLSubModel):
_sub_classes = [
DIFAdisTSSpec,
]
_id_cnt = 0
def __init__(self, id: int = -1, name: str = "default",
status=None):
super(DIFAdisTS, self).__init__()
self._status = status
if id == -1:
self.id = DIFAdisTS._id_cnt
else:
self.id = id
self._name = name
self._method = None
self._dif = None
self._b = None
self._c = None
self._enabled = True
self._types = ["iwasa", "fisher", "elder", "constante", "generique"]
self._data = []
DIFAdisTS._id_cnt = max(
DIFAdisTS._id_cnt + 1,
self.id
)
@classmethod
def _db_create(cls, execute):
execute("""
CREATE TABLE dif_adists(
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
method TEXT NOT NULL,
dif REAL NOT NULL,
b REAL,
c REAL,
enabled BOOLEAN NOT NULL
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 6:
cls._db_create(execute)
return True
@classmethod
def _db_load(cls, execute, data=None):
new = []
table = execute(
"SELECT id, name, method, dif, b, c, enabled " +
"FROM dif_adists"
)
if table is not None:
for row in table:
dif_id = row[0]
name = row[1]
method = row[2]
dif = row[3]
b = row[4]
c = row[5]
enabled = (row[6] == 1)
DIF = cls(
id=dif_id,
name=name,
status=data['status']
)
DIF.method = method
DIF.dif = dif
DIF.b = b
DIF.c = c
DIF.enabled = enabled
data['dif_default_id'] = dif_id
DIF._data = DIFAdisTSSpec._db_load(execute, data)
new.append(DIF)
return new
def _db_save(self, execute, data=None):
execute(f"DELETE FROM dif_adists WHERE id = {self.id}")
method = ""
if self.method is not None:
method = self.method
dif = -1.
if self.dif is not None:
dif = self.dif
b = -1.
if self.b is not None:
b = self.b
c = -1.
if self.dif is not None:
c = self.c
sql = (
"INSERT INTO " +
"dif_adists(" +
"id, name, method, dif, b, c, enabled" +
") " +
"VALUES (" +
f"{self.id}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._method)}', " +
f"{dif}, {b}, {c}, {self._enabled}" +
")"
)
execute(sql)
data['dif_default_id'] = self.id
execute(
"DELETE FROM dif_spec " +
f"WHERE dif_default = {self.id}"
)
for dif_spec in self._data:
dif_spec._db_save(execute, data)
return True
def __len__(self):
return len(self._data)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
self._status.modified()
@property
def method(self):
return self._method
@method.setter
def method(self, method):
self._method = method
self._status.modified()
@property
def types(self):
return self._types
@property
def dif(self):
return self._dif
@dif.setter
def dif(self, dif):
self._dif = dif
self._status.modified()
@property
def b(self):
return self._b
@b.setter
def b(self, b):
self._b = b
self._status.modified()
@property
def c(self):
return self._c
@c.setter
def c(self, c):
self._c = c
self._status.modified()
@property
def enabled(self):
return self._enabled
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self._status.modified()
def new(self, index):
n = DIFAdisTSSpec(status=self._status)
self._data.insert(index, n)
self._status.modified()
return n
def delete(self, data):
self._data = list(
filter(
lambda x: x not in data,
self._data
)
)
self._status.modified()
def delete_i(self, indexes):
for ind in indexes:
del self._data[ind]
self._status.modified()
def insert(self, index, data):
self._data.insert(index, data)
self._status.modified()

View File

@ -0,0 +1,62 @@
# DIFAdisTSList.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from copy import copy
from tools import trace, timer
from Model.Tools.PamhyrList import PamhyrModelList
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
class DIFAdisTSList(PamhyrModelList):
_sub_classes = [
DIFAdisTS,
]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data['status'])
if data is None:
data = {}
new._lst = DIFAdisTS._db_load(
execute, data
)
return new
def _db_save(self, execute, data=None):
execute("DELETE FROM dif_adists")
if data is None:
data = {}
for dif in self._lst:
dif._db_save(execute, data=data)
return True
def new(self, index):
n = DIFAdisTS(status=self._status)
self._lst.insert(index, n)
self._status.modified()
return n

View File

@ -0,0 +1,228 @@
# DIFAdisTSSpec.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from tools import trace, timer
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
class DIFAdisTSSpec(SQLSubModel):
_sub_classes = [
]
_id_cnt = 0
def __init__(self, id: int = -1, method: str = "",
status=None):
super(DIFAdisTSSpec, self).__init__()
self._status = status
if id == -1:
self.id = DIFAdisTSSpec._id_cnt
else:
self.id = id
self._method = method
self._reach = None
self._start_kp = None
self._end_kp = None
self._dif = None
self._b = None
self._c = None
self._enabled = True
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
@classmethod
def _db_create(cls, execute):
execute("""
CREATE TABLE dif_spec(
id INTEGER NOT NULL PRIMARY KEY,
dif_default INTEGER NOT NULL,
method TEXT NOT NULL,
reach INTEGER NOT NULL,
start_kp REAL NOT NULL,
end_kp REAL NOT NULL,
dif REAL NOT NULL,
b REAL,
c REAL,
enabled BOOLEAN NOT NULL,
FOREIGN KEY(dif_default) REFERENCES dif_adists(id),
FOREIGN KEY(reach) REFERENCES river_reach(id)
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 6:
cls._db_create(execute)
return True
@classmethod
def _db_load(cls, execute, data=None):
new = []
table = execute(
"SELECT id, dif_default, method, reach, start_kp, end_kp, " +
"dif, b, c, enabled " +
"FROM dif_spec " +
f"WHERE dif_default = {data['dif_default_id']} "
)
for row in table:
id = row[0]
method = row[2]
reach = row[3]
start_kp = row[4]
end_kp = row[5]
dif = row[6]
b = row[7]
c = row[8]
enabled = (row[9] == 1)
new_spec = cls(
id=id,
method=method,
status=data['status']
)
new_spec.reach = reach
new_spec.start_kp = start_kp
new_spec.end_kp = end_kp
new_spec.dif = dif
new_spec.b = b
new_spec.c = c
new_spec.enabled = enabled
new.append(new_spec)
return new
def _db_save(self, execute, data=None):
dif_default = data['dif_default_id']
sql = (
"INSERT INTO " +
"dif_spec(id, dif_default, method, reach, " +
"start_kp, end_kp, dif, b, c, enabled) " +
"VALUES (" +
f"{self.id}, " +
f"{dif_default}, " +
f"'{self._db_format(self._method)}', " +
f"{self._reach}, " +
f"{self._start_kp}, " +
f"{self._end_kp}, " +
f"{self._dif}, " +
f"{self._b}, " +
f"{self._c}, " +
f"{self._enabled}" +
")"
)
execute(sql)
return True
@property
def method(self):
return self._method
@method.setter
def method(self, method):
self._method = method
self._status.modified()
@property
def reach(self):
return self._reach
@reach.setter
def reach(self, reach):
self._reach = reach
self._status.modified()
@property
def start_kp(self):
return self._start_kp
@start_kp.setter
def start_kp(self, start_kp):
self._start_kp = start_kp
self._status.modified()
@property
def end_kp(self):
return self._end_kp
@end_kp.setter
def end_kp(self, end_kp):
self._end_kp = end_kp
self._status.modified()
@property
def dif(self):
return self._dif
@dif.setter
def dif(self, dif):
self._dif = dif
self._status.modified()
@property
def b(self):
return self._b
@b.setter
def b(self, b):
self._b = b
self._status.modified()
@property
def c(self):
return self._c
@c.setter
def c(self, c):
self._c = c
self._status.modified()
@property
def enabled(self):
return self._enabled
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self._status.modified()

View File

@ -51,6 +51,7 @@ from Model.InitialConditionsAdisTS.InitialConditionsAdisTSList import InitialCon
from Model.BoundaryConditionsAdisTS.BoundaryConditionsAdisTSList import BoundaryConditionsAdisTSList
from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList import LateralContributionsAdisTSList
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
class RiverNode(Node, SQLSubModel):
@ -241,6 +242,7 @@ class River(Graph, SQLSubModel):
BoundaryConditionsAdisTSList,
LateralContributionsAdisTSList,
D90AdisTSList,
DIFAdisTSList,
]
def __init__(self, status=None):
@ -270,6 +272,7 @@ class River(Graph, SQLSubModel):
self._BoundaryConditionsAdisTS = BoundaryConditionsAdisTSList(status=self._status)
self._LateralContributionsAdisTS = LateralContributionsAdisTSList(status=self._status)
self._D90AdisTS = D90AdisTSList(status=self._status)
self._DIFAdisTS = DIFAdisTSList(status=self._status)
@classmethod
def _db_create(cls, execute):
@ -358,6 +361,8 @@ class River(Graph, SQLSubModel):
new._D90AdisTS = D90AdisTSList._db_load(execute, data)
new._DIFAdisTS = DIFAdisTSList._db_load(execute, data)
return new
def _db_save(self, execute, data=None):
@ -383,6 +388,7 @@ class River(Graph, SQLSubModel):
objs.append(self._BoundaryConditionsAdisTS)
objs.append(self._LateralContributionsAdisTS)
objs.append(self._D90AdisTS)
objs.append(self._DIFAdisTS)
self._save_submodel(execute, objs, data)
return True
@ -530,6 +536,10 @@ Last export at: @date."""
def d90_adists(self):
return self._D90AdisTS
@property
def dif_adists(self):
return self._DIFAdisTS
def get_params(self, solver):
if solver in self._parameters:
return self._parameters[solver]

217
src/View/DIFAdisTS/Table.py Normal file
View File

@ -0,0 +1,217 @@
# Table.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
import traceback
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.D90AdisTS.UndoCommand import (
SetCommand, AddCommand, SetCommandSpec,
DelCommand,
)
logger = logging.getLogger()
_translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, ic_spec_lst=None, trad=None, parent=None, mode="reaches"):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._mode = mode
self._trad = trad
self._ic_spec_lst = ic_spec_lst
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
val = []
if self._mode == "kp":
reach_id = self._ic_spec_lst[index.row()].reach
reach = next(filter(lambda edge: edge.id == reach_id, self._data.edges()))
if reach_id is not None:
val = list(
map(
lambda kp: str(kp), reach.reach.get_kp()
)
)
else:
val = list(
map(
lambda n: n.name, self._data.edges()
)
)
self.editor.addItems(
[self._trad['not_associated']] +
val
)
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
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:
if 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 D90TableModel(PamhyrTableModel):
def __init__(self, river=None, data=None, **kwargs):
self._river = river
super(D90TableModel, self).__init__(data=data, **kwargs)
self._data = data
def _setup_lst(self):
self._lst = self._data._data
def rowCount(self, parent):
return len(self._lst)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
row = index.row()
column = index.column()
if self._headers[column] is "name":
n = self._lst[row].name
if n is None or n == "":
return self._trad['not_associated']
return n
elif self._headers[column] is "reach":
n = self._lst[row].reach
if n is None:
return self._trad['not_associated']
return next(filter(lambda edge: edge.id == n, self._river.edges())).name
elif self._headers[column] is "start_kp":
n = self._lst[row].start_kp
if n is None:
return self._trad['not_associated']
return n
elif self._headers[column] is "end_kp":
n = self._lst[row].end_kp
if n is None:
return self._trad['not_associated']
return n
elif self._headers[column] is "d90":
n = self._lst[row].d90
if n is None:
return self._trad['not_associated']
return n
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
row = index.row()
column = index.column()
try:
if self._headers[column] != "reach":
self._undo.push(
SetCommandSpec(
self._lst, row, self._headers[column], value
)
)
elif self._headers[column] == "reach":
print(self._river.edge(value).id)
self._undo.push(
SetCommandSpec(
self._lst, row, self._headers[column], self._river.edge(value).id
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo.push(
AddCommand(
self._data, self._lst, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._data, self._lst, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()

View File

@ -0,0 +1,95 @@
# Table.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
import traceback
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.D90AdisTS.UndoCommand import (
SetCommand,
)
logger = logging.getLogger()
_translate = QCoreApplication.translate
class D90TableDefaultModel(PamhyrTableModel):
def __init__(self, **kwargs):
super(D90TableDefaultModel, self).__init__(**kwargs)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
row = index.row()
column = index.column()
if self._headers[column] is "name":
return self._data[row].name
elif self._headers[column] is "d90":
n = self._data[row].d90
if n is None:
return self._trad['not_associated']
return n
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
row = index.row()
column = index.column()
try:
if self._headers[column] is not None:
self._undo.push(
SetCommand(
self._data, row, self._headers[column], value
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
return True
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()

View File

@ -0,0 +1,150 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.D90AdisTS.D90AdisTS import D90AdisTS
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
class SetCommand(QUndoCommand):
def __init__(self, data, row, column, new_value):
QUndoCommand.__init__(self)
self._data = data
self._row = row
self._column = column
if self._column == "name":
self._old = self._data[self._row].name
elif self._column == "d90":
self._old = self._data[self._row].d90
_type = float
if column == "name":
_type = str
self._new = _type(new_value)
def undo(self):
if self._column == "name":
self._data[self._row].name = self._old
elif self._column == "d90":
self._data[self._row].d90 = self._old
def redo(self):
if self._column == "name":
self._data[self._row].name = self._new
elif self._column == "d90":
self._data[self._row].d90 = self._new
class SetCommandSpec(QUndoCommand):
def __init__(self, data, row, column, new_value):
QUndoCommand.__init__(self)
self._data = data
self._row = row
self._column = column
if self._column == "name":
self._old = self._data[self._row].name
elif self._column == "reach":
self._old = self._data[self._row].reach
elif self._column == "start_kp":
self._old = self._data[self._row].start_kp
elif self._column == "end_kp":
self._old = self._data[self._row].end_kp
elif self._column == "d90":
self._old = self._data[self._row].d90
_type = float
if column == "name":
_type = str
elif column == "reach":
_type = int
self._new = _type(new_value)
def undo(self):
if self._column == "name":
self._data[self._row].name = self._old
elif self._column == "reach":
self._data[self._row].reach = self._old
elif self._column == "start_kp":
self._data[self._row].start_kp = self._old
elif self._column == "end_kp":
self._data[self._row].end_kp = self._old
elif self._column == "d90":
self._data[self._row].d90 = self._old
def redo(self):
if self._column == "name":
self._data[self._row].name = self._new
elif self._column == "reach":
self._data[self._row].reach = self._new
elif self._column == "start_kp":
self._data[self._row].start_kp = self._new
elif self._column == "end_kp":
self._data[self._row].end_kp = self._new
elif self._column == "d90":
self._data[self._row].d90 = self._new
class AddCommand(QUndoCommand):
def __init__(self, data, ics_spec, index):
QUndoCommand.__init__(self)
self._data = data
self._ics_spec = ics_spec
self._index = index
self._new = None
def undo(self):
self._data.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._data.new(self._index)
else:
self._data.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, data, ics_spec, rows):
QUndoCommand.__init__(self)
self._data = data
self._ics_spec = ics_spec
self._rows = rows
#self._data = data
self._ic = []
for row in rows:
self._ic.append((row, self._ics_spec[row]))
self._ic.sort()
def undo(self):
for row, el in self._ic:
self._data.insert(row, el)
def redo(self):
self._data.delete_i(self._rows)

View File

@ -0,0 +1,283 @@
# Window.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import os
import logging
from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence, QIcon,
)
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect, QItemSelectionModel,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QVBoxLayout, QToolBar, QAction, QToolButton,
)
from Modules import Modules
from View.InitialConditionsAdisTS.UndoCommand import (
SetCommand,
)
from View.D90AdisTS.TableDefault import (
D90TableDefaultModel,
)
from View.D90AdisTS.Table import (
D90TableModel, ComboBoxDelegate,
)
from View.D90AdisTS.translate import D90AdisTSTranslate
from Solver.Mage import Mage8
_translate = QCoreApplication.translate
logger = logging.getLogger()
class D90AdisTSWindow(PamhyrWindow):
_pamhyr_ui = "D90AdisTS"
_pamhyr_name = "D90 AdisTS"
def __init__(self, data=None, study=None, config=None, parent=None):
self._data = []
self._data.append(data)
trad = D90AdisTSTranslate()
name = (
trad[self._pamhyr_name] +
" - " + study.name +
" - " + self._data[0].name
)
super(D90AdisTSWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
parent=parent
)
self._hash_data.append(data)
self._d90_adists_lst = study.river.d90_adists
self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
table_default = self.find(QTableView, f"tableView")
self._table = D90TableDefaultModel(
table_view=table_default,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=["name", "d90"],
delegates={},
data=self._data,
undo=self._undo_stack,
trad=self._trad
)
table_default.setModel(self._table)
table_default.setSelectionBehavior(QAbstractItemView.SelectRows)
table_default.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table_default.setAlternatingRowColors(True)
layout = self.find(QVBoxLayout, f"verticalLayout_1")
toolBar = QToolBar()
layout.addWidget(toolBar)
action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
action_add.triggered.connect(self.add)
action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add)
toolBar.addAction(action_delete)
self.table_spec = QTableView()
layout.addWidget(self.table_spec)
self._delegate_reach = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="reaches"
)
self._delegate_kp = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="kp"
)
self._table_spec = D90TableModel(
table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=["name", "reach", "start_kp", "end_kp", "d90"],
delegates={
"reach": self._delegate_reach,
"start_kp": self._delegate_kp,
"end_kp": self._delegate_kp
},
data=self._data[0],
undo=self._undo_stack,
trad=self._trad,
river=self._study.river
)
self.table_spec.setModel(self._table_spec)
self.table_spec.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table_spec.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.table_spec.setAlternatingRowColors(True)
selectionModel = self.table_spec.selectionModel()
index = self.table_spec.model().index(0, 0)
selectionModel.select(
index,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
self.table_spec.scrollTo(index)
def index_selected_row(self):
#table = self.find(QTableView, f"tableView")
table = self.table_spec
rows = table.selectionModel()\
.selectedRows()
if len(rows) == 0:
return 0
return rows[0].row()
def index_selected_rows(self):
#table = self.find(QTableView, f"tableView")
table = self.table_spec
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def move_up(self):
row = self.index_selected_row()
self._table.move_up(row)
self._update()
def move_down(self):
row = self.index_selected_row()
self._table.move_down(row)
self._update()
def _copy(self):
rows = list(
map(
lambda row: row.row(),
self.tableView.selectionModel().selectedRows()
)
)
table = list(
map(
lambda eic: list(
map(
lambda k: eic[1][k],
["kp", "discharge", "elevation"]
)
),
filter(
lambda eic: eic[0] in rows,
enumerate(self._ics.lst())
)
)
)
self.copyTableIntoClipboard(table)
def _paste(self):
header, data = self.parseClipboardTable()
if len(data) + len(header) == 0:
return
logger.debug(
"IC: Paste: " +
f"header = {header}, " +
f"data = {data}"
)
try:
row = self.index_selected_row()
# self._table.paste(row, header, data)
self._table.paste(row, [], data)
except Exception as e:
logger_exception(e)
self._update()
def _undo(self):
self._table.undo()
self._update()
def _redo(self):
self._table.redo()
self._update()
def add(self):
rows = self.index_selected_rows()
if len(self._data[0]._data) == 0 or len(rows) == 0:
self._table_spec.add(0)
else:
self._table_spec.add(rows[0])
def delete(self):
print("del")
rows = self.index_selected_rows()
if len(rows) == 0:
print("len 0")
return
self._table_spec.delete(rows)

View File

@ -0,0 +1,46 @@
# translate.py -- Pamhyr
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
from View.Translate import MainTranslate
_translate = QCoreApplication.translate
class D90AdisTSTranslate(MainTranslate):
def __init__(self):
super(D90AdisTSTranslate, self).__init__()
self._dict["D90 AdisTS"] = _translate(
"D90AdisTS", "D90 AdisTS")
self._dict["kp"] = self._dict["unit_kp"]
self._sub_dict["table_headers"] = {
"name": self._dict["name"],
"d90": _translate("Unit", "D90"),
}
self._sub_dict["table_headers_spec"] = {
"name": self._dict["name"],
"reach": self._dict["reach"],
"start_kp": _translate("Unit", "Start_KP (m)"),
"end_kp": _translate("Unit", "End_KP (m)"),
"d90": _translate("Unit", "D90"),
}

View File

@ -78,6 +78,7 @@ from View.Debug.Window import ReplWindow
from View.OutputKpAdisTS.Window import OutputKpAdisTSWindow
from View.Pollutants.Window import PollutantsWindow
from View.D90AdisTS.Window import D90AdisTSWindow
from View.DIFAdisTS.Window import D90AdisTSWindow
# Optional internal display of documentation for make the application
# package lighter...
@ -123,7 +124,7 @@ define_model_action = [
"action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_kp",
"action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90",
"action_menu_d90", "action_menu_dif",
]
action = (
@ -237,6 +238,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"""
actions = {
# Menu action
"action_menu_dif": self.open_dif,
"action_menu_d90": self.open_d90,
"action_menu_pollutants": self.open_pollutants,
"action_menu_run_adists":self.run_solver_adists,
@ -888,6 +890,25 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
D90AdisTS.show()
def open_dif(self):
if len(self._study.river.d90_adists.lst) != 0:
d90_default = self._study.river.d90_adists.lst[0]
else:
d90_default = self._study.river.d90_adists.new(0)
if self.sub_window_exists(
D90AdisTSWindow,
data=[self._study, None, d90_default]
):
return
D90AdisTS = D90AdisTSWindow(
study=self._study,
parent=self,
data=d90_default
)
D90AdisTS.show()
def open_pollutants(self):
if self.sub_window_exists(
PollutantsWindow,

View File

@ -220,6 +220,7 @@
<addaction name="action_menu_output_kp"/>
<addaction name="action_menu_pollutants"/>
<addaction name="action_menu_d90"/>
<addaction name="action_menu_dif"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_network"/>
@ -767,6 +768,11 @@
<string>D90</string>
</property>
</action>
<action name="action_menu_dif">
<property name="text">
<string>DIF</string>
</property>
</action>
</widget>
<resources/>
<connections>