Compare commits

..
Author SHA1 Message Date
Theophile Terraz 22771c7f3a debug + pep8 2025-11-06 16:40:12 +01:00
Theophile Terraz 6d60a2017c add sound option in results for Q 2025-11-06 16:28:59 +01:00
54 changed files with 530 additions and 2749 deletions

No files matched your search

-38
View File
@@ -38,7 +38,6 @@ variables:
variables: variables:
MAGE_8_VERSION: "permalink/latest" MAGE_8_VERSION: "permalink/latest"
ADISTS_VERSION: "permalink/latest" ADISTS_VERSION: "permalink/latest"
RUBAR_VERSION: "permalink/latest"
dl-mage8-doc: dl-mage8-doc:
stage: downloads stage: downloads
@@ -112,36 +111,6 @@ dl-adists-windows:
paths: paths:
- adists-windows/adists.exe - adists-windows/adists.exe
dl-rubar-linux:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
script:
- mkdir -p rubar-linux
- cd rubar-linux
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_linux.tgz
- tar xf rubar.tgz
artifacts:
paths:
- rubar-linux/rubarbe
dl-rubar-windows:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
script:
- mkdir -p rubar-windows
- cd rubar-windows
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_windows.tgz
- tar xf rubar.tgz
artifacts:
paths:
- rubar-windows/rubarbe.exe
############# #############
# CONFIGURE # # CONFIGURE #
############# #############
@@ -304,8 +273,6 @@ build-linux:
needs: needs:
- job: dl-adists-linux - job: dl-adists-linux
artifacts: true artifacts: true
- job: dl-rubar-linux
artifacts: true
- job: dl-mage8-linux - job: dl-mage8-linux
artifacts: true artifacts: true
- job: dl-mage8-doc - job: dl-mage8-doc
@@ -347,9 +314,6 @@ build-linux:
# Copy adists # Copy adists
- mkdir -p pamhyr/adists - mkdir -p pamhyr/adists
- cp -v ../adists-linux/* pamhyr/adists/ - cp -v ../adists-linux/* pamhyr/adists/
# Copy rubar
- mkdir -p pamhyr/rubar
- cp -v ../rubar-linux/* pamhyr/rubar/
# Copy Pamhyr # Copy Pamhyr
- cp -r dist/pamhyr/* pamhyr/ - cp -r dist/pamhyr/* pamhyr/
# Pamhyr script to force x11 # Pamhyr script to force x11
@@ -415,8 +379,6 @@ build-windows:
artifacts: true artifacts: true
- job: dl-adists-windows - job: dl-adists-windows
artifacts: true artifacts: true
- job: dl-rubar-windows
artifacts: true
- job: dl-mage8-doc - job: dl-mage8-doc
artifacts: true artifacts: true
- job: set-version - job: set-version
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

-4
View File
@@ -46,10 +46,6 @@ copy /y ..\mage8-windows\mage_extraire.exe pamhyr\mage8\
copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\ copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\
copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\ copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\
rem rubar
mkdir pamhyr\rubar
copy /y ..\adists-windows\rubarbe.exe pamhyr\rubar\
rem adists rem adists
mkdir pamhyr\adists mkdir pamhyr\adists
copy /y ..\adists-windows\adists.exe pamhyr\adists\ copy /y ..\adists-windows\adists.exe pamhyr\adists\
+62 -77
View File
@@ -40,24 +40,20 @@ class InternalMeshing(AMeshingTool):
step: float = 50, step: float = 50,
limites=[-1, -1], limites=[-1, -1],
linear: bool = True): linear: bool = True):
if reach is None or len(reach.profiles) == 0:
return reach
# pre process if limites[0] > limites[1]:
lim = limites[1]
limites[1] = limites[0]
limites[0] = lim
elif limites[0] == limites[1]:
return reach
profiles = self.get_profiles(reach, limites) # we never modify original profiles, we work on copies
if profiles is None: m_profiles = self.st_to_m(reach, limites)
return [] new_profiles = self.interpolate_transversal_step(m_profiles,
step)
for p in profiles:
if len(p) < 1:
return []
guide_list = self.get_guide_list(reach, profiles)
if guide_list is None:
return []
# we never modify original profiles, we work on the copies
m_profiles = self.st_to_m(profiles, guide_list)
new_profiles = self.interpolate_transversal_step(m_profiles, step)
for new_profiles2 in new_profiles: for new_profiles2 in new_profiles:
for p in new_profiles2: for p in new_profiles2:
@@ -65,31 +61,52 @@ class InternalMeshing(AMeshingTool):
return new_profiles return new_profiles
def st_to_m(self, profiles, guide_list): def st_to_m(self, reach, limites):
guide_list = ["un"] + guide_list + ["np"] gl = reach.compute_guidelines()
max_values = [0] * (len(guide_list) - 1) profiles = [reach.profile(i).copy()
max_values_index = [0] * (len(guide_list) - 1) for i in range(limites[0], limites[1]+1)
]
# we make sure that the lines are in the left-to-right order
guide_list = [
x.name
for x in profiles[0].named_points()
if x.name in gl[0]
]
gl1 = [""] + guide_list
gl2 = guide_list + [""]
max_values = [0] * (len(guide_list) + 1)
max_values_index = [0] * (len(guide_list) + 1)
# we work between each couple of guidelines # we work between each couple of guidelines
for j, p in enumerate(profiles): for j, p in enumerate(profiles):
index1 = 0
index2 = p.named_point_index(guide_list[0])
if index2 - index1 > max_values[0]:
max_values[0] = index2 - index1
max_values_index[0] = j
for i in range(len(guide_list) - 1): for i in range(len(guide_list) - 1):
index1 = p.named_point_index(guide_list[i]) index1 = p.named_point_index(guide_list[i])
index2 = p.named_point_index(guide_list[i+1]) index2 = p.named_point_index(guide_list[i+1])
if index2 - index1 > max_values[i]: if index2 - index1 > max_values[i+1]:
max_values[i] = index2 - index1 max_values[i+1] = index2 - index1
max_values_index[i] = j max_values_index[i+1] = j
index1 = p.named_point_index(guide_list[-1])
index2 = len(p) - 1
if index2 - index1 > max_values[-1]:
max_values[-1] = index2 - index1
max_values_index[-1] = j
for i in range(len(max_values)): for i in range(len(max_values)):
for isect in range(max_values_index[i], len(profiles)-1): for isect in range(max_values_index[i], len(profiles)-1):
self.compl_sect(profiles[isect], self.compl_sect(profiles[isect],
profiles[isect+1], profiles[isect+1],
guide_list[i], gl1[i], gl2[i])
guide_list[i+1])
for isect in reversed(range(0, max_values_index[i])): for isect in reversed(range(0, max_values_index[i])):
self.compl_sect(profiles[isect+1], self.compl_sect(profiles[isect+1],
profiles[isect], profiles[isect],
guide_list[i], gl1[i], gl2[i])
guide_list[i+1])
return profiles return profiles
def interpolate_transversal_step(self, def interpolate_transversal_step(self,
@@ -139,9 +156,17 @@ class InternalMeshing(AMeshingTool):
# len1 and 2: number of intervals # len1 and 2: number of intervals
# len1: target len # len1: target len
if tag1 == '': # left end point
start1 = 0
start2 = 0
else:
start1 = sect1.named_point_index(tag1) start1 = sect1.named_point_index(tag1)
start2 = sect2.named_point_index(tag1) start2 = sect2.named_point_index(tag1)
if tag2 == '': # right end point
end1 = sect1.nb_points-1
end2 = sect2.nb_points-1
else:
end1 = sect1.named_point_index(tag2) end1 = sect1.named_point_index(tag2)
end2 = sect2.named_point_index(tag2) end2 = sect2.named_point_index(tag2)
@@ -149,7 +174,8 @@ class InternalMeshing(AMeshingTool):
len2 = end2 - start2 len2 = end2 - start2
if len1 < len2: if len1 < len2:
self.compl_sect(sect2, sect1, tag1, tag2) self.compl_sect(sect2, sect1, start2, start1,
len2, len1, tag1, tag2)
return return
elif len1 == len2: elif len1 == len2:
return return
@@ -168,21 +194,23 @@ class InternalMeshing(AMeshingTool):
for i in range(len2): for i in range(len2):
ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1)) ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1))
beta.append(ltot2) beta.append(ltot2)
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
for i in range(len1 - len2):
beta.append(1.0)
beta.append(0.0) # beta[-1]
if len2 < 1 or ltot2 < 0.0001: if len2 < 1 or ltot2 < 0.0001:
# unique point (copy len1 times) # unique point (copy len1 times)
# we are at an edge of the profile # we are at an edge of the profile
for i in range(len1-len2): for i in range(len1-len2):
p = sect2.point(start2).copy() p = sect2.point(start2).copy()
sect2.insert_point(start2, p) sect2.insert_point(start2, p)
len2 += 1 if tag1 == '': # left end point
sect2.point(start2).name = ''
if tag2 == '': # left end point
sect2.point(start2+1).name = ''
elif ltot1 < 0.0001: elif ltot1 < 0.0001:
sect2.add_npoints(len1-len2) sect2.add_npoints(len1-len2)
len2 = len1
else: # regular case else: # regular case
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
for i in range(len1 - len2):
beta.append(1.0)
beta.append(0.0) # beta[-1]
# loop on the points to insert # loop on the points to insert
for k in range(len1-len2): for k in range(len1-len2):
trouve = False trouve = False
@@ -241,9 +269,7 @@ class InternalMeshing(AMeshingTool):
p = sect2.point(start2+len2).copy() p = sect2.point(start2+len2).copy()
sect2.insert_point(start2+len2, p) sect2.insert_point(start2+len2, p)
sect2.point(start2+len2).name = '' sect2.point(start2+len2).name = ''
if tag1 != "un":
sect2.point(start2).name = tag1 sect2.point(start2).name = tag1
if tag1 != "np":
sect2.point(start2+len2).name = tag2 sect2.point(start2+len2).name = tag2
sect2.modified() sect2.modified()
@@ -278,8 +304,6 @@ class InternalMeshing(AMeshingTool):
sgn = -1.0 sgn = -1.0
else: else:
sgn = old_orientation sgn = old_orientation
if abs(sgn) < 0.5:
sgn = 1.0
new_rk = [0.0]*nprof new_rk = [0.0]*nprof
new_rk[origin] = origin_value new_rk[origin] = origin_value
@@ -320,42 +344,3 @@ class InternalMeshing(AMeshingTool):
return new_rk, new_end_rk, new_begin_rk return new_rk, new_end_rk, new_begin_rk
else: else:
return new_rk, new_begin_rk, new_end_rk return new_rk, new_begin_rk, new_end_rk
def get_guide_list(self, reach, profiles):
gl = reach.compute_guidelines()
# we make sure that the lines are in the left-to-right order
guide_list = [
[
x.name
for x in p.named_points()
if x.name in gl[0]
]
for p in profiles]
# we make sure the lines never cross
for gl in guide_list:
for i in range(len(guide_list[0])):
if gl[i] != guide_list[0][i]:
return None
return guide_list[0]
def get_profiles(self, reach, limites):
if reach is None or len(reach.profiles) == 0:
return None
if limites[0] > limites[1]:
lim = limites[1]
limites[1] = limites[0]
limites[0] = lim
elif limites[0] == limites[1]:
return None
profiles = [reach.profile(i).copy()
for i in range(limites[0], limites[1]+1)
]
return profiles
-368
View File
@@ -1,368 +0,0 @@
# GeoTIFF.py -- Pamhyr
# Copyright (C) 2024-2025 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import os
import struct
import logging
from functools import reduce
from tools import trace, timer
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.Scenario import Scenario
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger()
class GeoTIFF(SQLSubModel):
_sub_classes = []
def __init__(self, id: int = -1, enabled=True,
name="", description="",
path="", coordinates=None,
status=None, owner_scenario=-1):
super(GeoTIFF, self).__init__(
id=id, status=status,
owner_scenario=owner_scenario
)
self._enabled = enabled
self._name = f"GeoTIFF #{self._pamhyr_id}" if name == "" else name
self._description = description
self._file_bytes = b''
if coordinates is None:
self._coordinates = {
"bottom": 0.0,
"top": 0.0,
"left": 0.0,
"right": 0.0,
}
else:
self._coordinates = coordinates
self._file_name = ""
if path != "":
self.read_file(path)
self._memfile = None
def __getitem__(self, key):
value = None
if key == "enabled":
value = self._enabled
elif key == "name":
value = self.name
elif key == "description":
value = self.description
elif key == "file_name":
value = self.file_name
elif key == "coordinates":
value = self.coordinates
elif key == "coordinates_bottom":
value = self.coordinates["bottom"]
elif key == "coordinates_top":
value = self.coordinates["top"]
elif key == "coordinates_left":
value = self.coordinates["left"]
elif key == "coordinates_right":
value = self.coordinates["right"]
elif key == "memfile":
value = self.memfile
return value
def __setitem__(self, key, value):
if key == "enabled":
self.enabled = value
elif key == "name":
self.name = value
elif key == "description":
self.description = value
elif key == "file_name":
if self._file_name != value:
self.read_file(value)
elif key == "coordinates":
self.coordinates = value
elif key == "coordinates_bottom" or key == "bottom":
self.coordinates["bottom"] = value
elif key == "coordinates_top" or key == "top":
self.coordinates["top"] = value
elif key == "coordinates_left" or key == "left":
self.coordinates["left"] = value
elif key == "coordinates_right" or key == "right":
self.coordinates["right"] = value
self.modified()
@property
def enabled(self):
return self._enabled
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self.modified()
def is_enabled(self):
return self._enabled
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
self.modified()
@property
def description(self):
return self._description
@description.setter
def description(self, description):
self._description = description
self.modified()
@property
def file_name(self):
return self._file_name
@file_name.setter
def file_name(self, file_name):
self._file_name = file_name
self.modified()
@property
def coordinates(self):
return self._coordinates
@coordinates.setter
def coordinates(self, coordinates):
self._coordinates = coordinates
self.modified()
@property
def coord_bottom(self):
if self._coordinates is None:
return 0.0
return self._coordinates["bottom"]
@property
def coord_top(self):
if self._coordinates is None:
return 0.0
return self._coordinates["top"]
@property
def coord_left(self):
if self._coordinates is None:
return 0.0
return self._coordinates["left"]
@property
def coord_right(self):
if self._coordinates is None:
return 0.0
return self._coordinates["right"]
@property
def memfile(self):
if not _rasterio_loaded:
return None
if self._file_bytes == b'':
return None
if self._memfile is None:
self._memfile = MemoryFile()
self._memfile.write(self._file_bytes)
return self._memfile
def read_file(self, path):
logger.debug(f"Read GeoTIFF file at : '{path}'")
self._file_name = path
self._file_bytes = b''
self._memfile = None
nbytes = 0
with open(path, "rb") as f:
while True:
data = f.read(4096)
if not data:
break
nbytes += len(data)
self._file_bytes += data
logger.debug(f"Read GeoTIFF: {nbytes} bytes readed")
def write_file(self, path):
with open(path, "w+b") as f:
f.write(self._file_bytes)
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE geotiff{ext} (
{cls.create_db_add_pamhyr_id()},
enabled BOOLEAN NOT NULL,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
name TEXT NOT NULL,
description TEXT NOT NULL,
file_name TEXT NOT NULL,
file_bytes BLOB NOT NULL,
coordinates_bottom REAL NOT NULL,
coordinates_top REAL NOT NULL,
coordinates_left REAL NOT NULL,
coordinates_right REAL NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
PRIMARY KEY(pamhyr_id, scenario)
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) < 2:
cls._db_create(execute)
if major == "0" and int(minor) == 2:
if int(release) < 3:
cls._db_create(execute)
return True
@classmethod
def _db_load(cls, execute, data=None):
new = []
scenario = data["scenario"]
loaded = data['loaded_pid']
if scenario is None:
return new
table = execute(
"SELECT pamhyr_id, enabled, deleted, " +
"name, description, file_name, file_bytes, " +
"coordinates_bottom, coordinates_top, " +
"coordinates_left, coordinates_right, " +
"scenario " +
"FROM geotiff " +
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
)
if table is not None:
for row in table:
it = iter(row)
id = next(it)
enabled = (next(it) == 1)
deleted = (next(it) == 1)
name = next(it)
description = next(it)
file_name = next(it)
file_bytes = next(it)
coordinates_bottom = next(it)
coordinates_top = next(it)
coordinates_left = next(it)
coordinates_right = next(it)
owner_scenario = next(it)
f = cls(
id=id, enabled=enabled, name=name,
description=description, coordinates={
"bottom": coordinates_bottom,
"top": coordinates_top,
"left": coordinates_left,
"right": coordinates_right,
},
status=data['status'],
owner_scenario=owner_scenario
)
if deleted:
f.set_as_deleted()
f._file_bytes = file_bytes
loaded.add(id)
new.append(f)
data["scenario"] = scenario.parent
new += cls._db_load(execute, data)
data["scenario"] = scenario
return new
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute(
"INSERT INTO geotiff (" +
"pamhyr_id, enabled, deleted, " +
"name, description, file_name, file_bytes, " +
"coordinates_bottom, coordinates_top, " +
"coordinates_left, coordinates_right, " +
"scenario) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
self._pamhyr_id,
self._enabled,
self.is_deleted(),
self.name,
self.description,
self.file_name,
self._file_bytes,
self.coordinates['bottom'],
self.coordinates['top'],
self.coordinates['left'],
self.coordinates['right'],
self._status.scenario_id,
)
return True
-59
View File
@@ -1,59 +0,0 @@
# GeoTIFFList.py -- Pamhyr
# Copyright (C) 2024-2025 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 trace, timer
from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.GeoTIFF.GeoTIFF import GeoTIFF
class GeoTIFFList(PamhyrModelList):
_sub_classes = [GeoTIFF]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
new._lst = GeoTIFF._db_load(execute, data)
return new
def _db_save(self, execute, data=None):
ok = True
# Delete previous data
execute(
"DELETE FROM geotiff " +
f"WHERE scenario = {self._status.scenario_id}"
)
for gt in self._lst:
ok &= gt._db_save(execute, data)
return ok
@property
def files(self):
return self.lst
def new(self, index):
n = GeoTIFF(status=self._status)
self.insert(index, n)
self._status.modified()
return n
+1 -12
View File
@@ -76,22 +76,11 @@ class Profile(object):
return self.points[index] return self.points[index]
def named_point(self, name): def named_point(self, name):
if name == "un":
return self.points[0]
elif name == "np":
return self.points[-1]
else:
return next((p for p in self.points if p.name == name), None) return next((p for p in self.points if p.name == name), None)
def named_point_index(self, name): def named_point_index(self, name):
if name == "un":
return 0
elif name == "np":
return len(self.points) - 1
else:
return next( return next(
(p for p in enumerate(self.points) if p[1].name == name), (p for p in enumerate(self.points) if p[1].name == name), None
None
)[0] )[0]
@property @property
+3 -1
View File
@@ -1101,9 +1101,11 @@ class ProfileXYZ(Profile, SQLSubModel):
self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z
def copy(self): def copy(self):
p = ProfileXYZ(name=self.name, p = ProfileXYZ(id=self.id,
name=self.name,
rk=self.rk, rk=self.rk,
reach=self.reach, reach=self.reach,
num=self.num,
status=self._status) status=self._status)
for i, k in enumerate(self.points): for i, k in enumerate(self.points):
p.insert_point(i, k.copy()) p.insert_point(i, k.copy())
-2
View File
@@ -195,9 +195,7 @@ class Reach(SQLSubModel):
""" """
if profile in self._profiles: if profile in self._profiles:
self.undelete([profile]) self.undelete([profile])
profile.num = index
else: else:
profile.num = index
self._profiles.insert(index, profile) self._profiles.insert(index, profile)
self.modified() self.modified()
@@ -537,10 +537,7 @@ class InitialConditions(SQLSubModel):
strickler = 25.0 strickler = 25.0
if not compute_discharge: if not compute_discharge:
if profile.rk in data_discharge:
discharge = data_discharge[profile.rk] discharge = data_discharge[profile.rk]
else:
discharge = 0.0
else: else:
discharge = ( discharge = (
# ((width * 0.8) # ((width * 0.8)
@@ -608,10 +605,7 @@ class InitialConditions(SQLSubModel):
strickler = 25.0 strickler = 25.0
if not compute_height: if not compute_height:
if profile.rk in data_height:
height = data_height[profile.rk] height = data_height[profile.rk]
else:
height = 0.0
else: else:
if abs(incline) <= 0: if abs(incline) <= 0:
height = 0.0 height = 0.0
@@ -662,10 +656,7 @@ class InitialConditions(SQLSubModel):
for profile in profiles: for profile in profiles:
if not compute_discharge: if not compute_discharge:
if profile.rk in data_discharge:
d = data_discharge[profile.rk] d = data_discharge[profile.rk]
else:
d = 0.0
else: else:
d = discharge d = discharge
elevation = interp(profile.rk, elevation = interp(profile.rk,
+2 -17
View File
@@ -307,21 +307,6 @@ class Pollutants(SQLSubModel):
def data(self): def data(self):
return self._data return self._data
@data.setter
def data(self, data):
self._data = [PollutantCharacteristics(type=int(data[0]),
diametre=float(data[1]),
rho=float(data[2]),
porosity=float(data[3]),
cdc_riv=float(data[4]),
cdc_cas=float(data[5]),
apd=float(data[6]),
ac=float(data[7]),
bc=float(data[8]),
pollutant=self,
status=self._status
)]
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
execute(f""" execute(f"""
@@ -412,7 +397,7 @@ class Pollutants(SQLSubModel):
data["pollutant"] = new_pollutant data["pollutant"] = new_pollutant
new_pollutant._data = PollutantCharacteristics._db_load( new_pollutant._data = PollutantCharacteristics._db_load(
execute, data=data execute, data.copy()
) )
loaded.add(pid) loaded.add(pid)
@@ -436,7 +421,7 @@ class Pollutants(SQLSubModel):
f"AND scenario = {self._status.scenario_id}" f"AND scenario = {self._status.scenario_id}"
) )
execute( execute(
"DELETE FROM pollutants_characteristics " + f"DELETE FROM pollutants_characteristics " +
f"WHERE pollutant = {self.id} " + f"WHERE pollutant = {self.id} " +
f"AND scenario = {self._status.scenario_id}" f"AND scenario = {self._status.scenario_id}"
) )
+1 -1
View File
@@ -310,7 +310,7 @@ class Reservoir(SQLSubModel):
new_reservoir.set_as_deleted() new_reservoir.set_as_deleted()
new_reservoir._node = None new_reservoir._node = None
if node_id != -1 and node_id is not None: if node_id != -1:
new_reservoir._node = next( new_reservoir._node = next(
filter( filter(
lambda n: n.id == node_id, data["nodes"] lambda n: n.id == node_id, data["nodes"]
+1 -141
View File
@@ -29,134 +29,8 @@ from Model.Results.River.River import River
logger = logging.getLogger() logger = logging.getLogger()
class AdditionalData(SQLSubModel):
_sub_classes = []
def __init__(self, id=-1, study=None, data=None):
super(AdditionalData, self).__init__(
id=id, status=study.status,
owner_scenario=study.status.scenario.id
)
self._study = study
self._data = data
@property
def data(self):
return self._data
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE results_add_data{ext} (
{cls.create_db_add_pamhyr_id()},
result INTEGER NOT NULL,
type_x TEXT NOT NULL,
type_y TEXT NOT NULL,
legend TEXT NOT NULL,
unit TEXT NOT NULL,
data_len INTEGER NOT NULL,
x BLOB NOT NULL,
y BLOB NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
PRIMARY KEY(pamhyr_id, result, scenario)
)
""")
if ext != "":
return True
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) == 2 and int(release) <= 1:
cls._db_create(execute)
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
new = []
study = data['study']
status = data['status']
scenario = data["scenario"]
table = execute(
"SELECT pamhyr_id, type_x, type_y, " +
"legend, unit, data_len, x, y, " +
"scenario " +
"FROM results_add_data " +
f"WHERE scenario = {scenario.id}"
)
if table is None:
return new
for v in table:
it = iter(v)
pid = next(it)
type_x = next(it)
type_y = next(it)
legend = next(it)
unit = next(it)
data_len = next(it)
bx = next(it)
by = next(it)
owner_scenario = next(it)
data_format = ">" + ''.join(itertools.repeat("d", data_len))
x = struct.unpack(data_format, bx)
y = struct.unpack(data_format, by)
data = {
'type_x': type_x,
'type_y': type_y,
'legend': legend,
'unit': unit,
'x': x, 'y': y
}
new_data = cls(study=study)
new_data._data = data
new.append(new_data)
return new
def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario:
return
pid = self._pamhyr_id
data_len = len(self._data["x"])
data_format = ">" + ''.join(itertools.repeat("d", data_len))
bx = struct.pack(data_format, *self._data["x"])
by = struct.pack(data_format, *self._data["y"])
execute(
"INSERT INTO " +
"results_add_data (pamhyr_id, result, " +
"type_x, type_y, " +
"legend, unit, data_len, x, y, " +
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
self._pamhyr_id, data["result"],
self._data["type_x"], self._data["type_y"],
self._data["legend"], self._data["unit"],
data_len, bx, by, self._owner_scenario
)
return True
class Results(SQLSubModel): class Results(SQLSubModel):
_sub_classes = [River, AdditionalData] _sub_classes = [River]
def __init__(self, id=-1, study=None, solver=None, def __init__(self, id=-1, study=None, solver=None,
repertory="", name="0"): repertory="", name="0"):
@@ -176,7 +50,6 @@ class Results(SQLSubModel):
# Keep results creation date # Keep results creation date
"creation_date": datetime.now(), "creation_date": datetime.now(),
"study_revision": study.status.version, "study_revision": study.status.version,
"additional_data": [],
} }
if solver is not None: if solver is not None:
@@ -311,11 +184,6 @@ class Results(SQLSubModel):
data["timestamps"] = sorted(ts) data["timestamps"] = sorted(ts)
new_results._river = River._db_load(execute, data) new_results._river = River._db_load(execute, data)
new_results.set(
"additional_data",
AdditionalData._db_load(execute, data)
)
yield (solver_type, new_results) yield (solver_type, new_results)
def _db_save_clear(self, execute, solver_type, data=None): def _db_save_clear(self, execute, solver_type, data=None):
@@ -338,11 +206,6 @@ class Results(SQLSubModel):
f"WHERE scenario = {self._owner_scenario} " + f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}" f"AND result = {pid}"
) )
execute(
"DELETE FROM results_add_data " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}"
)
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario: if self._status.scenario.id != self._owner_scenario:
@@ -375,7 +238,4 @@ class Results(SQLSubModel):
data["result"] = self._pamhyr_id data["result"] = self._pamhyr_id
self._river._db_save(execute, data) self._river._db_save(execute, data)
for add_data in self.get("additional_data"):
add_data._db_save(execute, data)
return True return True
-3
View File
@@ -50,9 +50,6 @@ class Results(object):
self._phys_var_list = ["C", "G", "M", "D", "L", "N", "R"] self._phys_var_list = ["C", "G", "M", "D", "L", "N", "R"]
if solver is not None:
self.set("solver_type", solver._type)
@property @property
def date(self): def date(self):
date = self._meta_data["creation_date"] date = self._meta_data["creation_date"]
+10
View File
@@ -105,6 +105,16 @@ class Profile(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
create = False
if major == "0" and int(minor) < 2:
cls._db_create(execute)
create = True
if major == "0" and int(minor) == 2:
if int(release) < 1 and not create:
cls._db_create(execute)
create = True
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
-13
View File
@@ -59,7 +59,6 @@ from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList \
import LateralContributionsAdisTSList import LateralContributionsAdisTSList
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
from Model.GeoTIFF.GeoTIFFList import GeoTIFFList
from Model.Results.Results import Results from Model.Results.Results import Results
logger = logging.getLogger() logger = logging.getLogger()
@@ -469,7 +468,6 @@ class River(Graph):
LateralContributionsAdisTSList, LateralContributionsAdisTSList,
D90AdisTSList, D90AdisTSList,
DIFAdisTSList, DIFAdisTSList,
GeoTIFFList,
Results Results
] ]
@@ -507,8 +505,6 @@ class River(Graph):
self._D90AdisTS = D90AdisTSList(status=self._status) self._D90AdisTS = D90AdisTSList(status=self._status)
self._DIFAdisTS = DIFAdisTSList(status=self._status) self._DIFAdisTS = DIFAdisTSList(status=self._status)
self._geotiff = GeoTIFFList(status=self._status)
self._results = {} self._results = {}
@classmethod @classmethod
@@ -621,8 +617,6 @@ class River(Graph):
new._DIFAdisTS = DIFAdisTSList._db_load(execute, data) new._DIFAdisTS = DIFAdisTSList._db_load(execute, data)
new._geotiff = GeoTIFFList._db_load(execute, data)
return new return new
def _db_load_results(self, execute, data=None): def _db_load_results(self, execute, data=None):
@@ -656,8 +650,6 @@ class River(Graph):
objs.append(self._D90AdisTS) objs.append(self._D90AdisTS)
objs.append(self._DIFAdisTS) objs.append(self._DIFAdisTS)
objs.append(self._geotiff)
for solv_type in self.results: for solv_type in self.results:
objs.append(self.results[solv_type]) objs.append(self.results[solv_type])
@@ -734,7 +726,6 @@ class River(Graph):
self._BoundaryConditionsAdisTS, self._BoundaryConditionsAdisTS,
self._LateralContributionsAdisTS, self._LateralContributionsAdisTS,
self._D90AdisTS, self._DIFAdisTS, self._D90AdisTS, self._DIFAdisTS,
self._geotiff,
] ]
for solver in self._parameters: for solver in self._parameters:
@@ -827,10 +818,6 @@ Last export at: @date."""
def additional_files(self): def additional_files(self):
return self._additional_files return self._additional_files
@property
def geotiff(self):
return self._geotiff
@property @property
def rep_lines(self): def rep_lines(self):
return self._rep_lines return self._rep_lines
+1 -1
View File
@@ -31,7 +31,7 @@ class Scenario(SQLSubModel):
id: int = -1, id: int = -1,
name: str = "", name: str = "",
description: str = "", description: str = "",
x: int = 1000.0, y: int = 1000.0, x: int = 0.0, y: int = 0.0,
revision: int = 0, revision: int = 0,
parent=None): parent=None):
super(Scenario, self).__init__() super(Scenario, self).__init__()
+1 -1
View File
@@ -37,7 +37,7 @@ logger = logging.getLogger()
class Study(SQLModel): class Study(SQLModel):
_version = "0.2.3" _version = "0.2.1"
_sub_classes = [ _sub_classes = [
Scenario, Scenario,
-6
View File
@@ -56,7 +56,6 @@ class Modules(IterableFlag):
SEDIMENT_LAYER = auto() SEDIMENT_LAYER = auto()
ADDITIONAL_FILES = auto() ADDITIONAL_FILES = auto()
OUTPUT_RK = auto() OUTPUT_RK = auto()
GEOTIFF = auto()
# Results # Results
RESULTS = auto() RESULTS = auto()
@@ -82,7 +81,6 @@ class Modules(IterableFlag):
cls.RESULTS, cls.RESULTS,
cls.WINDOW_LIST, cls.WINDOW_LIST,
cls.OUTPUT_RK, cls.OUTPUT_RK,
cls.GEOTIFF
] ]
@classmethod @classmethod
@@ -101,7 +99,6 @@ class Modules(IterableFlag):
| cls.HYDRAULIC_STRUCTURES | cls.HYDRAULIC_STRUCTURES
| cls.RESERVOIR | cls.RESERVOIR
| cls.SEDIMENT_LAYER | cls.SEDIMENT_LAYER
| cls.GEOTIFF
) )
@classmethod @classmethod
@@ -117,7 +114,6 @@ class Modules(IterableFlag):
cls.HYDRAULIC_STRUCTURES, cls.HYDRAULIC_STRUCTURES,
cls.RESERVOIR, cls.RESERVOIR,
cls.SEDIMENT_LAYER, cls.SEDIMENT_LAYER,
cls.GEOTIFF,
] ]
@classmethod @classmethod
@@ -133,7 +129,6 @@ class Modules(IterableFlag):
cls.HYDRAULIC_STRUCTURES: "Hydraulic structures", cls.HYDRAULIC_STRUCTURES: "Hydraulic structures",
cls.RESERVOIR: "Reservoir", cls.RESERVOIR: "Reservoir",
cls.SEDIMENT_LAYER: "Sediment layer", cls.SEDIMENT_LAYER: "Sediment layer",
cls.GEOTIFF: "GeoTIFF",
} }
def impact(self): def impact(self):
@@ -173,5 +168,4 @@ _impact = {
Modules.HYDRAULIC_STRUCTURES: [], Modules.HYDRAULIC_STRUCTURES: [],
Modules.RESERVOIR: [], Modules.RESERVOIR: [],
Modules.SEDIMENT_LAYER: [], Modules.SEDIMENT_LAYER: [],
Modules.GEOTIFF: [],
} }
+6 -68
View File
@@ -96,69 +96,6 @@ class AdisTS(CommandLineSolver):
return lst return lst
##########
# Export #
##########
_alph = list(
map(
chr,
chain(
range(48, 58), # 0..9
range(65, 91), # A..Z
range(97, 123) # a..z
)
)
)
_l_alph = len(_alph)
_nodes_cnt = 0
_nodes_names = {}
_nodes_views = set()
def get_reach_name(self, reach):
return f"Reach_{reach.pamhyr_id:>3}".replace(" ", "0")
def get_node_name(self, node):
"""Generate a 3 char name for node
Args:
node: The node
Returns:
A 3 char name string
"""
n = node.pamhyr_id
if n in self._nodes_names:
return self._nodes_names[n]
name = ""
checked_new = False
while not checked_new:
self._nodes_cnt += 1
nc = self._nodes_cnt
name = "".join(
map(
lambda i: self._alph[i % self._l_alph],
[
int(nc / (self._l_alph * self._l_alph)),
int(nc / self._l_alph),
nc
]
)
)
checked_new = name not in self._nodes_views
self._nodes_views.add(name)
self._nodes_names[n] = name
return name
def cmd_args(self, study): def cmd_args(self, study):
lst = super(AdisTS, self).cmd_args(study) lst = super(AdisTS, self).cmd_args(study)
return lst return lst
@@ -260,7 +197,7 @@ class AdisTS(CommandLineSolver):
_nodes_views = set() _nodes_views = set()
def get_reach_name(self, reach): def get_reach_name(self, reach):
return f"Reach_{reach.pamhyr_id:>3}".replace(" ", "0") return f"Reach_{reach.id:>3}".replace(" ", "0")
def get_node_name(self, node): def get_node_name(self, node):
"""Generate a 3 char name for node """Generate a 3 char name for node
@@ -271,7 +208,7 @@ class AdisTS(CommandLineSolver):
Returns: Returns:
A 3 char name string A 3 char name string
""" """
n = node.pamhyr_id n = node.id
# print("node name id: ", n) # print("node name id: ", n)
@@ -401,9 +338,9 @@ class AdisTSwc(AdisTS):
) as f: ) as f:
for LC in POL_LC: for LC in POL_LC:
reach = next(filter( reach = next(filter(
lambda edge: edge.id == LC.edge, study.river.enable_edges() lambda edge: edge.id == LC.edge, study.river.edges()
)) # .name )) # .name
reach_name = self.get_reach_name(reach) reach_name = self.get_reach_name(self, reach)
f.write(f"${reach_name} {LC.begin_rk} {LC.end_rk}\n") f.write(f"${reach_name} {LC.begin_rk} {LC.end_rk}\n")
f.write(f"*temps |débit massique (kg/s)\n") f.write(f"*temps |débit massique (kg/s)\n")
f.write(f"*---------++++++++++\n") f.write(f"*---------++++++++++\n")
@@ -428,7 +365,8 @@ class AdisTSwc(AdisTS):
node = next(filter( node = next(filter(
lambda x: x.id == BC.node, study.river._nodes lambda x: x.id == BC.node, study.river._nodes
)) ))
f.write(f"${self.get_node_name(node)}\n") node_name = f"{node.id:3}".replace(" ", "x")
f.write(f"${node_name}\n")
if BC.type == "Concentration": if BC.type == "Concentration":
f.write(f"*temps |concentration\n") f.write(f"*temps |concentration\n")
+7 -13
View File
@@ -169,8 +169,9 @@ class Rubar3(CommandLineSolver):
it = iter(params) it = iter(params)
param = next(it, None) line = 0
while param != None: while line < 27:
param = next(it)
name = param.name name = param.name
value = param.value value = param.value
@@ -203,7 +204,7 @@ class Rubar3(CommandLineSolver):
# New line # New line
f.write(f"\n") f.write(f"\n")
param = next(it, None) line += 1
def _export_ts(self, study, repertory, qlog, name="0"): def _export_ts(self, study, repertory, qlog, name="0"):
if qlog is not None: if qlog is not None:
@@ -574,12 +575,8 @@ class Rubar3(CommandLineSolver):
for bc in bcs: for bc in bcs:
f.write(f"{len(bc)}\n") f.write(f"{len(bc)}\n")
if bc.bctype == "ZD":
for d0, d1 in bc.data: for d0, d1 in bc.data:
f.write(f"{d1} {d0}\n") f.write(f"{d1} {d0}\n")
else:
for d0, d1 in bc.data:
f.write(f"{d0} {d1}\n")
def read_profil(self, study, fname, results, qlog=None, name="0"): def read_profil(self, study, fname, results, qlog=None, name="0"):
logger.info(f"read: profil.{name}") logger.info(f"read: profil.{name}")
@@ -593,10 +590,7 @@ class Rubar3(CommandLineSolver):
def read_data_line(f): def read_data_line(f):
line = f.readline().split() line = f.readline().split()
if len(line) > 3:
return tuple(map(float, line)) return tuple(map(float, line))
else:
return (0.0, 0.0, 0.0, 0.0)
def set_and_compute_limites(reach, ind, z, q, s): def set_and_compute_limites(reach, ind, z, q, s):
reach.set(ind, timestamp, "Z", z) reach.set(ind, timestamp, "Z", z)
@@ -634,9 +628,9 @@ class Rubar3(CommandLineSolver):
h, s, q, z = read_data_line(f) h, s, q, z = read_data_line(f)
set_and_compute_limites( set_and_compute_limites(
reach, ind, reach, ind,
(pz + z + ph + h) / 2, (ph + pz + ph + h) / 2,
(pq + q) / 2, (pz + s) / 2,
(ps + s) / 2 (pq + q) / 2
) )
ph = h ph = h
-4
View File
@@ -58,11 +58,7 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def get_true_data_row(self, row): def get_true_data_row(self, row):
if len(self._data.data) > 0:
bc = self._data.data[row] bc = self._data.data[row]
else:
return 0
return next( return next(
map( map(
+1 -3
View File
@@ -17,7 +17,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging import logging
import traceback
from tools import trace, timer from tools import trace, timer
@@ -93,8 +92,7 @@ class ReplWindow(PamhyrWindow):
value = exec(rich_code) value = exec(rich_code)
value = self.__debug_exec_result__ value = self.__debug_exec_result__
except Exception as e: except Exception as e:
value = f"<font color=\"red\">" + str(e) + "</font>\n" value = f"<font color=\"red\">" + str(e) + "</font>"
value += f"<font color=\"grey\">{traceback.format_exc()}</font>"
# Display code # Display code
msg = f"<font color=\"grey\"> # " + code + " #</font>" msg = f"<font color=\"grey\"> # " + code + " #</font>"
+6 -5
View File
@@ -274,14 +274,15 @@ class FrictionTableModel(PamhyrTableModel):
with open(file_name, encoding="utf-8") as rug_file: with open(file_name, encoding="utf-8") as rug_file:
for line in rug_file: for line in rug_file:
if line.upper().startswith("K"): if line.upper().startswith("K"):
if int(line[1:4]) == reach_id: line = line.split()
data.append(line[4:].split()) if int(line[1]) == reach_id:
data.append(line[1:])
new_data = [] new_data = []
for d in data: for d in data:
new = None new = None
minor = float(d[2]) minor = float(d[3])
medium = float(d[3]) medium = float(d[4])
for s in self._study.river.stricklers.stricklers: for s in self._study.river.stricklers.stricklers:
if s.minor == minor and s.medium == medium: if s.minor == minor and s.medium == medium:
new = s new = s
@@ -291,5 +292,5 @@ class FrictionTableModel(PamhyrTableModel):
self._study.river.stricklers)) self._study.river.stricklers))
new.minor = minor new.minor = minor
new.medium = medium new.medium = medium
new_data.append([self._data, float(d[0]), float(d[1]), new, new]) new_data.append([self._data, float(d[1]), float(d[2]), new, new])
self.replace_data(new_data) self.replace_data(new_data)
-296
View File
@@ -1,296 +0,0 @@
# Window.py -- Pamhyr
# Copyright (C) 2024-2025 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 Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtWidgets import (
QLabel, QPlainTextEdit, QPushButton, QCheckBox,
QFileDialog, QVBoxLayout, QDoubleSpinBox,
)
from PyQt5.QtCore import (
QSettings
)
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.UndoCommand import (
SetCommand
)
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger()
class EditGeoTIFFWindow(PamhyrWindow):
_pamhyr_ui = "GeoTIFF"
_pamhyr_name = "Edit GeoTIFF"
def __init__(self, study=None, config=None, geotiff=None,
trad=None, undo=None, parent=None):
super(EditGeoTIFFWindow, self).__init__(
title=self._pamhyr_name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self._geotiff = geotiff
self._file_name = geotiff.file_name
self._hash_data.append(self._geotiff)
self._undo = undo
self.setup_values()
self.setup_graph()
self.setup_connection()
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout,
"verticalLayout_geotiff")
self._toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.plot_layout.addWidget(self._toolbar)
self.plot_layout.addWidget(self.canvas)
self.plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
trad=self._trad,
toolbar=None,
parent=self
)
self.plot.update()
self._plot_img = None
memfile = self._geotiff.memfile
if memfile is not None:
self.draw_geotiff(memfile=memfile)
def setup_values(self):
self.set_check_box("checkBox", self._geotiff.enabled)
self.set_line_edit_text("lineEdit_name", self._geotiff.name)
self.set_line_edit_text("lineEdit_description",
self._geotiff.description)
bounds = list(self._geotiff.coordinates.values())
self._set_values_from_bounds(bounds)
self._set_default_values_from_bounds(bounds)
self._reset_spinboxes()
if self._study.is_read_only():
self.set_check_box_enable("checkBox", False)
self.set_line_edit_enable("lineEdit_name", False)
self.set_line_edit_enable("lineEdit_description", False)
for button in ["import", "bottom", "top", "left", "right"]:
self.find(QPushButton, f"pushButton_{button}")\
.setEnabled(False)
for spin in ["bottom", "top", "left", "right"]:
self.find(QDoubleSpinBox, f"doubleSpinBox_{spin}")\
.setEnabled(False)
def _set_values_from_bounds(self, bounds):
self._values = {
"bottom": bounds[0],
"top": bounds[1],
"left": bounds[2],
"right": bounds[3],
}
def _set_default_values_from_bounds(self, bounds):
self._values_default = {
"bottom": bounds[0],
"top": bounds[1],
"left": bounds[2],
"right": bounds[3],
}
def _reset_spinboxes(self):
for key in self._values:
self._reset_spinbox(key)
def _reset_spinbox(self, key):
self.set_double_spin_box(
f"doubleSpinBox_{key}", self._values_default[key]
)
def setup_connection(self):
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.close)
self.find(QPushButton, "pushButton_ok")\
.clicked.connect(self.accept)
self.find(QPushButton, "pushButton_import")\
.clicked.connect(self._import)
self.find(QPushButton, "pushButton_bottom")\
.clicked.connect(lambda: self._reset_spinbox("bottom"))
self.find(QPushButton, "pushButton_top")\
.clicked.connect(lambda: self._reset_spinbox("top"))
self.find(QPushButton, f"pushButton_left")\
.clicked.connect(lambda: self._reset_spinbox("left"))
self.find(QPushButton, f"pushButton_right")\
.clicked.connect(lambda: self._reset_spinbox("right"))
self.find(QDoubleSpinBox, f"doubleSpinBox_bottom")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("bottom")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_top")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("top")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_left")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("left")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_right")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("right")
)
def update_values_from_spinbox(self, key):
self._values[key] = self.get_double_spin_box(f"doubleSpinBox_{key}")
left = self._values["left"]
right = self._values["right"]
bottom = self._values["bottom"]
top = self._values["top"]
self._plot_img.set_extent((left, right, bottom, top))
self.plot.idle()
def draw_geotiff(self, memfile=None):
if not _rasterio_loaded:
return
if memfile is None:
if self._file_name == "":
return
with rasterio.open(self._file_name) as data:
img = data.read()
b = data.bounds[:] # left, bottom, right, top
if b[2] > b[0] and b[1] < b[3]:
coord = [b[1], b[3], b[0], b[2]]
else:
xlim = self.canvas.axes.get_xlim()
ylim = self.canvas.axes.get_ylim()
coord = ylim + xlim
self._set_values_from_bounds(coord)
self._set_default_values_from_bounds(coord)
else:
with memfile.open() as gt:
img = gt.read()
if self._plot_img is not None:
self._plot_img.remove()
left = self._values["left"]
right = self._values["right"]
bottom = self._values["bottom"]
top = self._values["top"]
self._plot_img = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
self.plot.idle()
self._reset_spinboxes()
def _import(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_geotiff"],
self._trad["file_all"],
]
filename, _ = QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if filename != "":
self._file_name = filename
self.draw_geotiff()
def accept(self):
if self._study.is_editable():
is_enabled = self.get_check_box("checkBox")
name = self.get_line_edit_text("lineEdit_name")
description = self.get_line_edit_text("lineEdit_description")
coord_bottom = self.get_double_spin_box("doubleSpinBox_bottom")
coord_top = self.get_double_spin_box("doubleSpinBox_top")
coord_left = self.get_double_spin_box("doubleSpinBox_left")
coord_right = self.get_double_spin_box("doubleSpinBox_right")
self._undo.push(
SetCommand(
self._geotiff, enabled=is_enabled,
name=name, description=description,
coordinates_bottom=coord_bottom,
coordinates_top=coord_top,
coordinates_left=coord_left,
coordinates_right=coord_right,
file_name=self._file_name,
)
)
self._propagate_update(key=Modules.GEOTIFF)
self.close()
-96
View File
@@ -1,96 +0,0 @@
# List.py -- Pamhyr
# Copyright (C) 2024-2025 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from functools import reduce
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant,
)
from PyQt5.QtGui import (
QColor, QBrush,
)
from View.Tools.PamhyrList import PamhyrListModel
from View.GeoTIFF.UndoCommand import (
AddCommand, DelCommand
)
logger = logging.getLogger()
class ListModel(PamhyrListModel):
def get_true_data_row(self, row):
el = self._data.get(row)
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == el,
enumerate(self._data._lst)
)
), 0
)
def data(self, index, role):
row = index.row()
column = index.column()
file = self._data.files[row]
if role == Qt.ForegroundRole:
color = Qt.gray
if file.is_enabled():
color = QColor("black")
else:
color = QColor("grey")
return QBrush(color)
if role == Qt.ItemDataRole.DisplayRole:
text = f"{file.name}: '{file.description}'"
if not file.is_enabled():
text += " (disabled)"
return text
return QVariant()
def add(self, row):
row = self.get_true_data_row(row)
self._undo.push(
AddCommand(
self._data, row
)
)
self.update()
def delete(self, row):
self._undo.push(
DelCommand(
self._data, self._data.files[row]
)
)
self.update()
-35
View File
@@ -1,35 +0,0 @@
# Translate.py -- Pamhyr
# Copyright (C) 2024-2025 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
from View.Translate import MainTranslate
_translate = QCoreApplication.translate
class GeoTIFFTranslate(MainTranslate):
def __init__(self):
super(GeoTIFFTranslate, self).__init__()
self._dict["GeoTIFF files"] = _translate(
"GeoTIFF", "GeoTIFF files"
)
self._dict["Edit additional file"] = _translate(
"GeoTIFF", "Edit GeoTIFF file"
)
-81
View File
@@ -1,81 +0,0 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2024-2025 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 trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
class SetCommand(QUndoCommand):
def __init__(self, geotiff, **kwargs):
QUndoCommand.__init__(self)
self._geotiff = geotiff
self._new = kwargs
self._old = None
def undo(self):
f = self._geotiff
for key in self._old:
f[key] = self._old[key]
def redo(self):
f = self._geotiff
if self._old is None:
self._old = {}
for key in self._new:
self._old[key] = f[key]
for key in self._new:
f[key] = self._new[key]
class AddCommand(QUndoCommand):
def __init__(self, files, row):
QUndoCommand.__init__(self)
self._files = files
self._row = row
self._new = None
def undo(self):
self._new.set_as_deleted()
def redo(self):
if self._new is None:
self._new = self._files.new(self._row)
else:
self._new.set_as_not_deleted()
class DelCommand(QUndoCommand):
def __init__(self, files, line):
QUndoCommand.__init__(self)
self._files = files
self._line = line
def undo(self):
self._line.set_as_not_deleted()
def redo(self):
self._line.set_as_deleted()
-220
View File
@@ -1,220 +0,0 @@
# Window.py -- Pamhyr
# Copyright (C) 2024-2025 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 trace, timer
from PyQt5.QtWidgets import (
QAction, QListView, QVBoxLayout,
)
from matplotlib.patches import Rectangle
from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from View.GeoTIFF.List import ListModel
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.Edit.Window import EditGeoTIFFWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
class GeoTIFFListWindow(PamhyrWindow):
_pamhyr_ui = "GeoTIFFList"
_pamhyr_name = "GeoTIFF files"
def __init__(self, study=None, config=None,
parent=None):
trad = GeoTIFFTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
super(GeoTIFFListWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self.setup_list()
self.setup_graph()
self.setup_connections()
def setup_list(self):
lst = self.find(QListView, "listView")
self._list = ListModel(
list_view=lst,
data=self._study.river.geotiff,
undo=self._undo_stack,
trad=self._trad,
)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self._plot_layout = self.find(QVBoxLayout, "verticalLayout")
self._toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout.addWidget(self._toolbar)
self._plot_layout.addWidget(self.canvas)
self._plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
geotiff=self._study.river.geotiff,
trad=self._trad,
toolbar=None,
parent=self
)
self._plot_rect = []
self._plot.update()
def setup_connections(self):
if self._study.is_editable():
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
else:
self.find(QAction, "action_add").setEnabled(False)
self.find(QAction, "action_delete").setEnabled(False)
self.find(QAction, "action_edit").triggered.connect(self.edit)
self.find(QListView, "listView")\
.selectionModel()\
.selectionChanged\
.connect(self._update_rectangle)
def _propagated_update(self, key=Modules(0)):
if Modules.GEOMETRY not in key and Modules.GEOTIFF not in key:
return
self.update()
def update(self):
self._list.update()
self._plot.update()
self._update_rectangle()
def _update_rectangle(self):
for rect in self._plot_rect:
rect.remove()
self._plot_rect = []
rows = self.selected_rows()
if len(rows) <= 0:
return
for row in rows:
files = self._study.river.geotiff.files
if len(files) <= row:
continue
geotiff = files[row]
coord = geotiff.coordinates
xy = (coord["left"], coord["bottom"])
width = abs(coord["right"] - coord["left"])
height = abs(coord["top"] - coord["bottom"])
rect = Rectangle(
xy, width, height,
edgecolor='red', facecolor='none',
lw=2
)
self._plot_rect.append(rect)
self.canvas.axes.add_patch(
rect
)
self._plot.idle()
def selected_rows(self):
lst = self.find(QListView, f"listView")
return list(map(lambda i: i.row(), lst.selectedIndexes()))
def add(self):
rows = self.selected_rows()
if len(rows) > 0:
row = rows[0]
else:
row = 0
self._list.add(row)
def delete(self):
rows = self.selected_rows()
if len(rows) == 0:
return
self._list.delete(rows[0])
self.update()
def edit(self):
rows = self.selected_rows()
for row in rows:
geotiff = self._study.river.geotiff.files[row]
if self.sub_window_exists(
EditGeoTIFFWindow,
data=[self._study, self._config, geotiff]
):
continue
win = EditGeoTIFFWindow(
study=self._study,
config=self._config,
geotiff=geotiff,
trad=self._trad,
undo=self._undo_stack,
parent=self,
)
win.show()
def _undo(self):
self._undo_stack.undo()
self.update()
def _redo(self):
self._undo_stack.redo()
self.update()
+1 -13
View File
@@ -251,23 +251,11 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def meshing(self, mesher, data, tableView): def meshing(self, mesher, data, tableView):
new_profiles = mesher.meshing(
self._data,
**data
)
if new_profiles is None:
return
if len(new_profiles) == 0:
return
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo.push( self._undo.push(
MeshingCommand( MeshingCommand(
self._data, new_profiles, data, tableView self._data, mesher, data, tableView
) )
) )
+9 -2
View File
@@ -274,14 +274,15 @@ class UpdateRKCommand(QUndoCommand):
class MeshingCommand(QUndoCommand): class MeshingCommand(QUndoCommand):
def __init__(self, reach, new_profiles, data, tableView): def __init__(self, reach, mesher, data, tableView):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._reach = reach self._reach = reach
self._data = data self._data = data
self._limites = data["limites"] self._limites = data["limites"]
self._mesher = mesher
self._new_profiles = new_profiles self._new_profiles = None
self._new_profiles_indexes = None self._new_profiles_indexes = None
self._tableView = tableView self._tableView = tableView
self._indexes = tableView.selectionModel().selection() self._indexes = tableView.selectionModel().selection()
@@ -307,6 +308,12 @@ class MeshingCommand(QUndoCommand):
) )
def redo(self): def redo(self):
if self._new_profiles is None:
self._new_profiles = self._mesher.meshing(
self._reach,
**self._data
)
if self._new_profiles_indexes is None: if self._new_profiles_indexes is None:
k = self._limites[0] k = self._limites[0]
self._new_profiles_indexes = [] self._new_profiles_indexes = []
+3 -21
View File
@@ -330,27 +330,9 @@ class InitialConditionTableModel(PamhyrTableModel):
if not (line.startswith("#") or if not (line.startswith("#") or
line.startswith("*") or line.startswith("*") or
line.startswith("$")): line.startswith("$")):
line_split = line.split() line = line.split()
formated = False if int(line[0]) == reach_id:
if len(line_split[0]) > 3: data.append([line[4], line[2], line[3]])
formated = True
elif len(line_split[2]) > 10:
formated = True
elif len(line_split[3]) > 11:
formated = True
elif len(line_split[4]) > 9:
formated = True
if formated: # old PamHyr format
if int(line[1:4]) == reach_id:
data.append([line[31:40],
line[10:20],
line[20:31]])
else:
if int(line_split[0]) == reach_id:
data.append([line_split[4],
line_split[2],
line_split[3]])
self._undo.push( self._undo.push(
ReplaceDataCommand( ReplaceDataCommand(
-4
View File
@@ -224,8 +224,6 @@ class InitialConditionsWindow(PamhyrWindow):
def _update_plot(self): def _update_plot(self):
self.plot_1.draw() self.plot_1.draw()
self.plot_2.draw() self.plot_2.draw()
self.plot_1.idle()
self.plot_2.idle()
def _propagated_update(self, key=Modules(0)): def _propagated_update(self, key=Modules(0)):
if Modules.GEOMETRY not in key: if Modules.GEOMETRY not in key:
@@ -322,12 +320,10 @@ class InitialConditionsWindow(PamhyrWindow):
def _import_from_results(self, results): def _import_from_results(self, results):
logger.debug(f"import from results: {results}") logger.debug(f"import from results: {results}")
self._table.import_from_results(results) self._table.import_from_results(results)
self._update()
def _import_from_ini_file(self, file_name): def _import_from_ini_file(self, file_name):
logger.debug(f"import from INI file: {file_name}") logger.debug(f"import from INI file: {file_name}")
self._table.read_from_ini(file_name) self._table.read_from_ini(file_name)
self._update()
def move_up(self): def move_up(self):
row = self.index_selected_row() row = self.index_selected_row()
+17 -131
View File
@@ -47,7 +47,7 @@ from PyQt5.QtWidgets import (
QMainWindow, QApplication, QAction, QMainWindow, QApplication, QAction,
QFileDialog, QShortcut, QMenu, QToolBar, QFileDialog, QShortcut, QMenu, QToolBar,
QMessageBox, QProgressDialog, QTabWidget, QMessageBox, QProgressDialog, QTabWidget,
QDialog, QVBoxLayout, QLabel, QInputDialog, QDialog, QVBoxLayout, QLabel,
) )
from PyQt5.uic import loadUi from PyQt5.uic import loadUi
@@ -79,7 +79,6 @@ from View.Frictions.Window import FrictionsWindow
from View.SedimentLayers.Window import SedimentLayersWindow from View.SedimentLayers.Window import SedimentLayersWindow
from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
from View.AdditionalFiles.Window import AddFileListWindow from View.AdditionalFiles.Window import AddFileListWindow
from View.GeoTIFF.Window import GeoTIFFListWindow
from View.REPLines.Window import REPLineListWindow from View.REPLines.Window import REPLineListWindow
from View.SolverParameters.Window import SolverParametersWindow from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import ( from View.RunSolver.Window import (
@@ -158,7 +157,7 @@ define_model_action = [
"action_menu_boundary_conditions_sediment", "action_menu_boundary_conditions_sediment",
"action_menu_rep_additional_lines", "action_menu_output_rk", "action_menu_rep_additional_lines", "action_menu_output_rk",
"action_menu_run_adists", "action_menu_pollutants", "action_menu_run_adists", "action_menu_pollutants",
"action_menu_d90", "action_menu_dif", "action_menu_edit_geotiff" "action_menu_d90", "action_menu_dif",
] ]
action = ( action = (
@@ -277,8 +276,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_new": self.open_new_study, "action_menu_new": self.open_new_study,
"action_menu_edit": self.open_edit_study, "action_menu_edit": self.open_edit_study,
"action_menu_open": self.open_model, "action_menu_open": self.open_model,
"action_menu_save": lambda: self.save_study(), "action_menu_save": self.save_study,
"action_menu_save_as": lambda: self.save_as_study(), "action_menu_save_as": self.save_as_study,
"action_menu_numerical_parameter": self.open_solver_parameters, "action_menu_numerical_parameter": self.open_solver_parameters,
"action_menu_edit_scenarios": self.open_scenarios, "action_menu_edit_scenarios": self.open_scenarios,
"action_menu_edit_network": self.open_network, "action_menu_edit_network": self.open_network,
@@ -298,7 +297,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.open_reach_sediment_layers, self.open_reach_sediment_layers,
"action_menu_additional_file": self.open_additional_files, "action_menu_additional_file": self.open_additional_files,
"action_menu_rep_additional_lines": self.open_rep_lines, "action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_edit_geotiff": self.open_geotiff,
"action_menu_close": self.close_model, "action_menu_close": self.close_model,
"action_menu_results_last": self.open_last_results, "action_menu_results_last": self.open_last_results,
"action_menu_open_results_from_file": self.open_results_from_file, "action_menu_open_results_from_file": self.open_results_from_file,
@@ -315,7 +313,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_about": self.open_about, "action_menu_about": self.open_about,
# ToolBar action # ToolBar action
"action_toolBar_open": self.open_model, "action_toolBar_open": self.open_model,
"action_toolBar_save": lambda: self.save_study(), "action_toolBar_save": self.save_study,
"action_toolBar_close": self.close_model, "action_toolBar_close": self.close_model,
"action_toolBar_run_solver": self.run_lasest_solver, "action_toolBar_run_solver": self.run_lasest_solver,
# Current actions # Current actions
@@ -608,16 +606,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return None return None
def get_last_results(self, solver):
if self._study is None:
return None
results = self._study.results
if solver in results:
return self._study.results[solver]
return None
@last_results.setter @last_results.setter
def last_results(self, results): def last_results(self, results):
if self._study is None: if self._study is None:
@@ -649,10 +637,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
logger.info(f"Open Study - {self._study.name}") logger.info(f"Open Study - {self._study.name}")
self.set_title() self.set_title()
def _save(self, source): def save_study(self):
self.save_study(progress_parent=source)
def save_study(self, progress_parent=None):
"""Save current study """Save current study
Save current study, if study as no associate file, open a Save current study, if study as no associate file, open a
@@ -682,15 +667,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self._backup_timer.blockSignals(True) self._backup_timer.blockSignals(True)
self._save_mutex.lock() self._save_mutex.lock()
parent = self
if progress_parent is not None:
parent = progress_parent
sql_request_count = self._study.sql_save_request_count() sql_request_count = self._study.sql_save_request_count()
progress = QProgressDialog( progress = QProgressDialog(
"Saving...", None, "Saving...", None,
0, sql_request_count, 0, sql_request_count,
parent=parent parent=self
) )
progress.setWindowModality(Qt.WindowModal) progress.setWindowModality(Qt.WindowModal)
progress.setValue(0) progress.setValue(0)
@@ -703,7 +684,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
progress=lambda: progress.setValue(progress.value() + 1) progress=lambda: progress.setValue(progress.value() + 1)
) )
progress.close()
status += " Done" status += " Done"
logger.info(status) logger.info(status)
self.statusbar.showMessage(status, 3000) self.statusbar.showMessage(status, 3000)
@@ -778,8 +758,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
progress.setValue(progress.value() + 1) progress.setValue(progress.value() + 1)
progress.close()
def save_as_study_single_scenario(self, sid=-1): def save_as_study_single_scenario(self, sid=-1):
sql_request_count = self._study.sql_save_request_count() sql_request_count = self._study.sql_save_request_count()
@@ -804,7 +782,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
status += " Done" status += " Done"
progress.close()
logger.info(status) logger.info(status)
self.statusbar.showMessage(status, 3000) self.statusbar.showMessage(status, 3000)
@@ -1206,31 +1183,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
Returns: Returns:
Nothing Nothing
""" """
if self._study is None:
return
if self._study.filename is None or self._study.filename == "":
self.message_box(
window_title=self._trad["Warning"],
text=self._trad["mb_unsaved_study_title"],
informative_text=self._trad["mb_unsaved_studyh_msg"]
)
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
file_name, _ = QFileDialog.getSaveFileName(
self, "Save File",
"", "Pamhyr(*.pamhyr)",
options=options,
)
if file_name is None or file_name == "":
return
if file_name.rsplit(".", 1)[-1] == "pamhyr":
self._study.filename = file_name
else:
self._study.filename = file_name + ".pamhyr"
if self._study is not None: if self._study is not None:
if self.sub_window_exists( if self.sub_window_exists(
ScenariosWindow, ScenariosWindow,
@@ -1387,19 +1339,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
self.additonal_files.show() self.additonal_files.show()
def open_geotiff(self):
if self._study is not None:
if self.sub_window_exists(
GeoTIFFListWindow,
data=[self._study, None]
):
return
self.geotiff = GeoTIFFListWindow(
study=self._study, parent=self
)
self.geotiff.show()
def open_rep_lines(self): def open_rep_lines(self):
if self._study is not None: if self._study is not None:
if self.sub_window_exists( if self.sub_window_exists(
@@ -1583,16 +1522,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
# If no specific results, get last results # If no specific results, get last results
if results is None: if results is None:
def reading_fn():
self._tmp_results = self.last_results
if self.last_results is None:
def reading_fn(): def reading_fn():
self._tmp_results = solver.results( self._tmp_results = solver.results(
self._study, self._study,
self._solver_workdir(solver), self._solver_workdir(solver),
) )
if solver == self._last_solver:
def reading_fn():
self._tmp_results = self.last_results
# Open from file # Open from file
if type(results) is str: if type(results) is str:
logger.info(f"Open results from {os.path.dirname(results)}") logger.info(f"Open results from {os.path.dirname(results)}")
@@ -1616,16 +1555,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
dlg.exec_() dlg.exec_()
results = self._tmp_results results = self._tmp_results
# self.last_results = results
# No results available # No results available
if results is None: if results is None:
self.msg_open_results_no_results()
return return
# results does not have values, for example if geometry missmatch # results does not have values, for example if geometry missmatch
if not results.is_valid: if not results.is_valid:
self.msg_open_results_invalid_results()
return return
if results.get('study_revision') != self._study.status.version: if results.get('study_revision') != self._study.status.version:
@@ -1651,20 +1587,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
res.show() res.show()
def msg_open_results_no_results(self):
self.message_box(
window_title=self._trad["Warning"],
text=self._trad["mb_open_results_title"],
informative_text=self._trad["mb_open_results_no_results_msg"]
)
def msg_open_results_invalid_results(self):
self.message_box(
window_title=self._trad["Error"],
text=self._trad["mb_open_results_title"],
informative_text=self._trad["mb_open_results_invalid_results_msg"]
)
def open_solver_results_adists(self, solver, results=None): def open_solver_results_adists(self, solver, results=None):
def reading_fn(): def reading_fn():
self._tmp_results = results self._tmp_results = results
@@ -1693,11 +1615,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
os.path.dirname(results), os.path.dirname(results),
name=name name=name
) )
dlg = WaitingDialog(
payload_fn=reading_fn, dlg = ReadingResultsDialog(reading_fn=reading_fn, parent=self)
title="waiting_result",
parent=self
)
dlg.exec_() dlg.exec_()
results = self._tmp_results results = self._tmp_results
@@ -1740,49 +1659,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
return workdir return workdir
def is_solver_workdir_exists(self, solver, scenario=None):
return os.path.exists(
self._solver_workdir(solver, scenario)
)
def open_last_results(self): def open_last_results(self):
if self._last_solver is None: if self._last_solver is None:
return return
solver_type = self._study.results
solver_name, ok = QInputDialog.getItem(
self, self._trad['Solver'],
self._trad['Solver'] + ":",
list(
map(
lambda s: s.name,
filter(
lambda s: (self.is_solver_workdir_exists(s)
or s._type in solver_type),
self.conf.solvers
)
)
)
)
if not ok:
return
solver = next(
filter(
lambda s: s.name == solver_name,
self.conf.solvers
)
)
if self._last_solver._type == "mage8": if self._last_solver._type == "mage8":
self.open_solver_results( self.open_solver_results(self._last_solver,
solver, # self.last_results self.last_results)
)
elif self._last_solver._type == "adistswc": elif self._last_solver._type == "adistswc":
self.open_solver_results_adists( self.open_solver_results_adists(self._last_solver,
solver, # self.last_results self.last_results)
)
def open_results_from_file(self): def open_results_from_file(self):
if self._study is None: if self._study is None:
-16
View File
@@ -27,20 +27,6 @@ from View.Tools.PamhyrWidget import PamhyrWidget
from PyQt5.QtWidgets import QVBoxLayout from PyQt5.QtWidgets import QVBoxLayout
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger() logger = logging.getLogger()
@@ -118,12 +104,10 @@ class WidgetInfo(PamhyrWidget):
self.plot = PlotXY( self.plot = PlotXY(
canvas=self.canvas, canvas=self.canvas,
data=self._study.river.enable_edges(), data=self._study.river.enable_edges(),
geotiff=self._study.river.geotiff,
trad=self.parent._trad, trad=self.parent._trad,
toolbar=self._toolbar_xy, toolbar=self._toolbar_xy,
parent=self parent=self
) )
self.plot.update() self.plot.update()
def set_network_values(self): def set_network_values(self):
+2 -61
View File
@@ -27,26 +27,12 @@ from PyQt5.QtCore import (
) )
from PyQt5.QtWidgets import QApplication, QTableView from PyQt5.QtWidgets import QApplication, QTableView
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class PlotXY(PamhyrPlot): class PlotXY(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, geotiff=None, def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
toolbar=None, table=None, parent=None): table=None, parent=None):
super(PlotXY, self).__init__( super(PlotXY, self).__init__(
canvas=canvas, canvas=canvas,
trad=trad, trad=trad,
@@ -57,14 +43,10 @@ class PlotXY(PamhyrPlot):
) )
self._data = data self._data = data
self._geotiff = geotiff
self.label_x = self._trad["x"] self.label_x = self._trad["x"]
self.label_y = self._trad["y"] self.label_y = self._trad["y"]
self.parent = parent self.parent = parent
self._plot_img = {}
self._isometric_axis = True self._isometric_axis = True
self._auto_relim_update = True self._auto_relim_update = True
@@ -87,12 +69,7 @@ class PlotXY(PamhyrPlot):
if data.reach.number_profiles != 0: if data.reach.number_profiles != 0:
self.draw_xy(data.reach) self.draw_xy(data.reach)
self.draw_lr(data.reach) self.draw_lr(data.reach)
if self._geotiff is not None:
self.draw_geotiff(self._geotiff.files)
self.idle() self.idle()
return return
def draw_xy(self, reach): def draw_xy(self, reach):
@@ -136,42 +113,6 @@ class PlotXY(PamhyrPlot):
) )
self.line_lr.append(line) self.line_lr.append(line)
def draw_geotiff(self, lst):
if not _rasterio_loaded:
return
for img in self._plot_img:
self._plot_img[img].remove()
self._plot_img = {}
for geotiff in lst:
if geotiff.is_deleted():
continue
memfile = geotiff.memfile
if memfile is None:
continue
with memfile.open() as gt:
img = gt.read()
coords = geotiff.coordinates
left = coords["left"]
right = coords["right"]
bottom = coords["bottom"]
top = coords["top"]
self._plot_img[geotiff] = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
if not geotiff.is_enabled():
self._plot_img[geotiff].set(alpha=0.5)
self.idle()
@timer @timer
def update(self): def update(self):
self.draw() self.draw()
+1 -1
View File
@@ -80,7 +80,7 @@ class AddCommand(QUndoCommand):
self._new = self._pollutants_lst.new( self._new = self._pollutants_lst.new(
self._pollutants_lst, self._index self._pollutants_lst, self._index
) )
self._new.data = [0, 0., 0., 0., 0., 0., 0., 0., 0.] self._new._data = [[0, 0., 0., 0., 0., 0., 0., 0., 0.]]
self._new_ic_adists = self._data.new(self._index, self._new.id) self._new_ic_adists = self._data.new(self._index, self._new.id)
else: else:
self._pollutants_lst.insert(self._index, self._new) self._pollutants_lst.insert(self._index, self._new)
+3 -12
View File
@@ -149,17 +149,8 @@ class PlotAC(PamhyrPlot):
self.annotation = [] self.annotation = []
self.cgl, self.igl = reach.geometry.compute_guidelines() self.cgl, self.igl = reach.geometry.compute_guidelines()
# we make sure that the lines are in the left-to-right order lcomplete = list(self.cgl)
lcomplete = [ lincomplete = list(self.igl)
x.name
for x in profile.geometry.named_points()
if x.name in self.cgl
]
lincomplete = [
x.name
for x in profile.geometry.named_points()
if x.name in self.igl
]
self.color_complete_gl = self.colors self.color_complete_gl = self.colors
self.color_incomplete_gl = 2 * ["grey"] self.color_incomplete_gl = 2 * ["grey"]
@@ -179,7 +170,7 @@ class PlotAC(PamhyrPlot):
] ]
else: else:
color = self.color_incomplete_gl[ color = self.color_incomplete_gl[
lincomplete.index(txt) % len(self.color_incomplete_gl) lincomplete.index(txt)
] ]
annotation = self.canvas.axes.annotate( annotation = self.canvas.axes.annotate(
-53
View File
@@ -31,20 +31,6 @@ from PyQt5.QtCore import (
) )
from PyQt5.QtWidgets import QApplication, QTableView from PyQt5.QtWidgets import QApplication, QTableView
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
@@ -66,8 +52,6 @@ class PlotXY(PamhyrPlot):
self.line_gl = [] self.line_gl = []
self.overflow = [] self.overflow = []
self._plot_img = {}
self._timestamps = parent._timestamps self._timestamps = parent._timestamps
self._current_timestamp = max(self._timestamps) self._current_timestamp = max(self._timestamps)
self._current_reach_id = reach_id self._current_reach_id = reach_id
@@ -169,7 +153,6 @@ class PlotXY(PamhyrPlot):
reach = results.river.reach(self._current_reach_id) reach = results.river.reach(self._current_reach_id)
reaches = results.river.reachs reaches = results.river.reachs
self.draw_geotiff()
self.draw_profiles(reach, reaches) self.draw_profiles(reach, reaches)
self.draw_water_elevation(reach) self.draw_water_elevation(reach)
self.draw_water_elevation_max(reach) self.draw_water_elevation_max(reach)
@@ -183,7 +166,6 @@ class PlotXY(PamhyrPlot):
if reach.geometry.number_profiles == 0: if reach.geometry.number_profiles == 0:
self._init = False self._init = False
return return
self.line_xy = [] self.line_xy = []
# TODO uncomment to draw all the reaches # TODO uncomment to draw all the reaches
# self.draw_other_profiles(reaches) # self.draw_other_profiles(reaches)
@@ -324,41 +306,6 @@ class PlotXY(PamhyrPlot):
alpha=0.7 alpha=0.7
) )
def draw_geotiff(self):
if not _rasterio_loaded:
return
lst = self._data[0]._study.river._geotiff.lst
for img in self._plot_img:
self._plot_img[img].remove()
self._plot_img = {}
for geotiff in lst:
memfile = geotiff.memfile
if memfile is None:
return
with memfile.open() as gt:
img = gt.read()
coords = geotiff.coordinates
left = coords["left"]
right = coords["right"]
bottom = coords["bottom"]
top = coords["top"]
self._plot_img[geotiff] = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
if not geotiff.is_enabled():
self._plot_img[geotiff].set(alpha=0.5)
self.idle()
def set_reach(self, reach_id): def set_reach(self, reach_id):
self._current_reach_id = reach_id self._current_reach_id = reach_id
self._current_profile_id = 0 self._current_profile_id = 0
+133 -89
View File
@@ -31,14 +31,23 @@ except Exception as e:
print(f"Module 'rasterio' is not available: {e}") print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False _rasterio_loaded = False
try:
import pyaudio
import wave
_audio_loaded = True
except Exception as e:
print(f"Either 'pyaudio' or 'wave' is not available: {e}")
_audio_loaded = False
from functools import reduce from functools import reduce
from numpy import sqrt import numpy as np
from datetime import datetime from datetime import datetime
from tools import trace, timer, logger_exception from tools import trace, timer, logger_exception
from View.Tools.PamhyrWindow import PamhyrWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.WaitingDialog import WaitingDialog
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QIcon, QPixmap, QKeySequence, QIcon, QPixmap,
@@ -56,12 +65,9 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView, QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate, QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget, QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QSlider, QLabel, QWidget, QGridLayout, QTabBar, QSlider, QLabel, QWidget, QGridLayout, QTabBar, QInputDialog
QInputDialog,
) )
from Model.Results.Results import AdditionalData
from View.Tools.Plot.PamhyrCanvas import MplCanvas from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
@@ -147,14 +153,11 @@ class ResultsWindow(PamhyrWindow):
profile_id=[0]) profile_id=[0])
self.update_table_selection_solver(0) self.update_table_selection_solver(0)
self.update_plot_additional_data()
def setup_table(self): def setup_table(self):
self._table = {} self._table = {}
for t in ["reach", "profile", "raw_data", "solver"]: for t in ["reach", "profile", "raw_data", "solver"]:
table = self.find(QTableView, f"tableView_{t}") table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel( self._table[t] = TableModel(
table_view=table, table_view=table,
table_headers=self._trad.get_dict(f"table_headers_{t}"), table_headers=self._trad.get_dict(f"table_headers_{t}"),
@@ -163,11 +166,8 @@ class ResultsWindow(PamhyrWindow):
opt_data=t, opt_data=t,
parent=self parent=self
) )
self._table[t]._timestamp = self._timestamps[ self._table[t]._timestamp = self._timestamps[
self._slider_time.value() self._slider_time.value()]
]
if len(self._results) <= 1: if len(self._results) <= 1:
table = self.find(QTableView, f"tableView_solver") table = self.find(QTableView, f"tableView_solver")
table.hide() table.hide()
@@ -198,15 +198,12 @@ class ResultsWindow(PamhyrWindow):
def setup_plots(self): def setup_plots(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100) self.canvas = MplCanvas(width=5, height=4, dpi=100)
tab_widget = self.find(QTabWidget, f"tabWidget") tab_widget = self.find(QTabWidget, f"tabWidget")
tab_widget.setTabsClosable(True) tab_widget.setTabsClosable(True)
tab_widget.tabCloseRequested.connect(self.delete_tab) tab_widget.tabCloseRequested.connect(self.delete_tab)
tab_widget.tabBar().setTabButton(0, QTabBar.RightSide, None) tab_widget.tabBar().setTabButton(0, QTabBar.RightSide, None)
tab_widget.tabBar().setTabButton(1, QTabBar.RightSide, None) tab_widget.tabBar().setTabButton(1, QTabBar.RightSide, None)
tab_widget.tabBar().setTabButton(2, QTabBar.RightSide, None) tab_widget.tabBar().setTabButton(2, QTabBar.RightSide, None)
self.canvas.setObjectName("canvas") self.canvas.setObjectName("canvas")
self.toolbar = PamhyrPlotToolbar( self.toolbar = PamhyrPlotToolbar(
self.canvas, self, items=[ self.canvas, self, items=[
@@ -214,7 +211,6 @@ class ResultsWindow(PamhyrWindow):
"iso", "back/forward" "iso", "back/forward"
] ]
) )
self.plot_layout = self.find(QVBoxLayout, "verticalLayout") self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.plot_layout.addWidget(self.toolbar) self.plot_layout.addWidget(self.toolbar)
self.plot_layout.addWidget(self.canvas) self.plot_layout.addWidget(self.canvas)
@@ -362,7 +358,8 @@ class ResultsWindow(PamhyrWindow):
"action_add": self._add_custom_plot, "action_add": self._add_custom_plot,
"action_export": self._export, "action_export": self._export,
# "action_export": self.export_current, # "action_export": self.export_current,
"action_import_data": self.import_data "action_import_data": self.import_data,
"action_play_sound": self.play_sound
} }
if _rasterio_loaded: if _rasterio_loaded:
actions["action_Geo_tiff"] = self.import_geotiff actions["action_Geo_tiff"] = self.import_geotiff
@@ -370,6 +367,12 @@ class ResultsWindow(PamhyrWindow):
self.find(QAction, "action_Geo_tiff")\ self.find(QAction, "action_Geo_tiff")\
.setEnabled(False) .setEnabled(False)
if _audio_loaded:
actions["action_play_sound"] = self.play_sound
else:
self.find(QAction, "action_play_sound")\
.setVisible(False)
if len(self._results) > 1: if len(self._results) > 1:
self.find(QAction, "action_reload").setEnabled(False) self.find(QAction, "action_reload").setEnabled(False)
@@ -972,7 +975,7 @@ class ResultsWindow(PamhyrWindow):
map( map(
lambda p: lambda p:
p.get_ts_key(timestamp, "V") / p.get_ts_key(timestamp, "V") /
sqrt(9.81 * ( np.sqrt(9.81 * (
p.geometry.wet_area( p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) / p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width( p.geometry.wet_width(
@@ -986,7 +989,7 @@ class ResultsWindow(PamhyrWindow):
map( map(
lambda p: lambda p:
p.get_ts_key(timestamp, "V") / p.get_ts_key(timestamp, "V") /
sqrt(9.81 * ( np.sqrt(9.81 * (
p.geometry.wet_area( p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) / p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width( p.geometry.wet_width(
@@ -999,7 +1002,7 @@ class ResultsWindow(PamhyrWindow):
map( map(
lambda p: lambda p:
p.get_ts_key(timestamp, "V") / p.get_ts_key(timestamp, "V") /
sqrt(9.81 * ( np.sqrt(9.81 * (
p.geometry.wet_area( p.geometry.wet_area(
p.get_ts_key(timestamp, "Z")) / p.get_ts_key(timestamp, "Z")) /
p.geometry.wet_width( p.geometry.wet_width(
@@ -1135,7 +1138,7 @@ class ResultsWindow(PamhyrWindow):
if self._current_results != 2: if self._current_results != 2:
fr = my_dict[dict_y["froude"]] = list( fr = my_dict[dict_y["froude"]] = list(
map(lambda z, v: map(lambda z, v:
v / sqrt(9.81 * ( v / np.sqrt(9.81 * (
profile.geometry.wet_area(z) / profile.geometry.wet_area(z) /
profile.geometry.wet_width(z)) profile.geometry.wet_width(z))
), z, v) ), z, v)
@@ -1144,7 +1147,7 @@ class ResultsWindow(PamhyrWindow):
fr1 = list( fr1 = list(
map(lambda z, v: map(lambda z, v:
v / v /
sqrt(9.81 * ( np.sqrt(9.81 * (
profile1.geometry.wet_area(z) / profile1.geometry.wet_area(z) /
profile1.geometry.wet_width(z)) profile1.geometry.wet_width(z))
), z1, v1) ), z1, v1)
@@ -1152,7 +1155,7 @@ class ResultsWindow(PamhyrWindow):
fr2 = list( fr2 = list(
map(lambda z, v: map(lambda z, v:
v / v /
sqrt(9.81 * ( np.sqrt(9.81 * (
profile2.geometry.wet_area(z) / profile2.geometry.wet_area(z) /
profile2.geometry.wet_width(z)) profile2.geometry.wet_width(z))
), z2, v2) ), z2, v2)
@@ -1230,7 +1233,8 @@ class ResultsWindow(PamhyrWindow):
extent=[b[0], b[2], b[1], b[3]]) extent=[b[0], b[2], b[1], b[3]])
else: else:
dlg = CoordinatesDialog( dlg = CoordinatesDialog(
xlim, ylim, xlim,
ylim,
trad=self._trad, trad=self._trad,
parent=self parent=self
) )
@@ -1243,6 +1247,7 @@ class ResultsWindow(PamhyrWindow):
return return
def import_data(self): def import_data(self):
file_types = [ file_types = [
self._trad["file_csv"], self._trad["file_csv"],
self._trad["file_all"], self._trad["file_all"],
@@ -1253,61 +1258,41 @@ class ResultsWindow(PamhyrWindow):
callback=lambda f: self.read_csv_file(f[0]), callback=lambda f: self.read_csv_file(f[0]),
default_suffix=".csv", default_suffix=".csv",
file_filter=file_types, file_filter=file_types,
directory=self._results[self._current_results[0]]._repertory,
) )
def read_csv_file(self, filename): def read_csv_file(self, filename):
if filename == "": if filename == "":
return return
x, y = self.read_csv_file_data(filename) sep = " "
data = self.read_csv_file_format(x, y)
results = self._results[self._current_results[0]] def is_float(string):
data_lst = results.get("additional_data") if string.replace(".", "").isnumeric():
data_lst.append( return True
AdditionalData( else:
study=self._study, return False
data=data
)
)
self.update_plot_additional_data()
def read_csv_file_data(self, filename):
sep = ","
x = []
y = []
with open(filename, 'r', newline='') as f: with open(filename, 'r', newline='') as f:
lines = f.readlines() lines = f.readlines()
x = []
y = []
for line in lines: for line in lines:
if line[0] != "*" and line[0] != "#" and line[0] != "$": if line[0] != "*" and line[0] != "#" and line[0] != "$":
row = line.split(sep) row = line.split(sep)
if len(row) >= 2:
if is_float(row[0]) and is_float(row[1]):
x.append(float(row[0]))
y.append(float(row[1]))
if len(row) < 2:
continue
try:
fx, fy = float(row[0]), float(row[1])
x.append(fx)
y.append(fy)
except Exception as e:
continue
return x, y
def read_csv_file_format(self, x, y):
data_type_lst = ['Q(t)', 'Z(t)', 'Z(x)'] data_type_lst = ['Q(t)', 'Z(t)', 'Z(x)']
data_type, ok = QInputDialog.getItem( data_type, ok = QInputDialog.getItem(
self, 'Data type', self, 'Data type', 'Chose the type of data:', data_type_lst)
'Chose the type of data:',
data_type_lst
)
if not ok: if not ok:
return return
legend, ok = QInputDialog.getText(self, 'Legend', 'Legend:') legend, ok = QInputDialog.getText(self, 'Legend', 'Legend:')
if not ok: if not ok:
return return
@@ -1322,45 +1307,104 @@ class ResultsWindow(PamhyrWindow):
tmp_unit = {'Z': ' (m)', tmp_unit = {'Z': ' (m)',
'Q': ' (m³/s)'} 'Q': ' (m³/s)'}
data = { data = {'type_x': tmp_dict[data_type[2]],
'type_x': tmp_dict[data_type[2]],
'type_y': tmp_dict[data_type[0]], 'type_y': tmp_dict[data_type[0]],
'legend': legend, 'legend': legend,
'unit': tmp_unit[data_type[0]], 'unit': tmp_unit[data_type[0]],
'x': x, 'y': y 'x': x,
} 'y': y}
return data if data_type == 'Z(x)':
line = self.canvas_2.axes.plot(x, y, marker="+",
def update_plot_additional_data(self): label=legend + ' (m)')
results = self._results[self._current_results[0]]
for data in results.get("additional_data"):
data = data._data
x, y = data['x'], data['y']
legend = data['legend']
unit = data['unit']
if (
data['type_x'] == 'water_elevation' and
data['type_y'] == 'time'
):
line = self.canvas_2.axes.plot(
x, y, marker="+",
label=legend + ' ' + unit
)
self.plot_rkc.canvas.draw_idle() self.plot_rkc.canvas.draw_idle()
self.plot_rkc.update_idle() self.plot_rkc.update_idle()
if data_type == 'Q(t)':
if data['type_x'] == 'time' and data['type_y'] == 'discharge': line = self.canvas_4.axes.plot(x, y, marker="+",
line = self.canvas_4.axes.plot( label=legend + ' (m³/s)')
x, y, marker="+",
label=legend + ' ' + unit
)
self.plot_h._line.append(line) self.plot_h._line.append(line)
self.plot_h.enable_legend() self.plot_h.enable_legend()
self.plot_h.canvas.draw_idle() self.plot_h.canvas.draw_idle()
self.plot_h.update_idle() self.plot_h.update_idle
for p in self._additional_plot: for p in self._additional_plot:
self._additional_plot[p].add_imported_plot(data) self._additional_plot[p].add_imported_plot(data)
def play_sound(self):
def get_sine_wave(frequency, duration,
sample_rate=44100, amplitude=4096):
t = np.linspace(0, duration, int(sample_rate*duration))
return amplitude*np.sin(2*np.pi*frequency*t)
if not _audio_loaded:
return
# results
results = self._results[self._current_results[0]]
if results is None:
return
# reach
reach = results.river.reachs[self._get_current_reach()]
profile_id = self._get_current_profile()
profile = reach.profile(profile_id)
t = self._timestamps
q = profile.get_key("Q")
scale = []
low = 30
high = 84
for k in range(low, high):
note = 440*2**((k-49)/12)
scale.append(note) # add musical note
n_notes = len(scale) # number of musical notes
# rescale
# we start at low to high
tmax = max(t)
tmin = min(t)
qmax = max(q)
qmin = min(q)
qfact = (n_notes) / (qmax - qmin + 1)
for i in range(len(q)):
q[i] = int((q[i] - qmin) * qfact)
nq = [q[0]] # (notes)
dq = [0.1] # durée
for i in range(len(t)-1):
if q[i] == q[i+1]:
dq[-1] += 0.1
else:
nq.append(q[i+1])
dq.append(0.1)
wq = []
volume = 2048
for i in range(len(dq)):
new_w = get_sine_wave(frequency=scale[nq[i]],
duration=dq[i],
amplitude=volume)
wq = np.concatenate((wq, new_w))
p = pyaudio.PyAudio()
chunk = 1048
stream = p.open(format=p.get_format_from_width(2),
channels=1,
rate=44100,
output=True)
def fn():
i = 0
while i < len(wq):
stream.write(
wq[i:min(i+chunk, len(wq))].astype(np.int16).tobytes()
)
i += chunk
dlg = WaitingDialog(
payload_fn=fn,
title="playing_sound",
parent=self
)
dlg.exec_()
stream.stop_stream()
stream.close()
p.terminate()
+5
View File
@@ -53,6 +53,11 @@ class ResultsTranslate(MainTranslate):
"Results", "Results",
"Max water elevation" "Max water elevation"
) )
self._dict["file_all"] = _translate("Results", "All files (*)")
self._dict["file_geotiff"] = _translate(
"Results", "GeoTIFF file (*.tiff *.tif)")
self._dict["file_csv"] = _translate(
"Results", "CSV file (*.csv)")
self._dict["ImageCoordinates"] = _translate( self._dict["ImageCoordinates"] = _translate(
"Results", "Image coordinates" "Results", "Image coordinates"
) )
+2 -6
View File
@@ -45,7 +45,6 @@ from PyQt5.QtWidgets import (
from View.RunSolver.Log.Window import SolverLogFileWindow from View.RunSolver.Log.Window import SolverLogFileWindow
from View.Results.ReadingResultsDialog import ReadingResultsDialog from View.Results.ReadingResultsDialog import ReadingResultsDialog
from View.WaitingDialog import WaitingDialog
try: try:
from signal import SIGTERM, SIGSTOP, SIGCONT from signal import SIGTERM, SIGSTOP, SIGCONT
@@ -83,8 +82,7 @@ class SelectSolverWindowAdisTS(PamhyrDialog):
# solvers = self._config.solvers # solvers = self._config.solvers
# solvers mage # solvers mage
solvers = list(filter( solvers = list(filter(
lambda x: "mage" not in x._type and "rubar" not in x._type, lambda x: "mage" not in x._type, self._config.solvers
self._config.solvers
)) ))
solvers_name = list( solvers_name = list(
map( map(
@@ -427,9 +425,7 @@ class SolverLogWindowAdisTS(PamhyrWindow):
self._study, dir_path, qlog=self._output self._study, dir_path, qlog=self._output
) )
dlg = WaitingDialog(payload_fn=reading_fn, dlg = ReadingResultsDialog(reading_fn=reading_fn, parent=self)
title="waiting_result",
parent=self)
dlg.exec_() dlg.exec_()
self._parent.set_results(self._solver, self._results) self._parent.set_results(self._solver, self._results)
-14
View File
@@ -182,7 +182,6 @@ class PamhyrWindow(ASubMainWindow, ListedSubWindow, PamhyrWindowTools):
self._set_title() self._set_title()
self._set_icon() self._set_icon()
self._setup_save_sc()
def _set_title(self): def _set_title(self):
title = self._title title = self._title
@@ -197,25 +196,12 @@ class PamhyrWindow(ASubMainWindow, ListedSubWindow, PamhyrWindowTools):
self.ui.setWindowTitle(title) self.ui.setWindowTitle(title)
def _setup_save_sc(self):
if self._parent is None:
return
self._save_sc = QShortcut(QKeySequence("Ctrl+S"), self)
self._save_sc.activated.connect(lambda: self._save(self))
def closeEvent(self, event): def closeEvent(self, event):
self._close_sub_window() self._close_sub_window()
self._propagate_update(Modules.WINDOW_LIST) self._propagate_update(Modules.WINDOW_LIST)
super(PamhyrWindow, self).closeEvent(event) super(PamhyrWindow, self).closeEvent(event)
def _save(self, source):
if self._parent is None:
return
return self._parent._save(source)
class PamhyrDialog(ASubWindow, ListedSubWindow, PamhyrWindowTools): class PamhyrDialog(ASubWindow, ListedSubWindow, PamhyrWindowTools):
_pamhyr_ui = "dummy" _pamhyr_ui = "dummy"
+2 -36
View File
@@ -54,18 +54,6 @@ class CommonWordTranslate(PamhyrTranslate):
self._dict["method"] = _translate("CommonWord", "Method") self._dict["method"] = _translate("CommonWord", "Method")
# Files
self._dict["open_file"] = _translate(
"CommonWord", "Open file"
)
self._dict["file_all"] = _translate("CommonWord", "All files (*)")
self._dict["file_geotiff"] = _translate(
"CommonWord", "GeoTIFF file (*.tiff *.tif)"
)
self._dict["file_csv"] = _translate(
"CommonWord", "CSV file (*.csv)"
)
class UnitTranslate(CommonWordTranslate): class UnitTranslate(CommonWordTranslate):
def __init__(self): def __init__(self):
@@ -192,9 +180,6 @@ class MainTranslate(UnitTranslate):
) )
# Message box # Message box
self._dict["Error"] = _translate(
"MainWindow", "Error"
)
self._dict["Warning"] = _translate( self._dict["Warning"] = _translate(
"MainWindow", "Warning" "MainWindow", "Warning"
) )
@@ -249,31 +234,13 @@ class MainTranslate(UnitTranslate):
self._dict["mb_diff_results_param_msg"] = _translate( self._dict["mb_diff_results_param_msg"] = _translate(
"MainWindow", "Results comparison parameters is invalid" "MainWindow", "Results comparison parameters is invalid"
) )
self._dict["mb_open_results_title"] = _translate(
"MainWindow", "Open results"
)
self._dict["mb_open_results_no_results_msg"] = _translate(
"MainWindow", "No results found"
)
self._dict["mb_open_results_invalid_results_msg"] = _translate(
"MainWindow", "Failed to read results"
)
self._dict["mb_diff_results_compatibility_msg"] = _translate( self._dict["mb_diff_results_compatibility_msg"] = _translate(
"MainWindow", "MainWindow",
"Results comparison with two " "Results comparison with two "
"incompatible study version" "incompatible study version"
) )
self._dict["playing_sound"] = _translate(
"MainWindow", "Playing sound..."
self._dict["mb_unsaved_study_title"] = _translate(
"MainWindow", "Open scenarios window"
)
self._dict["mb_unsaved_studyh_msg"] = _translate(
"MainWindow",
"You must save your study to open "
"the scenario windows"
) )
self._dict["x"] = _translate("MainWindow", "X (m)") self._dict["x"] = _translate("MainWindow", "X (m)")
@@ -283,4 +250,3 @@ class MainTranslate(UnitTranslate):
self._dict["Cancel"] = _translate("MainWindow", "Cancel") self._dict["Cancel"] = _translate("MainWindow", "Cancel")
self._dict["Save"] = _translate("MainWindow", "Save") self._dict["Save"] = _translate("MainWindow", "Save")
self._dict["Close"] = _translate("MainWindow", "Close") self._dict["Close"] = _translate("MainWindow", "Close")
self._dict["Solver"] = _translate("MainWindow", "Solver")
-264
View File
@@ -1,264 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>896</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_ok">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Informations</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Enabled</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_description">
<property name="toolTip">
<string>The relative file path on executable directory</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_name"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>GeoTIFF file</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_import">
<property name="text">
<string>Import GeoTIFF file</string>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout_geotiff"/>
</widget>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="2">
<widget class="QPushButton" name="pushButton_left">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_right">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Right coordinate</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pushButton_right">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_left">
<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="2">
<widget class="QPushButton" name="pushButton_bottom">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButton_top">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_right">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_top">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_top">
<property name="text">
<string>Top coordinate</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_bottom">
<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_left">
<property name="text">
<string>Left coordinate</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_bottom">
<property name="text">
<string>Bottom coordinate</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
-84
View File
@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>896</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QListView" name="listView"/>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="action_add"/>
<addaction name="action_delete"/>
<addaction name="action_edit"/>
</widget>
<action name="action_add">
<property name="icon">
<iconset>
<normaloff>ressources/add.png</normaloff>ressources/add.png</iconset>
</property>
<property name="text">
<string>Add</string>
</property>
<property name="toolTip">
<string>Add a new file</string>
</property>
</action>
<action name="action_delete">
<property name="icon">
<iconset>
<normaloff>ressources/del.png</normaloff>ressources/del.png</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete selected file(s)</string>
</property>
</action>
<action name="action_edit">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit</string>
</property>
<property name="toolTip">
<string>Edit file</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
-6
View File
@@ -129,7 +129,6 @@
<string>&amp;Geometry</string> <string>&amp;Geometry</string>
</property> </property>
<addaction name="action_menu_edit_geometry"/> <addaction name="action_menu_edit_geometry"/>
<addaction name="action_menu_edit_geotiff"/>
</widget> </widget>
<widget class="QMenu" name="menu_run"> <widget class="QMenu" name="menu_run">
<property name="locale"> <property name="locale">
@@ -812,11 +811,6 @@
<string>Compare results</string> <string>Compare results</string>
</property> </property>
</action> </action>
<action name="action_menu_edit_geotiff">
<property name="text">
<string>GeoTIFF</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections> <connections>
+13
View File
@@ -240,6 +240,7 @@
<addaction name="action_reload"/> <addaction name="action_reload"/>
<addaction name="action_Geo_tiff"/> <addaction name="action_Geo_tiff"/>
<addaction name="action_import_data"/> <addaction name="action_import_data"/>
<addaction name="action_play_sound"/>
</widget> </widget>
<action name="action_add"> <action name="action_add">
<property name="icon"> <property name="icon">
@@ -301,6 +302,18 @@
<string>Import data from SCV file</string> <string>Import data from SCV file</string>
</property> </property>
</action> </action>
<action name="action_play_sound">
<property name="icon">
<iconset>
<normaloff>ressources/multimedia-volume-control-symbolic.symbolic.png</normaloff>ressources/multimedia-volume-control-symbolic.symbolic.png</iconset>
</property>
<property name="text">
<string>play_sound</string>
</property>
<property name="toolTip">
<string>Play discharge as sound</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

+1 -31
View File
@@ -37,7 +37,7 @@ logger = logging.getLogger()
class Config(SQL): class Config(SQL):
def __init__(self): def __init__(self):
self._version = '0.0.7' self._version = '0.0.6'
self.filename = Config.filename() self.filename = Config.filename()
self.set_default_value() self.set_default_value()
@@ -165,29 +165,6 @@ class Config(SQL):
"WHERE name='default-AdisTS'" "WHERE name='default-AdisTS'"
) )
if int(release) < 7:
posix = os.name == 'posix'
ext = "" if posix else ".exe"
self.execute(f"""
INSERT INTO solver VALUES (
'rubar3',
'default-Rubar3',
'Default Pamhyr2 Rubar 3 version',
'', '', '',
'',
'@install_dir/rubar/rubar3{ext} @args @input',
''
)
""")
self.execute(
"UPDATE solver SET cmd_solver=" +
f"'@install_dir/rubar/rubar3{ext} @args @input' "
"WHERE name='default-Rubar3'"
)
self.execute( self.execute(
f"UPDATE info SET value='{self._version}' " + f"UPDATE info SET value='{self._version}' " +
"WHERE key='version'" "WHERE key='version'"
@@ -360,13 +337,6 @@ class Config(SQL):
new._cmd_solver = f""""@install_dir/adists/adists{ext}" @args @input""" new._cmd_solver = f""""@install_dir/adists/adists{ext}" @args @input"""
self._solvers.append(new) self._solvers.append(new)
# Rubar3
ctor = solver_type_list["rubar3"]
new = ctor("default-Rubar3")
new._description = "Default Pamhyr2 Rubar 3 version"
new._cmd_solver = f""""@install_dir/rubar/rubar3{ext}" @args @input"""
self._solvers.append(new)
# Mage fake 7 # Mage fake 7
# ctor = solver_type_list["mage_fake7"] # ctor = solver_type_list["mage_fake7"]
# new = ctor("default-mage-fake-7") # new = ctor("default-mage-fake-7")
+236 -331
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.