From 2c0b155bafb96889eba7e1ba6d9d3691e6f5a8a5 Mon Sep 17 00:00:00 2001 From: Dylan Jeannin Date: Wed, 20 May 2026 10:40:11 +0200 Subject: [PATCH 01/12] error in the name of a db table --- src/Model/Scenario.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Scenario.py b/src/Model/Scenario.py index 10c5a3f7..1bcf38b2 100644 --- a/src/Model/Scenario.py +++ b/src/Model/Scenario.py @@ -35,7 +35,7 @@ class Scenario(SQLSubModel): "output_rk_adists", "boundary_condition_adists", "boundary_condition_data_adists", "lateral_contribution_adists", "lateral_contribution_data_adists", - "initial_conditions_adists", "initial_conditions_adists_data", + "initial_conditions_adists", "initial_conditions_adists_spec", "d90_adists", "d90_adists_spec", "dif_adists_spec", "pollutants", "pollutants_characteristics", From f9d83ac68cc980891791acfb3f190a477d41998f Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Thu, 21 May 2026 11:16:31 +0200 Subject: [PATCH 02/12] Scenario: Add memory clean for Pamhyr list, dict and reaches. --- src/Model/Geometry/Reach.py | 3 +++ src/Model/Scenario.py | 17 ++++++++++--- src/Model/Study.py | 43 +++++++++++++++++++++++++++++--- src/Model/Tools/PamhyrDict.py | 3 +++ src/Model/Tools/PamhyrList.py | 3 +++ src/Model/Tools/PamhyrListExt.py | 3 +++ 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 318adcb8..2516c75a 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -103,6 +103,9 @@ class Reach(SQLSubModel): predicate=lambda obj, data: True, modifier=lambda obj, data: None, data={}): + if predicate(self, data): + modifier(self, data) + for p in self._profiles: p._data_traversal(predicate, modifier, data) diff --git a/src/Model/Scenario.py b/src/Model/Scenario.py index 1bcf38b2..cc4c01f4 100644 --- a/src/Model/Scenario.py +++ b/src/Model/Scenario.py @@ -19,7 +19,7 @@ import os import logging -from tools import logger_exception +from tools import logger_exception, timer, flatten from Model.Tools.PamhyrDB import SQLSubModel @@ -225,10 +225,13 @@ class Scenario(SQLSubModel): return aux(self, []) - def drop_deleted_data(self, execute): + @timer + def clean_deleted_data(self, execute): tables = self.tables_with_deleted_column branch = self.get_parent_branch() + all_ids = [] + for table in tables: if self.parent is None: # This scenario is the default scenario, so we can @@ -257,17 +260,23 @@ class Scenario(SQLSubModel): if ids is None or len(ids) == 0: continue + ids = flatten(ids) + logger.debug( - f"(s{self.id}) Drop deleted data into '{table}' : {ids}" + f"({self.name}) Drop deleted data into '{table}' : {ids}" ) execute( f"DELETE FROM {table} " + f"WHERE scenario = {self.id} " + "AND pamhyr_id IN " + - f"({', '.join(map(lambda x: str(x[0]), ids))})" + f"({', '.join(map(str, ids))})" ) + all_ids += ids + + return all_ids + @property def id(self): return self._id diff --git a/src/Model/Study.py b/src/Model/Study.py index 2ec031a7..522ef7fb 100644 --- a/src/Model/Study.py +++ b/src/Model/Study.py @@ -24,12 +24,15 @@ from datetime import datetime from tools import timer, timestamp from functools import reduce +from Model.Tools.PamhyrListExt import PamhyrModelList +from Model.Tools.PamhyrDict import PamhyrModelDict from Model.Tools.PamhyrDB import SQLModel from Model.Scenarios import Scenarios from Model.Scenario import Scenario from Model.Status import StudyStatus from Model.Except import NotImplementedMethodeError from Model.River import River +from Model.Geometry.Reach import Reach from Checker.Study import * @@ -450,14 +453,48 @@ class Study(SQLModel): data=progress ) - # Clear DB for each scenarios - for scenar in self.scenarios.lst: - scenar.drop_deleted_data(self.execute) + # Clear DB for current scenario + ids = self.status.scenario.clean_deleted_data(self.execute) + self.memory_clean(ids) progress() self.commit() + @timer + def memory_clean(self, ids): + list_classes = set(PamhyrModelList.__subclasses__()) + dict_classes = set(PamhyrModelDict.__subclasses__()) + reach_class = Reach + + def modifier(obj, data): + t = type(obj) + + if t in list_classes: + obj._lst = list( + filter( + lambda el: el.id not in ids, + obj._lst + ) + ) + elif t in dict_classes: + new = {} + for key in obj._dict: + if obj._dict[key].id not in ids: + new[key] = obj._dict[key] + obj._dict = new + elif t is reach_class: + obj._profiles = list( + filter( + lambda el: el.id not in ids, + obj._profiles + ) + ) + + self.river._data_traversal( + lambda o, d: True, modifier, {} + ) + def sql_save_request_count(self, *args, **kargs): return self._count() diff --git a/src/Model/Tools/PamhyrDict.py b/src/Model/Tools/PamhyrDict.py index 52ac2e3b..7bddc1c8 100644 --- a/src/Model/Tools/PamhyrDict.py +++ b/src/Model/Tools/PamhyrDict.py @@ -97,6 +97,9 @@ class PamhyrModelDict(SQLSubModel): Nothing """ + if predicate(self, data): + modifier(self, data) + for key in self._dict: self._dict[key]\ ._data_traversal(predicate, modifier, data) diff --git a/src/Model/Tools/PamhyrList.py b/src/Model/Tools/PamhyrList.py index 5d735443..a97f869a 100644 --- a/src/Model/Tools/PamhyrList.py +++ b/src/Model/Tools/PamhyrList.py @@ -100,6 +100,9 @@ class PamhyrModelList(SQLSubModel): Nothing """ + if predicate(self, data): + modifier(self, data) + for el in self._lst: el._data_traversal(predicate, modifier, data) diff --git a/src/Model/Tools/PamhyrListExt.py b/src/Model/Tools/PamhyrListExt.py index 07d0ed58..187d437f 100644 --- a/src/Model/Tools/PamhyrListExt.py +++ b/src/Model/Tools/PamhyrListExt.py @@ -107,6 +107,9 @@ class PamhyrModelList(SQLSubModel): Returns: Nothing """ + if predicate(self, data): + modifier(self, data) + for el in self._lst: el._data_traversal(predicate, modifier, data) From 363b9c7132df6048004c094edfe821970d3aebe1 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Thu, 21 May 2026 15:05:17 +0200 Subject: [PATCH 03/12] Scenario: Add HS to memory clean and minor comment change. --- src/Model/Geometry/Reach.py | 4 ++-- src/Model/Study.py | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 2516c75a..45602018 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -208,7 +208,7 @@ class Reach(SQLSubModel): self.modified() def delete(self, indexes): - """Delete some elements in profile list + """Set some elements as deleted in profile list Args: indexes: The list of index to delete @@ -250,7 +250,7 @@ class Reach(SQLSubModel): self.modified() def delete_profiles(self, profiles): - """Delete some elements in profile list + """Set profiles list as deleted Args: profiles: The list of profile to delete diff --git a/src/Model/Study.py b/src/Model/Study.py index 522ef7fb..e5ecdbbf 100644 --- a/src/Model/Study.py +++ b/src/Model/Study.py @@ -33,6 +33,12 @@ from Model.Status import StudyStatus from Model.Except import NotImplementedMethodeError from Model.River import River from Model.Geometry.Reach import Reach +from Model.HydraulicStructures.HydraulicStructures import ( + HydraulicStructure +) +from Model.HydraulicStructures.Basic.HydraulicStructures import ( + BasicHS +) from Checker.Study import * @@ -463,18 +469,29 @@ class Study(SQLModel): @timer def memory_clean(self, ids): + if len(ids) == 0: + return + + reach_class = Reach + hs_classes = [HydraulicStructure, BasicHS] list_classes = set(PamhyrModelList.__subclasses__()) dict_classes = set(PamhyrModelDict.__subclasses__()) - reach_class = Reach def modifier(obj, data): t = type(obj) - if t in list_classes: - obj._lst = list( + if t is reach_class: + obj._profiles = list( filter( lambda el: el.id not in ids, - obj._lst + obj._profiles + ) + ) + elif t in hs_classes: + obj._data = list( + filter( + lambda el: el.id not in ids, + obj._data ) ) elif t in dict_classes: @@ -483,11 +500,11 @@ class Study(SQLModel): if obj._dict[key].id not in ids: new[key] = obj._dict[key] obj._dict = new - elif t is reach_class: - obj._profiles = list( + elif t in list_classes: + obj._lst = list( filter( lambda el: el.id not in ids, - obj._profiles + obj._lst ) ) From 4537a829affe0f600d33cdcabac73b1c5b99f8fd Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Thu, 21 May 2026 16:26:25 +0200 Subject: [PATCH 04/12] WaitingDialog: Try fix deadlock. --- src/View/WaitingDialog.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/View/WaitingDialog.py b/src/View/WaitingDialog.py index b61228e6..b9ee9947 100644 --- a/src/View/WaitingDialog.py +++ b/src/View/WaitingDialog.py @@ -90,6 +90,7 @@ class WaitingDialog(PamhyrDialog): options=[], parent=parent ) + self._to_close = False self._payload_fn = payload_fn @@ -131,7 +132,7 @@ class WaitingDialog(PamhyrDialog): ) def end_worker(self): - self._worker_thread.terminate() + self._worker_thread.quit() self._worker_thread.wait() def close(self): @@ -141,4 +142,12 @@ class WaitingDialog(PamhyrDialog): except Exception as e: logger_exception(e) + self._to_close = True + super().close() + + def closeEvent(self, event): + if self._to_close: + super().closeEvent(event) + else: + event.ignore() From d922436ada65947ecd4b250e1bbe17e104ecd5c1 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Thu, 21 May 2026 16:26:51 +0200 Subject: [PATCH 05/12] Scenario: Add Discard option to scenario switching dialog. --- src/View/Scenarios/GraphWidget.py | 15 ++++++++++----- src/View/Translate.py | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/View/Scenarios/GraphWidget.py b/src/View/Scenarios/GraphWidget.py index 0fff07d3..8b8ea3fa 100644 --- a/src/View/Scenarios/GraphWidget.py +++ b/src/View/Scenarios/GraphWidget.py @@ -426,30 +426,35 @@ class GraphWidget(QGraphicsView): dlg.setWindowTitle(self._trad["mb_save_title"]) dlg.setText(self._trad["mb_save_msg"]) - opt = QMessageBox.Save | QMessageBox.Cancel + opt = QMessageBox.Cancel | QMessageBox.Save | QMessageBox.Discard dlg.setStandardButtons(opt) dlg.setIcon(QMessageBox.Warning) dlg.button(QMessageBox.Save).setText(self._trad["Save"]) + dlg.button(QMessageBox.Discard).setText(self._trad["Don't save"]) dlg.button(QMessageBox.Cancel).setText(self._trad["Cancel"]) res = dlg.exec() if res == QMessageBox.Save: - return True + return "Save" elif res == QMessageBox.Cancel: - return False + return "Cancel" + else: + return "Discard" def select_scenario(self, item): if type(item) is not ScenarioItem: return - must_saved = self.dialog_save() + must_save = self.dialog_save() + if must_save == "Cancel": + return def fn(): self._close_other_window() - if must_saved: + if must_save == "Save": self._study.save() self._undo.push( diff --git a/src/View/Translate.py b/src/View/Translate.py index 82f29b50..c0872a74 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -281,6 +281,7 @@ class MainTranslate(UnitTranslate): self._dict["No"] = _translate("MainWindow", "No") self._dict["Cancel"] = _translate("MainWindow", "Cancel") self._dict["Save"] = _translate("MainWindow", "Save") + self._dict["Don't save"] = _translate("MainWindow", "Don't save") self._dict["Close"] = _translate("MainWindow", "Close") self._dict["Solver"] = _translate("MainWindow", "Solver") From 5d8ec51417865f77cd6f6473facb674795be04bb Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 09:21:55 +0200 Subject: [PATCH 06/12] Scenario: Remove posibility to remove non leaf scenario. --- src/Model/Scenarios.py | 11 +++++++++++ src/View/Scenarios/ContextMenu.py | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Model/Scenarios.py b/src/Model/Scenarios.py index a2db1f98..3dee2beb 100644 --- a/src/Model/Scenarios.py +++ b/src/Model/Scenarios.py @@ -16,6 +16,8 @@ # -*- coding: utf-8 -*- +from functools import reduce + from tools import logger_exception from Model.Tools.PamhyrDict import PamhyrModelDict @@ -78,3 +80,12 @@ class Scenarios(PamhyrModelDict): self._dict[key].set_as_deleted() self._status.modified() + + def is_leaf(self, scenario): + return not reduce( + lambda acc, s: ( + acc or s.parent is scenario + ), + self._dict.values(), + False + ) diff --git a/src/View/Scenarios/ContextMenu.py b/src/View/Scenarios/ContextMenu.py index 319c0d92..1a733082 100644 --- a/src/View/Scenarios/ContextMenu.py +++ b/src/View/Scenarios/ContextMenu.py @@ -57,6 +57,8 @@ class DefaultMenu(AbstractMenu): class ScenarioMenu(AbstractMenu): def run(self): item = self._items[0] + scenarios = item.graph._study.scenarios + current_scenario = item.graph._study.status.scenario.id select = self._menu.addAction(self._trad["menu_select_scenario"]) @@ -64,7 +66,9 @@ class ScenarioMenu(AbstractMenu): delete = None if item.scenario.id != 0: - delete = self._menu.addAction(self._trad["menu_del_scenario"]) + if scenarios.is_leaf(item.scenario): + delete = self._menu.addAction(self._trad["menu_del_scenario"]) + if item.scenario.id == current_scenario: duplicate = self._menu.addAction( self._trad["menu_dup_scenario"] From 190ca5d171777778b0e2714144fd9118228a740c Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 09:22:35 +0200 Subject: [PATCH 07/12] Scenario: Add some save dialog. --- src/View/Scenarios/GraphWidget.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/View/Scenarios/GraphWidget.py b/src/View/Scenarios/GraphWidget.py index 8b8ea3fa..bba78edd 100644 --- a/src/View/Scenarios/GraphWidget.py +++ b/src/View/Scenarios/GraphWidget.py @@ -470,9 +470,16 @@ class GraphWidget(QGraphicsView): self.changeScenario.emit(self.sender()) def new_scenario(self, pos): + must_save = self.dialog_save() + if must_save == "Cancel": + return + def fn(): self._close_other_window() - self._study.save() + + if must_save == "Save": + self._study.save() + self._undo.push( AddScenariosCommand( self._study, @@ -500,9 +507,16 @@ class GraphWidget(QGraphicsView): self.changeScenario.emit(self.sender()) def duplicate_scenario(self, item): + must_save = self.dialog_save() + if must_save == "Cancel": + return + def fn(): self._close_other_window() - # self._study.save() + + if must_save == "Save": + self._study.save() + self._undo.push( DuplicateScenariosCommand( self._study, From 7cb3ac8a8025f77f3794368453f2fa1f294435ec Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 09:40:56 +0200 Subject: [PATCH 08/12] Clean code: Remove some print function call. --- src/Meshing/Internal.py | 2 +- .../BoundaryConditionsAdisTS/BoundaryConditionAdisTS.py | 6 ++++-- src/Model/Geometry/ProfileXYZ.py | 1 - src/Model/Geometry/Reach.py | 1 - src/Model/HydraulicStructures/Basic/HydraulicStructures.py | 1 - src/Model/InitialConditions/InitialConditions.py | 2 -- src/Model/Reservoir/Reservoir.py | 2 -- src/Model/Scenarios.py | 2 +- src/Model/Tools/PamhyrDB.py | 6 ++++-- src/Solver/RubarBE.py | 2 -- 10 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/Meshing/Internal.py b/src/Meshing/Internal.py index 2134b8c4..7a14bad5 100644 --- a/src/Meshing/Internal.py +++ b/src/Meshing/Internal.py @@ -214,7 +214,7 @@ class InternalMeshing(AMeshingTool): ratio = (alpha[j0] - beta[j0-1]) \ / (beta[j0] - beta[j0-1]) if ratio < 0.0: - print(f"ratio négatif {ratio}") + logger.warning(f"ratio négatif {ratio}") # on double le point a gauche p = sect2.point(start2+j0-1).copy() sect2.insert_point(start2+j0-1, p) diff --git a/src/Model/BoundaryConditionsAdisTS/BoundaryConditionAdisTS.py b/src/Model/BoundaryConditionsAdisTS/BoundaryConditionAdisTS.py index b33c1a03..b38c8ae2 100644 --- a/src/Model/BoundaryConditionsAdisTS/BoundaryConditionAdisTS.py +++ b/src/Model/BoundaryConditionsAdisTS/BoundaryConditionAdisTS.py @@ -304,8 +304,10 @@ class BoundaryConditionAdisTS(SQLSubModel): if pol_id not in pid_pol: # ⚠️ cas important : probablement déjà migré - print(f"[WARN] pol_id {pol_id} not in pid_pol " + - f"→ probably already migrated") + logger.warning( + f"pol_id {pol_id} not in pid_pol " + + "→ probably already migrated" + ) continue execute( diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 2ccb75fd..1b821051 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -396,7 +396,6 @@ class ProfileXYZ(Profile, SQLSubModel): for point in self._points: p = point.copy() - print(p) new_p._points.append(p) new_p.modified() diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 45602018..9812df21 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -202,7 +202,6 @@ class Reach(SQLSubModel): else: gi = self.get_global_profil_index(index) profile.num = gi - print(f"gi = {gi}") self._profiles.insert(gi, profile) self.modified() diff --git a/src/Model/HydraulicStructures/Basic/HydraulicStructures.py b/src/Model/HydraulicStructures/Basic/HydraulicStructures.py index 6df03953..757d8bfb 100644 --- a/src/Model/HydraulicStructures/Basic/HydraulicStructures.py +++ b/src/Model/HydraulicStructures/Basic/HydraulicStructures.py @@ -183,7 +183,6 @@ class BasicHS(SQLSubModel): bhs._data = BHSValue._db_load( execute, data ) - print(f"{bhs_pid} : {deleted}") if deleted: bhs.set_as_deleted() diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index bbec0492..fc299a77 100644 --- a/src/Model/InitialConditions/InitialConditions.py +++ b/src/Model/InitialConditions/InitialConditions.py @@ -588,8 +588,6 @@ class InitialConditions(SQLSubModel): for data in self._data: data_height[data["rk"].rk] = data["height"] - print(data_height) - incline = self._reach.reach.get_incline_median_mean() logger.debug(f"incline = {incline}") diff --git a/src/Model/Reservoir/Reservoir.py b/src/Model/Reservoir/Reservoir.py index c093691c..e120f9ec 100644 --- a/src/Model/Reservoir/Reservoir.py +++ b/src/Model/Reservoir/Reservoir.py @@ -263,8 +263,6 @@ class Reservoir(SQLSubModel): cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes) Scenario.update_db_add_scenario(execute, table) - print(execute(f"SELECT * FROM {table}")) - cls._db_create(execute, ext="_tmp") execute( f"INSERT INTO {table}_tmp " + diff --git a/src/Model/Scenarios.py b/src/Model/Scenarios.py index 3dee2beb..e37a504e 100644 --- a/src/Model/Scenarios.py +++ b/src/Model/Scenarios.py @@ -84,7 +84,7 @@ class Scenarios(PamhyrModelDict): def is_leaf(self, scenario): return not reduce( lambda acc, s: ( - acc or s.parent is scenario + acc or (not s.is_deleted() and s.parent is scenario) ), self._dict.values(), False diff --git a/src/Model/Tools/PamhyrDB.py b/src/Model/Tools/PamhyrDB.py index 9ca7257c..caf56bb0 100644 --- a/src/Model/Tools/PamhyrDB.py +++ b/src/Model/Tools/PamhyrDB.py @@ -313,8 +313,10 @@ class SQLSubModel(PamhyrID): if node_id not in nodes: # ⚠️ cas important : probablement déjà migré - print(f"[WARN] node_id {node_id} not in nodes " + - f"→ probably already migrated") + logger.warning( + f"node_id {node_id} not in nodes " + + "→ probably already migrated" + ) continue execute( diff --git a/src/Solver/RubarBE.py b/src/Solver/RubarBE.py index f33b04dc..e92ca2cd 100644 --- a/src/Solver/RubarBE.py +++ b/src/Solver/RubarBE.py @@ -426,7 +426,6 @@ class Rubar3(CommandLineSolver): coeff = coeff_min else: for s in lst: - print(s.begin_rk, s.end_rk) if (rk >= s.begin_rk and rk <= s.end_rk or rk <= s.begin_rk and rk >= s.end_rk): coeff = s.begin_strickler # TODO: inerpolate @@ -473,7 +472,6 @@ class Rubar3(CommandLineSolver): last = profiles[-1] if first not in data or last not in data: - print(data) logger.error( "Study initial condition is not fully defined" ) From 3a3b5ea2dd78badbcf115eb7bcc0debd8a7cbd0e Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 10:09:34 +0200 Subject: [PATCH 09/12] PamhyrDB: Minor change. --- src/Model/Tools/PamhyrDB.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Model/Tools/PamhyrDB.py b/src/Model/Tools/PamhyrDB.py index caf56bb0..00ca8c40 100644 --- a/src/Model/Tools/PamhyrDB.py +++ b/src/Model/Tools/PamhyrDB.py @@ -48,11 +48,9 @@ class SQLModel(SQL): self._cur = self._db.cursor() if is_new: - logger.info("Create database") self._create() # Create db # self._save() # Save else: - logger.info("Update database") self._update() # Update db scheme if necessary # self._load() # Load data From 64d2b2678a0336e9c70e02fd5f4d9877976d5867 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 10:39:10 +0200 Subject: [PATCH 10/12] Geometry: Profiles: Fix shift methode. --- src/Model/Geometry/PointXYZ.py | 7 +++++++ src/Model/Geometry/ProfileXYZ.py | 10 ++-------- src/Model/Tools/PamhyrDB.py | 2 ++ src/View/Geometry/Table.py | 4 ---- src/View/Geometry/UndoCommand.py | 4 ++-- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Model/Geometry/PointXYZ.py b/src/Model/Geometry/PointXYZ.py index 3215d009..4c28a07a 100644 --- a/src/Model/Geometry/PointXYZ.py +++ b/src/Model/Geometry/PointXYZ.py @@ -338,6 +338,13 @@ class PointXYZ(Point): self._z = float(value) self.modified() + def shift(self, x, y, z): + self.x += x + self.y += y + self.z += z + + self.modified() + def get_coordinate(self): return (self._x, self._y, self._z) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 1b821051..d928fdd1 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -24,7 +24,7 @@ from typing import List from functools import reduce from dataclasses import dataclass -from tools import timer, flatten +from tools import trace, timer, flatten from shapely import geometry from Model.Tools.PamhyrDB import SQLSubModel @@ -1073,16 +1073,10 @@ class ProfileXYZ(Profile, SQLSubModel): def shift(self, x, y, z): for p in self._points: - p.x = p.x + x - p.y = p.y + y - p.z = p.z + z + p.shift(x, y, z) self.modified() - def modified(self): - self.tab_up_to_date = False - self.station_up_to_date = False - def add_npoints(self, npoints): # add npoints in a profile for k in range(npoints): diff --git a/src/Model/Tools/PamhyrDB.py b/src/Model/Tools/PamhyrDB.py index 00ca8c40..3ee72f0c 100644 --- a/src/Model/Tools/PamhyrDB.py +++ b/src/Model/Tools/PamhyrDB.py @@ -26,6 +26,8 @@ from functools import reduce from SQL import SQL from Model.Except import NotImplementedMethodeError +from tools import trace + from Model.Tools.PamhyrID import PamhyrID logger = logging.getLogger() diff --git a/src/View/Geometry/Table.py b/src/View/Geometry/Table.py index 0c2f5552..d51d1f22 100644 --- a/src/View/Geometry/Table.py +++ b/src/View/Geometry/Table.py @@ -251,7 +251,6 @@ class GeometryReachTableModel(PamhyrTableModel): self.layoutChanged.emit() def meshing(self, mesher, data, tableView): - new_profiles = mesher.meshing( self._data, **data @@ -287,7 +286,6 @@ class GeometryReachTableModel(PamhyrTableModel): self.layoutChanged.emit() def purge(self, np_purge): - self._undo.push( PurgeCommand( self._data, np_purge @@ -296,7 +294,6 @@ class GeometryReachTableModel(PamhyrTableModel): self.layoutChanged.emit() def shift(self, rows, dx, dy, dz): - self._undo.push( ShiftCommand( self._data, rows, dx, dy, dz @@ -305,7 +302,6 @@ class GeometryReachTableModel(PamhyrTableModel): self.layoutChanged.emit() def change_reach(self, new_reach, parent): - self._undo.push( ChangeReachCommand( new_reach, diff --git a/src/View/Geometry/UndoCommand.py b/src/View/Geometry/UndoCommand.py index 3a5da35b..5414f358 100644 --- a/src/View/Geometry/UndoCommand.py +++ b/src/View/Geometry/UndoCommand.py @@ -411,13 +411,13 @@ class ShiftCommand(QUndoCommand): def undo(self): for i in self._rows: profile = self._reach.profiles[i] - self._reach.profiles[i].shift( + profile.shift( -self._dx, -self._dy, -self._dz ) def redo(self): for i in self._rows: profile = self._reach.profiles[i] - self._reach.profiles[i].shift( + profile.shift( self._dx, self._dy, self._dz ) From e26b170b0523be27be81c95bf87b73efb957dc7a Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 10:53:51 +0200 Subject: [PATCH 11/12] Network: Fix reach selection where the study is read only. --- src/View/Network/GraphWidget.py | 77 +++++++++++++++++---------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/View/Network/GraphWidget.py b/src/View/Network/GraphWidget.py index 83aa8014..4646a011 100644 --- a/src/View/Network/GraphWidget.py +++ b/src/View/Network/GraphWidget.py @@ -925,9 +925,11 @@ class GraphWidget(QGraphicsView): self.scale(scaleFactor, scaleFactor) def mousePressEvent(self, event): - if self._only_display or self.graph._status.is_read_only(): + if self._only_display: return + locked = self.graph._status.is_read_only() + pos = self.mapToScene(event.pos()) self.clicked = True @@ -936,9 +938,6 @@ class GraphWidget(QGraphicsView): super(GraphWidget, self).mousePressEvent(event) return - if self.graph._status.is_read_only(): - return - # Move item and select edge item if self._state == "move": self._selected_new_edge_src_node = None @@ -948,39 +947,41 @@ class GraphWidget(QGraphicsView): edge = items[0] if edge: self.set_current_edge(edge) - elif items and type(items[0]) is NodeItem: - self._mouse_origin_x = pos.x() - self._mouse_origin_y = pos.y() - self._current_moved_node = items[0] + if not locked: + if items and type(items[0]) is NodeItem: + self._mouse_origin_x = pos.x() + self._mouse_origin_y = pos.y() + self._current_moved_node = items[0] - # Add nodes and edges - elif self._state == "add": - items = self.items(event.pos()) - nodes = list(filter(lambda i: type(i) is NodeItem, items)) - if not nodes: - self.add_node(pos) - self.set_selected_new_edge_src_node(None) - else: - if self.selected_new_edge_src_node() is None: - self.set_selected_new_edge_src_node(nodes[0]) + if not locked: + # Add nodes and edges + if self._state == "add": + items = self.items(event.pos()) + nodes = list(filter(lambda i: type(i) is NodeItem, items)) + if not nodes: + self.add_node(pos) + self.set_selected_new_edge_src_node(None) else: - self.add_edge(self.selected_new_edge_src_node(), nodes[0]) + if self.selected_new_edge_src_node() is None: + self.set_selected_new_edge_src_node(nodes[0]) + else: + self.add_edge(self.selected_new_edge_src_node(), nodes[0]) - # Delete nodes and edges - elif self._state == "del": - self._selected_new_edge_src_node = None - items = list( - filter( - lambda i: type(i) is NodeItem or type(i) is EdgeItem, - self.items(event.pos()) + # Delete nodes and edges + elif self._state == "del": + self._selected_new_edge_src_node = None + items = list( + filter( + lambda i: type(i) is NodeItem or type(i) is EdgeItem, + self.items(event.pos()) + ) ) - ) - if len(items) > 0: - item = items[0] - if type(item) is NodeItem: - self.del_node(item) - elif type(item) is EdgeItem: - self.del_edge(item) + if len(items) > 0: + item = items[0] + if type(item) is NodeItem: + self.del_node(item) + elif type(item) is EdgeItem: + self.del_edge(item) self.update() super(GraphWidget, self).mousePressEvent(event) @@ -988,10 +989,12 @@ class GraphWidget(QGraphicsView): def mouseReleaseEvent(self, event): self.clicked = False - if self._only_display or self.graph._status.is_read_only(): + if self._only_display: return - if self._state == "move": + locked = self.graph._status.is_read_only() + + if not locked and self._state == "move": if self._current_moved_node is not None: pos = self.mapToScene(event.pos()) self._undo.push( @@ -1007,10 +1010,8 @@ class GraphWidget(QGraphicsView): super(GraphWidget, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): - if self.graph._status.is_read_only(): - return - pos = self.mapToScene(event.pos()) + locked = self.graph._status.is_read_only() # Selecte item on the fly items = self.items(event.pos()) From 61156f1c3eefcab45255cac005f21c919a4874bc Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 22 May 2026 11:51:18 +0200 Subject: [PATCH 12/12] Scenario: Fix scenario duplication. --- src/Model/Geometry/Reach.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 9812df21..668ac5bc 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -52,6 +52,11 @@ class Reach(SQLSubModel): self._guidelines_is_valid = False self._guidelines = {} + super(Reach, self).__init__( + id=parent.pamhyr_id, + status=status, + ) + @property def pamhyr_id(self): return self._parent.pamhyr_id @@ -60,6 +65,10 @@ class Reach(SQLSubModel): def _pamhyr_id(self): return self._parent.pamhyr_id + @_pamhyr_id.setter + def _pamhyr_id(self, id): + return + @classmethod def _db_create(cls, execute): return cls._create_submodel(execute)