diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8920060c1c228f44ca964b3d8eb121233868b68..0f0ec0d541aabe0f7e3ab76bb8daf6be2010f2f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ stages: ############# variables: - MAGE_8_VERSION: "v8.3.5" + MAGE_8_VERSION: "v8.3.6" dl-mage8-doc: stage: downloads @@ -262,6 +262,11 @@ build-linux: - cp -v ../mage8-linux/* pamhyr/mage8/ # Copy Pamhyr - cp -r dist/pamhyr/* pamhyr/ + # Pamhyr script to force x11 + - echo '#!/usr/bin/env bash' > pamhyr/pamhyr-x11 + - echo "" >> pamhyr/pamhyr-x11 + - echo "GDK_BACKEND=x11 ./pamhyr $@" >> pamhyr/pamhyr-x11 + - chmod +x pamhyr/pamhyr-x11 # Copy Pamhyr resources - mkdir -p pamhyr/_internal/View - mkdir -p pamhyr/_internal/View/ui @@ -333,7 +338,7 @@ build-windows: - python -m pip install -U -r ..\requirements.txt # Run Pyinstaller # - pyinstaller --noconsole -y ..\src\pamhyr.py - - pyinstaller --hide-console hide-early -y ..\src\pamhyr.py + - pyinstaller -i ../src/View/ui/ressources/icon.ico --hide-console hide-early -y ..\src\pamhyr.py # Create directory - mkdir pamhyr - dir diff --git a/doc/dev/documentation.org b/doc/dev/documentation.org index 28fad83926138762f693f6c2e408bf75806b86a2..f7285945ad04561cbb8e81531466006c4ca30640 100644 --- a/doc/dev/documentation.org +++ b/doc/dev/documentation.org @@ -1334,7 +1334,7 @@ https://gitlab.irstea.fr/theophile.terraz/pamhyr You can improve or add translation for the project. To contribute to Pamhyr2 translate, you need to use Qt Linguist[fn:qt-linguist]. Open -Qt-linguist and edite the translation ({{{file(.ts)}}}) file, finally, +Qt-linguist and edit the translation ({{{file(.ts)}}}) file, finally, commit the new version of file and make a merge request. If you want add a new language, edit the script diff --git a/doc/tools/PamhyrDoc.cls b/doc/tools/PamhyrDoc.cls index a2cc8473734f6ede033a0d85a9cff3b4eb50103e..a58837204ea56de17d7b82ab38738f00fdb1fcad 100644 --- a/doc/tools/PamhyrDoc.cls +++ b/doc/tools/PamhyrDoc.cls @@ -11,7 +11,8 @@ \usepackage[absolute]{textpos} \usepackage{titlesec} \usepackage{amssymb} -\usepackage{tcolorbox} +\usepackage[minted,xparse,breakable]{tcolorbox} +\usepackage{pmboxdraw} \usepackage{enumitem} \setlist[enumerate]{itemsep=-0.5em} @@ -133,6 +134,7 @@ \setminted[c]{fontsize=\footnotesize,encoding=utf8,linenos} \setminted[c++]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos} \setminted[python]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos} +\setminted[sql]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos} \setminted[shell]{breaklines,fontsize=\footnotesize,encoding=utf8} \setminted[scheme]{breaklines,fontsize=\footnotesize,encoding=utf8} \setminted[commun-lisp]{breaklines,fontsize=\footnotesize,encoding=utf8} @@ -140,7 +142,7 @@ \setminted[llvm]{breaklines,fontsize=\footnotesize,encoding=utf8} %% Add box arrond of minted -\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}% +\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[breakable, boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}% \AfterEndEnvironment{minted}{\end{tcolorbox}}% \makeatletter diff --git a/doc/users/Tuto1/step2.pamhyr b/doc/users/Tuto1/step2.pamhyr index e10e685b6ce4b9e25e4542a1b28f15fdaa745870..c7516dff606939dd005eafa8e54776cee528a4ee 100644 Binary files a/doc/users/Tuto1/step2.pamhyr and b/doc/users/Tuto1/step2.pamhyr differ diff --git a/mage8/mage b/mage8/mage deleted file mode 100755 index dc109d6501bed63680fc49d3ff39bfe0fcadd0f5..0000000000000000000000000000000000000000 Binary files a/mage8/mage and /dev/null differ diff --git a/mage8/mage_as7 b/mage8/mage_as7 deleted file mode 100755 index 74f79fc89126373f12b6a3439125a1d53ebe0a56..0000000000000000000000000000000000000000 Binary files a/mage8/mage_as7 and /dev/null differ diff --git a/mage8/mage_extraire b/mage8/mage_extraire deleted file mode 100755 index 5526370e8ef023fe7b011791a7f39fa567e7e1fc..0000000000000000000000000000000000000000 Binary files a/mage8/mage_extraire and /dev/null differ diff --git a/mage8/mage_extraire_gra b/mage8/mage_extraire_gra deleted file mode 100755 index 07c594dc62cfe6295ea20645bbfcea3a3ffa5c39..0000000000000000000000000000000000000000 Binary files a/mage8/mage_extraire_gra and /dev/null differ diff --git a/mage8/mailleurTT b/mage8/mailleurTT deleted file mode 100755 index 5ea642d7f6c9c4a483123778461c7b8186ad4895..0000000000000000000000000000000000000000 Binary files a/mage8/mailleurTT and /dev/null differ diff --git a/packages/pamhyr.nsi b/packages/pamhyr.nsi index 7555a6183cf557d5b461aca5889b7435c4eaf956..dd4a80530c467c47f82a0ab17c3819a946119602 100644 --- a/packages/pamhyr.nsi +++ b/packages/pamhyr.nsi @@ -3,6 +3,7 @@ !define LIC_NAME "LICENSE" !define APP_NAME "PAMHYR" +Icon "pamhyr\_internal\View\ui\ressources\icon.ico" Name "Pamhyr2" OutFile "pamhyr-windows.exe" LicenseData "pamhyr\_internal\LICENSE" diff --git a/src/Checker/Mage.py b/src/Checker/Mage.py index e7f8352b6d16ec8b2e003f4040b6ce8e6ca6fca6..acb2ddc8d41774fac45c6babfc8296ef8dfa2fbe 100644 --- a/src/Checker/Mage.py +++ b/src/Checker/Mage.py @@ -206,11 +206,11 @@ class MageGeometryGuideLineChecker(StudyGeometryChecker): def __init__(self, version="mage8"): super(MageGeometryGuideLineChecker, self).__init__() - self._name = _translate("Checker", "Mage geometry guide line checker") + self._name = _translate("Checker", "Mage geometry guideline checker") self._description = _translate( "Checker", - "Check if exists geometry guide line is correctly defined \ -for each reaches of the study" + "Check if exists geometry guidelines are correctly defined \ +for each reach" ) self._solver = version self._modules = Modules.GEOMETRY @@ -241,7 +241,7 @@ for each reaches of the study" for profile in profiles: if not profile.has_standard_named_points(): self._status = STATUS.WARNING - self._summary = "no_standard_guileline_defined" + self._summary = "no_standard_guideline_defined" return False for gl in gls[1:]: diff --git a/src/Checker/Study.py b/src/Checker/Study.py index f0d692629511f5297550e8b0d4b67cb7c2644946..b893a507e8378021d1712fa36db2e275a9a529fc 100644 --- a/src/Checker/Study.py +++ b/src/Checker/Study.py @@ -34,9 +34,9 @@ class StudyNetworkReachChecker(AbstractModelChecker): def __init__(self): super(StudyNetworkReachChecker, self).__init__() - self._name = _translate("Checker", "Study network reach checker") + self._name = _translate("Checker", "Study reach network checker") self._description = _translate( - "Checker", "Check if exists at least one reach for study" + "Checker", "Check if exists at least one reach exists" ) self._modules = Modules.NETWORK @@ -63,7 +63,7 @@ class StudyGeometryChecker(AbstractModelChecker): self._name = _translate("Checker", "Study geometry checker") self._description = _translate( - "Checker", "Check if exists geometry for each reach of study" + "Checker", "Check if the geometry of each reach exists" ) self._modules = Modules.GEOMETRY diff --git a/src/Meshing/Mage.py b/src/Meshing/Mage.py index 1e8f4c2d32f8d6f7a014d484219041881a93b1ea..fb5912925fe4e174222a3949b7c001f9c3f0a3b1 100644 --- a/src/Meshing/Mage.py +++ b/src/Meshing/Mage.py @@ -363,7 +363,7 @@ class MeshingWithMageMailleurTT(AMeshingTool): self.import_m_file(reach, m_file) return reach - def update_kp(self, reach, + def update_rk(self, reach, step: float = 50, limites=[-1, -1], origin=0, @@ -371,7 +371,8 @@ class MeshingWithMageMailleurTT(AMeshingTool): lplan: bool = False, lm: int = 3, linear: bool = False, - origin_value=0.0): + origin_value=0.0, + orientation=0): if reach is None or len(reach.profiles) == 0: return reach @@ -380,8 +381,6 @@ class MeshingWithMageMailleurTT(AMeshingTool): st_file = self.export_reach_to_st(reach, tmp) m_file = st_file.rsplit(".ST", 1)[0] + ".M" - os.sync() - proc = QProcess() proc.setWorkingDirectory(tmp) @@ -400,8 +399,9 @@ class MeshingWithMageMailleurTT(AMeshingTool): f"{str(step)} " + f"{limites[0]} {limites[1]} " + f"{directrices[0]} {directrices[1]} " + - f"{lplan} {lm} {linear} " + - f"{origin} " + f"{orientation} {lm} {linear} " + + f"{origin} " + + f"{origin_value} " ) proc.start( self._exe_path(), @@ -413,7 +413,7 @@ class MeshingWithMageMailleurTT(AMeshingTool): "update_kp", step, limites[0], limites[1], directrices[0], directrices[1], - lplan, lm, linear, origin, origin_value + orientation, lm, linear, origin, origin_value ] ) ) diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index 918af8b38bb7548c4970fb460964ae3e47197423..fd62afe98f2c7069876d939f7b9de86cd248c39b 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -315,6 +315,9 @@ class BoundaryCondition(SQLSubModel): self._data.sort(reverse=_reverse, key=key) self._status.modified() + def index(self, bc): + self._data.index(bc) + def get_i(self, index): return self.data[index] @@ -368,3 +371,15 @@ class BoundaryCondition(SQLSubModel): d = self._data d[index], d[prev] = d[prev], d[index] self._status.modified() + + def reach(self, river): + r = [] + if self._node is not None: + if river is not None: + for edge in river.edges(): + if edge.node1.name == self._node.name: + r.append(edge.reach) + if edge.node2.name == self._node.name: + r.append(edge.reach) + + return r diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index 1a68db0784c6d302e7976cada9843383335d05c7..9f0e0f3ca2c74c3505a3560f15901eab9725860e 100644 --- a/src/Model/Friction/Friction.py +++ b/src/Model/Friction/Friction.py @@ -22,6 +22,8 @@ from tools import trace, timer from Model.Tools.PamhyrDB import SQLSubModel +from numpy import interp + logger = logging.getLogger() @@ -43,8 +45,8 @@ class Friction(SQLSubModel): self._name = name self._edge = None - self._begin_kp = 0.0 - self._end_kp = 0.0 + self._begin_rk = 0.0 + self._end_rk = 0.0 self._begin_strickler = None self._end_strickler = None @@ -54,8 +56,8 @@ class Friction(SQLSubModel): CREATE TABLE friction( id INTEGER NOT NULL PRIMARY KEY, ind INTEGER NOT NULL, - begin_kp REAL NOT NULL, - end_kp REAL NOT NULL, + begin_rk REAL NOT NULL, + end_rk REAL NOT NULL, reach INTEGER NOT NULL, begin_strickler INTEGER NOT NULL, end_strickler INTEGER NOT NULL, @@ -69,6 +71,13 @@ class Friction(SQLSubModel): @classmethod def _db_update(cls, execute, version): + major, minor, release = version.strip().split(".") + if major == minor == "0": + if int(release) < 11: + execute("ALTER TABLE friction " + + "RENAME COLUMN begin_kp TO begin_rk") + execute("ALTER TABLE friction RENAME COLUMN end_kp TO end_rk") + return True @classmethod @@ -80,7 +89,7 @@ class Friction(SQLSubModel): stricklers = data["stricklers"].stricklers table = execute( - "SELECT id, ind, begin_kp, end_kp, " + + "SELECT id, ind, begin_rk, end_rk, " + "begin_strickler, end_strickler " + f"FROM friction WHERE reach = {reach.id}" ) @@ -102,8 +111,8 @@ class Friction(SQLSubModel): # Create friction sec = cls(status=status) sec.edge = reach - sec.begin_kp = row[2] - sec.end_kp = row[3] + sec.begin_rk = row[2] + sec.end_rk = row[3] sec.begin_strickler = bs sec.end_strickler = es @@ -124,11 +133,11 @@ class Friction(SQLSubModel): execute( "INSERT INTO " + - "friction(id, ind, begin_kp, end_kp, " + + "friction(id, ind, begin_rk, end_rk, " + "reach, begin_strickler, end_strickler) " + "VALUES (" + f"{self.id}, {ind}, " + - f"{self._begin_kp}, {self._end_kp}, " + + f"{self._begin_rk}, {self._end_rk}, " + f"{self._edge.id}, " + f"{b_s_id}, {e_s_id}" + ")" @@ -157,10 +166,10 @@ class Friction(SQLSubModel): def edge(self, edge): self._edge = edge if (edge is not None and - self._begin_kp == 0.0 and - self._end_kp == 0.0): - self._begin_kp = self._edge.reach.get_kp_min() - self._end_kp = self._edge.reach.get_kp_max() + self._begin_rk == 0.0 and + self._end_rk == 0.0): + self._begin_rk = self._edge.reach.get_rk_min() + self._end_rk = self._edge.reach.get_rk_max() self._status.modified() def has_edge(self): @@ -176,45 +185,45 @@ class Friction(SQLSubModel): return self.has_edge() and self.has_coefficient() @property - def begin_kp(self): - return self._begin_kp + def begin_rk(self): + return self._begin_rk - @begin_kp.setter - def begin_kp(self, begin_kp): + @begin_rk.setter + def begin_rk(self, begin_rk): if self._edge is None: - self._begin_kp = begin_kp + self._begin_rk = begin_rk else: - _min = self._edge.reach.get_kp_min() - _max = self._edge.reach.get_kp_max() + _min = self._edge.reach.get_rk_min() + _max = self._edge.reach.get_rk_max() - if _min <= begin_kp <= _max: - self._begin_kp = begin_kp + if _min <= begin_rk <= _max: + self._begin_rk = begin_rk self._status.modified() @property - def end_kp(self): - return self._end_kp + def end_rk(self): + return self._end_rk - @end_kp.setter - def end_kp(self, end_kp): + @end_rk.setter + def end_rk(self, end_rk): if self._edge is None: - self._end_kp = end_kp + self._end_rk = end_rk else: - _min = self._edge.reach.get_kp_min() - _max = self._edge.reach.get_kp_max() + _min = self._edge.reach.get_rk_min() + _max = self._edge.reach.get_rk_max() - if _min <= end_kp <= _max: - self._end_kp = end_kp + if _min <= end_rk <= _max: + self._end_rk = end_rk self._status.modified() - def __contains__(self, kp): - return self.contains_kp(kp) + def __contains__(self, rk): + return self.contains_rk(rk) - def contains_kp(self, kp): + def contains_rk(self, rk): return ( - self._begin_kp <= kp <= self._end_kp + self._begin_rk <= rk <= self._end_rk ) @property @@ -234,3 +243,17 @@ class Friction(SQLSubModel): def end_strickler(self, strickler): self._end_strickler = strickler self._status.modified() + + def get_friction(self, rk): + if not self.contains_rk(rk): + return None + minor = interp(rk, + [self.begin_rk, self.end_rk], + [self.begin_strickler.minor, + self.end_strickler.minor]) + medium = interp(rk, + [self.begin_rk, self.end_rk], + [self.begin_strickler.medium, + self.end_strickler.medium]) + + return minor, medium diff --git a/src/Model/Friction/FrictionList.py b/src/Model/Friction/FrictionList.py index 8a7adf5b131e023d8c8f4d3d9fc61a595f352cf2..0158d9a4f86985751128fa5b434f1aa502849d19 100644 --- a/src/Model/Friction/FrictionList.py +++ b/src/Model/Friction/FrictionList.py @@ -44,7 +44,7 @@ class FrictionList(PamhyrModelList): logger.info(f"Update friction TABLE from {version}") cls._db_update_0_0_1(execute, version) - return True + return cls._update_submodel(execute, version) @classmethod def _db_load(cls, execute, data=None): diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py index 17bf5c605f0079b5ce4a78c7b9f4ba3227a3401d..6a4b45ef7f1507512b8c3f5b361526a92b2db3e1 100644 --- a/src/Model/Geometry/Profile.py +++ b/src/Model/Geometry/Profile.py @@ -31,7 +31,7 @@ class Profile(object): _id_cnt = 0 def __init__(self, id: int = -1, num: int = 0, - kp: float = 0.0, name: str = "", + rk: float = 0.0, name: str = "", code1: int = 0, code2: int = 0, _type: str = "", reach=None, status=None): @@ -49,7 +49,7 @@ class Profile(object): self._num = int(num) self._code1 = int(code1) self._code2 = int(code2) - self._kp = float(kp) + self._rk = float(rk) self._name = str(name) self._reach = reach self._sl = None @@ -136,16 +136,16 @@ class Profile(object): return len(self.points) @property - def kp(self): + def rk(self): """ Returns: Kilometer point. """ - return self._kp + return self._rk - @kp.setter - def kp(self, value: float): - self._kp = float(value) + @rk.setter + def rk(self, value: float): + self._rk = float(value) self._status.modified() @property @@ -351,8 +351,17 @@ class Profile(object): def wet_points(self, z): raise NotImplementedMethodeError(self, self.wet_point) + def wet_width(self, z): + raise NotImplementedMethodeError(self, self.wet_width) + def wet_perimeter(self, z): raise NotImplementedMethodeError(self, self.wet_perimeter) def wet_area(self, z): raise NotImplementedMethodeError(self, self.wet_area) + + def wet_radius(self, z): + raise NotImplementedMethodeError(self, self.wet_radius) + + def get_nb_wet_areas(self, z): + raise NotImplementedMethodeError(self, self.get_nb_wet_areas) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index ff5ae5995f7337140e324903477166220ffc08fb..7936342aee3b547e13ea36c927edfd5e5a2877bf 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -41,7 +41,7 @@ class ProfileXYZ(Profile, SQLSubModel): def __init__(self, id: int = -1, name: str = "", - kp: float = 0., + rk: float = 0., reach=None, num=0, nb_point: int = 0, @@ -53,7 +53,7 @@ class ProfileXYZ(Profile, SQLSubModel): num: The number of this profile code1: The interpolation code 1 code2: The interpolation code 2 - kp: Kilometer point + rk: Kilometer point name: The name of profile Returns: @@ -63,7 +63,7 @@ class ProfileXYZ(Profile, SQLSubModel): id=id, num=num, name=name, - kp=kp, + rk=rk, code1=code1, code2=code2, _type="XYZ", reach=reach, @@ -78,7 +78,7 @@ class ProfileXYZ(Profile, SQLSubModel): ind INTEGER NOT NULL, name TEXT, reach INTEGER NOT NULL, - kp REAL NOT NULL, + rk REAL NOT NULL, num INTEGER NOT NULL, code1 INTEGER NOT NULL, code2 INTEGER NOT NULL, @@ -94,7 +94,8 @@ class ProfileXYZ(Profile, SQLSubModel): def _db_update(cls, execute, version): major, minor, release = version.strip().split(".") if major == minor == "0": - if int(release) < 2: + rl = int(release) + if rl < 2: execute( """ ALTER TABLE geometry_profileXYZ @@ -103,6 +104,14 @@ class ProfileXYZ(Profile, SQLSubModel): """ ) + if rl < 11: + execute( + """ + ALTER TABLE geometry_profileXYZ + RENAME COLUMN kp TO rk + """ + ) + return cls._update_submodel(execute, version) @classmethod @@ -112,7 +121,7 @@ class ProfileXYZ(Profile, SQLSubModel): reach = data["reach"] table = execute( - "SELECT id, ind, name, kp, num, code1, code2, sl " + + "SELECT id, ind, name, rk, num, code1, code2, sl " + "FROM geometry_profileXYZ " + f"WHERE reach = {reach.id}" ) @@ -121,7 +130,7 @@ class ProfileXYZ(Profile, SQLSubModel): id = row[0] ind = row[1] name = row[2] - kp = row[3] + rk = row[3] num = row[5] code1 = row[5] code2 = row[6] @@ -129,7 +138,7 @@ class ProfileXYZ(Profile, SQLSubModel): new = cls( id=id, num=num, - name=name, kp=kp, + name=name, rk=rk, code1=code1, code2=code2, reach=reach, status=status @@ -159,10 +168,10 @@ class ProfileXYZ(Profile, SQLSubModel): sql = ( "INSERT OR REPLACE INTO " + "geometry_profileXYZ(id, ind, name, reach, " + - "kp, num, code1, code2, sl) " + + "rk, num, code1, code2, sl) " + "VALUES (" + f"{self.id}, {ind}, '{self._db_format(self._name)}', " + - f"{self.reach.id}, {self.kp}, {self.num}, " + + f"{self.reach.id}, {self.rk}, {self.num}, " + f"{self.code1}, {self.code1}, {sl}" + ")" ) @@ -187,19 +196,19 @@ class ProfileXYZ(Profile, SQLSubModel): try: if len(header) == 0: name = data[0] - kp = data[1] + rk = data[1] reach = data[2] status = data[3] profile = cls( id=-1, name=name, - kp=kp, + rk=rk, reach=reach, status=status ) else: - valid_header = {'name', 'reach', 'kp', 'status'} + valid_header = {'name', 'reach', 'rk', 'status'} d = {} for i, v in enumerate(data): h = header[i].strip().lower().split(' ')[0] @@ -323,7 +332,7 @@ class ProfileXYZ(Profile, SQLSubModel): logger.debug(f"{e}") raise IndexError( f"Invalid point name: {name} " + - f"for profile ({self.id}) kp = {self.kp}" + f"for profile ({self.id}) rk = {self.rk}" ) def has_standard_named_points(self): @@ -392,31 +401,125 @@ class ProfileXYZ(Profile, SQLSubModel): return abs(rg.dist(rd)) + def wet_width(self, z): + start, end = self.get_all_water_limits_ac(z) + + if len(start) == 0: + return 0 + + length = 0.0 + for s, e in zip(start, end): + length += abs(s - e) + return length + def wet_perimeter(self, z): - poly = self.wet_polygon(z) + lines = self.wet_lines(z) - if poly is None: + if lines is None: return 0 - return poly.length + length = 0.0 + for line in lines: + length += line.length + return length def wet_area(self, z): - poly = self.wet_polygon(z) + lines = self.wet_lines(z) + + if lines is None: + return 0 + + area = 0.0 + for line in lines: + if len(line.coords) > 2: + poly = geometry.Polygon(line) + area += poly.area + return area + + def wet_radius(self, z): + p = self.wet_perimeter(z) + a = self.wet_area(z) + + if p == 0: + return 0 + + return a/p + + def wet_line(self, z): + points = self.wet_points(z) + if len(points) < 3: + return None + + zz = map(lambda p: p.z, points) + station = self._get_station(points) + + line = geometry.LineString(list(zip(station, zz))) + return line + + def wet_lines(self, z): + points = self._points + if len(points) < 3: + return None + + lines = [] + + zz = list(map(lambda p: p.z, points)) + station = self._get_station(points) - if poly is None: + line = [] + for i in range(self.number_points-1): + + if zz[i] >= z and zz[i+1] < z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + line.append([y, z]) + + if zz[i] < z: + line.append([station[i], zz[i]]) + + if zz[i] <= z and zz[i+1] >= z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + line.append([y, z]) + if len(line) > 2: + lines.append(geometry.LineString(line)) + line = [] + + if zz[self.number_points-1] < z: + line.append([station[self.number_points-1], z]) + if len(line) > 2: + lines.append(geometry.LineString(line)) + line = [] + + return lines + + def max_water_depth(self, z): + return z - self.z_min() + + def mean_water_depth(self, z): + a = self.wet_area(z) + w = self.wet_width(z) + + if w == 0: return 0 - return poly.area + return a/w def wet_polygon(self, z): points = self.wet_points(z) if len(points) < 3: return None - z = map(lambda p: p.z, points) + zz = map(lambda p: p.z, points) station = self._get_station(points) - poly = geometry.Polygon(list(zip(station, z))) + poly = geometry.Polygon(list(zip(station, zz))) return poly def wet_points(self, z): @@ -428,6 +531,63 @@ class ProfileXYZ(Profile, SQLSubModel): return points + def get_nb_wet_areas(self, z): + + n_zones = 0 + points = self._points + if points[0].z <= z: + n_zones += 1 + + for i in range(self.number_points-1): + if points[i].z > z and points[i+1].z <= z: + n_zones += 1 + + return n_zones + + def get_all_water_limits_ac(self, z): + """ + Determine all water limits for z elevation. + """ + + points = self._points + if len(points) < 3: + return None + + zz = list(map(lambda p: p.z, points)) + station = self._get_station(points) + + start = [] + if points[0].z <= z: + start.append(station[0]) + + for i in range(self.number_points-1): + if zz[i] > z and zz[i+1] <= z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + start.append(y) + + end = [] + if points[-1].z <= z: + end.append(station[-1]) + + for i in reversed(range(self.number_points-1)): + if zz[i] <= z and zz[i+1] > z: + y = np.interp( + z, + [zz[i], zz[i+1]], + [station[i], station[i+1]] + ) + end.append(y) + + if len(start) != len(end): + logger.error(f"ERROR in get_all_water_limits_ac") + return [], [] + + return start, list(reversed(end)) + def get_water_limits(self, z): """ Determine left and right limits of water elevation. @@ -491,8 +651,8 @@ class ProfileXYZ(Profile, SQLSubModel): Returns: Projection of the points of the profile on a plane. """ - if self.nb_points < 3: - return None + if self.nb_points < 2: + return [0.0] else: return self._get_station(self.points) @@ -624,3 +784,9 @@ class ProfileXYZ(Profile, SQLSubModel): self.point(i), self.point(i+1) ) + + 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 diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 812ddc51c243d25b6f83eef0a8db00547b7653c4..f33eb5deee050f5d42177dfc3aa7c24072f9ac40 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -141,10 +141,10 @@ class Reach(SQLSubModel): def profiles(self, profiles): self._profiles = profiles - def get_profiles_from_kp(self, kp): + def get_profiles_from_rk(self, rk): return list( filter( - lambda p: p.kp == kp, self.profiles + lambda p: p.rk == rk, self.profiles ) ) @@ -335,18 +335,18 @@ class Reach(SQLSubModel): ) ) - def get_kp(self): - """List of profiles kp + def get_rk(self): + """List of profiles rk Returns: - List of profiles kp + List of profiles rk """ - return [profile.kp for profile in self.profiles] + return [profile.rk for profile in self.profiles] - def get_kp_complete_profiles(self): + def get_rk_complete_profiles(self): return list( map( - lambda profile: profile.kp, + lambda profile: profile.rk, filter( lambda profile: len(profile) > 0, self.profiles @@ -354,41 +354,41 @@ class Reach(SQLSubModel): ) ) - def get_kp_min(self): - if len(self.get_kp()) > 0: - return min(self.get_kp()) + def get_rk_min(self): + if len(self.get_rk()) > 0: + return min(self.get_rk()) else: return 0.0 - def get_kp_max(self): - if len(self.get_kp()) > 0: - return max(self.get_kp()) + def get_rk_max(self): + if len(self.get_rk()) > 0: + return max(self.get_rk()) else: return 0.0 - def inter_profiles_kp(self): - profiles = sorted(self.profiles, key=lambda p: p.kp) + def inter_profiles_rk(self): + profiles = sorted(self.profiles, key=lambda p: p.rk) first = profiles[0] last = profiles[-1] - res_kp = [first.kp] + res_rk = [first.rk] profiles = iter(profiles) previous = next(profiles) for profile in profiles: - prev = previous.kp - curr = profile.kp + prev = previous.rk + curr = profile.rk - diff = abs(previous.kp - profile.kp) + diff = abs(previous.rk - profile.rk) - new = previous.kp + (diff / 2.0) - res_kp.append(new) + new = previous.rk + (diff / 2.0) + res_rk.append(new) previous = profile - res_kp.append(last.kp) - return res_kp + res_rk.append(last.rk) + return res_rk # Sediment Layers @@ -536,7 +536,7 @@ class Reach(SQLSubModel): def sort(self, is_reversed: bool = False): self.profiles = sorted( self.profiles, - key=lambda profile: profile.kp, + key=lambda profile: profile.rk, reverse=is_reversed ) self._status.modified() @@ -613,7 +613,7 @@ class Reach(SQLSubModel): self.profiles = profiles + self.profiles self._update_profile_numbers() - self._recompute_kp() + self._recompute_rk() return profiles @@ -633,7 +633,7 @@ class Reach(SQLSubModel): try: list_profile, list_header = self.read_file_st(str(file_path_name)) profile_header = ["num", "code1", - "code2", "nb_point", "kp", "name"] + "code2", "nb_point", "rk", "name"] if list_profile and list_header: for ind, profile in enumerate(list_profile): @@ -721,12 +721,12 @@ class Reach(SQLSubModel): c1 = f"{profile.code1:>6}" c2 = f"{profile.code2:>6}" t = f"{len(profile.points):>6}" - kp = f"{profile.kp:>12f}"[0:12] + rk = f"{profile.rk:>12f}"[0:12] pname = profile.name if profile.name == "": pname = f"p{profile.id:>3}".replace(" ", "0") - st.write(f"{num}{c1}{c2}{t} {kp} {pname}\n") + st.write(f"{num}{c1}{c2}{t} {rk} {pname}\n") for point in profile.points: x = f"{point.x:<12.4f}"[0:12] @@ -745,15 +745,15 @@ class Reach(SQLSubModel): last = self.profile(len(self) - 1) z_first = first.z_min() - kp_first = first.kp + rk_first = first.rk z_last = last.z_min() - kp_last = last.kp + rk_last = last.rk return ( (z_last - z_first) / - (kp_last - kp_first) + (rk_last - rk_first) ) def get_incline_mean(self): @@ -764,15 +764,15 @@ class Reach(SQLSubModel): for profile in profiles[1:]: z_first = previous.z_min() - kp_first = previous.kp + rk_first = previous.rk z_last = profile.z_min() - kp_last = profile.kp + rk_last = profile.rk incline_acc += ( (z_last - z_first) / - (kp_last - kp_first) + (rk_last - rk_first) ) previous = profile @@ -787,15 +787,15 @@ class Reach(SQLSubModel): for profile in profiles[1:]: z_first = previous.z_min() - kp_first = previous.kp + rk_first = previous.rk z_last = profile.z_min() - kp_last = profile.kp + rk_last = profile.rk incline_acc += [ (z_last - z_first) / - (kp_last - kp_first) + (rk_last - rk_first) ] previous = profile @@ -811,15 +811,15 @@ class Reach(SQLSubModel): for profile in profiles[1:]: z_first = previous.z_min() - kp_first = previous.kp + rk_first = previous.rk z_last = profile.z_min() - kp_last = profile.kp + rk_last = profile.rk incline_acc += [ (z_last - z_first) / - (kp_last - kp_first) + (rk_last - rk_first) ] previous = profile @@ -847,14 +847,14 @@ class Reach(SQLSubModel): logger_exception(e) return 0 - def _recompute_kp(self, offset=0.0): - self._recompute_kp_no_gl(offset=offset) + def _recompute_rk(self, offset=0.0): + self._recompute_rk_no_gl(offset=offset) - def _recompute_kp_no_gl(self, offset=0.0): + def _recompute_rk_no_gl(self, offset=0.0): profiles = iter(self.profiles) previous = next(profiles) - previous.kp = offset + previous.rk = offset for profile in profiles: prev_points = previous.points @@ -865,5 +865,5 @@ class Reach(SQLSubModel): prev_points[-1].dist_2d(curr_points[-1]) ) / 2.0 - profile.kp = previous.kp + dist + profile.rk = previous.rk + dist previous = profile diff --git a/src/Model/HydraulicStructures/HydraulicStructures.py b/src/Model/HydraulicStructures/HydraulicStructures.py index b71dbda4a3951c5d691df5b927d7e3f34dadfdb5..5bb77af0f3a3846e636470ce778c6b70efe671cc 100644 --- a/src/Model/HydraulicStructures/HydraulicStructures.py +++ b/src/Model/HydraulicStructures/HydraulicStructures.py @@ -50,8 +50,8 @@ class HydraulicStructure(SQLSubModel): self.id = id self._name = name - self._input_kp = None - self._output_kp = None + self._input_rk = None + self._output_rk = None self._input_reach = None self._output_reach = None self._enabled = True @@ -70,8 +70,8 @@ class HydraulicStructure(SQLSubModel): id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, enabled BOOLEAN NOT NULL, - input_kp REAL NOT NULL, - output_kp REAL NOT NULL, + input_rk REAL NOT NULL, + output_rk REAL NOT NULL, input_reach INTEGER, output_reach INTEGER, FOREIGN KEY(input_reach) REFERENCES river_reach(id), @@ -85,9 +85,25 @@ class HydraulicStructure(SQLSubModel): def _db_update(cls, execute, version): major, minor, release = version.strip().split(".") if major == minor == "0": - if int(release) < 6: + rl = int(release) + + if rl < 6: cls._db_create(execute) + if rl < 11: + execute( + """ + ALTER TABLE hydraulic_structures + RENAME COLUMN input_kp TO input_rk + """ + ) + execute( + """ + ALTER TABLE hydraulic_structures + RENAME COLUMN output_kp TO output_rk + """ + ) + return True @classmethod @@ -96,7 +112,7 @@ class HydraulicStructure(SQLSubModel): table = execute( "SELECT id, name, enabled, " + - "input_kp, output_kp, " + + "input_rk, output_rk, " + "input_reach, output_reach " + "FROM hydraulic_structures " ) @@ -107,8 +123,8 @@ class HydraulicStructure(SQLSubModel): hs_id = next(it) name = next(it) enabled = (next(it) == 1) - input_kp = next(it) - output_kp = next(it) + input_rk = next(it) + output_rk = next(it) input_reach_id = next(it) output_reach_id = next(it) @@ -119,8 +135,8 @@ class HydraulicStructure(SQLSubModel): ) hs.enabled = enabled - hs.input_kp = input_kp if input_kp != -1 else None - hs.output_kp = output_kp if output_kp != -1 else None + hs.input_rk = input_rk if input_rk != -1 else None + hs.output_rk = output_rk if output_rk != -1 else None hs.input_reach, hs.output_reach = reduce( lambda acc, n: ( @@ -150,24 +166,24 @@ class HydraulicStructure(SQLSubModel): if self._output_reach is not None: output_reach_id = self._output_reach.id - input_kp = -1 - if self.input_kp is not None: - input_kp = self.input_kp + input_rk = -1 + if self.input_rk is not None: + input_rk = self.input_rk - output_kp = -1 - if self.output_kp is not None: - output_kp = self.output_kp + output_rk = -1 + if self.output_rk is not None: + output_rk = self.output_rk sql = ( "INSERT INTO " + "hydraulic_structures(" + - " id, name, enabled, input_kp, output_kp, " + + " id, name, enabled, input_rk, output_rk, " + " input_reach, output_reach" + ") " + "VALUES (" + f"{self.id}, '{self._db_format(self._name)}', " + f"{self._db_format(self.enabled)}, " + - f"{input_kp}, {output_kp}, " + + f"{input_rk}, {output_rk}, " + f"{input_reach_id}, {output_reach_id}" + ")" ) @@ -200,24 +216,24 @@ class HydraulicStructure(SQLSubModel): self._status.modified() @property - def input_kp(self): - return self._input_kp + def input_rk(self): + return self._input_rk - @input_kp.setter - def input_kp(self, input_kp): - if input_kp is not None: - input_kp = float(input_kp) + @input_rk.setter + def input_rk(self, input_rk): + if input_rk is not None: + input_rk = float(input_rk) - self._input_kp = input_kp + self._input_rk = input_rk self._status.modified() @property - def output_kp(self): - return self._output_kp + def output_rk(self): + return self._output_rk - @output_kp.setter - def output_kp(self, output_kp): - self._output_kp = output_kp + @output_rk.setter + def output_rk(self, output_rk): + self._output_rk = output_rk self._status.modified() @property diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index d77e2498d96dd71c65666c9805c8ed5a033d3ff1..82756070569a3ca624913d26db09dcca19d19a41 100644 --- a/src/Model/InitialConditions/InitialConditions.py +++ b/src/Model/InitialConditions/InitialConditions.py @@ -21,6 +21,7 @@ import logging from copy import copy, deepcopy from tools import trace, timer from functools import reduce +from numpy import interp from Model.Tools.PamhyrDB import SQLSubModel @@ -30,7 +31,7 @@ logger = logging.getLogger() class Data(SQLSubModel): def __init__(self, name: str = "", comment: str = "", reach=None, - kp: float = 0.0, discharge: float = 0.0, + rk: float = 0.0, discharge: float = 0.0, height: float = 0.0, status=None): super(Data, self).__init__() @@ -42,14 +43,14 @@ class Data(SQLSubModel): self._name = name self._comment = comment - self._kp = kp + self._rk = rk self._discharge = discharge self._speed = 0.0 self._elevation = 0.0 self._height = height - if self._kp != 0.0: - self._update_from_kp() + if self._rk != 0.0: + self._update_from_rk() if self._height != 0.0: self._update_from_height() if self._discharge != 0.0: @@ -64,7 +65,7 @@ class Data(SQLSubModel): name TEXT NOT NULL, comment TEXT NOT NULL, reach INTEGER, - kp REAL NOT NULL, + rk REAL NOT NULL, discharge REAL NOT NULL, height REAL NOT NULL, FOREIGN KEY(reach) REFERENCES river_reach(id) @@ -75,13 +76,20 @@ class Data(SQLSubModel): @classmethod def _db_update(cls, execute, version): + major, minor, release = version.strip().split(".") + if major == minor == "0": + if int(release) < 11: + execute( + "ALTER TABLE initial_conditions RENAME COLUMN kp TO rk" + ) + return cls._update_submodel(execute, version) @classmethod def _db_load(cls, execute, data=None): id = data["reach"].id table = execute( - "SELECT ind, name, comment, kp, discharge, height " + + "SELECT ind, name, comment, rk, discharge, height " + "FROM initial_conditions " + f"WHERE reach = {id}" ) @@ -95,7 +103,7 @@ class Data(SQLSubModel): ind = row[0] name = row[1] comment = row[2] - kp = row[3] + rk = row[3] discharge = row[4] height = row[5] @@ -104,7 +112,7 @@ class Data(SQLSubModel): status=data["status"], name=name, comment=comment, - kp=kp, + rk=rk, discharge=discharge, height=height, ) @@ -118,12 +126,12 @@ class Data(SQLSubModel): execute( "INSERT INTO " + - "initial_conditions(ind, name, comment, kp, " + + "initial_conditions(ind, name, comment, rk, " + "discharge, height, reach) " + "VALUES (" + f"{ind}, '{self._db_format(self.name)}', " + f"'{self._db_format(self._comment)}', " + - f"{self._kp}, {self._discharge}, {self._height}, " + + f"{self._rk}, {self._discharge}, {self._height}, " + f"{self._reach.id}" + ")" ) @@ -134,13 +142,12 @@ class Data(SQLSubModel): new = Data( name=self.name, comment=self._comment, - kp=self._kp, + rk=self._rk, discharge=self._discharge, height=self._height, reach=self._reach, status=self._status, ) - return new @property @@ -154,8 +161,8 @@ class Data(SQLSubModel): val = self._name elif key == "comment": val = self._comment - elif key == "kp": - val = self._kp + elif key == "rk": + val = self._rk elif key == "speed": val = self._speed elif key == "discharge": @@ -168,7 +175,7 @@ class Data(SQLSubModel): return val def _update_get_min(self): - profile = self._reach.reach.get_profiles_from_kp(self._kp) + profile = self._reach.reach.get_profiles_from_rk(self._rk) if len(profile) > 0: min = profile[0].z_min() else: @@ -176,7 +183,7 @@ class Data(SQLSubModel): return min - def _update_from_kp(self): + def _update_from_rk(self): min = self._update_get_min() self._elevation = min + self._height @@ -197,9 +204,9 @@ class Data(SQLSubModel): self._name = str(value) elif key == "comment": self._comment = str(value) - elif key == "kp": - self._kp = float(value) - self._update_from_kp() + elif key == "rk": + self._rk = float(value) + self._update_from_rk() elif key == "speed": # Not supposed to be modified self._speed = float(value) @@ -298,10 +305,10 @@ class InitialConditions(SQLSubModel): self._data.insert(index, n) self._status.modified() - def new_from_data(self, kp, discharge, elevation): + def new_from_data(self, rk, discharge, elevation): n = Data(reach=self._reach, status=self._status) - n['kp'] = kp + n['rk'] = rk n['discharge'] = discharge n['elevation'] = elevation @@ -344,8 +351,8 @@ class InitialConditions(SQLSubModel): ) ) - def get_kp(self): - return self._data_get("kp") + def get_rk(self): + return self._data_get("rk") def get_elevation(self): return self._data_get("elevation") @@ -353,10 +360,10 @@ class InitialConditions(SQLSubModel): def get_discharge(self): return self._data_get("discharge") - def _sort_by_z_and_kp(self, profiles): + def _sort_by_z_and_rk(self, profiles): profiles.sort( reverse=False, - key=lambda p: p.kp + key=lambda p: p.rk ) first_z = profiles[0].z() @@ -365,87 +372,119 @@ class InitialConditions(SQLSubModel): if first_z > last_z: profiles.sort( reverse=True, - key=lambda p: p.kp + key=lambda p: p.rk ) - def generate_growing_constante_height(self, height: float): - self._data = [] + def generate_growing_constant_depth(self, height: float, + compute_discharge: bool): profiles = self._reach.reach.profiles.copy() - self._sort_by_z_and_kp(profiles) + self._sort_by_z_and_rk(profiles) + + previous_elevation = -99999.99 + + data_discharge = {} + if not compute_discharge: + if len(self._data) == 0: + for profile in profiles: + data_discharge[profile.rk] = 0.0 + else: + for data in self._data: + data_discharge[data["rk"]] = data["discharge"] incline = self._reach.reach.get_incline_median_mean() logger.debug(f"incline = {incline}") - - previous_elevation = -99999.99 + self._data = [] for profile in profiles: - width = profile.width_approximation() - strickler = 25 - discharge = ( - ((width * 0.8) - * strickler - * (height ** (5/3)) - * (abs(incline) ** (0.5))) - ) + width = profile.wet_width(profile.z_min() + height) + frictions = self._reach.frictions.frictions + strickler = None + for f in frictions: + if f.contains_rk(profile.rk): + strickler = f.get_friction(profile.rk)[0] + if strickler is None: + strickler = 25.0 + + if not compute_discharge: + discharge = data_discharge[profile.rk] + else: + discharge = ( + ((width * 0.8) + * strickler + * (height ** (5/3)) + * (abs(incline) ** (0.5))) + ) elevation = max( profile.z_min() + height, previous_elevation ) - logger.debug(f"({profile.kp}):") + logger.debug(f"({profile.rk}):") logger.debug(f" width = {width}") logger.debug(f" strickler = {strickler}") logger.debug(f" discharge = {discharge}") new = Data(reach=self._reach, status=self._status) - new["kp"] = profile.kp + new["rk"] = profile.rk new["discharge"] = discharge - new["elevation"] = elevation - self._data.append(new) previous_elevation = elevation + self._data.append(new) self._generate_resort_data(profiles) - def generate_discharge(self, discharge: float): - self._data = [] - - self._generate_height_estimation_from_discharge( - discharge - ) + def generate_discharge(self, discharge: float, compute_height: bool): - def _generate_height_estimation_from_discharge(self, discharge: float): profiles = self._reach.reach.profiles.copy() - self._sort_by_z_and_kp(profiles) + self._sort_by_z_and_rk(profiles) previous_elevation = -99999.99 + data_height = {} + if not compute_height: + if len(self._data) == 0: + for profile in profiles: + data_height[profile.rk] = 0.0 + else: + for data in self._data: + data_height[data["rk"]] = data["height"] + incline = self._reach.reach.get_incline_median_mean() logger.debug(f"incline = {incline}") - + self._data = [] for profile in profiles: width = profile.width_approximation() - strickler = 25 - height = ( - discharge - / - ((width * 0.8) * strickler * (abs(incline) ** (0.5))) - ) ** (0.6) + frictions = self._reach.frictions.frictions + strickler = None + for f in frictions: + if f.contains_rk(profile.rk): + strickler = f.get_friction(profile.rk)[0] + if strickler is None: + strickler = 25.0 + + if not compute_height: + height = data_height[profile.rk] + else: + height = ( + discharge + / + ((width * 0.8) * strickler * (abs(incline) ** (0.5))) + ) ** (0.6) elevation = max( profile.z_min() + height, previous_elevation ) - logger.debug(f"({profile.kp}):") + logger.debug(f"({profile.rk}):") logger.debug(f" width = {width}") logger.debug(f" strickler = {strickler}") logger.debug(f" height = {height}") new = Data(reach=self._reach, status=self._status) - new["kp"] = profile.kp + new["rk"] = profile.rk new["discharge"] = discharge new["elevation"] = elevation @@ -454,12 +493,45 @@ class InitialConditions(SQLSubModel): self._generate_resort_data(profiles) + def generate_height(self, + elevation1: float, + elevation2: float, + compute_discharge: bool, + discharge: float): + profiles = self._reach.reach.profiles.copy() + upstream_rk = profiles[0].rk + downstream_rk = profiles[-1].rk + data_discharge = {} + if not compute_discharge: + if len(self._data) == 0: + for profile in profiles: + data_discharge[profile.rk] = 0.0 + else: + for data in self._data: + data_discharge[data["rk"]] = data["discharge"] + + self._data = [] + for profile in profiles: + + if not compute_discharge: + d = data_discharge[profile.rk] + else: + d = discharge + elevation = interp(profile.rk, + [upstream_rk, downstream_rk], + [elevation1, elevation2]) + new = Data(reach=self._reach, status=self._status) + new["rk"] = profile.rk + new["discharge"] = d + new["elevation"] = elevation + self._data.append(new) + def _generate_resort_data(self, profiles): is_reverse = False - if profiles[0].kp > profiles[-1].kp: + if profiles[0].rk > profiles[-1].rk: is_reverse = True self._data.sort( reverse=not is_reverse, - key=lambda d: d['kp'] + key=lambda d: d['rk'] ) diff --git a/src/Model/LateralContribution/LateralContribution.py b/src/Model/LateralContribution/LateralContribution.py index 808479e02ffca2f38ff4eaaad005378490e987c8..bfed1d14ca97567049a2fb698803e690df288a35 100644 --- a/src/Model/LateralContribution/LateralContribution.py +++ b/src/Model/LateralContribution/LateralContribution.py @@ -48,8 +48,8 @@ class LateralContribution(SQLSubModel): self._name = name self._type = "" self._edge = None - self._begin_kp = 0.0 - self._end_kp = 0.0 + self._begin_rk = 0.0 + self._end_rk = 0.0 self._data = [] self._header = [] self._types = [float, float] @@ -66,8 +66,8 @@ class LateralContribution(SQLSubModel): type TEXT NOT NULL, tab TEXT NOT NULL, edge INTEGER, - begin_kp REAL NOT NULL, - end_kp REAL NOT NULL, + begin_rk REAL NOT NULL, + end_rk REAL NOT NULL, FOREIGN KEY(edge) REFERENCES river_reach(id) ) """) @@ -87,6 +87,22 @@ class LateralContribution(SQLSubModel): @classmethod def _db_update(cls, execute, version): + major, minor, release = version.strip().split(".") + if major == minor == "0": + if int(release) < 11: + execute( + """ + ALTER TABLE lateral_contribution + RENAME COLUMN begin_kp TO begin_rk + """ + ) + execute( + """ + ALTER TABLE lateral_contribution + RENAME COLUMN end_kp TO end_rk + """ + ) + return True @classmethod @@ -110,7 +126,7 @@ class LateralContribution(SQLSubModel): tab = data["tab"] table = execute( - "SELECT id, name, type, edge, begin_kp, end_kp " + + "SELECT id, name, type, edge, begin_rk, end_rk " + f"FROM lateral_contribution WHERE tab = '{tab}'" ) @@ -125,8 +141,8 @@ class LateralContribution(SQLSubModel): lc.edge = None if row[3] != -1: lc.edge = next(filter(lambda e: e.id == row[3], data["edges"])) - lc._begin_kp = row[4] - lc._end_kp = row[5] + lc._begin_rk = row[4] + lc._end_rk = row[5] values = execute( "SELECT ind, data0, data1 FROM lateral_contribution_data " + @@ -160,11 +176,11 @@ class LateralContribution(SQLSubModel): sql = ( "INSERT INTO " + "lateral_contribution(id, name, type, tab, " + - "edge, begin_kp, end_kp) " + + "edge, begin_rk, end_rk) " + "VALUES (" + f"{self.id}, '{self._db_format(self._name)}', " + f"'{self._db_format(self._type)}', '{tab}', {edge}, " + - f"{self._begin_kp}, {self._end_kp}" + + f"{self._begin_rk}, {self._end_rk}" + ")" ) execute(sql) @@ -231,44 +247,44 @@ class LateralContribution(SQLSubModel): def edge(self, edge): self._edge = edge if edge is not None: - self._begin_kp = self._edge.reach.get_kp_min() - self._end_kp = self._edge.reach.get_kp_max() + self._begin_rk = self._edge.reach.get_rk_min() + self._end_rk = self._edge.reach.get_rk_max() self._status.modified() def has_edge(self): return self._edge is not None @property - def begin_kp(self): - return self._begin_kp + def begin_rk(self): + return self._begin_rk - @begin_kp.setter - def begin_kp(self, begin_kp): + @begin_rk.setter + def begin_rk(self, begin_rk): if self._edge is None: - self._begin_kp = begin_kp + self._begin_rk = begin_rk else: - _min = self._edge.reach.get_kp_min() - _max = self._edge.reach.get_kp_max() + _min = self._edge.reach.get_rk_min() + _max = self._edge.reach.get_rk_max() - if _min <= begin_kp <= _max: - self._begin_kp = begin_kp + if _min <= begin_rk <= _max: + self._begin_rk = begin_rk self._status.modified() @property - def end_kp(self): - return self._end_kp + def end_rk(self): + return self._end_rk - @end_kp.setter - def end_kp(self, end_kp): + @end_rk.setter + def end_rk(self, end_rk): if self._edge is None: - self._end_kp = end_kp + self._end_rk = end_rk else: - _min = self._edge.reach.get_kp_min() - _max = self._edge.reach.get_kp_max() + _min = self._edge.reach.get_rk_min() + _max = self._edge.reach.get_rk_max() - if _min <= end_kp <= _max: - self._end_kp = end_kp + if _min <= end_rk <= _max: + self._end_rk = end_rk self._status.modified() @@ -377,8 +393,8 @@ class LateralContribution(SQLSubModel): def convert(self, cls): new = cls(name=self.name, status=self._status) new.edge = self.edge - new.begin_kp = self.begin_kp - new.end_kp = self.end_kp + new.begin_rk = self.begin_rk + new.end_rk = self.end_rk for i, _ in enumerate(self.data): new.add(i) diff --git a/src/Model/Results/River/River.py b/src/Model/Results/River/River.py index 4505b145a4ed5a9b51cf8b83a5cd3abe5b27bd0c..c37479e658fad824c1aad66dce8da0642447ce26 100644 --- a/src/Model/Results/River/River.py +++ b/src/Model/Results/River/River.py @@ -35,8 +35,8 @@ class Profile(object): return self._profile.name @property - def kp(self): - return self._profile.kp + def rk(self): + return self._profile.rk @property def geometry(self): diff --git a/src/Model/Saved.py b/src/Model/Saved.py index 90667a92236aa266e9cc8ddf8209b4fe22542f13..6b180b407f5fbcb74016ae959c9abcecefb8b9fb 100644 --- a/src/Model/Saved.py +++ b/src/Model/Saved.py @@ -22,10 +22,19 @@ logger = logging.getLogger() class SavedStatus(object): - def __init__(self): + def __init__(self, version=0): super(SavedStatus, self).__init__() + self._version = version self._saved = True + @property + def version(self): + return self._version + + @version.setter + def version(self, version): + self._version = version + def is_saved(self): return self._saved @@ -34,5 +43,11 @@ class SavedStatus(object): self._saved = True def modified(self): - # logger.debug("model status set as modified") + if self._saved: + self._version += 1 + + logger.debug( + "STATUS: Model status set as modified " + + f"at version {self._version}" + ) self._saved = False diff --git a/src/Model/Study.py b/src/Model/Study.py index 1efdce00ddcc314090cdddf8167b502fd9b06576..f4448be3a6de49e222c6d7f7d30c5a3b408d9291 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.9" + self._version = "0.0.11" self.creation_date = datetime.now() self.last_modification_date = datetime.now() self.last_save_date = datetime.now() @@ -117,6 +117,9 @@ class Study(SQLModel): if ".backup" in self.filename: is_new = True + if not os.path.exists(self.filename): + is_new = True + self._init_db_file(self.filename, is_new=is_new) self.commit() @@ -209,7 +212,12 @@ class Study(SQLModel): def _create(self): # Info (metadata) self.execute( - "CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)") + "INSERT INTO info VALUES ('study_release', '0')" + ) + + self.execute( + "CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)" + ) self.execute( "INSERT INTO info VALUES ('version', " + f"'{self._db_format(self._version)}')", @@ -218,19 +226,20 @@ class Study(SQLModel): self.execute("INSERT INTO info VALUES ('name', '')") self.execute("INSERT INTO info VALUES ('description', '')") self.execute( - f"INSERT INTO info VALUES ('time_system', '{self._time_system}')") + f"INSERT INTO info VALUES ('time_system', '{self._time_system}')" + ) self.execute( - f"INSERT INTO info VALUES ('date', " + - "'{self._date.timestamp()}')" + "INSERT INTO info VALUES ('date', " + + f"'{timestamp(self._date)}')" ) self.execute( - f"INSERT INTO info VALUES ('creation_date', " + - "'{self.creation_time.timestamp()}')" + "INSERT INTO info VALUES ('creation_date', " + + f"'{timestamp(self.creation_date)}')" ) self.execute( - f"INSERT INTO info VALUES ('last_save_date', " + - "'{self.last_save_time.timestamp()}')" + "INSERT INTO info VALUES ('last_save_date', " + + f"'{timestamp(self.last_save_date)}')" ) self._create_submodel() @@ -245,6 +254,12 @@ class Study(SQLModel): logger.debug("Update database") + major, minor, release = version[0].split('.') + if major == "0" and minor == "0" and int(release) < 10: + self.execute( + "INSERT INTO info VALUES ('study_release', '0')" + ) + if self._update_submodel(version[0]): self.execute( f"UPDATE info SET value='{self._version}' WHERE key='version'" @@ -258,6 +273,11 @@ class Study(SQLModel): def _load(cls, filename): new = cls(init_new=False, filename=filename) + version = new.execute( + "SELECT value FROM info WHERE key='study_release'" + ) + new.status.version = int(version[0]) + # TODO: Load metadata new.name = new.execute("SELECT value FROM info WHERE key='name'")[0] new.description = new.execute( @@ -292,6 +312,11 @@ class Study(SQLModel): def _save(self, progress=None): progress = progress if progress is not None else lambda: None + self.execute( + "INSERT INTO info VALUES ('study_release', " + + f"'{self.status.version}')" + ) + self.execute( f"UPDATE info SET " + f"value='{self._db_format(self.name)}' WHERE key='name'" diff --git a/src/Model/Tools/PamhyrDict.py b/src/Model/Tools/PamhyrDict.py index 93e563eb8384efade5f928b38b4e5bbac32f9a7c..30ef98e99d3fcb3234fe934b6525901f3671da68 100644 --- a/src/Model/Tools/PamhyrDict.py +++ b/src/Model/Tools/PamhyrDict.py @@ -67,7 +67,7 @@ class PamhyrModelDict(SQLSubModel): if key in self._dict: v = self._dict[key] - if type(v) is types.GeneratorType: + if isinstance(v, types.GeneratorType): return list(v) return v diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py index ec83660e44ff8854fe7c13afe0302dfd65c600af..fe0823d528843bf379e5770551c517cf0827c029 100644 --- a/src/Solver/Mage.py +++ b/src/Solver/Mage.py @@ -180,7 +180,7 @@ class Mage(CommandLineSolver): c1 = f"{profile.code1:>6}" c2 = f"{profile.code2:>6}" t = f"{len(profile.points):>6}" - kp = f"{profile.kp:>12f}"[0:12] + rk = f"{profile.rk:>12f}"[0:12] pname = profile.name if profile.name == "": # Generate name from profile id prefixed with @@ -207,7 +207,7 @@ class Mage(CommandLineSolver): ) # Profile header line - wfile.write(f"{num}{c1}{c2}{t} {kp} {pname} {sediment}\n") + wfile.write(f"{num}{c1}{c2}{t} {rk} {pname} {sediment}\n") def _export_ST_point_line(self, wfile, files, point): x = f"{point.x:<12.4f}"[0:12] @@ -264,7 +264,7 @@ class Mage(CommandLineSolver): if t in ["HYD", "QSO", "LIM"]: v0 /= 60 # Convert first column to minute - f.write(f"{v0:10}{v1:10}\n") + f.write(f"{v0:9} {v1:9} \n") return files @@ -339,7 +339,7 @@ class Mage(CommandLineSolver): f.write(f"* {lateral.edge.name} ({name}) {lateral.lctype}\n") f.write( f"${name} " + - f"{lateral.begin_kp:>10.4f} {lateral.end_kp:>10.4f}\n" + f"{lateral.begin_rk:>10.4f} {lateral.end_rk:>10.4f}\n" ) header = lateral.header f.write(f"*{header[0]:>9}|{header[1]:>10}\n") @@ -371,17 +371,17 @@ class Mage(CommandLineSolver): continue num = f"{id:>3}" - bkp = f"{friction.begin_kp:>10.3f}" - ekp = f"{friction.end_kp:>10.3f}" + brk = f"{friction.begin_rk:>10.3f}" + erk = f"{friction.end_rk:>10.3f}" - # if friction.begin_kp != friction.end_kp: + # if friction.begin_rk != friction.end_rk: # print("TODO") strickler = friction.begin_strickler coef_1 = f"{strickler.minor:>10.3f}" coef_2 = f"{strickler.medium:>10.3f}" - f.write(f"K{num} {bkp}{ekp}{coef_1}{coef_2}\n") + f.write(f"K{num} {brk}{erk}{coef_1}{coef_2}\n") id += 1 @@ -401,7 +401,7 @@ class Mage(CommandLineSolver): # TODO put real date... f.write(f"$ date en minutes : 0.00\n") - f.write(f"* IB IS discharge elevation kp\n") + f.write(f"* IB IS discharge elevation rk\n") id = 1 for reach in reachs: @@ -418,9 +418,9 @@ class Mage(CommandLineSolver): IS = f"{id_sec}" discharge = f"{d['discharge']:>10.5f}" elevation = f"{d['elevation']:>11.6f}" - kp = f"{d['kp']:>9.2f}" + rk = f"{d['rk']:>9.2f}" - f.write(f"{IR} {IS} {discharge} {elevation} {kp}\n") + f.write(f"{IR} {IS} {discharge} {elevation} {rk}\n") id_sec += 1 id += 1 @@ -495,12 +495,12 @@ class Mage(CommandLineSolver): if not hs.enabled: continue - if hs.input_kp is None: + if hs.input_rk is None: continue f.write( '* ouvrage au pk ' + - f"{float(hs.input_kp):>12.1f}" + ' ' + + f"{float(hs.input_rk):>12.1f}" + ' ' + hs.name + '\n' ) @@ -527,7 +527,7 @@ class Mage(CommandLineSolver): f.write( f"{sin_dict[bhs._type]} " + - f"{reach_id} {float(hs.input_kp):>12.3f} " + + f"{reach_id} {float(hs.input_rk):>12.3f} " + f"{param_str} {name}\n" ) diff --git a/src/Solver/RubarBE.py b/src/Solver/RubarBE.py index 43addf3cffe20aba581ebf306fb214ba5499a403..bf946f4242641d7951a0a853b083a812d780abfe 100644 --- a/src/Solver/RubarBE.py +++ b/src/Solver/RubarBE.py @@ -30,13 +30,13 @@ from Model.Results.River.River import River, Reach, Profile logger = logging.getLogger() -class RubarBE(CommandLineSolver): - _type = "rubarbe" +class Rubar3(CommandLineSolver): + _type = "rubar3" def __init__(self, name): - super(RubarBE, self).__init__(name) + super(Rubar3, self).__init__(name) - self._type = "rubarbe" + self._type = "rubar3" self._cmd_input = "" self._cmd_solver = "@path @input -o @output" @@ -44,9 +44,9 @@ class RubarBE(CommandLineSolver): @classmethod def default_parameters(cls): - lst = super(RubarBE, cls).default_parameters() + # lst = super(Rubar3, cls).default_parameters() - lst += [ + lst = [ ("rubarbe_cfl", "0.50000E+00"), ("rubarbe_condam", "1"), ("rubarbe_condav", "3"), @@ -60,10 +60,10 @@ class RubarBE(CommandLineSolver): ("rubarbe_tinit", "000:00:00:00"), ("rubarbe_tmax", "999:99:99:00"), ("rubarbe_tiopdt", "000:00:00:00"), - ("rubarbe_dt", "3000.0"), + ("rubarbe_dt", "5.0"), ("rubarbe_ts", "999:99:99:00"), - ("rubarbe_dtsauv", "999:99:99:00"), - ("rubarbe_psave", "999:99:99:00"), + ("rubarbe_dtsauv", "00:00:00:05"), + ("rubarbe_psave", "00:00:00:05"), ("rubarbe_fdeb1", "1"), ("rubarbe_fdeb2", "10"), ("rubarbe_fdeb3", "100"), @@ -119,21 +119,21 @@ class RubarBE(CommandLineSolver): ########## def cmd_args(self, study): - lst = super(RubarBE, self).cmd_args(study) + lst = super(Rubar3, self).cmd_args(study) return lst def input_param(self): name = self._study.name - return f"{name}.REP" + return f"{name}" def output_param(self): name = self._study.name - return f"{name}.BIN" + return f"{name}" def log_file(self): name = self._study.name - return f"{name}.TRA" + return f"{name}" def export(self, study, repertory, qlog=None): self._study = study @@ -143,8 +143,12 @@ class RubarBE(CommandLineSolver): self._export_ts(study, repertory, qlog, name=name) self._export_geomac_i(study, repertory, qlog, name=name) self._export_mail(study, repertory, qlog, name=name) - self._export_tps(study, repertory, qlog, name=name) + self._export_condin(study, repertory, qlog, name=name) self._export_stricklers(study, repertory, qlog, name=name) + self._export_hydro(study, repertory, qlog, name=name) + self._export_condav(study, repertory, qlog, name=name) + + return True def _export_donnee(self, study, repertory, qlog, name="0"): if qlog is not None: @@ -168,6 +172,9 @@ class RubarBE(CommandLineSolver): name = param.name value = param.value + if "all_" in name: + continue + if value != "": # Value format if value.count(':') == 3: @@ -265,10 +272,10 @@ class RubarBE(CommandLineSolver): ind = 1 for profile in reach.profiles: - kp = profile.kp + rk = profile.rk n_points = len(profile) - f.write(f"{ind:>4} {kp:>11.3f} {n_points:>4}\n") + f.write(f"{ind:>4} {rk:>11.3f} {n_points:>4}\n") for point in profile.points: label = point.name.lower() @@ -276,7 +283,9 @@ class RubarBE(CommandLineSolver): if label[0] == "r": label = label[1].upper() else: - label = lable[0] + label = label[1].upper() + else: + label = " " y = point.y z = point.z @@ -307,8 +316,8 @@ class RubarBE(CommandLineSolver): lm = len(reach) + 1 f.write(f"{lm:>13}\n") - for mails in [reach.inter_profiles_kp(), - reach.get_kp()]: + for mails in [reach.inter_profiles_rk(), + reach.get_rk()]: ind = 0 for mail in mails: f.write(f"{mail:15.3f}") @@ -338,7 +347,7 @@ class RubarBE(CommandLineSolver): lm = len(reach) + 1 f.write(f"{lm:>6}\n") - def get_stricklers_from_kp(kp): + def get_stricklers_from_rk(rk): return next( map( lambda s: ( @@ -346,28 +355,28 @@ class RubarBE(CommandLineSolver): else s.begin_strickler.minor ), filter( - lambda f: kp in f, + lambda f: rk in f, edge.frictions.lst ) ) ) ind = 1 - for mail in edge.reach.inter_profiles_kp(): - coef = get_stricklers_from_kp(mail) + for mail in edge.reach.inter_profiles_rk(): + coef = get_stricklers_from_rk(mail) f.write(f"{ind:>6} {coef:>12.5f}") ind += 1 f.write("\n") - def _export_tps(self, study, repertory, qlog, name="0"): + def _export_condin(self, study, repertory, qlog, name="0"): if qlog is not None: - qlog.put("Export TPS file") + qlog.put("Export CONDIN file") with open( os.path.join( - repertory, f"tps.{name}" + repertory, f"condin.{name}" ), "w+" ) as f: for edge in study.river.enable_edges(): @@ -376,23 +385,23 @@ class RubarBE(CommandLineSolver): f.write(f"0.0\n") ics = study.river.initial_conditions.get(edge) - data = self._export_tps_init_data(ics) + data = self._export_condin_init_data(ics) profiles = reach.profiles first = profiles[0] last = profiles[-1] - if first.kp not in data or last.kp not in data: + if first.rk not in data or last.rk not in data: logger.error( "Study initial condition is not fully defined" ) return - f_h_s = self._export_tps_profile_height_speed(first, data) - l_h_s = self._export_tps_profile_height_speed(last, data) + f_h_s = self._export_condin_profile_height_speed(first, data) + l_h_s = self._export_condin_profile_height_speed(last, data) # First mail - f.write(f"{1:>5} {f_h_s[0]} {f_h_s[1]}") + f.write(f"{1:>5} {f_h_s[0]} {f_h_s[1]}\n") ind = 2 it = iter(profiles) @@ -400,11 +409,11 @@ class RubarBE(CommandLineSolver): prev_h, prev_s = f_h_s for profile in it: - if profile.kp not in data: + if profile.rk not in data: ind += 1 continue - cur_h, cur_s = self._export_tps_profile_height_speed( + cur_h, cur_s = self._export_condin_profile_height_speed( profile, data ) @@ -418,24 +427,83 @@ class RubarBE(CommandLineSolver): ind += 1 # Last mail - f.write(f"{ind:>5} {f_h_s[0]} {f_h_s[1]}") + f.write(f"{ind:>5} {f_h_s[0]} {f_h_s[1]}\n") - def _export_tps_init_data(self, ics): + def _export_condin_init_data(self, ics): data = {} for d in ics.data: - data[d['kp']] = ( + data[d['rk']] = ( d['elevation'], d['discharge'], ) return data - def _export_tps_profile_height_speed(self, profile, data): - z = data[profile.kp][0] - q = data[profile.kp][1] + def _export_condin_profile_height_speed(self, profile, data): + z = data[profile.rk][0] + q = data[profile.rk][1] - height = z - profile.z_min() + # height = z - profile.z_min() speed = profile.speed(q, z) - return height, speed + return z, speed + + def _export_hydro(self, study, repertory, qlog, name="0"): + if qlog is not None: + qlog.put("Export HYDRO file") + + with open( + os.path.join( + repertory, f"hydro.{name}" + ), "w+" + ) as f: + bcs = [] + for edge in study.river.enable_edges(): + for bound in study.river.boundary_condition.get_tab("liquid"): + # BC is an hydrogramme + if bound.bctype == "TD" or bound.bctype == "PC": + # BC is on input node of this reach + if bound.node == edge.node1: + bcs.append(bound) + + for bc in bcs: + f.write(f"{len(bc)}\n") + for d0, d1 in bc.data: + f.write(f"{d0} {d1}\n") + + def _export_condav(self, study, repertory, qlog, name="0"): + if qlog is not None: + qlog.put("Export CONDAV file") + + with open( + os.path.join( + repertory, f"condav.{name}" + ), "w+" + ) as f: + bcs = [] + for edge in study.river.enable_edges(): + for bound in study.river.boundary_condition.get_tab("liquid"): + # BC is an hydrogramme + if bound.bctype == "ZD" or bound.bctype == "TZ": + # BC is on input node of this reach + if bound.node == edge.node2: + bcs.append(bound) + + for bc in bcs: + f.write(f"{len(bc)}\n") + for d0, d1 in bc.data: + f.write(f"{d0} {d1}\n") + + +class RubarBE(Rubar3): + _type = "rubarbe" + + def __init__(self, name): + super(RubarBE, self).__init__(name) + + self._type = "rubarbe" + + self._cmd_input = "" + self._cmd_solver = "@path @input -o @output" + self._cmd_output = "" diff --git a/src/Solver/Solvers.py b/src/Solver/Solvers.py index 699e2f3bee6fd39ceb80e5b490cf8c25a825e414..ca92af7baeefdd1306369a04860333567e31a368 100644 --- a/src/Solver/Solvers.py +++ b/src/Solver/Solvers.py @@ -22,8 +22,8 @@ from Solver.GenericSolver import GenericSolver from Solver.Mage import ( Mage7, Mage8, MageFake7, ) -from Solver.RubarBE import RubarBE from Solver.AdisTS import AdisTSlc +from Solver.RubarBE import Rubar3, RubarBE _translate = QCoreApplication.translate @@ -32,8 +32,9 @@ solver_long_name = { # "mage7": "Mage v7", "mage8": "Mage v8", # "mage_fake7": "Mage fake v7", - "rubarbe": "RubarBE", "adistslc": "Adis-TS_LC", + # "rubarbe": "RubarBE", + # "rubar3": "Rubar3", } solver_type_list = { @@ -41,6 +42,7 @@ solver_type_list = { # "mage7": Mage7, "mage8": Mage8, # "mage_fake7": MageFake7, - "rubarbe": RubarBE, "adistslc": AdisTSlc, + # "rubarbe": RubarBE, + # "rubar3": Rubar3, } diff --git a/src/View/BoundaryCondition/Edit/GenerateDialog.py b/src/View/BoundaryCondition/Edit/GenerateDialog.py new file mode 100644 index 0000000000000000000000000000000000000000..3b07a4d07d8de5166eb3b743fc3fe52ba188b65f --- /dev/null +++ b/src/View/BoundaryCondition/Edit/GenerateDialog.py @@ -0,0 +1,68 @@ +# GenerateDialog.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 View.Tools.PamhyrWindow import PamhyrDialog + +from PyQt5.QtGui import ( + QKeySequence, +) + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QComboBox, QUndoStack, QShortcut, + QDoubleSpinBox, QCheckBox, QPushButton +) + + +class GenerateDialog(PamhyrDialog): + _pamhyr_ui = "BoundaryConditionsDialogGenerator" + _pamhyr_name = "Boundary Condition Options" + + def __init__(self, + value, + reach, + title="Boundary Condition Options", + trad=None, + parent=None): + super(GenerateDialog, self).__init__( + title=trad[self._pamhyr_name], + options=[], + trad=trad, + parent=parent + ) + + self.value = value + self.find(QDoubleSpinBox, "doubleSpinBox").setValue(self.value) + self.reach = reach + self.find(QPushButton, "EstimateButton").clicked.connect( + self.estimate + ) + + def accept(self): + self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value() + super().accept() + + def reject(self): + self.close() + + def estimate(self): + self.value = abs(self.reach.get_incline_median_mean()) + self.find(QDoubleSpinBox, "doubleSpinBox").setValue(self.value) diff --git a/src/View/BoundaryCondition/Edit/Table.py b/src/View/BoundaryCondition/Edit/Table.py index 287a8fe42608c186849a2ab028dd139b56ce90ac..a7481666bf2d49ab221df704d15ad9643b84de8a 100644 --- a/src/View/BoundaryCondition/Edit/Table.py +++ b/src/View/BoundaryCondition/Edit/Table.py @@ -48,6 +48,7 @@ from Model.BoundaryCondition.BoundaryConditionTypes import ( from View.BoundaryCondition.Edit.UndoCommand import ( SetDataCommand, AddCommand, DelCommand, SortCommand, MoveCommand, PasteCommand, + ReplaceDataCommand, ) _translate = QCoreApplication.translate @@ -99,7 +100,7 @@ class TableModel(PamhyrTableModel): logger.info(e) logger.debug(traceback.format_exc()) - self.dataChanged.emit(index, index) + self.update() return True def add(self, row, parent=QModelIndex()): @@ -112,7 +113,7 @@ class TableModel(PamhyrTableModel): ) self.endInsertRows() - self.layoutChanged.emit() + self.update() def delete(self, rows, parent=QModelIndex()): self.beginRemoveRows(parent, rows[0], rows[-1]) @@ -135,7 +136,7 @@ class TableModel(PamhyrTableModel): ) self.layoutAboutToBeChanged.emit() - self.layoutChanged.emit() + self.update() def move_up(self, row, parent=QModelIndex()): if row <= 0: @@ -152,7 +153,7 @@ class TableModel(PamhyrTableModel): ) self.endMoveRows() - self.layoutChanged.emit() + self.update() def move_down(self, index, parent=QModelIndex()): if row > len(self._data): @@ -169,7 +170,7 @@ class TableModel(PamhyrTableModel): ) self.endMoveRows() - self.layoutChanged.emit() + self.update() def paste(self, row, header, data): if len(data) == 0: @@ -190,4 +191,23 @@ class TableModel(PamhyrTableModel): ) self.layoutAboutToBeChanged.emit() + self.update() + + def auto_sort(self): + self.layoutAboutToBeChanged.emit() + self._data.sort(key=lambda x: x[0]) + self.layoutAboutToBeChanged.emit() + + def update(self): + # self.auto_sort() self.layoutChanged.emit() + + def replace_data(self, data1, data2): + self.layoutAboutToBeChanged.emit() + self._undo.push( + ReplaceDataCommand( + self._data, data1, data2 + ) + ) + self.layoutAboutToBeChanged.emit() + self.update() diff --git a/src/View/BoundaryCondition/Edit/UndoCommand.py b/src/View/BoundaryCondition/Edit/UndoCommand.py index 02e0a48ed0ed63f6a2bf832317b9291cc49cee11..4dcc811b2ab67aecf2cc6db795bf4dd93f25f830 100644 --- a/src/View/BoundaryCondition/Edit/UndoCommand.py +++ b/src/View/BoundaryCondition/Edit/UndoCommand.py @@ -181,3 +181,30 @@ class PasteCommand(QUndoCommand): def redo(self): for bc in self._bcs: self._data.insert(self._row, bc) + + +class ReplaceDataCommand(QUndoCommand): + def __init__(self, data, data1, data2): + QUndoCommand.__init__(self) + self._data = data + self._old_rows = len(data) + self._data1 = data1 + self._data2 = data2 + self._rows = len(data1) + + self._old_bc = [] + for row in range(self._old_rows): + self._old_bc.append((row, self._data.get_i(row))) + self._old_bc.sort() + + def undo(self): + self._data.delete_i(list(range(self._rows))) + for row, el in self._old_bc: + self._data.insert(row, el) + + def redo(self): + self._data.delete_i(list(range(self._old_rows))) + for row in range(self._rows): + self._data.add(row) + self._data._set_i_c_v(row, 0, self._data1[row]) + self._data._set_i_c_v(row, 1, self._data2[row]) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index faf418964eb3dcca530f2c51eafbfd0a893359fd..dba64da5d8a6d5340f770f89d6573f889aabe713 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -20,6 +20,8 @@ import logging from tools import timer, trace +from numpy import sqrt + from View.Tools.PamhyrWindow import PamhyrWindow from View.Tools.PamhyrWidget import PamhyrWidget from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate @@ -48,6 +50,7 @@ from View.BoundaryCondition.Edit.translate import BCETranslate from View.BoundaryCondition.Edit.UndoCommand import SetMetaDataCommand from View.BoundaryCondition.Edit.Table import TableModel from View.BoundaryCondition.Edit.Plot import Plot +from View.BoundaryCondition.Edit.GenerateDialog import GenerateDialog _translate = QCoreApplication.translate @@ -104,6 +107,7 @@ class EditBoundaryConditionWindow(PamhyrWindow): self._data = data trad = BCETranslate() self._long_types = trad.get_dict("long_types") + self._study = study name = trad[self._pamhyr_name] if self._data is not None: @@ -129,6 +133,7 @@ class EditBoundaryConditionWindow(PamhyrWindow): self.setup_plot() self.setup_data() self.setup_connections() + self.setup_dialog() def setup_data(self): self._is_solid = self._data.bctype == "SL" @@ -193,13 +198,35 @@ 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_generate_uniform").triggered.connect( + self.generate_uniform + ) + self.find(QAction, "action_generate_critical").triggered.connect( + self.generate_critical + ) + self.find(QAction, "action_increasing").triggered.connect( + self.make_increasing + ) + if self._data.bctype != "ZD" or not self._data.has_node: + self.find(QAction, "action_generate_uniform").setVisible(False) + self.find(QAction, "action_generate_critical").setVisible(False) + self.find(QAction, "action_increasing").setVisible(False) + else: + self.find(QAction, "action_generate_uniform").setVisible(True) + self.find(QAction, "action_generate_critical").setVisible(True) + self.find(QAction, "action_increasing").setVisible(True) self._table.dataChanged.connect(self.update) + self._table.layoutChanged.connect(self.update) if self._is_solid: self._d50sigma.d50Changed.connect(self.d50_changed) self._d50sigma.sigmaChanged.connect(self.sigma_changed) + def setup_dialog(self): + reach = self._data.reach(self._study.river)[0] + self.slope_value = abs(reach.get_incline_median_mean()) + def d50_changed(self, value): self._undo_stack.push( SetMetaDataCommand( @@ -310,3 +337,71 @@ class EditBoundaryConditionWindow(PamhyrWindow): self._table.redo() self.plot.update() self.widget_update() + + def generate_uniform(self): + if self._data.has_node: + node = self._data.node + if node is None: + return + reach = self._data.reach(self._study.river)[0] + profile = reach.profiles[-1] + dlg = GenerateDialog(self.slope_value, + reach, + trad=self._trad, + parent=self) + if dlg.exec(): + self.slope_value = dlg.value + frictions = reach._parent.frictions.frictions + z_min = profile.z_min() + z_max = profile.z_max() + strickler = None + for f in frictions: + if f.contains_rk(profile.rk): + strickler = f.get_friction(profile.rk)[0] + if strickler is None: + strickler = 25.0 + height = [(i)*(z_max-z_min)/50 for i in range(51)] + q = [((profile.wet_width(z_min + h) * 0.8) * strickler + * (h ** (5/3)) * (abs(self.slope_value) ** (0.5))) + for h in height] + for i in range(len(height)): + height[i] += z_min + self._table.replace_data(height, q) + + return + + def generate_critical(self): + if self._data.has_node: + node = self._data.node + if node is None: + return + reach = self._data.reach(self._study.river)[0] + profile = reach.profiles[-1] + z_min = profile.z_min() + z_max = profile.z_max() + height = [(i)*(z_max-z_min)/50 for i in range(51)] + q = [sqrt(9.81 * (profile.wet_area(z_min + h) ** 3) + / profile.wet_width(z_min + h)) + for h in height] + for i in range(len(height)): + height[i] += z_min + self._table.replace_data(height, q) + return + + def make_increasing(self): + if self._data.has_node: + node = self._data.node + if node is None: + return + if len(self._table._data) < 2: + return + h = [self._data.get_i(0)[0]] + q = [self._data.get_i(0)[1]] + for i in range(len(self._table._data)): + if i == 0: + continue + row = self._data.get_i(i) + if row[1] > q[-1]: + h.append(row[0]) + q.append(row[1]) + self._table.replace_data(h, q) diff --git a/src/View/BoundaryCondition/Edit/translate.py b/src/View/BoundaryCondition/Edit/translate.py index 9d8355505cf0477acafa811fe48d16f33b04f695..006c49df0fc6d97fdf66722fcb825cc48a4af8cf 100644 --- a/src/View/BoundaryCondition/Edit/translate.py +++ b/src/View/BoundaryCondition/Edit/translate.py @@ -32,6 +32,8 @@ class BCETranslate(BCTranslate): self._dict["Edit Boundary Conditions"] = _translate( "BoundaryCondition", "Edit boundary conditions" ) + self._dict["Boundary Condition Options"] = _translate( + "BoundaryCondition", "Boundary Condition Options") self._sub_dict["table_headers"] = { "x": _translate("BoundaryCondition", "X"), diff --git a/src/View/BoundaryCondition/translate.py b/src/View/BoundaryCondition/translate.py index 19244076b8a30640d7c1dea61d61b83c79202e63..02903daebe4116c13425c43392ab44378fc7e8a2 100644 --- a/src/View/BoundaryCondition/translate.py +++ b/src/View/BoundaryCondition/translate.py @@ -48,7 +48,7 @@ class BCTranslate(MainTranslate): self._sub_dict["long_types"] = { "ND": self._dict["not_defined"], - "PC": _translate("BoundaryCondition", "Ponctual contribution"), + "PC": _translate("BoundaryCondition", "Point sources"), "TZ": _translate("BoundaryCondition", "Z(t)"), "TD": _translate("BoundaryCondition", "Q(t)"), "ZD": _translate("BoundaryCondition", "Q(Z)"), diff --git a/src/View/CheckList/Worker.py b/src/View/CheckList/Worker.py index f8246fbac537aed17a7f61ddab5034f1b45ce4b2..58295bee1bc6fa8d6f2e645480e9144fdf556dc3 100644 --- a/src/View/CheckList/Worker.py +++ b/src/View/CheckList/Worker.py @@ -46,7 +46,7 @@ class Worker(QObject): self.signalStatus.emit(checker.name) # Run checker - checker._run(self._study) + checker.run(self._study) self.signalStatus.emit("progress") diff --git a/src/View/Configure/Solver/Window.py b/src/View/Configure/Solver/Window.py index d3551ba4f44dd0a3a5d6631742e10799bd73942d..6293832f292841eb910e1c587a7990b4f01b9d03 100644 --- a/src/View/Configure/Solver/Window.py +++ b/src/View/Configure/Solver/Window.py @@ -74,17 +74,17 @@ class ConfigureSolverWindow(PamhyrDialog): # File button buttons = { "pushButton_input": (lambda: self.file_dialog( - select_file=True, + select_file="ExistingFile", callback=lambda f: self.set_line_edit_text( "lineEdit_input", f[0]) )), "pushButton_solver": (lambda: self.file_dialog( - select_file=True, + select_file="ExistingFile", callback=lambda f: self.set_line_edit_text( "lineEdit_solver", f[0]) )), "pushButton_output": (lambda: self.file_dialog( - select_file=True, + select_file="ExistingFile", callback=lambda f: self.set_line_edit_text( "lineEdit_output", f[0]) )), diff --git a/src/View/Configure/Window.py b/src/View/Configure/Window.py index 60f1dd675852062e14c93ad8c849dc88f56ef7a2..1ed011ca5d276e1bba69c5ec470f6e487cbb8ad5 100644 --- a/src/View/Configure/Window.py +++ b/src/View/Configure/Window.py @@ -157,7 +157,7 @@ class ConfigureWindow(PamhyrDialog): "pushButton_stricklers_sort": self.sort_stricklers, # Others # "pushButton_backup_path": lambda: self.file_dialog( - # select_file=False, + # select_file="Directory", # callback=lambda f: self.set_line_edit_text( # "lineEdit_backup_path", f[0] # ) diff --git a/src/View/Frictions/PlotRKZ.py b/src/View/Frictions/PlotRKZ.py new file mode 100644 index 0000000000000000000000000000000000000000..b00cae8d1883fcb9d1cecf9d5e2771ee31873ab8 --- /dev/null +++ b/src/View/Frictions/PlotRKZ.py @@ -0,0 +1,50 @@ +# PlotRKC.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.HydraulicStructures.PlotRKC import PlotRKC + +_translate = QCoreApplication.translate + + +class PlotRKZ(PlotRKC): + def __init__(self, canvas=None, trad=None, toolbar=None, + river=None, reach=None, profile=None, + parent=None): + super(PlotRKC, self).__init__( + canvas=canvas, + trad=trad, + data=river, + toolbar=toolbar, + parent=parent + ) + + self._current_reach = reach + self._current_profile = profile + + self.label_x = self._trad["unit_rk"] + self.label_y = self._trad["unit_elevation"] + + self._isometric_axis = False + + self._auto_relim_update = True + self._autoscale_update = True + self.parent = parent + + def onpick(self, event): + return diff --git a/src/View/Frictions/PlotStricklers.py b/src/View/Frictions/PlotStricklers.py index c7f0abc3dbe2cbfbb680b8909ca88e99ab1be368..4685781388e73ac2c05d1db269a79e2a07181b75 100644 --- a/src/View/Frictions/PlotStricklers.py +++ b/src/View/Frictions/PlotStricklers.py @@ -41,10 +41,10 @@ class PlotStricklers(PamhyrPlot): parent=parent ) - self.label_x = self._trad["kp"] - self.label_y = self._trad["stricklers"] + self.label_x = self._trad["rk"] + self.label_y = self._trad["strickler_plot"] - self.line_kp_elevation = [None, None] + self.line_rk_elevation = [None, None] self._isometric_axis = False @@ -65,9 +65,9 @@ class PlotStricklers(PamhyrPlot): self._init = True def draw_data(self): - kp = self.data.reach.get_kp() + rk = self.data.reach.get_rk() self.canvas.axes.set_xlim( - left=min(kp), right=max(kp) + left=min(rk), right=max(rk) ) frictions = self.data.frictions @@ -76,14 +76,14 @@ class PlotStricklers(PamhyrPlot): self.draw_frictions(lst, self.color_plot) # HightLight - kp_min, kp_max = (-1, -1) + rk_min, rk_max = (-1, -1) if self._highlight_data is not None: - kp_min, kp_max = self._highlight_data + rk_min, rk_max = self._highlight_data lst = list( filter( - lambda s: (s.begin_kp == kp_min and - s.end_kp == kp_max), + lambda s: (s.begin_rk == rk_min and + s.end_rk == rk_max), frictions.frictions ) ) @@ -96,7 +96,7 @@ class PlotStricklers(PamhyrPlot): lambda f: f.is_full_defined(), frictions ), - key=lambda s: s.begin_kp + key=lambda s: s.begin_rk ) coef = flatten( @@ -106,9 +106,9 @@ class PlotStricklers(PamhyrPlot): ) ) - kp = flatten( + rk = flatten( map( - lambda s: [s.begin_kp, s.end_kp], + lambda s: [s.begin_rk, s.end_rk], lst ) ) @@ -116,13 +116,13 @@ class PlotStricklers(PamhyrPlot): coef_minor = list(map(lambda s: s.minor, coef)) coef_medium = list(map(lambda s: s.medium, coef)) - self.line_kp_elevation[0] = self.canvas.axes.plot( - kp, coef_minor, + self.line_rk_elevation[0] = self.canvas.axes.plot( + rk, coef_minor, color=color, lw=1. ) - self.line_kp_elevation[1] = self.canvas.axes.plot( - kp, coef_medium, + self.line_rk_elevation[1] = self.canvas.axes.plot( + rk, coef_medium, color=color, lw=1. ) diff --git a/src/View/Frictions/Table.py b/src/View/Frictions/Table.py index 6b4dd8ce974ec65475d6c151959b95990943b220..2347a41d5690c60c03143e6198c156b2718193b9 100644 --- a/src/View/Frictions/Table.py +++ b/src/View/Frictions/Table.py @@ -113,10 +113,10 @@ class TableModel(PamhyrTableModel): if self._headers[column] == "name": return self._lst.get(row).name - elif self._headers[column] == "begin_kp": - return self._lst.get(row).begin_kp - elif self._headers[column] == "end_kp": - return self._lst.get(row).end_kp + elif self._headers[column] == "begin_rk": + return self._lst.get(row).begin_rk + elif self._headers[column] == "end_rk": + return self._lst.get(row).end_rk elif self._headers[column] == "begin_strickler": value = self._lst.get(row).begin_strickler if value is None: @@ -144,13 +144,13 @@ class TableModel(PamhyrTableModel): self._lst, row, value ) ) - elif self._headers[column] == "begin_kp": + elif self._headers[column] == "begin_rk": self._undo.push( SetBeginCommand( self._lst, row, value ) ) - elif self._headers[column] == "end_kp": + elif self._headers[column] == "end_rk": self._undo.push( SetEndCommand( self._lst, row, value diff --git a/src/View/Frictions/UndoCommand.py b/src/View/Frictions/UndoCommand.py index ce87575793b9a76b236825ea625889bc4f05d6ab..6aab35d5ed8f49c9d5ca8746c856fb202a4f7c25 100644 --- a/src/View/Frictions/UndoCommand.py +++ b/src/View/Frictions/UndoCommand.py @@ -49,14 +49,14 @@ class SetBeginCommand(QUndoCommand): self._frictions = frictions self._index = index - self._old = self._frictions.get(self._index).begin_kp + self._old = self._frictions.get(self._index).begin_rk self._new = float(new_value) def undo(self): - self._frictions.get(self._index).begin_kp = float(self._old) + self._frictions.get(self._index).begin_rk = float(self._old) def redo(self): - self._frictions.get(self._index).begin_kp = float(self._new) + self._frictions.get(self._index).begin_rk = float(self._new) class SetEndCommand(QUndoCommand): @@ -65,14 +65,14 @@ class SetEndCommand(QUndoCommand): self._frictions = frictions self._index = index - self._old = self._frictions.get(self._index).end_kp + self._old = self._frictions.get(self._index).end_rk self._new = float(new_value) def undo(self): - self._frictions.get(self._index).end_kp = float(self._old) + self._frictions.get(self._index).end_rk = float(self._old) def redo(self): - self._frictions.get(self._index).end_kp = float(self._new) + self._frictions.get(self._index).end_rk = float(self._new) class SetBeginStricklerCommand(QUndoCommand): diff --git a/src/View/Frictions/Window.py b/src/View/Frictions/Window.py index a1922ddf538ea4a5ebb809be5b69a74ac1c4d64e..a7dd9e19888e61d7363bcb69b3d84ab60949c9f3 100644 --- a/src/View/Frictions/Window.py +++ b/src/View/Frictions/Window.py @@ -50,7 +50,7 @@ from View.Frictions.Table import ( ) from View.Tools.Plot.PamhyrCanvas import MplCanvas -from View.Geometry.PlotKPZ import PlotKPZ +from View.Frictions.PlotRKZ import PlotRKZ from View.Frictions.PlotStricklers import PlotStricklers from View.Frictions.translate import FrictionsTranslate @@ -110,7 +110,7 @@ class FrictionsWindow(PamhyrWindow): table_view=table, table_headers=self._trad.get_dict("table_headers"), editable_headers=[ - "name", "begin_kp", "end_kp", + "name", "begin_rk", "end_rk", "begin_strickler", "end_strickler" ], delegates={ @@ -133,11 +133,13 @@ class FrictionsWindow(PamhyrWindow): self.plot_layout = self.find(QVBoxLayout, "verticalLayout") self.plot_layout.addWidget(self.canvas) - self.plot = PlotKPZ( + self.plot = PlotRKZ( canvas=self.canvas, - data=self._reach.reach, + reach=self._reach, + river=self._study.river, trad=self._trad, toolbar=None, + parent=self ) self.plot.draw() @@ -202,7 +204,7 @@ class FrictionsWindow(PamhyrWindow): data = self._reach reach = self._reach.reach sec = self._frictions.get(rows[0]) - highlight = (sec.begin_kp, sec.end_kp) + highlight = (sec.begin_rk, sec.end_rk) self.update_plot(highlight) diff --git a/src/View/Frictions/translate.py b/src/View/Frictions/translate.py index 57b823a7db6e33e453ed5dc1be6c4b44c775d360..5c62ba6c0ccf7fbca6c74b9a6329625643fb2cbd 100644 --- a/src/View/Frictions/translate.py +++ b/src/View/Frictions/translate.py @@ -27,7 +27,10 @@ class FrictionsTranslate(MainTranslate): def __init__(self): super(FrictionsTranslate, self).__init__() - self._dict["kp"] = self._dict["unit_kp"] + self._dict["rk"] = self._dict["unit_rk"] + self._dict["strickler_plot"] = _translate( + "Frictions", "Strickler ($m^{1/3}/s$)" + ) self._dict["stricklers"] = _translate( "Frictions", "Stricklers" ) @@ -39,8 +42,8 @@ class FrictionsTranslate(MainTranslate): self._sub_dict["table_headers"] = { # "name": self._dict["name"], # "edge": self._dict["reach"], - "begin_kp": _translate("Frictions", "Begin kp (m)"), - "end_kp": _translate("Frictions", "End kp (m)"), - "begin_strickler": _translate("Frictions", "Begin strickler"), - "end_strickler": _translate("Frictions", "End strickler"), + "begin_rk": _translate("Frictions", "Start (m)"), + "end_rk": _translate("Frictions", "End (m)"), + "begin_strickler": _translate("Frictions", "Start coefficient"), + "end_strickler": _translate("Frictions", "End coefficient"), } diff --git a/src/View/Geometry/MeshingDialog.py b/src/View/Geometry/MeshingDialog.py index 974aa187a5f944863034479c34f5e8aa03a13bd7..1ee8daf61ac82d279fa15c16ffc80f83e1141873 100644 --- a/src/View/Geometry/MeshingDialog.py +++ b/src/View/Geometry/MeshingDialog.py @@ -76,11 +76,11 @@ class MeshingDialog(PamhyrDialog): def _init_default_values_profiles(self): profiles = self.profiles - self.combobox_add_items("comboBox_begin_kp", profiles) - self.combobox_add_items("comboBox_end_kp", profiles) + self.combobox_add_items("comboBox_begin_rk", profiles) + self.combobox_add_items("comboBox_end_rk", profiles) - self.set_combobox_text("comboBox_begin_kp", profiles[0]) - self.set_combobox_text("comboBox_end_kp", profiles[-1]) + self.set_combobox_text("comboBox_begin_rk", profiles[0]) + self.set_combobox_text("comboBox_end_rk", profiles[-1]) @property def profiles(self): @@ -98,9 +98,9 @@ class MeshingDialog(PamhyrDialog): name = profile.name if name == "": - name = f"{profile.kp}" + name = f"{profile.rk}" else: - name += f" ({profile.kp})" + name += f" ({profile.rk})" return name @@ -125,14 +125,6 @@ class MeshingDialog(PamhyrDialog): def lplan(self): return self._lplan - @property - def origin(self): - return self._origin - - @property - def lm(self): - return int(self._lm) - @property def linear(self): return self._linear @@ -159,21 +151,15 @@ class MeshingDialog(PamhyrDialog): ) self._linear = self.get_radio_button("radioButton_linear") - p1 = self.get_combobox_text("comboBox_begin_kp") - p2 = self.get_combobox_text("comboBox_end_kp") + p1 = self.get_combobox_text("comboBox_begin_rk") + p2 = self.get_combobox_text("comboBox_end_rk") self._begin_cs = self.profiles.index(p1) self._end_cs = self.profiles.index(p2) - origin = self.get_combobox_text("comboBox_origin") - self._origin = self.profiles.index(origin) - self._begin_dir = self.get_combobox_text("comboBox_begin_gl") self._end_dir = self.get_combobox_text("comboBox_end_gl") - r_lm_dict = self._trad.get_dict("r_lm_dict") - self._lm = r_lm_dict[self.get_combobox_text("comboBox_lm")] - super().accept() def reject(self): diff --git a/src/View/Geometry/PlotAC.py b/src/View/Geometry/PlotAC.py index cccffe24b750761066a2e1607bd408c13b5ffb31..ad43fdf3b1d13b469fcd38f04e36b13b9b758670 100644 --- a/src/View/Geometry/PlotAC.py +++ b/src/View/Geometry/PlotAC.py @@ -98,6 +98,7 @@ class PlotAC(PamhyrPlot): label=self.label_next_plot_selected, color=self.color_plot_next, lw=1.6, linestyle='--' ) + self.canvas.axes.set_visible(False) def draw_gl(self): if self._current_data_update: @@ -106,12 +107,16 @@ class PlotAC(PamhyrPlot): profile_id = 0 profile = self.data.profile(profile_id) + if profile is None: + return + station = profile.get_station() elevation = profile.z() gl = profile.names() self.annotation = [] self.complete_gl, self.incomplete_gl = self.data.compute_guidelines() + lcomplete = list(self.complete_gl) lincomplete = list(self.incomplete_gl) @@ -160,6 +165,9 @@ class PlotAC(PamhyrPlot): self.draw() return + if self.data is None: + return + self.update_current() self.update_gl() @@ -171,6 +179,10 @@ class PlotAC(PamhyrPlot): previous_id = profile_id - 1 next_id = profile_id + 1 + self.previous_plot_selected.set_visible(False) + self.plot_selected.set_visible(False) + self.next_plot_selected.set_visible(False) + self.previous_plot_selected.set_data([], []) self.plot_selected.set_data([], []) self.next_plot_selected.set_data([], []) @@ -180,18 +192,23 @@ class PlotAC(PamhyrPlot): self.data.profile(previous_id).get_station(), self.data.profile(previous_id).z() ) + self.previous_plot_selected.set_visible(True) if 0 <= profile_id < self.data.number_profiles: self.plot_selected.set_data( self.data.profile(profile_id).get_station(), self.data.profile(profile_id).z() ) + self.plot_selected.set_visible(True) if 0 <= next_id < self.data.number_profiles: self.next_plot_selected.set_data( self.data.profile(next_id).get_station(), self.data.profile(next_id).z() ) + self.next_plot_selected.set_visible(True) + + self.canvas.axes.set_visible(True) def update_full(self): self.draw() diff --git a/src/View/Geometry/PlotKPZ.py b/src/View/Geometry/PlotKPZ.py deleted file mode 100644 index 1c80af12abae4b103a01569fb08b5e5685eba76d..0000000000000000000000000000000000000000 --- a/src/View/Geometry/PlotKPZ.py +++ /dev/null @@ -1,303 +0,0 @@ -# PlotKPC.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 timer -from View.Tools.PamhyrPlot import PamhyrPlot - -logger = logging.getLogger() - - -class PlotKPZ(PamhyrPlot): - def __init__(self, canvas=None, trad=None, - study=None, data=None, toolbar=None, - parent=None): - self._study = study - - super(PlotKPZ, self).__init__( - canvas=canvas, - trad=trad, - data=data, - toolbar=toolbar, - parent=parent - ) - - self._isometric_axis = False - - self.line_kp_zgl = [] - self.line_kp_zmin = None - self.line_kp_zmin_zmax = None - self.line_kp_zmin_zmax_highlight = None - - self.label_x = self._trad["unit_kp"] - self.label_y = self._trad["unit_height"] - - self.before_plot_selected = None - self.plot_selected = None - self.after_plot_selected = None - - @timer - def draw(self): - self.init_axes() - - if self.data is None: - return - - if len(self.data.profiles) == 0: - return - - profiles_defined = any( - filter( - lambda profile: len(profile) > 0, - self.data.profiles - ) - ) - - if not profiles_defined: - self._init = False - return - - self.draw_z_line() - self.draw_z_line_highlight() - self.draw_current() - self.draw_gl() - self.draw_bottom() - self.draw_profiles_hs(self._data) - - self.idle() - self._init = True - - def draw_z_line(self): - kp = self.data.get_kp_complete_profiles() - z_min = self.data.get_z_min() - z_max = self.data.get_z_max() - - self.line_kp_zmin_zmax = self.canvas.axes.vlines( - x=kp, ymin=z_min, ymax=z_max, - color=self.color_plot, - lw=1. - ) - - def draw_z_line_highlight(self): - if self._highlight_data is not None: - kp = self.data.get_kp_complete_profiles() - z_min = self.data.get_z_min() - z_max = self.data.get_z_max() - - kp_min, kp_max = self._highlight_data - - indexes = list( - map( - lambda x: x[0], - filter( - lambda x: kp_min <= x[1] <= kp_max, - enumerate(kp) - ) - ) - ) - - def indexes_filter(data): return list( - map( - lambda x: x[1], - filter( - lambda x: x[0] in indexes, - enumerate(data) - ) - ) - ) - - ikp = indexes_filter(kp) - imin = indexes_filter(z_min) - imax = indexes_filter(z_max) - - self.line_kp_zmin_zmax_highlight = self.canvas.axes.vlines( - x=ikp, - ymin=imin, ymax=imax, - color=self.color_plot_highlight, - lw=1. - ) - - def draw_current(self): - kp = self.data.get_kp_complete_profiles() - z_min = self.data.get_z_min() - z_max = self.data.get_z_max() - - self.plot_selected, = self.canvas.axes.plot( - (kp[0], kp[0]), - (z_min[0], z_max[0]), - color=self.color_plot_current, lw=1.5 - ) - self.plot_selected.set_visible(False) - - self.before_plot_selected, = self.canvas.axes.plot( - (kp[0], kp[0]), - (z_min[0], z_max[0]), - color=self.color_plot_previous, lw=1.5, linestyle='--' - ) - self.before_plot_selected.set_visible(False) - - self.after_plot_selected, = self.canvas.axes.plot( - (kp[0], kp[0]), - (z_min[0], z_max[0]), - color=self.color_plot_next, lw=1.5, linestyle='--' - ) - self.after_plot_selected.set_visible(False) - - def draw_gl(self): - kp = self.data.get_kp_complete_profiles() - - ind = 0 - self.line_kp_zgl = [] - for z in self.data.get_guidelines_z(): - # Is incomplete guideline? - if len(z) != len(kp): - continue - - self.line_kp_zgl.append( - self.canvas.axes.plot( - kp, z, lw=1., - color=self.colors[ind % len(self.colors)], - linestyle=self.linestyle[ind // len(self.colors)] - ) - ) - ind += 1 - - def draw_bottom(self): - kp = self.data.get_kp_complete_profiles() - z_min = self.data.get_z_min() - - self.line_kp_zmin, = self.canvas.axes.plot( - kp, z_min, - linestyle=":", lw=1.5, - color='lightgrey' - ) - - def draw_profiles_hs(self, reach): - lhs = filter( - lambda hs: hs._input_reach.reach is reach, - self._study.river.hydraulic_structures.lst - ) - - for hs in lhs: - x = hs.input_kp - z_min = reach.get_z_min() - z_max = reach.get_z_max() - - self.canvas.axes.plot( - [x, x], - [min(z_min), max(z_max)], - linestyle="--", - lw=1., - color=self.color_plot_previous, - ) - - self.canvas.axes.annotate( - " > " + hs.name, - (x, max(z_max)), - horizontalalignment='left', - verticalalignment='top', - annotation_clip=True, - fontsize=9, color=self.color_plot_previous, - ) - - @timer - def update(self): - if not self._init: - self.draw() - return - - self.update_gl() - self.update_current() - - self.update_idle() - - def update_current(self): - if self._current_data_update: - ind = self._current_data - before = ind - 1 - after = ind + 1 - - self.before_plot_selected.set_visible(False) - self.plot_selected.set_visible(False) - self.after_plot_selected.set_visible(False) - - if 0 <= before < self.data.number_profiles: - profile = self.data.profile(before) - if len(profile) > 0: - kp_i = profile.kp - z_min_i = profile.z_min() - z_max_i = profile.z_max() - - self.before_plot_selected.set_data( - (kp_i, kp_i), - (z_min_i, z_max_i) - ) - self.before_plot_selected.set_visible(True) - - if 0 <= ind < self.data.number_profiles: - profile = self.data.profile(ind) - if len(profile) > 0: - kp_i = profile.kp - z_min_i = profile.z_min() - z_max_i = profile.z_max() - - self.plot_selected.set_data( - (kp_i, kp_i), - (z_min_i, z_max_i) - ) - self.plot_selected.set_visible(True) - - if 0 <= after < self.data.number_profiles: - profile = self.data.profile(after) - if len(profile) > 0: - kp_i = profile.kp - z_min_i = profile.z_min() - z_max_i = profile.z_max() - - self.after_plot_selected.set_data( - (kp_i, kp_i), - (z_min_i, z_max_i) - ) - self.after_plot_selected.set_visible(True) - - def update_gl(self): - if self._current_data_update: - return - - kp = self.data.get_kp_complete_profiles() - z_min = self.data.get_z_min() - z_max = self.data.get_z_max() - - self.line_kp_zmin.set_data(kp, z_min) - - self.line_kp_zmin_zmax.remove() - self.line_kp_zmin_zmax = self.canvas.axes.vlines( - x=kp, - ymin=z_min, ymax=z_max, - color='r', lw=1. - ) - - z_complete = self.data.get_guidelines_z() - try: - for i in range(len(self.line_kp_zgl)): - self.line_kp_zgl[i][0].set_data( - kp, z_complete[i] - ) - except Exception as e: - logger.warning(f"Failed to update graphic KPZ: {e}") diff --git a/src/View/Geometry/PlotRKZ.py b/src/View/Geometry/PlotRKZ.py new file mode 100644 index 0000000000000000000000000000000000000000..848f85c29617e248e772cbdc17f510290a7d0365 --- /dev/null +++ b/src/View/Geometry/PlotRKZ.py @@ -0,0 +1,372 @@ +# PlotRKC.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 timer +from View.Tools.PamhyrPlot import PamhyrPlot +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import ( + Qt, QItemSelectionModel, + QItemSelection, QItemSelectionRange, +) + +logger = logging.getLogger() + + +class PlotRKZ(PamhyrPlot): + def __init__(self, canvas=None, trad=None, + study=None, data=None, toolbar=None, + table=None, parent=None): + self._study = study + + super(PlotRKZ, self).__init__( + canvas=canvas, + trad=trad, + data=data, + toolbar=toolbar, + table=table, + parent=parent + ) + + self._isometric_axis = False + + self.line_rk_zgl = [] + self.line_rk_zmin = None + self.line_rk_zmin_zmax = None + self.line_rk_zmin_zmax_highlight = None + + self.label_x = self._trad["unit_rk"] + self.label_y = self._trad["unit_height"] + + self.before_plot_selected = None + self.plot_selected = None + self.after_plot_selected = None + self.parent = parent + self._table = table + self._colors = [] + + def onpick(self, event): + if event.mouseevent.inaxes != self.canvas.axes: + return + if event.mouseevent.button.value != 1: + return + + modifiers = QApplication.keyboardModifiers() + if modifiers not in [Qt.ControlModifier, + Qt.NoModifier, + Qt.ShiftModifier]: + return + + ind, point = self._closest_rk(event) + if self.parent._table is None: + return + self.parent._table.blockSignals(True) + if modifiers == Qt.ControlModifier: + rows = list( + set( + (i.row() for i in self.parent.tableView.selectedIndexes()) + ) + ) + if ind in rows: + rows.remove(ind) + else: + rows.append(ind) + self._select_in_table(rows) + elif modifiers == Qt.ShiftModifier: + rows = sorted(list( + set( + (i.row() for i in self.parent.tableView.selectedIndexes()) + ) + )) + if len(rows) > 0: + i1 = min(rows[0], rows[-1], ind) + i2 = max(rows[0], rows[-1], ind) + else: + i1 = ind + i2 = ind + self._select_range_in_table(i1, i2) + else: + self._select_in_table([ind]) + self.parent._table.blockSignals(False) + + return + + def _closest_rk(self, event): + + s = event.artist.get_segments() + x = [i[0, 0] for i in s] + mx = event.mouseevent.xdata + points = enumerate(x) + + def dist_mouse(point): + x = point[1] + d = abs(mx - x) + return d + + closest = min( + points, key=dist_mouse + ) + + return closest + + def _select_in_table(self, ind): + if self._table is None: + return + + self._table.setFocus() + selection = self._table.selectionModel() + index = QItemSelection() + + if len(ind) == 0: + return + + for i in ind: + index.append(QItemSelectionRange(self._table.model().index(i, 0))) + + selection.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + + if len(ind) > 0: + self._table.scrollTo(self._table.model().index(ind[-1], 0)) + + def _select_range_in_table(self, ind1, ind2): + if self._table is not None: + self._table.setFocus() + selection = self._table.selectionModel() + index = QItemSelection(self._table.model().index(ind1, 0), + self._table.model().index(ind2, 0)) + selection.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + self._table.scrollTo(self._table.model().index(ind2, 0)) + + @timer + def draw(self): + self.init_axes() + + if self.data is None: + return + + if len(self.data.profiles) == 0: + return + + profiles_defined = any( + filter( + lambda profile: len(profile) > 0, + self.data.profiles + ) + ) + + if not profiles_defined: + self._init = False + return + + self.draw_z_line() + self.draw_lr() + self.draw_gl() + self.draw_bottom() + self.draw_profiles_hs(self._data) + + self.idle() + self._init = True + + def draw_z_line(self): + rk = self.data.get_rk_complete_profiles() + z_min = self.data.get_z_min() + z_max = self.data.get_z_max() + + self._colors, self._style = self.color_hightlight() + + self.line_rk_zmin_zmax = self.canvas.axes.vlines( + x=rk, ymin=z_min, ymax=z_max, + color=self._colors, + linestyle=self._style, + lw=1., + picker=10, + ) + + def color_hightlight(self): + rows = sorted(list( + set( + (i.row() for i in self.parent.tableView.selectedIndexes()) + ) + )) + colors = [self.color_plot for row in range(len(self._data))] + style = ["-" for row in range(len(self._data))] + if len(rows) > 0: + for row in rows: + colors[row] = self.color_plot_current + if rows[0] > 0: + colors[rows[0]-1] = self.color_plot_previous + style[rows[0]-1] = "--" + if rows[-1] < len(self._data)-1: + colors[rows[-1]+1] = self.color_plot_next + style[rows[-1]+1] = "--" + return colors, style + + def draw_lr(self): + rk = self.data.get_rk_complete_profiles() + lz = [] + rz = [] + + self.line_lr = [] + for z in self.data.get_z(): + lz.append(z[0]) + rz.append(z[-1]) + + line = self.canvas.axes.plot( + rk, lz, + color=self.color_plot_river_bottom, + linestyle="dotted", + lw=1., + ) + self.line_lr.append(line) + + line = self.canvas.axes.plot( + rk, rz, + color=self.color_plot_river_bottom, + linestyle="dotted", + lw=1., + ) + self.line_lr.append(line) + + def draw_gl(self): + rk = self.data.get_rk_complete_profiles() + + ind = 0 + self.line_rk_zgl = [] + for z in self.data.get_guidelines_z(): + # Is incomplete guideline? + if len(z) != len(rk): + continue + + self.line_rk_zgl.append( + self.canvas.axes.plot( + rk, z, lw=1., + color=self.colors[ind % len(self.colors)], + linestyle=self.linestyle[ind // len(self.colors)] + ) + ) + ind += 1 + + def draw_bottom(self): + rk = self.data.get_rk_complete_profiles() + z_min = self.data.get_z_min() + + self.line_rk_zmin, = self.canvas.axes.plot( + rk, z_min, + linestyle=":", lw=1.5, + color='lightgrey' + ) + + def draw_profiles_hs(self, reach): + lhs = filter( + lambda hs: hs._input_reach.reach is reach, + filter( + lambda hs: hs._input_reach is not None, + self._study.river.hydraulic_structures.lst + ) + ) + + for hs in lhs: + x = hs.input_rk + if x is not None: + z_min = reach.get_z_min() + z_max = reach.get_z_max() + + self.canvas.axes.plot( + [x, x], + [min(z_min), max(z_max)], + linestyle="--", + lw=1., + color=self.color_plot_previous, + ) + + self.canvas.axes.annotate( + " > " + hs.name, + (x, max(z_max)), + horizontalalignment='left', + verticalalignment='top', + annotation_clip=True, + fontsize=9, color=self.color_plot_previous, + ) + + @timer + def update(self): + if not self._init: + self.draw() + return + + self.update_lr() + self.update_gl() + self.update_current() + + self.update_idle() + + def update_current(self): + if self._current_data_update: + self._colors, self._style = self.color_hightlight() + self.line_rk_zmin_zmax.set_colors(self._colors) + self.line_rk_zmin_zmax.set_linestyle(self._style) + + def update_gl(self): + if self._current_data_update: + return + + rk = self.data.get_rk_complete_profiles() + z_min = self.data.get_z_min() + z_max = self.data.get_z_max() + + self.line_rk_zmin.set_data(rk, z_min) + +# TODO comprendre à quoi sert ce bout de code +# ========> +# self.line_rk_zmin_zmax.remove() +# self._colors, self._style = self.color_hightlight() +# self.line_rk_zmin_zmax = self.canvas.axes.vlines( +# x=rk, +# ymin=z_min, +# ymax=z_max, +# color=self._colors, +# linestyle = self._style, +# lw=1. +# ) +# <======== + + z_complete = self.data.get_guidelines_z() + try: + for i in range(len(self.line_rk_zgl)): + self.line_rk_zgl[i][0].set_data( + rk, z_complete[i] + ) + except Exception as e: + logger.warning(f"Failed to update graphic RKZ: {e}") + + def update_lr(self): + for line in self.line_lr: + line[0].remove() + + self.draw_lr() diff --git a/src/View/Geometry/PlotXY.py b/src/View/Geometry/PlotXY.py index baa46d26af2e99e8686fa2d8cd292365eb9fa83b..548b7ee048ea7551842527ecefb4b3f9527469c9 100644 --- a/src/View/Geometry/PlotXY.py +++ b/src/View/Geometry/PlotXY.py @@ -18,22 +18,27 @@ from tools import timer, trace from View.Tools.PamhyrPlot import PamhyrPlot +from matplotlib import collections +import numpy as np from PyQt5.QtCore import ( - QCoreApplication + QCoreApplication, Qt, QItemSelectionModel, + QItemSelection, QItemSelectionRange, ) +from PyQt5.QtWidgets import QApplication _translate = QCoreApplication.translate class PlotXY(PamhyrPlot): def __init__(self, canvas=None, trad=None, data=None, toolbar=None, - parent=None): + table=None, parent=None): super(PlotXY, self).__init__( canvas=canvas, trad=trad, data=data, toolbar=toolbar, + table=table, parent=parent ) @@ -46,6 +51,116 @@ class PlotXY(PamhyrPlot): self.before_plot_selected = None self.plot_selected = None self.after_plot_selected = None + self.parent = parent + self.line_xy_collection = None + self._table = table + self._colors = [] + self._style = [] + + def onpick(self, event): + if event.mouseevent.inaxes != self.canvas.axes: + return + if event.mouseevent.button.value != 1: + return + + modifiers = QApplication.keyboardModifiers() + if modifiers not in [Qt.ControlModifier, + Qt.NoModifier, + Qt.ShiftModifier]: + return + + ind, point = self._closest_section(event) + if self._table is None: + return + self._table.blockSignals(True) + if modifiers == Qt.ControlModifier: + rows = list( + set( + (i.row() for i in self.parent.tableView.selectedIndexes()) + ) + ) + if ind in rows: + rows.remove(ind) + self._select_in_table(rows) + else: + self._select_in_table(rows + [ind]) + elif modifiers == Qt.ShiftModifier: + rows = list( + set( + (i.row() for i in self.parent.tableView.selectedIndexes()) + ) + ) + if len(rows) > 0: + i1 = min(rows[0], rows[-1], ind) + i2 = max(rows[0], rows[-1], ind) + else: + i1 = ind + i2 = ind + self._select_range_in_table(i1, i2) + else: + self._select_in_table([ind]) + self._table.blockSignals(False) + + return + + def _closest_section(self, event): + axes = self.canvas.axes + mx = event.mouseevent.xdata + my = event.mouseevent.ydata + bx, by = axes.get_xlim(), axes.get_ylim() + ratio = (bx[0] - bx[1]) / (by[0] - by[1]) + + segments = event.artist.get_segments() + ind = event.ind + + points = [] + for i in ind: + points = points + [[i, j] for j in segments[i]] + + def dist_mouse(point): + x, y = point[1] + d2 = (((mx - x) / ratio) ** 2) + ((my - y) ** 2) + return d2 + + closest = min( + points, key=dist_mouse + ) + + return closest + + def _select_in_table(self, ind): + if self._table is None: + return + self._table.setFocus() + selection = self._table.selectionModel() + index = QItemSelection() + if len(ind) == 0: + return + for i in ind: + index.append(QItemSelectionRange(self._table.model().index(i, 0))) + selection.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + + if len(ind) > 0: + self._table.scrollTo(self._table.model().index(ind[-1], 0)) + + def _select_range_in_table(self, ind1, ind2): + if self._table is not None: + self._table.setFocus() + selection = self._table.selectionModel() + index = QItemSelection(self._table.model().index(ind1, 0), + self._table.model().index(ind2, 0)) + selection.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + self._table.scrollTo(self._table.model().index(ind2, 0)) @timer def draw(self): @@ -63,33 +178,42 @@ class PlotXY(PamhyrPlot): self.draw_xy() self.draw_lr() self.draw_gl() - self.draw_current() self.idle() self._init = True def draw_xy(self): - kp = self.data.get_kp_complete_profiles() - - kp_min, kp_max = (-1, -1) - if self._highlight_data is not None: - kp_min, kp_max = self._highlight_data - - def color_hightlight(kp): - if kp_min <= kp <= kp_max: - return self.color_plot_highlight - return self.color_plot - self.line_xy = [] - for x, y, kp in zip(self.data.get_x(), - self.data.get_y(), - kp): - line = self.canvas.axes.plot( - x, y, - color=color_hightlight(kp), - **self.plot_default_kargs + for xy in zip(self.data.get_x(), self.data.get_y()): + self.line_xy.append(np.column_stack(xy)) + + self._colors, self._style = self.color_hightlight() + self.line_xy_collection = collections.LineCollection( + self.line_xy, + colors=self._colors, + linestyle=self._style, + picker=10 + ) + self.canvas.axes.add_collection(self.line_xy_collection) + + def color_hightlight(self): + rows = sorted(list( + set( + (i.row() for i in self.parent.tableView.selectedIndexes()) ) - self.line_xy.append(line) + )) + colors = [self.color_plot for row in range(len(self._data))] + style = ["-" for row in range(len(self._data))] + if len(rows) > 0: + for row in rows: + colors[row] = self.color_plot_current + if rows[0] > 0: + colors[rows[0]-1] = self.color_plot_previous + style[rows[0]-1] = "--" + if rows[-1] < len(self._data)-1: + colors[rows[-1]+1] = self.color_plot_next + style[rows[-1]+1] = "--" + return colors, style def draw_lr(self): lx = [] @@ -136,33 +260,6 @@ class PlotXY(PamhyrPlot): self.line_gl.append(line) ind += 1 - def draw_current(self): - # Previous profile - self.before_plot_selected, = self.canvas.axes.plot( - self.data.profile(0).x(), - self.data.profile(0).y(), - color=self.color_plot_previous, linestyle="--", - **self.plot_default_kargs - ) - self.before_plot_selected.set_visible(False) - - # Current profile - self.plot_selected, = self.canvas.axes.plot( - self.data.profile(0).x(), - self.data.profile(0).y(), - color=self.color_plot_current, **self.plot_default_kargs - ) - self.plot_selected.set_visible(False) - - # Next profile - self.after_plot_selected, = self.canvas.axes.plot( - self.data.profile(0).x(), - self.data.profile(0).y(), - color=self.color_plot_next, linestyle='--', - **self.plot_default_kargs - ) - self.after_plot_selected.set_visible(False) - @timer def update(self): if not self._init: @@ -183,21 +280,24 @@ class PlotXY(PamhyrPlot): x_complete = list(self.data.get_guidelines_x()) y_complete = list(self.data.get_guidelines_y()) - for i in range(self.data.number_profiles): - if i < len(self.line_xy): - self.line_xy[i][0].set_data( - self.data.profile(i).x(), - self.data.profile(i).y() - ) - else: - self.line_xy.append( - self.canvas.axes.plot( - self.data.profile(i).x(), - self.data.profile(i).y(), - color='r', - **self.plot_default_kargs - ) - ) +# TODO comprendre à quoi sert ce bout de code +# ========> +# for i in range(self.data.number_profiles): +# if i < len(self.line_xy): +# self.line_xy[i][0].set_data( +# self.data.profile(i).x(), +# self.data.profile(i).y() +# ) +# else: +# self.line_xy.append( +# self.canvas.axes.plot( +# self.data.profile(i).x(), +# self.data.profile(i).y(), +# color='r', +# **self.plot_default_kargs +# ) +# ) +# <======== for i in range(len(x_complete)): if i < len(self.line_gl): @@ -215,32 +315,9 @@ class PlotXY(PamhyrPlot): def update_current(self): if self._current_data_update: - ind = self._current_data - before = ind - 1 - after = ind + 1 - - self.before_plot_selected.set_visible(False) - self.plot_selected.set_visible(False) - self.after_plot_selected.set_visible(False) - - if 0 <= before < self.data.number_profiles: - self.before_plot_selected.set_data( - self.data.profile(before).x(), - self.data.profile(before).y() - ) - self.before_plot_selected.set_visible(True) - - if 0 <= ind < self.data.number_profiles: - self.plot_selected.set_data(self.data.profile(ind).x(), - self.data.profile(ind).y()) - self.plot_selected.set_visible(True) - - if 0 <= after < self.data.number_profiles: - self.after_plot_selected.set_data( - self.data.profile(after).x(), - self.data.profile(after).y() - ) - self.after_plot_selected.set_visible(True) + self._colors, self._style = self.color_hightlight() + self.line_xy_collection.set_colors(self._colors) + self.line_xy_collection.set_linestyle(self._style) def update_lr(self): for line in self.line_lr: diff --git a/src/View/Geometry/Profile/Plot.py b/src/View/Geometry/Profile/Plot.py index 5f488592dbf482e3e3170beeb967f1f6049cacbd..0eb1543f150cfb4f190c43c2b98529f90bdf1bf9 100644 --- a/src/View/Geometry/Profile/Plot.py +++ b/src/View/Geometry/Profile/Plot.py @@ -24,10 +24,12 @@ from tools import timer, trace from View.Tools.PamhyrPlot import PamhyrPlot from PyQt5.QtCore import ( - Qt, QCoreApplication, QItemSelectionModel + Qt, QCoreApplication, QItemSelectionModel, + QItemSelection, QItemSelectionRange, ) from PyQt5.QtWidgets import QApplication +from matplotlib.widgets import RectangleSelector _translate = QCoreApplication.translate @@ -46,6 +48,7 @@ class Plot(PamhyrPlot): ) self._table = table + self._parent = parent self._z_note = None self._z_line = None self._z_fill_between = None @@ -53,7 +56,7 @@ class Plot(PamhyrPlot): self.line_xy = [] self.line_gl = [] - self.label_x = self._trad["unit_kp"] + self.label_x = self._trad["unit_rk"] self.label_y = self._trad["unit_height"] self.before_plot_selected = None @@ -68,40 +71,86 @@ class Plot(PamhyrPlot): None # Hydrolic values (z, wet_area, # wet_preimeter, water_width) ) + self._onpickevent = None + self._rect_select = RectangleSelector( + ax=self.canvas.axes, + onselect=self.rect_select_callback, + useblit=True, + button=[1], # don't use middle nor right button + minspanx=2.0, + minspany=2.0, + spancoords='pixels', + interactive=False + ) + + def onrelease(self, event): + # we need to do that to prevent conflicst + # between onpick and rect_select_callback + modifiers = QApplication.keyboardModifiers() + points, hyd = self.highlight + if self._onpickevent is not None: + ind, point = self._closest_point(self._onpickevent) + if modifiers == Qt.ControlModifier: + rows = self._parent.index_selected_rows() + if ind in rows: + rows.remove(ind) + del (points[ind]) + self.highlight = (points, hyd) + self._select_in_table(rows) + else: + self.highlight = (points+[point], hyd) + self._select_in_table(rows+[ind]) + elif modifiers == Qt.ShiftModifier: + rows = self._parent.index_selected_rows() + if len(rows) > 0: + i1 = min(rows[0], rows[-1], ind) + i2 = max(rows[0], rows[-1], ind) + p = [ + [self.data.points[i].x, self.data.points[i].y] + for i in range(i1, i2) + ] + else: + i1 = ind + i2 = ind + p = [point] + self.highlight = (p, hyd) + self._select_range_in_table(i1, i2) + else: + self.highlight = ([point], hyd) + self._select_in_table([ind]) + + self._onpickevent = None def onpick(self, event): if event.mouseevent.inaxes != self.canvas.axes: return + if event.mouseevent.button.value != 1: + return modifiers = QApplication.keyboardModifiers() - if modifiers != Qt.ControlModifier: + if modifiers not in [Qt.ControlModifier, + Qt.NoModifier, + Qt.ShiftModifier]: return - _, hyd = self.highlight - - ind, point = self._closer_point(event) - - self.highlight = ([point], hyd) - self._select_in_table(ind) - - self.update() + self._onpickevent = event return def onclick(self, event): if event.inaxes != self.canvas.axes: return - - modifiers = QApplication.keyboardModifiers() - if modifiers != Qt.ShiftModifier: + if event.button.value == 1: return points, _ = self.highlight z = self._get_z_from_click(event) - if z < self.data.z_min(): + if z < self.data.z_min() or event.button.value == 2: + self.highlight = (points, None) + self.update() return - a, p, w = self._compute_hydrolics(z) + a, p, w = self._compute_hydraulics(z) logger.debug(f"{z, a, p, w}") @@ -110,7 +159,7 @@ class Plot(PamhyrPlot): self.update() return - def select_points_from_indexes(self, indexes): + def select_points_from_indices(self, indices): data = self.data _, hyd = self.highlight @@ -118,7 +167,7 @@ class Plot(PamhyrPlot): map( lambda e: e[1], filter( - lambda e: e[0] in indexes, + lambda e: e[0] in indices, enumerate( zip(data.get_station(), data.z()) ) @@ -133,9 +182,13 @@ class Plot(PamhyrPlot): if self._table is not None: self._table.blockSignals(True) self._table.setFocus() - selection = self._table.selectionModel() - index = self._table.model().index(ind, 0) + index = QItemSelection() + if len(ind) > 0: + for i in ind: + index.append(QItemSelectionRange( + self._table.model().index(i, 0)) + ) selection.select( index, QItemSelectionModel.Rows | @@ -143,10 +196,27 @@ class Plot(PamhyrPlot): QItemSelectionModel.Select ) - self._table.scrollTo(index) + if len(ind) > 0: + self._table.scrollTo(self._table.model().index(ind[-1], 0)) self._table.blockSignals(False) - def _closer_point(self, event): + def _select_range_in_table(self, ind1, ind2): + if self._table is not None: + self._table.blockSignals(True) + self._table.setFocus() + selection = self._table.selectionModel() + index = QItemSelection(self._table.model().index(ind1, 0), + self._table.model().index(ind2, 0)) + selection.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + self._table.scrollTo(self._table.model().index(ind2, 0)) + self._table.blockSignals(False) + + def _closest_point(self, event): points_ind = event.ind axes = self.canvas.axes bx, by = axes.get_xlim(), axes.get_ylim() @@ -166,22 +236,62 @@ class Plot(PamhyrPlot): def dist_mouse(point): x, y = point[1] - d = sqrt( - (((mx - x) / ratio) ** 2) + - ((my - y) ** 2) - ) - return d + d2 = ((mx - x) / ratio) ** 2 + ((my - y) ** 2) + return d2 - closer = min( + closest = min( points, key=dist_mouse ) - return closer + return closest def _get_z_from_click(self, event): return event.ydata - def _compute_hydrolics(self, z): + def rect_select_callback(self, eclick, erelease): + + points, hyd = self.highlight + x1, y1 = eclick.xdata, eclick.ydata + x2, y2 = erelease.xdata, erelease.ydata + + if (max(abs(x1-x2), abs(y1-y2)) < 0.001): + return + modifiers = QApplication.keyboardModifiers() + + x1, y1 = eclick.xdata, eclick.ydata + x2, y2 = erelease.xdata, erelease.ydata + + inds, points2 = self._points_in_rectangle(x1, y1, x2, y2) + self._onclickevent = None + if modifiers == Qt.ControlModifier: + rows = self._parent.index_selected_rows() + if all(i in rows for i in inds): + for ind in sorted(inds, reverse=True): + rows.remove(ind) + del (points[ind]) + self.highlight = (points, hyd) + self._select_in_table(rows) + else: + self.highlight = (points+points2, hyd) + self._select_in_table(rows+inds) + else: + self.highlight = (points2, hyd) + self._select_in_table(inds) + return + + def _points_in_rectangle(self, x1, y1, x2, y2): + # TODO: use lambdas + listi = [] + listp = [] + station = self.data._get_station(self.data.points) + for i, p in enumerate(self.data.points): + if (min(x1, x2) < station[i] < max(x1, x2) + and min(y1, y2) < p.z < max(y1, y2)): + listi.append(i) + listp.append((station[i], p.z)) + return listi, listp + + def _compute_hydraulics(self, z): profile = self.data points = profile.wet_points(z) @@ -203,14 +313,10 @@ class Plot(PamhyrPlot): x_carto = self.data.x() y_carto = self.data.y() - if (len(x_carto) < 3 or len(y_carto) < 3 or len(x) < 3): - # Noting to do in this case - return - self.profile_line2D, = self.canvas.axes.plot( x, y, color=self.color_plot, lw=1.5, markersize=7, marker='+', - picker=30 + picker=10 ) self.draw_annotation(x, y) @@ -269,12 +375,16 @@ class Plot(PamhyrPlot): x, y, color=self.color_plot_highlight, lw=1.5, markersize=7, marker='+', - picker=30 ) ) if hyd is not None: self.draw_highligth_z_line(*hyd) + else: + if self._z_note is not None: + self._z_note.set_visible(False) + self._z_line[0].set_visible(False) + self._z_fill_between.set_visible(False) def draw_highligth_z_line(self, z, a, p, w): text = ( @@ -299,6 +409,7 @@ class Plot(PamhyrPlot): xlim, [z, z], color=self.color_plot_river_water ) + self._z_line[0].set_visible(True) self._z_note = self.canvas.axes.annotate( text, pos, @@ -310,12 +421,15 @@ class Plot(PamhyrPlot): fontweight='bold', alpha=0.7 ) + self._z_note.set_visible(True) else: self.draw_highligth_z_line_fill(x, y, z) self._z_line[0].set_data(xlim, [z, z]) self._z_note.set_position(pos) self._z_note.set_text(text) + self._z_line[0].set_visible(True) + self._z_note.set_visible(True) def draw_highligth_z_line_fill(self, x, y, z): if self._z_fill_between is not None: diff --git a/src/View/Geometry/Profile/Table.py b/src/View/Geometry/Profile/Table.py index 21e202f8ee9fafffe0d4bec3b275355592118f0c..141545fa3983f045ddc844cb17ee0269f15217c3 100644 --- a/src/View/Geometry/Profile/Table.py +++ b/src/View/Geometry/Profile/Table.py @@ -82,16 +82,6 @@ class GeometryProfileTableModel(PamhyrTableModel): elif value == self._data.z_max(): return QColor("blue") - if role == Qt.ToolTipRole: - if value == self._data.z_min(): - return _translate("MainWindowProfile", - "La cote du fond", - "Z minimale") - elif value == self._data.z_max(): - return _translate("MainWindowProfile", - "La cote maximale", - "Z maximale") - if index.column() == 3: value = self._data.point(index.row()).name @@ -104,16 +94,6 @@ class GeometryProfileTableModel(PamhyrTableModel): if role == Qt.ForegroundRole: return QColor("darkRed") - if role == Qt.ToolTipRole: - if value.strip().upper() == "RG": - return _translate( - "MainWindowProfile", "Rive gauche" - ) - else: - return _translate( - "MainWindowProfile", "Rive droite" - ) - return QVariant() def setData(self, index, value, role=Qt.EditRole): @@ -250,11 +230,11 @@ class GeometryProfileTableModel(PamhyrTableModel): self.endMoveRows() self.layoutChanged.emit() - def purge(self): + def purge(self, np_purge): self._undo.push( PurgeCommand( - self._data, 24 + self._data, np_purge ) ) diff --git a/src/View/Geometry/Profile/Window.py b/src/View/Geometry/Profile/Window.py index 655096d7907ba7ed338e00a8eff5efa57351e442..cbd20febe9f00ce93199dcdb8d27ffa879a1ec88 100644 --- a/src/View/Geometry/Profile/Window.py +++ b/src/View/Geometry/Profile/Window.py @@ -20,8 +20,9 @@ import copy import sys import csv from time import time +import logging -from tools import trace, timer +from tools import trace, timer, logger_exception from Modules import Modules from PyQt5.QtGui import ( @@ -46,9 +47,12 @@ from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Geometry.Profile.Plot import Plot from View.Geometry.Profile.Table import GeometryProfileTableModel from View.Geometry.Profile.Translate import GeometryProfileTranslate +from View.Geometry.PurgeDialog import PurgeDialog _translate = QCoreApplication.translate +logger = logging.getLogger() + class ProfileWindow(PamhyrWindow): _pamhyr_ui = "GeometryCrossSection" @@ -60,7 +64,7 @@ class ProfileWindow(PamhyrWindow): trad = GeometryProfileTranslate() name = ( trad[self._pamhyr_name] + - f" - {self._profile.name} {self._profile.kp}" + f" - {self._profile.name} {self._profile.rk}" ) super(ProfileWindow, self).__init__( @@ -111,7 +115,8 @@ class ProfileWindow(PamhyrWindow): data=self._profile, trad=self._trad, toolbar=self._toolbar, - table=self.find(QTableView, "tableView") + table=self.find(QTableView, "tableView"), + parent=self ) self._plot.draw() @@ -142,7 +147,7 @@ class ProfileWindow(PamhyrWindow): def update_points_selection(self): rows = self.index_selected_rows() - self._plot.select_points_from_indexes(rows) + self._plot.select_points_from_indices(rows) def update(self): self.update_plot() @@ -165,7 +170,6 @@ class ProfileWindow(PamhyrWindow): if Modules.GEOMETRY not in key: return - logger.debug("TOTO") self._tablemodel.layoutChanged.emit() self._update(redraw=True, propagate=False) @@ -258,6 +262,19 @@ class ProfileWindow(PamhyrWindow): self._tablemodel.purge() self.update() + def purge(self): + try: + dlg = PurgeDialog( + trad=self._trad, + parent=self + ) + if dlg.exec(): + self._tablemodel.purge(dlg.np_purge) + self._plot.draw() + except Exception as e: + logger_exception(e) + return + def reverse(self): self._tablemodel.reverse() self.update() diff --git a/src/View/Geometry/PurgeDialog.py b/src/View/Geometry/PurgeDialog.py new file mode 100644 index 0000000000000000000000000000000000000000..af4fcc34e0d776fad5a3c45be7e6bf47eaf9c3f6 --- /dev/null +++ b/src/View/Geometry/PurgeDialog.py @@ -0,0 +1,60 @@ +# PurgeDialog.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 View.Tools.PamhyrWindow import PamhyrDialog + +from PyQt5.QtGui import ( + QKeySequence, +) + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, +) + +from PyQt5.QtWidgets import ( + QUndoStack, QShortcut, QSpinBox, +) + + +class PurgeDialog(PamhyrDialog): + _pamhyr_ui = "PurgeOptions" + _pamhyr_name = "Purge" + + def __init__(self, trad=None, parent=None): + super(PurgeDialog, self).__init__( + title=trad[self._pamhyr_name], + trad=trad, + options=[], + parent=parent + ) + + self._init_default_values() + + def _init_default_values(self): + self._np_purge = 24 + + @property + def np_purge(self): + return self._np_purge + + def accept(self): + self._np_purge = self.get_spin_box("spinBox_np_purge") + super().accept() + + def reject(self): + self.close() diff --git a/src/View/Geometry/ShiftDialog.py b/src/View/Geometry/ShiftDialog.py new file mode 100644 index 0000000000000000000000000000000000000000..4953855aef8631d4bea72dcf0155bb8f97b2a460 --- /dev/null +++ b/src/View/Geometry/ShiftDialog.py @@ -0,0 +1,60 @@ +# ShiftDialog.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 View.Tools.PamhyrWindow import PamhyrDialog + + +class ShiftDialog(PamhyrDialog): + _pamhyr_ui = "GeometryReachShift" + _pamhyr_name = "Shift" + + def __init__(self, trad=None, parent=None): + super(ShiftDialog, self).__init__( + title=trad[self._pamhyr_name], + trad=trad, + options=[], + parent=parent + ) + + self._init_default_values() + + def _init_default_values(self): + self._dx = 0.0 + self._dy = 0.0 + self._dz = 0.0 + + @property + def dx(self): + return self._dx + + @property + def dy(self): + return self._dy + + @property + def dz(self): + return self._dz + + def accept(self): + self._dx = self.get_double_spin_box("doubleSpinBox_X") + self._dy = self.get_double_spin_box("doubleSpinBox_Y") + self._dz = self.get_double_spin_box("doubleSpinBox_Z") + super().accept() + + def reject(self): + self.close() diff --git a/src/View/Geometry/Table.py b/src/View/Geometry/Table.py index 3c5db448a5afee1c037e44df399cbc9fb956032d..fd91707a691d48f0c8f5dc9fedf1743d050176dc 100644 --- a/src/View/Geometry/Table.py +++ b/src/View/Geometry/Table.py @@ -55,8 +55,8 @@ class GeometryReachTableModel(PamhyrTableModel): return self._data.profile(index.row()).name if role == Qt.DisplayRole and index.column() == 1: - kp = self._data.profile(index.row()).kp - return f"{kp:.4f}" + rk = self._data.profile(index.row()).rk + return f"{rk:.4f}" if role == Qt.DisplayRole and index.column() == 2: return str(self._data.profile(index.row()).nb_points) @@ -94,9 +94,9 @@ class GeometryReachTableModel(PamhyrTableModel): if index.column() == 1: self._undo.push( - SetKPCommand( + SetRKCommand( self._data, index.row(), - self._data.profile(index.row()).kp, + self._data.profile(index.row()).rk, value ) ) @@ -247,23 +247,32 @@ class GeometryReachTableModel(PamhyrTableModel): self.layoutAboutToBeChanged.emit() self.layoutChanged.emit() - def update_kp(self, mesher, data): + def update_rk(self, mesher, data): self.layoutAboutToBeChanged.emit() self._undo.push( MeshingCommand( - self._data, mesher, data, "update_kp" + self._data, mesher, data, "update_rk" ) ) self.layoutAboutToBeChanged.emit() self.layoutChanged.emit() - def purge(self): + def purge(self, np_purge): self._undo.push( PurgeCommand( - self._data, 24 + self._data, np_purge + ) + ) + self.layoutChanged.emit() + + def shift(self, rows, dx, dy, dz): + + self._undo.push( + ShiftCommand( + self._data, rows, dx, dy, dz ) ) self.layoutChanged.emit() diff --git a/src/View/Geometry/Translate.py b/src/View/Geometry/Translate.py index ec8c451a6ce5d8983fd5bd27177880f580f8888e..7944d66150fdc046f200ff83e7a4b0cb513d9b6d 100644 --- a/src/View/Geometry/Translate.py +++ b/src/View/Geometry/Translate.py @@ -33,12 +33,12 @@ class GeometryTranslate(MainTranslate): self._dict["open_file"] = _translate("Geometry", "Open a file") self._dict["file_st"] = _translate( - "Geometry", "File mage geometry (*.ST *.st)") + "Geometry", "Mage geometry file (*.ST *.st)") self._dict["file_m"] = _translate( - "Geometry", "File mage meshed geometry (*.M *.m)") + "Geometry", "Mage meshed geometry file (*.M *.m)") self._dict["file_shp"] = _translate( "Geometry", "Shapefile (*.SHP *.shp)") - self._dict["file_all"] = _translate("Geometry", "All file (*)") + self._dict["file_all"] = _translate("Geometry", "All files (*)") self._dict["cross_section"] = _translate("Geometry", "cross-section") self._dict["cross_sections"] = _translate("Geometry", "cross-sections") @@ -61,7 +61,7 @@ class GeometryTranslate(MainTranslate): self._sub_dict["table_headers"] = { "name": self._dict["name"], - "kp": self._dict["unit_kp"], + "rk": self._dict["unit_rk"], "poins": _translate("Geometry", "Points"), } @@ -70,7 +70,7 @@ class GeometryTranslate(MainTranslate): "2": _translate("Geometry", "the second guide-line"), "3": _translate( "Geometry", - "the means between the two guide-lines" + "the mean over the two guide-lines" ), } @@ -82,3 +82,12 @@ class GeometryTranslate(MainTranslate): self._dict["Meshing"] = _translate( "Geometry", "Meshing" ) + self._dict["UpdateRK"] = _translate( + "Geometry", "UpdateRK" + ) + self._dict["Purge"] = _translate( + "Geometry", "Purge" + ) + self._dict["Shift"] = _translate( + "Geometry", "Shift" + ) diff --git a/src/View/Geometry/UndoCommand.py b/src/View/Geometry/UndoCommand.py index f50299fc93c633a6048d936d17443618b3346c7b..d3cc29efcd4549f6da25f98c41a1b90cbd48afe8 100644 --- a/src/View/Geometry/UndoCommand.py +++ b/src/View/Geometry/UndoCommand.py @@ -56,16 +56,16 @@ class SetNameCommand(SetDataCommand): self._reach.profile(self._index).name = self._new -class SetKPCommand(SetDataCommand): +class SetRKCommand(SetDataCommand): def __init__(self, reach, index, old_value, new_value): self.type = float - super(SetKPCommand, self).__init__(reach, index, old_value, new_value) + super(SetRKCommand, self).__init__(reach, index, old_value, new_value) def undo(self): - self._reach.profile(self._index).kp = self._old + self._reach.profile(self._index).rk = self._old def redo(self): - self._reach.profile(self._index).kp = self._new + self._reach.profile(self._index).rk = self._new class AddCommand(QUndoCommand): @@ -243,8 +243,8 @@ class MeshingCommand(QUndoCommand): def redo(self): if self._new_profiles is None: - if self._command == "update_kp": - self._mesher.update_kp( + if self._command == "update_rk": + self._mesher.update_rk( self._reach, **self._data ) @@ -281,3 +281,32 @@ class PurgeCommand(QUndoCommand): def redo(self): for profile in self._reach._profiles: profile.purge(self._np_purge) + + +class ShiftCommand(QUndoCommand): + def __init__(self, reach, rows, dx, dy, dz): + QUndoCommand.__init__(self) + + self._reach = reach + self._rows = rows + self._dx = dx + self._dy = dy + self._dz = dz + + self._old = [] + for profile in self._reach.profiles: + self._old.append(profile.points.copy()) + + def undo(self): + for i in self._rows: + profile = self._reach.profiles[i] + self._reach.profiles[i].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(self._dx, + self._dy, + self._dz) diff --git a/src/View/Geometry/UpdateKPDialog.py b/src/View/Geometry/UpdateRKDialog.py similarity index 70% rename from src/View/Geometry/UpdateKPDialog.py rename to src/View/Geometry/UpdateRKDialog.py index 87afc9778247286063719fff7974cf7f8724a572..867a10a5735327b484e2ae301207658468aa274e 100644 --- a/src/View/Geometry/UpdateKPDialog.py +++ b/src/View/Geometry/UpdateRKDialog.py @@ -1,4 +1,4 @@ -# MeshingDialog.py -- Pamhyr +# UpdateRKDialog.py -- Pamhyr # Copyright (C) 2023-2024 INRAE # # This program is free software: you can redistribute it and/or modify @@ -28,16 +28,16 @@ from PyQt5.QtCore import ( from PyQt5.QtWidgets import ( QDialogButtonBox, QComboBox, QUndoStack, QShortcut, - QDoubleSpinBox, + QDoubleSpinBox, QButtonGroup, ) -class UpdateKPDialog(PamhyrDialog): - _pamhyr_ui = "UpdateKPOptions" - _pamhyr_name = "UpdateKP" +class UpdateRKDialog(PamhyrDialog): + _pamhyr_ui = "UpdateRKOptions" + _pamhyr_name = "UpdateRK" def __init__(self, reach, trad=None, parent=None): - super(UpdateKPDialog, self).__init__( + super(UpdateRKDialog, self).__init__( title=trad[self._pamhyr_name], trad=trad, options=[], @@ -50,16 +50,22 @@ class UpdateKPDialog(PamhyrDialog): self._init_default_values() def _init_default_values(self): - self._space_step = 50.0 - self._lplan = False - self._lm = "3" - self._linear = False - self._begin_cs = -1 - self._end_cs = -1 + gl, _ = self._reach.compute_guidelines() + self._gl = list(gl) + self._begin_dir = "un" self._end_dir = "np" + + lower_gl = list(map(str.lower, self._gl)) + for i, gl in enumerate(lower_gl): + if gl == "rd": + self._begin_dir = self._gl[i] + elif gl == "rg": + self._end_dir = self._gl[i] + + self._orientation = 0 self._origin = self._reach.profile(0) - self._origin_value = self._reach.profile(0).kp + self._origin_value = self._reach.profile(0).rk self._init_default_values_profiles() self._init_default_values_guidelines() @@ -69,14 +75,25 @@ class UpdateKPDialog(PamhyrDialog): self.combobox_add_items("comboBox_origin", profiles) - self.find(QComboBox, "comboBox_origin").currentIndexChanged.connect( - self.changed_profile) + self.find(QComboBox, "comboBox_origin")\ + .currentIndexChanged\ + .connect( + self.changed_profile + ) + + buttonbox = self.find(QButtonGroup, "buttonGroup_orientation") + + for button in buttonbox.buttons(): + name = button.objectName() + i = int(name.split('_')[-1]) + + buttonbox.setId(button, i) def changed_profile(self): origin = self.get_combobox_text("comboBox_origin") self.set_double_spin_box( "doubleSpinBox_origin", - self._reach.profile(self.profiles.index(origin)).kp + self._reach.profile(self.profiles.index(origin)).rk ) @property @@ -95,18 +112,15 @@ class UpdateKPDialog(PamhyrDialog): name = profile.name if name == "": - name = f"{profile.kp}" + name = f"{profile.rk}" else: - name += f" ({profile.kp})" + name += f" ({profile.rk})" return name def _init_default_values_guidelines(self): - gl, _ = self._reach.compute_guidelines() - gl = list(gl) - - bgl = ['un'] + gl + ['np'] - egl = ['un'] + gl + ['np'] + bgl = ['un'] + self._gl + ['np'] + egl = ['un'] + self._gl + ['np'] self.combobox_add_items("comboBox_begin_gl", bgl) self.combobox_add_items("comboBox_end_gl", egl) @@ -114,6 +128,10 @@ class UpdateKPDialog(PamhyrDialog): self.set_combobox_text("comboBox_begin_gl", self._begin_dir) self.set_combobox_text("comboBox_end_gl", self._end_dir) + @property + def orientation(self): + return self._orientation + @property def origin(self): return self._origin @@ -133,11 +151,12 @@ class UpdateKPDialog(PamhyrDialog): def accept(self): origin = self.get_combobox_text("comboBox_origin") self._origin = self.profiles.index(origin) - self._origin_value = self.get_double_spin_box("doubleSpinBox_origin") - self._begin_dir = self.get_combobox_text("comboBox_begin_gl") self._end_dir = self.get_combobox_text("comboBox_end_gl") + self._orientation = self.get_checked_id_button_group( + "buttonGroup_orientation" + ) super().accept() diff --git a/src/View/Geometry/Window.py b/src/View/Geometry/Window.py index 65ffb8f5c47b749aa1f0cdec105aaf3747307b7f..6c24b22e4a70f3c23d0b1d8af2d7d2f51dd0dc9f 100644 --- a/src/View/Geometry/Window.py +++ b/src/View/Geometry/Window.py @@ -36,7 +36,7 @@ from PyQt5.QtCore import ( from PyQt5.QtWidgets import ( QApplication, QMainWindow, QFileDialog, QCheckBox, QUndoStack, QShortcut, QTableView, QHeaderView, - QAction, QSlider, QPushButton, QVBoxLayout, + QAction, QPushButton, QVBoxLayout, QLabel, QAbstractItemView, ) @@ -54,9 +54,11 @@ from Meshing.Mage import ( from View.Geometry.Table import GeometryReachTableModel from View.Geometry.PlotXY import PlotXY from View.Geometry.PlotAC import PlotAC -from View.Geometry.PlotKPZ import PlotKPZ +from View.Geometry.PlotRKZ import PlotRKZ from View.Geometry.MeshingDialog import MeshingDialog -from View.Geometry.UpdateKPDialog import UpdateKPDialog +from View.Geometry.UpdateRKDialog import UpdateRKDialog +from View.Geometry.PurgeDialog import PurgeDialog +from View.Geometry.ShiftDialog import ShiftDialog from View.Geometry.Translate import GeometryTranslate from View.Geometry.Profile.Window import ProfileWindow @@ -96,7 +98,6 @@ class GeometryWindow(PamhyrWindow): self.setup_plots() self.setup_statusbar() self.setup_connections() - self.changed_slider_value() def setup_table(self): table_headers = self._trad.get_dict("table_headers") @@ -105,7 +106,7 @@ class GeometryWindow(PamhyrWindow): self._table = GeometryReachTableModel( table_view=table, table_headers=table_headers, - editable_headers=["name", "kp"], + editable_headers=["name", "rk"], data=self._reach, undo=self._undo_stack ) @@ -116,7 +117,7 @@ class GeometryWindow(PamhyrWindow): def setup_plots(self): self.setup_plots_xy() - self.setup_plots_kpc() + self.setup_plots_rkc() self.setup_plots_ac() def setup_plots_xy(self): @@ -131,17 +132,17 @@ class GeometryWindow(PamhyrWindow): self._plot_layout_xy.addWidget(self._canvas_xy) self.plot_xy() - def setup_plots_kpc(self): - self._canvas_kpc = MplCanvas(width=6, height=4, dpi=100) - self._canvas_kpc.setObjectName("canvas_kpc") - self._toolbar_kpc = PamhyrPlotToolbar( - self._canvas_kpc, self, + def setup_plots_rkc(self): + self._canvas_rkc = MplCanvas(width=6, height=4, dpi=100) + self._canvas_rkc.setObjectName("canvas_rkc") + self._toolbar_rkc = PamhyrPlotToolbar( + self._canvas_rkc, self, items=["home", "zoom", "save", "iso", "back/forward", "move"] ) - self._plot_layout_kpc = self.find(QVBoxLayout, "verticalLayout_2") - self._plot_layout_kpc.addWidget(self._toolbar_kpc) - self._plot_layout_kpc.addWidget(self._canvas_kpc) - self.plot_kpc() + self._plot_layout_rkc = self.find(QVBoxLayout, "verticalLayout_2") + self._plot_layout_rkc.addWidget(self._toolbar_rkc) + self._plot_layout_rkc.addWidget(self._canvas_rkc) + self.plot_rkc() def setup_plots_ac(self): self._canvas_ac = MplCanvas(width=9, height=4, dpi=100) @@ -157,9 +158,12 @@ class GeometryWindow(PamhyrWindow): def _compute_status_label(self): row = self.index_selected_row() + if row is None: + return + profile = self._reach.profile(row) - name = profile.name + " " + str(profile.kp) + name = profile.name + " " + str(profile.rk) return ( "<font color=\"Grey\">" + @@ -189,21 +193,15 @@ class GeometryWindow(PamhyrWindow): "action_delete": self.delete, "action_edit": self.edit_profile, "action_meshing": self.edit_meshing, - "action_update_kp": self.update_kp, + "action_update_rk": self.update_rk, "action_purge": self.purge, + "action_shift": self.shift, } for action in actions: self.find(QAction, action)\ .triggered.connect(actions[action]) - self.find(QSlider, "verticalSlider").valueChanged.connect( - self.changed_slider_value) - self.find(QPushButton, "pushButton_up").clicked.connect( - self.decrement_value_slider) - self.find(QPushButton, "pushButton_down").clicked.connect( - self.increment_value_slider) - # Profile selection when line change in table self.find(QTableView, "tableView").selectionModel()\ .selectionChanged\ @@ -220,11 +218,10 @@ class GeometryWindow(PamhyrWindow): def _update(self, redraw=False, propagate=True): if redraw: self._plot_xy.draw() - self._plot_kpc.draw() + self._plot_rkc.draw() self._plot_ac.draw() self.select_current_profile() - self.changed_slider_value() if propagate: self._propagate_update(key=Modules.GEOMETRY) @@ -298,10 +295,8 @@ class GeometryWindow(PamhyrWindow): data = { "step": dlg.space_step, "limites": [dlg.begin_cs, dlg.end_cs], - "origin": dlg.origin, "directrices": [dlg.begin_dir, dlg.end_dir], "lplan": dlg.lplan, - "lm": dlg.lm, "linear": dlg.linear, } self._edit_meshing(data) @@ -322,9 +317,9 @@ class GeometryWindow(PamhyrWindow): src_except=e ) - def update_kp(self): + def update_rk(self): try: - dlg = UpdateKPDialog( + dlg = UpdateRKDialog( reach=self._reach, trad=self._trad, parent=self @@ -334,16 +329,17 @@ class GeometryWindow(PamhyrWindow): "origin": dlg.origin, "directrices": [dlg.begin_dir, dlg.end_dir], "origin_value": dlg.origin_value, + "orientation": dlg.orientation, } - self._update_kp(data) + self._update_rk(data) except Exception as e: logger_exception(e) return - def _update_kp(self, data): + def _update_rk(self, data): try: mesher = MeshingWithMageMailleurTT() - self._table.update_kp(mesher, data) + self._table.update_rk(mesher, data) except Exception as e: logger_exception(e) raise ExternFileMissingError( @@ -360,7 +356,9 @@ class GeometryWindow(PamhyrWindow): canvas=self._canvas_xy, data=self._reach, trad=self._trad, - toolbar=self._toolbar_xy + toolbar=self._toolbar_xy, + table=self.find(QTableView, "tableView"), + parent=self ) self._plot_xy.draw() @@ -371,23 +369,25 @@ class GeometryWindow(PamhyrWindow): self._plot_xy.update() self.tableView.model().blockSignals(False) - def plot_kpc(self): + def plot_rkc(self): self.tableView.model().blockSignals(True) - self._plot_kpc = PlotKPZ( - canvas=self._canvas_kpc, + self._plot_rkc = PlotRKZ( + canvas=self._canvas_rkc, study=self._study, data=self._reach, trad=self._trad, - toolbar=self._toolbar_kpc + toolbar=self._toolbar_rkc, + table=self.find(QTableView, "tableView"), + parent=self ) - self._plot_kpc.draw() + self._plot_rkc.draw() self.tableView.model().blockSignals(False) - def update_plot_kpc(self): + def update_plot_rkc(self): self.tableView.model().blockSignals(True) - self._plot_kpc.update() + self._plot_rkc.update() self.tableView.model().blockSignals(False) def plot_ac(self): @@ -405,7 +405,6 @@ class GeometryWindow(PamhyrWindow): def update_plot_ac(self, ind: int): self.tableView.model().blockSignals(True) - self._plot_ac.current = ind self._plot_ac.update() self.tableView.model().blockSignals(False) @@ -421,10 +420,10 @@ class GeometryWindow(PamhyrWindow): self._plot_xy.update() self.tableView.model().blockSignals(False) - def select_plot_kpc(self, ind: int): + def select_plot_rkc(self, ind: int): self.tableView.model().blockSignals(True) - self._plot_kpc.current = ind - self._plot_kpc.update() + self._plot_rkc.current = ind + self._plot_rkc.update() self.tableView.model().blockSignals(False) def select_plot_ac(self, ind: int): @@ -433,64 +432,21 @@ class GeometryWindow(PamhyrWindow): self._plot_ac.update() self.tableView.model().blockSignals(False) - def select_row_profile_slider(self, ind: int = 0): - if self.tableView is not None: - selectionModel = self.tableView.selectionModel() - index = self.tableView.model().index(ind, 0) - - selectionModel.select( - index, - QItemSelectionModel.Rows | - QItemSelectionModel.ClearAndSelect | - QItemSelectionModel.Select - ) - - self.tableView.scrollTo(index) - def select_current_profile(self): self.tableView.model().blockSignals(True) + row = self.index_selected_row() - if len(self.tableView.selectedIndexes()) > 0: - row = self.index_selected_row() - - self.find(QSlider, "verticalSlider").setValue(row) + if row is not None: self.select_plot_xy(row) - self.select_plot_kpc(row) + self.select_plot_rkc(row) self.select_plot_ac(row) else: self._plot_xy.draw() - self._plot_kpc.draw() + self._plot_rkc.draw() self._plot_ac.draw() self.tableView.model().blockSignals(False) - def changed_slider_value(self): - self.tableView.model().blockSignals(True) - - if self._table.rowCount() != 0: - slider = self.find(QSlider, "verticalSlider") - slider.setMaximum(self._table.rowCount() - 1) - - slider_value = slider.value() - kp = self._reach.profile(slider_value).kp - - self.select_plot_xy(slider_value) - self.select_plot_kpc(slider_value) - self.select_row_profile_slider(slider_value) - self.update_statusbar() - - self.tableView.model().blockSignals(False) - - def increment_value_slider(self): - slider = self.find(QSlider, "verticalSlider") - if 0 <= slider.value() < self._table.rowCount() - 1: - slider.setValue(slider.value() + 1) - - def decrement_value_slider(self): - slider = self.find(QSlider, "verticalSlider") - if 0 < slider.value() < self._table.rowCount(): - slider.setValue(slider.value() - 1) - def add(self): if len(self.tableView.selectedIndexes()) == 0: self._table.add(self._table.rowCount()) @@ -511,35 +467,73 @@ class GeometryWindow(PamhyrWindow): self._table.delete(rows) def index_selected_row(self): - return self.tableView\ - .selectionModel()\ - .selectedRows()[0]\ - .row() + r = self.tableView\ + .selectionModel()\ + .selectedRows() + + if len(r) == 1: + return r[0].row() + else: + return None def sort_ascending(self): self._table.sort_profiles(False) self.select_current_profile() - self.changed_slider_value() def sort_descending(self): self._table.sort_profiles(True) self.select_current_profile() - self.changed_slider_value() def move_up(self): row = self.index_selected_row() + if row is None: + return + self._table.move_up(row) self.select_current_profile() def move_down(self): row = self.index_selected_row() + if row is None: + return + self._table.move_down(row) self.select_current_profile() def purge(self): - self._table.purge() - self.update_redraw() + try: + dlg = PurgeDialog( + trad=self._trad, + parent=self + ) + if dlg.exec(): + self._table.purge(dlg.np_purge) + except Exception as e: + logger_exception(e) + return + + def shift(self): + rows = sorted( + list( + set( + [index.row() for index in self.tableView.selectedIndexes()] + ) + ) + ) + try: + dlg = ShiftDialog( + trad=self._trad, + parent=self + ) + if dlg.exec(): + self._table.shift(rows, + dlg.dx, + dlg.dy, + dlg.dz) + except Exception as e: + logger_exception(e) + return def duplicate(self): rows = [ @@ -565,12 +559,12 @@ class GeometryWindow(PamhyrWindow): .selectedRows() table = [] - # table.append(["name", "kp"]) + # table.append(["name", "rk"]) for row in rows: profile = self._reach.profile(row.row()) table.append( - [profile.name, profile.kp] + [profile.name, profile.rk] ) self.copyTableIntoClipboard(table) @@ -614,14 +608,14 @@ class GeometryWindow(PamhyrWindow): self.select_current_profile() # self.update_plot_ac() # self.update_plot_xy() - # self.update_plot_kpc() + # self.update_plot_rkc() def _redo(self): self._table.redo() self.select_current_profile() # self.update_plot_ac() # self.update_plot_xy() - # self.update_plot_kpc() + # self.update_plot_rkc() def export_to_file(self): settings = QSettings( @@ -629,7 +623,7 @@ class GeometryWindow(PamhyrWindow): QSettings.UserScope, 'MyOrg' ) - if self._study.filename != "" or self._study.filename is not None: + if self._study.filename != "" and self._study.filename is not None: default_directory = os.path.basename(self._study.filename) current_dir = settings.value( 'current_directory', @@ -668,13 +662,13 @@ class GeometryWindow(PamhyrWindow): c1 = f"{profile.code1:>6}" c2 = f"{profile.code2:>6}" t = f"{len(profile.points):>6}" - kp = f"{profile.kp:>12f}"[0:12] + rk = f"{profile.rk:>12f}"[0:12] pname = profile.name if profile.name == "": pname = f"p{profile.id:>3}".replace(" ", "0") name = f"{pname:<19}" - wfile.write(f"{num}{c1}{c2}{t} {kp} {pname}\n") + wfile.write(f"{num}{c1}{c2}{t} {rk} {pname}\n") for point in profile.points: self._export_to_file_st_point(wfile, point) diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py b/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py index f55fb2d778e212588520e94dafd133c101c38413..29a12f350feedfe000ae42954affc90e7d30ac9a 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/Translate.py @@ -28,7 +28,7 @@ class BasicHydraulicStructuresTranslate(MainTranslate): super(BasicHydraulicStructuresTranslate, self).__init__() self._dict["Basic Hydraulic Structures"] = _translate( - "BasicHydraulicStructures", "Basic Hydraulic Structures" + "BasicHydraulicStructures", "Basic Hydraulic Structure" ) self._dict['msg_type_change_title'] = _translate( @@ -138,7 +138,7 @@ hydraulic structure values?" "BasicHydraulicStructures", "Check valve" ), "UD": _translate( - "BasicHydraulicStructures", "User defined" + "BasicHydraulicStructures", "User-defined" ), } diff --git a/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py b/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py index 9a39f49c64749b995b3ac3733038ee0e720003b5..c0a133a16ed8bf0b2f22f6559e2a6177e295e577 100644 --- a/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py +++ b/src/View/HydraulicStructures/BasicHydraulicStructures/Window.py @@ -150,9 +150,9 @@ class BasicHydraulicStructuresWindow(PamhyrWindow): self.plot_layout.addWidget(self.canvas) reach = self._hs.input_reach - profile_kp = self._hs.input_kp - if profile_kp is not None: - profiles = reach.reach.get_profiles_from_kp(float(profile_kp)) + profile_rk = self._hs.input_rk + if profile_rk is not None: + profiles = reach.reach.get_profiles_from_rk(float(profile_rk)) else: profiles = None if profiles is not None: diff --git a/src/View/HydraulicStructures/PlotAC.py b/src/View/HydraulicStructures/PlotAC.py index c63dc9bfdef5fb2e311b54eb90cf85e4937a4b1c..31c17b22b6c8eb92a843f2de3c66408c76ae94a1 100644 --- a/src/View/HydraulicStructures/PlotAC.py +++ b/src/View/HydraulicStructures/PlotAC.py @@ -57,11 +57,11 @@ class PlotAC(PamhyrPlot): self.init_axes() if self.data is None: - self.line_kp = None + self.line_rk = None return if self._current_reach is None: - self.line_kp = None + self.line_rk = None return self.draw_data() @@ -73,13 +73,13 @@ class PlotAC(PamhyrPlot): reach = self._current_reach if self._current_profile is None: - self.line_kp = None + self.line_rk = None else: profile = self._current_profile x = profile.get_station() z = profile.z() - self.line_kp, = self.canvas.axes.plot( + self.line_rk, = self.canvas.axes.plot( x, z, color=self.color_plot_river_bottom, **self.plot_default_kargs @@ -94,7 +94,7 @@ class PlotAC(PamhyrPlot): self.update() def update(self): - if self.line_kp is None: + if self.line_rk is None: self.draw() return @@ -110,11 +110,11 @@ class PlotAC(PamhyrPlot): x = profile.get_station() z = profile.z() - self.line_kp.set_data(x, z) + self.line_rk.set_data(x, z) def clear(self): self.update_clear() def update_clear(self): - if self.line_kp is not None: - self.line_kp.set_data([], []) + if self.line_rk is not None: + self.line_rk.set_data([], []) diff --git a/src/View/HydraulicStructures/PlotKPC.py b/src/View/HydraulicStructures/PlotRKC.py similarity index 60% rename from src/View/HydraulicStructures/PlotKPC.py rename to src/View/HydraulicStructures/PlotRKC.py index 3327f6e627114ac8513dda1ca57670b0cdf722e8..72a16a8fd4cc860a35141f6f820fbc615b60343a 100644 --- a/src/View/HydraulicStructures/PlotKPC.py +++ b/src/View/HydraulicStructures/PlotRKC.py @@ -1,4 +1,4 @@ -# PlotKPC.py -- Pamhyr +# PlotRKC.py -- Pamhyr # Copyright (C) 2023-2024 INRAE # # This program is free software: you can redistribute it and/or modify @@ -20,19 +20,20 @@ from tools import timer from View.Tools.PamhyrPlot import PamhyrPlot from PyQt5.QtCore import ( - QCoreApplication + QCoreApplication, Qt, ) +from PyQt5.QtWidgets import QApplication from matplotlib.collections import LineCollection _translate = QCoreApplication.translate -class PlotKPC(PamhyrPlot): +class PlotRKC(PamhyrPlot): def __init__(self, canvas=None, trad=None, toolbar=None, river=None, reach=None, profile=None, parent=None): - super(PlotKPC, self).__init__( + super(PlotRKC, self).__init__( canvas=canvas, trad=trad, data=river, @@ -43,13 +44,14 @@ class PlotKPC(PamhyrPlot): self._current_reach = reach self._current_profile = profile - self.label_x = self._trad["unit_kp"] + self.label_x = self._trad["unit_rk"] self.label_y = self._trad["unit_elevation"] self._isometric_axis = False self._auto_relim_update = True self._autoscale_update = True + self.parent = parent @property def river(self): @@ -65,14 +67,14 @@ class PlotKPC(PamhyrPlot): if self.data is None: self.profile = None - self.line_kp_zmin_zmax = None - self.line_kp_zmin = None + self.line_rk_zmin_zmax = None + self.line_rk_zmin = None return if self._current_reach is None: self.profile = None - self.line_kp_zmin_zmax = None - self.line_kp_zmin = None + self.line_rk_zmin_zmax = None + self.line_rk_zmin = None return self.draw_data() @@ -84,35 +86,43 @@ class PlotKPC(PamhyrPlot): def draw_data(self): reach = self._current_reach - kp = reach.reach.get_kp() + rk = reach.reach.get_rk() z_min = reach.reach.get_z_min() z_max = reach.reach.get_z_max() - self.line_kp_zmin, = self.canvas.axes.plot( - kp, z_min, + self.line_rk_zmin, = self.canvas.axes.plot( + rk, z_min, color=self.color_plot_river_bottom, - lw=1. + lw=1., ) - if len(kp) != 0: - self.line_kp_zmin_zmax = self.canvas.axes.vlines( - x=kp, + self.line_rk_zmax, = self.canvas.axes.plot( + rk, z_max, + color=self.color_plot_river_bottom, + lw=1., + alpha=0.0 + ) + + if len(rk) != 0: + self.line_rk_zmin_zmax = self.canvas.axes.vlines( + x=rk, ymin=z_min, ymax=z_max, color=self.color_plot, - lw=1. + lw=1., + picker=10 ) def draw_current(self): if self._current_profile is None: self.profile = None else: - kp = [self._current_profile.kp, - self._current_profile.kp] + rk = [self._current_profile.rk, + self._current_profile.rk] min_max = [self._current_profile.z_min(), self._current_profile.z_max()] self.profile = self.canvas.axes.plot( - kp, min_max, + rk, min_max, color=self.color_plot_current, lw=1. ) @@ -131,7 +141,7 @@ class PlotKPC(PamhyrPlot): def update_current_profile(self): reach = self._current_reach - kp = reach.reach.get_kp() + rk = reach.reach.get_rk() z_min = reach.reach.get_z_min() z_max = reach.reach.get_z_max() @@ -139,7 +149,7 @@ class PlotKPC(PamhyrPlot): self.draw() else: self.profile.set_data( - [self._current_profile.kp, self._current_profile.kp], + [self._current_profile.rk, self._current_profile.rk], [self._current_profile.z_min(), self._current_profile.z_max()], ) @@ -149,12 +159,12 @@ class PlotKPC(PamhyrPlot): if self.profile is not None: self.profile[0].set_data([], []) - if self.line_kp_zmin_zmax is not None: - self.line_kp_zmin_zmax.remove() - self.line_kp_zmin_zmax = None + if self.line_rk_zmin_zmax is not None: + self.line_rk_zmin_zmax.remove() + self.line_rk_zmin_zmax = None - if self.line_kp_zmin is not None: - self.line_kp_zmin.set_data([], []) + if self.line_rk_zmin is not None: + self.line_rk_zmin.set_data([], []) self.canvas.figure.canvas.draw_idle() @@ -163,3 +173,40 @@ class PlotKPC(PamhyrPlot): self.profile.set_data([], []) self.canvas.figure.canvas.draw_idle() + + def onpick(self, event): + if event.mouseevent.inaxes != self.canvas.axes: + return + if event.mouseevent.button.value != 1: + return + + modifiers = QApplication.keyboardModifiers() + if modifiers not in [Qt.ControlModifier, + Qt.NoModifier, + Qt.ShiftModifier]: + return + + closest = self._closest_profile(event) + index = self.parent.tableView.selectedIndexes() + if self.parent._table is not None: + self.parent._table.setData(index[2], closest) + + return + + def _closest_profile(self, event): + + s = event.artist.get_segments() + x = [i[0, 0] for i in s] + mx = event.mouseevent.xdata + points = enumerate(x) + + def dist_mouse(point): + x = point[1] + d = abs(mx - x) + return d + + closest = min( + points, key=dist_mouse + ) + + return closest[1] diff --git a/src/View/HydraulicStructures/Table.py b/src/View/HydraulicStructures/Table.py index a794f0a0d2ffc9bf14cf14071086d361e042dbfc..e8530b1a24faac926b642aeb9b53ac567811e4c8 100644 --- a/src/View/HydraulicStructures/Table.py +++ b/src/View/HydraulicStructures/Table.py @@ -37,7 +37,7 @@ from PyQt5.QtWidgets import ( from View.Tools.PamhyrTable import PamhyrTableModel from View.HydraulicStructures.UndoCommand import ( - SetNameCommand, SetReachCommand, SetKpCommand, + SetNameCommand, SetReachCommand, SetRkCommand, SetEnabledCommand, AddCommand, DelCommand, ) @@ -58,14 +58,14 @@ class ComboBoxDelegate(QItemDelegate): self.editor = QComboBox(parent) val = [] - if self._mode == "kp": + if self._mode == "rk": reach = self._data.hydraulic_structures\ .get(index.row())\ .input_reach if reach is not None: val = list( map( - lambda kp: str(kp), reach.reach.get_kp() + lambda rk: str(rk), reach.reach.get_rk() ) ) else: @@ -126,8 +126,8 @@ class TableModel(PamhyrTableModel): if n is None: return self._trad['not_associated'] return n.name - elif self._headers[column] == "kp": - n = self._lst.get(row).input_kp + elif self._headers[column] == "rk": + n = self._lst.get(row).input_rk if n is None: return self._trad['not_associated'] return n @@ -158,12 +158,12 @@ class TableModel(PamhyrTableModel): self._lst, row, self._data.edge(value) ) ) - elif self._headers[column] == "kp": + elif self._headers[column] == "rk": if value == na: value = None self._undo.push( - SetKpCommand( + SetRkCommand( self._lst, row, value ) ) diff --git a/src/View/HydraulicStructures/Translate.py b/src/View/HydraulicStructures/Translate.py index c82d90ff76eb1cc960f972bb23ee7e638df9421d..1888b5757d1b2e1292bfaa2d117ad28dcea543d8 100644 --- a/src/View/HydraulicStructures/Translate.py +++ b/src/View/HydraulicStructures/Translate.py @@ -36,5 +36,5 @@ class HydraulicStructuresTranslate(MainTranslate): self._sub_dict["table_headers"] = { "name": self._dict["name"], "reach": self._dict["reach"], - "kp": self._dict["unit_kp"], + "rk": self._dict["unit_rk"], } diff --git a/src/View/HydraulicStructures/UndoCommand.py b/src/View/HydraulicStructures/UndoCommand.py index cbb8a2e7927237c3b1a2d97f689c0a547270794d..f7b83b975479245318c4b4024771b7fb89b66751 100644 --- a/src/View/HydraulicStructures/UndoCommand.py +++ b/src/View/HydraulicStructures/UndoCommand.py @@ -52,34 +52,34 @@ class SetReachCommand(QUndoCommand): self._index = index self._old = self._h_s_lst.get(self._index).input_reach self._new = reach - self._old_kp = self._h_s_lst.get(self._index).input_kp - self._new_kp = None + self._old_rk = self._h_s_lst.get(self._index).input_rk + self._new_rk = None def undo(self): i = self._h_s_lst.get(self._index) i.input_reach = self._old - i.input_kp = self._old_kp + i.input_rk = self._old_rk def redo(self): i = self._h_s_lst.get(self._index) i.input_reach = self._new - i.input_kp = self._new_kp + i.input_rk = self._new_rk -class SetKpCommand(QUndoCommand): - def __init__(self, h_s_lst, index, kp): +class SetRkCommand(QUndoCommand): + def __init__(self, h_s_lst, index, rk): QUndoCommand.__init__(self) self._h_s_lst = h_s_lst self._index = index - self._old = self._h_s_lst.get(self._index).input_kp - self._new = kp + self._old = self._h_s_lst.get(self._index).input_rk + self._new = rk def undo(self): - self._h_s_lst.get(self._index).input_kp = self._old + self._h_s_lst.get(self._index).input_rk = self._old def redo(self): - self._h_s_lst.get(self._index).input_kp = self._new + self._h_s_lst.get(self._index).input_rk = self._new class SetEnabledCommand(QUndoCommand): diff --git a/src/View/HydraulicStructures/Window.py b/src/View/HydraulicStructures/Window.py index 1c6af72c6803393728312dd9632352033a39da40..a1296d4414b455293c7cdd30d8820545c3b3bbb2 100644 --- a/src/View/HydraulicStructures/Window.py +++ b/src/View/HydraulicStructures/Window.py @@ -39,7 +39,7 @@ from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar from View.HydraulicStructures.PlotAC import PlotAC -from View.HydraulicStructures.PlotKPC import PlotKPC +from View.HydraulicStructures.PlotRKC import PlotRKC from View.HydraulicStructures.Table import ( TableModel, ComboBoxDelegate @@ -89,21 +89,21 @@ class HydraulicStructuresWindow(PamhyrWindow): parent=self, mode="reaches" ) - self._delegate_kp = ComboBoxDelegate( + self._delegate_rk = ComboBoxDelegate( trad=self._trad, data=self._study.river, parent=self, - mode="kp" + mode="rk" ) table = self.find(QTableView, f"tableView") self._table = TableModel( table_view=table, table_headers=self._trad.get_dict("table_headers"), - editable_headers=["name", "reach", "kp"], + editable_headers=["name", "reach", "rk"], delegates={ "reach": self._delegate_reach, - "kp": self._delegate_kp, + "rk": self._delegate_rk, }, trad=self._trad, data=self._study.river, @@ -135,15 +135,16 @@ class HydraulicStructuresWindow(PamhyrWindow): self.plot_layout.addWidget(self.toolbar) self.plot_layout.addWidget(self.canvas) - self.plot_kpc = PlotKPC( + self.plot_rkc = PlotRKC( canvas=self.canvas, river=self._study.river, reach=None, profile=None, trad=self._trad, - toolbar=self.toolbar + toolbar=self.toolbar, + parent=self ) - self.plot_kpc.draw() + self.plot_rkc.draw() self.canvas_2 = MplCanvas(width=5, height=4, dpi=100) self.canvas_2.setObjectName("canvas_2") @@ -284,20 +285,20 @@ class HydraulicStructuresWindow(PamhyrWindow): reach = self._hs_lst.get(rows[0]).input_reach if reach is not None: - self.plot_kpc.set_reach(reach) + self.plot_rkc.set_reach(reach) self.plot_ac.set_reach(reach) - profile_kp = self._hs_lst.get(rows[0]).input_kp - if profile_kp is not None: + profile_rk = self._hs_lst.get(rows[0]).input_rk + if profile_rk is not None: profiles = reach.reach\ - .get_profiles_from_kp( - float(profile_kp) + .get_profiles_from_rk( + float(profile_rk) ) if len(profiles) != 0 and profiles is not None: profile = profiles[0] - self.plot_kpc.set_profile(profile) + self.plot_rkc.set_profile(profile) self.plot_ac.set_profile(profile) else: self._update_clear_profile() @@ -307,9 +308,9 @@ class HydraulicStructuresWindow(PamhyrWindow): self._update_clear_all() def _update_clear_all(self): - self.plot_kpc.clear() + self.plot_rkc.clear() self.plot_ac.clear() def _update_clear_profile(self): self.plot_ac.clear() - self.plot_kpc.clear_profile() + self.plot_rkc.clear_profile() diff --git a/src/View/InitialConditions/DialogDepth.py b/src/View/InitialConditions/DialogDepth.py new file mode 100644 index 0000000000000000000000000000000000000000..53abfe3987e6fb95c07ac2b8d8105da029df473b --- /dev/null +++ b/src/View/InitialConditions/DialogDepth.py @@ -0,0 +1,60 @@ +# DialogDepth.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 View.Tools.PamhyrWindow import PamhyrDialog + +from PyQt5.QtGui import ( + QKeySequence, +) + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QComboBox, QUndoStack, QShortcut, + QDoubleSpinBox, QCheckBox, +) + + +class DepthDialog(PamhyrDialog): + _pamhyr_ui = "InitialConditions_Dialog_Generator_Depth" + _pamhyr_name = "Depth" + + def __init__(self, value, option, trad=None, parent=None): + super(DepthDialog, self).__init__( + title=trad[self._pamhyr_name], + options=[], + trad=trad, + parent=parent + ) + + self.value = value + self.option = option + self.sb = self.find(QDoubleSpinBox, "doubleSpinBox") + self.sb.setValue(self.value) + self.cb = self.find(QCheckBox, "checkBox") + self.cb.setChecked(self.option) + + def accept(self): + self.value = self.sb.value() + self.option = self.cb.isChecked() + super().accept() + + def reject(self): + self.close() diff --git a/src/View/InitialConditions/DialogDischarge.py b/src/View/InitialConditions/DialogDischarge.py index 9b63bdabd70c5285cc12ceddef3c5c98861d7c06..c7640037f294d79680c17c084f43b1d672512d89 100644 --- a/src/View/InitialConditions/DialogDischarge.py +++ b/src/View/InitialConditions/DialogDischarge.py @@ -28,7 +28,7 @@ from PyQt5.QtCore import ( from PyQt5.QtWidgets import ( QDialogButtonBox, QComboBox, QUndoStack, QShortcut, - QDoubleSpinBox, + QDoubleSpinBox, QCheckBox, ) @@ -36,7 +36,12 @@ class DischargeDialog(PamhyrDialog): _pamhyr_ui = "InitialConditions_Dialog_Generator_Discharge" _pamhyr_name = "Discharge" - def __init__(self, title="Discharge", trad=None, parent=None): + def __init__(self, + value, + option, + title="Discharge", + trad=None, + parent=None): super(DischargeDialog, self).__init__( title=trad[self._pamhyr_name], options=[], @@ -44,10 +49,16 @@ class DischargeDialog(PamhyrDialog): parent=parent ) - self.value = None + self.value = value + self.option = option + self.sb = self.find(QDoubleSpinBox, "doubleSpinBox") + self.sb.setValue(self.value) + self.cb = self.find(QCheckBox, "checkBox") + self.cb.setChecked(self.option) def accept(self): - self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value() + self.value = self.sb.value() + self.option = self.cb.isChecked() super().accept() def reject(self): diff --git a/src/View/InitialConditions/DialogHeight.py b/src/View/InitialConditions/DialogHeight.py index 408212324c0f47116c97795436f2fe97812d1954..0f9814c3fa4ff324774fadaa6e49dfd9d4b3e258 100644 --- a/src/View/InitialConditions/DialogHeight.py +++ b/src/View/InitialConditions/DialogHeight.py @@ -28,7 +28,7 @@ from PyQt5.QtCore import ( from PyQt5.QtWidgets import ( QDialogButtonBox, QComboBox, QUndoStack, QShortcut, - QDoubleSpinBox, + QDoubleSpinBox, QCheckBox, QLabel ) @@ -36,7 +36,7 @@ class HeightDialog(PamhyrDialog): _pamhyr_ui = "InitialConditions_Dialog_Generator_Height" _pamhyr_name = "Height" - def __init__(self, trad=None, parent=None): + def __init__(self, values, option, trad=None, parent=None): super(HeightDialog, self).__init__( title=trad[self._pamhyr_name], options=[], @@ -44,10 +44,29 @@ class HeightDialog(PamhyrDialog): parent=parent ) - self.value = None + self.values = values + self.option = option + self.sb1 = self.find(QDoubleSpinBox, "doubleSpinBox_1") + self.sb1.setValue(self.values[0]) + self.sb2 = self.find(QDoubleSpinBox, "doubleSpinBox_2") + self.sb2.setValue(self.values[1]) + self.sb3 = self.find(QDoubleSpinBox, "doubleSpinBox_3") + self.sb3.setValue(self.values[2]) + self.cb = self.find(QCheckBox, "checkBox") + self.cb.setChecked(self.option) + self.enable_discharge() + self.cb.clicked.connect(self.enable_discharge) + + def enable_discharge(self): + label = self.find(QLabel, "label_3") + self.sb3.setEnabled(self.cb.isChecked()) + label.setEnabled(self.cb.isChecked()) def accept(self): - self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value() + self.values[0] = self.sb1.value() + self.values[1] = self.sb2.value() + self.values[2] = self.sb3.value() + self.option = self.cb.isChecked() super().accept() def reject(self): diff --git a/src/View/InitialConditions/PlotDKP.py b/src/View/InitialConditions/PlotDRK.py similarity index 80% rename from src/View/InitialConditions/PlotDKP.py rename to src/View/InitialConditions/PlotDRK.py index d29356fa01fc1d0b2d9480db5a113189287c078b..352f7e4790460f0488769e2f16c4c9d6d4ad3a49 100644 --- a/src/View/InitialConditions/PlotDKP.py +++ b/src/View/InitialConditions/PlotDRK.py @@ -1,4 +1,4 @@ -# PlotDKP.py -- Pamhyr +# PlotDRK.py -- Pamhyr # Copyright (C) 2023-2024 INRAE # # This program is free software: you can redistribute it and/or modify @@ -30,10 +30,10 @@ logger = logging.getLogger() _translate = QCoreApplication.translate -class PlotDKP(PamhyrPlot): +class PlotDRK(PamhyrPlot): def __init__(self, canvas=None, trad=None, toolbar=None, data=None, parent=None): - super(PlotDKP, self).__init__( + super(PlotDRK, self).__init__( canvas=canvas, trad=trad, data=data, @@ -41,7 +41,7 @@ class PlotDKP(PamhyrPlot): parent=parent ) - self.label_x = self._trad["kp"] + self.label_x = self._trad["rk"] self.label_y = self._trad["elevation"] self._isometric_axis = False @@ -63,41 +63,41 @@ class PlotDKP(PamhyrPlot): self._init = True def draw_river_bottom(self): - kp = self.data.reach.reach.get_kp() + rk = self.data.reach.reach.get_rk() z_min = self.data.reach.reach.get_z_min() - self.line_kp_zmin = self.canvas.axes.plot( - kp, z_min, + self.line_rk_zmin = self.canvas.axes.plot( + rk, z_min, color=self.color_plot_river_bottom, lw=1. ) def draw_water(self): if len(self.data) != 0: - kp = self.data.get_kp() + rk = self.data.get_rk() elevation = self.data.get_elevation() - self.line_kp_elevation = self.canvas.axes.plot( - kp, elevation, + self.line_rk_elevation = self.canvas.axes.plot( + rk, elevation, color=self.color_plot_river_water, **self.plot_default_kargs ) z_min = self.data.reach.reach.get_z_min() - geometry_kp = self.data.reach.reach.get_kp() + geometry_rk = self.data.reach.reach.get_rk() filtred_elevation = list( map( lambda x: elevation[x[0]], filter( - lambda x: x[1] in geometry_kp, - enumerate(kp) + lambda x: x[1] in geometry_rk, + enumerate(rk) ) ) ) self.collection = self.canvas.axes.fill_between( - geometry_kp, z_min, filtred_elevation, + geometry_rk, z_min, filtred_elevation, color=self.color_plot_river_water_zone, alpha=0.7, interpolate=True ) diff --git a/src/View/InitialConditions/PlotDischarge.py b/src/View/InitialConditions/PlotDischarge.py index 7c43c1e557815071c4908de1de08783b1034cc91..0494fa1aec93819243961aa208f35b94e210f2c6 100644 --- a/src/View/InitialConditions/PlotDischarge.py +++ b/src/View/InitialConditions/PlotDischarge.py @@ -31,7 +31,7 @@ class PlotDischarge(PamhyrPlot): parent=parent ) - self.label_x = self._trad["kp"] + self.label_x = self._trad["rk"] self.label_y = self._trad["discharge"] self._isometric_axis = False @@ -52,18 +52,41 @@ class PlotDischarge(PamhyrPlot): self._init = True def draw_data(self): - kp = self.data.reach.reach.get_kp() + self.line_discharge = [] if len(self.data) != 0: - kp = self.data.get_kp() + rk = self.data.get_rk() discharge = self.data.get_discharge() - self.line_kp_zmin = self.canvas.axes.plot( - kp, discharge, + line, = self.canvas.axes.plot( + rk, discharge, color=self.color_plot, **self.plot_default_kargs ) + self.line_discharge.append(line) @timer def update(self, ind=None): - self.draw() + if not self._init: + self.draw() + + self.update_data() + + self.update_idle() + + def update_data(self): + if len(self.data) == len(self.line_discharge): + rk = self.data.get_rk() + discharge = self.data.get_discharge() + + line, = self.canvas.axes.plot( + rk, discharge, + color=self.color_plot, + **self.plot_default_kargs + ) + self.line_discharge.append(line) + else: + for line in self.line_discharge: + line.remove() + + self._draw_data() diff --git a/src/View/InitialConditions/Table.py b/src/View/InitialConditions/Table.py index 5461e862105ec08b3bb7cf0a9ad0e17210b246b7..1f6e2e8ac0d77e18c72e3c31ee4006a1f855a2c8 100644 --- a/src/View/InitialConditions/Table.py +++ b/src/View/InitialConditions/Table.py @@ -59,7 +59,7 @@ class ComboBoxDelegate(QItemDelegate): list( map( str, - self._reach.get_kp() + self._reach.get_rk() ) ) ) @@ -107,8 +107,8 @@ class InitialConditionTableModel(PamhyrTableModel): if self._headers[column] is "speed": z = self._lst.get(row)["elevation"] q = self._lst.get(row)["discharge"] - profile = self._reach.reach.get_profiles_from_kp( - self._lst.get(row)["kp"] + profile = self._reach.reach.get_profiles_from_rk( + self._lst.get(row)["rk"] ) if len(profile) >= 1: speed = profile[0].speed(q, z) @@ -258,7 +258,7 @@ class InitialConditionTableModel(PamhyrTableModel): data = list( map( lambda p: [ - p.geometry.kp, + p.geometry.rk, p.get_ts_key(ts, "Q"), p.get_ts_key(ts, "Z"), ], @@ -289,10 +289,10 @@ class InitialConditionTableModel(PamhyrTableModel): self._undo.redo() self.layoutChanged.emit() - def generate(self, generator, param): + def generate(self, generator, param, option): self._undo.push( GenerateCommand( - self._lst, generator, param + self._lst, generator, param, option ) ) self.layoutChanged.emit() diff --git a/src/View/InitialConditions/UndoCommand.py b/src/View/InitialConditions/UndoCommand.py index 4a87c87932d2f8c17048ba2aea247d30665a67ce..fd5e4de28c109ce03ed608278174777b7a280832 100644 --- a/src/View/InitialConditions/UndoCommand.py +++ b/src/View/InitialConditions/UndoCommand.py @@ -106,7 +106,7 @@ class SortCommand(QUndoCommand): def redo(self): self._ics.sort( reverse=self._reverse, - key=lambda x: x["kp"] + key=lambda x: x["rk"] ) if self._indexes is None: self._indexes = list( @@ -172,11 +172,12 @@ class DuplicateCommand(QUndoCommand): class GenerateCommand(QUndoCommand): - def __init__(self, ics, generator, param): + def __init__(self, ics, generator, param, option): QUndoCommand.__init__(self) self._ics = ics self._param = param + self._option = option self._copy = self._ics.data self._generator = generator @@ -185,6 +186,13 @@ class GenerateCommand(QUndoCommand): def redo(self): if self._generator == "growing": - self._ics.generate_growing_constante_height(self._param) + self._ics.generate_growing_constant_depth(self._param, + self._option) elif self._generator == "discharge": - self._ics.generate_discharge(self._param) + self._ics.generate_discharge(self._param, + self._option) + elif self._generator == "height": + self._ics.generate_height(self._param[0], + self._param[1], + self._option, + self._param[2]) diff --git a/src/View/InitialConditions/Window.py b/src/View/InitialConditions/Window.py index 82946e84d2bb6c0b48fe16a8742c77039709742b..58156cb1091503f8442ef75308d763cea9e0cb94 100644 --- a/src/View/InitialConditions/Window.py +++ b/src/View/InitialConditions/Window.py @@ -55,9 +55,10 @@ from View.InitialConditions.Table import ( from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar -from View.InitialConditions.PlotDKP import PlotDKP +from View.InitialConditions.PlotDRK import PlotDRK from View.InitialConditions.PlotDischarge import PlotDischarge from View.InitialConditions.translate import ICTranslate +from View.InitialConditions.DialogDepth import DepthDialog from View.InitialConditions.DialogHeight import HeightDialog from View.InitialConditions.DialogDischarge import DischargeDialog from View.Results.ReadingResultsDialog import ReadingResultsDialog @@ -103,12 +104,13 @@ class InitialConditionsWindow(PamhyrWindow): self.setup_table() self.setup_plot() self.setup_connections() + self.setub_dialogs() self.ui.setWindowTitle(self._title) def setup_table(self): table = self.find(QTableView, f"tableView") - self._delegate_kp = ComboBoxDelegate( + self._delegate_rk = ComboBoxDelegate( reach=self._reach, parent=self ) @@ -117,8 +119,8 @@ class InitialConditionsWindow(PamhyrWindow): reach=self._reach, table_view=table, table_headers=self._trad.get_dict("table_headers"), - editable_headers=["kp", "discharge", "elevation", "height"], - delegates={"kp": self._delegate_kp}, + editable_headers=["rk", "discharge", "elevation", "height"], + delegates={"rk": self._delegate_rk}, data=self._study, undo=self._undo_stack, trad=self._trad @@ -139,7 +141,7 @@ class InitialConditionsWindow(PamhyrWindow): self.plot_layout_1.addWidget(self.toolbar_1) self.plot_layout_1.addWidget(self.canvas_1) - self.plot_1 = PlotDKP( + self.plot_1 = PlotDRK( canvas=self.canvas_1, data=self._ics, trad=self._trad, @@ -174,15 +176,27 @@ class InitialConditionsWindow(PamhyrWindow): .connect(self.import_from_file) self.find(QPushButton, "pushButton_generate_1").clicked.connect( - self.generate_growing_constante_height + self.generate_growing_constant_depth ) self.find(QPushButton, "pushButton_generate_2").clicked.connect( self.generate_discharge ) + self.find(QPushButton, "pushButton_generate_3").clicked.connect( + self.generate_height + ) + self._table.dataChanged.connect(self._update_plot) + def setub_dialogs(self): + self.height_values = [0.0, 0.0, 0.0] + self.height_option = True + self.discharge_value = 0.0 + self.discharge_option = True + self.depth_value = 0.0 + self.depth_option = True + def index_selected_row(self): table = self.find(QTableView, f"tableView") rows = table.selectionModel()\ @@ -194,8 +208,13 @@ class InitialConditionsWindow(PamhyrWindow): return rows[0].row() def update(self): + self._update(propagate=False) + + def _update(self, propagate=True): self._update_plot() - self._propagate_update(key=Modules.INITIAL_CONDITION) + + if propagate: + self._propagate_update(key=Modules.INITIAL_CONDITION) def _update_plot(self): self.plot_1.draw() @@ -244,6 +263,7 @@ class InitialConditionsWindow(PamhyrWindow): workdir = os.path.dirname(self._study.filename) return self.file_dialog( + select_file="ExistingFile", callback=lambda d: self._import_from_file(d[0]), directory=workdir, default_suffix=".BIN", @@ -299,7 +319,7 @@ class InitialConditionsWindow(PamhyrWindow): lambda eic: list( map( lambda k: eic[1][k], - ["kp", "discharge", "elevation"] + ["rk", "discharge", "elevation"] ) ), filter( @@ -340,16 +360,41 @@ class InitialConditionsWindow(PamhyrWindow): self._table.redo() self._update() - def generate_growing_constante_height(self): - dlg = HeightDialog(trad=self._trad, parent=self) + def generate_growing_constant_depth(self): + dlg = DepthDialog(self.depth_value, + self.depth_option, + trad=self._trad, + parent=self) if dlg.exec(): - value = dlg.value - self._table.generate("growing", value) + self.depth_value = dlg.value + self.depth_option = dlg.option + self._table.generate("growing", + self.depth_value, + self.depth_option) self._update() def generate_discharge(self): - dlg = DischargeDialog(trad=self._trad, parent=self) + dlg = DischargeDialog(self.discharge_value, + self.discharge_option, + trad=self._trad, + parent=self) + if dlg.exec(): + self.discharge_value = dlg.value + self.discharge_option = dlg.option + self._table.generate("discharge", + self.discharge_value, + self.discharge_option) + self._update() + + def generate_height(self): + dlg = HeightDialog(self.height_values, + self.height_option, + trad=self._trad, + parent=self) if dlg.exec(): - value = dlg.value - self._table.generate("discharge", value) + self.height_values = dlg.values + self.height_option = dlg.option + self._table.generate("height", + self.height_values, + self.height_option) self._update() diff --git a/src/View/InitialConditions/translate.py b/src/View/InitialConditions/translate.py index 46d5f4040f3b3f44efad35d6aecd8cdf1a809572..3ed1c398935446b06a0451d463e10e4679a2428d 100644 --- a/src/View/InitialConditions/translate.py +++ b/src/View/InitialConditions/translate.py @@ -36,11 +36,11 @@ class ICTranslate(MainTranslate): self._dict["elevation"] = self._dict["unit_elevation"] self._dict["discharge"] = self._dict["unit_discharge"] - self._dict["kp"] = self._dict["unit_kp"] + self._dict["rk"] = self._dict["unit_rk"] self._sub_dict["table_headers"] = { # "name": _translate("InitialCondition", "Name"), - "kp": self._dict["unit_kp"], + "rk": self._dict["unit_rk"], "discharge": self._dict["unit_discharge"], "elevation": self._dict["unit_elevation"], "height": self._dict["unit_height"], diff --git a/src/View/LateralContribution/Edit/Table.py b/src/View/LateralContribution/Edit/Table.py index c0ab3b4450d50c010aa67348e529673a7dd4896a..accf71422c0cada3e13d93ee1a24bdee10cbf501 100644 --- a/src/View/LateralContribution/Edit/Table.py +++ b/src/View/LateralContribution/Edit/Table.py @@ -99,7 +99,7 @@ class TableModel(PamhyrTableModel): logger.info(e) logger.debug(traceback.format_exc()) - self.dataChanged.emit(index, index) + self.update() return True def add(self, row, parent=QModelIndex()): @@ -112,7 +112,7 @@ class TableModel(PamhyrTableModel): ) self.endInsertRows() - self.layoutChanged.emit() + self.update() def delete(self, rows, parent=QModelIndex()): self.beginRemoveRows(parent, rows[0], rows[-1]) @@ -135,7 +135,7 @@ class TableModel(PamhyrTableModel): ) self.layoutAboutToBeChanged.emit() - self.layoutChanged.emit() + self.update() def move_up(self, row, parent=QModelIndex()): if row <= 0: @@ -152,7 +152,7 @@ class TableModel(PamhyrTableModel): ) self.endMoveRows() - self.layoutChanged.emit() + self.update() def move_down(self, index, parent=QModelIndex()): if row > len(self._data): @@ -169,7 +169,7 @@ class TableModel(PamhyrTableModel): ) self.endMoveRows() - self.layoutChanged.emit() + self.update() def paste(self, row, header, data): if len(data) == 0: @@ -190,4 +190,13 @@ class TableModel(PamhyrTableModel): ) self.layoutAboutToBeChanged.emit() + self.update() + + def auto_sort(self): + self.layoutAboutToBeChanged.emit() + self._data.sort(key=lambda x: x[0]) + self.layoutAboutToBeChanged.emit() + + def update(self): + # self.auto_sort() self.layoutChanged.emit() diff --git a/src/View/LateralContribution/Edit/Window.py b/src/View/LateralContribution/Edit/Window.py index 53c774cb51f6bfcb3a3c38c370d79a964010ac61..1b5beec78d564673879ec03c0969c75047144694 100644 --- a/src/View/LateralContribution/Edit/Window.py +++ b/src/View/LateralContribution/Edit/Window.py @@ -137,6 +137,7 @@ class EditLateralContributionWindow(PamhyrWindow): self.find(QAction, "action_sort").triggered.connect(self.sort) self._table.dataChanged.connect(self.update) + self._table.layoutChanged.connect(self.update) def update(self): self.plot.update() diff --git a/src/View/LateralContribution/Edit/translate.py b/src/View/LateralContribution/Edit/translate.py index eeb889304838a4b75339bfdcb481d6de96e55b4c..14b23772fe2c8df691e3c5ab559eb9e2d4b67813 100644 --- a/src/View/LateralContribution/Edit/translate.py +++ b/src/View/LateralContribution/Edit/translate.py @@ -28,7 +28,7 @@ class LCETranslate(LCTranslate): def __init__(self): super(LCETranslate, self).__init__() self._dict["Edit lateral contribution"] = _translate( - "LateralContribution", "Edit lateral contribution" + "LateralContribution", "Edit lateral sources" ) self._sub_dict["table_headers"] = { diff --git a/src/View/LateralContribution/PlotXY.py b/src/View/LateralContribution/PlotXY.py new file mode 100644 index 0000000000000000000000000000000000000000..17044b2be4e0da65bdf7b50e932363e3cc0c5434 --- /dev/null +++ b/src/View/LateralContribution/PlotXY.py @@ -0,0 +1,260 @@ +# PlotXY.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 tools import timer, trace +from View.Tools.PamhyrPlot import PamhyrPlot +from matplotlib import collections +import numpy as np + +from PyQt5.QtCore import ( + QCoreApplication, Qt, QItemSelectionModel, + QItemSelection, QItemSelectionRange, +) +from PyQt5.QtWidgets import QApplication, QTableView + +_translate = QCoreApplication.translate + + +class PlotXY(PamhyrPlot): + def __init__(self, canvas=None, trad=None, data=None, toolbar=None, + table=None, parent=None): + super(PlotXY, self).__init__( + canvas=canvas, + trad=trad, + data=data, + toolbar=toolbar, + table=table, + parent=parent + ) + + self.line_xy = [] + self.line_gl = [] + + self.label_x = self._trad["x"] + self.label_y = self._trad["y"] + + self.before_plot_selected = None + self.plot_selected = None + self.after_plot_selected = None + self.parent = parent + self.line_xy_collection = None + self._table = table + self._colors = [] + self._style = [] + + def onpick(self, event): + if event.mouseevent.inaxes != self.canvas.axes: + return + if event.mouseevent.button.value not in [1, 3]: + return + + closest = self._closest_section(event) + t = self.parent.current_tab() + tableView = self.parent.find(QTableView, f"tableView_{t}") + table = self.parent._table[t] + index = tableView.selectedIndexes() + rk = self.data.get_rk() + if self.parent._table is not None: + if event.mouseevent.button.value == 1: + table.setData(index[3], rk[closest[0]]) + if event.mouseevent.button.value == 3: + table.setData(index[4], rk[closest[0]]) + + return + + def _closest_section(self, event): + axes = self.canvas.axes + mx = event.mouseevent.xdata + my = event.mouseevent.ydata + bx, by = axes.get_xlim(), axes.get_ylim() + ratio = (bx[0] - bx[1]) / (by[0] - by[1]) + + segments = event.artist.get_segments() + ind = event.ind + + points = [] + for i in ind: + points = points + [[i, j] for j in segments[i]] + + def dist_mouse(point): + x, y = point[1] + d2 = (((mx - x) / ratio) ** 2) + ((my - y) ** 2) + return d2 + + closest = min( + points, key=dist_mouse + ) + + return closest + + @timer + def draw(self): + self.init_axes() + + if self.data is None: + self.idle() + return + + if self.data.number_profiles == 0: + self._init = False + self.idle() + return + + self.draw_xy() + self.draw_lr() + self.draw_gl() + + self.idle() + self._init = True + + def draw_xy(self): + self.line_xy = [] + for xy in zip(self.data.get_x(), self.data.get_y()): + self.line_xy.append(np.column_stack(xy)) + + self._colors, self._style = self.color_hightlight() + self.line_xy_collection = collections.LineCollection( + self.line_xy, + colors=self._colors, + linestyle=self._style, + picker=10 + ) + self.canvas.axes.add_collection(self.line_xy_collection) + + def color_hightlight(self): + rk_min, rk_max = (-1, -1) + if self._highlight_data is not None: + rk_min, rk_max = self._highlight_data + + colors = [self.color_plot for row in range(len(self._data))] + for i, rk in enumerate(self.data.get_rk_complete_profiles()): + if rk_min <= rk <= rk_max: + colors[i] = self.color_plot_current + style = ["-" for row in range(len(self._data))] + + return colors, style + + def draw_lr(self): + lx = [] + ly = [] + rx = [] + ry = [] + + self.line_lr = [] + for x, y in zip(self.data.get_x(), + self.data.get_y()): + lx.append(x[0]) + ly.append(y[0]) + + rx.append(x[-1]) + ry.append(y[-1]) + + line = self.canvas.axes.plot( + lx, ly, + color=self.color_plot_river_bottom, + linestyle="dotted", + lw=1., + ) + self.line_lr.append(line) + + line = self.canvas.axes.plot( + rx, ry, + color=self.color_plot_river_bottom, + linestyle="dotted", + lw=1., + ) + self.line_lr.append(line) + + def draw_gl(self): + x_complete = self.data.get_guidelines_x() + y_complete = self.data.get_guidelines_y() + + ind = 0 + self.line_gl = [] + for x, y in zip(x_complete, y_complete): + line = self.canvas.axes.plot( + x, y, color=self.colors[ind % len(self.colors)], + linestyle=self.linestyle[ind // len(self.colors)] + ) + self.line_gl.append(line) + ind += 1 + + @timer + def update(self): + if not self._init: + self.draw() + return + + if self.data is None: + return + + self.update_lr() + self.update_gl() + self.update_current() + + self.update_idle() + + def update_gl(self): + self.data.compute_guidelines() + x_complete = list(self.data.get_guidelines_x()) + y_complete = list(self.data.get_guidelines_y()) + +# TODO comprendre à quoi sert ce bout de code +# ========> +# for i in range(self.data.number_profiles): +# if i < len(self.line_xy): +# self.line_xy[i][0].set_data( +# self.data.profile(i).x(), +# self.data.profile(i).y() +# ) +# else: +# self.line_xy.append( +# self.canvas.axes.plot( +# self.data.profile(i).x(), +# self.data.profile(i).y(), +# color='r', +# **self.plot_default_kargs +# ) +# ) +# <======== + + for i in range(len(x_complete)): + if i < len(self.line_gl): + self.line_gl[i][0].set_data( + x_complete[i], + y_complete[i] + ) + else: + self.line_gl.append( + self.canvas.axes.plot( + x_complete[i], + y_complete[i] + ) + ) + + def update_current(self): + if self._current_data_update: + self._colors, self._style = self.color_hightlight() + self.line_xy_collection.set_colors(self._colors) + self.line_xy_collection.set_linestyle(self._style) + + def update_lr(self): + for line in self.line_lr: + line[0].remove() + + self.draw_lr() diff --git a/src/View/LateralContribution/Table.py b/src/View/LateralContribution/Table.py index c6684f6c7ff34b5bf7f274b8f2d690667d92de3a..e5c533dd361d3bd8dddb7e8d3b1f36b4500d6130 100644 --- a/src/View/LateralContribution/Table.py +++ b/src/View/LateralContribution/Table.py @@ -86,7 +86,7 @@ class ComboBoxDelegate(QItemDelegate): ) ) self.editor.addItems(lst) - elif self._mode == "kp": + elif self._mode == "rk": if self._data is None: self.editor.addItems( ["0"] @@ -94,7 +94,7 @@ class ComboBoxDelegate(QItemDelegate): else: self.editor.addItems( list( - map(str, self._data.reach.get_kp()) + map(str, self._data.reach.get_rk()) ) ) else: @@ -154,10 +154,10 @@ class TableModel(PamhyrTableModel): if n is None: return self._trad['not_associated'] return n.name - elif self._headers[column] == "begin_kp": - return str(self._lst.get(self._tab, row).begin_kp) - elif self._headers[column] == "end_kp": - return str(self._lst.get(self._tab, row).end_kp) + elif self._headers[column] == "begin_rk": + return str(self._lst.get(self._tab, row).begin_rk) + elif self._headers[column] == "end_rk": + return str(self._lst.get(self._tab, row).end_rk) return QVariant() @@ -189,13 +189,13 @@ class TableModel(PamhyrTableModel): self._lst, self._tab, row, self._data.edge(value) ) ) - elif self._headers[column] == "begin_kp": + elif self._headers[column] == "begin_rk": self._undo.push( SetBeginCommand( self._lst, self._tab, row, value ) ) - elif self._headers[column] == "end_kp": + elif self._headers[column] == "end_rk": self._undo.push( SetEndCommand( self._lst, self._tab, row, value diff --git a/src/View/LateralContribution/UndoCommand.py b/src/View/LateralContribution/UndoCommand.py index aeea4e1243136b00fcba5e8ee6d41a7a37cb6f2f..e101f513f119e769a436f62a1d284c4fb44ecb7e 100644 --- a/src/View/LateralContribution/UndoCommand.py +++ b/src/View/LateralContribution/UndoCommand.py @@ -53,14 +53,14 @@ class SetBeginCommand(QUndoCommand): self._lcs = lcs self._tab = tab self._index = index - self._old = self._lcs.get(self._tab, self._index).begin_kp + self._old = self._lcs.get(self._tab, self._index).begin_rk self._new = float(new_value) def undo(self): - self._lcs.get(self._tab, self._index).begin_kp = float(self._old) + self._lcs.get(self._tab, self._index).begin_rk = float(self._old) def redo(self): - self._lcs.get(self._tab, self._index).begin_kp = float(self._new) + self._lcs.get(self._tab, self._index).begin_rk = float(self._new) class SetEndCommand(QUndoCommand): @@ -70,14 +70,14 @@ class SetEndCommand(QUndoCommand): self._lcs = lcs self._tab = tab self._index = index - self._old = self._lcs.get(self._tab, self._index).end_kp + self._old = self._lcs.get(self._tab, self._index).end_rk self._new = float(new_value) def undo(self): - self._lcs.get(self._tab, self._index).end_kp = float(self._old) + self._lcs.get(self._tab, self._index).end_rk = float(self._old) def redo(self): - self._lcs.get(self._tab, self._index).end_kp = float(self._new) + self._lcs.get(self._tab, self._index).end_rk = float(self._new) class SetEdgeCommand(QUndoCommand): diff --git a/src/View/LateralContribution/Window.py b/src/View/LateralContribution/Window.py index 879955bbb72c7657a903d96c51e5c5ad13dd442c..db8b2791eafbe9010a8daec7f4a986278ec2abb6 100644 --- a/src/View/LateralContribution/Window.py +++ b/src/View/LateralContribution/Window.py @@ -54,7 +54,7 @@ from View.LateralContribution.Table import ( ) from View.Tools.Plot.PamhyrCanvas import MplCanvas -from View.Geometry.PlotXY import PlotXY +from View.LateralContribution.PlotXY import PlotXY from View.LateralContribution.translate import ( LC_types, LCTranslate, ) @@ -83,12 +83,13 @@ class LateralContributionWindow(PamhyrWindow): self.setup_table() self.setup_graph() + self.tabs = self.find(QTabWidget, "tabWidget") self.setup_connections() def setup_table(self): self._table = {} - self._delegate_kp = [] + self._delegate_rk = [] for t in ["liquid", "solid", "suspenssion"]: self._delegate_type = ComboBoxDelegate( @@ -99,14 +100,14 @@ class LateralContributionWindow(PamhyrWindow): parent=self ) - delegate_kp = ComboBoxDelegate( + delegate_rk = ComboBoxDelegate( data=None, - mode="kp", + mode="rk", tab=t, trad=self._trad, parent=self ) - self._delegate_kp.append(delegate_kp) + self._delegate_rk.append(delegate_rk) self._delegate_edge = ComboBoxDelegate( data=self._study.river, @@ -124,8 +125,8 @@ class LateralContributionWindow(PamhyrWindow): delegates={ "type": self._delegate_type, "edge": self._delegate_edge, - "begin_kp": delegate_kp, - "end_kp": delegate_kp, + "begin_rk": delegate_rk, + "end_rk": delegate_rk, }, data=self._study.river, undo=self._undo_stack, @@ -148,6 +149,7 @@ class LateralContributionWindow(PamhyrWindow): data=None, trad=self._trad, toolbar=None, + parent=self ) def setup_connections(self): @@ -156,6 +158,8 @@ class LateralContributionWindow(PamhyrWindow): self.find(QAction, "action_edit").triggered.connect(self.edit) self.find(QAction, "action_sort").triggered.connect(self.sort) + self.tabs.currentChanged.connect(self._set_current_reach) + for t in ["liquid", "solid", "suspenssion"]: table = self.find(QTableView, f"tableView_{t}") table.selectionModel()\ @@ -206,9 +210,9 @@ class LateralContributionWindow(PamhyrWindow): if edge: data = edge.reach lc = self._lcs.get(tab, rows[0]) - highlight = (lc.begin_kp, lc.end_kp) + highlight = (lc.begin_rk, lc.end_rk) - for delegate in self._delegate_kp: + for delegate in self._delegate_rk: delegate.data = edge self.plot = PlotXY( @@ -216,6 +220,7 @@ class LateralContributionWindow(PamhyrWindow): data=data, trad=self._trad, toolbar=None, + parent=self ) self.plot.highlight = highlight self.plot.update() diff --git a/src/View/LateralContribution/translate.py b/src/View/LateralContribution/translate.py index 145e137f826a345ef8ad6ac0fdf51b4726562958..e3de3d8762b9a887f1d35955639d2858e834e7fd 100644 --- a/src/View/LateralContribution/translate.py +++ b/src/View/LateralContribution/translate.py @@ -39,12 +39,12 @@ class LCTranslate(MainTranslate): super(LCTranslate, self).__init__() self._dict["Lateral contribution"] = _translate( - "LateralContribution", "Lateral contribution" + "LateralContribution", "Lateral sources" ) self._sub_dict["long_types"] = { "ND": self._dict["not_defined"], - "LC": _translate("LateralContribution", "Lateral contribution"), + "LC": _translate("LateralContribution", "Lateral sources"), "RA": _translate("LateralContribution", "Rain"), "EV": _translate("LateralContribution", "Evaporation"), } @@ -57,6 +57,6 @@ class LCTranslate(MainTranslate): "name": self._dict["name"], "type": self._dict["type"], "edge": self._dict["reach"], - "begin_kp": _translate("LateralContribution", "Begin kp (m)"), - "end_kp": _translate("LateralContribution", "End kp (m)") + "begin_rk": _translate("LateralContribution", "Start (m)"), + "end_rk": _translate("LateralContribution", "End (m)") } diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py index d9b9b45bd79e0f6d65588ead647ff335377a1e61..2272b86d665d6544c10331fd5a5a6b7e96b4cdd0 100644 --- a/src/View/MainWindow.py +++ b/src/View/MainWindow.py @@ -152,10 +152,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): # Translate self._trad = MainTranslate() - # Results - self._last_solver = None - self._last_results = None - # UI self.ui = loadUi( os.path.join(os.path.dirname(__file__), "ui", "MainWindow.ui"), @@ -464,8 +460,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): if Modules.CONFIG in keys: self._do_update_config() - if Modules.STUDY in keys: - self._tab_widget_info.update() + self._do_propagate_update_info_tab(keys) logger.debug(f"Propagation of {keys}") for _, window in self.sub_win_list: @@ -474,6 +469,18 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): self._tab_widget_checker.update(modules=keys) + def _do_propagate_update_info_tab(self, keys): + modules = Modules.modelling_list() + modules.append(Modules.STUDY) + + has_info_mod = reduce( + lambda acc, m: acc or (m in keys), + modules, False + ) + + if has_info_mod: + self._tab_widget_info.update() + def _do_propagate_update_rec(self, window, keys): for _, win in window.sub_win_list: win._propagated_update(key=keys) @@ -584,9 +591,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): Nothing """ if self._study.filename is None or self._study.filename == "": + options = QFileDialog.Options() + options |= QFileDialog.DontUseNativeDialog + file_name, _ = QFileDialog.getSaveFileName( self, "Save File", - "", "Pamhyr(*.pamhyr)" + "", "Pamhyr(*.pamhyr)", + options=options, ) if file_name.rsplit(".", 1)[-1] == "pamhyr": @@ -635,9 +646,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): Returns: Nothing """ + options = QFileDialog.Options() + options |= QFileDialog.DontUseNativeDialog + file_name, _ = QFileDialog.getSaveFileName( self, "Save File", - "", "Pamhyr(*.pamhyr)" + "", "Pamhyr(*.pamhyr)", + options=options, ) logger.debug(f"Save study as : {repr(file_name)}") @@ -981,7 +996,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): Nothing """ if self._study is None: - dialog = QFileDialog(self) + options = QFileDialog.Options() + options |= QFileDialog.DontUseNativeDialog + + dialog = QFileDialog(self, options=options) dialog.setFileMode(QFileDialog.FileMode.ExistingFile) dialog.setDefaultSuffix(".pamhyr") # dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden) @@ -1481,7 +1499,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): if self._study.filename == "": return - dialog = QFileDialog(self) + options = QFileDialog.Options() + options |= QFileDialog.DontUseNativeDialog + + dialog = QFileDialog(self, options=options) + dialog.setFileMode(QFileDialog.FileMode.ExistingFile) dialog.setDefaultSuffix(".BIN") # dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden) diff --git a/src/View/MainWindowTabInfo.py b/src/View/MainWindowTabInfo.py index 2d77234a519355c0fd3c73280ebb6db54bc79dae..f655d7999dc1857374b5a774c7cad37fa21890be 100644 --- a/src/View/MainWindowTabInfo.py +++ b/src/View/MainWindowTabInfo.py @@ -191,7 +191,7 @@ class WidgetInfo(PamhyrWidget): n_na_hs = 0 for h in hs.lst: - if h.input_reach is not None and h.input_kp is not None: + if h.input_reach is not None and h.input_rk is not None: n_hs += 1 else: n_na_hs += 1 diff --git a/src/View/Network/GraphWidget.py b/src/View/Network/GraphWidget.py index 2eb77ced1e26062dad760ab70224ec205b19e070..5b55d764cd13f508ae4f9eb54a83c76b24a40ffe 100644 --- a/src/View/Network/GraphWidget.py +++ b/src/View/Network/GraphWidget.py @@ -73,7 +73,10 @@ class NodeItem(QGraphicsItem): def shape(self): path = QPainterPath() - path.addEllipse(-10, -10, 20, 20) + if self.graph.graph._reservoir.get_assoc_to_node(self.node) is None: + path.addEllipse(-10, -10, 20, 20) + else: + path.addRect(-10, -10, 20, 20) return path @timer @@ -95,7 +98,10 @@ class NodeItem(QGraphicsItem): color = Qt.green painter.setBrush(QBrush(color)) - painter.drawEllipse(-10, -10, 20, 20) + if self.graph.graph._reservoir.get_assoc_to_node(self.node) is None: + painter.drawEllipse(-10, -10, 20, 20) + else: + painter.drawRect(-10, -10, 20, 20) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionHasChanged: @@ -610,11 +616,15 @@ class GraphWidget(QGraphicsView): def add_node_reservoir(self, node): self.parent.add_node_reservoir(node) + self.display_update() def del_node_reservoir(self, node): self.parent.del_node_reservoir(node) + self.changeNode.emit(self.sender()) + self.display_update() def edit_node_reservoir(self, node): + self.changeNode.emit(self.sender()) self.parent.edit_node_reservoir(node) def enable_edge(self, edge, prev): @@ -999,12 +1009,20 @@ class GraphWidget(QGraphicsView): items = self.items(event.pos()) # Select current menu + while len(items) > 0: + if type(items[0]) in [NodeItem, EdgeItem]: + break + else: + items = items[1:] + if len(items) == 0: m_type = DefaultMenu elif type(items[0]) is NodeItem: m_type = NodeMenu elif type(items[0]) is EdgeItem: m_type = EdgeMenu + else: + m_type = DefaultMenu # Create and exec menu m = m_type( @@ -1012,3 +1030,4 @@ class GraphWidget(QGraphicsView): graph=self.graph, trad=self._trad, parent=self ) m.run() + self.clicked = False diff --git a/src/View/Network/translate.py b/src/View/Network/translate.py index 1b2b8c0c361380969d7f7fd5806e76e8de48ece5..ea999db2ae4b7402c617b6eceede21a798b6eff7 100644 --- a/src/View/Network/translate.py +++ b/src/View/Network/translate.py @@ -54,10 +54,12 @@ class NetworkTranslate(MainTranslate): self._sub_dict["table_headers_node"] = { "name": self._dict['name'], "type": self._dict['type'], + "id": _translate("Network", "Index"), } self._sub_dict["table_headers_edge"] = { "name": self._dict['name'], "node1": _translate("Network", "Source node"), "node2": _translate("Network", "Destination node"), + "id": _translate("Network", "Index"), } diff --git a/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py b/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py index 796934fb7a1b2cef8627d71fee56d56ffbb8fc45..e88e538644785cb81dfcc1e1969761f2add079f2 100644 --- a/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py +++ b/src/View/Results/CustomPlot/CustomPlotValuesSelectionDialog.py @@ -95,4 +95,5 @@ class CustomPlotValuesSelectionDialog(PamhyrDialog): ) self.value = x, y + super().accept() diff --git a/src/View/Results/CustomPlot/Plot.py b/src/View/Results/CustomPlot/Plot.py index 91770042de361ba0c094d14f74267c11c7397a87..a3073c7a1bf4dd2b21cd737793f12f574cae6239 100644 --- a/src/View/Results/CustomPlot/Plot.py +++ b/src/View/Results/CustomPlot/Plot.py @@ -20,6 +20,7 @@ import logging from functools import reduce from datetime import datetime +from numpy import sqrt from tools import timer from View.Tools.PamhyrPlot import PamhyrPlot @@ -32,6 +33,11 @@ unit = { "elevation": "0-meter", "water_elevation": "0-meter", "discharge": "1-m3s", + "velocity": "2-ms", + "depth": "3-meter", + "mean_depth": "3-meter", + "froude": "4-dimensionless", + "wet_area": "5-m2", } @@ -71,78 +77,159 @@ class CustomPlot(PamhyrPlot): self._axes = {} - def _draw_kp(self): + def _draw_rk(self): results = self.data reach = results.river.reach(self._reach) - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() + q = list( + map( + lambda p: p.get_ts_key(self._timestamp, "Q"), + reach.profiles + ) + ) + z = list( + map( + lambda p: p.get_ts_key(self._timestamp, "Z"), + reach.profiles + ) + ) - # self.canvas.axes.set_xlim( - # left=min(kp), right=max(kp) - # ) - - meter_axes = self.canvas.axes - m3S_axes = self.canvas.axes - if "0-meter" in self._y_axes and "1-m3s" in self._y_axes: - m3s_axes = self._axes["1-m3s"] + shift = 0 + compt = 0 + for ax in sorted(self._axes): + if compt == 0: + self._axes[ax].spines['left'].set_position(('outward', shift)) + compt += 1 + else: + self._axes[ax].spines['right'].set_position(('outward', shift)) + shift += 60 lines = {} if "elevation" in self._y: - # meter_axes.set_ylim( - # bottom=min(0, min(z_min)), - # top=max(z_min) + 1 - # ) - line = meter_axes.plot( - kp, z_min, + ax = self._axes[unit["elevation"]] + line = ax.plot( + rk, z_min, color='grey', lw=1., ) lines["elevation"] = line if "water_elevation" in self._y: - # Water elevation - water_z = list( - map( - lambda p: p.get_ts_key(self._timestamp, "Z"), - reach.profiles - ) - ) - - # meter_axes.set_ylim( - # bottom=min(0, min(z_min)), - # top=max(water_z) + 1 - # ) - line = meter_axes.plot( - kp, water_z, lw=1., + ax = self._axes[unit["water_elevation"]] + line = ax.plot( + rk, z, lw=1., color='blue', ) lines["water_elevation"] = line if "elevation" in self._y: - meter_axes.fill_between( - kp, z_min, water_z, + ax.fill_between( + rk, z_min, z, color='blue', alpha=0.5, interpolate=True ) if "discharge" in self._y: - q = list( + + ax = self._axes[unit["discharge"]] + line = ax.plot( + rk, q, lw=1., + color='r', + ) + lines["discharge"] = line + + if "velocity" in self._y: + + ax = self._axes[unit["velocity"]] + v = list( map( - lambda p: p.get_ts_key(self._timestamp, "Q"), + lambda p: p.geometry.speed( + p.get_ts_key(self._timestamp, "Q"), + p.get_ts_key(self._timestamp, "Z")), reach.profiles ) ) - # m3s_axes.set_ylim( - # bottom=min(0, min(q)), - # top=max(q) + 1 - # ) + line = ax.plot( + rk, v, lw=1., + color='g', + ) + lines["velocity"] = line - line = m3s_axes.plot( - kp, q, lw=1., - color='r', + if "depth" in self._y: + + ax = self._axes[unit["depth"]] + d = list( + map( + lambda p: p.geometry.max_water_depth( + p.get_ts_key(self._timestamp, "Z")), + reach.profiles + ) ) - lines["discharge"] = line + line = ax.plot( + rk, d, + color='brown', lw=1., + ) + lines["depth"] = line + + if "mean_depth" in self._y: + + ax = self._axes[unit["mean_depth"]] + d = list( + map( + lambda p: p.geometry.mean_water_depth( + p.get_ts_key(self._timestamp, "Z")), + reach.profiles + ) + ) + + line = ax.plot( + rk, d, + color='orange', lw=1., + ) + lines["mean_depth"] = line + + if "froude" in self._y: + + ax = self._axes[unit["froude"]] + fr = list( + map( + lambda p: + p.geometry.speed( + p.get_ts_key(self._timestamp, "Q"), + p.get_ts_key(self._timestamp, "Z")) / + sqrt(9.81 * ( + p.geometry.wet_area( + p.get_ts_key(self._timestamp, "Z")) / + p.geometry.wet_width( + p.get_ts_key(self._timestamp, "Z")) + )), + reach.profiles + ) + ) + + line = ax.plot( + rk, fr, color='black', linestyle='--', lw=1., + ) + lines["froude"] = line + + if "wet_area" in self._y: + + ax = self._axes[unit["wet_area"]] + d = list( + map( + lambda p: p.geometry.wet_area( + p.get_ts_key(self._timestamp, "Z")), + reach.profiles + ) + ) + + line = ax.plot( + rk, d, + color='blue', linestyle='--', lw=1., + ) + lines["wet_area"] = line # Legend lns = reduce( @@ -151,7 +238,7 @@ class CustomPlot(PamhyrPlot): [] ) labs = list(map(lambda line: self._trad[line], lines)) - self.canvas.axes.legend(lns, labs, loc="lower left") + self.canvas.axes.legend(lns, labs, loc="best") def _customize_x_axes_time(self, ts, mode="time"): # Custom time display @@ -198,79 +285,137 @@ class CustomPlot(PamhyrPlot): reach = results.river.reach(self._reach) profile = reach.profile(self._profile) - meter_axes = self.canvas.axes - m3S_axes = self.canvas.axes - if "0-meter" in self._y_axes and "1-m3s" in self._y_axes: - m3s_axes = self._axes["1-m3s"] + shift = 0 + compt = 0 + for ax in sorted(self._axes): + if compt == 0: + self._axes[ax].spines['left'].set_position(('outward', shift)) + compt += 1 + else: + self._axes[ax].spines['right'].set_position(('outward', shift)) + shift += 60 ts = list(results.get("timestamps")) ts.sort() - # self.canvas.axes.set_xlim( - # left=min(ts), right=max(ts) - # ) + q = profile.get_key("Q") + z = profile.get_key("Z") + z_min = profile.geometry.z_min() + ts_z_min = list( + map( + lambda ts: z_min, + ts + ) + ) - x = ts lines = {} if "elevation" in self._y: # Z min is constant in time - z_min = profile.geometry.z_min() - ts_z_min = list( - map( - lambda ts: z_min, - ts - ) - ) - line = meter_axes.plot( + ax = self._axes[unit["elevation"]] + + line = ax.plot( ts, ts_z_min, color='grey', lw=1. ) lines["elevation"] = line if "water_elevation" in self._y: - # Water elevation - z = profile.get_key("Z") - - # meter_axes.set_ylim( - # bottom=min(0, min(z)), - # top=max(z) + 1 - # ) - line = meter_axes.plot( + ax = self._axes[unit["water_elevation"]] + line = ax.plot( ts, z, lw=1., color='b', ) lines["water_elevation"] = line if "elevation" in self._y: - z_min = profile.geometry.z_min() - ts_z_min = list( - map( - lambda ts: z_min, - ts - ) - ) - meter_axes.fill_between( + ax.fill_between( ts, ts_z_min, z, color='blue', alpha=0.5, interpolate=True ) if "discharge" in self._y: - q = profile.get_key("Q") - - # m3s_axes.set_ylim( - # bottom=min(0, min(q)), - # top=max(q) + 1 - # ) - line = m3s_axes.plot( + ax = self._axes[unit["discharge"]] + line = ax.plot( ts, q, lw=1., color='r', ) lines["discharge"] = line + if "velocity" in self._y: + + ax = self._axes[unit["velocity"]] + v = list( + map( + lambda q, z: profile.geometry.speed(q, z), + q, z + ) + ) + + line = ax.plot( + ts, v, lw=1., + color='g', + ) + lines["velocity"] = line + + if "depth" in self._y: + + ax = self._axes[unit["depth"]] + d = list( + map(lambda z: profile.geometry.max_water_depth(z), z) + ) + + line = ax.plot( + ts, d, + color='brown', lw=1., + ) + lines["depth"] = line + + if "mean_depth" in self._y: + + ax = self._axes[unit["mean_depth"]] + d = list( + map(lambda z: profile.geometry.mean_water_depth(z), z) + ) + + line = ax.plot( + ts, d, + color='orange', lw=1., + ) + lines["depth"] = line + + if "froude" in self._y: + + ax = self._axes[unit["froude"]] + d = list( + map(lambda z, q: + profile.geometry.speed(q, z) / + sqrt(9.81 * ( + profile.geometry.wet_area(z) / + profile.geometry.wet_width(z)) + ), z, q) + ) + + line = ax.plot( + ts, d, color='black', linestyle='--', lw=1., + ) + lines["froude"] = line + + if "wet_area" in self._y: + + ax = self._axes[unit["wet_area"]] + d = list( + map(lambda z: profile.geometry.wet_area(z), z) + ) + + line = ax.plot( + ts, d, color='blue', linestyle='--', lw=1., + ) + lines["wet_area"] = line + self._customize_x_axes_time(ts) # Legend @@ -280,7 +425,7 @@ class CustomPlot(PamhyrPlot): [] ) labs = list(map(lambda line: self._trad[line], lines)) - self.canvas.axes.legend(lns, labs, loc="lower left") + self.canvas.axes.legend(lns, labs, loc="best") @timer def draw(self): @@ -300,6 +445,7 @@ class CustomPlot(PamhyrPlot): color='black', fontsize=10 ) + self._axes[self._y_axes[0]] = self.canvas.axes for axes in self._y_axes[1:]: if axes in self._axes: self._axes[axes].clear() @@ -312,12 +458,26 @@ class CustomPlot(PamhyrPlot): ) self._axes[axes] = ax_new - if self._x == "kp": - self._draw_kp() + if self._x == "rk": + self._draw_rk() elif self._x == "time": self._draw_time() + if self._x == "rk": + reach = self.data.river.reach(self._reach) + profile = reach.profile(self._profile) + x = profile.rk + elif self._x == "time": + x = self._timestamp + + self._current, = self.canvas.axes.plot( + [x, x], + self.canvas.axes.get_ylim(), + # label=self.label_timestamp, + color='grey', + linestyle="dashed", + lw=1., + ) - self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() if self.toolbar is not None: self.toolbar.update() @@ -326,6 +486,7 @@ class CustomPlot(PamhyrPlot): def update(self): if not self._init: self.draw() + self.draw_current() return def set_reach(self, reach_id): @@ -337,11 +498,25 @@ class CustomPlot(PamhyrPlot): def set_profile(self, profile_id): self._profile = profile_id - if self._x != "kp": + if self._x != "rk": self.update() + else: + self.draw_current() def set_timestamp(self, timestamp): self._timestamp = timestamp if self._x != "time": self.update() + else: + self.draw_current() + + def draw_current(self): + if self._x == "rk": + reach = self.data.river.reach(self._reach) + profile = reach.profile(self._profile) + x = profile.rk + elif self._x == "time": + x = self._timestamp + self._current.set_data([x, x], self.canvas.axes.get_ylim()) + self.canvas.figure.canvas.draw_idle() diff --git a/src/View/Results/CustomPlot/Translate.py b/src/View/Results/CustomPlot/Translate.py index 6c4236b8603f5a555cebba9adb0c1ad9db757f83..abc6b244b4c854ab3c42df17c28b939bd90ae338 100644 --- a/src/View/Results/CustomPlot/Translate.py +++ b/src/View/Results/CustomPlot/Translate.py @@ -28,34 +28,34 @@ class CustomPlotTranslate(ResultsTranslate): super(CustomPlotTranslate, self).__init__() self._dict["Custom Plot Selection"] = _translate( - "CustomPlot", "Custom Plot Selection" + "CustomPlot", "Custom plot selection" ) # Value type self._dict['time'] = self._dict["unit_time_s"] - self._dict['kp'] = self._dict["unit_kp"] + self._dict['rk'] = self._dict["unit_rk"] self._dict['water_elevation'] = self._dict["unit_water_elevation"] self._dict['discharge'] = self._dict["unit_discharge"] self._dict['elevation'] = _translate( - "CustomPlot", "Bed load elevation (m)" + "CustomPlot", "Bed elevation (m)" ) + self._dict['velocity'] = self._dict["unit_speed"] + self._dict['width'] = self._dict["unit_width"] + self._dict['max_depth'] = self._dict["unit_max_height"] + self._dict['mean_depth'] = self._dict["unit_mean_height"] + self._dict['wet_area'] = self._dict["unit_wet_area"] + self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"] + self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"] + self._dict['froude'] = self._dict["unit_froude"] # Unit corresponding long name (plot axes display) self._dict['0-meter'] = _translate( - "CustomPlot", "Bed load elevation (m)" + "CustomPlot", "Bed elevation (m)" ) self._dict['1-m3s'] = self._dict["unit_discharge"] - - # SubDict - - self._sub_dict["values_x"] = { - "kp": self._dict["kp"], - "time": self._dict["time"], - } - self._sub_dict["values_y"] = { - "elevation": self._dict["elevation"], - "water_elevation": self._dict["water_elevation"], - "discharge": self._dict["discharge"], - } + self._dict['2-ms'] = self._dict["unit_speed"] + self._dict['3-meter'] = self._dict["unit_height"] + self._dict['4-dimensionless'] = self._dict["unit_froude"] + self._dict['5-m2'] = self._dict["wet_area"] diff --git a/src/View/Results/PlotAC.py b/src/View/Results/PlotAC.py index 5da1edd1da9e600353b48df335b2f1111908a999..98e86cfc4662d5ca4523e1b48c413ff68d1aba90 100644 --- a/src/View/Results/PlotAC.py +++ b/src/View/Results/PlotAC.py @@ -87,7 +87,7 @@ class PlotAC(PamhyrPlot): x = profile.geometry.get_station() z = profile.geometry.z() - self.line_kp, = self.canvas.axes.plot( + self.line_rk, = self.canvas.axes.plot( x, z, linestyle="solid", lw=1.5, @@ -98,7 +98,7 @@ class PlotAC(PamhyrPlot): def draw_water_elevation(self, reach, profile): x = profile.geometry.get_station() z = profile.geometry.z() - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() water_z = profile.get_ts_key(self._current_timestamp, "Z") self.water, = self.canvas.axes.plot( @@ -118,7 +118,7 @@ class PlotAC(PamhyrPlot): def draw_water_elevation_max(self, reach, profile): x = profile.geometry.get_station() z = profile.geometry.z() - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() water_z = max(profile.get_key("Z")) self.water_max, = self.canvas.axes.plot( @@ -164,7 +164,7 @@ class PlotAC(PamhyrPlot): self.update_idle() def update_river_bottom(self, reach, profile, x, z): - self.line_kp.set_data(x, z) + self.line_rk.set_data(x, z) def update_water(self, reach, profile, x, z): water_z = profile.get_ts_key(self._current_timestamp, "Z") diff --git a/src/View/Results/PlotH.py b/src/View/Results/PlotH.py index 2ac499e4a00e5abb88467142ddb6859e68a0de76..5920d837d40124849920cae26a619eea6d1be431 100644 --- a/src/View/Results/PlotH.py +++ b/src/View/Results/PlotH.py @@ -88,13 +88,14 @@ class PlotH(PamhyrPlot): self.draw_max(reach) self.draw_data(reach, profile) - self.draw_current(reach, profile) + self.draw_current() self.set_ticks_time_formater() self.enable_legend() self.idle() + self.update_current() self._init = True def draw_data(self, reach, profile): @@ -111,21 +112,12 @@ class PlotH(PamhyrPlot): **self.plot_default_kargs ) - def draw_current(self, reach, profile): - min_y, max_y = reduce( - lambda acc, p: ( - acc[0] + [min(p.get_key("Q"))], - acc[1] + [max(p.get_key("Q"))] - ), - reach.profiles, - ([], []) - ) - + def draw_current(self): self._current, = self.canvas.axes.plot( [self._current_timestamp, self._current_timestamp], - [min(min_y), max(max_y)], + self.canvas.axes.get_ylim(), # label=self.label_timestamp, - color=self.color_plot_river_bottom, + color="grey", linestyle="dashed", lw=1., ) @@ -162,14 +154,14 @@ class PlotH(PamhyrPlot): def set_timestamp(self, timestamp): self._current_timestamp = timestamp - self.update() + self.update_current() + self.update_idle() def update(self): if not self._init: self.draw() self.update_data() - self.update_idle() def update_data(self): @@ -181,8 +173,13 @@ class PlotH(PamhyrPlot): self._line.set_data(x, y) - _, min_max = self._current.get_data() self._current.set_data( - self._current_timestamp, - min_max + [self._current_timestamp, self._current_timestamp], + self.canvas.axes.get_ylim() + ) + + def update_current(self): + self._current.set_data( + [self._current_timestamp, self._current_timestamp], + self.canvas.axes.get_ylim() ) diff --git a/src/View/Results/PlotKPC.py b/src/View/Results/PlotRKC.py similarity index 90% rename from src/View/Results/PlotKPC.py rename to src/View/Results/PlotRKC.py index 77e101dc2cd7c6096d111555c2cbd3abdbcd630d..9a77d0d7869c6a424479ff445f3242de7ba463c8 100644 --- a/src/View/Results/PlotKPC.py +++ b/src/View/Results/PlotRKC.py @@ -1,4 +1,4 @@ -# PlotKPC.py -- Pamhyr +# PlotRKC.py -- Pamhyr # Copyright (C) 2023-2024 INRAE # # This program is free software: you can redistribute it and/or modify @@ -30,11 +30,11 @@ from PyQt5.QtCore import ( logger = logging.getLogger() -class PlotKPC(PamhyrPlot): +class PlotRKC(PamhyrPlot): def __init__(self, canvas=None, trad=None, toolbar=None, results=None, reach_id=0, profile_id=0, parent=None): - super(PlotKPC, self).__init__( + super(PlotRKC, self).__init__( canvas=canvas, trad=trad, data=results, @@ -47,7 +47,7 @@ class PlotKPC(PamhyrPlot): self._current_reach_id = reach_id self._current_profile_id = profile_id - self.label_x = self._trad["unit_kp"] + self.label_x = self._trad["unit_rk"] self.label_y = self._trad["unit_elevation"] self.label_bottom = self._trad["label_bottom"] @@ -95,11 +95,11 @@ class PlotKPC(PamhyrPlot): def draw_bottom_with_bedload(self, reach): self._bedrock = self.sl_compute_bedrock(reach) - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z = self.sl_compute_current_z(reach) self.line_bottom, = self.canvas.axes.plot( - kp, z, + rk, z, linestyle="solid", lw=1., color=self.color_plot_river_bottom, ) @@ -109,11 +109,14 @@ class PlotKPC(PamhyrPlot): def draw_profiles_hs(self, reach): lhs = filter( lambda hs: hs._input_reach.reach is reach.geometry, - self.results.study.river.hydraulic_structures.lst + filter( + lambda hs: hs._input_reach is not None, + self.results.study.river.hydraulic_structures.lst + ) ) for hs in lhs: - x = hs.input_kp + x = hs.input_rk z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() @@ -187,12 +190,12 @@ class PlotKPC(PamhyrPlot): ) def draw_bottom_geometry(self, reach): - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() - self.line_kp_zmin = self.canvas.axes.plot( - kp, z_min, + self.line_rk_zmin = self.canvas.axes.plot( + rk, z_min, color=self.color_plot_river_bottom, lw=1. ) @@ -201,7 +204,7 @@ class PlotKPC(PamhyrPlot): def draw_water_elevation(self, reach): if len(reach.geometry.profiles) != 0: - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() water_z = list( @@ -214,12 +217,12 @@ class PlotKPC(PamhyrPlot): ) self.water = self.canvas.axes.plot( - kp, water_z, + rk, water_z, lw=1., color=self.color_plot_river_water, ) self.water_fill = self.canvas.axes.fill_between( - kp, self._river_bottom, water_z, + rk, self._river_bottom, water_z, color=self.color_plot_river_water_zone, alpha=0.7, interpolate=True @@ -227,7 +230,7 @@ class PlotKPC(PamhyrPlot): def draw_water_elevation_max(self, reach): if len(reach.geometry.profiles) != 0: - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() water_z = list( @@ -238,20 +241,20 @@ class PlotKPC(PamhyrPlot): ) self.canvas.axes.plot( - kp, water_z, lw=1., + rk, water_z, lw=1., color=self.color_plot_river_water, linestyle='dotted', ) def draw_current(self, reach): - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() self.profile, = self.canvas.axes.plot( [ - kp[self._current_profile_id], - kp[self._current_profile_id] + rk[self._current_profile_id], + rk[self._current_profile_id] ], [ z_max[self._current_profile_id], @@ -282,7 +285,7 @@ class PlotKPC(PamhyrPlot): for profile, z in overflow: self.canvas.axes.plot( - profile.kp, z, + profile.rk, z, lw=1., color=self.color_plot, markersize=3, @@ -327,7 +330,7 @@ class PlotKPC(PamhyrPlot): def update_water_elevation(self): reach = self.results.river.reach(self._current_reach_id) - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() water_z = list( @@ -340,38 +343,38 @@ class PlotKPC(PamhyrPlot): ) self.water[0].set_data( - kp, water_z + rk, water_z ) self.water_fill.remove() self.water_fill = self.canvas.axes.fill_between( - kp, self._river_bottom, water_z, + rk, self._river_bottom, water_z, color=self.color_plot_river_water_zone, alpha=0.7, interpolate=True ) def update_current(self): reach = self.results.river.reach(self._current_reach_id) - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() cid = self._current_profile_id self.profile.set_data( - [kp[cid], kp[cid]], + [rk[cid], rk[cid]], [z_max[cid], z_min[cid]] ) self.canvas.figure.canvas.draw_idle() def update_bottom_with_bedload(self): reach = self.results.river.reach(self._current_reach_id) - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z = self.sl_compute_current_z(reach) self.line_bottom.remove() self.line_bottom, = self.canvas.axes.plot( - kp, z, + rk, z, linestyle="solid", lw=1., color=self.color_plot_river_bottom, ) diff --git a/src/View/Results/PlotSedProfile.py b/src/View/Results/PlotSedProfile.py index d9dcbcb45934864c7ec0de08adaaa47089f8f8e1..507ef9e190d265c40387c4db56ca090d5e335dd1 100644 --- a/src/View/Results/PlotSedProfile.py +++ b/src/View/Results/PlotSedProfile.py @@ -164,10 +164,10 @@ class PlotSedProfile(PamhyrPlot): z_sl = self.get_zsl(profile) - self.line_kp_sl = [] + self.line_rk_sl = [] for i, zsl in enumerate(z_sl): - self.line_kp_sl.append(None) - self.line_kp_sl[i], = self.canvas.axes.plot( + self.line_rk_sl.append(None) + self.line_rk_sl[i], = self.canvas.axes.plot( x, zsl, linestyle="solid" if i == len(z_sl) - 1 else "--", lw=1.8, diff --git a/src/View/Results/PlotSedReach.py b/src/View/Results/PlotSedReach.py index d77339a1891895a1c07cde3b1ece880be60e713d..69bca99042174c6d18b52cd8b01f5d22d72f79e7 100644 --- a/src/View/Results/PlotSedReach.py +++ b/src/View/Results/PlotSedReach.py @@ -62,7 +62,7 @@ class PlotSedReach(PamhyrPlot): # DEPRECATED version of sediment layser display # def _get_zsl(self, reach): - # kp = reach.geometry.get_kp() + # rk = reach.geometry.get_rk() # z_min = reach.geometry.get_z_min() # z_max = reach.geometry.get_z_max() @@ -91,7 +91,7 @@ class PlotSedReach(PamhyrPlot): # sl.append(cur) # self.canvas.axes.set_xlim( - # left = min(kp) - 10, right = max(kp) + 10 + # left = min(rk) - 10, right = max(rk) + 10 # ) # # Dummy layer with height = 0 @@ -133,7 +133,7 @@ class PlotSedReach(PamhyrPlot): # return z_sl def get_zsl(self, reach): - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() @@ -231,7 +231,7 @@ class PlotSedReach(PamhyrPlot): return self.canvas.axes.set_xlabel( - _translate("MainWindow_reach", "Kp (m)"), + _translate("MainWindow_reach", "Position (m)"), color='black', fontsize=10 ) self.canvas.axes.set_ylabel( @@ -239,18 +239,18 @@ class PlotSedReach(PamhyrPlot): color='black', fontsize=10 ) - kp = reach.geometry.get_kp() + rk = reach.geometry.get_rk() z_min = reach.geometry.get_z_min() z_max = reach.geometry.get_z_max() z_sl = self.get_zsl(reach) # Draw - self.line_kp_sl = [] + self.line_rk_sl = [] for i, z in enumerate(z_sl): - self.line_kp_sl.append(None) - self.line_kp_sl[i], = self.canvas.axes.plot( - kp, z, + self.line_rk_sl.append(None) + self.line_rk_sl[i], = self.canvas.axes.plot( + rk, z, linestyle="solid" if i == len(z_sl) - 1 else "--", lw=1.8, color='grey' if i == len(z_sl) - 1 else None diff --git a/src/View/Results/PlotXY.py b/src/View/Results/PlotXY.py index 22a7149ff2b11a058d6976f4d43dc89fa0b6e583..1621e282f4feab3145206bd05736013ef6751a3f 100644 --- a/src/View/Results/PlotXY.py +++ b/src/View/Results/PlotXY.py @@ -22,10 +22,14 @@ from functools import reduce from tools import timer, trace from View.Tools.PamhyrPlot import PamhyrPlot +import numpy as np +from matplotlib import collections from PyQt5.QtCore import ( - QCoreApplication + QCoreApplication, Qt, QItemSelectionModel, + QItemSelection, QItemSelectionRange ) +from PyQt5.QtWidgets import QApplication, QTableView _translate = QCoreApplication.translate @@ -57,6 +61,73 @@ class PlotXY(PamhyrPlot): self.label_y = _translate("Results", "Y (m)") self._isometric_axis = True + self._tablemodel = parent._table["profile"] + self._table = parent.find(QTableView, f"tableView_profile") + + def onpick(self, event): + if event.mouseevent.inaxes != self.canvas.axes: + return + if event.mouseevent.button.value != 1: + return + + modifiers = QApplication.keyboardModifiers() + if modifiers not in [Qt.ControlModifier, + Qt.NoModifier, + Qt.ShiftModifier]: + return + + ind, point = self._closest_section(event) + if self._table is None: + return + self._select_in_table([ind]) + self._table.blockSignals(False) + + return + + def _closest_section(self, event): + axes = self.canvas.axes + mx = event.mouseevent.xdata + my = event.mouseevent.ydata + bx, by = axes.get_xlim(), axes.get_ylim() + ratio = (bx[0] - bx[1]) / (by[0] - by[1]) + + segments = event.artist.get_segments() + ind = event.ind + + points = [] + for i in ind: + points = points + [[i, j] for j in segments[i]] + + def dist_mouse(point): + x, y = point[1] + d2 = (((mx - x) / ratio) ** 2) + ((my - y) ** 2) + return d2 + + closest = min( + points, key=dist_mouse + ) + + return closest + + def _select_in_table(self, ind): + if self._table is None: + return + self._table.setFocus() + selection = self._table.selectionModel() + index = QItemSelection() + if len(ind) == 0: + return + for i in ind: + index.append(QItemSelectionRange(self._table.model().index(i, 0))) + selection.select( + index, + QItemSelectionModel.Rows | + QItemSelectionModel.ClearAndSelect | + QItemSelectionModel.Select + ) + + if len(ind) > 0: + self._table.scrollTo(self._table.model().index(ind[-1], 0)) @property def results(self): @@ -90,18 +161,36 @@ class PlotXY(PamhyrPlot): self._init = False return - self.line_xy = [ - self.canvas.axes.plot( - x, y, - color=self.color_plot_river_bottom, - **self.plot_default_kargs - ) - for x, y, kp in zip( - reach.geometry.get_x(), - reach.geometry.get_y(), - reach.geometry.get_kp() - ) - ] + self.line_xy = [] + for xy in zip(reach.geometry.get_x(), + reach.geometry.get_y()): + self.line_xy.append(np.column_stack(xy)) + + self._colors, self._style = self.color_hightlight() + self.line_xy_collection = collections.LineCollection( + self.line_xy, + colors=self._colors, + linestyle=self._style, + picker=10 + ) + self.canvas.axes.add_collection(self.line_xy_collection) + + def color_hightlight(self): + reach = self.results.river.reach(self._current_reach_id) + rows = [self._current_profile_id] + colors = [self.color_plot + for row in range(reach.geometry.number_profiles)] + style = ["-" for row in range(reach.geometry.number_profiles)] + if len(rows) > 0: + for row in rows: + colors[row] = self.color_plot_current + if rows[0] > 0: + colors[rows[0]-1] = self.color_plot_previous + style[rows[0]-1] = "--" + if rows[-1] < reach.geometry.number_profiles-1: + colors[rows[-1]+1] = self.color_plot_next + style[rows[-1]+1] = "--" + return colors, style def draw_guide_lines(self, reach): x_complete = reach.geometry.get_guidelines_x() diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py index 03e7668b7ee7fe3d2be41b4ee9a2d64e66868579..0a3dcb6c9ceae8a9e33767feba6f3b838e050760 100644 --- a/src/View/Results/Table.py +++ b/src/View/Results/Table.py @@ -19,6 +19,8 @@ import logging import traceback +from numpy import sqrt + from tools import timer, trace from PyQt5.QtGui import ( @@ -75,14 +77,14 @@ class TableModel(PamhyrTableModel): if self._headers[column] == "name": v = self._lst[row].name return str(v) - elif self._headers[column] == "kp": - v = self._lst[row].kp + elif self._headers[column] == "rk": + v = self._lst[row].rk return f"{v:.4f}" elif self._opt_data == "raw_data": p = self._lst[row] if self._headers[column] == "name": if p.name == "": - return f"{p.kp:.4f}" + return f"{p.rk:.4f}" return f"{p.name}" elif self._headers[column] == "water_elevation": v = self._lst[row].get_ts_key(self._timestamp, "Z") @@ -90,11 +92,45 @@ class TableModel(PamhyrTableModel): elif self._headers[column] == "discharge": v = self._lst[row].get_ts_key(self._timestamp, "Q") return f"{v:.4f}" - elif self._headers[column] == "speed": + elif self._headers[column] == "velocity": q = self._lst[row].get_ts_key(self._timestamp, "Q") - z = self._lst[row].get_ts_key(self._timestamp, "Q") - + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.speed(q, z) + return f"{v:.4f}" + elif self._headers[column] == "width": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_width(z) + return f"{v:.4f}" + elif self._headers[column] == "max_depth": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.max_water_depth(z) + return f"{v:.4f}" + elif self._headers[column] == "mean_depth": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.mean_water_depth(z) + return f"{v:.4f}" + elif self._headers[column] == "wet_area": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_area(z) + return f"{v:.4f}" + elif self._headers[column] == "wet_perimeter": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_perimeter(z) + return f"{v:.4f}" + elif self._headers[column] == "hydraulic_radius": + z = self._lst[row].get_ts_key(self._timestamp, "Z") + v = self._lst[row].geometry.wet_radius(z) + return f"{v:.4f}" + elif self._headers[column] == "froude": + q = self._lst[row].get_ts_key(self._timestamp, "Q") + z = self._lst[row].get_ts_key(self._timestamp, "Z") v = self._lst[row].geometry.speed(q, z) + a = self._lst[row].geometry.wet_area(z) + b = self._lst[row].geometry.wet_width(z) + froude = v / sqrt(9.81 * (a / b)) + return f"{froude:.4f}" + else: + v = 0.0 return f"{v:.4f}" return QVariant() diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index 155b971e5c4ba7081aadf9ae2820c5777b6dec01..b1872102f8b16ca929601e39a205cdc9bb5601cc 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -20,6 +20,8 @@ import os import csv import logging +from numpy import sqrt + from datetime import datetime from tools import trace, timer, logger_exception @@ -40,7 +42,7 @@ from PyQt5.QtWidgets import ( QFileDialog, QTableView, QAbstractItemView, QUndoStack, QShortcut, QAction, QItemDelegate, QComboBox, QVBoxLayout, QHeaderView, QTabWidget, - QSlider, QLabel, QWidget, QGridLayout, + QSlider, QLabel, QWidget, QGridLayout, QTabBar ) from View.Tools.Plot.PamhyrCanvas import MplCanvas @@ -48,7 +50,7 @@ from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar from View.Results.PlotXY import PlotXY from View.Results.PlotAC import PlotAC -from View.Results.PlotKPC import PlotKPC +from View.Results.PlotRKC import PlotRKC from View.Results.PlotH import PlotH from View.Results.PlotSedReach import PlotSedReach from View.Results.PlotSedProfile import PlotSedProfile @@ -108,9 +110,9 @@ class ResultsWindow(PamhyrWindow): try: self._timestamps = sorted(list(self._results.get("timestamps"))) + self.setup_slider() self.setup_table() self.setup_plots() - self.setup_slider() self.setup_statusbar() self.setup_connections() except Exception as e: @@ -128,12 +130,11 @@ class ResultsWindow(PamhyrWindow): undo=self._undo_stack, opt_data=t ) + self._table[t]._timestamp = self._timestamps[ + self._slider_time.value()] def setup_slider(self): - self._slider_profile = self.find(QSlider, f"verticalSlider_profile") default_reach = self._results.river.reach(0) - self._slider_profile.setMaximum(len(default_reach.profiles) - 1) - self._slider_profile.setValue(0) self._slider_time = self.find(QSlider, f"horizontalSlider_time") self._slider_time.setMaximum(len(self._timestamps) - 1) @@ -158,6 +159,12 @@ class ResultsWindow(PamhyrWindow): def setup_plots(self): self.canvas = MplCanvas(width=5, height=4, dpi=100) + tab_widget = self.find(QTabWidget, f"tabWidget") + tab_widget.setTabsClosable(True) + tab_widget.tabCloseRequested.connect(self.delete_tab) + tab_widget.tabBar().setTabButton(0, QTabBar.RightSide, None) + tab_widget.tabBar().setTabButton(1, QTabBar.RightSide, None) + tab_widget.tabBar().setTabButton(2, QTabBar.RightSide, None) self.canvas.setObjectName("canvas") self.toolbar = PamhyrPlotToolbar( self.canvas, self, items=[ @@ -176,7 +183,8 @@ class ResultsWindow(PamhyrWindow): profile_id=0, trad=self._trad, toolbar=self.toolbar, - display_current=True + display_current=True, + parent=self ) self.plot_xy.draw() @@ -192,7 +200,7 @@ class ResultsWindow(PamhyrWindow): self.plot_layout_2.addWidget(self.toolbar_2) self.plot_layout_2.addWidget(self.canvas_2) - self.plot_kpc = PlotKPC( + self.plot_rkc = PlotRKC( canvas=self.canvas_2, results=self._results, reach_id=0, @@ -200,7 +208,7 @@ class ResultsWindow(PamhyrWindow): trad=self._trad, toolbar=self.toolbar_2 ) - self.plot_kpc.draw() + self.plot_rkc.draw() self.canvas_3 = MplCanvas(width=5, height=4, dpi=100) self.canvas_3.setObjectName("canvas_3") @@ -270,9 +278,9 @@ class ResultsWindow(PamhyrWindow): table = self.find(QTableView, f"tableView_reach") indexes = table.selectedIndexes() if len(indexes) == 0: - reach = self._study.river.edges()[0] + reach = self._study.river.enable_edges()[0] else: - reach = self._study.river.edges()[indexes[0].row()] + reach = self._study.river.enable_edges()[indexes[0].row()] # Profile table = self.find(QTableView, f"tableView_profile") @@ -282,7 +290,7 @@ class ResultsWindow(PamhyrWindow): else: profile = reach.reach.profile(indexes[0].row()) - pname = profile.name if profile.name != "" else profile.kp + pname = profile.name if profile.name != "" else profile.rk return (f"Reach: {reach.name} | " + f"Profile: {pname} | " + @@ -302,7 +310,8 @@ class ResultsWindow(PamhyrWindow): actions = { "action_reload": self._reload, "action_add": self._add_custom_plot, - "action_export": self.export, + "action_export": self._export, + # "action_export": self.export_current, } for action in actions: @@ -326,8 +335,6 @@ class ResultsWindow(PamhyrWindow): self._table[t].dataChanged.connect(fun[t]) - self._slider_profile.valueChanged.connect( - self._set_current_profile_slider) self._slider_time.valueChanged.connect(self._set_current_timestamp) self._button_play.setChecked(False) self._button_play.clicked.connect(self._pause) @@ -371,7 +378,7 @@ class ResultsWindow(PamhyrWindow): if reach_id is not None: self.plot_xy.set_reach(reach_id) self.plot_ac.set_reach(reach_id) - self.plot_kpc.set_reach(reach_id) + self.plot_rkc.set_reach(reach_id) self.plot_h.set_reach(reach_id) for plot in self._additional_plot: @@ -383,7 +390,7 @@ class ResultsWindow(PamhyrWindow): if profile_id is not None: self.plot_xy.set_profile(profile_id) self.plot_ac.set_profile(profile_id) - self.plot_kpc.set_profile(profile_id) + self.plot_rkc.set_profile(profile_id) self.plot_h.set_profile(profile_id) for plot in self._additional_plot: @@ -394,7 +401,7 @@ class ResultsWindow(PamhyrWindow): if timestamp is not None: self.plot_xy.set_timestamp(timestamp) self.plot_ac.set_timestamp(timestamp) - self.plot_kpc.set_timestamp(timestamp) + self.plot_rkc.set_timestamp(timestamp) self.plot_h.set_timestamp(timestamp) for plot in self._additional_plot: @@ -441,7 +448,6 @@ class ResultsWindow(PamhyrWindow): ind = indexes[0].row() self.update(profile_id=ind) - self._slider_profile.setValue(ind) def _set_current_profile_raw_data(self): table = self.find(QTableView, f"tableView_raw_data") @@ -451,11 +457,6 @@ class ResultsWindow(PamhyrWindow): ind = indexes[0].row() self.update(profile_id=ind) - self._slider_profile.setValue(ind) - - def _set_current_profile_slider(self): - pid = self._slider_profile.value() - self.update(profile_id=pid) def _set_current_timestamp(self): timestamp = self._timestamps[self._slider_time.value()] @@ -464,12 +465,12 @@ class ResultsWindow(PamhyrWindow): def _reload_plots(self): self.plot_xy.results = self._results self.plot_ac.results = self._results - self.plot_kpc.results = self._results + self.plot_rkc.results = self._results self.plot_h.results = self._results self.plot_xy.draw() self.plot_ac.draw() - self.plot_kpc.draw() + self.plot_rkc.draw() self.plot_h.draw() def _reload_slider(self): @@ -499,7 +500,7 @@ class ResultsWindow(PamhyrWindow): tab_widget = self.find(QTabWidget, f"tabWidget") # This plot already exists - if name in self._additional_plot: + if name in [tab_widget.tabText(i) for i in range(tab_widget.count())]: tab_widget.setCurrentWidget( tab_widget.findChild(QWidget, wname) ) @@ -535,6 +536,7 @@ class ResultsWindow(PamhyrWindow): grid.addWidget(canvas, 1, 0) widget.setLayout(grid) tab_widget.addTab(widget, name) + tab_widget.setCurrentWidget(widget) def _copy(self): logger.info("TODO: copy") @@ -583,19 +585,54 @@ class ResultsWindow(PamhyrWindow): self._button_last.setEnabled(True) self._button_play.setIcon(self._icon_start) - def export(self): + def _export(self): + + dlg = CustomPlotValuesSelectionDialog(parent=self) + if dlg.exec(): + x, y = dlg.value + else: + return + + logger.debug( + "Export custom plot for: " + + f"{x} -> {','.join(y)}" + ) self.file_dialog( - select_file=False, - callback=lambda d: self.export_to(d[0]) + select_file="AnyFile", + callback=lambda f: self.export_to(f[0], x, y), + default_suffix=".csv", + file_filter=["CSV (*.csv)"], ) - def export_to(self, directory): - for reach in self._results.river.reachs: - self.export_reach(reach, directory) + def export_to(self, filename, x, y): + timestamps = sorted(self._results.get("timestamps")) + if x == "rk": + timestamp = self._get_current_timestamp() + val_dict = self._export_rk(timestamp, y, filename) + elif x == "time": + profile = self._get_current_profile() + val_dict = self._export_time(profile, y, filename) - def export_reach(self, reach, directory): + with open(filename, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=',', + quotechar='|', quoting=csv.QUOTE_MINIMAL) + dict_x = self._trad.get_dict("values_x") + dict_y = self._trad.get_dict("values_y") + header = [dict_x[x]] + for text in y: + header.append(dict_y[text]) + writer.writerow(header) + for row in range(len(val_dict[x])): + line = [val_dict[x][row]] + for var in y: + line.append(val_dict[var][row]) + writer.writerow(line) + + def export_all(self, reach, directory, timestamps): name = reach.name name = name.replace(" ", "-") + if len(timestamps) == 1: + name = f"{name}_t{timestamps[0]}" file_name = os.path.join( directory, @@ -605,32 +642,144 @@ class ResultsWindow(PamhyrWindow): with open(file_name, 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) - writer.writerow(["name", "kp", "data-file"]) - for profile in reach.profiles: - p_file_name = os.path.join( - directory, - f"cs_{profile.geometry.id}.csv" - ) - - writer.writerow([ - profile.name, - profile.kp, - p_file_name - ]) + ts = timestamps[0] + writer.writerow(self._table["raw_data"]._headers) + for row in range(self._table["raw_data"].rowCount()): + line = [] + for column in range(self._table["raw_data"].columnCount()): + index = self._table["raw_data"].index(row, column) + line.append(self._table["raw_data"].data(index)) + writer.writerow(line) + + def export_current(self): + self.file_dialog( + select_file="Directory", + callback=lambda d: self.export_current_to(d[0]) + ) - self.export_profile(reach, profile, p_file_name) + def export_current_to(self, directory): + reach = self._results.river.reachs[self._get_current_reach()] + self.export_all(reach, directory, [self._get_current_timestamp()]) - def export_profile(self, reach, profile, file_name): - with open(file_name, 'w', newline='') as csvfile: - writer = csv.writer(csvfile, delimiter=',', - quotechar='|', quoting=csv.QUOTE_MINIMAL) + def delete_tab(self, index): + tab_widget = self.find(QTabWidget, f"tabWidget") + self._additional_plot.pop(tab_widget.tabText(index)) + tab_widget.removeTab(index) + + def _export_rk(self, timestamp, y, filename): + reach = self._results.river.reachs[self._get_current_reach()] + my_dict = {} + my_dict["rk"] = reach.geometry.get_rk() + if "elevation" in y: + my_dict["elevation"] = reach.geometry.get_z_min() + if "discharge" in y: + my_dict["discharge"] = list( + map( + lambda p: p.get_ts_key(timestamp, "Q"), + reach.profiles + ) + ) + if "water_elevation" in y: + my_dict["water_elevation"] = list( + map( + lambda p: p.get_ts_key(timestamp, "Z"), + reach.profiles + ) + ) + if "velocity" in y: + my_dict["velocity"] = list( + map( + lambda p: p.geometry.speed( + p.get_ts_key(timestamp, "Q"), + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + if "depth" in y: + my_dict["depth"] = list( + map( + lambda p: p.geometry.max_water_depth( + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + if "mean_depth" in y: + my_dict["mean_depth"] = list( + map( + lambda p: p.geometry.mean_water_depth( + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) + if "froude" in y: + my_dict["froude"] = list( + map( + lambda p: + p.geometry.speed( + p.get_ts_key(timestamp, "Q"), + p.get_ts_key(timestamp, "Z")) / + sqrt(9.81 * ( + p.geometry.wet_area( + p.get_ts_key(timestamp, "Z")) / + p.geometry.wet_width( + p.get_ts_key(timestamp, "Z")) + )), + reach.profiles + ) + ) + if "wet_area" in y: + my_dict["wet_area"] = list( + map( + lambda p: p.geometry.wet_area( + p.get_ts_key(timestamp, "Z")), + reach.profiles + ) + ) - writer.writerow(["timestamp", "z", "q"]) - timestamps = sorted(self._results.get("timestamps")) + return my_dict + + def _export_time(self, profile, y, filename): + reach = self._results.river.reachs[self._get_current_reach()] + profile = reach.profile(profile) + ts = list(self._results.get("timestamps")) + ts.sort() + my_dict = {} + my_dict["time"] = ts + z = profile.get_key("Z") + q = profile.get_key("Q") + if "elevation" in y: + my_dict["elevation"] = [profile.geometry.z_min()] * len(ts) + if "discharge" in y: + my_dict["discharge"] = q + if "water_elevation" in y: + my_dict["water_elevation"] = z + if "velocity" in y: + my_dict["velocity"] = list( + map( + lambda q, z: profile.geometry.speed(q, z), + q, z + ) + ) + if "depth" in y: + my_dict["depth"] = list( + map(lambda z: profile.geometry.max_water_depth(z), z) + ) + if "mean_depth" in y: + my_dict["mean_depth"] = list( + map(lambda z: profile.geometry.mean_water_depth(z), z) + ) + if "froude" in y: + my_dict["froude"] = list( + map(lambda z, q: + profile.geometry.speed(q, z) / + sqrt(9.81 * ( + profile.geometry.wet_area(z) / + profile.geometry.wet_width(z)) + ), z, q) + ) + if "wet_area" in y: + my_dict["wet_area"] = list( + map(lambda z: profile.geometry.wet_area(z), z) + ) - for ts in timestamps: - writer.writerow([ - ts, - profile.get_ts_key(ts, "Z"), - profile.get_ts_key(ts, "Q"), - ]) + return my_dict diff --git a/src/View/Results/translate.py b/src/View/Results/translate.py index b210dd2ad40f38770b7467d305b3441e785767ba..bddc63b00e21baa022b87deec301e5a23bd96bfe 100644 --- a/src/View/Results/translate.py +++ b/src/View/Results/translate.py @@ -60,13 +60,43 @@ class ResultsTranslate(MainTranslate): self._sub_dict["table_headers_profile"] = { "name": self._dict["name"], - "kp": self._dict["unit_kp"], + "rk": self._dict["unit_rk"], } self._sub_dict["table_headers_pollutants"] = { "name": _translate("Results", "Pollutant name"), } + self._sub_dict["table_headers_raw_data"] = { + "name": _translate("Results", "Profile"), + "water_elevation": self._dict["unit_water_elevation"], + "discharge": self._dict["unit_discharge"], + "velocity": self._dict["unit_speed"], + "width": self._dict["unit_width"], + "max_depth": self._dict["unit_max_height"], + "mean_depth": self._dict["unit_mean_height"], + "wet_area": self._dict["unit_wet_area"], + "wet_perimeter": self._dict["unit_wet_perimeter"], + "hydraulic_radius": self._dict["unit_hydraulic_radius"], + "froude": self._dict["unit_froude"], + } + + self._sub_dict["values_x"] = { + "rk": self._dict["unit_rk"], + "time": self._dict["unit_time_s"], + } + + self._sub_dict["values_y"] = { + "elevation": self._dict["unit_elevation"], + "water_elevation": self._dict["unit_water_elevation"], + "discharge": self._dict["unit_discharge"], + "velocity": self._dict["unit_speed"], + "depth": self._dict["unit_max_height"], + "mean_depth": self._dict["unit_mean_height"], + "froude": self._dict["unit_froude"], + "wet_area": self._dict["unit_wet_area"], + } + if self.pollutants is not None: self._sub_dict["table_headers_raw_data"] = { "name": _translate("Results", "Profile"), diff --git a/src/View/SedimentLayers/Edit/Plot.py b/src/View/SedimentLayers/Edit/Plot.py index 72f7cfb45a73d7b704ad3be86a6a2772171760cc..006a670b7c4fbd6a0fc1cff45f729780e9a6c63a 100644 --- a/src/View/SedimentLayers/Edit/Plot.py +++ b/src/View/SedimentLayers/Edit/Plot.py @@ -42,8 +42,8 @@ class Plot(PamhyrPlot): self.label_y = self._trad["height"] - self.line_kp_zmin = None - self.line_kp_sl = [] + self.line_rk_zmin = None + self.line_rk_sl = [] self._isometric_axis = False @@ -73,8 +73,8 @@ class Plot(PamhyrPlot): self.canvas.axes.set_xlim(*x) for i, zsl in enumerate(reversed(z_sl)): - self.line_kp_sl.append(None) - self.line_kp_sl[i], = self.canvas.axes.plot( + self.line_rk_sl.append(None) + self.line_rk_sl[i], = self.canvas.axes.plot( x, zsl, label=names[-(i+1)], linestyle="solid" if i == len(names) - 1 else "--", diff --git a/src/View/SedimentLayers/Reach/Plot.py b/src/View/SedimentLayers/Reach/Plot.py index 5198128aaa521218e15b1a0bff5bc75141df87e9..7d5ff7000f5380c8d3c501b07ec42ebc32d74736 100644 --- a/src/View/SedimentLayers/Reach/Plot.py +++ b/src/View/SedimentLayers/Reach/Plot.py @@ -43,11 +43,11 @@ class Plot(PamhyrPlot): parent=parent ) - self.label_x = self._trad["kp"] + self.label_x = self._trad["rk"] self.label_y = self._trad["height"] - self.line_kp_zmin = None - self.line_kp_sl = [] + self.line_rk_zmin = None + self.line_rk_sl = [] self._isometric_axis = False @@ -71,12 +71,12 @@ class Plot(PamhyrPlot): self._init = True def draw_data(self): - kp = self.data.get_kp() + rk = self.data.get_rk() sl = self.data.get_sl() z_min = self.data.get_z_min() self.canvas.axes.set_xlim( - left=min(kp), right=max(kp) + left=min(rk), right=max(rk) ) z_sl = reduce( @@ -93,9 +93,9 @@ class Plot(PamhyrPlot): ) for i, z in enumerate(reversed(z_sl)): - self.line_kp_sl.append(None) - self.line_kp_sl[i], = self.canvas.axes.plot( - kp, z, + self.line_rk_sl.append(None) + self.line_rk_sl[i], = self.canvas.axes.plot( + rk, z, linestyle="solid" if i == len(z_sl) - 1 else "--", lw=1.5, color='grey' if i == len(z_sl) - 1 else None diff --git a/src/View/SedimentLayers/Reach/Profile/Plot.py b/src/View/SedimentLayers/Reach/Profile/Plot.py index fe92bf760cad2a7e34315591ea4a5ab49fcf78a0..8069cc202d471184ddcc75f1030b34d576e3abe7 100644 --- a/src/View/SedimentLayers/Reach/Profile/Plot.py +++ b/src/View/SedimentLayers/Reach/Profile/Plot.py @@ -46,8 +46,8 @@ class Plot(PamhyrPlot): self.label_x = self._trad["x"] self.label_y = self._trad["height"] - self.line_kp_zmin = None - self.line_kp_sl = [] + self.line_rk_zmin = None + self.line_rk_sl = [] self._isometric_axis = False @@ -86,8 +86,8 @@ class Plot(PamhyrPlot): ) for i, zsl in enumerate(reversed(z_sl)): - self.line_kp_sl.append(None) - self.line_kp_sl[i], = self.canvas.axes.plot( + self.line_rk_sl.append(None) + self.line_rk_sl[i], = self.canvas.axes.plot( x, zsl, linestyle="solid" if i == len(z_sl) - 1 else "--", lw=1.8, diff --git a/src/View/SedimentLayers/Reach/Profile/Window.py b/src/View/SedimentLayers/Reach/Profile/Window.py index 63be359db626e5d285070bf364dc6c946eff989a..4db837dcb4282ad77ea3d3695312ca57b59e2c72 100644 --- a/src/View/SedimentLayers/Reach/Profile/Window.py +++ b/src/View/SedimentLayers/Reach/Profile/Window.py @@ -91,7 +91,7 @@ class ProfileSedimentLayersWindow(PamhyrWindow): if pname == "": pname = _translate( "SedimentLayers", - "(no name - @kp)").replace("@kp", str(self._profile.kp) + "(no name - @rk)").replace("@rk", str(self._profile.rk) ) return ( diff --git a/src/View/SedimentLayers/Reach/Table.py b/src/View/SedimentLayers/Reach/Table.py index 7c6298f2f48b5baad5bd8e14e4452bdcd5a9b9a5..f61df249ca110fb250065ac34fa008cab3ac3912 100644 --- a/src/View/SedimentLayers/Reach/Table.py +++ b/src/View/SedimentLayers/Reach/Table.py @@ -101,8 +101,8 @@ class TableModel(PamhyrTableModel): if self._headers[column] == "name": return self._data.profile(row).name - if self._headers[column] == "kp": - return self._data.profile(row).kp + if self._headers[column] == "rk": + return self._data.profile(row).rk if self._headers[column] == "sl": value = self._data.profile(row).sl if value is None: diff --git a/src/View/SedimentLayers/Reach/translate.py b/src/View/SedimentLayers/Reach/translate.py index 496a0afc33a4943beb74435f869d279f48b5a660..38ae810840b7559ff02dbdc0e832b6aeffbace60 100644 --- a/src/View/SedimentLayers/Reach/translate.py +++ b/src/View/SedimentLayers/Reach/translate.py @@ -34,11 +34,11 @@ class SedimentReachTranslate(SedimentTranslate): "SedimentLayers", "Apply sediment layers to reach" ) - self._dict["kp"] = self._dict["unit_kp"] + self._dict["rk"] = self._dict["unit_rk"] self._dict["height"] = self._dict["unit_height"] self._sub_dict["table_headers"] = { "name": self._dict["name"], - "kp": self._dict["unit_kp"], + "rk": self._dict["unit_rk"], "sl": _translate("SedimentLayers", "Sediment layers"), } diff --git a/src/View/SolverParameters/translate.py b/src/View/SolverParameters/translate.py index c1a45d2bb9128232685d5cf3c3e1919e59b1e80c..46bae698196f67f4663f7319e8369d8bb5f49f57 100644 --- a/src/View/SolverParameters/translate.py +++ b/src/View/SolverParameters/translate.py @@ -99,7 +99,7 @@ class ParamTranslate(MainTranslate): "Minimum water height (meter)"), "mage_max_niter": _translate("SolverParameters", - "Maximun number of iterations (< 100)"), + "Maximum number of iterations (< 100)"), "mage_timestep_reduction_factor": _translate("SolverParameters", "Timestep reduction factor"), @@ -114,10 +114,10 @@ class ParamTranslate(MainTranslate): "Reduction precision factor of residue"), "mage_niter_max_precision": _translate("SolverParameters", - "Number of iteration at maximum precision"), + "Number of iterations at maximum precision"), "mage_niter_before_switch": _translate("SolverParameters", - "Number of iteration before switch"), + "Number of iterations before switch"), "mage_max_froude": _translate("SolverParameters", "Maximum accepted Froude number"), @@ -152,7 +152,7 @@ class ParamTranslate(MainTranslate): "mage_methode_modification_geometrie": _translate("SolverParameters", "Geometry modification method"), "mage_shields_critique": - _translate("SolverParameters", "Critic shields"), + _translate("SolverParameters", "Critical Shields number"), "mage_shields_correction": _translate("SolverParameters", "Shields correction"), "mage_capacite_solide": diff --git a/src/View/Stricklers/translate.py b/src/View/Stricklers/translate.py index 01275bb1f23bca223834b55356eea15001363482..eb8b53619a2025ac7737762e3b6bdcd9cdbb6f97 100644 --- a/src/View/Stricklers/translate.py +++ b/src/View/Stricklers/translate.py @@ -28,7 +28,7 @@ class StricklersTranslate(MainTranslate): super(StricklersTranslate, self).__init__() self._dict["Stricklers"] = _translate( - "Stricklers", "Stricklers" + "Stricklers", "Strickler coefficients" ) self._sub_dict["table_headers"] = { diff --git a/src/View/Tools/ASubWindow.py b/src/View/Tools/ASubWindow.py index 786553589d2a1f8a05369e51972975e8d79a6db4..f483b7b0c6b3d81339c7c8590eef534a9bf7dfaa 100644 --- a/src/View/Tools/ASubWindow.py +++ b/src/View/Tools/ASubWindow.py @@ -35,7 +35,7 @@ from PyQt5.QtWidgets import ( QRadioButton, QComboBox, QFileDialog, QMessageBox, QTableView, QAction, QDateTimeEdit, QWidget, QPlainTextEdit, - QLabel, QDoubleSpinBox, + QLabel, QDoubleSpinBox, QButtonGroup, ) from PyQt5.QtCore import ( QTime, QDateTime, @@ -85,7 +85,7 @@ class WindowToolKit(object): return header, values - def file_dialog(self, select_file=True, + def file_dialog(self, select_file="ExistingFile", callback=lambda x: None, directory=None, default_suffix=None, @@ -102,18 +102,23 @@ class WindowToolKit(object): Returns: The returns of callback """ - dialog = QFileDialog(self) + options = QFileDialog.Options() + options |= QFileDialog.DontUseNativeDialog - if select_file: + dialog = QFileDialog(self, options=options) + + if select_file == "Existing_file": mode = QFileDialog.FileMode.ExistingFile - else: + elif select_file == "Directory": mode = QFileDialog.FileMode.Directory + else: + mode = QFileDialog.FileMode.AnyFile dialog.setFileMode(mode) if directory is not None: dialog.setDirectory(directory) - if select_file: + if select_file != "Directory": if default_suffix is not None: dialog.setDefaultSuffix(default_suffix) @@ -498,6 +503,17 @@ class ASubWindowFeatures(object): qdate = QDateTime.fromString(date.isoformat(), "yyyy-MM-ddThh:mm:ss") self.find(QDateTimeEdit, name).setDateTime(qdate) + def get_checked_id_button_group(self, name: str): + """Get current checked button id in a buttonGroup + + Args: + name: The buttonGroup component name + + Returns: + Current checked id + """ + return self.find(QButtonGroup, name).checkedId() + # Top level interface diff --git a/src/View/Tools/PamhyrDelegate.py b/src/View/Tools/PamhyrDelegate.py index 00e09eedb180e825dff0a2d3b82676baff3cb94c..1a9d4cbfb4b230da043119a53dd953bd1046b1d1 100644 --- a/src/View/Tools/PamhyrDelegate.py +++ b/src/View/Tools/PamhyrDelegate.py @@ -18,6 +18,8 @@ import logging +from tools import timestamp + from PyQt5.QtCore import ( Qt, QRect, QTime, QDateTime, pyqtSlot, ) @@ -55,8 +57,8 @@ class PamhyrExTimeDelegate(QItemDelegate): if self._mode == "time": model.setData(index, int(time.total_seconds())) else: - logger.debug(str(time.timestamp())) - model.setData(index, int(time.timestamp())) + logger.debug(str(timestamp(time))) + model.setData(index, int(timestamp(time))) editor.close() editor.deleteLater() diff --git a/src/View/Tools/PamhyrPlot.py b/src/View/Tools/PamhyrPlot.py index 843be242d169f06aeb9aa25ab80d1a2859698aa8..b893db844da529002432d83d58d5636d9e9ffdf4 100644 --- a/src/View/Tools/PamhyrPlot.py +++ b/src/View/Tools/PamhyrPlot.py @@ -16,6 +16,8 @@ # -*- coding: utf-8 -*- +import logging + import matplotlib.colors as mplcolors from matplotlib import ticker @@ -25,6 +27,8 @@ from View.Tools.Plot.APlot import APlot from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar +logger = logging.getLogger() + class PamhyrPlot(APlot): color_axes = "black" @@ -38,7 +42,6 @@ class PamhyrPlot(APlot): color_plot_river_bottom = "grey" color_plot_river_water = "blue" color_plot_river_water_zone = "skyblue" - colors = list(mplcolors.TABLEAU_COLORS) linestyle = ['solid', 'dashed', 'dashdot', 'dotted'] @@ -77,6 +80,7 @@ class PamhyrPlot(APlot): connector = { 'pick_event': self.onpick, 'button_press_event': self.onclick, + 'button_release_event': self.onrelease, } for event in connector: @@ -100,6 +104,7 @@ class PamhyrPlot(APlot): self._highlight_data_update = False self._current_data = None #: Current data identifier self._current_data_update = False + self._rect_select = None @property def canvas(self): @@ -187,7 +192,6 @@ class PamhyrPlot(APlot): self.canvas.axes.autoscale_view(True, True, True) self.canvas.axes.autoscale() - self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() self.toolbar_update() @@ -200,7 +204,6 @@ class PamhyrPlot(APlot): self.canvas.axes.autoscale_view(True, True, True) self.canvas.axes.autoscale() - self.canvas.figure.tight_layout() self.canvas.figure.canvas.draw_idle() self.toolbar_update() @@ -231,3 +234,13 @@ class PamhyrPlot(APlot): def onclick(self, event): return + + def onrelease(self, event): + return + + def rect_select_callback(self, eclick, erelease): + 'eclick and erelease are the press and release events' + x1, y1 = eclick.xdata, eclick.ydata + x2, y2 = erelease.xdata, erelease.ydata + logging.debug("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) + return diff --git a/src/View/Tools/Plot/PamhyrCanvas.py b/src/View/Tools/Plot/PamhyrCanvas.py index 1694d1fdbb4f64dea99b24e9616709a21c43856b..2101aa60945580c5fbcb7cc184087f677643a421 100644 --- a/src/View/Tools/Plot/PamhyrCanvas.py +++ b/src/View/Tools/Plot/PamhyrCanvas.py @@ -23,7 +23,7 @@ class MplCanvas(FigureCanvasQTAgg): fig = Figure( figsize=(width, height), dpi=dpi, - layout='tight', + layout='constrained', ) super(MplCanvas, self).__init__(fig) @@ -36,7 +36,6 @@ class MplCanvas(FigureCanvasQTAgg): self.axes.yaxis.tick_left() self.axes.xaxis.tick_bottom() self.axes.spines[['top', 'right']].set_color('none') - self.figure.tight_layout() self.add_arrows() def add_arrows(self): diff --git a/src/View/Tools/Plot/PamhyrToolbar.py b/src/View/Tools/Plot/PamhyrToolbar.py index 2a851c924f4f9653d3f6ab2ccf6cd63ec1abbaf1..5b4e292713e08356750d97ef142766ee16e25cc1 100644 --- a/src/View/Tools/Plot/PamhyrToolbar.py +++ b/src/View/Tools/Plot/PamhyrToolbar.py @@ -112,7 +112,7 @@ class PamhyrPlotToolbar(NavigationToolbar2QT): self.toolitems.append( ( 'Forward', - _translate("Toolbar", 'Return to next view'), + _translate("Toolbar", 'Back to next view'), 'forward', 'forward' ) ) @@ -121,7 +121,7 @@ class PamhyrPlotToolbar(NavigationToolbar2QT): self.toolitems.append( ( 'Pan', - _translate("Toolbar", 'Axes panoramic'), + _translate("Toolbar", 'Panoramic axes'), 'move', 'pan' ) ) @@ -184,7 +184,7 @@ class PamhyrPlotToolbar(NavigationToolbar2QT): self.toolitems.append( ( 'Save', - _translate("Toolbar", 'Save the figure'), + _translate("Toolbar", 'Save figure'), 'filesave', 'save_figure' ) ) @@ -253,12 +253,12 @@ class PamhyrPlotToolbar(NavigationToolbar2QT): self._actions['back'].setToolTip( _translate("Toolbar", "Back to previous view")) self._actions['forward'].setToolTip( - _translate("Toolbar", "Return to next view")) + _translate("Toolbar", "Back to next view")) self._actions['pan'].setToolTip( - _translate("Toolbar", "Axes panoramic")) + _translate("Toolbar", "Panoramic axes")) self._actions['zoom'].setToolTip(_translate("Toolbar", "Zoom")) self._actions['save_figure'].setToolTip( - _translate("Toolbar", "Save the figure")) + _translate("Toolbar", "Save figure")) self.action_isometric_view.setToolTip( _translate("Toolbar", "Isometric view (Shift+W)")) self.action_auto_global_view.setToolTip( diff --git a/src/View/Translate.py b/src/View/Translate.py index 126043b5be21c6e48d4fc710a66a86b991cd2695..1405a2cf8eb8f68baffd95cb8803f23a6977eb6d 100644 --- a/src/View/Translate.py +++ b/src/View/Translate.py @@ -55,16 +55,18 @@ class UnitTranslate(CommonWordTranslate): def __init__(self): super(UnitTranslate, self).__init__() - self._dict["unit_kp"] = _translate("Unit", "KP (m)") + self._dict["unit_rk"] = _translate("Unit", "River Kilometer (m)") self._dict["unit_width"] = _translate("Unit", "Width (m)") - self._dict["unit_height"] = _translate("Unit", "Height (m)") + self._dict["unit_height"] = _translate("Unit", "Depth (m)") + self._dict["unit_max_height"] = _translate("Unit", "Max Depth (m)") + self._dict["unit_mean_height"] = _translate("Unit", "Mean Depth (m)") self._dict["unit_diameter"] = _translate("Unit", "Diameter (m)") self._dict["unit_thickness"] = _translate("Unit", "Thickness (m)") self._dict["unit_elevation"] = _translate("Unit", "Elevation (m)") self._dict["unit_water_elevation"] = _translate( "Unit", "Water elevation (m)" ) - self._dict["unit_speed"] = _translate("Unit", "Speed (m/s)") + self._dict["unit_speed"] = _translate("Unit", "Velocity (m/s)") self._dict["unit_discharge"] = _translate("Unit", "Discharge (m³/s)") self._dict["unit_area"] = _translate("Unit", "Area (hectare)") @@ -84,6 +86,15 @@ class UnitTranslate(CommonWordTranslate): self._dict["unit_bc"] = _translate("Unit", "BC") self._dict["unit_concentration"] = _translate("Unit", "Concentration (g/l)") + self._dict["unit_wet_area"] = _translate("Unit", "Wet Area (m²)") + self._dict["unit_wet_perimeter"] = _translate( + "Unit", "Wet Perimeter (m)" + ) + self._dict["unit_hydraulic_radius"] = _translate( + "Unit", "Hydraulic Radius (m)" + ) + self._dict["unit_froude"] = _translate("Unit", "Froude number") + class MainTranslate(UnitTranslate): def __init__(self): @@ -103,5 +114,5 @@ class MainTranslate(UnitTranslate): "MainWindow", "Open SQLite debuging tool ('sqlitebrowser')" ) self._dict["active_window"] = _translate( - "MainWindow", "Activate this window" + "MainWindow", "Enable this window" ) diff --git a/src/View/ui/BoundaryConditions.ui b/src/View/ui/BoundaryConditions.ui index 9f2f1b99419c50c6e2ca7e1a4587468e55a4f15e..e023eff03cc0ab9be9581df2089b7e1dd6940aba 100644 --- a/src/View/ui/BoundaryConditions.ui +++ b/src/View/ui/BoundaryConditions.ui @@ -37,7 +37,7 @@ </size> </property> <property name="currentIndex"> - <number>0</number> + <number>2</number> </property> <widget class="QWidget" name="tab_liquid"> <attribute name="title"> @@ -61,7 +61,7 @@ </widget> <widget class="QWidget" name="tab_suspenssion"> <attribute name="title"> - <string>Suspenssion</string> + <string>Suspension</string> </attribute> <layout class="QGridLayout" name="gridLayout_4"> <item row="0" column="0"> @@ -114,7 +114,7 @@ <string>Add</string> </property> <property name="toolTip"> - <string>Add a new boundary condition or punctual contribution</string> + <string>Add a new boundary condition or point source</string> </property> <property name="shortcut"> <string>Ctrl+N</string> @@ -144,7 +144,7 @@ <string>Edit</string> </property> <property name="toolTip"> - <string>Edit boundary condition or punctual contribution</string> + <string>Edit boundary condition or point source</string> </property> <property name="shortcut"> <string>Ctrl+E</string> @@ -159,7 +159,7 @@ <string>Sort</string> </property> <property name="toolTip"> - <string>Sort boundary condition by name</string> + <string>Sort by names</string> </property> </action> </widget> diff --git a/src/View/ui/BoundaryConditionsDialogGenerator.ui b/src/View/ui/BoundaryConditionsDialogGenerator.ui new file mode 100644 index 0000000000000000000000000000000000000000..6d3793bf05a657fdfbda15600cd791699db6fe86 --- /dev/null +++ b/src/View/ui/BoundaryConditionsDialogGenerator.ui @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>356</width> + <height>107</height> + </rect> + </property> + <property name="windowTitle"> + <string>Options</string> + </property> + <property name="accessibleName"> + <string/> + </property> + <property name="locale"> + <locale language="English" country="Europe"/> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Slope</string> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="doubleSpinBox"> + <property name="decimals"> + <number>6</number> + </property> + <property name="maximum"> + <double>999999.998999999952503</double> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="EstimateButton"> + <property name="text"> + <string>Estimate</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/View/ui/ConfigureDialog.ui b/src/View/ui/ConfigureDialog.ui index 590d0ca2ce4ea50d0537a09cd1b5a67613205c6f..9c23ce2dd6a24f6dd70a5f665ec7b9058b63ed0b 100644 --- a/src/View/ui/ConfigureDialog.ui +++ b/src/View/ui/ConfigureDialog.ui @@ -164,7 +164,7 @@ <item> <widget class="QLabel" name="label_9"> <property name="text"> - <string>Frequence</string> + <string>Frequency</string> </property> </widget> </item> @@ -349,7 +349,7 @@ </font> </property> <property name="text"> - <string>This value must be used for reading or editing files in speficic case.</string> + <string>This value must be used for reading or editing files in speficic cases.</string> </property> </widget> </item> @@ -374,7 +374,7 @@ <item row="1" column="0"> <widget class="QLabel" name="label_12"> <property name="text"> - <string> - The "@file" keyworkd is replace by the path of file to open.</string> + <string> - The "@file" keyword is replaced by the path of file to open.</string> </property> </widget> </item> @@ -382,7 +382,7 @@ </widget> <widget class="QWidget" name="tab_language"> <attribute name="title"> - <string>Language</string> + <string>Langue</string> </attribute> <layout class="QGridLayout" name="gridLayout_6"> <item row="0" column="0"> diff --git a/src/View/ui/CustomPlotValuesSelectionDialog.ui b/src/View/ui/CustomPlotValuesSelectionDialog.ui index 88ca363caf69660b6a17b4b88797b54e7f826ff2..f7daec375828061a2af5c6aed5d9df0ca1b18a15 100644 --- a/src/View/ui/CustomPlotValuesSelectionDialog.ui +++ b/src/View/ui/CustomPlotValuesSelectionDialog.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>414</width> - <height>482</height> + <height>70</height> </rect> </property> <property name="windowTitle"> @@ -59,32 +59,32 @@ <connections> <connection> <sender>buttonBox</sender> - <signal>accepted()</signal> + <signal>rejected()</signal> <receiver>Dialog</receiver> - <slot>accept()</slot> + <slot>reject()</slot> <hints> <hint type="sourcelabel"> - <x>248</x> - <y>254</y> + <x>316</x> + <y>260</y> </hint> <hint type="destinationlabel"> - <x>157</x> + <x>286</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> - <signal>rejected()</signal> + <signal>accepted()</signal> <receiver>Dialog</receiver> - <slot>reject()</slot> + <slot>accept()</slot> <hints> <hint type="sourcelabel"> - <x>316</x> - <y>260</y> + <x>248</x> + <y>254</y> </hint> <hint type="destinationlabel"> - <x>286</x> + <x>157</x> <y>274</y> </hint> </hints> diff --git a/src/View/ui/DebugRepl.ui b/src/View/ui/DebugRepl.ui index db4d069e5c599c88346dbd20c801103becc79680..39f30ac383fc2fe5f29b4dbcf86f3a2782e18dc2 100644 --- a/src/View/ui/DebugRepl.ui +++ b/src/View/ui/DebugRepl.ui @@ -23,7 +23,7 @@ <item> <widget class="QPushButton" name="pushButton"> <property name="text"> - <string>Eval</string> + <string>Evaluate</string> </property> <property name="icon"> <iconset> diff --git a/src/View/ui/EditBoundaryConditions.ui b/src/View/ui/EditBoundaryConditions.ui index bfcf95052c191fd5d66efd9a4fdd009614f4cfb5..f24120f416f464e18bc4200d55e6a877a69aaff5 100644 --- a/src/View/ui/EditBoundaryConditions.ui +++ b/src/View/ui/EditBoundaryConditions.ui @@ -73,6 +73,9 @@ <addaction name="action_add"/> <addaction name="action_del"/> <addaction name="action_sort"/> + <addaction name="action_generate_uniform"/> + <addaction name="action_generate_critical"/> + <addaction name="action_increasing"/> </widget> <action name="action_add"> <property name="checkable"> @@ -86,7 +89,7 @@ <string>Add</string> </property> <property name="toolTip"> - <string>Add a new point in boundary condition or punctual contribution</string> + <string>Add a new point</string> </property> <property name="shortcut"> <string>Ctrl+N</string> @@ -116,7 +119,34 @@ <string>Sort</string> </property> <property name="toolTip"> - <string>Sort boundary condition points</string> + <string>Sort points</string> + </property> + </action> + <action name="action_generate_uniform"> + <property name="text"> + <string>Generate uniform</string> + </property> + <property name="iconText"> + <string>Generate uniform</string> + </property> + <property name="toolTip"> + <string>Generate rating curve from Manning law</string> + </property> + </action> + <action name="action_generate_critical"> + <property name="text"> + <string>Generate critical</string> + </property> + <property name="toolTip"> + <string>Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z))</string> + </property> + </action> + <action name="action_increasing"> + <property name="text"> + <string>Make increasing</string> + </property> + <property name="toolTip"> + <string>Remove points to make the curve increasing</string> </property> </action> </widget> diff --git a/src/View/ui/EditLateralContribution.ui b/src/View/ui/EditLateralContribution.ui index 02e14bcba61effb9392b6108ba35f85941514708..21736a6b2bae8d01d593c1b325f9e4ae52229457 100644 --- a/src/View/ui/EditLateralContribution.ui +++ b/src/View/ui/EditLateralContribution.ui @@ -79,7 +79,7 @@ <string>Add</string> </property> <property name="toolTip"> - <string>Add a new point in boundary condition or lateral contribution</string> + <string>Add a new point in boundary condition or lateral source</string> </property> <property name="shortcut"> <string>Ctrl+N</string> @@ -109,7 +109,7 @@ <string>Sort</string> </property> <property name="toolTip"> - <string>Sort boundary condition point</string> + <string>Sort points</string> </property> </action> </widget> diff --git a/src/View/ui/Frictions.ui b/src/View/ui/Frictions.ui index e219bf7365efc584353af1f3bf6a8c23798ad6e8..77c3218161fb56ac141675f6eacced47d7015c5e 100644 --- a/src/View/ui/Frictions.ui +++ b/src/View/ui/Frictions.ui @@ -98,7 +98,10 @@ <normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset> </property> <property name="text"> - <string>Edit stricklers</string> + <string>Edit Strickler coefficients</string> + </property> + <property name="toolTip"> + <string>Edit Strickler coefficients</string> </property> <property name="shortcut"> <string>Ctrl+E</string> diff --git a/src/View/ui/GeometryCrossSection.ui b/src/View/ui/GeometryCrossSection.ui index c9c29e61d14e3cfc4f5c37e72562d6a0ae91aaee..dfc47bb61690f98d540b7a9bde02eff1e1d8d68d 100644 --- a/src/View/ui/GeometryCrossSection.ui +++ b/src/View/ui/GeometryCrossSection.ui @@ -67,10 +67,10 @@ <normaloff>ressources/add.png</normaloff>ressources/add.png</iconset> </property> <property name="text"> - <string>add</string> + <string>Add</string> </property> <property name="toolTip"> - <string>Add a point on cross-section</string> + <string>Add a point to cross-section</string> </property> </action> <action name="action_delete"> @@ -91,7 +91,7 @@ <normaloff>ressources/up.png</normaloff>ressources/up.png</iconset> </property> <property name="text"> - <string>up</string> + <string>Up</string> </property> <property name="toolTip"> <string>Move up selected point(s)</string> @@ -103,10 +103,10 @@ <normaloff>ressources/down.png</normaloff>ressources/down.png</iconset> </property> <property name="text"> - <string>down</string> + <string>Down</string> </property> <property name="toolTip"> - <string>Mode down selected point(s)</string> + <string>Move down selected point(s)</string> </property> </action> <action name="action_sort_asc"> diff --git a/src/View/ui/GeometryReach.ui b/src/View/ui/GeometryReach.ui index 2ba386506c8275818b928d84ae167a84a4cc9942..2d175bdcf52c51f7f456ee2e5868c71d638f74f0 100644 --- a/src/View/ui/GeometryReach.ui +++ b/src/View/ui/GeometryReach.ui @@ -28,49 +28,6 @@ <item> <widget class="QTableView" name="tableView"/> </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <widget class="QPushButton" name="pushButton_up"> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset> - <normaloff>ressources/go-up2.png</normaloff>ressources/go-up2.png</iconset> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QSlider" name="verticalSlider"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="invertedAppearance"> - <bool>true</bool> - </property> - <property name="invertedControls"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QPushButton" name="pushButton_down"> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset> - <normaloff>ressources/go-down1.png</normaloff>ressources/go-down1.png</iconset> - </property> - </widget> - </item> - </layout> - </item> </layout> </widget> <widget class="QSplitter" name="splitter_2"> @@ -133,8 +90,9 @@ <addaction name="action_down"/> <addaction name="action_export"/> <addaction name="action_meshing"/> - <addaction name="action_update_kp"/> + <addaction name="action_update_rk"/> <addaction name="action_purge"/> + <addaction name="action_shift"/> </widget> <action name="action_import"> <property name="icon"> @@ -166,7 +124,7 @@ <normaloff>ressources/add.png</normaloff>ressources/add.png</iconset> </property> <property name="text"> - <string>add</string> + <string>Add</string> </property> <property name="toolTip"> <string>Add a cross-section</string> @@ -190,7 +148,7 @@ <normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset> </property> <property name="text"> - <string>edit</string> + <string>Edit</string> </property> <property name="toolTip"> <string>Edit selected cross section(s)</string> @@ -205,7 +163,7 @@ <string>sort_asc</string> </property> <property name="toolTip"> - <string>Sort cross-sections by ascending KP</string> + <string>Sort cross-sections by ascending position</string> </property> </action> <action name="action_sort_des"> @@ -217,7 +175,7 @@ <string>sort_des</string> </property> <property name="toolTip"> - <string>Sort cross-sections by descending KP</string> + <string>Sort cross-sections by descending position</string> </property> </action> <action name="action_up"> @@ -226,7 +184,7 @@ <normaloff>ressources/up.png</normaloff>ressources/up.png</iconset> </property> <property name="text"> - <string>up</string> + <string>Up</string> </property> <property name="toolTip"> <string>Move up selected cross-section(s)</string> @@ -238,7 +196,7 @@ <normaloff>ressources/down.png</normaloff>ressources/down.png</iconset> </property> <property name="text"> - <string>down</string> + <string>Down</string> </property> <property name="toolTip"> <string>Move down selected cross-section(s)</string> @@ -253,12 +211,12 @@ <string>Meshing</string> </property> </action> - <action name="action_update_kp"> + <action name="action_update_rk"> <property name="text"> - <string>Update KP</string> + <string>Update RK</string> </property> <property name="toolTip"> - <string>Recompute KP</string> + <string>Recompute RK</string> </property> </action> <action name="action_purge"> @@ -269,6 +227,14 @@ <string>Purge cross-sections to keep a given number of points</string> </property> </action> + <action name="action_shift"> + <property name="text"> + <string>Shift</string> + </property> + <property name="toolTip"> + <string>Shift selected sections coordinates</string> + </property> + </action> </widget> <resources/> <connections/> diff --git a/src/View/ui/GeometryReachShift.ui b/src/View/ui/GeometryReachShift.ui new file mode 100644 index 0000000000000000000000000000000000000000..575dbb989a8886fa811f3b2d821010df32759d12 --- /dev/null +++ b/src/View/ui/GeometryReachShift.ui @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>381</width> + <height>144</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="3" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Y coordinate</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_1"> + <property name="text"> + <string>X coordinate</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QDoubleSpinBox" name="doubleSpinBox_Y"> + <property name="decimals"> + <number>4</number> + </property> + <property name="minimum"> + <double>-99999999.000000000000000</double> + </property> + <property name="maximum"> + <double>99999999.000000000000000</double> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QDoubleSpinBox" name="doubleSpinBox_X"> + <property name="decimals"> + <number>4</number> + </property> + <property name="minimum"> + <double>-99999999.000000000000000</double> + </property> + <property name="maximum"> + <double>99999999.000000000000000</double> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Z coordinate</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QDoubleSpinBox" name="doubleSpinBox_Z"> + <property name="decimals"> + <number>4</number> + </property> + <property name="minimum"> + <double>-99999999.000000000000000</double> + </property> + <property name="maximum"> + <double>99999999.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/View/ui/InitialConditions.ui b/src/View/ui/InitialConditions.ui index 38f9673437b45964903f27dd16125fa44fb32dc2..e026887269058768b6304804bba6cfc67ed6cb7f 100644 --- a/src/View/ui/InitialConditions.ui +++ b/src/View/ui/InitialConditions.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>1024</width> + <width>849</width> <height>576</height> </rect> </property> @@ -27,14 +27,21 @@ <item> <widget class="QPushButton" name="pushButton_generate_1"> <property name="text"> - <string>Generate from height</string> + <string>Generate depth</string> </property> </widget> </item> <item> <widget class="QPushButton" name="pushButton_generate_2"> <property name="text"> - <string>Generate from discharge</string> + <string>Generate discharge</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_generate_3"> + <property name="text"> + <string>Generate elevation</string> </property> </widget> </item> @@ -65,7 +72,7 @@ <rect> <x>0</x> <y>0</y> - <width>1024</width> + <width>849</width> <height>22</height> </rect> </property> @@ -119,7 +126,7 @@ <string>sort</string> </property> <property name="toolTip"> - <string>Sort inital condition</string> + <string>Sort inital conditions</string> </property> </action> <action name="action_import"> diff --git a/src/View/ui/InitialConditions_Dialog_Generator_Depth.ui b/src/View/ui/InitialConditions_Dialog_Generator_Depth.ui new file mode 100644 index 0000000000000000000000000000000000000000..deb89169eb381231ca1ae703cb9f5f97b42ca4ec --- /dev/null +++ b/src/View/ui/InitialConditions_Dialog_Generator_Depth.ui @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>284</width> + <height>107</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <property name="locale"> + <locale language="English" country="Europe"/> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Depth (m)</string> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="doubleSpinBox"> + <property name="maximum"> + <double>1000000.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="checkBox"> + <property name="text"> + <string>Generate discharge</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui b/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui index 8a0b7a8ab080603bcceace0dc64843ac87c8043e..58ae2b98cd665336835c75f06af958df315dd462 100644 --- a/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui +++ b/src/View/ui/InitialConditions_Dialog_Generator_Discharge.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>284</width> - <height>80</height> + <height>107</height> </rect> </property> <property name="windowTitle"> @@ -17,6 +17,16 @@ <locale language="English" country="Europe"/> </property> <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> <item row="0" column="0"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> @@ -39,12 +49,12 @@ </layout> </item> <item row="1" column="0"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <widget class="QCheckBox" name="checkBox"> + <property name="text"> + <string>Generate height</string> </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> diff --git a/src/View/ui/InitialConditions_Dialog_Generator_Height.ui b/src/View/ui/InitialConditions_Dialog_Generator_Height.ui index 027fb6210c2bae7055d326a3a604d24a1b057b9c..36d77a44dab0aa6537eaf1b6783f0c0f74b29630 100644 --- a/src/View/ui/InitialConditions_Dialog_Generator_Height.ui +++ b/src/View/ui/InitialConditions_Dialog_Generator_Height.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>284</width> - <height>80</height> + <width>396</width> + <height>182</height> </rect> </property> <property name="windowTitle"> @@ -22,20 +22,74 @@ <item> <widget class="QLabel" name="label"> <property name="text"> - <string>Height (m)</string> + <string>Upstream height (m)</string> </property> </widget> </item> <item> - <widget class="QDoubleSpinBox" name="doubleSpinBox"> + <widget class="QDoubleSpinBox" name="doubleSpinBox_1"> + <property name="minimum"> + <double>-1000000.000000000000000</double> + </property> <property name="maximum"> - <double>999999.998999999952503</double> + <double>1000000.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="3" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_3"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Discharge</string> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="doubleSpinBox_3"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimum"> + <double>-1000000.000000000000000</double> + </property> + <property name="maximum"> + <double>1000000.000000000000000</double> </property> </widget> </item> </layout> </item> <item row="1" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Downstream height (m)</string> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="doubleSpinBox_2"> + <property name="minimum"> + <double>-1000000.000000000000000</double> + </property> + <property name="maximum"> + <double>1000000.000000000000000</double> + </property> + <property name="value"> + <double>0.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="4" column="0"> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -45,6 +99,17 @@ </property> </widget> </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QCheckBox" name="checkBox"> + <property name="text"> + <string>Generate constant discharge</string> + </property> + </widget> + </item> + </layout> + </item> </layout> </widget> <resources/> diff --git a/src/View/ui/LateralContributions.ui b/src/View/ui/LateralContributions.ui index bf9e5b4531ac85bc39c0a0d8618594428fff28e6..32f4f60a3ddd5d3fc208041f17bb7bac26c4aa11 100644 --- a/src/View/ui/LateralContributions.ui +++ b/src/View/ui/LateralContributions.ui @@ -61,7 +61,7 @@ </widget> <widget class="QWidget" name="tab_suspenssion"> <attribute name="title"> - <string>Suspenssion</string> + <string>Suspension</string> </attribute> <layout class="QGridLayout" name="gridLayout_4"> <item row="0" column="0"> @@ -114,7 +114,7 @@ <string>Add</string> </property> <property name="toolTip"> - <string>Add a new boundary condition or lateral contribution</string> + <string>Add a new boundary condition or lateral source</string> </property> <property name="shortcut"> <string>Ctrl+N</string> @@ -144,7 +144,7 @@ <string>Edit</string> </property> <property name="toolTip"> - <string>Edit boundary condition or lateral contribution</string> + <string>Edit boundary condition or lateral source</string> </property> <property name="shortcut"> <string>Ctrl+E</string> @@ -159,7 +159,7 @@ <string>Sort</string> </property> <property name="toolTip"> - <string>Sort boundary condition by name</string> + <string>Sort by names</string> </property> </action> </widget> diff --git a/src/View/ui/MainWindow.ui b/src/View/ui/MainWindow.ui index c2bda3ebb0d78d3f0a76d722413cfe8c88a49a7b..5d7a4d9baa5a5989e1c8ddf116c1e0a60745fafb 100644 --- a/src/View/ui/MainWindow.ui +++ b/src/View/ui/MainWindow.ui @@ -117,7 +117,7 @@ <locale language="English" country="Europe"/> </property> <property name="title"> - <string>&River Network</string> + <string>&River network</string> </property> <addaction name="action_menu_edit_network"/> </widget> @@ -422,7 +422,7 @@ </action> <action name="action_menu_config"> <property name="text"> - <string>Pamhyr configuration</string> + <string>Pamhyr2 configuration</string> </property> </action> <action name="action_menu_quit"> @@ -460,10 +460,10 @@ </action> <action name="action_menu_numerical_parameter"> <property name="text"> - <string>Numerical parameter for solvers</string> + <string>Numerical parameters of solvers</string> </property> <property name="toolTip"> - <string>Numerical parameter for solvers</string> + <string>Numerical parameters of solvers</string> </property> </action> <action name="action_menu_boundary_conditions"> @@ -472,7 +472,7 @@ <normaloff>ressources/boundary_condition.png</normaloff>ressources/boundary_condition.png</iconset> </property> <property name="text"> - <string>Boundary conditions and punctual contributions</string> + <string>Boundary conditions and point sources</string> </property> <property name="font"> <font/> @@ -493,7 +493,7 @@ <normaloff>ressources/friction.png</normaloff>ressources/friction.png</iconset> </property> <property name="text"> - <string>Edit friction</string> + <string>Edit frictions</string> </property> </action> <action name="action_menu_edit_lateral_contribution"> @@ -502,7 +502,7 @@ <normaloff>ressources/lateral_contribution.png</normaloff>ressources/lateral_contribution.png</iconset> </property> <property name="text"> - <string>Edit lateral contributions</string> + <string>Edit lateral sources</string> </property> </action> <action name="action_menu_run_solver"> @@ -611,7 +611,7 @@ <string>Boundary conditions</string> </property> <property name="toolTip"> - <string>Edit boundary conditions and punctual contributions</string> + <string>Edit boundary conditions and point sources</string> </property> <property name="font"> <font/> @@ -623,10 +623,10 @@ <normaloff>ressources/lateral_contribution.png</normaloff>ressources/lateral_contribution.png</iconset> </property> <property name="text"> - <string>Lateral contribution</string> + <string>Lateral sources</string> </property> <property name="toolTip"> - <string>Edit lateral contribution</string> + <string>Edit lateral sources</string> </property> </action> <action name="action_toolBar_frictions"> @@ -638,7 +638,7 @@ <string>Friction</string> </property> <property name="toolTip"> - <string>Edit frictions</string> + <string>Edit friction</string> </property> </action> <action name="action_menu_edit"> @@ -683,7 +683,7 @@ <string>Mage</string> </property> <property name="toolTip"> - <string>Open mage documentation</string> + <string>Open Mage documentation</string> </property> </action> <action name="action_menu_pamhyr_users_wiki"> @@ -731,12 +731,12 @@ <normaloff>ressources/boundary_condition.png</normaloff>ressources/boundary_condition.png</iconset> </property> <property name="text"> - <string>Boundary conditions and punctual contributions</string> + <string>Boundary conditions and point sources</string> </property> </action> <action name="action_menu_additional_file"> <property name="text"> - <string>&Additional file</string> + <string>&Additional files</string> </property> </action> <action name="action_menu_rep_additional_lines"> diff --git a/src/View/ui/MeshingOptions.ui b/src/View/ui/MeshingOptions.ui index 6ff87a52465ff2c9a79e8f65a77af32c6c816033..de48342aa4cb815a6781cec12de14e5fad5078d5 100644 --- a/src/View/ui/MeshingOptions.ui +++ b/src/View/ui/MeshingOptions.ui @@ -33,7 +33,7 @@ <item row="0" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> - <string>Section space step (m)</string> + <string>Space step (m)</string> </property> </widget> </item> @@ -111,7 +111,7 @@ <item row="1" column="0"> <widget class="QGroupBox" name="groupBox_3"> <property name="title"> - <string>Distance computation</string> + <string>Guideline used for distance computation</string> </property> <layout class="QGridLayout" name="gridLayout_3"> <item row="1" column="0"> @@ -120,7 +120,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Second guide-line</string> + <string>Second guideline</string> </property> </widget> </item> @@ -130,7 +130,7 @@ <bool>true</bool> </property> <property name="text"> - <string>First guide-line</string> + <string>First guideline</string> </property> </widget> </item> @@ -165,12 +165,12 @@ <bool>true</bool> </property> <property name="text"> - <string>First cross section</string> + <string>First cross-section</string> </property> </widget> </item> <item> - <widget class="QComboBox" name="comboBox_begin_kp"> + <widget class="QComboBox" name="comboBox_begin_rk"> <property name="enabled"> <bool>true</bool> </property> @@ -186,12 +186,12 @@ <bool>true</bool> </property> <property name="text"> - <string>Last cross section</string> + <string>Last cross-section</string> </property> </widget> </item> <item> - <widget class="QComboBox" name="comboBox_end_kp"> + <widget class="QComboBox" name="comboBox_end_rk"> <property name="enabled"> <bool>true</bool> </property> diff --git a/src/View/ui/NewStudy.ui b/src/View/ui/NewStudy.ui index 47b216c1d01f55a259fb74c6827983a0f0e05d5c..e087241c63dad058c72eae3b9238fa1f5f9b3cac 100644 --- a/src/View/ui/NewStudy.ui +++ b/src/View/ui/NewStudy.ui @@ -127,7 +127,7 @@ <bool>false</bool> </property> <property name="text"> - <string>Staring date</string> + <string>Starting date</string> </property> </widget> </item> diff --git a/src/View/ui/PurgeOptions.ui b/src/View/ui/PurgeOptions.ui new file mode 100644 index 0000000000000000000000000000000000000000..9b027f970082de86d835c0dfbfbeacadc0594f7e --- /dev/null +++ b/src/View/ui/PurgeOptions.ui @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>194</width> + <height>114</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="1" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Number of points to keep</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QSpinBox" name="spinBox_np_purge"> + <property name="minimum"> + <number>3</number> + </property> + <property name="maximum"> + <number>999999999</number> + </property> + <property name="value"> + <number>24</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/View/ui/REPLineDialog.ui b/src/View/ui/REPLineDialog.ui index 9d5722ae52ed71a9e1bbd84abad31beab815390b..25902f3f6ea53187d52f40eff243ae6cf4a3aac5 100644 --- a/src/View/ui/REPLineDialog.ui +++ b/src/View/ui/REPLineDialog.ui @@ -54,7 +54,7 @@ <item row="1" column="1"> <widget class="QLineEdit" name="lineEdit_line"> <property name="toolTip"> - <string>Comment lines start with '*' char (let see the mage documentation for more details)</string> + <string><html><head/><body><p>Comment lines start with '*' char (let see Mage documentation for more details)</p></body></html></string> </property> </widget> </item> diff --git a/src/View/ui/REPLineList.ui b/src/View/ui/REPLineList.ui index ea3fe793a471c9680229d8d66be671074eb572a5..b2c713365230c2f8feada04d43056e127ee4712f 100644 --- a/src/View/ui/REPLineList.ui +++ b/src/View/ui/REPLineList.ui @@ -37,13 +37,13 @@ <action name="action_add"> <property name="icon"> <iconset> - <normaloff>ressources/add.png</normaloff>ressources/gtk-add.png</iconset> + <normaloff>ressources/add.png</normaloff>ressources/add.png</iconset> </property> <property name="text"> <string>Add</string> </property> <property name="toolTip"> - <string>Add new additional line</string> + <string>Add new line</string> </property> <property name="shortcut"> <string>Ctrl+N</string> @@ -52,7 +52,7 @@ <action name="action_delete"> <property name="icon"> <iconset> - <normaloff>ressources/del.png</normaloff>ressources/gtk-remove.png</iconset> + <normaloff>ressources/del.png</normaloff>ressources/del.png</iconset> </property> <property name="text"> <string>Delete</string> diff --git a/src/View/ui/ReachSedimentLayers.ui b/src/View/ui/ReachSedimentLayers.ui index dfe39a823d819abf8351e0cf9cc490438fb209be..4aaf140d0bc93ed6f97914becb62a5b1eefa5540 100644 --- a/src/View/ui/ReachSedimentLayers.ui +++ b/src/View/ui/ReachSedimentLayers.ui @@ -35,7 +35,7 @@ <item> <widget class="QPushButton" name="pushButton_apply"> <property name="text"> - <string>Apply sediment layers on all reach</string> + <string>Apply sediment layers to all reaches</string> </property> </widget> </item> @@ -80,7 +80,7 @@ <string>Edit profile</string> </property> <property name="toolTip"> - <string>Edit profile sediment layer</string> + <string>Edit sediment layers of the profile</string> </property> </action> </widget> diff --git a/src/View/ui/Reservoir.ui b/src/View/ui/Reservoir.ui index 97e0d7a1972f79fcafd68dd2adeb7ee62fde570d..b5f89ff02070628227cac98db65163038fdf6d67 100644 --- a/src/View/ui/Reservoir.ui +++ b/src/View/ui/Reservoir.ui @@ -99,7 +99,7 @@ <string>Edit</string> </property> <property name="toolTip"> - <string>Edit elevation/surface law</string> + <string>Edit stage-area relation</string> </property> </action> <action name="action_sort"> @@ -111,7 +111,7 @@ <string>Sort</string> </property> <property name="toolTip"> - <string>Sort points by elevation</string> + <string>Sort points by elevations</string> </property> </action> </widget> diff --git a/src/View/ui/ReservoirList.ui b/src/View/ui/ReservoirList.ui index c53a7cd6602bc975f6f3fe7352eceb2db5c1464c..aecfc26aae130b6eeb6d07f01a08e00c18ef6c1c 100644 --- a/src/View/ui/ReservoirList.ui +++ b/src/View/ui/ReservoirList.ui @@ -75,7 +75,7 @@ <action name="action_add"> <property name="icon"> <iconset> - <normaloff>ressources/add.png</normaloff>ressources/gtk-add.png</iconset> + <normaloff>ressources/add.png</normaloff>ressources/add.png</iconset> </property> <property name="text"> <string>Add</string> @@ -87,7 +87,7 @@ <action name="action_delete"> <property name="icon"> <iconset> - <normaloff>ressources/del.png</normaloff>ressources/gtk-remove.png</iconset> + <normaloff>ressources/del.png</normaloff>ressources/del.png</iconset> </property> <property name="text"> <string>Delete</string> @@ -105,7 +105,7 @@ <string>Edit</string> </property> <property name="toolTip"> - <string>Edit reservoir law</string> + <string>Edit reservoir equations</string> </property> </action> </widget> diff --git a/src/View/ui/Results.ui b/src/View/ui/Results.ui index c7c319a0bd6fe066d6ba46bcf777cb628e277905..e92bdab43aa2bd8be8990d5e165be34f81376aab 100644 --- a/src/View/ui/Results.ui +++ b/src/View/ui/Results.ui @@ -39,19 +39,6 @@ <item> <widget class="QTableView" name="tableView_profile"/> </item> - <item> - <widget class="QSlider" name="verticalSlider_profile"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="invertedAppearance"> - <bool>true</bool> - </property> - <property name="invertedControls"> - <bool>true</bool> - </property> - </widget> - </item> </layout> </widget> </widget> @@ -147,6 +134,9 @@ <property name="currentIndex"> <number>1</number> </property> + <property name="tabsClosable"> + <bool>true</bool> + </property> <widget class="QWidget" name="tab_4"> <attribute name="title"> <string>Raw data</string> @@ -198,7 +188,7 @@ </widget> <widget class="QWidget" name="tab_2"> <attribute name="title"> - <string>Hydrograph</string> + <string>Discharge time series</string> </attribute> <layout class="QGridLayout" name="gridLayout_4"> <item row="0" column="0"> @@ -251,7 +241,7 @@ <string>Add</string> </property> <property name="toolTip"> - <string>Add custom visualization</string> + <string>Add customized visualization</string> </property> </action> <action name="action_reload"> @@ -272,7 +262,7 @@ <string>Export</string> </property> <property name="toolTip"> - <string>Export raw data</string> + <string>Export data as CSV</string> </property> <property name="shortcut"> <string>Ctrl+E</string> diff --git a/src/View/ui/Stricklers.ui b/src/View/ui/Stricklers.ui index a7dbd8e9d7efdd562fbca1da0d9d912a147e1036..437ee7828a40ba15d8e58bdd3bc0952a8000dc86 100644 --- a/src/View/ui/Stricklers.ui +++ b/src/View/ui/Stricklers.ui @@ -25,7 +25,7 @@ </property> <widget class="QGroupBox" name="groupBox_2"> <property name="title"> - <string>Study stricklers</string> + <string>Strickler coefficients of the study</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> @@ -35,7 +35,7 @@ </widget> <widget class="QGroupBox" name="groupBox"> <property name="title"> - <string>Application stricklers</string> + <string>Strickler coefficients of the application</string> </property> <layout class="QGridLayout" name="gridLayout_2"> <item row="0" column="0"> @@ -70,7 +70,7 @@ <string>Add</string> </property> <property name="toolTip"> - <string>Add new stricklers</string> + <string>Add new Strickler coefficients</string> </property> </action> <action name="action_del"> @@ -82,7 +82,7 @@ <string>Delete</string> </property> <property name="toolTip"> - <string>Delete selected stricklers</string> + <string>Delete selected Strickler coefficients</string> </property> </action> <action name="action_sort"> @@ -94,7 +94,7 @@ <string>Sort</string> </property> <property name="toolTip"> - <string>Sort stricklers</string> + <string>Sort Strickler coefficients</string> </property> </action> </widget> diff --git a/src/View/ui/UpdateKPOptions.ui b/src/View/ui/UpdateRKOptions.ui similarity index 67% rename from src/View/ui/UpdateKPOptions.ui rename to src/View/ui/UpdateRKOptions.ui index 881491bad9fac97dbf4c6a70017cf771630df7d1..0f5f1b88a5e90a09b3cd46079115d1851c6d4230 100644 --- a/src/View/ui/UpdateKPOptions.ui +++ b/src/View/ui/UpdateRKOptions.ui @@ -6,37 +6,23 @@ <rect> <x>0</x> <y>0</y> - <width>340</width> - <height>204</height> + <width>381</width> + <height>302</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> <layout class="QGridLayout" name="gridLayout_4"> - <item row="1" column="0"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> <item row="0" column="0"> <widget class="QGroupBox" name="groupBox_3"> <property name="title"> <string>Distance computation</string> </property> <layout class="QGridLayout" name="gridLayout_3"> - <item row="3" column="1"> - <widget class="QComboBox" name="comboBox_end_gl"> - <property name="enabled"> - <bool>true</bool> - </property> - </widget> - </item> <item row="3" column="0"> <widget class="QLabel" name="label_6"> <property name="enabled"> @@ -47,11 +33,17 @@ </property> </widget> </item> - <item row="2" column="1"> - <widget class="QComboBox" name="comboBox_begin_gl"> - <property name="enabled"> - <bool>true</bool> + <item row="5" column="1"> + <widget class="QRadioButton" name="radioButton_1"> + <property name="text"> + <string>Upstream to downstream</string> </property> + <property name="checked"> + <bool>false</bool> + </property> + <attribute name="buttonGroup"> + <string notr="true">buttonGroup_orientation</string> + </attribute> </widget> </item> <item row="2" column="0"> @@ -67,6 +59,19 @@ <item row="0" column="1"> <widget class="QComboBox" name="comboBox_origin"/> </item> + <item row="1" column="1"> + <widget class="QDoubleSpinBox" name="doubleSpinBox_origin"> + <property name="decimals"> + <number>4</number> + </property> + <property name="minimum"> + <double>-99999999.000000000000000</double> + </property> + <property name="maximum"> + <double>99999999.000000000000000</double> + </property> + </widget> + </item> <item row="0" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> @@ -74,6 +79,13 @@ </property> </widget> </item> + <item row="2" column="1"> + <widget class="QComboBox" name="comboBox_begin_gl"> + <property name="enabled"> + <bool>true</bool> + </property> + </widget> + </item> <item row="1" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> @@ -81,22 +93,59 @@ </property> </widget> </item> - <item row="1" column="1"> - <widget class="QDoubleSpinBox" name="doubleSpinBox_origin"> - <property name="decimals"> - <number>4</number> + <item row="9" column="1"> + <widget class="QRadioButton" name="radioButton_2"> + <property name="text"> + <string>Downstream to upstream</string> </property> - <property name="minimum"> - <double>-99999999.000000000000000</double> + <attribute name="buttonGroup"> + <string notr="true">buttonGroup_orientation</string> + </attribute> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="comboBox_end_gl"> + <property name="enabled"> + <bool>true</bool> </property> - <property name="maximum"> - <double>99999999.000000000000000</double> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Orientation</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QRadioButton" name="radioButton_0"> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> </property> + <property name="text"> + <string>Keep current</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <attribute name="buttonGroup"> + <string notr="true">buttonGroup_orientation</string> + </attribute> </widget> </item> </layout> </widget> </item> + <item row="1" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> </layout> </widget> <resources/> @@ -134,4 +183,7 @@ </hints> </connection> </connections> + <buttongroups> + <buttongroup name="buttonGroup_orientation"/> + </buttongroups> </ui> diff --git a/src/View/ui/Widgets/MainWindowTabInfo.ui b/src/View/ui/Widgets/MainWindowTabInfo.ui index 9c09579d0b14308eb7aea4ea63d0181bfd0f0326..8de4e3233b8f8fb78e4002e4423cf159738a968f 100644 --- a/src/View/ui/Widgets/MainWindowTabInfo.ui +++ b/src/View/ui/Widgets/MainWindowTabInfo.ui @@ -182,7 +182,7 @@ <item row="2" column="3"> <widget class="QLabel" name="label_11"> <property name="text"> - <string>Lateral contributions:</string> + <string>Lateral sources:</string> </property> </widget> </item> diff --git a/src/View/ui/Widgets/d50sigma.ui b/src/View/ui/Widgets/d50sigma.ui index d1af908b9cd03702ac6042059fd5920f31f0ac7e..5518cbcd8c8a96c3183f242f411ad5452186d196 100644 --- a/src/View/ui/Widgets/d50sigma.ui +++ b/src/View/ui/Widgets/d50sigma.ui @@ -22,7 +22,7 @@ <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <widget class="QWidget" name=""> + <widget class="QWidget" name="layoutWidget"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="label_d50"> @@ -37,13 +37,13 @@ <locale language="English" country="Europe"/> </property> <property name="decimals"> - <number>4</number> + <number>8</number> </property> </widget> </item> </layout> </widget> - <widget class="QWidget" name=""> + <widget class="QWidget" name="layoutWidget"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QLabel" name="label_sigma"> diff --git a/src/View/ui/ressources/icon.ico b/src/View/ui/ressources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8b8894aa4667a5c60f06a3d6e741c4fc35c330e3 Binary files /dev/null and b/src/View/ui/ressources/icon.ico differ diff --git a/src/lang/fr.ts b/src/lang/fr.ts index 4b3a4a4d4ba613ce312a551c2dd776f42103ee11..dc68faec58666e9fbf3176e22580bdfb3b424b39 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -23,14 +23,14 @@ <message> <location filename="../View/AdditionalFiles/Translate.py" line="33"/> <source>Edit additional file</source> - <translation>Éditer le fichier supplémentaire</translation> + <translation>Modifier le fichier supplémentaire</translation> </message> </context> <context> <name>BasicHydraulicStructures</name> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="30"/> - <source>Basic Hydraulic Structures</source> + <source>Basic Hydraulic Structure</source> <translation>Ouvrage hydraulique élémentaire</translation> </message> <message> @@ -56,7 +56,7 @@ <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="57"/> <source>Half-angle tangent</source> - <translation>Tangeante du demi angle</translation> + <translation>Tangeante du demi-angle</translation> </message> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="60"/> @@ -96,7 +96,7 @@ <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="81"/> <source>Weir</source> - <translation>Seuil</translation> + <translation>Déversoir</translation> </message> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="84"/> @@ -128,20 +128,15 @@ <source>Parameter 5</source> <translation>Paramètre 5</translation> </message> - <message> - <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="110"/> - <source>Discharge weir</source> - <translation>Seuil déversoir</translation> - </message> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="113"/> <source>Trapezoidal weir</source> - <translation>Seuil trapézoidal</translation> + <translation>Déversoir trapézoidal</translation> </message> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="116"/> <source>Triangular weir</source> - <translation>Seuil triangulaire</translation> + <translation>Déversoir triangulaire</translation> </message> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="119"/> @@ -180,16 +175,21 @@ </message> <message> <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="140"/> - <source>User defined</source> + <source>User-defined</source> <translation>Défini par l'utilisateur</translation> </message> + <message> + <location filename="../View/HydraulicStructures/BasicHydraulicStructures/Translate.py" line="110"/> + <source>Discharge weir</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>BoundaryCondition</name> <message> <location filename="../View/BoundaryCondition/Edit/translate.py" line="32"/> <source>Edit boundary conditions</source> - <translation>Éditer les conditions aux limites</translation> + <translation>Modifier les conditions aux limites</translation> </message> <message> <location filename="../View/BoundaryCondition/Edit/translate.py" line="37"/> @@ -208,7 +208,7 @@ </message> <message> <location filename="../View/BoundaryCondition/translate.py" line="51"/> - <source>Ponctual contribution</source> + <source>Point sources</source> <translation>Apports ponctuels</translation> </message> <message> @@ -267,49 +267,79 @@ </message> <message> <location filename="../Checker/Mage.py" line="209"/> - <source>Mage geometry guide line checker</source> + <source>Mage geometry guideline checker</source> <translation>Vérificateur des lignes directrices pour Mage</translation> </message> <message> <location filename="../Checker/Mage.py" line="210"/> - <source>Check if exists geometry guide line is correctly defined for each reaches of the study</source> - <translation>Vérifie si les lignes directrices sont définies pour tous les biefs</translation> + <source>Check if geometry guidelines are correctly defined for each reach</source> + <translation type="obsolete">Vérifie si les lignes directrices sont définies pour tous les biefs</translation> </message> <message> - <location filename="../Checker/Study.py" line="33"/> - <source>Study network reach checker</source> + <location filename="../Checker/Study.py" line="37"/> + <source>Study reach network checker</source> <translation>Vérificateur des biefs de l'étude</translation> </message> <message> <location filename="../Checker/Study.py" line="34"/> - <source>Check if exists at least one reach for study</source> - <translation>Vérifie si il existe au moins un bief dans l'étude</translation> + <source>Check if at least one reach exists</source> + <translation type="obsolete">Vérifie s'il existe au moins un bief dans l'étude</translation> </message> <message> - <location filename="../Checker/Study.py" line="66"/> + <location filename="../Checker/Study.py" line="64"/> <source>Study geometry checker</source> <translation>Vérificateur de géometrie de l'étude</translation> </message> <message> - <location filename="../Checker/Study.py" line="67"/> - <source>Check if exists geometry for each reach of study</source> - <translation>Vérifie si la géométrie existe pour chaque bief de l'étude</translation> + <location filename="../Checker/Study.py" line="65"/> + <source>Check if the geometry of each reach exists</source> + <translation>Vérifie si la géométrie de chaque bief de l'étude existe</translation> </message> <message> - <location filename="../Checker/Study.py" line="114"/> + <location filename="../Checker/Study.py" line="224"/> <source>Dummy ok</source> <translation>Dummy ok</translation> </message> <message> - <location filename="../Checker/Study.py" line="130"/> + <location filename="../Checker/Study.py" line="240"/> <source>Dummy warning</source> <translation>Dummy warning</translation> </message> <message> - <location filename="../Checker/Study.py" line="146"/> + <location filename="../Checker/Study.py" line="256"/> <source>Dummy error</source> <translation>Dummy error</translation> </message> + <message> + <location filename="../Checker/Study.py" line="105"/> + <source>Study initial conditions checker</source> + <translation>Vérificateur des conditions initial de l'étude</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="106"/> + <source>Check initial conditions for each node of study</source> + <translation>Vérifie les conditions initial de l'étude pour chaque nÅ“ud</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="154"/> + <source>Study boundary conditions checker</source> + <translation>Vérificateur des conditions aux limites de l'étude</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="155"/> + <source>Check boundary conditions for each node of study</source> + <translation>Vérifie les conditions aux limites de l'étude pour chaque nÅ“ud</translation> + </message> + <message> + <location filename="../Checker/Mage.py" line="210"/> + <source>Check if exists geometry guidelines are correctly defined for each reach</source> + <translation>Vérifie si il existe des lignes directrices correctement définie pour chaque bief</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="38"/> + <source>Check if exists at least one reach exists</source> + <translation>Vérificateur si il exists au moins un bief</translation> + </message> </context> <context> <name>Checklist</name> @@ -414,12 +444,12 @@ <name>CustomPlot</name> <message> <location filename="../View/Results/CustomPlot/Translate.py" line="30"/> - <source>Custom Plot Selection</source> + <source>Custom plot selection</source> <translation>Sélection des graphiques personnalisés</translation> </message> <message> <location filename="../View/Results/CustomPlot/Translate.py" line="46"/> - <source>Bed load elevation (m)</source> + <source>Bed elevation (m)</source> <translation>Cote du fond (m)</translation> </message> </context> @@ -434,9 +464,9 @@ <context> <name>Dialog</name> <message> - <location filename="../View/ui/SelectSolver.ui" line="14"/> + <location filename="../View/ui/MeshingOptions.ui" line="14"/> <source>Dialog</source> - <translation>Dialog</translation> + <translation>Dialogue</translation> </message> <message> <location filename="../View/ui/REPLineDialog.ui" line="50"/> @@ -449,7 +479,7 @@ <translation>Type</translation> </message> <message> - <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="48"/> + <location filename="../View/ui/NewStudy.ui" line="92"/> <source>Description</source> <translation>Description</translation> </message> @@ -471,7 +501,7 @@ <message> <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="110"/> <source><html><head/><body><p>Command line format, for input formater (optional), solver execution and output formater (optional). This format can use some replacement values like:</p><p>- <span style=" font-style:italic;">@install_dir</span>: The Pamhyr2 install path</p><p>- <span style=" font-style:italic;">@path</span>: The associate path</p><p>- <span style=" font-style:italic;">@input</span>: Solver input data (depend of solver type)</p><p>-<span style=" font-style:italic;"> @output</span>: Solver output data (depend of solver type)</p><p>- <span style=" font-style:italic;">@dir</span>: The working dir at solver execution</p><p>- <span style=" font-style:italic;">@args</span>: Solver arguments and additional arguments defined on solver parameters</p></body></html></source> - <translation><html><head/><body><p>Format des lignes de commandes. Ce format peut contenir des valeurs de remplacement, tel que :</p><p>- <span style=" font-style:italic;">@install_dir</span>: Le dossier d'installation de Pamhyr2</p><p>- <span style=" font-style:italic;">@path</span>: Le chemin associé à cette commande</p><p>- <span style=" font-style:italic;">@input</span>: Les données d'entrées du solveur générées par Pamhyr2 (dépend du solveur)</p><p>-<span style=" font-style:italic;"> @output</span>: Les données d'entrées du solveur générées par Pamhyr2 (dépend du solveur)</p><p>- <span style=" font-style:italic;">@dir</span>: Le chemin vers le dossier d'execution du solveur</p><p>- <span style=" font-style:italic;">@args</span>: Arguments du solveur et arguments complémentaires définis dans les paramètres du solveur</p></body></html></translation> + <translation><html><head/><body><p>Format des lignes de commandes. Ce format peut contenir des valeurs de remplacement, telles que :</p><p>- <span style=" font-style:italic;">@install_dir</span>: Le dossier d'installation de Pamhyr2</p><p>- <span style=" font-style:italic;">@path</span>: Le chemin associé à cette commande</p><p>- <span style=" font-style:italic;">@input</span>: Les données d'entrées du solveur générées par Pamhyr2 (dépend du solveur)</p><p>-<span style=" font-style:italic;"> @output</span>: Les données d'entrées du solveur générées par Pamhyr2 (dépend du solveur)</p><p>- <span style=" font-style:italic;">@dir</span>: Le chemin vers le dossier d'execution du solveur</p><p>- <span style=" font-style:italic;">@args</span>: Arguments du solveur et arguments complémentaires définis dans les paramètres du solveur</p></body></html></translation> </message> <message> <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="116"/> @@ -506,7 +536,7 @@ <message> <location filename="../View/ui/InitialConditions_Dialog_Generator_Height.ui" line="25"/> <source>Height</source> - <translation>Hauteur</translation> + <translation type="obsolete">Hauteur</translation> </message> <message> <location filename="../View/ui/ConfigureDialog.ui" line="47"/> @@ -514,62 +544,62 @@ <translation>Solveurs</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="121"/> + <location filename="../View/ui/ConfigureDialog.ui" line="148"/> <source>Backup</source> <translation>Sauvegarde auto</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="133"/> + <location filename="../View/ui/ConfigureDialog.ui" line="160"/> <source>Auto save</source> <translation>Sauvegarde auto</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="140"/> - <source>Frequence</source> + <location filename="../View/ui/ConfigureDialog.ui" line="167"/> + <source>Frequency</source> <translation>Fréquence</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="151"/> + <location filename="../View/ui/ConfigureDialog.ui" line="178"/> <source>Enable</source> <translation>Activée</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="177"/> + <location filename="../View/ui/ConfigureDialog.ui" line="204"/> <source>HH:mm:ss</source> <translation>HH:mm:ss</translation> </message> <message> <location filename="../View/ui/ConfigureDialog.ui" line="214"/> - <source>Stricklers</source> - <translation>Stricklers</translation> + <source>Strickler coefficients</source> + <translation type="obsolete">Coefficients de Strickler</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="274"/> + <location filename="../View/ui/ConfigureDialog.ui" line="328"/> <source>Editor</source> <translation>Editeur</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="298"/> - <source>This value must be used for reading or editing files in speficic case.</source> + <location filename="../View/ui/ConfigureDialog.ui" line="352"/> + <source>This value must be used for reading or editing files in speficic cases.</source> <translation>Cette valeur peut être utilisée dans des cas spécifiques pour lire ou écrire dans un fichier.</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="307"/> + <location filename="../View/ui/ConfigureDialog.ui" line="361"/> <source>Editor command</source> - <translation>Commande d'édition</translation> + <translation>Commande d'modification</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="323"/> - <source> - The "@file" keyworkd is replace by the path of file to open.</source> + <location filename="../View/ui/ConfigureDialog.ui" line="377"/> + <source> - The "@file" keyword is replaced by the path of file to open.</source> <translation> - Le mot clef "@file" sera remplacé par le chemin du fichier à ouvrir.</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="353"/> + <location filename="../View/ui/ConfigureDialog.ui" line="407"/> <source>Language</source> - <translation>Langage</translation> + <translation>Langue</translation> </message> <message> - <location filename="../View/ui/ConfigureDialog.ui" line="344"/> + <location filename="../View/ui/ConfigureDialog.ui" line="398"/> <source>Please restart application after language modification</source> <translation>Un redémarrage de l'application est requis après le changement de langue</translation> </message> @@ -590,7 +620,7 @@ </message> <message> <location filename="../View/ui/NewStudy.ui" line="130"/> - <source>Staring date</source> + <source>Starting date</source> <translation>Date de départ</translation> </message> <message> @@ -606,7 +636,7 @@ <message> <location filename="../View/ui/NewStudy.ui" line="167"/> <source>Last modification :</source> - <translation>Date de modification :</translation> + <translation>Dernière modification :</translation> </message> <message> <location filename="../View/ui/CustomPlotValuesSelectionDialog.ui" line="27"/> @@ -621,45 +651,45 @@ <message> <location filename="../View/ui/InitialConditions_Dialog_Generator_Discharge.ui" line="25"/> <source>Discharge</source> - <translation>Débit</translation> + <translation type="obsolete">Débit</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="33"/> - <source>First cross section</source> + <location filename="../View/ui/MeshingOptions.ui" line="168"/> + <source>First cross-section</source> <translation>Première section en travers</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="54"/> - <source>Last cross section</source> + <location filename="../View/ui/MeshingOptions.ui" line="189"/> + <source>Last cross-section</source> <translation>Dernière section en travers</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="79"/> - <source>First guide line</source> + <location filename="../View/ui/MeshingOptions.ui" line="133"/> + <source>First guideline</source> <translation>Première ligne directrice</translation> </message> <message> <location filename="../View/ui/MeshingOptions.ui" line="100"/> - <source>Last guide line</source> - <translation>Dernière ligne directrice</translation> + <source>Last guideline</source> + <translation type="obsolete">Dernière ligne directrice</translation> </message> <message> <location filename="../View/ui/MeshingOptions.ui" line="114"/> - <source>Section space step</source> + <source>Space step</source> <translation type="obsolete">Pas d'espace</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="164"/> - <source>Distance computation guild line</source> - <translation>Ligne directrice pour le calcule des distances</translation> + <location filename="../View/ui/MeshingOptions.ui" line="114"/> + <source>Guideline used for distance computation</source> + <translation>Ligne directrice pour le calcul des distances</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="204"/> + <location filename="../View/ui/MeshingOptions.ui" line="79"/> <source>Spline</source> <translation>Spline</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="214"/> + <location filename="../View/ui/MeshingOptions.ui" line="89"/> <source>Linear</source> <translation>Linéaire</translation> </message> @@ -690,29 +720,94 @@ </message> <message> <location filename="../View/ui/REPLineDialog.ui" line="57"/> - <source>Comment lines start with '*' char (let see the mage documentation for more details)</source> - <translation>Les lignes de commentaire commence pas un charactère '*' (voir la documentation de mage pour plus de détails)</translation> + <source>Comment lines start with '*' char (let see Mage documentation for more details)</source> + <translation type="obsolete">Les lignes de commentaire commencent par un caractère '*' (voir la documentation de Mage pour plus de détails)</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="20"/> + <location filename="../View/ui/MeshingOptions.ui" line="157"/> <source>Limits</source> <translation>Limites</translation> </message> <message> <location filename="../View/ui/MeshingOptions.ui" line="121"/> <source>Options</source> - <translation>Options</translation> + <translation type="obsolete">Options</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="127"/> - <source>Section space step (m)</source> + <location filename="../View/ui/MeshingOptions.ui" line="36"/> + <source>Space step (m)</source> <translation>Pas d'espace (m)</translation> </message> <message> - <location filename="../View/ui/MeshingOptions.ui" line="195"/> + <location filename="../View/ui/MeshingOptions.ui" line="70"/> <source>Type of interpolation:</source> <translation>Type d'interpolation :</translation> </message> + <message> + <location filename="../View/ui/InitialConditions_Dialog_Generator_Height.ui" line="25"/> + <source>Height (m)</source> + <translation>Hauteur (m)</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="241"/> + <source>Stricklers</source> + <translation>Stricklers</translation> + </message> + <message> + <location filename="../View/ui/UpdateRKOptions.ui" line="30"/> + <source>Distance computation</source> + <translation>Calcule des distances</translation> + </message> + <message> + <location filename="../View/ui/UpdateRKOptions.ui" line="46"/> + <source>Second guide-line</source> + <translation>Deuxième ligne directrice</translation> + </message> + <message> + <location filename="../View/ui/UpdateRKOptions.ui" line="63"/> + <source>First guide-line</source> + <translation>Première ligne directrice</translation> + </message> + <message> + <location filename="../View/ui/UpdateRKOptions.ui" line="73"/> + <source>Origin</source> + <translation>Origine</translation> + </message> + <message> + <location filename="../View/ui/UpdateRKOptions.ui" line="80"/> + <source>Origin value</source> + <translation>Valeur de l'origine</translation> + </message> + <message> + <location filename="../View/ui/InitialConditions_Dialog_Generator_Discharge.ui" line="25"/> + <source>Discharge (m³/s)</source> + <translation type="obsolete">Débit (m³/s)</translation> + </message> + <message> + <location filename="../View/ui/MeshingOptions.ui" line="30"/> + <source>Parameters</source> + <translation>Paramètres</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="385"/> + <source>Langue</source> + <translation>Langue</translation> + </message> + <message> + <location filename="../View/ui/REPLineDialog.ui" line="57"/> + <source><html><head/><body><p>Comment lines start with '*' char (let see Mage documentation for more details)</p></body></html></source> + <translation>Les lignes de commentaire commencent par un caractère '*' (voir la documentation de Mage pour plus de détails)</translation> + </message> + <message> + <location filename="../View/ui/MeshingOptions.ui" line="123"/> + <source>Second guideline</source> + <translation>Seconde ligne directrice</translation> + </message> + <message encoding="UTF-8"> + <location filename="../View/ui/InitialConditions_Dialog_Generator_Discharge.ui" line="25"/> + <source>Discharge (m³/s)</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>Documentation</name> @@ -725,102 +820,102 @@ <context> <name>Exception</name> <message> - <location filename="../Model/Except.py" line="54"/> + <location filename="../Model/Except.py" line="55"/> <source>Generic error message</source> <translation>Message d'erreur générique</translation> </message> <message> - <location filename="../Model/Except.py" line="60"/> + <location filename="../Model/Except.py" line="61"/> <source>Undefined error message</source> <translation>Message d'erreur non définie</translation> </message> <message> - <location filename="../Model/Except.py" line="75"/> + <location filename="../Model/Except.py" line="76"/> <source>Method not implemented</source> <translation>Méthode non implémentée</translation> </message> <message> - <location filename="../Model/Except.py" line="104"/> + <location filename="../Model/Except.py" line="105"/> <source>Method</source> <translation>Méthode</translation> </message> <message> - <location filename="../Model/Except.py" line="104"/> + <location filename="../Model/Except.py" line="105"/> <source>not implemented</source> <translation>non implémenté</translation> </message> <message> - <location filename="../Model/Except.py" line="104"/> + <location filename="../Model/Except.py" line="105"/> <source>for class</source> <translation>pour la classe</translation> </message> <message> - <location filename="../Model/Except.py" line="101"/> + <location filename="../Model/Except.py" line="102"/> <source>Not implemented method</source> <translation>Méthode non implémentée</translation> </message> <message> - <location filename="../Model/Except.py" line="115"/> + <location filename="../Model/Except.py" line="116"/> <source>FileFormatError</source> <translation>ErreurFormatDeFichier</translation> </message> <message> - <location filename="../Model/Except.py" line="123"/> + <location filename="../Model/Except.py" line="124"/> <source>Invalid file format:</source> <translation>Format de fichier invalide :</translation> </message> <message> - <location filename="../Model/Except.py" line="129"/> + <location filename="../Model/Except.py" line="130"/> <source>File format error</source> <translation>Erreur de format de fichier</translation> </message> <message> - <location filename="../Model/Except.py" line="132"/> + <location filename="../Model/Except.py" line="133"/> <source>Invalid file format</source> <translation>Format de fichier invalide</translation> </message> <message> - <location filename="../Model/Except.py" line="135"/> + <location filename="../Model/Except.py" line="136"/> <source>Invalid file</source> <translation>Fichier invalide</translation> </message> <message> - <location filename="../Model/Except.py" line="135"/> + <location filename="../Model/Except.py" line="136"/> <source>format because of</source> <translation>format à cause de</translation> </message> <message> - <location filename="../Model/Except.py" line="175"/> + <location filename="../Model/Except.py" line="176"/> <source>Clipboard format error</source> <translation>Erreur de format dans le presse-papier</translation> </message> <message> - <location filename="../Model/Except.py" line="157"/> + <location filename="../Model/Except.py" line="158"/> <source>without header</source> <translation>Sans en-tête</translation> </message> <message> - <location filename="../Model/Except.py" line="159"/> + <location filename="../Model/Except.py" line="160"/> <source>with header</source> <translation>Avec en-tête</translation> </message> <message> - <location filename="../Model/Except.py" line="164"/> + <location filename="../Model/Except.py" line="165"/> <source>Invalid clipboard data format:</source> <translation>Format des données du presse-papier invalide :</translation> </message> <message> - <location filename="../Model/Except.py" line="178"/> + <location filename="../Model/Except.py" line="179"/> <source>Clipboard format unknown</source> <translation>Presse-papier format inconnu</translation> </message> <message> - <location filename="../Model/Except.py" line="213"/> + <location filename="../Model/Except.py" line="214"/> <source>External file dependence is missing</source> <translation>Fichier externe d'une dépendence manquant</translation> </message> <message> - <location filename="../Model/Except.py" line="193"/> + <location filename="../Model/Except.py" line="194"/> <source>'@file' is missing for module @module: '@path'</source> <translation>le fichier '@file' est manquant pour le module @module : '@path'</translation> @@ -829,32 +924,32 @@ <context> <name>Form</name> <message> - <location filename="../View/ui/dummy.ui" line="14"/> + <location filename="../View/ui/Widgets/MainWindowTabCheckers.ui" line="14"/> <source>Form</source> <translation>Formulaire</translation> </message> <message> - <location filename="../View/ui/about.ui" line="42"/> + <location filename="../View/ui/about.ui" line="32"/> <source>About Pamhyr2</source> <translation>À propos de Pamhyr2</translation> </message> <message> - <location filename="../View/ui/about.ui" line="49"/> + <location filename="../View/ui/about.ui" line="39"/> <source>...</source> <translation>...</translation> </message> <message> - <location filename="../View/ui/about.ui" line="56"/> + <location filename="../View/ui/about.ui" line="46"/> <source>Version: @version</source> - <translation>Version: @version</translation> + <translation>Version : @version</translation> </message> <message> - <location filename="../View/ui/about.ui" line="63"/> + <location filename="../View/ui/about.ui" line="53"/> <source>License: GPLv3+</source> - <translation>Licence: GPLv3+</translation> + <translation>Licence : GPLv3+</translation> </message> <message> - <location filename="../View/ui/about.ui" line="70"/> + <location filename="../View/ui/about.ui" line="60"/> <source><a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a></source> <translation><a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Code source</a></translation> </message> @@ -906,7 +1001,7 @@ <message> <location filename="../View/ui/Widgets/MainWindowTabInfo.ui" line="74"/> <source>River network</source> - <translation>Réseau de la rivière</translation> + <translation>Réseau hydrographique</translation> </message> <message> <location filename="../View/ui/Widgets/MainWindowTabInfo.ui" line="90"/> @@ -985,8 +1080,8 @@ </message> <message> <location filename="../View/ui/Widgets/MainWindowTabInfo.ui" line="185"/> - <source>Lateral contributions:</source> - <translation>Contributions latérales :</translation> + <source>Lateral sources:</source> + <translation>Apports latéraux :</translation> </message> <message> <location filename="../View/ui/Widgets/MainWindowTabInfo.ui" line="192"/> @@ -1016,56 +1111,66 @@ <message> <location filename="../View/ui/Widgets/MainWindowTabCheckers.ui" line="39"/> <source>Errors summary</source> - <translation>Résumer des erreurs</translation> + <translation>Résumé des erreurs</translation> </message> </context> <context> <name>Frictions</name> <message> <location filename="../View/Frictions/translate.py" line="31"/> - <source>Stricklers</source> - <translation>Stricklers</translation> + <source>Strickler coefficients</source> + <translation type="obsolete">Coefficients de Strickler</translation> </message> <message> <location filename="../View/Frictions/translate.py" line="35"/> - <source>Edit frictions</source> - <translation>Éditer les frottements</translation> + <source>Edit friction coefficients</source> + <translation type="obsolete">Modifier les frottements</translation> </message> <message> <location filename="../View/Frictions/translate.py" line="42"/> - <source>Begin kp (m)</source> - <translation>Pk de départ (m)</translation> + <source>Start (m)</source> + <translation>PK de départ (m)</translation> </message> <message> <location filename="../View/Frictions/translate.py" line="43"/> - <source>End kp (m)</source> - <translation>Pk de fin (m)</translation> + <source>End (m)</source> + <translation>PK de fin (m)</translation> </message> <message> <location filename="../View/Frictions/translate.py" line="44"/> - <source>Begin strickler</source> - <translation>Strickler de départ</translation> + <source>Start coefficient</source> + <translation>Coefficient de départ</translation> </message> <message> <location filename="../View/Frictions/translate.py" line="45"/> - <source>End strickler</source> - <translation>Strickler de fin</translation> + <source>End coefficient</source> + <translation>Coefficient de fin</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="31"/> + <source>Stricklers</source> + <translation>Stricklers</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="35"/> + <source>Edit frictions</source> + <translation>Éditer les frotements</translation> </message> </context> <context> <name>Geometry</name> <message> - <location filename="../View/LateralContribution/translate.py" line="52"/> + <location filename="../View/Geometry/Translate.py" line="58"/> <source>X (m)</source> <translation>X (m)</translation> </message> <message> - <location filename="../View/LateralContribution/translate.py" line="53"/> + <location filename="../View/Geometry/Translate.py" line="59"/> <source>Y (m)</source> <translation>Y (m)</translation> </message> <message> - <location filename="../View/LateralContribution/translate.py" line="54"/> + <location filename="../View/Geometry/Translate.py" line="60"/> <source>Z (m)</source> <translation>Z (m)</translation> </message> @@ -1081,71 +1186,71 @@ </message> <message> <location filename="../View/Geometry/Translate.py" line="35"/> - <source>File mage geometry (*.ST *.st)</source> - <translation>Fichier géométrie Mage (*.ST *.st)</translation> + <source>Mage geometry file (*.ST *.st)</source> + <translation>Fichier Mage de géométrie (*.ST *.st)</translation> </message> <message> <location filename="../View/Geometry/Translate.py" line="37"/> - <source>File mage meshed geometry (*.M *.m)</source> - <translation>Fichier géométrie maillée (*.M *.m)</translation> + <source>Mage meshed geometry file (*.M *.m)</source> + <translation>Fichier Mage de géométrie maillée (*.M *.m)</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="39"/> - <source>All file (*)</source> + <location filename="../View/Geometry/Translate.py" line="41"/> + <source>All files (*)</source> <translation>Tous les fichiers (*)</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="43"/> + <location filename="../View/Geometry/Translate.py" line="45"/> <source>cross-section</source> <translation>section en travers</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="44"/> + <location filename="../View/Geometry/Translate.py" line="46"/> <source>cross-sections</source> <translation>sections en travers</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="46"/> + <location filename="../View/Geometry/Translate.py" line="48"/> <source>Transverse abscissa (m)</source> <translation>Abscisse en travers (m)</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="50"/> + <location filename="../View/Geometry/Translate.py" line="52"/> <source>Previous cross-section</source> <translation>Section en travers précédente</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="53"/> + <location filename="../View/Geometry/Translate.py" line="55"/> <source>Cross-section</source> <translation>Section en travers</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="54"/> + <location filename="../View/Geometry/Translate.py" line="56"/> <source>Next cross-section</source> <translation>Section en travers suivante</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="63"/> + <location filename="../View/Geometry/Translate.py" line="65"/> <source>Points</source> <translation>Points</translation> </message> <message> <location filename="../View/Geometry/Translate.py" line="67"/> <source>First guideline</source> - <translation>Première ligne directrice</translation> + <translation type="obsolete">Première ligne directrice</translation> </message> <message> <location filename="../View/Geometry/Translate.py" line="68"/> <source>Second guideline</source> - <translation>Seconde ligne directrice</translation> + <translation type="obsolete">Seconde ligne directrice</translation> </message> <message> <location filename="../View/Geometry/Translate.py" line="69"/> - <source>Means between the two guideline</source> - <translation>Moyenne entre les deux lignes directrices</translation> + <source>Mean over the two guidelines</source> + <translation type="obsolete">Moyenne sur les deux lignes directrices</translation> </message> <message> - <location filename="../View/Geometry/Translate.py" line="77"/> + <location filename="../View/Geometry/Translate.py" line="82"/> <source>Meshing</source> <translation>Maillage</translation> </message> @@ -1154,11 +1259,6 @@ <source>Geometry cross-section</source> <translation>Géométrie des sections en travers</translation> </message> - <message> - <location filename="../View/Geometry/Profile/Translate.py" line="40"/> - <source>Traversal abs (m)</source> - <translation>Absice en travers (m)</translation> - </message> <message> <location filename="../View/Geometry/Table.py" line="71"/> <source>upstream</source> @@ -1169,13 +1269,53 @@ <source>downstream</source> <translation>aval</translation> </message> + <message> + <location filename="../View/Geometry/Translate.py" line="39"/> + <source>Shapefile (*.SHP *.shp)</source> + <translation>Fichier shape (*.SHP *.shp)</translation> + </message> + <message> + <location filename="../View/Geometry/Translate.py" line="69"/> + <source>the first guide-line</source> + <translation>la première ligne directrice</translation> + </message> + <message> + <location filename="../View/Geometry/Translate.py" line="70"/> + <source>the second guide-line</source> + <translation>la seconde ligne directrice</translation> + </message> + <message> + <location filename="../View/Geometry/Profile/Translate.py" line="35"/> + <source>Width</source> + <translation>Largeur</translation> + </message> + <message> + <location filename="../View/Geometry/Profile/Translate.py" line="36"/> + <source>Area</source> + <translation>Aire</translation> + </message> + <message> + <location filename="../View/Geometry/Profile/Translate.py" line="37"/> + <source>Perimeter</source> + <translation>Périmètre</translation> + </message> + <message> + <location filename="../View/Geometry/Profile/Translate.py" line="44"/> + <source>Traversal abs (m)</source> + <translation>Abscisse en travers (m)</translation> + </message> + <message> + <location filename="../View/Geometry/Translate.py" line="71"/> + <source>the mean over the two guide-lines</source> + <translation>la moyenne entre les deux ligne directrice</translation> + </message> </context> <context> <name>HydraulicStructures</name> <message> <location filename="../View/HydraulicStructures/Translate.py" line="30"/> <source>Hydraulic Structures</source> - <translation>Structures hydrauliques</translation> + <translation>Ouvrages hydrauliques</translation> </message> <message> <location filename="../View/HydraulicStructures/Translate.py" line="34"/> @@ -1205,8 +1345,8 @@ <name>LateralContribution</name> <message> <location filename="../View/LateralContribution/Edit/translate.py" line="30"/> - <source>Edit lateral contribution</source> - <translation>Éditer les contributions latérales</translation> + <source>Edit lateral sources</source> + <translation>Modifier les contributions latérales</translation> </message> <message> <location filename="../View/LateralContribution/Edit/translate.py" line="35"/> @@ -1220,7 +1360,7 @@ </message> <message> <location filename="../View/LateralContribution/translate.py" line="47"/> - <source>Lateral contribution</source> + <source>Lateral sources</source> <translation>Contributions latérales</translation> </message> <message> @@ -1235,44 +1375,44 @@ </message> <message> <location filename="../View/LateralContribution/translate.py" line="60"/> - <source>Begin kp (m)</source> - <translation>Pk de départ (m)</translation> + <source>Start (m)</source> + <translation>PK de départ (m)</translation> </message> <message> <location filename="../View/LateralContribution/translate.py" line="61"/> - <source>End kp (m)</source> - <translation>Pk de fin (m)</translation> + <source>End (m)</source> + <translation>PK de fin (m)</translation> </message> </context> <context> <name>MainWindow</name> <message> - <location filename="../View/Translate.py" line="85"/> + <location filename="../View/Translate.py" line="86"/> <source>Open debug window</source> - <translation>Ouvrir la fenêtre de dégogage</translation> + <translation>Ouvrir la fenêtre de débogage</translation> </message> <message> - <location filename="../View/Translate.py" line="88"/> + <location filename="../View/Translate.py" line="89"/> <source>Open SQLite debuging tool ('sqlitebrowser')</source> - <translation>Ouvrir l'outis de dégogage SQLite ('sqlitebrowser')</translation> + <translation>Ouvrir l'outil de débogage SQLite ('sqlitebrowser')</translation> </message> <message> - <location filename="../View/Translate.py" line="91"/> - <source>Activate this window</source> + <location filename="../View/Translate.py" line="92"/> + <source>Enable this window</source> <translation>Activer cette fenêtre</translation> </message> <message> - <location filename="../View/ui/Stricklers.ui" line="14"/> + <location filename="../View/ui/GeometryReach.ui" line="14"/> <source>MainWindow</source> <translation>Fenêtre principale</translation> </message> <message> - <location filename="../View/ui/Stricklers.ui" line="52"/> - <source>toolBar</source> - <translation>Barre d'outils</translation> + <location filename="../View/ui/GeometryReach.ui" line="112"/> + <source>Toolbar</source> + <translation type="obsolete">Barre d'outils</translation> </message> <message> - <location filename="../View/ui/Stricklers.ui" line="70"/> + <location filename="../View/ui/GeometryReach.ui" line="169"/> <source>Add</source> <translation>Ajouter</translation> </message> @@ -1282,52 +1422,52 @@ <translation>Ajouter un casier</translation> </message> <message> - <location filename="../View/ui/Stricklers.ui" line="82"/> + <location filename="../View/ui/EditLateralContribution.ui" line="94"/> <source>Delete</source> <translation>Supprimer</translation> </message> <message> <location filename="../View/ui/ReservoirList.ui" line="96"/> <source>Delete reservoirs</source> - <translation>Supprimer casier(s)</translation> + <translation>Supprimer les casier(s)</translation> </message> <message> - <location filename="../View/ui/ReservoirList.ui" line="105"/> + <location filename="../View/ui/GeometryReach.ui" line="193"/> <source>Edit</source> - <translation>Éditer</translation> + <translation>modifier</translation> </message> <message> <location filename="../View/ui/ReservoirList.ui" line="108"/> - <source>Edit reservoir law</source> - <translation>Éditer la loi du(es) casier(s)</translation> + <source>Edit reservoir equations</source> + <translation>Modifier la loi des casiers</translation> </message> <message> - <location filename="../View/ui/Network.ui" line="218"/> + <location filename="../View/ui/Network.ui" line="118"/> <source>Add node or edge</source> <translation>Ajouter un nÅ“ud ou une arête</translation> </message> <message> - <location filename="../View/ui/Network.ui" line="230"/> + <location filename="../View/ui/Network.ui" line="130"/> <source>Remove node or edge</source> <translation>Supprimer un nÅ“ud ou une arête</translation> </message> <message> <location filename="../View/ui/EditSedimentLayers.ui" line="63"/> <source>Add sediment layer</source> - <translation>Ajouter une couche sedimentaire</translation> + <translation>Ajouter une couche sédimentaire</translation> </message> <message> <location filename="../View/ui/EditSedimentLayers.ui" line="78"/> <source>Delete sediment layer</source> - <translation>Supprimer une couche sedimentaire</translation> + <translation>Supprimer une couche sédimentaire</translation> </message> <message> <location filename="../View/ui/SedimentLayersList.ui" line="86"/> <source>Edit sediment layer</source> - <translation>Éditer la couche sedimentaire</translation> + <translation>Modifier la couche sédimentaire</translation> </message> <message> - <location filename="../View/ui/Results.ui" line="270"/> + <location filename="../View/ui/REPLineList.ui" line="79"/> <source>Ctrl+E</source> <translation>Ctrl+E</translation> </message> @@ -1337,192 +1477,192 @@ <translation>PAMHYR</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="98"/> + <location filename="../View/ui/MainWindow.ui" line="100"/> <source>&File</source> <translation>&Fichier</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="116"/> - <source>&River Network</source> + <location filename="../View/ui/MainWindow.ui" line="118"/> + <source>&River network</source> <translation>&Réseau</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="125"/> + <location filename="../View/ui/MainWindow.ui" line="127"/> <source>&Geometry</source> <translation>&Géometrie</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="134"/> + <location filename="../View/ui/MainWindow.ui" line="136"/> <source>&Execute</source> <translation>&Exécuter</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="144"/> + <location filename="../View/ui/MainWindow.ui" line="146"/> <source>&Hydraulics</source> <translation>&Hydraulique</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="155"/> + <location filename="../View/ui/MainWindow.ui" line="157"/> <source>&Results</source> <translation>&Résultats</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="162"/> + <location filename="../View/ui/MainWindow.ui" line="164"/> <source>&Help</source> <translation>&Aide</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="166"/> + <location filename="../View/ui/MainWindow.ui" line="168"/> <source>Help</source> <translation>Aide</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="170"/> + <location filename="../View/ui/MainWindow.ui" line="172"/> <source>Pamhyr2 </source> <translation>Pamhyr2 </translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="184"/> + <location filename="../View/ui/MainWindow.ui" line="186"/> <source>&Sediment</source> <translation>&Sédiment</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="195"/> + <location filename="../View/ui/MainWindow.ui" line="197"/> <source>&Windows</source> <translation>&Fenêtres</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="302"/> + <location filename="../View/ui/MainWindow.ui" line="333"/> <source>New study</source> <translation>Nouvelle étude</translation> </message> <message> - <location filename="../View/ui/BoundaryConditions.ui" line="120"/> + <location filename="../View/ui/EditLateralContribution.ui" line="85"/> <source>Ctrl+N</source> <translation>Ctrl+N</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="330"/> + <location filename="../View/ui/MainWindow.ui" line="361"/> <source>Open a study</source> <translation>Ouvrir une étude</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="333"/> + <location filename="../View/ui/MainWindow.ui" line="364"/> <source>Ctrl+O</source> <translation>Ctrl+O</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="342"/> + <location filename="../View/ui/MainWindow.ui" line="373"/> <source>Close</source> <translation>Fermer</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="487"/> + <location filename="../View/ui/MainWindow.ui" line="546"/> <source>Close current study</source> <translation>Fermer l'étude en cours</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="354"/> + <location filename="../View/ui/MainWindow.ui" line="385"/> <source>Save</source> <translation>Sauvegarder</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="357"/> + <location filename="../View/ui/MainWindow.ui" line="388"/> <source>Save study</source> <translation>Sauvegarder l'étude</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="360"/> + <location filename="../View/ui/MainWindow.ui" line="391"/> <source>Ctrl+S</source> <translation>Ctrl+S</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="369"/> + <location filename="../View/ui/MainWindow.ui" line="400"/> <source>Save as ...</source> <translation>Sauvegarder sous ...</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="372"/> + <location filename="../View/ui/MainWindow.ui" line="403"/> <source>Save study as ...</source> <translation>Sauvegarder l'étude sous ...</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="375"/> + <location filename="../View/ui/MainWindow.ui" line="406"/> <source>Ctrl+Shift+S</source> <translation>Ctrl+Shift+S</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="380"/> - <source>Pamhyr configuration</source> + <location filename="../View/ui/MainWindow.ui" line="411"/> + <source>Pamhyr2 configuration</source> <translation>Configuration de Pamhyr2</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="389"/> + <location filename="../View/ui/MainWindow.ui" line="420"/> <source>Quit</source> <translation>Quitter</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="392"/> + <location filename="../View/ui/MainWindow.ui" line="423"/> <source>Quit application</source> <translation>Quitter l'application</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="395"/> + <location filename="../View/ui/MainWindow.ui" line="426"/> <source>Ctrl+F4</source> <translation>Ctrl+F4</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="517"/> + <location filename="../View/ui/MainWindow.ui" line="576"/> <source>Edit river network</source> - <translation>Éditer le réseau</translation> + <translation>Modifier le réseau</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="405"/> + <location filename="../View/ui/MainWindow.ui" line="444"/> <source>Edit geometry</source> - <translation>Éditer la géométrie</translation> + <translation>Modifier la géométrie</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="138"/> + <location filename="../View/ui/GeometryReach.ui" line="148"/> <source>Import geometry</source> <translation>Importer une géométrie</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="146"/> + <location filename="../View/ui/GeometryReach.ui" line="160"/> <source>Export geometry</source> <translation>Exporter la géométrie</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="413"/> - <source>Numerical parameter for solvers</source> - <translation>Paramètres numeriques des solvers</translation> + <location filename="../View/ui/MainWindow.ui" line="452"/> + <source>Numerical parameters of solvers</source> + <translation>Paramètres numériques des solveurs</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="641"/> - <source>Boundary conditions and punctual contributions</source> - <translation>Condition aux limites et apports ponctuels</translation> + <location filename="../View/ui/MainWindow.ui" line="720"/> + <source>Boundary conditions and point sources</source> + <translation>Conditions aux limites et apports ponctuels</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="573"/> + <location filename="../View/ui/MainWindow.ui" line="648"/> <source>Initial conditions</source> <translation>Conditions initiales</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="431"/> + <location filename="../View/ui/MainWindow.ui" line="627"/> <source>Edit friction</source> - <translation>Éditer les frottements</translation> + <translation>Modifier les frottements</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="436"/> - <source>Edit lateral contributions</source> - <translation>Éditer les apports latéraux</translation> + <location filename="../View/ui/MainWindow.ui" line="615"/> + <source>Edit lateral sources</source> + <translation>Modifier les contributions latérales</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="502"/> + <location filename="../View/ui/MainWindow.ui" line="561"/> <source>Run solver</source> - <translation>Lancer solveur</translation> + <translation>Lancer le solveur</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="444"/> + <location filename="../View/ui/MainWindow.ui" line="503"/> <source>F5</source> <translation>F5</translation> </message> @@ -1532,167 +1672,162 @@ <translation>Ouvrir</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="452"/> + <location filename="../View/ui/MainWindow.ui" line="511"/> <source>Visualize last results</source> <translation>Visualisation des derniers résultats</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="455"/> + <location filename="../View/ui/MainWindow.ui" line="514"/> <source>Visualize the last results</source> - <translation>Visualisation des derniers resultats</translation> + <translation>Visualisation des derniers résultats</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="463"/> + <location filename="../View/ui/MainWindow.ui" line="522"/> <source>About</source> <translation>À propos</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="472"/> + <location filename="../View/ui/MainWindow.ui" line="531"/> <source>Save current study</source> <translation>Sauvegarder l'étude</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="475"/> + <location filename="../View/ui/MainWindow.ui" line="534"/> <source>Save the study (Ctrl+S)</source> <translation>Sauvegarde de l'étude (Ctrl+S)</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="490"/> + <location filename="../View/ui/MainWindow.ui" line="549"/> <source>Close the study (Ctrl+F)</source> <translation>Fermeture de l'étude (Ctrl+F)</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="493"/> + <location filename="../View/ui/MainWindow.ui" line="552"/> <source>Ctrl+F</source> <translation>Ctrl+F</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="505"/> + <location filename="../View/ui/MainWindow.ui" line="564"/> <source>Run a solver</source> <translation>Lancer un solveur</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="514"/> + <location filename="../View/ui/MainWindow.ui" line="573"/> <source>River network</source> <translation>Réseau</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="526"/> + <location filename="../View/ui/MainWindow.ui" line="585"/> <source>Geometry</source> <translation>Géometrie</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="529"/> + <location filename="../View/ui/MainWindow.ui" line="588"/> <source>Edit reach geometry</source> - <translation>Éditer la géométrie du bief actuel</translation> + <translation>Modifier la géométrie du bief actuel</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="534"/> + <location filename="../View/ui/MainWindow.ui" line="597"/> <source>Boundary conditions</source> <translation>Conditions aux limites</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="537"/> - <source>Edit boundary conditions and punctual contributions</source> - <translation>Éditer les conditions aux limites et les apports ponctuels</translation> + <location filename="../View/ui/MainWindow.ui" line="600"/> + <source>Edit boundary conditions and point sources</source> + <translation>Modifier les conditions aux limites et les apports ponctuels</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="545"/> - <source>Lateral contribution</source> + <location filename="../View/ui/MainWindow.ui" line="612"/> + <source>Lateral sources</source> <translation>Contributions latérales</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="548"/> - <source>Edit lateral contribution</source> - <translation>Éditer les contributions latérales</translation> - </message> - <message> - <location filename="../View/ui/MainWindow.ui" line="553"/> + <location filename="../View/ui/MainWindow.ui" line="624"/> <source>Friction</source> <translation>Frottements</translation> </message> <message> <location filename="../View/ui/MainWindow.ui" line="556"/> - <source>Edit friction frictions</source> - <translation>Éditer les frottements</translation> + <source>Edit friction coefficients</source> + <translation type="obsolete">Modifier les frottements</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="565"/> + <location filename="../View/ui/MainWindow.ui" line="636"/> <source>Edit study</source> - <translation>Éditer l'étude</translation> + <translation>Modifier l'étude</translation> </message> <message> <location filename="../View/ui/MainWindow.ui" line="568"/> <source>Edit the study metadata</source> - <translation type="obsolete">Éditer les informations de l'étude</translation> + <translation type="obsolete">Modifier les informations de l'étude</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="576"/> + <location filename="../View/ui/MainWindow.ui" line="651"/> <source>Define initial conditions</source> <translation>Définir les conditions initiales</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="581"/> + <location filename="../View/ui/MainWindow.ui" line="656"/> <source>Sediment layers</source> - <translation>Couche sédimentaires</translation> + <translation>Couches sédimentaires</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="584"/> + <location filename="../View/ui/MainWindow.ui" line="659"/> <source>Define sediment layers</source> <translation>Définition des couches sédimentaires</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="589"/> + <location filename="../View/ui/MainWindow.ui" line="664"/> <source>Edit reach sediment layers</source> - <translation>Éditer les couches sédimentaires</translation> + <translation>Modifier les couches sédimentaires</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="594"/> + <location filename="../View/ui/MainWindow.ui" line="669"/> <source>Mage</source> <translation>Mage</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="597"/> - <source>Open mage documentation</source> - <translation>Ouvrir la domumentation de mage</translation> + <location filename="../View/ui/MainWindow.ui" line="672"/> + <source>Open Mage documentation</source> + <translation>Ouvrir la documentation de Mage</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="602"/> + <location filename="../View/ui/MainWindow.ui" line="677"/> <source>Users (wiki)</source> <translation>Utilisateurs (wiki)</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="607"/> + <location filename="../View/ui/MainWindow.ui" line="682"/> <source>Developers (pdf)</source> - <translation>Développeur (pdf)</translation> + <translation>Développeurs (pdf)</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="612"/> + <location filename="../View/ui/MainWindow.ui" line="687"/> <source>Developers (html)</source> - <translation>Développeur (html)</translation> + <translation>Développeurs (html)</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="617"/> + <location filename="../View/ui/MainWindow.ui" line="692"/> <source>Reservoirs</source> <translation>Casiers</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="620"/> + <location filename="../View/ui/MainWindow.ui" line="695"/> <source>Edit reservoirs</source> - <translation>Éditer les casiers</translation> + <translation>Modifier les casiers</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="625"/> + <location filename="../View/ui/HydraulicStructures.ui" line="14"/> <source>Hydraulic structures</source> - <translation>Structures hydraulique</translation> + <translation>Ouvrages hydrauliques</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="628"/> + <location filename="../View/ui/MainWindow.ui" line="703"/> <source>Edit hydraulic structures</source> - <translation>Éditer les structures hydraulique</translation> + <translation>Modifier les ouvrages hydrauliques</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="636"/> + <location filename="../View/ui/MainWindow.ui" line="711"/> <source>Open results from file</source> <translation>Ouvrir des résultats depuis un fichier</translation> </message> @@ -1708,11 +1843,11 @@ </message> <message> <location filename="../View/ui/DebugRepl.ui" line="26"/> - <source>Eval</source> + <source>Evaluate</source> <translation>Évaluer</translation> </message> <message> - <location filename="../View/ui/DebugRepl.ui" line="33"/> + <location filename="../View/ui/DebugRepl.ui" line="39"/> <source>Ctrl+Return</source> <translation>Ctrl+Return</translation> </message> @@ -1724,10 +1859,10 @@ <message> <location filename="../View/ui/EditSedimentLayers.ui" line="81"/> <source>Delete selected sediment layer(s)</source> - <translation>Supprimer la(les) couche(s) sédimentaire(s) sélectionnée(s)</translation> + <translation>Supprimer les couches sédimentaires sélectionnées</translation> </message> <message> - <location filename="../View/ui/BoundaryConditions.ui" line="135"/> + <location filename="../View/ui/EditLateralContribution.ui" line="100"/> <source>Ctrl+D</source> <translation>Ctrl+D</translation> </message> @@ -1753,68 +1888,68 @@ </message> <message> <location filename="../View/ui/BoundaryConditions.ui" line="64"/> - <source>Suspenssion</source> + <source>Suspension</source> <translation>Suspension</translation> </message> <message> <location filename="../View/ui/LateralContributions.ui" line="117"/> - <source>Add a new boundary condition or lateral contribution</source> - <translation>Ajouter une condition aux limites ou un apport ponctuel</translation> + <source>Add a new boundary condition or lateral source</source> + <translation>Ajouter une condition aux limites ou un apport latéral</translation> </message> <message> - <location filename="../View/ui/BoundaryConditions.ui" line="132"/> + <location filename="../View/ui/EditLateralContribution.ui" line="97"/> <source>Delete current selected rows</source> <translation>Supprimer les lignes selectionnées</translation> </message> <message> <location filename="../View/ui/LateralContributions.ui" line="147"/> - <source>Edit boundary condition or lateral contribution</source> - <translation>Éditer une condition aux limites ou un apport ponctuel</translation> + <source>Edit boundary condition or lateral source</source> + <translation>Modifier une condition aux limites ou un apport latéral</translation> </message> <message> - <location filename="../View/ui/Stricklers.ui" line="94"/> + <location filename="../View/ui/EditLateralContribution.ui" line="109"/> <source>Sort</source> <translation>Trier</translation> </message> <message> <location filename="../View/ui/BoundaryConditions.ui" line="162"/> - <source>Sort boundary condition by name</source> + <source>Sort by names</source> <translation>Trier par nom</translation> </message> <message> <location filename="../View/ui/Stricklers.ui" line="28"/> - <source>Study stricklers</source> - <translation>Stricklers de l'étude</translation> + <source>Strickler coefficients of the study</source> + <translation>Coefficients de Strickler de l'étude</translation> </message> <message> <location filename="../View/ui/Stricklers.ui" line="38"/> - <source>Application stricklers</source> - <translation>Stricklers de l'application</translation> + <source>Strickler coefficients of the application</source> + <translation>Coefficients de Strickler de l'application</translation> </message> <message> <location filename="../View/ui/Stricklers.ui" line="73"/> - <source>Add new stricklers</source> - <translation>Ajouter un Strickler</translation> + <source>Add new Strickler coefficients</source> + <translation>Ajouter un coefficient de Strickler</translation> </message> <message> <location filename="../View/ui/Stricklers.ui" line="85"/> - <source>Delete selected stricklers</source> - <translation>Supprimer les Stricklers selectionnés</translation> + <source>Delete selected Strickler coefficients</source> + <translation>Supprimer les coefficients de Strickler selectionnés</translation> </message> <message> <location filename="../View/ui/Stricklers.ui" line="97"/> - <source>Sort stricklers</source> - <translation>Trier les Stricklers</translation> + <source>Sort Strickler coefficients</source> + <translation>Trier les coefficients de Strickler</translation> </message> <message> <location filename="../View/ui/BoundaryConditions.ui" line="117"/> - <source>Add a new boundary condition or punctual contribution</source> + <source>Add a new boundary condition or point source</source> <translation>Ajouter une condition aux limites ou un apport ponctuel</translation> </message> <message> <location filename="../View/ui/BoundaryConditions.ui" line="147"/> - <source>Edit boundary condition or punctual contribution</source> - <translation>Éditer une condition aux limites ou un apport ponctuel</translation> + <source>Edit boundary condition or point source</source> + <translation>Modifier une condition aux limites ou un apport ponctuel</translation> </message> <message> <location filename="../View/ui/Results.ui" line="152"/> @@ -1828,38 +1963,38 @@ </message> <message> <location filename="../View/ui/Results.ui" line="201"/> - <source>Hydrograph</source> + <source>Discharge time series</source> <translation>Hydrogramme</translation> </message> <message> <location filename="../View/ui/Results.ui" line="254"/> - <source>Add custom visualization</source> + <source>Add customized visualization</source> <translation>Ajouter une visualisation personnalisée</translation> </message> <message> - <location filename="../View/ui/Results.ui" line="259"/> + <location filename="../View/ui/Results.ui" line="263"/> <source>Reload</source> <translation>Recharger</translation> </message> <message> - <location filename="../View/ui/Results.ui" line="264"/> + <location filename="../View/ui/GeometryReach.ui" line="157"/> <source>Export</source> <translation>Exporter</translation> </message> <message> - <location filename="../View/ui/Results.ui" line="267"/> + <location filename="../View/ui/Results.ui" line="275"/> <source>Export raw data</source> <translation>Exporter les données brutes</translation> </message> <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="82"/> + <location filename="../View/ui/GeometryReach.ui" line="181"/> <source>delete</source> <translation>supprimer</translation> </message> <message> - <location filename="../View/ui/Frictions.ui" line="101"/> - <source>Edit stricklers</source> - <translation>Éditer des stricklers</translation> + <location filename="../View/ui/Frictions.ui" line="104"/> + <source>Edit Strickler coefficients</source> + <translation>Modifier les coefficients de Strickler</translation> </message> <message> <location filename="../View/ui/WebView.ui" line="56"/> @@ -1899,32 +2034,32 @@ <message> <location filename="../View/ui/BasicHydraulicStructures.ui" line="14"/> <source>Basic hydraulic structures</source> - <translation>Ouvrages hydrauliques élémentaire</translation> + <translation>Ouvrages hydrauliques élémentaires</translation> </message> <message> <location filename="../View/ui/BasicHydraulicStructures.ui" line="49"/> <source>Enable / Disable basic hydraulic structure</source> - <translation>Activer/Déactiver l'ouvrage hydraulique élémentaire</translation> + <translation>Activer/Désactiver l'ouvrage hydraulique élémentaire</translation> </message> <message> - <location filename="../View/ui/Reservoir.ui" line="78"/> + <location filename="../View/ui/EditBoundaryConditions.ui" line="89"/> <source>Add a new point</source> - <translation>Add a new point</translation> + <translation>Ajouter un nouveau point</translation> </message> <message> <location filename="../View/ui/Reservoir.ui" line="90"/> <source>Delete points</source> - <translation>Supprimer points</translation> + <translation>Supprimer les points</translation> </message> <message> - <location filename="../View/ui/BasicHydraulicStructures.ui" line="136"/> + <location filename="../View/ui/HydraulicStructures.ui" line="133"/> <source>Edit selected hydraulic structure</source> - <translation>Éditer l'ouvrage hydraulique sélectionné</translation> + <translation>Modifier l'ouvrage hydraulique sélectionné</translation> </message> <message> <location filename="../View/ui/SolverLog.ui" line="78"/> <source>Stop</source> - <translation>Stoper</translation> + <translation>Arrêter</translation> </message> <message> <location filename="../View/ui/SolverLog.ui" line="87"/> @@ -1944,45 +2079,40 @@ <message> <location filename="../View/ui/SolverLog.ui" line="110"/> <source>results</source> - <translation>resultats</translation> - </message> - <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="70"/> - <source>add</source> - <translation>Ajouter</translation> + <translation>résultats</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="73"/> - <source>Add a point on cross-section</source> + <source>Add a point to cross-section</source> <translation>Ajouter un point à la section en travers</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="85"/> <source>Delete selected point(s)</source> - <translation>Supprimer le(s) point(s) sélectionné(s)</translation> + <translation>Supprimer les points sélectionnés</translation> </message> <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="94"/> - <source>up</source> + <location filename="../View/ui/GeometryReach.ui" line="229"/> + <source>Up</source> <translation>Monter</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="97"/> <source>Move up selected point(s)</source> - <translation>Déplacer le point sélectionné vers le haut</translation> + <translation>Déplacer les points sélectionnés vers le haut</translation> </message> <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="106"/> - <source>down</source> + <location filename="../View/ui/GeometryReach.ui" line="241"/> + <source>Down</source> <translation>Descendre</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="109"/> - <source>Mode down selected point(s)</source> - <translation>Déplacer le point sélectionné vers le bas</translation> + <source>Move down selected point(s)</source> + <translation>Déplacer les points sélectionnés vers le bas</translation> </message> <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="118"/> + <location filename="../View/ui/GeometryReach.ui" line="205"/> <source>sort_asc</source> <translation>sort_asc</translation> </message> @@ -1992,109 +2122,104 @@ <translation>Trier les points par leurs plus proches voisins</translation> </message> <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="130"/> + <location filename="../View/ui/GeometryReach.ui" line="217"/> <source>sort_des</source> <translation>sort_des</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="133"/> <source>Sort reversed points by nearest neighbor</source> - <translation>Trie inverser les points par leurs plus proche voisins</translation> + <translation>Trier/inverser les points par leurs plus proches voisins</translation> </message> <message> <location filename="../View/ui/HydraulicStructures.ui" line="49"/> <source>Enable / Disable hydraulic structure</source> - <translation>Activer/Déactiver l'ouvrage hydraulique</translation> + <translation>Activer/Désactiver l'ouvrage hydraulique</translation> </message> <message> <location filename="../View/ui/Reservoir.ui" line="102"/> - <source>Edit elevation/surface law</source> - <translation>Éditer loi élévation/surface</translation> + <source>Edit stage-area relation</source> + <translation>Modifier loi cote/surface</translation> </message> <message> <location filename="../View/ui/Reservoir.ui" line="114"/> - <source>Sort points by elevation</source> - <translation>Trier les points par leur cote</translation> + <source>Sort points by elevations</source> + <translation>Trier les points par cote</translation> </message> <message> - <location filename="../View/ui/ReachSedimentLayers.ui" line="31"/> + <location filename="../View/ui/ProfileSedimentLayers.ui" line="75"/> <source>Edit sediment layers list</source> - <translation>Éditer la liste des couches sédimentaires</translation> + <translation>Modifier la liste des couches sédimentaires</translation> </message> <message> <location filename="../View/ui/ReachSedimentLayers.ui" line="38"/> - <source>Apply sediment layers on all reach</source> + <source>Apply sediment layers to all reaches</source> <translation>Appliquer une liste de couches sédimentaires</translation> </message> <message> <location filename="../View/ui/ReachSedimentLayers.ui" line="80"/> <source>Edit profile</source> - <translation>Éditer le profil</translation> + <translation>Modifier le profil</translation> </message> <message> <location filename="../View/ui/ReachSedimentLayers.ui" line="83"/> - <source>Edit profile sediment layer</source> - <translation>Éditer les couches sédimentaires du profil</translation> + <source>Edit sediment layers of the profile</source> + <translation>Modifier les couches sédimentaires du profil</translation> </message> <message> <location filename="../View/ui/InitialConditions.ui" line="30"/> - <source>Generate from height</source> - <translation>Générer pour une hauteur donnée</translation> + <source>Generate height</source> + <translation>Générer une hauteur</translation> </message> <message> <location filename="../View/ui/InitialConditions.ui" line="37"/> - <source>Generate from discharge</source> - <translation>Générer pour un débit donnée</translation> + <source>Generate discharge</source> + <translation>Générer un débit</translation> </message> <message> - <location filename="../View/ui/InitialConditions.ui" line="97"/> + <location filename="../View/ui/InitialConditions.ui" line="98"/> <source>Add new initial condition</source> <translation>Ajouter une nouvelle condition initiale</translation> </message> <message> - <location filename="../View/ui/InitialConditions.ui" line="109"/> + <location filename="../View/ui/InitialConditions.ui" line="110"/> <source>Delete inital condition</source> <translation>Supprimer une condition initiale</translation> </message> <message> - <location filename="../View/ui/InitialConditions.ui" line="118"/> + <location filename="../View/ui/InitialConditions.ui" line="119"/> <source>sort</source> <translation>sort</translation> </message> <message> - <location filename="../View/ui/InitialConditions.ui" line="121"/> - <source>Sort inital condition</source> + <location filename="../View/ui/InitialConditions.ui" line="122"/> + <source>Sort inital conditions</source> <translation>Trier les conditions initiales</translation> </message> <message> - <location filename="../View/ui/EditBoundaryConditions.ui" line="89"/> - <source>Add a new point in boundary condition or punctual contribution</source> - <translation>Ajouter un nouveau point</translation> + <location filename="../View/ui/BoundaryConditions.ui" line="117"/> + <source>Add a new point in boundary condition or point source</source> + <translation type="obsolete">Ajouter un nouveau point</translation> </message> <message> <location filename="../View/ui/EditBoundaryConditions.ui" line="119"/> <source>Sort boundary condition points</source> - <translation>Trier les points des conditions aux limites</translation> + <translation type="obsolete">Trier les points des conditions aux limites</translation> </message> <message> <location filename="../View/ui/EditLateralContribution.ui" line="82"/> - <source>Add a new point in boundary condition or lateral contribution</source> + <source>Add a new point in boundary condition or lateral source</source> <translation>Ajouter un nouveau point</translation> </message> - <message> - <location filename="../View/ui/EditLateralContribution.ui" line="112"/> - <source>Sort boundary condition point</source> - <translation>Trier les points des conditions aux limites</translation> - </message> <message> <location filename="../View/ui/ProfileSedimentLayers.ui" line="48"/> <source>Add sediment layers</source> - <translation>Ajouter une couche sedimentaire</translation> + <translation>Ajouter une couche sédimentaire</translation> </message> <message> <location filename="../View/ui/ProfileSedimentLayers.ui" line="51"/> <source>Add specific sediment layers on selected point(s)</source> - <translation>Ajouter des couches spécifiques au(x) point(s) sélectionné(s)</translation> + <translation>Ajouter des couches spécifiques aux points sélectionnés</translation> </message> <message> <location filename="../View/ui/ProfileSedimentLayers.ui" line="60"/> @@ -2104,97 +2229,92 @@ <message> <location filename="../View/ui/ProfileSedimentLayers.ui" line="63"/> <source>Delete specific sediment layers of selected point(s)</source> - <translation>Supprimer les couches sédimentaires au(x) point(s) sélectionné(s)</translation> + <translation>Supprimer les couches sédimentaires aux points sélectionnés</translation> </message> <message> <location filename="../View/ui/ProfileSedimentLayers.ui" line="72"/> <source>Edit sediment layers</source> - <translation>Éditer les couches sédimentaires</translation> + <translation>Modifier les couches sédimentaires</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="135"/> + <location filename="../View/ui/GeometryReach.ui" line="145"/> <source>Import</source> <translation>Importer</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="158"/> + <location filename="../View/ui/GeometryReach.ui" line="172"/> <source>Add a cross-section</source> <translation>Ajouter une section en travers</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="170"/> + <location filename="../View/ui/GeometryReach.ui" line="184"/> <source>Delete selected cross-section(s)</source> - <translation>Supprimer la(es) section(s) en travers sélectionnée(s)</translation> - </message> - <message> - <location filename="../View/ui/GeometryReach.ui" line="179"/> - <source>edit</source> - <translation>éditer</translation> + <translation>Supprimer les sections en travers sélectionnées</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="182"/> + <location filename="../View/ui/GeometryReach.ui" line="196"/> <source>Edit selected cross section(s)</source> - <translation>Éditer la(es) section(s) en travers sélectionnée(s)</translation> + <translation>Modifier les sections en travers sélectionnées</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="194"/> - <source>Sort cross-sections by ascending KP</source> + <location filename="../View/ui/GeometryReach.ui" line="208"/> + <source>Sort cross-sections by ascending position</source> <translation>Trier les sections en travers par PK croissant</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="206"/> - <source>Sort cross-sections by descending KP</source> + <location filename="../View/ui/GeometryReach.ui" line="220"/> + <source>Sort cross-sections by descending position</source> <translation>Trier les sections en travers par PK décroissant</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="218"/> + <location filename="../View/ui/GeometryReach.ui" line="232"/> <source>Move up selected cross-section(s)</source> - <translation>Déplacer la(s) section(s) en travers vers le haut</translation> + <translation>Déplacer les sections en travers vers le haut</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="230"/> + <location filename="../View/ui/GeometryReach.ui" line="244"/> <source>Move down selected cross-section(s)</source> - <translation>Déplacer la(es) section(s) en travers vers le bas</translation> + <translation>Déplacer les sections en travers vers le bas</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="235"/> + <location filename="../View/ui/GeometryReach.ui" line="253"/> <source>Meshing</source> <translation>Maillage</translation> </message> <message> - <location filename="../View/Translate.py" line="78"/> + <location filename="../View/Translate.py" line="79"/> <source>Summary</source> - <translation>Résumer</translation> + <translation>Résumé</translation> </message> <message> - <location filename="../View/Translate.py" line="81"/> + <location filename="../View/Translate.py" line="82"/> <source>Checks</source> <translation>Vérifications</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="200"/> - <source>&Advansed</source> + <location filename="../View/ui/MainWindow.ui" line="202"/> + <source>&Advanced</source> <translation>&Avancé</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="646"/> - <source>&Additional file</source> - <translation>Fichiers &supplémentaire</translation> + <location filename="../View/ui/MainWindow.ui" line="725"/> + <source>&Additional files</source> + <translation>Fichiers &supplémentaires</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="651"/> + <location filename="../View/ui/MainWindow.ui" line="730"/> <source>REP additional lines</source> - <translation>Lines REP supplémentaire</translation> + <translation>Lignes REP supplémentaires</translation> </message> <message> <location filename="../View/ui/REPLineList.ui" line="46"/> - <source>Add new additional line</source> + <source>Add new line</source> <translation>Ajouter une nouvelle ligne</translation> </message> <message> <location filename="../View/ui/REPLineList.ui" line="61"/> <source>Delete additional line(s)</source> - <translation>Supprimer la(es) ligne(s) supplementaire</translation> + <translation>Supprimer les lignes supplementaires</translation> </message> <message> <location filename="../View/ui/REPLineList.ui" line="64"/> @@ -2204,7 +2324,7 @@ <message> <location filename="../View/ui/REPLineList.ui" line="76"/> <source>Edit selected line(s)</source> - <translation>Éditer la(es) ligne(s) selectionnée(s)</translation> + <translation>Modifier les lignes selectionnées</translation> </message> <message> <location filename="../View/ui/AdditionalFile.ui" line="46"/> @@ -2214,7 +2334,7 @@ <message> <location filename="../View/ui/AdditionalFile.ui" line="55"/> <source>File text</source> - <translation>Text du fichiers</translation> + <translation>Texte du fichier</translation> </message> <message> <location filename="../View/ui/AdditionalFile.ui" line="67"/> @@ -2229,7 +2349,7 @@ <message> <location filename="../View/ui/AdditionalFile.ui" line="83"/> <source>The relative file path on executable directory</source> - <translation>Le chemin relatif du fichier dans le répertoire d'execution</translation> + <translation>Le chemin relatif du fichier dans le répertoire d'exécution</translation> </message> <message> <location filename="../View/ui/AdditionalFile.ui" line="93"/> @@ -2249,84 +2369,84 @@ <message> <location filename="../View/ui/AdditionalFileList.ui" line="58"/> <source>Delete selected file(s)</source> - <translation>Supprimer le(s) fichier(s) selectionné(s)</translation> + <translation>Supprimer les fichiers sélectionnés</translation> </message> <message> <location filename="../View/ui/AdditionalFileList.ui" line="70"/> <source>Edit file</source> - <translation>Éditer fichier</translation> + <translation>Modifier fichier</translation> </message> <message> - <location filename="../View/ui/MainWindow.ui" line="568"/> + <location filename="../View/ui/MainWindow.ui" line="639"/> <source>Edit the study information</source> - <translation>Éditer les information de l'étude</translation> + <translation>Modifier les informations de l'étude</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="240"/> - <source>Update KP</source> + <location filename="../View/ui/GeometryReach.ui" line="112"/> + <source>toolBar</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="243"/> - <source>Recompute KP</source> + <location filename="../View/ui/MainWindow.ui" line="303"/> + <source>toolBar_2</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../View/ui/GeometryCrossSection.ui" line="138"/> - <source>Purge</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/MainWindow.ui" line="482"/> + <source>Edit frictions</source> + <translation>Éditer les frotements</translation> </message> <message> - <location filename="../View/ui/GeometryReach.ui" line="251"/> - <source>Purge cross-sections to keep a given number of points</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/GeometryReach.ui" line="266"/> + <source>Purge</source> + <translation>Purger</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="141"/> <source>Purge the cross-section to keep a given number of points</source> - <translation type="unfinished"></translation> + <translation>Purger les profiles pour garder qu'un nombre donné de points</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="146"/> <source>Reverse</source> - <translation type="unfinished"></translation> + <translation>Retourner</translation> </message> <message> <location filename="../View/ui/GeometryCrossSection.ui" line="149"/> <source>Reverse the points order</source> - <translation type="unfinished"></translation> + <translation>Retourner l'ordre des points</translation> </message> -</context> -<context> - <name>MainWindowProfile</name> <message> - <location filename="../View/Geometry/Profile/Table.py" line="87"/> - <source>La cote du fond</source> - <comment>Z minimale</comment> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="134"/> + <source>Import from file</source> + <translation>Importer depuis un fichier</translation> </message> <message> - <location filename="../View/Geometry/Profile/Table.py" line="91"/> - <source>La cote maximale</source> - <comment>Z maximale</comment> - <translation type="unfinished"></translation> + <location filename="../View/ui/GeometryReach.ui" line="258"/> + <source>Update RK</source> + <translation>Mise à jour des PK</translation> </message> <message> - <location filename="../View/Geometry/Profile/Table.py" line="109"/> - <source>Rive gauche</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/GeometryReach.ui" line="261"/> + <source>Recompute RK</source> + <translation>Recalcule des PK</translation> </message> <message> - <location filename="../View/Geometry/Profile/Table.py" line="113"/> - <source>Rive droite</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/GeometryReach.ui" line="269"/> + <source>Purge cross-sections to keep a given number of points</source> + <translation>Purger les profiles pour garder un nombre fixer de points</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="112"/> + <source>Sort points</source> + <translation>Trier les points</translation> </message> </context> <context> <name>MainWindow_reach</name> <message> <location filename="../View/Results/PlotSedReach.py" line="233"/> - <source>Kp (m)</source> + <source>Position (m)</source> <translation>Pk (m)</translation> </message> <message> @@ -2340,7 +2460,7 @@ <translation>X (m)</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="213"/> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="225"/> <source>Select destination file</source> <translation>Selectionner fichier de destination</translation> </message> @@ -2365,7 +2485,7 @@ <message> <location filename="../View/Network/translate.py" line="36"/> <source>Edit node reservoir</source> - <translation>Éditer le casier du nÅ“ud</translation> + <translation>Modifier le casier du nÅ“ud</translation> </message> <message> <location filename="../View/Network/translate.py" line="39"/> @@ -2380,17 +2500,17 @@ <message> <location filename="../View/Network/translate.py" line="47"/> <source>Enable the reach</source> - <translation>Activer l'arrête</translation> + <translation>Activer l'arête</translation> </message> <message> <location filename="../View/Network/translate.py" line="48"/> <source>Disable the reach</source> - <translation>Déactiver l'arrête</translation> + <translation>Désactiver l'arête</translation> </message> <message> <location filename="../View/Network/translate.py" line="50"/> <source>Reverse the reach orientation</source> - <translation>Inverser l'orientation de l'arrête</translation> + <translation>Inverser l'orientation de l'arête</translation> </message> <message> <location filename="../View/Network/translate.py" line="61"/> @@ -2426,7 +2546,7 @@ <message> <location filename="../View/REPLines/Translate.py" line="33"/> <source>Edit Mage REP lines</source> - <translation>Éditer le lignes REP Mage</translation> + <translation>Modifier les lignes REP Mage</translation> </message> </context> <context> @@ -2444,7 +2564,7 @@ <message> <location filename="../View/Reservoir/Edit/Translate.py" line="32"/> <source>Edit Reservoir</source> - <translation>Éditer le casier</translation> + <translation>Modifier le casier</translation> </message> </context> <context> @@ -2545,7 +2665,7 @@ <message> <location filename="../View/SedimentLayers/Edit/translate.py" line="30"/> <source>Edit Sediment Layers</source> - <translation>Éditer les couches sédimentaires</translation> + <translation>Modifier les couches sédimentaires</translation> </message> <message> <location filename="../View/SedimentLayers/Edit/translate.py" line="40"/> @@ -2564,8 +2684,8 @@ </message> <message> <location filename="../View/SedimentLayers/Reach/Profile/Window.py" line="92"/> - <source>(no name - @kp)</source> - <translation>(sans nom - @kp)</translation> + <source>(no name - @rk)</source> + <translation>(sans nom - @rk)</translation> </message> <message> <location filename="../View/SedimentLayers/Reach/Profile/translate.py" line="40"/> @@ -2600,12 +2720,12 @@ <message> <location filename="../View/SedimentLayers/Reach/translate.py" line="33"/> <source>Apply sediment layers to reach</source> - <translation>Appliquer des couches sédimentaire au bief</translation> + <translation>Appliquer des couches sédimentaires au bief</translation> </message> <message> <location filename="../View/SedimentLayers/translate.py" line="30"/> <source>Sediment Layers List</source> - <translation>Liste des ensembles de couches sedimentaires</translation> + <translation>Liste des ensembles de couches sédimentaires</translation> </message> </context> <context> @@ -2623,7 +2743,7 @@ <message> <location filename="../View/RunSolver/Log/Window.py" line="58"/> <source>Solver logs</source> - <translation>Selection des logs</translation> + <translation>Sélection des logs</translation> </message> </context> <context> @@ -2691,7 +2811,7 @@ <message> <location filename="../View/SolverParameters/translate.py" line="77"/> <source>Implicitation parameter</source> - <translation>Parametre d'implicitation</translation> + <translation>Paramètre d'implicitation</translation> </message> <message> <location filename="../View/SolverParameters/translate.py" line="80"/> @@ -2730,7 +2850,7 @@ </message> <message> <location filename="../View/SolverParameters/translate.py" line="101"/> - <source>Maximun number of iterations (< 100)</source> + <source>Maximum number of iterations (< 100)</source> <translation>Nombre maximum d'itérations (< 100)</translation> </message> <message> @@ -2755,12 +2875,12 @@ </message> <message> <location filename="../View/SolverParameters/translate.py" line="116"/> - <source>Number of iteration at maximum precision</source> + <source>Number of iterations at maximum precision</source> <translation>Nombre d'itérations à la précision maximum</translation> </message> <message> <location filename="../View/SolverParameters/translate.py" line="119"/> - <source>Number of iteration before switch</source> + <source>Number of iterations before switch</source> <translation>Nombre d'itérations avant changement</translation> </message> <message> @@ -2791,7 +2911,7 @@ <message> <location filename="../View/SolverParameters/translate.py" line="137"/> <source>Use Mage internal initialization (Y/N)</source> - <translation>Utiliser l'initialisation interne de mage (O/N)</translation> + <translation>Utiliser l'initialisation interne de Mage (O/N)</translation> </message> <message> <location filename="../View/SolverParameters/translate.py" line="141"/> @@ -2830,8 +2950,8 @@ </message> <message> <location filename="../View/SolverParameters/translate.py" line="155"/> - <source>Critic shields</source> - <translation>Shields critique</translation> + <source>Critical Shields number</source> + <translation>Nombre de Shields critique</translation> </message> <message> <location filename="../View/SolverParameters/translate.py" line="157"/> @@ -2856,10 +2976,15 @@ </context> <context> <name>Stricklers</name> + <message> + <location filename="../View/Stricklers/translate.py" line="30"/> + <source>Strickler coefficients</source> + <translation>Coefficient de Strickler</translation> + </message> <message> <location filename="../View/Stricklers/translate.py" line="30"/> <source>Stricklers</source> - <translation>Stricklers</translation> + <translation type="obsolete">Stricklers</translation> </message> </context> <context> @@ -2867,7 +2992,7 @@ <message> <location filename="../View/Study/Window.py" line="51"/> <source>Edit study</source> - <translation>Éditer l'étude</translation> + <translation>Modifier l'étude</translation> </message> <message> <location filename="../View/Study/Window.py" line="56"/> @@ -2878,43 +3003,43 @@ <context> <name>Toolbar</name> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="240"/> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="252"/> <source>Default view</source> <translation>Vue par défaut</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="241"/> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="253"/> <source>Back to previous view</source> <translation>Retour à la vue précédente</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="243"/> - <source>Return to next view</source> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="255"/> + <source>Back to next view</source> <translation>Retour à la vue suivante</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="245"/> - <source>Axes panoramic</source> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="257"/> + <source>Panoramic axes</source> <translation>Axes panoramiques</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="247"/> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="259"/> <source>Zoom</source> <translation>Zoom</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="250"/> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="262"/> <source>Isometric view (Shift+W)</source> <translation>Vue isométrique (Shift+W)</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="252"/> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="264"/> <source>Auto scale view (Shift+X)</source> <translation>Vue automatique (Shift+X)</translation> </message> <message> - <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="248"/> - <source>Save the figure</source> + <location filename="../View/Tools/Plot/PamhyrToolbar.py" line="260"/> + <source>Save figure</source> <translation>Sauvegarder la figure</translation> </message> </context> @@ -2922,7 +3047,7 @@ <name>Unit</name> <message> <location filename="../View/Translate.py" line="55"/> - <source>KP (m)</source> + <source>Position (m)</source> <translation>PK (m)</translation> </message> <message> @@ -2932,7 +3057,7 @@ </message> <message> <location filename="../View/Translate.py" line="57"/> - <source>Height (m)</source> + <source>Depth (m)</source> <translation>Hauteur (m)</translation> </message> <message> @@ -2956,27 +3081,27 @@ <translation>Cote de l'eau (m)</translation> </message> <message> - <location filename="../View/Translate.py" line="65"/> + <location filename="../View/Translate.py" line="66"/> <source>Area (hectare)</source> <translation>Aire (hectare)</translation> </message> <message> - <location filename="../View/Translate.py" line="67"/> + <location filename="../View/Translate.py" line="68"/> <source>Time (sec)</source> - <translation>Temps (sec)</translation> + <translation>Temps (s)</translation> </message> <message> - <location filename="../View/Translate.py" line="68"/> + <location filename="../View/Translate.py" line="69"/> <source>Time (JJJ:HH:MM:SS)</source> <translation>Temps (JJJ:HH:MM:SS)</translation> </message> <message> - <location filename="../View/Translate.py" line="70"/> + <location filename="../View/Translate.py" line="71"/> <source>Date (sec)</source> - <translation>Date (sec)</translation> + <translation>Date (s)</translation> </message> <message> - <location filename="../View/Translate.py" line="71"/> + <location filename="../View/Translate.py" line="72"/> <source>Date (ISO format)</source> <translation>Date (format ISO)</translation> </message> @@ -2985,8 +3110,13 @@ <source>Discharge (m³/s)</source> <translation type="obsolete">Débit (m³/s)</translation> </message> - <message encoding="UTF-8"> + <message> <location filename="../View/Translate.py" line="64"/> + <source>Speed (m/s)</source> + <translation>Vitesse (m/s)</translation> + </message> + <message encoding="UTF-8"> + <location filename="../View/Translate.py" line="65"/> <source>Discharge (m³/s)</source> <translation type="unfinished"></translation> </message> diff --git a/src/tools.py b/src/tools.py index 3965707712fd17f4d5e2e5c06dc81358c9dab2ae..12308a7b23afc1c27f0c4c48f7a9a21f84a8558b 100644 --- a/src/tools.py +++ b/src/tools.py @@ -237,7 +237,7 @@ def timestamp(dt: datetime): def date_iso_to_timestamp(date: str): if type(date) is str: - return datetime.fromisoformat(date).timestamp() + return timestamp(datetime.fromisoformat(date)) else: return datetime.isoformat(date) diff --git a/tools/license.el b/tools/license.el index 1d08c959f6cdd909006995e26f92155a4d045ee7..442c0630e4eae7593c565ec32c1a97bf332b2b6c 100644 --- a/tools/license.el +++ b/tools/license.el @@ -110,41 +110,51 @@ (mapcar (lambda (file) (concat root "/" file)) files-without-copyright)))) -(defvar pamhyr-mail-template "Bonjour, +(defvar pamhyr-mail-template "Dear users, -La version @version de Pamhyr2 est disponible. +✨ The version @version of Pamhyr2 is available. <NEWS> ----Change-logs-------------------@version--- +---📄--Change-logs-------------@version--- @description ------------------------------------------ ----Liens-utiles--------------------------- - Télécharger cette version : +<FUTURE WORK> + +---💡--Useful-links----------------------- + Download this version : https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/releases/@version - La documentation (en anglais) : + The documentation : https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home - Rapporter un problème : + Repport an issue : https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/issues - ou directement par mail à : + or by email at : <pierre-antoine.rouby@inrae.fr> ------------------------------------------ -<FUTURE_WORK> +---âš ï¸--Warning--------------------------- + + For Windows users: Some antivirus programs may detect Pamhyr2 + as a virus 🦠, but this is a false positive. The problem is + known and comes from the executable generated by PyInstaller. + We don't yet have a solution to this problem. If this is your + case, you'll need to add an exception to your antivirus + software if you want to use Pamhyr2. Alternatively, you can use + WSL (Windows Subsystem for Linux) and use the Linux version on + Windows. + + Antivirus analysis : <TOTAL VIRUS REPORT> ----/!\--Attention------------------------- - Pour les utilisateurs Windows : Certains antivirus peuvent détecter Pamhyr2 comme un virus, c'est un faux positif, le problème est connu et vient de l'exécutable généré par PyInstaller. - Nous n'avons pas encore de solution pour régler ce problème. - Si c'est votre cas, il faudra ajouter une exception dans votre antivirus si vous voulez utiliser Pamhyr2. - Sinon, il est aussi possible de passer par WSL et utiliser la version Linux sous Windows. + For Linux 🧠users: Some systems are not compatible with the + compiled version, in which case you'll have to go straight to + the source code (see the documentation for more details). - Rapport d'antivirus : <LINK_VIRUSTOTAL> ------------------------------------------ -Bon weekend, +Br, ") (require 'web)