diff --git a/src/Model/REPLine/REPLine.py b/src/Model/REPLine/REPLine.py
new file mode 100644
index 00000000..aea23d8c
--- /dev/null
+++ b/src/Model/REPLine/REPLine.py
@@ -0,0 +1,150 @@
+# REPLine.py -- Pamhyr
+# Copyright (C) 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 .
+
+# -*- coding: utf-8 -*-
+
+from functools import reduce
+
+from tools import trace, timer
+
+from Model.Tools.PamhyrDB import SQLSubModel
+from Model.Except import NotImplementedMethodeError
+
+
+class REPLine(SQLSubModel):
+ _sub_classes = []
+ _id_cnt = 0
+
+ def __init__(self, id: int = -1, enabled=True,
+ name="", line="", solvers=set(),
+ status=None):
+ super(REPLine, self).__init__()
+
+ if id == -1:
+ self.id = REPLine._id_cnt
+ else:
+ self.id = id
+
+ self._status = status
+
+ self._enabled = enabled
+ self._name = f"Line{self.id}" if name == "" else name
+ self._line = line
+ self._solvers = solvers
+
+ REPLine._id_cnt = max(id, REPLine._id_cnt+1)
+
+ @property
+ def enabled(self):
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, enabled):
+ self._enabled = enabled
+
+ def is_enabled(self):
+ return self._enabled
+
+ @property
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, name):
+ self._name = name
+
+ @property
+ def line(self):
+ return self._line
+
+ @line.setter
+ def line(self, line):
+ self._line = line
+
+ @property
+ def solvers(self):
+ return self._solvers
+
+ @solvers.setter
+ def solvers(self, solvers):
+ self._solvers = solvers
+
+ @classmethod
+ def _db_create(cls, execute):
+ execute("""
+ CREATE TABLE rep_lines(
+ id INTEGER NOT NULL PRIMARY KEY,
+ enabled BOOLEAN NOT NULL,
+ name TEXT NOT NULL,
+ line TEXT NOT NULL,
+ solvers TEXT 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) < 9:
+ cls._db_create(execute)
+
+ return True
+
+ @classmethod
+ def _db_load(cls, execute, data=None):
+ new = []
+
+ table = execute(
+ "SELECT id, enabled, name, line, solvers " +
+ "FROM rep_lines"
+ )
+
+ for row in table:
+ it = iter(row)
+
+ id = next(it)
+ enabled = (next(it) == 1)
+ name = next(it)
+ line = next(it)
+ solvers = set(next(it).split(";;"))
+
+ f = cls(
+ id=id, enabled=enabled, name=name, line=line,
+ solvers=solvers, status=data['status']
+ )
+
+ new.append(f)
+
+ return new
+
+ def _db_save(self, execute, data=None):
+ solvers = ";;".join(self._solvers)
+
+ sql = (
+ "INSERT INTO " +
+ "rep_lines(id, enabled, name, line, solvers) " +
+ "VALUES (" +
+ f"{self.id}, {self._enabled}, " +
+ f"'{self._db_format(self._name)}', " +
+ f"'{self._db_format(self._line)}', " +
+ f"'{self._db_format(solvers)}'" +
+ ")"
+ )
+ execute(sql)
+
+ return True
diff --git a/src/Model/REPLine/REPLineList.py b/src/Model/REPLine/REPLineList.py
new file mode 100644
index 00000000..bbed40dc
--- /dev/null
+++ b/src/Model/REPLine/REPLineList.py
@@ -0,0 +1,56 @@
+# REPLineList.py -- Pamhyr
+# Copyright (C) 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 .
+
+# -*- coding: utf-8 -*-
+
+from tools import trace, timer
+
+from Model.Except import NotImplementedMethodeError
+from Model.Tools.PamhyrList import PamhyrModelList
+from Model.REPLine.REPLine import REPLine
+
+
+class REPLineList(PamhyrModelList):
+ _sub_classes = [REPLine]
+
+ @classmethod
+ def _db_load(cls, execute, data=None):
+ new = cls(status=data["status"])
+
+ new._lst = REPLine._db_load(execute, data)
+
+ return new
+
+ def _db_save(self, execute, data=None):
+ ok = True
+
+ # Delete previous data
+ execute("DELETE FROM rep_lines")
+
+ for af in self._lst:
+ ok &= af._db_save(execute, data)
+
+ return ok
+
+ @property
+ def lines(self):
+ return self.lst
+
+ def new(self, index):
+ n = REPLine(status=self._status)
+ self.insert(index, n)
+ self._status.modified()
+ return n
diff --git a/src/Model/River.py b/src/Model/River.py
index 41d4e0d8..a5525841 100644
--- a/src/Model/River.py
+++ b/src/Model/River.py
@@ -41,6 +41,7 @@ from Model.HydraulicStructures.HydraulicStructuresList import (
HydraulicStructureList,
)
from Model.AdditionalFile.AddFileList import AddFileList
+from Model.REPLine.REPLineList import REPLineList
from Solver.Solvers import solver_type_list
@@ -226,6 +227,7 @@ class River(Graph, SQLSubModel):
ReservoirList,
HydraulicStructureList,
AddFileList,
+ REPLineList,
]
def __init__(self, status=None):
@@ -248,6 +250,7 @@ class River(Graph, SQLSubModel):
status=self._status
)
self._additional_files = AddFileList(status=self._status)
+ self._rep_lines = REPLineList(status=self._status)
@classmethod
def _db_create(cls, execute):
@@ -320,6 +323,7 @@ class River(Graph, SQLSubModel):
new._additional_files = AddFileList._db_load(
execute, data
)
+ new._rep_lines = REPLineList._db_load(execute, data)
return new
@@ -335,6 +339,7 @@ class River(Graph, SQLSubModel):
objs.append(self._reservoir)
objs.append(self._hydraulic_structures)
objs.append(self._additional_files)
+ objs.append(self._rep_lines)
for solver in self._parameters:
objs.append(self._parameters[solver])
@@ -438,6 +443,10 @@ Last export at: @date."""
def additional_files(self):
return self._additional_files
+ @property
+ def rep_lines(self):
+ return self._rep_lines
+
@property
def parameters(self):
return self._parameters
diff --git a/src/Model/Study.py b/src/Model/Study.py
index 14bb5c91..4bb347b8 100644
--- a/src/Model/Study.py
+++ b/src/Model/Study.py
@@ -41,7 +41,7 @@ class Study(SQLModel):
def __init__(self, filename=None, init_new=True):
# Metadata
- self._version = "0.0.8"
+ self._version = "0.0.9"
self.creation_date = datetime.now()
self.last_modification_date = datetime.now()
self.last_save_date = datetime.now()
diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py
index 5594e68b..1a3f829b 100644
--- a/src/Solver/Mage.py
+++ b/src/Solver/Mage.py
@@ -128,6 +128,15 @@ class Mage(CommandLineSolver):
name = self._study.name
return f"{name}.TRA"
+ def _export_REP_additional_lines(self, study, rep_file):
+ lines = filter(
+ lambda line: line.is_enabled(),
+ study.river.rep_lines.lines
+ )
+
+ for line in lines:
+ rep_file.write(line.line)
+
@timer
def _export_ST(self, study, repertory, qlog, name="0"):
files = []
@@ -661,6 +670,8 @@ class Mage(CommandLineSolver):
if EXT in ["GRA"]:
f.write(f"{EXT} {file}\n")
+ self._export_REP_additional_lines(study, f)
+
@timer
def export(self, study, repertory, qlog=None):
self._study = study
diff --git a/src/View/CheckList/List.py b/src/View/CheckList/List.py
index 3afae091..ab83eaeb 100644
--- a/src/View/CheckList/List.py
+++ b/src/View/CheckList/List.py
@@ -50,7 +50,7 @@ class TabListModel(PamhyrListModel):
if status is STATUS.OK:
color = Qt.green
elif status is STATUS.WARNING:
- color = Qt.yellow
+ color = QColor("orange")
elif status is STATUS.ERROR:
color = Qt.red
diff --git a/src/View/CheckList/Table.py b/src/View/CheckList/Table.py
index 658c3b32..fc95b901 100644
--- a/src/View/CheckList/Table.py
+++ b/src/View/CheckList/Table.py
@@ -57,7 +57,7 @@ class TableModel(PamhyrTableModel):
if self._data[row].is_ok():
color = Qt.green
elif self._data[row].is_warning():
- color = Qt.yellow
+ color = QColor("orange")
elif self._data[row].is_error():
color = Qt.red
@@ -136,7 +136,7 @@ class TabTableModel(PamhyrTableModel):
if status is STATUS.OK:
color = Qt.green
elif status is STATUS.WARNING:
- color = Qt.yellow
+ color = QColor("orange")
elif status is STATUS.ERROR:
color = Qt.red
diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py
index 836f6d7e..4c5cf62f 100644
--- a/src/View/MainWindow.py
+++ b/src/View/MainWindow.py
@@ -68,6 +68,7 @@ from View.Frictions.Window import FrictionsWindow
from View.SedimentLayers.Window import SedimentLayersWindow
from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
from View.AdditionalFiles.Window import AddFileListWindow
+from View.REPLines.Window import REPLineListWindow
from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow
from View.CheckList.Window import CheckListWindow
@@ -121,6 +122,7 @@ define_model_action = [
"action_menu_edit_hydraulic_structures", "action_menu_additional_file",
"action_menu_results_last", "action_open_results_from_file",
"action_menu_boundary_conditions_sediment",
+ "action_menu_rep_additional_lines",
]
action = (
@@ -254,6 +256,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_edit_reach_sediment_layers":
self.open_reach_sediment_layers,
"action_menu_additional_file": self.open_additional_files,
+ "action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results,
"action_open_results_from_file": self.open_results_from_file,
@@ -1078,6 +1081,19 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
self.additonal_files.show()
+ def open_rep_lines(self):
+ if self._study is not None:
+ if self.sub_window_exists(
+ REPLineListWindow,
+ data=[self._study, None]
+ ):
+ return
+
+ self.rep_lines = REPLineListWindow(
+ study=self._study, parent=self
+ )
+ self.rep_lines.show()
+
def open_solver_parameters(self):
if self.sub_window_exists(
SolverParametersWindow,
diff --git a/src/View/REPLines/Edit/Window.py b/src/View/REPLines/Edit/Window.py
new file mode 100644
index 00000000..120a0a29
--- /dev/null
+++ b/src/View/REPLines/Edit/Window.py
@@ -0,0 +1,71 @@
+# Window.py -- Pamhyr
+# Copyright (C) 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 .
+
+# -*- coding: utf-8 -*-
+
+import logging
+
+from Modules import Modules
+from View.Tools.PamhyrWindow import PamhyrDialog
+
+from PyQt5.QtWidgets import (
+ QLabel, QPlainTextEdit, QPushButton,
+ QCheckBox,
+)
+
+from View.REPLines.Translate import REPLineTranslate
+
+logger = logging.getLogger()
+
+
+class EditREPLineWindow(PamhyrDialog):
+ _pamhyr_ui = "REPLineDialog"
+ _pamhyr_name = "Edit Mage REP lines"
+
+ def __init__(self, study=None, config=None, rep_line=None,
+ trad=None, parent=None):
+
+ name = trad[self._pamhyr_name] + " - " + study.name
+ super(EditREPLineWindow, self).__init__(
+ title=name,
+ study=study,
+ config=config,
+ options=[],
+ parent=parent
+ )
+
+ self._rep_line = rep_line
+ self._hash_data.append(self._rep_line)
+
+ self.setup_values()
+
+ def setup_values(self):
+ self.set_check_box("checkBox_enabled", self._rep_line.enabled)
+ self.set_line_edit_text("lineEdit_name", self._rep_line.name)
+ self.set_line_edit_text("lineEdit_line", self._rep_line.line)
+
+ def accept(self):
+ is_enabled = self.get_check_box("checkBox_enabled")
+ name = self.get_line_edit_text("lineEdit_name")
+ line = self.get_line_edit_text("lineEdit_line")
+
+ self._rep_line.enabled = is_enabled
+ self._rep_line.name = name
+ self._rep_line.line = line
+ self._rep_line.solvers = set()
+
+ self._propagate_update(key=Modules.ADDITIONAL_FILES)
+ self.close()
diff --git a/src/View/REPLines/List.py b/src/View/REPLines/List.py
new file mode 100644
index 00000000..a591a4b1
--- /dev/null
+++ b/src/View/REPLines/List.py
@@ -0,0 +1,71 @@
+# List.py -- Pamhyr
+# Copyright (C) 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 .
+
+# -*- coding: utf-8 -*-
+
+import logging
+
+from functools import reduce
+from tools import trace, timer
+
+from PyQt5.QtCore import (
+ Qt, QVariant,
+)
+
+from PyQt5.QtGui import (
+ QColor, QBrush,
+)
+
+from View.Tools.PamhyrList import PamhyrListModel
+
+logger = logging.getLogger()
+
+
+class ListModel(PamhyrListModel):
+ def data(self, index, role):
+ row = index.row()
+ column = index.column()
+
+ file = self._data.lines[row]
+
+ if role == Qt.ForegroundRole:
+ color = Qt.gray
+
+ if file.is_enabled():
+ color = QColor("black")
+ else:
+ color = QColor("grey")
+
+ return QBrush(color)
+
+ if role == Qt.ItemDataRole.DisplayRole:
+ text = f"{file.name}: '{file.line}'"
+
+ if not file.is_enabled():
+ text += " (disabled)"
+
+ return text
+
+ return QVariant()
+
+ def add(self, row):
+ self._data.new(row)
+ self.update()
+
+ def delete(self, rows):
+ logger.info(f"add_files: delete {rows}")
+ self._data.delete_i(rows)
+ self.update()
diff --git a/src/View/REPLines/Translate.py b/src/View/REPLines/Translate.py
new file mode 100644
index 00000000..3849345e
--- /dev/null
+++ b/src/View/REPLines/Translate.py
@@ -0,0 +1,35 @@
+# Translate.py -- Pamhyr
+# Copyright (C) 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 .
+
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtCore import QCoreApplication
+from View.Translate import MainTranslate
+
+_translate = QCoreApplication.translate
+
+
+class REPLineTranslate(MainTranslate):
+ def __init__(self):
+ super(REPLineTranslate, self).__init__()
+
+ self._dict["Mage REP lines"] = _translate(
+ "REPLines", "Mage REP lines"
+ )
+
+ self._dict["Edit Mage REP lines"] = _translate(
+ "REPLines", "Edit Mage REP lines"
+ )
diff --git a/src/View/REPLines/Window.py b/src/View/REPLines/Window.py
new file mode 100644
index 00000000..92cb015b
--- /dev/null
+++ b/src/View/REPLines/Window.py
@@ -0,0 +1,104 @@
+# Window.py -- Pamhyr
+# Copyright (C) 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 .
+
+# -*- coding: utf-8 -*-
+
+from tools import trace, timer
+
+from PyQt5.QtWidgets import (
+ QAction, QListView,
+)
+
+from View.Tools.PamhyrWindow import PamhyrWindow
+
+from View.REPLines.List import ListModel
+from View.REPLines.Translate import REPLineTranslate
+from View.REPLines.Edit.Window import EditREPLineWindow
+
+
+class REPLineListWindow(PamhyrWindow):
+ _pamhyr_ui = "REPLineList"
+ _pamhyr_name = "Mage REP lines"
+
+ def __init__(self, study=None, config=None,
+ parent=None):
+ trad = REPLineTranslate()
+ name = trad[self._pamhyr_name] + " - " + study.name
+
+ super(REPLineListWindow, self).__init__(
+ title=name,
+ study=study,
+ config=config,
+ trad=trad,
+ options=[],
+ parent=parent
+ )
+
+ self.setup_list()
+ self.setup_connections()
+
+ def setup_list(self):
+ lst = self.find(QListView, f"listView")
+ self._list = ListModel(
+ list_view=lst,
+ data=self._study.river.rep_lines,
+ )
+
+ def setup_connections(self):
+ self.find(QAction, "action_add").triggered.connect(self.add)
+ self.find(QAction, "action_delete").triggered.connect(self.delete)
+ self.find(QAction, "action_edit").triggered.connect(self.edit)
+
+ def update(self):
+ self._list.update()
+
+ def selected_rows(self):
+ lst = self.find(QListView, f"listView")
+ return list(map(lambda i: i.row(), lst.selectedIndexes()))
+
+ def add(self):
+ rows = self.selected_rows()
+ if len(rows) > 0:
+ row = rows[0]
+ else:
+ row = 0
+
+ self._list.add(row)
+
+ def delete(self):
+ rows = self.selected_rows()
+ self._list.delete(rows)
+
+ def edit(self):
+ rows = self.selected_rows()
+
+ for row in rows:
+ rep_line = self._study.river.rep_lines.lines[row]
+
+ if self.sub_window_exists(
+ EditREPLineWindow,
+ data=[self._study, self._config, rep_line]
+ ):
+ continue
+
+ win = EditREPLineWindow(
+ study=self._study,
+ config=self._config,
+ rep_line=rep_line,
+ trad=self._trad,
+ parent=self,
+ )
+ win.show()
diff --git a/src/View/Study/Window.py b/src/View/Study/Window.py
index de7c759f..0a2cad3f 100644
--- a/src/View/Study/Window.py
+++ b/src/View/Study/Window.py
@@ -119,7 +119,7 @@ This is my new study description
## Copyright
-(c) {get_user_name()} - {datetime.now().year}
+© {get_user_name()} - {datetime.now().year}
All right reserved.
"""
diff --git a/src/View/ui/MainWindow.ui b/src/View/ui/MainWindow.ui
index 35e05ab6..4ea3eb60 100644
--- a/src/View/ui/MainWindow.ui
+++ b/src/View/ui/MainWindow.ui
@@ -208,7 +208,14 @@
&Advansed
+
+
@@ -1000,6 +1007,11 @@
&Additional file
+
+
+ REP additional lines
+
+
diff --git a/src/View/ui/REPLineDialog.ui b/src/View/ui/REPLineDialog.ui
new file mode 100644
index 00000000..9d5722ae
--- /dev/null
+++ b/src/View/ui/REPLineDialog.ui
@@ -0,0 +1,110 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 452
+ 145
+
+
+
+ Dialog
+
+
+ -
+
+
-
+
+
+ Enabled
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+
+ -
+
+
-
+
+
+ Line
+
+
+
+ -
+
+
+ -
+
+
+ Name
+
+
+
+ -
+
+
+ Comment lines start with '*' char (let see the mage documentation for more details)
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/View/ui/REPLineList.ui b/src/View/ui/REPLineList.ui
new file mode 100644
index 00000000..f9722d37
--- /dev/null
+++ b/src/View/ui/REPLineList.ui
@@ -0,0 +1,85 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 896
+ 504
+
+
+
+ MainWindow
+
+
+
+ -
+
+
+
+
+
+
+ toolBar
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+
+ ressources/gtk-add.pngressources/gtk-add.png
+
+
+ Add
+
+
+ Add new additional line
+
+
+ Ctrl+N
+
+
+
+
+
+ ressources/gtk-remove.pngressources/gtk-remove.png
+
+
+ Delete
+
+
+ Delete additional line(s)
+
+
+ Del
+
+
+
+
+
+ ressources/edit.pngressources/edit.png
+
+
+ Edit
+
+
+ Edit selected line(s)
+
+
+ Ctrl+E
+
+
+
+
+
+