diff --git a/doc/users/TP_Hydraulique_Hogneau/step2.pamhyr b/doc/users/TP_Hydraulique_Hogneau/step2.pamhyr index c7516dff..95c66480 100644 Binary files a/doc/users/TP_Hydraulique_Hogneau/step2.pamhyr and b/doc/users/TP_Hydraulique_Hogneau/step2.pamhyr differ diff --git a/src/View/BoundaryCondition/Edit/Table.py b/src/View/BoundaryCondition/Edit/Table.py index 597e693e..02b48d68 100644 --- a/src/View/BoundaryCondition/Edit/Table.py +++ b/src/View/BoundaryCondition/Edit/Table.py @@ -234,3 +234,23 @@ class TableModel(PamhyrTableModel): ) self.layoutAboutToBeChanged.emit() self.update() + + def read_from_file(self, file_name, bctype): + + logger.debug(f"Import boundary conditions from {file_name}") + data0 = [] + data1 = [] + if bctype == "ZD": + mult = 1 + else: + mult = 60 + with open(file_name, encoding="utf-8") as bc_file: + for line in bc_file: + if not (line.startswith("#") or + line.startswith("*") or + line.startswith("$")): + line = line.split() + data0.append(float(line[0]) * mult) + data1.append(line[1]) + + self.replace_data(data0, data1) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index 18f817e1..a78fd469 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -30,10 +30,10 @@ from PyQt5.QtGui import ( QKeySequence, ) -from PyQt5 import QtCore +from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import ( Qt, QVariant, QAbstractTableModel, QCoreApplication, - pyqtSlot, pyqtSignal, + pyqtSlot, pyqtSignal, QSettings, ) from PyQt5.QtWidgets import ( @@ -204,6 +204,8 @@ class EditBoundaryConditionWindow(PamhyrWindow): self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_sort").triggered.connect(self.sort) + self.find(QAction, "action_import").triggered\ + .connect(self.import_from_file) self.find(QAction, "action_generate_uniform")\ .triggered.connect(self.generate_uniform) @@ -301,6 +303,31 @@ class EditBoundaryConditionWindow(PamhyrWindow): self._table.sort(False) self.plot.update() + def import_from_file(self): + options = QFileDialog.Options() + settings = QSettings(QSettings.IniFormat, + QSettings.UserScope, 'MyOrg', ) + options |= QFileDialog.DontUseNativeDialog + + if self._data.bctype == "TD": + file_types = [self._trad["file_hyd"]] + if self._data.bctype == "TZ": + file_types = [self._trad["file_lim"]] + if self._data.bctype == "ZD": + file_types = [self._trad["file_ava"]] + file_types.append(self._trad["file_all"]) + + file_name, _ = QtWidgets.QFileDialog.getOpenFileName( + self, + self._trad["open_file"], + "", + ";; ".join(file_types), + options=options + ) + + if file_name != "": + self._table.read_from_file(file_name, self._data.bctype) + def move_up(self): row = self.index_selected_row() self._table.move_up(row) diff --git a/src/View/BoundaryCondition/Edit/translate.py b/src/View/BoundaryCondition/Edit/translate.py index 16a2c5bd..bec95728 100644 --- a/src/View/BoundaryCondition/Edit/translate.py +++ b/src/View/BoundaryCondition/Edit/translate.py @@ -35,6 +35,17 @@ class BCETranslate(BCTranslate): self._dict["Boundary Condition Options"] = _translate( "BoundaryCondition", "Boundary Condition Options") + self._dict["open_file"] = _translate( + "BoundaryCondition", "Open a file") + self._dict["file_hyd"] = _translate( + "BoundaryCondition", "Mage hydrograph file (*.HYD)") + self._dict["file_lim"] = _translate( + "BoundaryCondition", "Mage limnigraph file (*.LIM)") + self._dict["file_lim"] = _translate( + "BoundaryCondition", "Mage rating curve file (*.AVA)") + self._dict["file_all"] = _translate( + "BoundaryCondition", "All files (*)") + self._sub_dict["table_headers"] = { "x": _translate("BoundaryCondition", "X"), "y": _translate("BoundaryCondition", "Y"), diff --git a/src/View/Frictions/Table.py b/src/View/Frictions/Table.py index 231333f5..5d02430f 100644 --- a/src/View/Frictions/Table.py +++ b/src/View/Frictions/Table.py @@ -38,7 +38,7 @@ from View.Frictions.UndoCommand import ( SetNameCommand, SetBeginCommand, SetEndCommand, SetBeginStricklerCommand, SetEndStricklerCommand, AddCommand, DelCommand, SortCommand, - MoveCommand, PasteCommand, DuplicateCommand, + MoveCommand, PasteCommand, DuplicateCommand, ReplaceDataCommand, ) from View.Tools.PamhyrTable import PamhyrTableModel @@ -99,7 +99,7 @@ class ComboBoxDelegate(QItemDelegate): self.commitData.emit(self.sender()) -class TableModel(PamhyrTableModel): +class FrictionTableModel(PamhyrTableModel): def _setup_lst(self): self._lst = self._data.frictions self._study = self._opt_data @@ -199,6 +199,18 @@ class TableModel(PamhyrTableModel): self.endRemoveRows() self.layoutChanged.emit() + def replace_data(self, new_data): + self.layoutAboutToBeChanged.emit() + + self._undo.push( + ReplaceDataCommand( + self._lst, new_data + ) + ) + + self.layoutAboutToBeChanged.emit() + self.layoutChanged.emit() + def sort(self, _reverse, parent=QModelIndex()): self.layoutAboutToBeChanged.emit() @@ -252,3 +264,33 @@ class TableModel(PamhyrTableModel): def redo(self): self._undo.redo() self.layoutChanged.emit() + + def read_from_file(self, file_name): + + reach_id = self._study.river.enable_edges().index(self._data) + 1 + + logger.debug(f"Import frictions from {file_name}") + data = [] + with open(file_name, encoding="utf-8") as rug_file: + for line in rug_file: + if line.upper().startswith("K"): + line = line.split() + if int(line[1]) == reach_id: + data.append(line[1:]) + + new_data = [] + for d in data: + new = None + minor = float(d[3]) + medium = float(d[4]) + for s in self._study.river.stricklers.stricklers: + if s.minor == minor and s.medium == medium: + new = s + break + if new is None: + new = self._study.river.stricklers.new(len( + self._study.river.stricklers)) + new.minor = minor + new.medium = medium + new_data.append([self._data, float(d[1]), float(d[2]), new, new]) + self.replace_data(new_data) diff --git a/src/View/Frictions/UndoCommand.py b/src/View/Frictions/UndoCommand.py index 20bf308f..b428ae84 100644 --- a/src/View/Frictions/UndoCommand.py +++ b/src/View/Frictions/UndoCommand.py @@ -145,6 +145,36 @@ class AddCommand(QUndoCommand): self._frictions.insert(self._index, self._new) +class ReplaceDataCommand(QUndoCommand): + def __init__(self, frictions, new_data): + QUndoCommand.__init__(self) + + self._frictions = frictions + self._new_data = new_data + self._old_rows = list(range(len(frictions))) + self._new_rows = list(range(len(new_data))) + + self._old_friction = [] + for row in self._old_rows: + self._old_friction.append((row, self._frictions.lst[row])) + + def undo(self): + self._frictions.delete_i(self._new_rows) + for row, el in self._old_friction: + self._frictions.insert(row, el) + + def redo(self): + self._frictions.delete_i(self._old_rows) + for row in self._new_rows: + new = self._frictions.new(row) + d = self._new_data[row] + new.edge = d[0] + new.begin_rk = d[1] + new.end_rk = d[2] + new.begin_strickler = d[3] + new.end_strickler = d[4] + + class DelCommand(QUndoCommand): def __init__(self, frictions, rows): QUndoCommand.__init__(self) diff --git a/src/View/Frictions/Window.py b/src/View/Frictions/Window.py index 99cc02d5..a9356eca 100644 --- a/src/View/Frictions/Window.py +++ b/src/View/Frictions/Window.py @@ -26,10 +26,11 @@ from PyQt5.QtGui import ( QKeySequence, ) +from PyQt5 import QtWidgets from PyQt5.QtCore import ( Qt, QVariant, QAbstractTableModel, QCoreApplication, QModelIndex, pyqtSlot, - QRect, + QRect, QSettings, ) from PyQt5.QtWidgets import ( @@ -46,7 +47,7 @@ from View.Frictions.UndoCommand import ( ) from View.Frictions.Table import ( - TableModel, ComboBoxDelegate + FrictionTableModel, ComboBoxDelegate ) from View.Tools.Plot.PamhyrCanvas import MplCanvas @@ -114,7 +115,7 @@ class FrictionsWindow(PamhyrWindow): editable_headers = [] table = self.find(QTableView, f"tableView") - self._table = TableModel( + self._table = FrictionTableModel( table_view=table, table_headers=self._trad.get_dict("table_headers"), editable_headers=editable_headers, @@ -166,7 +167,8 @@ class FrictionsWindow(PamhyrWindow): self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_sort").triggered.connect(self.sort) - + self.find(QAction, "action_import").triggered\ + .connect(self.import_from_file) self.find(QAction, "action_edit_stricklers").triggered.connect( self.edit_stricklers ) @@ -273,3 +275,22 @@ class FrictionsWindow(PamhyrWindow): parent=self ) strick.show() + + def import_from_file(self): + options = QFileDialog.Options() + settings = QSettings(QSettings.IniFormat, + QSettings.UserScope, 'MyOrg', ) + options |= QFileDialog.DontUseNativeDialog + + file_types = [self._trad["file_rug"], self._trad["file_all"]] + + file_name, _ = QtWidgets.QFileDialog.getOpenFileName( + self, + self._trad["open_file"], + "", + ";; ".join(file_types), + options=options + ) + + if file_name != "": + self._table.read_from_file(file_name) diff --git a/src/View/Frictions/translate.py b/src/View/Frictions/translate.py index 3b1a1a88..3de25fc9 100644 --- a/src/View/Frictions/translate.py +++ b/src/View/Frictions/translate.py @@ -38,6 +38,10 @@ class FrictionsTranslate(MainTranslate): self._dict["Edit frictions"] = _translate( "Frictions", "Edit frictions" ) + self._dict["file_rug"] = _translate( + "Frictions", "Mage initial frictions file (*.RUG *.rug)") + self._dict["file_all"] = _translate( + "Frictions", "All files (*)") self._sub_dict["table_headers"] = { # "name": self._dict["name"], diff --git a/src/View/InitialConditions/Table.py b/src/View/InitialConditions/Table.py index 1915b4e2..1f4ace72 100644 --- a/src/View/InitialConditions/Table.py +++ b/src/View/InitialConditions/Table.py @@ -282,7 +282,7 @@ class InitialConditionTableModel(PamhyrTableModel): self.layoutAboutToBeChanged.emit() self.layoutChanged.emit() - def import_from_results(self, index, results): + def import_from_results(self, results): if results is None: logger.error("No results data") return @@ -319,6 +319,33 @@ class InitialConditionTableModel(PamhyrTableModel): self.layoutAboutToBeChanged.emit() self.layoutChanged.emit() + def read_from_ini(self, file_name): + + reach_id = self._data.river.enable_edges().index(self._reach) + 1 + + logger.debug(f"Import initial conditions from {file_name}") + data = [] + with open(file_name, encoding="utf-8") as ini_file: + for line in ini_file: + if not (line.startswith("#") or + line.startswith("*") or + line.startswith("$")): + line = line.split() + if int(line[0]) == reach_id: + data.append([line[4], line[2], line[3]]) + + self._undo.push( + ReplaceDataCommand( + self._lst, + list( + map( + lambda d: self._lst.new_from_data(*d), + data + ) + ) + ) + ) + def undo(self): self._undo.undo() self.layoutChanged.emit() diff --git a/src/View/InitialConditions/Window.py b/src/View/InitialConditions/Window.py index e2cbeedf..b78c07b4 100644 --- a/src/View/InitialConditions/Window.py +++ b/src/View/InitialConditions/Window.py @@ -269,20 +269,29 @@ class InitialConditionsWindow(PamhyrWindow): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', ) options |= QFileDialog.DontUseNativeDialog - filename, _ = QtWidgets.QFileDialog.getOpenFileName( + + file_types = [ + self._trad["file_bin"], + self._trad["file_ini"], + self._trad["file_all"], + ] + file_name, _ = QtWidgets.QFileDialog.getOpenFileName( self, self._trad["open_file"], "", - ";; ".join(["Mage (*.BIN)"]), + ";; ".join(file_types), options=options ) - if filename != "": - size = os.stat(filename).st_size - # self._table.import_geometry(0, filename) - self._import_from_file(filename) + if file_name != "": + size = os.stat(file_name).st_size + # self._table.import_geometry(0, file_name) + if file_name[-4:] == ".BIN": + self._import_from_bin_file(file_name) + elif file_name[-4:].upper() == ".INI": + self._import_from_ini_file(file_name) - def _import_from_file(self, file_name): + def _import_from_bin_file(self, file_name): solver = Mage8("dummy") name = os.path.basename(file_name)\ .replace(".BIN", "") @@ -310,10 +319,11 @@ class InitialConditionsWindow(PamhyrWindow): def _import_from_results(self, results): logger.debug(f"import from results: {results}") - row = self.index_selected_row() - self._table.import_from_results(row, results) + def _import_from_ini_file(self, file_name): + self._table.read_from_ini(file_name) + def move_up(self): row = self.index_selected_row() self._table.move_up(row) diff --git a/src/View/InitialConditions/translate.py b/src/View/InitialConditions/translate.py index 46aa5e76..19655f02 100644 --- a/src/View/InitialConditions/translate.py +++ b/src/View/InitialConditions/translate.py @@ -34,6 +34,15 @@ class ICTranslate(MainTranslate): self._dict["discharge"] = self._dict["unit_discharge"] self._dict["rk"] = self._dict["unit_rk"] + self._dict["open_file"] = _translate( + "InitialCondition", "Open a file") + self._dict["file_bin"] = _translate( + "InitialCondition", "Mage results file (*.BIN)") + self._dict["file_ini"] = _translate( + "InitialCondition", "Mage initial conditions file (*.INI *.ini)") + self._dict["file_all"] = _translate( + "InitialCondition", "All files (*)") + self._sub_dict["table_headers"] = { # "name": _translate("InitialCondition", "Name"), "rk": self._dict["unit_rk"], diff --git a/src/View/ui/EditBoundaryConditions.ui b/src/View/ui/EditBoundaryConditions.ui index 056c02a8..9d4f9293 100644 --- a/src/View/ui/EditBoundaryConditions.ui +++ b/src/View/ui/EditBoundaryConditions.ui @@ -70,6 +70,7 @@ false + @@ -149,6 +150,18 @@ Remove points to make the curve increasing + + + + ressources/import.pngressources/import.png + + + Import + + + Import from file + + diff --git a/src/View/ui/Frictions.ui b/src/View/ui/Frictions.ui index 77c32181..44127713 100644 --- a/src/View/ui/Frictions.ui +++ b/src/View/ui/Frictions.ui @@ -60,6 +60,7 @@ false + @@ -107,6 +108,18 @@ Ctrl+E + + + + ressources/import.pngressources/import.png + + + Import + + + Import from file + + diff --git a/src/lang/fr.ts b/src/lang/fr.ts index 91bf0e67..b25cab15 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -1,6 +1,5 @@ - - + About @@ -193,17 +192,17 @@ Éditer les conditions aux limites - + X X - + Y Y - + Solid (kg/s) Solide (kg/s) @@ -243,19 +242,19 @@ Options des conditions limites - + No geometry Pas de géométrie - + No geometry found for this reach. This feature requires a reach with a geometry. Aucune géométrie n'a été trouvée sur ce bief. Cette fonctionnalité nécessite un bief muni d'une géométrie. - + Warning Avertissement @@ -264,6 +263,31 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Pollutant Polluant + + + Open a file + Ouvrir un fichier + + + + Mage hydrograph file (*.HYD) + Hydrogramme Mage (*.HYD) + + + + Mage limnigraph file (*.LIM) + Limnigramme Mage (*.LIM) + + + + Mage rating curve file (*.AVA) + Courbe de tarage Mage (*.AVA) + + + + All files (*) + Tous les fichiers (*) + BoundaryConditions @@ -448,27 +472,27 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Biefs - + Main channel Lit mineur - + Floodway Lit moyen - + Not defined Non défini - + Not associated Non associé - + Cross-section Section en travers @@ -478,10 +502,15 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Titre - + Method Méthode + + + Select reach + Sélectionner un bief + Configure @@ -1267,9 +1296,14 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie. - Copyright © 2022-2025 INRAE + Copyright © 2022-2025 INRAE Copyright © 2022-2025 INRAE + + + Copyright © 2022-2025 INRAE + + Frictions @@ -1485,6 +1519,26 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Initial conditions Conditions initiales + + + Open a file + Ouvrir un fichier + + + + Mage results file (*.BIN) + Fichier de résultats Mage (*.BIN) + + + + Mage initial conditions file (*.INI *.ini) + Fichiers de conditions initiales Mage (*.INI *.ini) + + + + All files (*) + Tous les fichiers (*) + InitialConditionAdisTS @@ -1573,17 +1627,17 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie. MainWindow - + Open debug window Ouvrir la fenêtre de débogage - + Open SQLite debuging tool ('sqlitebrowser') Ouvrir l'outil de débogage SQLite ('sqlitebrowser') - + Enable this window Activer cette fenêtre @@ -1803,12 +1857,12 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Éditer la géométrie - + Import geometry Importer une géométrie - + Export geometry Exporter la géométrie @@ -2388,52 +2442,52 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Importer - + Add a cross-section Ajouter une section en travers - + Delete selected cross-section(s) Supprimer les sections en travers sélectionnées - + Edit selected cross section(s) Éditer les sections en travers sélectionnées - + Sort cross-sections by ascending position Trier les sections en travers par PK croissant - + Sort cross-sections by descending position Trier les sections en travers par PK décroissant - + Move up selected cross-section(s) Déplacer les sections en travers vers le haut - + Move down selected cross-section(s) Déplacer les sections en travers vers le bas - + Meshing Maillage - + Summary Résumé - + Checks Vérifications @@ -2568,17 +2622,17 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Importer depuis un fichier - + Update RK Mise à jour des PK - + Recompute RK Recalcule des PK - + Purge cross-sections to keep a given number of points Purger les profiles pour garder un nombre fixer de points @@ -2593,42 +2647,42 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Exporter les données au format CSV - + Generate uniform Générer un regime uniforme - + Generate rating curve from Manning law Générer une courbe de tarage (loi de Maning) - + Generate critical Générer régime critique - + Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z)) Générer une courbe de tarage (Q(z) = Sqrt(g*S(z)^3/L(z))) - + Make increasing Augmenter - + Remove points to make the curve increasing Supprimer des points pour rendre la courbe croissante - + Shift Translater - + Shift selected sections coordinates Translater les coordonnées des sections sélectionnées @@ -2653,47 +2707,47 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Données - + Please select a reach Veuillez sélectionner un bief - + Last open study Dernière étude ouverte - + Do you want to open again the last open study? Voulez-vous rouvrir la dernière étude ? - + This edition window need a reach selected into the river network to work on it Cette fenêtre d'édition a besoin d'un bief sélectionné dans le réseau pour travailler dessus - + Close without saving study Fermer sans sauvegarder l'étude - + Do you want to save current study before closing it? Souhaitez-vous sauvegarder l'étude en cours avant de la fermer ? - + Warning Avertissement - + X (m) X (m) - + Y (m) Y (m) @@ -2868,12 +2922,12 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Export données brutes - + Yes Oui - + No Non @@ -2892,6 +2946,16 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie.Import background image Importer une image de fond + + + Select reach + Sélectionner un bief + + + + Change current reach + Changer le bief courrant + MainWindow_reach @@ -3618,87 +3682,87 @@ Cette fonctionnalité nécessite un bief muni d'une géométrie. Unit - + Width (m) Largeur (m) - + Depth (m) Profondeur (m) - + Diameter (m) Diamètre (m) - + Thickness (m) Épaisseur (m) - + Elevation (m) Cote (m) - + Area (hectare) Aire (hectare) - + Time (sec) Temps (s) - + Time (JJJ:HH:MM:SS) Temps (JJJ:HH:MM:SS) - + Date (sec) Date (s) - + Date (ISO format) Date (format ISO) - + River Kilometer (m) Point Kilométrique (m) - + Max Depth (m) Profondeur max (m) - + Mean Depth (m) Profondeur moyenne (m) - + Velocity (m/s) Vitesse (m/s) - + Wet Perimeter (m) Périmètre mouillé (m) - + Hydraulic Radius (m) Rayon hydraulique (m) - + Froude number Nombre de Froude @@ -3741,132 +3805,132 @@ moyen droit (m) D90 - + Width Envelop (m) Enveloppe de la argeur (m) - + Max Width (m) Largeur max (m) - + Min Width (m) Largeur min (m) - + Min Depth (m) Profondeur min (m) - + Depth Envelop (m) Enveloppe de la profondeur (m) - + Bed Elevation (m) Cote du fond (m) - + Bed Elevation Envelop (m) Enveloppe de la cote du fond (m) - + Max Bed Elevation (m) Cote du fond max (m) - + Min Bed Elevation (m) Cote du fond min (m) - + Water Elevation (m) Cote de l'eau (m) - + Water Elevation Envelop (m) Enveloppe de la cote de l'eau (m) - + Max Water Elevation (m) Cote de l'eau max (m) - + Min Water Elevation (m) Cote de l'eau min (m) - + Velocity Envelop (m/s) Enveloppe de la vitesse (m/s) - + Max Velocity (m/s) Vitesse max (m/s) - + Min Velocity (m/s) Vitesse min (m/s) - + Area Aire - + Rho Rho - + Porosity Porosité - + CDC_RIV CDC_RIV - + CDC_CAS CDC_CAS - + APD APD - + AC AC - + BC BC - + Concentration (g/l) Concentration (g/l) - + Mass (kg) Masse @@ -3886,32 +3950,32 @@ moyen droit (m) Coeff c - + Discharge Débit - + Discharge Envelop Enveloppe du débit - + Max Discharge Débit max - + Min Discharge Débit min - + Concentration Concentration - + Wet Area Aire mouillée