mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
147
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8913a774ea | ||
|
|
36c3785cde | ||
|
|
f75a25d010 | ||
|
|
5ed905df71 | ||
|
|
ea4fa81cc4 | ||
|
|
8862be991e | ||
|
|
8aea55909e | ||
|
|
1289967abb | ||
|
|
41d1fb9d59 | ||
|
|
8d9a196059 | ||
|
|
9db90debeb | ||
|
|
2333ab540e | ||
|
|
c81d65f527 | ||
|
|
bf44f5c027 | ||
|
|
d1400f12a6 | ||
|
|
c610717220 | ||
|
|
5edcf15072 | ||
|
|
e4bffce6f4 | ||
|
|
54fe032791 | ||
|
|
c668dddcfd | ||
|
|
bdc975f280 | ||
|
|
4315fb94f3 | ||
|
|
eb1163f712 | ||
|
|
42f3c02c3e | ||
|
|
31ebcf07e0 | ||
|
|
95612e8ce9 | ||
|
|
e56d88937a | ||
|
|
8c697a9621 | ||
|
|
2d3747b3d3 | ||
|
|
b38e252746 | ||
|
|
f1d6eb8990 | ||
|
|
c2c77304d7 | ||
|
|
0b051ad3c6 | ||
|
|
b72ffe03c3 | ||
|
|
01de0cfe93 | ||
|
|
d6f152958f | ||
|
|
b85962bfcd | ||
|
|
a1ae3f42f4 | ||
|
|
21876586df | ||
|
|
a317cd849f | ||
|
|
3b8a49d429 | ||
|
|
872235e94f | ||
|
|
da7ef60bef | ||
|
|
824669f70f | ||
|
|
558085705f | ||
|
|
19a3f3d310 | ||
|
|
49aab685fb | ||
|
|
4383599d30 | ||
|
|
ae6bca3033 | ||
|
|
1266884f93 | ||
|
|
f91527464d | ||
|
|
97f8a3ccde | ||
|
|
cef67540c2 | ||
|
|
ff38571933 | ||
|
|
4f69553bd3 | ||
|
|
55da743977 | ||
|
|
e711f0fb6f | ||
|
|
36fd8cdc8a | ||
|
|
4a24e39224 | ||
|
|
97c161fc3f | ||
|
|
00c43d91cd | ||
|
|
100cfd095e | ||
|
|
5047dbc85e | ||
|
|
c7eb10884a | ||
|
|
0ec1a39328 | ||
|
|
92c9b106e3 | ||
|
|
91e780432d | ||
|
|
81f15208cf | ||
|
|
c9c5bfe093 | ||
|
|
0483a2463b | ||
|
|
9364b99158 | ||
|
|
fa2cc303cd | ||
|
|
2b85f221bb | ||
|
|
55d96a3767 | ||
|
|
6000a91e74 | ||
|
|
bd6e200a70 | ||
|
|
143f468c26 | ||
|
|
334633f0fb | ||
|
|
a52e6b86f4 | ||
|
|
9ae16ca6ec | ||
|
|
c7406d3bce | ||
|
|
cb8ecc752b | ||
|
|
4c522ae050 | ||
|
|
689d5a7592 | ||
|
|
c118d4fae8 | ||
|
|
1baf2e8dd5 | ||
|
|
1fcdaa8b81 | ||
|
|
71729578dd | ||
|
|
abe7bf969a | ||
|
|
673ae3908c | ||
|
|
1a31c9122e | ||
|
|
c53d7d9c25 | ||
|
|
02f14ffdf1 | ||
|
|
84fd841f86 | ||
|
|
629b856788 | ||
|
|
f3007e2fe2 | ||
|
|
a7a1a4ddd1 | ||
|
|
0719db17ed | ||
|
|
018192714c | ||
|
|
e1f69e41b0 | ||
|
|
501000fa6b | ||
|
|
75149000db | ||
|
|
3c82148f44 | ||
|
|
7f2ff9d341 | ||
|
|
57be80bcd4 | ||
|
|
29b5c97345 | ||
|
|
b171eb7d32 | ||
|
|
0e18d43496 | ||
|
|
bd4222adfa | ||
|
|
1e4761609d | ||
|
|
de72e2e96b | ||
|
|
95a0dabe68 | ||
|
|
9f53ad685e | ||
|
|
02c597d109 | ||
|
|
510bee115b | ||
|
|
ed250f836d | ||
|
|
a47410a0f1 | ||
|
|
704130966e | ||
|
|
b927aa81b3 | ||
|
|
d154ac96c2 | ||
|
|
6067ba601e | ||
|
|
716d0af96f | ||
|
|
f917dd9284 | ||
|
|
cd68bebf03 | ||
|
|
35cc348f03 | ||
|
|
6f3e1e31f0 | ||
|
|
7df0750c58 | ||
|
|
bcf825a4f7 | ||
|
|
3afd9f5025 | ||
|
|
3cb47ee5c8 | ||
|
|
1535247e1b | ||
|
|
37dd4ebaef | ||
|
|
1e8949067b | ||
|
|
1945e94190 | ||
|
|
1ba971af24 | ||
|
|
0b5357d12a | ||
|
|
3be361b03c | ||
|
|
7681b5d326 | ||
|
|
0a35d387f7 | ||
|
|
b40efc2426 | ||
|
|
59aae7fd40 | ||
|
|
a633e4bccf | ||
|
|
b56b8099ab | ||
|
|
f9fe5c9b6c | ||
|
|
13b954d580 | ||
|
|
f53289ef1b | ||
|
|
1b3ce91582 |
No files matched your search
@@ -9,6 +9,8 @@
|
|||||||
*.class
|
*.class
|
||||||
|
|
||||||
tmp
|
tmp
|
||||||
|
mage8
|
||||||
|
adists
|
||||||
|
|
||||||
*.tar
|
*.tar
|
||||||
*.tar.*
|
*.tar.*
|
||||||
|
|||||||
@@ -83,6 +83,34 @@ dl-mage8-windows:
|
|||||||
- mage8-windows/mailleurTT.exe
|
- mage8-windows/mailleurTT.exe
|
||||||
- mage8-windows/libbief.dll
|
- mage8-windows/libbief.dll
|
||||||
|
|
||||||
|
dl-adists-linux:
|
||||||
|
stage: downloads
|
||||||
|
tags:
|
||||||
|
- linux
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
|
script:
|
||||||
|
- mkdir -p adists-linux
|
||||||
|
- cd adists-linux
|
||||||
|
- curl -L -o adists https://gitlab.irstea.fr/jean-baptiste.faure/adists/-/releases/v1.0.1/downloads/executables/adists
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- adists-linux/adists
|
||||||
|
|
||||||
|
dl-adists-windows:
|
||||||
|
stage: downloads
|
||||||
|
tags:
|
||||||
|
- linux
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
|
script:
|
||||||
|
- mkdir -p adists-windows
|
||||||
|
- cd adists-windows
|
||||||
|
- curl -L -o adists.exe https://gitlab.irstea.fr/jean-baptiste.faure/adists/-/releases/v1.0.1/downloads/executables/adists_win64.exe.exe
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- adists-windows/adists.exe
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# CONFIGURE #
|
# CONFIGURE #
|
||||||
#############
|
#############
|
||||||
@@ -224,6 +252,8 @@ build-linux:
|
|||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
needs:
|
needs:
|
||||||
|
- job: dl-adists-linux
|
||||||
|
artifacts: true
|
||||||
- job: dl-mage8-linux
|
- job: dl-mage8-linux
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: dl-mage8-doc
|
- job: dl-mage8-doc
|
||||||
@@ -260,6 +290,9 @@ build-linux:
|
|||||||
# Copy mage
|
# Copy mage
|
||||||
- mkdir -p pamhyr/mage8
|
- mkdir -p pamhyr/mage8
|
||||||
- cp -v ../mage8-linux/* pamhyr/mage8/
|
- cp -v ../mage8-linux/* pamhyr/mage8/
|
||||||
|
# Copy adists
|
||||||
|
- mkdir -p pamhyr/adists
|
||||||
|
- cp -v ../adists-linux/* pamhyr/adists/
|
||||||
# Copy Pamhyr
|
# Copy Pamhyr
|
||||||
- cp -r dist/pamhyr/* pamhyr/
|
- cp -r dist/pamhyr/* pamhyr/
|
||||||
# Pamhyr script to force x11
|
# Pamhyr script to force x11
|
||||||
@@ -317,6 +350,8 @@ build-windows:
|
|||||||
needs:
|
needs:
|
||||||
- job: dl-mage8-windows
|
- job: dl-mage8-windows
|
||||||
artifacts: true
|
artifacts: true
|
||||||
|
- job: dl-adists-windows
|
||||||
|
artifacts: true
|
||||||
- job: dl-mage8-doc
|
- job: dl-mage8-doc
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: set-version
|
- job: set-version
|
||||||
|
|||||||
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.10 (pamhyr)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (pamhyr)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/pamhyr.iml" filepath="$PROJECT_DIR$/.idea/pamhyr.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10 (pamhyr)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PyDocumentationSettings">
|
||||||
|
<option name="format" value="PLAIN" />
|
||||||
|
<option name="myDocStringFormat" value="Plain" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -46,6 +46,10 @@ 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 adists
|
||||||
|
mkdir pamhyr\adists
|
||||||
|
copy /y ..\adists-windows\adists.exe pamhyr\adists\
|
||||||
|
|
||||||
rem Copy tests_cases
|
rem Copy tests_cases
|
||||||
mkdir pamhyr\tests_cases
|
mkdir pamhyr\tests_cases
|
||||||
mkdir pamhyr\tests_cases\Saar
|
mkdir pamhyr\tests_cases\Saar
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -0,0 +1,52 @@
|
|||||||
|
# Adists.py -- Pamhyr study checkers
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from Modules import Modules
|
||||||
|
from Checker.Checker import AbstractModelChecker, STATUS
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class AdistsOutputRKChecker(AbstractModelChecker):
|
||||||
|
def __init__(self):
|
||||||
|
super(AdistsOutputRKChecker, self).__init__()
|
||||||
|
|
||||||
|
self._name = _translate("Checker", "AdisTS output RK checker")
|
||||||
|
self._description = _translate(
|
||||||
|
"Checker", "Check output RK"
|
||||||
|
)
|
||||||
|
self._modules = Modules.OUTPUT_RK
|
||||||
|
|
||||||
|
def run(self, study):
|
||||||
|
ok = True
|
||||||
|
nerror = 0
|
||||||
|
self._summary = "ok"
|
||||||
|
self._status = STATUS.OK
|
||||||
|
|
||||||
|
if not self.basic_check(study):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return ok
|
||||||
+2
-2
@@ -395,7 +395,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
logger.info(
|
logger.info(
|
||||||
f"! {self._exe_path()} " +
|
f"! {self._exe_path()} " +
|
||||||
f"{st_file} {m_file} " +
|
f"{st_file} {m_file} " +
|
||||||
f"update_kp " +
|
f"update_rk " +
|
||||||
f"{str(step)} " +
|
f"{str(step)} " +
|
||||||
f"{limites[0]} {limites[1]} " +
|
f"{limites[0]} {limites[1]} " +
|
||||||
f"{directrices[0]} {directrices[1]} " +
|
f"{directrices[0]} {directrices[1]} " +
|
||||||
@@ -410,7 +410,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
str,
|
str,
|
||||||
[
|
[
|
||||||
st_file, m_file,
|
st_file, m_file,
|
||||||
"update_kp", step,
|
"update_rk", step,
|
||||||
limites[0], limites[1],
|
limites[0], limites[1],
|
||||||
directrices[0], directrices[1],
|
directrices[0], directrices[1],
|
||||||
orientation, lm, linear, origin, origin_value
|
orientation, lm, linear, origin, origin_value
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
|
|
||||||
bc.node = None
|
bc.node = None
|
||||||
if row[3] != -1:
|
if row[3] != -1:
|
||||||
bc.node = next(filter(lambda n: n.id == row[3], data["nodes"]))
|
bc.node = next(filter(lambda n: n.id == row[3], data["nodes"]),
|
||||||
|
None)
|
||||||
|
|
||||||
values = execute(
|
values = execute(
|
||||||
"SELECT ind, data0, data1 FROM boundary_condition_data " +
|
"SELECT ind, data0, data1 FROM boundary_condition_data " +
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# BoundaryConditionsAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer,
|
||||||
|
old_pamhyr_date_to_timestamp,
|
||||||
|
date_iso_to_timestamp,
|
||||||
|
date_dmy_to_timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class BoundaryConditionAdisTS(SQLSubModel):
|
||||||
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1,
|
||||||
|
pollutant: int = -1, status=None):
|
||||||
|
super(BoundaryConditionAdisTS, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = BoundaryConditionAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._type = ""
|
||||||
|
self._node = None
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._data = []
|
||||||
|
self._header = []
|
||||||
|
self._types = [self.time_convert, float]
|
||||||
|
|
||||||
|
BoundaryConditionAdisTS._id_cnt = max(
|
||||||
|
BoundaryConditionAdisTS._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE boundary_condition_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
pollutant INTEGER NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
node INTEGER,
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id),
|
||||||
|
FOREIGN KEY(node) REFERENCES river_node(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE boundary_condition_data_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
data0 TEXT NOT NULL,
|
||||||
|
data1 TEXT NOT NULL,
|
||||||
|
bc INTEGER,
|
||||||
|
FOREIGN KEY(bc) REFERENCES boundary_condition_adists(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, pollutant, type, node " +
|
||||||
|
"FROM boundary_condition_adists"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
bc = cls(
|
||||||
|
id=row[0],
|
||||||
|
pollutant=row[1],
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
bc.type = row[2]
|
||||||
|
|
||||||
|
bc.node = None
|
||||||
|
if row[3] != -1:
|
||||||
|
tmp = next(filter(
|
||||||
|
lambda n: n.id == row[3], data["nodes"]),
|
||||||
|
None)
|
||||||
|
if tmp is not None:
|
||||||
|
bc.node = tmp.id
|
||||||
|
else:
|
||||||
|
bc.node = -1
|
||||||
|
|
||||||
|
values = execute(
|
||||||
|
"SELECT data0, data1 FROM " +
|
||||||
|
"boundary_condition_data_adists " +
|
||||||
|
f"WHERE bc = '{bc.id}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write data
|
||||||
|
for v in values:
|
||||||
|
data0 = bc._types[0](v[0])
|
||||||
|
data1 = bc._types[1](v[1])
|
||||||
|
# Replace data at pos ind
|
||||||
|
bc._data.append((data0, data1))
|
||||||
|
|
||||||
|
new.append(bc)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
|
||||||
|
execute(f"DELETE FROM boundary_condition_adists WHERE id = {self.id}")
|
||||||
|
execute(f"DELETE FROM boundary_condition_data_adists" +
|
||||||
|
f" WHERE bc = {self.id}")
|
||||||
|
|
||||||
|
node = -1
|
||||||
|
if self._node is not None:
|
||||||
|
node = self._node
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"boundary_condition_adists(id, pollutant, type, node) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, {self._pollutant}, " +
|
||||||
|
f"'{self._db_format(self._type)}', {node}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
for d in self._data:
|
||||||
|
data0 = self._db_format(str(d[0]))
|
||||||
|
data1 = self._db_format(str(d[1]))
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"boundary_condition_data_adists(data0, data1, bc) " +
|
||||||
|
f"VALUES ('{data0}', {data1}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def time_convert(cls, data):
|
||||||
|
if type(data) is str:
|
||||||
|
if data.count("-") == 2:
|
||||||
|
return date_iso_to_timestamp(data)
|
||||||
|
if data.count("/") == 2:
|
||||||
|
return date_dmy_to_timestamp(data)
|
||||||
|
if data.count(":") == 3:
|
||||||
|
return old_pamhyr_date_to_timestamp(data)
|
||||||
|
if data.count(":") == 2:
|
||||||
|
return old_pamhyr_date_to_timestamp("00:" + data)
|
||||||
|
if data.count(".") == 1:
|
||||||
|
return round(float(data))
|
||||||
|
|
||||||
|
return int(data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def node(self):
|
||||||
|
return self._node
|
||||||
|
|
||||||
|
@node.setter
|
||||||
|
def node(self, node):
|
||||||
|
self._node = node
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def header(self):
|
||||||
|
return self._header.copy()
|
||||||
|
|
||||||
|
@header.setter
|
||||||
|
def header(self, header):
|
||||||
|
self._header = header
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pollutant(self):
|
||||||
|
return self._pollutant
|
||||||
|
|
||||||
|
@pollutant.setter
|
||||||
|
def pollutant(self, pollutant):
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@type.setter
|
||||||
|
def type(self, type):
|
||||||
|
self._type = type
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
return self._data.copy()
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# BoundaryConditionsAdisTSList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
||||||
|
import BoundaryConditionAdisTS
|
||||||
|
|
||||||
|
|
||||||
|
class BoundaryConditionsAdisTSList(PamhyrModelList):
|
||||||
|
_sub_classes = [
|
||||||
|
BoundaryConditionAdisTS,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
new._lst = BoundaryConditionAdisTS._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute("DELETE FROM boundary_condition_adists")
|
||||||
|
execute("DELETE FROM boundary_condition_data_adists")
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for bc in self._lst:
|
||||||
|
bc._db_save(execute, data=data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def new(self, index, pollutant):
|
||||||
|
n = BoundaryConditionAdisTS(pollutant=pollutant, status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
@property
|
||||||
|
def BCs_AdisTS_List(self):
|
||||||
|
return self.lst
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# D90AdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.D90AdisTS.D90AdisTSSpec import D90AdisTSSpec
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class D90AdisTS(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
D90AdisTSSpec,
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "default",
|
||||||
|
status=None):
|
||||||
|
super(D90AdisTS, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = D90AdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._d90 = None
|
||||||
|
self._enabled = True
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
D90AdisTS._id_cnt = max(
|
||||||
|
D90AdisTS._id_cnt + 1,
|
||||||
|
self.id
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE d90_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
d90 REAL NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == 0 and minor < 1:
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, name, d90, enabled " +
|
||||||
|
"FROM d90_adists"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
d90_id = row[0]
|
||||||
|
name = row[1]
|
||||||
|
d90 = row[2]
|
||||||
|
enabled = (row[3] == 1)
|
||||||
|
|
||||||
|
D90 = cls(
|
||||||
|
id=d90_id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
D90.d90 = d90
|
||||||
|
D90.enabled = enabled
|
||||||
|
|
||||||
|
data['d90_default_id'] = d90_id
|
||||||
|
D90._data = D90AdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
|
new.append(D90)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute(f"DELETE FROM d90_adists WHERE id = {self.id}")
|
||||||
|
|
||||||
|
d90 = -1.
|
||||||
|
if self.d90 is not None:
|
||||||
|
d90 = self.d90
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"d90_adists(" +
|
||||||
|
"id, name, d90, enabled" +
|
||||||
|
") " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
|
f"{d90}, {self._enabled}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
data['d90_default_id'] = self.id
|
||||||
|
execute(
|
||||||
|
"DELETE FROM d90_spec " +
|
||||||
|
f"WHERE d90_default = {self.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for d90_spec in self._data:
|
||||||
|
d90_spec._db_save(execute, data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def d90(self):
|
||||||
|
return self._d90
|
||||||
|
|
||||||
|
@d90.setter
|
||||||
|
def d90(self, d90):
|
||||||
|
self._d90 = d90
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def new(self, index):
|
||||||
|
n = D90AdisTSSpec(status=self._status)
|
||||||
|
self._data.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
def delete(self, data):
|
||||||
|
self._data = list(
|
||||||
|
filter(
|
||||||
|
lambda x: x not in data,
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def delete_i(self, indexes):
|
||||||
|
for ind in indexes:
|
||||||
|
del self._data[ind]
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def insert(self, index, data):
|
||||||
|
self._data.insert(index, data)
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# D90AdisTSList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.D90AdisTS.D90AdisTS import D90AdisTS
|
||||||
|
|
||||||
|
|
||||||
|
class D90AdisTSList(PamhyrModelList):
|
||||||
|
_sub_classes = [
|
||||||
|
D90AdisTS,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
new._lst = D90AdisTS._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute("DELETE FROM d90_adists")
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for d90 in self._lst:
|
||||||
|
d90._db_save(execute, data=data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def new(self, index):
|
||||||
|
n = D90AdisTS(status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
@property
|
||||||
|
def D90_AdisTS_List(self):
|
||||||
|
return self.lst
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# D90AdisTSSpec.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class D90AdisTSSpec(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(D90AdisTSSpec, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = D90AdisTSSpec._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name_section = name
|
||||||
|
self._reach = None
|
||||||
|
self._start_rk = None
|
||||||
|
self._end_rk = None
|
||||||
|
self._d90 = None
|
||||||
|
self._enabled = True
|
||||||
|
|
||||||
|
D90AdisTSSpec._id_cnt = max(D90AdisTSSpec._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE d90_spec(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
d90_default INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
reach INTEGER NOT NULL,
|
||||||
|
start_rk REAL NOT NULL,
|
||||||
|
end_rk REAL NOT NULL,
|
||||||
|
d90 REAL NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL,
|
||||||
|
FOREIGN KEY(d90_default) REFERENCES d90_adists(id),
|
||||||
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == 0 and minor < 1:
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, d90_default, name, reach, start_rk, end_rk, " +
|
||||||
|
"d90, enabled " +
|
||||||
|
"FROM d90_spec " +
|
||||||
|
f"WHERE d90_default = {data['d90_default_id']} "
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
id = row[0]
|
||||||
|
name = row[2]
|
||||||
|
reach = row[3]
|
||||||
|
start_rk = row[4]
|
||||||
|
end_rk = row[5]
|
||||||
|
d90 = row[6]
|
||||||
|
enabled = (row[7] == 1)
|
||||||
|
|
||||||
|
new_spec = cls(
|
||||||
|
id=id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
new_spec.reach = reach
|
||||||
|
new_spec.start_rk = start_rk
|
||||||
|
new_spec.end_rk = end_rk
|
||||||
|
new_spec.d90 = d90
|
||||||
|
new_spec.enabled = enabled
|
||||||
|
|
||||||
|
new.append(new_spec)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
d90_default = data['d90_default_id']
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"d90_spec(id, d90_default, name, reach, " +
|
||||||
|
"start_rk, end_rk, d90, enabled) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, " +
|
||||||
|
f"{d90_default}, " +
|
||||||
|
f"'{self._db_format(self._name_section)}', " +
|
||||||
|
f"{self._reach}, " +
|
||||||
|
f"{self._start_rk}, " +
|
||||||
|
f"{self._end_rk}, " +
|
||||||
|
f"{self._d90}, " +
|
||||||
|
f"{self._enabled}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name_section
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name_section = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reach(self):
|
||||||
|
return self._reach
|
||||||
|
|
||||||
|
@reach.setter
|
||||||
|
def reach(self, reach):
|
||||||
|
self._reach = reach
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_rk(self):
|
||||||
|
return self._start_rk
|
||||||
|
|
||||||
|
@start_rk.setter
|
||||||
|
def start_rk(self, start_rk):
|
||||||
|
self._start_rk = start_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_rk(self):
|
||||||
|
return self._end_rk
|
||||||
|
|
||||||
|
@end_rk.setter
|
||||||
|
def end_rk(self, end_rk):
|
||||||
|
self._end_rk = end_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def d90(self):
|
||||||
|
return self._d90
|
||||||
|
|
||||||
|
@d90.setter
|
||||||
|
def d90(self, d90):
|
||||||
|
self._d90 = d90
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
# DIFAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.DIFAdisTS.DIFAdisTSSpec import DIFAdisTSSpec
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class DIFAdisTS(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
DIFAdisTSSpec,
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "default",
|
||||||
|
status=None):
|
||||||
|
super(DIFAdisTS, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = DIFAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._method = None
|
||||||
|
self._dif = None
|
||||||
|
self._b = None
|
||||||
|
self._c = None
|
||||||
|
self._enabled = True
|
||||||
|
self._types = ["iwasa", "fisher", "elder", "constante", "generique"]
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
DIFAdisTS._id_cnt = max(
|
||||||
|
DIFAdisTS._id_cnt + 1,
|
||||||
|
self.id
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE dif_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
method TEXT NOT NULL,
|
||||||
|
dif REAL NOT NULL,
|
||||||
|
b REAL,
|
||||||
|
c REAL,
|
||||||
|
enabled BOOLEAN NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == 0 and minor < 1:
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, name, method, dif, b, c, enabled " +
|
||||||
|
"FROM dif_adists"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
dif_id = row[0]
|
||||||
|
name = row[1]
|
||||||
|
method = row[2]
|
||||||
|
dif = row[3]
|
||||||
|
b = row[4]
|
||||||
|
c = row[5]
|
||||||
|
enabled = (row[6] == 1)
|
||||||
|
|
||||||
|
DIF = cls(
|
||||||
|
id=dif_id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
DIF.method = method
|
||||||
|
DIF.dif = dif
|
||||||
|
DIF.b = b
|
||||||
|
DIF.c = c
|
||||||
|
DIF.enabled = enabled
|
||||||
|
|
||||||
|
data['dif_default_id'] = dif_id
|
||||||
|
DIF._data = DIFAdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
|
new.append(DIF)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute(f"DELETE FROM dif_adists WHERE id = {self.id}")
|
||||||
|
|
||||||
|
method = ""
|
||||||
|
if self.method is not None:
|
||||||
|
method = self.method
|
||||||
|
|
||||||
|
dif = -1.
|
||||||
|
if self.dif is not None:
|
||||||
|
dif = self.dif
|
||||||
|
|
||||||
|
b = -1.
|
||||||
|
if self.b is not None:
|
||||||
|
b = self.b
|
||||||
|
|
||||||
|
c = -1.
|
||||||
|
if self.dif is not None:
|
||||||
|
c = self.c
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"dif_adists(" +
|
||||||
|
"id, name, method, dif, b, c, enabled" +
|
||||||
|
") " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
|
f"'{self._db_format(self._method)}', " +
|
||||||
|
f"{dif}, {b}, {c}, {self._enabled}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
data['dif_default_id'] = self.id
|
||||||
|
execute(
|
||||||
|
"DELETE FROM dif_spec " +
|
||||||
|
f"WHERE dif_default = {self.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for dif_spec in self._data:
|
||||||
|
dif_spec._db_save(execute, data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def method(self):
|
||||||
|
return self._method
|
||||||
|
|
||||||
|
@method.setter
|
||||||
|
def method(self, method):
|
||||||
|
self._method = method
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def types(self):
|
||||||
|
return self._types
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dif(self):
|
||||||
|
return self._dif
|
||||||
|
|
||||||
|
@dif.setter
|
||||||
|
def dif(self, dif):
|
||||||
|
self._dif = dif
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def b(self):
|
||||||
|
return self._b
|
||||||
|
|
||||||
|
@b.setter
|
||||||
|
def b(self, b):
|
||||||
|
self._b = b
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def c(self):
|
||||||
|
return self._c
|
||||||
|
|
||||||
|
@c.setter
|
||||||
|
def c(self, c):
|
||||||
|
self._c = c
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def new(self, index):
|
||||||
|
n = DIFAdisTSSpec(status=self._status)
|
||||||
|
self._data.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
def delete(self, data):
|
||||||
|
self._data = list(
|
||||||
|
filter(
|
||||||
|
lambda x: x not in data,
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def delete_i(self, indexes):
|
||||||
|
for ind in indexes:
|
||||||
|
del self._data[ind]
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def insert(self, index, data):
|
||||||
|
self._data.insert(index, data)
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# DIFAdisTSList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
|
||||||
|
|
||||||
|
|
||||||
|
class DIFAdisTSList(PamhyrModelList):
|
||||||
|
_sub_classes = [
|
||||||
|
DIFAdisTS,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
new._lst = DIFAdisTS._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute("DELETE FROM dif_adists")
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for dif in self._lst:
|
||||||
|
dif._db_save(execute, data=data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def new(self, index):
|
||||||
|
n = DIFAdisTS(status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
@property
|
||||||
|
def DIF_AdisTS_List(self):
|
||||||
|
return self.lst
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
# DIFAdisTSSpec.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class DIFAdisTSSpec(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, method: str = "",
|
||||||
|
status=None):
|
||||||
|
super(DIFAdisTSSpec, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = DIFAdisTSSpec._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._method = method
|
||||||
|
self._reach = None
|
||||||
|
self._start_rk = None
|
||||||
|
self._end_rk = None
|
||||||
|
self._dif = None
|
||||||
|
self._b = None
|
||||||
|
self._c = None
|
||||||
|
self._enabled = True
|
||||||
|
|
||||||
|
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE dif_spec(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
dif_default INTEGER NOT NULL,
|
||||||
|
method TEXT NOT NULL,
|
||||||
|
reach INTEGER NOT NULL,
|
||||||
|
start_rk REAL NOT NULL,
|
||||||
|
end_rk REAL NOT NULL,
|
||||||
|
dif REAL NOT NULL,
|
||||||
|
b REAL,
|
||||||
|
c REAL,
|
||||||
|
enabled BOOLEAN NOT NULL,
|
||||||
|
FOREIGN KEY(dif_default) REFERENCES dif_adists(id),
|
||||||
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == 0 and minor < 1:
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, dif_default, method, reach, start_rk, end_rk, " +
|
||||||
|
"dif, b, c, enabled " +
|
||||||
|
"FROM dif_spec " +
|
||||||
|
f"WHERE dif_default = {data['dif_default_id']} "
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
id = row[0]
|
||||||
|
method = row[2]
|
||||||
|
reach = row[3]
|
||||||
|
start_rk = row[4]
|
||||||
|
end_rk = row[5]
|
||||||
|
dif = row[6]
|
||||||
|
b = row[7]
|
||||||
|
c = row[8]
|
||||||
|
enabled = (row[9] == 1)
|
||||||
|
|
||||||
|
new_spec = cls(
|
||||||
|
id=id,
|
||||||
|
method=method,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
new_spec.reach = reach
|
||||||
|
new_spec.start_rk = start_rk
|
||||||
|
new_spec.end_rk = end_rk
|
||||||
|
new_spec.dif = dif
|
||||||
|
new_spec.b = b
|
||||||
|
new_spec.c = c
|
||||||
|
new_spec.enabled = enabled
|
||||||
|
|
||||||
|
new.append(new_spec)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
dif_default = data['dif_default_id']
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"dif_spec(id, dif_default, method, reach, " +
|
||||||
|
"start_rk, end_rk, dif, b, c, enabled) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, " +
|
||||||
|
f"{dif_default}, " +
|
||||||
|
f"'{self._db_format(self._method)}', " +
|
||||||
|
f"{self._reach}, " +
|
||||||
|
f"{self._start_rk}, " +
|
||||||
|
f"{self._end_rk}, " +
|
||||||
|
f"{self._dif}, " +
|
||||||
|
f"{self._b}, " +
|
||||||
|
f"{self._c}, " +
|
||||||
|
f"{self._enabled}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def method(self):
|
||||||
|
return self._method
|
||||||
|
|
||||||
|
@method.setter
|
||||||
|
def method(self, method):
|
||||||
|
self._method = method
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reach(self):
|
||||||
|
return self._reach
|
||||||
|
|
||||||
|
@reach.setter
|
||||||
|
def reach(self, reach):
|
||||||
|
self._reach = reach
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_rk(self):
|
||||||
|
return self._start_rk
|
||||||
|
|
||||||
|
@start_rk.setter
|
||||||
|
def start_rk(self, start_rk):
|
||||||
|
self._start_rk = start_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_rk(self):
|
||||||
|
return self._end_rk
|
||||||
|
|
||||||
|
@end_rk.setter
|
||||||
|
def end_rk(self, end_rk):
|
||||||
|
self._end_rk = end_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dif(self):
|
||||||
|
return self._dif
|
||||||
|
|
||||||
|
@dif.setter
|
||||||
|
def dif(self, dif):
|
||||||
|
self._dif = dif
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def b(self):
|
||||||
|
return self._b
|
||||||
|
|
||||||
|
@b.setter
|
||||||
|
def b(self, b):
|
||||||
|
self._b = b
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def c(self):
|
||||||
|
return self._c
|
||||||
|
|
||||||
|
@c.setter
|
||||||
|
def c(self, c):
|
||||||
|
self._c = c
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
@@ -64,6 +64,7 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute):
|
||||||
|
|
||||||
execute("""
|
execute("""
|
||||||
CREATE TABLE hydraulic_structures(
|
CREATE TABLE hydraulic_structures(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
@@ -154,6 +155,7 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
|
print("save hs unit")
|
||||||
execute(f"DELETE FROM hydraulic_structures WHERE id = {self.id}")
|
execute(f"DELETE FROM hydraulic_structures WHERE id = {self.id}")
|
||||||
|
|
||||||
input_reach_id = -1
|
input_reach_id = -1
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
# InitialConditionsAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTSSpec \
|
||||||
|
import ICAdisTSSpec
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class InitialConditionsAdisTS(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
ICAdisTSSpec,
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "default",
|
||||||
|
pollutant: int = -1, status=None):
|
||||||
|
super(InitialConditionsAdisTS, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = InitialConditionsAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._concentration = None
|
||||||
|
self._eg = None
|
||||||
|
self._em = None
|
||||||
|
self._ed = None
|
||||||
|
self._enabled = True
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
InitialConditionsAdisTS._id_cnt = max(
|
||||||
|
InitialConditionsAdisTS._id_cnt + 1,
|
||||||
|
self.id
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE initial_conditions_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
pollutant INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
concentration REAL NOT NULL,
|
||||||
|
eg REAL NOT NULL,
|
||||||
|
em REAL NOT NULL,
|
||||||
|
ed REAL NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL,
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == 0 and minor < 1:
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, pollutant, name, concentration, eg, em, ed, " +
|
||||||
|
"enabled " +
|
||||||
|
"FROM initial_conditions_adists"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
IC_id = row[0]
|
||||||
|
pollutant = row[1]
|
||||||
|
name = row[2]
|
||||||
|
concentration = row[3]
|
||||||
|
eg = row[4]
|
||||||
|
em = row[5]
|
||||||
|
ed = row[6]
|
||||||
|
enabled = (row[7] == 1)
|
||||||
|
|
||||||
|
IC = cls(
|
||||||
|
id=IC_id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
IC.pollutant = pollutant
|
||||||
|
IC.concentration = concentration
|
||||||
|
IC.eg = eg
|
||||||
|
IC.em = em
|
||||||
|
IC.ed = ed
|
||||||
|
IC.enabled = enabled
|
||||||
|
|
||||||
|
data['ic_default_id'] = IC_id
|
||||||
|
IC._data = ICAdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
|
new.append(IC)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute(f"DELETE FROM initial_conditions_adists WHERE id = {self.id}")
|
||||||
|
|
||||||
|
pollutant = -1
|
||||||
|
if self.pollutant is not None:
|
||||||
|
pollutant = self.pollutant
|
||||||
|
|
||||||
|
concentration = -1.
|
||||||
|
if self.concentration is not None:
|
||||||
|
concentration = self.concentration
|
||||||
|
|
||||||
|
eg = -1.
|
||||||
|
if self.eg is not None:
|
||||||
|
eg = self.eg
|
||||||
|
|
||||||
|
em = -1.
|
||||||
|
if self.em is not None:
|
||||||
|
em = self.em
|
||||||
|
|
||||||
|
ed = -1.
|
||||||
|
if self.ed is not None:
|
||||||
|
ed = self.ed
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"initial_conditions_adists(" +
|
||||||
|
"id, pollutant, name, concentration, " +
|
||||||
|
"eg, em, ed, enabled" +
|
||||||
|
") " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, {pollutant}, '{self._db_format(self._name)}', " +
|
||||||
|
f"{concentration}, {eg}, {em}, {ed}, {self._enabled}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
data['ic_default_id'] = self.id
|
||||||
|
execute(
|
||||||
|
"DELETE FROM initial_conditions_spec " +
|
||||||
|
f"WHERE ic_default = {self.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for ic_spec in self._data:
|
||||||
|
ic_spec._db_save(execute, data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pollutant(self):
|
||||||
|
return self._pollutant
|
||||||
|
|
||||||
|
@pollutant.setter
|
||||||
|
def pollutant(self, pollutant):
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def concentration(self):
|
||||||
|
return self._concentration
|
||||||
|
|
||||||
|
@concentration.setter
|
||||||
|
def concentration(self, concentration):
|
||||||
|
self._concentration = concentration
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def eg(self):
|
||||||
|
return self._eg
|
||||||
|
|
||||||
|
@eg.setter
|
||||||
|
def eg(self, eg):
|
||||||
|
self._eg = eg
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def em(self):
|
||||||
|
return self._em
|
||||||
|
|
||||||
|
@em.setter
|
||||||
|
def em(self, em):
|
||||||
|
self._em = em
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ed(self):
|
||||||
|
return self._ed
|
||||||
|
|
||||||
|
@ed.setter
|
||||||
|
def ed(self, ed):
|
||||||
|
self._ed = ed
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def new(self, index):
|
||||||
|
n = ICAdisTSSpec(status=self._status)
|
||||||
|
self._data.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
def delete(self, data):
|
||||||
|
self._data = list(
|
||||||
|
filter(
|
||||||
|
lambda x: x not in data,
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def delete_i(self, indexes):
|
||||||
|
for ind in indexes:
|
||||||
|
del self._data[ind]
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def insert(self, index, data):
|
||||||
|
self._data.insert(index, data)
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# InitialConditionsAdisTSList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTS \
|
||||||
|
import InitialConditionsAdisTS
|
||||||
|
|
||||||
|
|
||||||
|
class InitialConditionsAdisTSList(PamhyrModelList):
|
||||||
|
_sub_classes = [
|
||||||
|
InitialConditionsAdisTS,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
new._lst = InitialConditionsAdisTS._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute("DELETE FROM initial_conditions_adists")
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for ic in self._lst:
|
||||||
|
ic._db_save(execute, data=data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def new(self, index, pollutant):
|
||||||
|
n = InitialConditionsAdisTS(pollutant=pollutant, status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Initial_Conditions_List(self):
|
||||||
|
return self.lst
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
# InitialConditionsAdisTSSpec.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class ICAdisTSSpec(SQLSubModel):
|
||||||
|
_sub_classes = [
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(ICAdisTSSpec, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = ICAdisTSSpec._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name_section = name
|
||||||
|
self._reach = None
|
||||||
|
self._start_rk = None
|
||||||
|
self._end_rk = None
|
||||||
|
self._concentration = None
|
||||||
|
self._eg = None
|
||||||
|
self._em = None
|
||||||
|
self._ed = None
|
||||||
|
self._rate = None
|
||||||
|
self._enabled = True
|
||||||
|
|
||||||
|
ICAdisTSSpec._id_cnt = max(ICAdisTSSpec._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE initial_conditions_spec(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
ic_default INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
reach INTEGER NOT NULL,
|
||||||
|
start_rk REAL NOT NULL,
|
||||||
|
end_rk REAL NOT NULL,
|
||||||
|
concentration REAL NOT NULL,
|
||||||
|
eg REAL NOT NULL,
|
||||||
|
em REAL NOT NULL,
|
||||||
|
ed REAL NOT NULL,
|
||||||
|
rate REAL NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL,
|
||||||
|
FOREIGN KEY(ic_default) REFERENCES initial_conditions_adists(id),
|
||||||
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == 0 and minor < 1:
|
||||||
|
if int(release) < 6:
|
||||||
|
cls._db_create(execute)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, ic_default, name, reach, start_rk, end_rk, " +
|
||||||
|
"concentration, eg, em, ed, rate, enabled " +
|
||||||
|
"FROM initial_conditions_spec " +
|
||||||
|
f"WHERE ic_default = {data['ic_default_id']} "
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
id = row[0]
|
||||||
|
name = row[2]
|
||||||
|
reach = row[3]
|
||||||
|
start_rk = row[4]
|
||||||
|
end_rk = row[5]
|
||||||
|
concentration = row[6]
|
||||||
|
eg = row[7]
|
||||||
|
em = row[8]
|
||||||
|
ed = row[9]
|
||||||
|
rate = row[10]
|
||||||
|
enabled = (row[11] == 1)
|
||||||
|
|
||||||
|
# new_spec = [name, reach, start_rk, end_rk,
|
||||||
|
# concentration, eg, em, ed, rate, enabled]
|
||||||
|
|
||||||
|
new_spec = cls(
|
||||||
|
id=id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
new_spec.reach = reach
|
||||||
|
new_spec.start_rk = start_rk
|
||||||
|
new_spec.end_rk = end_rk
|
||||||
|
new_spec.concentration = concentration
|
||||||
|
new_spec.eg = eg
|
||||||
|
new_spec.em = em
|
||||||
|
new_spec.ed = ed
|
||||||
|
new_spec.rate = rate
|
||||||
|
new_spec.enabled = enabled
|
||||||
|
|
||||||
|
new.append(new_spec)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
ic_default = data['ic_default_id']
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"initial_conditions_spec(id, ic_default, name, reach, " +
|
||||||
|
"start_rk, end_rk, concentration, eg, em, ed, rate, enabled) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, " +
|
||||||
|
f"{ic_default}, " +
|
||||||
|
f"'{self._db_format(self._name_section)}', " +
|
||||||
|
f"{self._reach}, " +
|
||||||
|
f"{self._start_rk}, " +
|
||||||
|
f"{self._end_rk}, " +
|
||||||
|
f"{self._concentration}, " +
|
||||||
|
f"{self._eg}, " +
|
||||||
|
f"{self._em}, " +
|
||||||
|
f"{self._ed}, " +
|
||||||
|
f"{self._rate}, " +
|
||||||
|
f"{self._enabled}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name_section
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name_section = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reach(self):
|
||||||
|
return self._reach
|
||||||
|
|
||||||
|
@reach.setter
|
||||||
|
def reach(self, reach):
|
||||||
|
self._reach = reach
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_rk(self):
|
||||||
|
return self._start_rk
|
||||||
|
|
||||||
|
@start_rk.setter
|
||||||
|
def start_rk(self, start_rk):
|
||||||
|
self._start_rk = start_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_rk(self):
|
||||||
|
return self._end_rk
|
||||||
|
|
||||||
|
@end_rk.setter
|
||||||
|
def end_rk(self, end_rk):
|
||||||
|
self._end_rk = end_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def concentration(self):
|
||||||
|
return self._concentration
|
||||||
|
|
||||||
|
@concentration.setter
|
||||||
|
def concentration(self, concentration):
|
||||||
|
self._concentration = concentration
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def eg(self):
|
||||||
|
return self._eg
|
||||||
|
|
||||||
|
@eg.setter
|
||||||
|
def eg(self, eg):
|
||||||
|
self._eg = eg
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def em(self):
|
||||||
|
return self._em
|
||||||
|
|
||||||
|
@em.setter
|
||||||
|
def em(self, em):
|
||||||
|
self._em = em
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ed(self):
|
||||||
|
return self._ed
|
||||||
|
|
||||||
|
@ed.setter
|
||||||
|
def ed(self, ed):
|
||||||
|
self._ed = ed
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rate(self):
|
||||||
|
return self._rate
|
||||||
|
|
||||||
|
@rate.setter
|
||||||
|
def rate(self, rate):
|
||||||
|
self._rate = rate
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# LateralContributionAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer,
|
||||||
|
old_pamhyr_date_to_timestamp,
|
||||||
|
date_iso_to_timestamp,
|
||||||
|
date_dmy_to_timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class LateralContributionAdisTS(SQLSubModel):
|
||||||
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, pollutant: int = -1,
|
||||||
|
name: str = "", status=None):
|
||||||
|
super(LateralContributionAdisTS, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = LateralContributionAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._edge = None
|
||||||
|
self._begin_rk = 0.0
|
||||||
|
self._end_rk = 0.0
|
||||||
|
self._data = []
|
||||||
|
self._header = ["time", "rate"]
|
||||||
|
self._types = [self.time_convert, float]
|
||||||
|
|
||||||
|
LateralContributionAdisTS._id_cnt = max(
|
||||||
|
LateralContributionAdisTS._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE lateral_contribution_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
pollutant INTEGER NOT NULL,
|
||||||
|
edge INTEGER NOT NULL,
|
||||||
|
begin_rk REAL NOT NULL,
|
||||||
|
end_rk REAL NOT NULL,
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id),
|
||||||
|
FOREIGN KEY(edge) REFERENCES river_reach(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE lateral_contribution_data_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
data0 TEXT NOT NULL,
|
||||||
|
data1 TEXT NOT NULL,
|
||||||
|
lc INTEGER,
|
||||||
|
FOREIGN KEY(lc) REFERENCES lateral_contribution(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, pollutant, edge, begin_rk, end_rk " +
|
||||||
|
"FROM lateral_contribution_adists"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
lc = cls(
|
||||||
|
id=row[0],
|
||||||
|
pollutant=row[1],
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
lc.edge = row[2]
|
||||||
|
lc.begin_rk = row[3]
|
||||||
|
lc.end_rk = row[4]
|
||||||
|
|
||||||
|
values = execute(
|
||||||
|
"SELECT data0," +
|
||||||
|
" data1 FROM lateral_contribution_data_adists " +
|
||||||
|
f"WHERE lc = '{lc.id}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write data
|
||||||
|
for v in values:
|
||||||
|
data0 = lc._types[0](v[0])
|
||||||
|
data1 = lc._types[1](v[1])
|
||||||
|
# Replace data at pos ind
|
||||||
|
lc._data.append((data0, data1))
|
||||||
|
|
||||||
|
new.append(lc)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
|
||||||
|
execute(f"DELETE FROM lateral_contribution_adists" +
|
||||||
|
f" WHERE id = {self.id}")
|
||||||
|
execute(f"DELETE FROM lateral_contribution_data_adists" +
|
||||||
|
f" WHERE lc = {self.id}")
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"lateral_contribution_adists(id, " +
|
||||||
|
"pollutant, edge, begin_rk, end_rk) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, {self._pollutant}, {self.edge}, " +
|
||||||
|
f"{self._begin_rk}, {self._end_rk}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
for d in self._data:
|
||||||
|
data0 = self._db_format(str(d[0]))
|
||||||
|
data1 = self._db_format(str(d[1]))
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"lateral_contribution_data_adists(data0, data1, lc) " +
|
||||||
|
f"VALUES ('{data0}', {data1}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def time_convert(cls, data):
|
||||||
|
if type(data) is str:
|
||||||
|
if data.count("-") == 2:
|
||||||
|
return date_iso_to_timestamp(data)
|
||||||
|
if data.count("/") == 2:
|
||||||
|
return date_dmy_to_timestamp(data)
|
||||||
|
if data.count(":") == 3:
|
||||||
|
return old_pamhyr_date_to_timestamp(data)
|
||||||
|
if data.count(":") == 2:
|
||||||
|
return old_pamhyr_date_to_timestamp("00:" + data)
|
||||||
|
if data.count(".") == 1:
|
||||||
|
return round(float(data))
|
||||||
|
|
||||||
|
return int(data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edge(self):
|
||||||
|
return self._edge
|
||||||
|
|
||||||
|
@edge.setter
|
||||||
|
def edge(self, edge):
|
||||||
|
self._edge = edge
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def header(self):
|
||||||
|
return self._header.copy()
|
||||||
|
|
||||||
|
@header.setter
|
||||||
|
def header(self, header):
|
||||||
|
self._header = header
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pollutant(self):
|
||||||
|
return self._pollutant
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
return self._data.copy()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def begin_rk(self):
|
||||||
|
return self._begin_rk
|
||||||
|
|
||||||
|
@begin_rk.setter
|
||||||
|
def begin_rk(self, begin_rk):
|
||||||
|
self._begin_rk = begin_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_rk(self):
|
||||||
|
return self._end_rk
|
||||||
|
|
||||||
|
@end_rk.setter
|
||||||
|
def end_rk(self, end_rk):
|
||||||
|
self._end_rk = end_rk
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# LateralContributionsAdisTSList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import copy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
||||||
|
import LateralContributionAdisTS
|
||||||
|
|
||||||
|
|
||||||
|
class LateralContributionsAdisTSList(PamhyrModelList):
|
||||||
|
_sub_classes = [
|
||||||
|
LateralContributionAdisTS,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
new._lst = LateralContributionAdisTS._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
execute("DELETE FROM lateral_contribution_adists")
|
||||||
|
execute("DELETE FROM lateral_condition_data_adists")
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
for lc in self._lst:
|
||||||
|
lc._db_save(execute, data=data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def new(self, index, pollutant):
|
||||||
|
n = LateralContributionAdisTS(pollutant=pollutant, status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Lat_Cont_List(self):
|
||||||
|
return self.lst
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
# OutputRKAdists.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer,
|
||||||
|
old_pamhyr_date_to_timestamp,
|
||||||
|
date_iso_to_timestamp,
|
||||||
|
date_dmy_to_timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class OutputRKAdists(SQLSubModel):
|
||||||
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, reach=None,
|
||||||
|
rk=None, title: str = "", status=None):
|
||||||
|
super(OutputRKAdists, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = OutputRKAdists._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._reach = reach
|
||||||
|
self._rk = rk
|
||||||
|
self._title = str(title)
|
||||||
|
self._enabled = True
|
||||||
|
|
||||||
|
OutputRKAdists._id_cnt = max(
|
||||||
|
OutputRKAdists._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reach(self):
|
||||||
|
return self._reach
|
||||||
|
|
||||||
|
@reach.setter
|
||||||
|
def reach(self, reach_id):
|
||||||
|
self._reach = reach_id
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rk(self):
|
||||||
|
return self._rk
|
||||||
|
|
||||||
|
@rk.setter
|
||||||
|
def rk(self, profile_id):
|
||||||
|
self._rk = profile_id
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self):
|
||||||
|
return self._title
|
||||||
|
|
||||||
|
@title.setter
|
||||||
|
def title(self, title):
|
||||||
|
self._title = title
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"CREATE TABLE OutputRKAdists(" +
|
||||||
|
"id INTEGER NOT NULL PRIMARY KEY, " +
|
||||||
|
"reach INTEGER NOT NULL, " +
|
||||||
|
"rk REAL NOT NULL, " +
|
||||||
|
"title TEXT NOT NULL, " +
|
||||||
|
"FOREIGN KEY(reach) REFERENCES river_reach(id)" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
# reach = data["reach"]
|
||||||
|
# profile = data["profile"]
|
||||||
|
status = data["status"]
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, reach, rk, title " +
|
||||||
|
f"FROM OutputRKAdists"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
id = row[0]
|
||||||
|
id_reach = row[1]
|
||||||
|
id_rk = row[2]
|
||||||
|
title = row[3]
|
||||||
|
|
||||||
|
new_output = cls(
|
||||||
|
id=id, reach=id_reach,
|
||||||
|
rk=id_rk, title=title,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
new.append(new_output)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
|
||||||
|
execute(f"DELETE FROM OutputRKAdists WHERE id = {self.id}")
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"OutputRKAdists(id, reach, rk, title) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, {self._reach}, {self._rk}, " +
|
||||||
|
f"'{self._db_format(self._title)}'" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# OutputRKListAdists.py -- Pamhyr
|
||||||
|
# Copyright (C) 2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.OutputRKAdists.OutputRKAdists import OutputRKAdists
|
||||||
|
|
||||||
|
|
||||||
|
class OutputRKAdistsList(PamhyrModelList):
|
||||||
|
_sub_classes = [OutputRKAdists]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data["status"])
|
||||||
|
|
||||||
|
new._lst = OutputRKAdists._db_load(execute, data)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
ok = True
|
||||||
|
|
||||||
|
# Delete previous data
|
||||||
|
execute("DELETE FROM OutputRKAdists")
|
||||||
|
|
||||||
|
for sl in self._lst:
|
||||||
|
ok &= sl._db_save(execute, data)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
|
||||||
|
@property
|
||||||
|
def OutputRK_List(self):
|
||||||
|
return self.lst
|
||||||
|
|
||||||
|
def new(self, lst, index):
|
||||||
|
n = OutputRKAdists(status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
# Pollutants.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer,
|
||||||
|
old_pamhyr_date_to_timestamp,
|
||||||
|
date_iso_to_timestamp,
|
||||||
|
date_dmy_to_timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class Pollutants(SQLSubModel):
|
||||||
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
|
super(Pollutants, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = Pollutants._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._name = str(name)
|
||||||
|
self._enabled = True
|
||||||
|
|
||||||
|
self._data = []
|
||||||
|
|
||||||
|
Pollutants._id_cnt = max(
|
||||||
|
Pollutants._id_cnt + 1, self.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
return self._data.copy()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE Pollutants(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL UNIQUE
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE Pollutants_characteristics(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
diametre REAL NOT NULL,
|
||||||
|
rho REAL NOT NULL,
|
||||||
|
porosity REAL NOT NULL,
|
||||||
|
cdc_riv REAL NOT NULL,
|
||||||
|
cdc_cas REAL NOT NULL,
|
||||||
|
apd REAL NOT NULL,
|
||||||
|
ac REAL NOT NULL,
|
||||||
|
bc REAL NOT NULL,
|
||||||
|
pollutant INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = []
|
||||||
|
|
||||||
|
status = data["status"]
|
||||||
|
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, name " +
|
||||||
|
f"FROM Pollutants"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for row in table:
|
||||||
|
id = row[0]
|
||||||
|
name = row[1]
|
||||||
|
|
||||||
|
new_pollutant = cls(
|
||||||
|
id=id, name=name,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
new_data = []
|
||||||
|
table = execute(
|
||||||
|
"SELECT * " +
|
||||||
|
"FROM Pollutants_characteristics " +
|
||||||
|
f"WHERE pollutant = {id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if table is not None:
|
||||||
|
for t in table:
|
||||||
|
new_data = t[1:]
|
||||||
|
|
||||||
|
new_pollutant._data.append(new_data)
|
||||||
|
|
||||||
|
new.append(new_pollutant)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
|
||||||
|
execute(f"DELETE FROM Pollutants WHERE id = {self.id}")
|
||||||
|
execute(f"DELETE FROM Pollutants_characteristics" +
|
||||||
|
f" WHERE pollutant = {self.id}")
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"Pollutants(id, name) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, " +
|
||||||
|
f"'{self._db_format(self._name)}'" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
for d in self._data:
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"Pollutants_characteristics(type, diametre, rho, porosity, " +
|
||||||
|
"cdc_riv, cdc_cas, apd, ac, bc, pollutant) " +
|
||||||
|
f"VALUES ({d[0]}, {d[1]}, {d[2]},{d[3]}, {d[4]}, "
|
||||||
|
f"{d[5]}, {d[6]}, {d[7]}, {d[8]}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
|
@enabled.setter
|
||||||
|
def enabled(self, enabled):
|
||||||
|
self._enabled = enabled
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def is_define(self):
|
||||||
|
return len(self._data) != 0
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# PollutantsList.py -- Pamhyr
|
||||||
|
# Copyright (C) 2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
from Model.Pollutants.Pollutants import Pollutants
|
||||||
|
|
||||||
|
|
||||||
|
class PollutantsList(PamhyrModelList):
|
||||||
|
_sub_classes = [Pollutants]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
new = cls(status=data["status"])
|
||||||
|
|
||||||
|
new._lst = Pollutants._db_load(execute, data)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _db_save(self, execute, data=None):
|
||||||
|
ok = True
|
||||||
|
|
||||||
|
# Delete previous data
|
||||||
|
execute("DELETE FROM Pollutants")
|
||||||
|
|
||||||
|
for sl in self._lst:
|
||||||
|
ok &= sl._db_save(execute, data)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Pollutants_List(self):
|
||||||
|
return self.lst
|
||||||
|
|
||||||
|
def new(self, lst, index):
|
||||||
|
n = Pollutants(status=self._status)
|
||||||
|
self._lst.insert(index, n)
|
||||||
|
self._status.modified()
|
||||||
|
return n
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Results.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from Model.Results.River.RiverAdisTS import River
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class Results(object):
|
||||||
|
def __init__(self, study=None, solver=None,
|
||||||
|
repertory="", name="0", type_pol=None):
|
||||||
|
self._study = study
|
||||||
|
self._solver = solver
|
||||||
|
self._repertory = repertory
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
self._river = River(self._study)
|
||||||
|
|
||||||
|
self._meta_data = {
|
||||||
|
# Keep results creation date
|
||||||
|
"creation_date": datetime.now(),
|
||||||
|
}
|
||||||
|
repertory_results = os.path.join(repertory, "resultats")
|
||||||
|
self._pollutants_list = [
|
||||||
|
el.split("/")[-1][0:-4]
|
||||||
|
for el in glob.glob(repertory_results + "/*.bin")
|
||||||
|
]
|
||||||
|
|
||||||
|
self._phys_var_list = ["C", "G", "M", "D", "L", "N", "R"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def date(self):
|
||||||
|
date = self._meta_data["creation_date"]
|
||||||
|
return f"{date.isoformat(sep=' ')}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def river(self):
|
||||||
|
return self._river
|
||||||
|
|
||||||
|
@property
|
||||||
|
def study(self):
|
||||||
|
return self._study
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pollutants_list(self):
|
||||||
|
return self._pollutants_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def phys_var_list(self):
|
||||||
|
return self._phys_var_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nb_pollutants(self):
|
||||||
|
return len(self._pollutants_list)
|
||||||
|
|
||||||
|
def set(self, key, value):
|
||||||
|
self._meta_data[key] = value
|
||||||
|
|
||||||
|
def get(self, key):
|
||||||
|
return self._meta_data[key]
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
return self._solver.results(
|
||||||
|
self._study,
|
||||||
|
self._repertory,
|
||||||
|
qlog=None,
|
||||||
|
)
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# River.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class Profile(object):
|
||||||
|
def __init__(self, profile, study):
|
||||||
|
self._study = study
|
||||||
|
self._profile = profile # Source profile in the study
|
||||||
|
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._profile.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rk(self):
|
||||||
|
return self._profile.rk
|
||||||
|
|
||||||
|
@property
|
||||||
|
def geometry(self):
|
||||||
|
return self._profile
|
||||||
|
|
||||||
|
def set(self, timestamp, key, data):
|
||||||
|
if timestamp not in self._data:
|
||||||
|
self._data[timestamp] = {}
|
||||||
|
|
||||||
|
self._data[timestamp][key] = data
|
||||||
|
|
||||||
|
def get_ts(self, timestamp):
|
||||||
|
return self._data[timestamp]
|
||||||
|
|
||||||
|
def get_key(self, key):
|
||||||
|
return list(
|
||||||
|
map(lambda ts: self._data[ts][key], self._data)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_ts_key(self, timestamp, key):
|
||||||
|
return self._data[timestamp][key]
|
||||||
|
|
||||||
|
def has_sediment(self):
|
||||||
|
return any(map(lambda ts: "sl" in self._data[ts], self._data))
|
||||||
|
|
||||||
|
|
||||||
|
class Reach(object):
|
||||||
|
def __init__(self, reach, study):
|
||||||
|
self._study = study
|
||||||
|
self._reach = reach # Source reach in the study
|
||||||
|
self._profiles = list(
|
||||||
|
map(
|
||||||
|
lambda p: Profile(p, self._study),
|
||||||
|
reach.profiles
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._profiles)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._reach.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def geometry(self):
|
||||||
|
return self._reach
|
||||||
|
|
||||||
|
@property
|
||||||
|
def profiles(self):
|
||||||
|
return self._profiles.copy()
|
||||||
|
|
||||||
|
def profile(self, id):
|
||||||
|
return self._profiles[id]
|
||||||
|
|
||||||
|
def set(self, profile_id, timestamp, key, data):
|
||||||
|
self._profiles[profile_id].set(timestamp, key, data)
|
||||||
|
|
||||||
|
def has_sediment(self):
|
||||||
|
return any(map(lambda profile: profile.has_sediment(), self._profiles))
|
||||||
|
|
||||||
|
|
||||||
|
class River(object):
|
||||||
|
def __init__(self, study):
|
||||||
|
self._study = study
|
||||||
|
|
||||||
|
# Dict with timestamps as key
|
||||||
|
self._reachs = []
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._reachs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reachs(self):
|
||||||
|
return self._reachs.copy()
|
||||||
|
|
||||||
|
def reach(self, id):
|
||||||
|
return self._reachs[id]
|
||||||
|
|
||||||
|
def has_reach(self, id):
|
||||||
|
return 0 <= id < len(self._reachs)
|
||||||
|
|
||||||
|
def add(self, reach_id):
|
||||||
|
reachs = self._study.river.enable_edges()
|
||||||
|
|
||||||
|
new = Reach(reachs[reach_id].reach, self._study)
|
||||||
|
|
||||||
|
self._reachs.append(new)
|
||||||
|
return new
|
||||||
|
|
||||||
|
def get_reach_by_geometry(self, geometry_reach):
|
||||||
|
return next(
|
||||||
|
filter(
|
||||||
|
lambda r: r.geometry is geometry_reach,
|
||||||
|
self._reachs
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -45,6 +45,17 @@ from Model.REPLine.REPLineList import REPLineList
|
|||||||
|
|
||||||
from Solver.Solvers import solver_type_list
|
from Solver.Solvers import solver_type_list
|
||||||
|
|
||||||
|
from Model.OutputRKAdists.OutputRKListAdists import OutputRKAdistsList
|
||||||
|
from Model.Pollutants.PollutantsList import PollutantsList
|
||||||
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTSList \
|
||||||
|
import InitialConditionsAdisTSList
|
||||||
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionsAdisTSList \
|
||||||
|
import BoundaryConditionsAdisTSList
|
||||||
|
from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList \
|
||||||
|
import LateralContributionsAdisTSList
|
||||||
|
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
|
||||||
|
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
|
||||||
|
|
||||||
|
|
||||||
class RiverNode(Node, SQLSubModel):
|
class RiverNode(Node, SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
@@ -228,6 +239,13 @@ class River(Graph, SQLSubModel):
|
|||||||
HydraulicStructureList,
|
HydraulicStructureList,
|
||||||
AddFileList,
|
AddFileList,
|
||||||
REPLineList,
|
REPLineList,
|
||||||
|
OutputRKAdistsList,
|
||||||
|
PollutantsList,
|
||||||
|
InitialConditionsAdisTSList,
|
||||||
|
BoundaryConditionsAdisTSList,
|
||||||
|
LateralContributionsAdisTSList,
|
||||||
|
D90AdisTSList,
|
||||||
|
DIFAdisTSList,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
@@ -251,6 +269,16 @@ class River(Graph, SQLSubModel):
|
|||||||
)
|
)
|
||||||
self._additional_files = AddFileList(status=self._status)
|
self._additional_files = AddFileList(status=self._status)
|
||||||
self._rep_lines = REPLineList(status=self._status)
|
self._rep_lines = REPLineList(status=self._status)
|
||||||
|
self._Output_rk_adists = OutputRKAdistsList(status=self._status)
|
||||||
|
self._Pollutants = PollutantsList(status=self._status)
|
||||||
|
self._InitialConditionsAdisTS = InitialConditionsAdisTSList(
|
||||||
|
status=self._status)
|
||||||
|
self._BoundaryConditionsAdisTS = BoundaryConditionsAdisTSList(
|
||||||
|
status=self._status)
|
||||||
|
self._LateralContributionsAdisTS = LateralContributionsAdisTSList(
|
||||||
|
status=self._status)
|
||||||
|
self._D90AdisTS = D90AdisTSList(status=self._status)
|
||||||
|
self._DIFAdisTS = DIFAdisTSList(status=self._status)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute):
|
||||||
@@ -325,6 +353,25 @@ class River(Graph, SQLSubModel):
|
|||||||
)
|
)
|
||||||
new._rep_lines = REPLineList._db_load(execute, data)
|
new._rep_lines = REPLineList._db_load(execute, data)
|
||||||
|
|
||||||
|
new._Output_rk_adists = OutputRKAdistsList._db_load(
|
||||||
|
execute, data
|
||||||
|
)
|
||||||
|
|
||||||
|
new._Pollutants = PollutantsList._db_load(execute, data)
|
||||||
|
|
||||||
|
new._InitialConditionsAdisTS = InitialConditionsAdisTSList._db_load(
|
||||||
|
execute, data)
|
||||||
|
|
||||||
|
new._BoundaryConditionsAdisTS = BoundaryConditionsAdisTSList._db_load(
|
||||||
|
execute, data)
|
||||||
|
|
||||||
|
new._LateralContributionsAdisTS = \
|
||||||
|
LateralContributionsAdisTSList._db_load(execute, data)
|
||||||
|
|
||||||
|
new._D90AdisTS = D90AdisTSList._db_load(execute, data)
|
||||||
|
|
||||||
|
new._DIFAdisTS = DIFAdisTSList._db_load(execute, data)
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
@@ -344,6 +391,14 @@ class River(Graph, SQLSubModel):
|
|||||||
for solver in self._parameters:
|
for solver in self._parameters:
|
||||||
objs.append(self._parameters[solver])
|
objs.append(self._parameters[solver])
|
||||||
|
|
||||||
|
objs.append(self._Output_rk_adists)
|
||||||
|
objs.append(self._Pollutants)
|
||||||
|
objs.append(self._InitialConditionsAdisTS)
|
||||||
|
objs.append(self._BoundaryConditionsAdisTS)
|
||||||
|
objs.append(self._LateralContributionsAdisTS)
|
||||||
|
objs.append(self._D90AdisTS)
|
||||||
|
objs.append(self._DIFAdisTS)
|
||||||
|
|
||||||
self._save_submodel(execute, objs, data)
|
self._save_submodel(execute, objs, data)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -466,6 +521,34 @@ Last export at: @date."""
|
|||||||
def parameters(self):
|
def parameters(self):
|
||||||
return self._parameters
|
return self._parameters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Output_rk_adists(self):
|
||||||
|
return self._Output_rk_adists
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Pollutants(self):
|
||||||
|
return self._Pollutants
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ic_adists(self):
|
||||||
|
return self._InitialConditionsAdisTS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def boundary_conditions_adists(self):
|
||||||
|
return self._BoundaryConditionsAdisTS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lateral_contributions_adists(self):
|
||||||
|
return self._LateralContributionsAdisTS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def d90_adists(self):
|
||||||
|
return self._D90AdisTS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dif_adists(self):
|
||||||
|
return self._DIFAdisTS
|
||||||
|
|
||||||
def get_params(self, solver):
|
def get_params(self, solver):
|
||||||
if solver in self._parameters:
|
if solver in self._parameters:
|
||||||
return self._parameters[solver]
|
return self._parameters[solver]
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ class Study(SQLModel):
|
|||||||
StudyNetworkReachChecker(),
|
StudyNetworkReachChecker(),
|
||||||
StudyGeometryChecker(),
|
StudyGeometryChecker(),
|
||||||
StudyInitialConditionsChecker(),
|
StudyInitialConditionsChecker(),
|
||||||
StudyBoundaryConditionChecker(),
|
# StudyBoundaryConditionChecker(),
|
||||||
# DummyOK(),
|
# DummyOK(),
|
||||||
# DummyWARNING(),
|
# DummyWARNING(),
|
||||||
# DummyERROR(),
|
# DummyERROR(),
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class SQLModel(SQL):
|
|||||||
self._create() # Create db
|
self._create() # Create db
|
||||||
# self._save() # Save
|
# self._save() # Save
|
||||||
else:
|
else:
|
||||||
|
logger.info("Update database")
|
||||||
self._update() # Update db scheme if necessary
|
self._update() # Update db scheme if necessary
|
||||||
# self._load() # Load data
|
# self._load() # Load data
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class Modules(IterableFlag):
|
|||||||
RESERVOIR = auto()
|
RESERVOIR = auto()
|
||||||
SEDIMENT_LAYER = auto()
|
SEDIMENT_LAYER = auto()
|
||||||
ADDITIONAL_FILES = auto()
|
ADDITIONAL_FILES = auto()
|
||||||
|
OUTPUT_RK = auto()
|
||||||
|
|
||||||
# Results
|
# Results
|
||||||
RESULTS = auto()
|
RESULTS = auto()
|
||||||
@@ -77,6 +78,7 @@ class Modules(IterableFlag):
|
|||||||
cls.ADDITIONAL_FILES,
|
cls.ADDITIONAL_FILES,
|
||||||
cls.RESULTS,
|
cls.RESULTS,
|
||||||
cls.WINDOW_LIST,
|
cls.WINDOW_LIST,
|
||||||
|
cls.OUTPUT_RK,
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -0,0 +1,875 @@
|
|||||||
|
# AdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer, logger_exception,
|
||||||
|
timestamp_to_old_pamhyr_date,
|
||||||
|
old_pamhyr_date_to_timestamp,
|
||||||
|
timestamp_to_old_pamhyr_date_adists
|
||||||
|
)
|
||||||
|
|
||||||
|
from Solver.CommandLine import CommandLineSolver
|
||||||
|
|
||||||
|
from Model.Results.ResultsAdisTS import Results
|
||||||
|
from Model.Results.River.River import River, Reach, Profile
|
||||||
|
|
||||||
|
from Checker.Adists import (
|
||||||
|
AdistsOutputRKChecker,
|
||||||
|
)
|
||||||
|
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def adists_file_open(filepath, mode):
|
||||||
|
f = open(filepath, mode)
|
||||||
|
|
||||||
|
if "w" in mode:
|
||||||
|
# Write header
|
||||||
|
comment = "*"
|
||||||
|
if ".ST" in filepath:
|
||||||
|
comment = "#"
|
||||||
|
|
||||||
|
f.write(
|
||||||
|
f"{comment} " +
|
||||||
|
"This file is generated by PAMHYR, please don't modify\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
class AdisTS(CommandLineSolver):
|
||||||
|
_type = "adists"
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super(AdisTS, self).__init__(name)
|
||||||
|
|
||||||
|
self._type = "adists"
|
||||||
|
|
||||||
|
self._cmd_input = ""
|
||||||
|
self._cmd_solver = "@path @input -o @output"
|
||||||
|
self._cmd_output = ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_parameters(cls):
|
||||||
|
lst = super(AdisTS, cls).default_parameters()
|
||||||
|
|
||||||
|
lst += [
|
||||||
|
("adists_implicitation_parameter", "0.5"),
|
||||||
|
("adists_timestep_screen", "60"),
|
||||||
|
("adists_timestep_bin", "60"),
|
||||||
|
("adists_timestep_csv", "60"),
|
||||||
|
("adists_timestep_mage", "60"),
|
||||||
|
("adists_initial_concentration", "60"),
|
||||||
|
]
|
||||||
|
|
||||||
|
return lst
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def checkers(cls):
|
||||||
|
lst = [
|
||||||
|
AdistsOutputRKChecker(),
|
||||||
|
]
|
||||||
|
|
||||||
|
return lst
|
||||||
|
|
||||||
|
def cmd_args(self, study):
|
||||||
|
lst = super(AdisTS, self).cmd_args(study)
|
||||||
|
return lst
|
||||||
|
|
||||||
|
def input_param(self):
|
||||||
|
name = self._study.name
|
||||||
|
return f"{name}.REP"
|
||||||
|
|
||||||
|
def log_file(self):
|
||||||
|
name = self._study.name
|
||||||
|
return f"{name}.TRA"
|
||||||
|
|
||||||
|
def _export_REP_additional_lines(self, study, rep_file):
|
||||||
|
lines = filter(
|
||||||
|
lambda line: line.is_enabled(),
|
||||||
|
study.river.rep_lines.lines
|
||||||
|
)
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
rep_file.write(line.line)
|
||||||
|
|
||||||
|
def _export_REP(self, study, repertory, mage_rep, files, qlog, name="0"):
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export REP file")
|
||||||
|
|
||||||
|
# Write header
|
||||||
|
with adists_file_open(
|
||||||
|
os.path.join(
|
||||||
|
repertory, f"{name}.REP"
|
||||||
|
), "w+"
|
||||||
|
) as f:
|
||||||
|
|
||||||
|
f.write(f"NET ../{mage_rep}/{name}.NET\n")
|
||||||
|
f.write(f"REP ../{mage_rep}/{name}.REP\n")
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
EXT = file.split('.')[1]
|
||||||
|
f.write(f"{EXT} {file}\n")
|
||||||
|
|
||||||
|
self._export_REP_additional_lines(study, f)
|
||||||
|
|
||||||
|
path_mage_net = os.path.join(os.path.abspath(
|
||||||
|
os.path.join(repertory, os.pardir)
|
||||||
|
), f"{mage_rep}/net")
|
||||||
|
path_adists_net = os.path.join(repertory, "net")
|
||||||
|
|
||||||
|
if os.path.exists(path_mage_net):
|
||||||
|
shutil.copytree(path_mage_net, path_adists_net, dirs_exist_ok=True)
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def export(self, study, repertory, qlog=None):
|
||||||
|
self._study = study
|
||||||
|
name = study.name.replace(" ", "_")
|
||||||
|
|
||||||
|
self.export_additional_files(study, repertory, qlog, name=name)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
###########
|
||||||
|
# RESULTS #
|
||||||
|
###########
|
||||||
|
|
||||||
|
def read_bin(self, study, repertory, results, qlog=None, name="0"):
|
||||||
|
return
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def results(self, study, repertory, qlog=None, name="0", type_pol=None):
|
||||||
|
results = Results(
|
||||||
|
study=study,
|
||||||
|
solver=self,
|
||||||
|
repertory=repertory,
|
||||||
|
name=name,
|
||||||
|
type_pol=type_pol,
|
||||||
|
)
|
||||||
|
self.read_bin(study, repertory, results, qlog, name=name)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def output_param(self):
|
||||||
|
name = ""
|
||||||
|
return f"{name}"
|
||||||
|
|
||||||
|
_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.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.id
|
||||||
|
|
||||||
|
# print("node name id: ", n)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# Adis-TS in weak coupling mode #
|
||||||
|
#################################
|
||||||
|
|
||||||
|
|
||||||
|
class AdisTSwc(AdisTS):
|
||||||
|
_type = "adistswc"
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super(AdisTSwc, self).__init__(name)
|
||||||
|
|
||||||
|
self._type = "adistswc"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_parameters(cls):
|
||||||
|
lst = super(AdisTSwc, cls).default_parameters()
|
||||||
|
|
||||||
|
# Insert new parameters at specific position
|
||||||
|
names = list(map(lambda t: t[0], lst))
|
||||||
|
|
||||||
|
return lst
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Export #
|
||||||
|
##########
|
||||||
|
|
||||||
|
def cmd_args(self, study):
|
||||||
|
lst = super(AdisTSwc, self).cmd_args(study)
|
||||||
|
|
||||||
|
return lst
|
||||||
|
|
||||||
|
def _export_POLs(self, study, repertory, qlog=None, name="0"):
|
||||||
|
|
||||||
|
files = []
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export POLS files")
|
||||||
|
|
||||||
|
pollutants = study.river.Pollutants.Pollutants_List
|
||||||
|
|
||||||
|
for pollutant in pollutants:
|
||||||
|
name = pollutant.name
|
||||||
|
with adists_file_open(
|
||||||
|
os.path.join(repertory, f"{name}.POL"), "w+"
|
||||||
|
) as f:
|
||||||
|
files.append(f"{name}.POL")
|
||||||
|
f.write(f"name = {name}\n")
|
||||||
|
|
||||||
|
self._export_POL_Characteristics(
|
||||||
|
study, pollutant._data, f, qlog
|
||||||
|
)
|
||||||
|
|
||||||
|
POL_ICs = next(filter(
|
||||||
|
lambda ic: ic.pollutant == pollutant.id,
|
||||||
|
study.river.ic_adists.Initial_Conditions_List
|
||||||
|
))
|
||||||
|
|
||||||
|
if POL_ICs.concentration is not None:
|
||||||
|
f.write(f"file_ini = {name}.INI\n")
|
||||||
|
self._export_ICs_AdisTS(
|
||||||
|
study, repertory, POL_ICs, qlog, name
|
||||||
|
)
|
||||||
|
|
||||||
|
POL_BCs = list(filter(
|
||||||
|
lambda bc: bc.pollutant == pollutant.id,
|
||||||
|
study.river.boundary_conditions_adists.BCs_AdisTS_List
|
||||||
|
))
|
||||||
|
|
||||||
|
if len(POL_BCs) != 0:
|
||||||
|
f.write(f"file_cl = {name}.CDT\n")
|
||||||
|
self._export_BCs_AdisTS(
|
||||||
|
study, repertory, POL_BCs, qlog, name
|
||||||
|
)
|
||||||
|
|
||||||
|
POL_LAT_Cont = list(filter(
|
||||||
|
lambda lc: lc.pollutant == pollutant.id,
|
||||||
|
study.river.lateral_contributions_adists.Lat_Cont_List
|
||||||
|
))
|
||||||
|
|
||||||
|
if len(POL_LAT_Cont) != 0:
|
||||||
|
f.write(f"file_ald = {name}.ALD\n")
|
||||||
|
f.write(f"*\n")
|
||||||
|
self._export_Lat_AdisTS(
|
||||||
|
study, repertory, POL_LAT_Cont, qlog, name
|
||||||
|
)
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
def _export_Lat_AdisTS(self, study, repertory, POL_LC, qlog, POL_name):
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export POL LCs files")
|
||||||
|
|
||||||
|
with adists_file_open(
|
||||||
|
os.path.join(repertory, f"{POL_name}.ALD"), "w+"
|
||||||
|
) as f:
|
||||||
|
for LC in POL_LC:
|
||||||
|
reach = next(filter(
|
||||||
|
lambda edge: edge.id == LC.edge, study.river.edges()
|
||||||
|
)) # .name
|
||||||
|
reach_name = self.get_reach_name(self, reach)
|
||||||
|
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"*---------++++++++++\n")
|
||||||
|
|
||||||
|
for LC_data in LC._data:
|
||||||
|
tmp = timestamp_to_old_pamhyr_date_adists(int(LC_data[0]))
|
||||||
|
f.write(" ".join((f"{tmp}",
|
||||||
|
f"{LC_data[1]}\n")))
|
||||||
|
f.write(f"*\n")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _export_BCs_AdisTS(self, study, repertory, POL_BC, qlog, POL_name):
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export POL BCs files")
|
||||||
|
|
||||||
|
with adists_file_open(os.path.join(
|
||||||
|
repertory, f"{POL_name}.CDT"), "w+"
|
||||||
|
) as f:
|
||||||
|
for BC in POL_BC:
|
||||||
|
node = next(filter(
|
||||||
|
lambda x: x.id == BC.node, study.river._nodes
|
||||||
|
))
|
||||||
|
node_name = f"{node.id:3}".replace(" ", "x")
|
||||||
|
f.write(f"${node_name}\n")
|
||||||
|
|
||||||
|
if BC.type == "Concentration":
|
||||||
|
f.write(f"*temps |concentration\n")
|
||||||
|
f.write(f"*JJ:HH:MM | (g/L)\n")
|
||||||
|
f.write(f"*---------++++++++++\n")
|
||||||
|
else:
|
||||||
|
f.write(f"*temps |rate\n")
|
||||||
|
f.write(f"*JJ:HH:MM | (kg/s)\n")
|
||||||
|
f.write(f"*---------++++++++++\n")
|
||||||
|
|
||||||
|
for BC_data in BC._data:
|
||||||
|
tmp = timestamp_to_old_pamhyr_date_adists(int(BC_data[0]))
|
||||||
|
f.write(" ".join((f"{tmp}",
|
||||||
|
f"{BC_data[1]}\n")))
|
||||||
|
f.write(f"*\n")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _export_ICs_AdisTS(self, study, repertory,
|
||||||
|
POL_IC_default, qlog, POL_name):
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export POL ICs files")
|
||||||
|
|
||||||
|
with adists_file_open(os.path.join(
|
||||||
|
repertory, f"{POL_name}.INI"
|
||||||
|
), "w+") as f:
|
||||||
|
f.write(f"*État initial pour le polluant {POL_name}\n")
|
||||||
|
f.write(" ".join(("DEFAULT =",
|
||||||
|
f"{POL_IC_default.concentration}",
|
||||||
|
f"{POL_IC_default.eg}",
|
||||||
|
f"{POL_IC_default.em}",
|
||||||
|
f"{POL_IC_default.ed}\n")))
|
||||||
|
|
||||||
|
if len(POL_IC_default._data) != 0:
|
||||||
|
self._export_ICs_AdisTS_Spec(
|
||||||
|
study, POL_IC_default._data, f, qlog
|
||||||
|
)
|
||||||
|
|
||||||
|
def _export_ICs_AdisTS_Spec(self, study, pol_ics_spec_data,
|
||||||
|
f, qlog, name="0"):
|
||||||
|
|
||||||
|
for ic_spec in pol_ics_spec_data:
|
||||||
|
f.write(" ".join((f"{ic_spec.name}",
|
||||||
|
"=",
|
||||||
|
f"{ic_spec.reach}",
|
||||||
|
f"{ic_spec.start_rk}",
|
||||||
|
f"{ic_spec.end_rk}",
|
||||||
|
f"{ic_spec.concentration}",
|
||||||
|
f"{ic_spec.eg}",
|
||||||
|
f"{ic_spec.em}",
|
||||||
|
f"{ic_spec.ed}",
|
||||||
|
f"{ic_spec.rate}\n")))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _export_POL_Characteristics(self, study, pol_data, f, qlog, name="0"):
|
||||||
|
|
||||||
|
list_characteristics = ["type", "diametre", "rho", "porosity",
|
||||||
|
"cdc_riv", "cdc_cas", "apd", "ac", "bc"]
|
||||||
|
|
||||||
|
if len(list_characteristics) == (len(pol_data[0])-1):
|
||||||
|
for i in range(len(list_characteristics)):
|
||||||
|
f.write(f"{list_characteristics[i]} = {pol_data[0][i]}\n")
|
||||||
|
|
||||||
|
def _export_D90(self, study, repertory, qlog=None, name="0"):
|
||||||
|
|
||||||
|
files = []
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export D90 file")
|
||||||
|
|
||||||
|
with adists_file_open(
|
||||||
|
os.path.join(repertory, f"{name}.D90"), "w+"
|
||||||
|
) as f:
|
||||||
|
files.append(f"{name}.D90")
|
||||||
|
|
||||||
|
f.write(f"*Diamètres caractéristiques du fond stable\n")
|
||||||
|
|
||||||
|
d90AdisTS = study.river.d90_adists.D90_AdisTS_List
|
||||||
|
|
||||||
|
f.write(f"DEFAULT = {d90AdisTS[0].d90}\n")
|
||||||
|
|
||||||
|
self._export_d90_spec(study, d90AdisTS[0]._data, f, qlog)
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
def _export_d90_spec(self, study, d90_spec_data, f, qlog, name="0"):
|
||||||
|
|
||||||
|
for d90_spec in d90_spec_data:
|
||||||
|
if (d90_spec.name is None
|
||||||
|
or d90_spec.reach is None
|
||||||
|
or d90_spec.start_rk is None
|
||||||
|
or d90_spec.end_rk is None
|
||||||
|
or d90_spec.d90 is None):
|
||||||
|
return
|
||||||
|
|
||||||
|
edges = study.river.enable_edges()
|
||||||
|
|
||||||
|
id_edges = list(map(lambda x: x.id, edges))
|
||||||
|
|
||||||
|
id_reach = d90_spec.reach
|
||||||
|
|
||||||
|
if id_reach not in id_edges:
|
||||||
|
return
|
||||||
|
|
||||||
|
f.write(" ".join((f"{d90_spec.name}",
|
||||||
|
"=",
|
||||||
|
f"{id_reach}",
|
||||||
|
f"{d90_spec.start_rk}",
|
||||||
|
f"{d90_spec.end_rk}",
|
||||||
|
f"{d90_spec.d90}\n")))
|
||||||
|
|
||||||
|
def _export_DIF(self, study, repertory, qlog=None, name="0"):
|
||||||
|
|
||||||
|
files = []
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export DIF file")
|
||||||
|
|
||||||
|
with adists_file_open(
|
||||||
|
os.path.join(repertory, f"{name}.DIF"), "w+"
|
||||||
|
) as f:
|
||||||
|
files.append(f"{name}.DIF")
|
||||||
|
|
||||||
|
f.write(f"*Définition des paramètres des fonctions de calcul du\n")
|
||||||
|
f.write(f"*coefficient de diffusion\n")
|
||||||
|
|
||||||
|
difAdisTS = study.river.dif_adists.DIF_AdisTS_List
|
||||||
|
|
||||||
|
if difAdisTS[0].method != "generique":
|
||||||
|
f.write(" ".join((f"defaut = ",
|
||||||
|
f"{difAdisTS[0].method}",
|
||||||
|
f"{difAdisTS[0].dif}\n")))
|
||||||
|
else:
|
||||||
|
f.write(" ".join((f"defaut ="
|
||||||
|
f"{difAdisTS[0].method}",
|
||||||
|
f"{difAdisTS[0].dif}",
|
||||||
|
f"{difAdisTS[0].b}",
|
||||||
|
f"{difAdisTS[0].c}\n")))
|
||||||
|
|
||||||
|
self._export_dif_spec(study, difAdisTS[0]._data, f, qlog)
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
def _export_dif_spec(self, study, dif_spec_data, f, qlog, name="0"):
|
||||||
|
|
||||||
|
for dif_spec in dif_spec_data:
|
||||||
|
if (dif_spec.reach is None
|
||||||
|
or dif_spec.start_rk is None
|
||||||
|
or dif_spec.end_rk is None
|
||||||
|
or dif_spec.dif is None
|
||||||
|
or dif_spec.b is None
|
||||||
|
or dif_spec.c is None):
|
||||||
|
return
|
||||||
|
|
||||||
|
edges = study.river.enable_edges()
|
||||||
|
|
||||||
|
id_edges = list(map(lambda x: x.id, edges))
|
||||||
|
|
||||||
|
id_reach = dif_spec.reach
|
||||||
|
|
||||||
|
if id_reach not in id_edges:
|
||||||
|
return
|
||||||
|
|
||||||
|
if dif_spec.method != "generique":
|
||||||
|
f.write(" ".join((f"{dif_spec.method}",
|
||||||
|
"=",
|
||||||
|
f"{id_reach}",
|
||||||
|
f"{dif_spec.start_rk}",
|
||||||
|
f"{dif_spec.end_rk}",
|
||||||
|
f"{dif_spec.dif}\n")))
|
||||||
|
else:
|
||||||
|
f.write(" ".join((f"{dif_spec.method}",
|
||||||
|
f"=" f"{id_reach}",
|
||||||
|
f"{dif_spec.start_rk}",
|
||||||
|
f"{dif_spec.end_rk}",
|
||||||
|
f"{dif_spec.dif}",
|
||||||
|
f"{dif_spec.b}",
|
||||||
|
f"{dif_spec.c}\n")))
|
||||||
|
|
||||||
|
def _export_NUM(self, study, repertory, qlog=None, name="0"):
|
||||||
|
|
||||||
|
dict_names = {"init_time": "start_date",
|
||||||
|
"final_time": "end_date",
|
||||||
|
"timestep": "dt0",
|
||||||
|
"implicitation_parameter": "theta",
|
||||||
|
"timestep_screen": "dtscr",
|
||||||
|
"timestep_bin": "dtbin",
|
||||||
|
"timestep_csv": "dtcsv",
|
||||||
|
"timestep_mage": "dtMage",
|
||||||
|
"initial_concentration": "c_initiale"}
|
||||||
|
files = []
|
||||||
|
|
||||||
|
if qlog is not None:
|
||||||
|
qlog.put("Export NUM file")
|
||||||
|
|
||||||
|
with adists_file_open(
|
||||||
|
os.path.join(repertory, f"{name}.NUM"), "w+"
|
||||||
|
) as f:
|
||||||
|
files.append(f"{name}.NUM")
|
||||||
|
|
||||||
|
params = study.river.get_params(self.type).parameters
|
||||||
|
for p in params:
|
||||||
|
name = p.name\
|
||||||
|
.replace("all_", "")\
|
||||||
|
.replace("adists_", "")
|
||||||
|
value = p.value
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"export: NUM: {name}: {value} ({p.value})"
|
||||||
|
)
|
||||||
|
|
||||||
|
if name != "command_line_arguments":
|
||||||
|
f.write(f"{dict_names[name]} = {value}\n")
|
||||||
|
|
||||||
|
outputrks = study.river.Output_rk_adists.OutputRK_List
|
||||||
|
|
||||||
|
for outputrk in outputrks:
|
||||||
|
self._export_outputrk(study, outputrk, f, qlog)
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
def _export_outputrk(self, study, outputrk, f, qlog, name="0"):
|
||||||
|
if (outputrk.reach is None
|
||||||
|
or outputrk.rk is None
|
||||||
|
or outputrk.title is None):
|
||||||
|
return
|
||||||
|
|
||||||
|
edges = study.river.enable_edges()
|
||||||
|
|
||||||
|
id_edges = list(map(lambda x: x.id, edges))
|
||||||
|
|
||||||
|
id_reach = outputrk.reach
|
||||||
|
rk = outputrk.rk
|
||||||
|
title = outputrk.title
|
||||||
|
|
||||||
|
if id_reach not in id_edges:
|
||||||
|
return
|
||||||
|
|
||||||
|
f.write(f"output = {id_reach} {rk} {title}\n")
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def read_bin(self, study, repertory, results, qlog=None, name="0"):
|
||||||
|
repertory_results = os.path.join(repertory, "resultats")
|
||||||
|
|
||||||
|
files_bin_names = [el.split("/")[-1]
|
||||||
|
for el in glob.glob(repertory_results+"/*.bin")]
|
||||||
|
print("files names resultats: ", files_bin_names)
|
||||||
|
|
||||||
|
ifilename = os.path.join(repertory_results, files_bin_names[0])
|
||||||
|
|
||||||
|
logger.info(f"read_bin: Start reading '{ifilename}' ...")
|
||||||
|
|
||||||
|
print("reading ", ifilename)
|
||||||
|
with open(ifilename, 'rb') as f:
|
||||||
|
# header
|
||||||
|
# first line
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=3)
|
||||||
|
ibmax = data[0] # number of reaches
|
||||||
|
ismax = data[1] # total number of cross sections
|
||||||
|
kbl = data[2] * -1 # block size for .BIN header
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# second line
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
ibu = np.fromfile(f, dtype=np.int32, count=ibmax)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# third line
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=2 * ibmax)
|
||||||
|
is1 = np.zeros(ibmax, dtype=np.int32)
|
||||||
|
is2 = np.zeros(ibmax, dtype=np.int32)
|
||||||
|
print("nombre de biefs : ", ibmax)
|
||||||
|
|
||||||
|
logger.debug(f"read_bin: nb_reach = {ibmax}")
|
||||||
|
logger.debug(f"read_bin: nb_profile = {ismax}")
|
||||||
|
|
||||||
|
results.set("nb_reach", f"{ibmax}")
|
||||||
|
results.set("nb_profile", f"{ismax}")
|
||||||
|
|
||||||
|
reachs = []
|
||||||
|
iprofiles = {}
|
||||||
|
reach_offset = {}
|
||||||
|
|
||||||
|
for i in range(ibmax):
|
||||||
|
# Add results reach to reach list
|
||||||
|
r = results.river.add(i)
|
||||||
|
reachs.append(r)
|
||||||
|
|
||||||
|
is1[i] = data[2 * i] - 1 # first section of reach i
|
||||||
|
is2[i] = data[2 * i + 1] - 1 # last section of reach i
|
||||||
|
|
||||||
|
key = (is1[i], is2[i])
|
||||||
|
iprofiles[key] = r
|
||||||
|
|
||||||
|
reach_offset[r] = is1[i]
|
||||||
|
|
||||||
|
logger.debug(f"read_bin: iprofiles = {iprofiles}")
|
||||||
|
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# fourth line
|
||||||
|
pk = np.zeros(ismax, dtype=np.float32)
|
||||||
|
for k in range(0, ismax, kbl):
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
pk[k:min(k + kbl, ismax)] = np.fromfile(f,
|
||||||
|
dtype=np.float32,
|
||||||
|
count=min(
|
||||||
|
k + kbl, ismax
|
||||||
|
) - k)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
|
||||||
|
# fifth line (useless)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
zmin_OLD = np.fromfile(f, dtype=np.float32, count=1)[0]
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# sixth line
|
||||||
|
zf = np.zeros(ismax, dtype=np.float32)
|
||||||
|
z = np.zeros(ismax * 3, dtype=np.float32)
|
||||||
|
for k in range(0, ismax, kbl):
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
z[3 * k:3 * min(k + kbl, ismax)] = \
|
||||||
|
np.fromfile(f,
|
||||||
|
dtype=np.float32,
|
||||||
|
count=3 * (min(k + kbl, ismax) - k)
|
||||||
|
)
|
||||||
|
# z[i*3+1] and z[i*3+2] are useless
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
zf = [z[i * 3] for i in range(ismax)]
|
||||||
|
# seventh line (useless)
|
||||||
|
for k in range(0, ismax, kbl):
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
zero = np.fromfile(f, dtype=np.int32, count=ismax)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# end header
|
||||||
|
|
||||||
|
def ip_to_r(i):
|
||||||
|
return iprofiles[
|
||||||
|
next(
|
||||||
|
filter(
|
||||||
|
lambda k: k[0] <= i <= k[1],
|
||||||
|
iprofiles
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def ip_to_ri(r, i): return i - reach_offset[r]
|
||||||
|
|
||||||
|
path_files = map(lambda file: os.path.join(
|
||||||
|
repertory_results, file), files_bin_names)
|
||||||
|
|
||||||
|
data_tmp = {}
|
||||||
|
|
||||||
|
for file_bin in path_files:
|
||||||
|
key_pol = file_bin.split("/")[-1][0:-4]
|
||||||
|
data_tmp[key_pol] = {}
|
||||||
|
with open(file_bin, 'rb') as f:
|
||||||
|
# header
|
||||||
|
# first line
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=3)
|
||||||
|
ibmax = data[0] # number of reaches
|
||||||
|
ismax = data[1] # total number of cross sections
|
||||||
|
kbl = data[2] * -1 # block size for .BIN header
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# second line
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
ibu = np.fromfile(f, dtype=np.int32, count=ibmax)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# third line
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=2 * ibmax)
|
||||||
|
is1 = np.zeros(ibmax, dtype=np.int32)
|
||||||
|
is2 = np.zeros(ibmax, dtype=np.int32)
|
||||||
|
for i in range(ibmax):
|
||||||
|
# first section of reach i (FORTRAN numbering)
|
||||||
|
is1[i] = data[2 * i]
|
||||||
|
# last section of reach i (FORTRAN numbering)
|
||||||
|
is2[i] = data[2 * i + 1]
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# fourth line
|
||||||
|
pk = np.zeros(ismax, dtype=np.float32)
|
||||||
|
for k in range(0, ismax, kbl):
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
pk[k:min(k + kbl, ismax)] = np.fromfile(
|
||||||
|
f, dtype=np.float32, count=min(k + kbl, ismax) - k
|
||||||
|
)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# fifth line (useless)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
zmin_OLD = np.fromfile(f, dtype=np.float32, count=1)[0]
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# sixth line
|
||||||
|
zf = np.zeros(ismax, dtype=np.float32)
|
||||||
|
z = np.zeros(ismax * 3, dtype=np.float32)
|
||||||
|
for k in range(0, ismax, kbl):
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
z[3 * k:3 * min(k + kbl, ismax)] = np.fromfile(
|
||||||
|
f, dtype=np.float32,
|
||||||
|
count=3 * (min(k + kbl, ismax) - k)
|
||||||
|
)
|
||||||
|
# z[i*3+1] and z[i*3+2] are useless
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
zf = [z[i * 3] for i in range(ismax)]
|
||||||
|
# seventh line (useless)
|
||||||
|
for k in range(0, ismax, kbl):
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
zero = np.fromfile(f, dtype=np.int32, count=ismax)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
# end header
|
||||||
|
# data
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
while data.size > 0:
|
||||||
|
ismax = np.fromfile(f, dtype=np.int32, count=1)[0]
|
||||||
|
t = np.fromfile(f, dtype=np.float64, count=1)[0]
|
||||||
|
if t not in data_tmp[key_pol]:
|
||||||
|
data_tmp[key_pol][t] = {}
|
||||||
|
c = np.fromfile(f, dtype=np.byte, count=1)
|
||||||
|
# possible values :
|
||||||
|
# sediment : C, G, M, D, L, N, R
|
||||||
|
# polutant : C
|
||||||
|
phys_var = bytearray(c).decode()
|
||||||
|
data_tmp[key_pol][t][phys_var] = {}
|
||||||
|
real_data = np.fromfile(f, dtype=np.float32, count=ismax)
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
|
||||||
|
data_tmp[key_pol][t][phys_var] = real_data
|
||||||
|
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
|
||||||
|
# end data
|
||||||
|
|
||||||
|
pollutants_keys = list(data_tmp.keys())
|
||||||
|
timestamps_keys = list(data_tmp[pollutants_keys[0]].keys())
|
||||||
|
phys_data_names = list(data_tmp[pollutants_keys[0]]
|
||||||
|
[timestamps_keys[0]].keys())
|
||||||
|
type_pol_index = len(phys_data_names)
|
||||||
|
|
||||||
|
# ##print("pol keys: ", pollutants_keys)
|
||||||
|
# ##print("t keys: ", timestamps_keys)
|
||||||
|
# ##print("phys var: ", phys_data_names)
|
||||||
|
# ##print("phys data names mass zero:",
|
||||||
|
# list(data_tmp[pollutants_keys[1]][timestamps_keys[0]].keys()))
|
||||||
|
# print("set timestamps keys: ", set(timestamps_keys))
|
||||||
|
# print("isma")
|
||||||
|
# ##print("iprofiles: ", iprofiles)
|
||||||
|
|
||||||
|
for r, reach in enumerate(reachs):
|
||||||
|
for i in range(is1[r]-1, is2[r]):
|
||||||
|
p_i = ip_to_ri(reach, i)
|
||||||
|
|
||||||
|
for t_data in timestamps_keys:
|
||||||
|
pol_view = []
|
||||||
|
for pol in pollutants_keys:
|
||||||
|
pol_view.append(tuple(list(map(
|
||||||
|
lambda data_el: data_el[i],
|
||||||
|
list(data_tmp[pol][t_data].values())
|
||||||
|
))))
|
||||||
|
|
||||||
|
reach.set(p_i, t_data, "pols", pol_view)
|
||||||
|
|
||||||
|
results.set("timestamps", set(timestamps_keys))
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def results(self, study, repertory, qlog=None, name=None):
|
||||||
|
self._study = study
|
||||||
|
if name is None:
|
||||||
|
name = study.name.replace(" ", "_")
|
||||||
|
|
||||||
|
print("adist ts results repertory: ", repertory)
|
||||||
|
|
||||||
|
results = super(AdisTSwc, self).results(study,
|
||||||
|
repertory,
|
||||||
|
qlog,
|
||||||
|
name=name)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def export_func_dict(self):
|
||||||
|
return [
|
||||||
|
self._export_NUM,
|
||||||
|
self._export_DIF,
|
||||||
|
self._export_D90,
|
||||||
|
self._export_POLs,
|
||||||
|
]
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def export(self, study, repertory, mage_rep, qlog=None, name="0"):
|
||||||
|
print("cmd solver adistswc : ", self._cmd_solver)
|
||||||
|
self._study = study
|
||||||
|
name = study.name.replace(" ", "_")
|
||||||
|
|
||||||
|
# Generate files
|
||||||
|
files = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
for func in self.export_func_dict():
|
||||||
|
files = files + func(study, repertory, qlog, name=name)
|
||||||
|
|
||||||
|
self.export_additional_files(study, repertory, qlog, name=name)
|
||||||
|
self._export_REP(study, repertory, mage_rep,
|
||||||
|
files, qlog, name=name)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to export study to {self._type}")
|
||||||
|
logger_exception(e)
|
||||||
|
return False
|
||||||
@@ -1001,6 +1001,7 @@ class Mage8(Mage):
|
|||||||
|
|
||||||
# Add profile id correspondance to reach
|
# Add profile id correspondance to reach
|
||||||
key = (i1, i2)
|
key = (i1, i2)
|
||||||
|
print("mage keys reachs: ", key)
|
||||||
iprofiles[key] = r
|
iprofiles[key] = r
|
||||||
|
|
||||||
# Profile ID offset
|
# Profile ID offset
|
||||||
@@ -1033,6 +1034,8 @@ class Mage8(Mage):
|
|||||||
]
|
]
|
||||||
def ip_to_ri(r, i): return i - reach_offset[r]
|
def ip_to_ri(r, i): return i - reach_offset[r]
|
||||||
|
|
||||||
|
print("mage iprofiles: ", iprofiles)
|
||||||
|
|
||||||
ts = set()
|
ts = set()
|
||||||
end = False
|
end = False
|
||||||
while not end:
|
while not end:
|
||||||
@@ -1068,6 +1071,7 @@ class Mage8(Mage):
|
|||||||
|
|
||||||
logger.debug(reachs[0].profiles[0]._data)
|
logger.debug(reachs[0].profiles[0]._data)
|
||||||
results.set("timestamps", ts)
|
results.set("timestamps", ts)
|
||||||
|
print("set timestamps mage: ", ts)
|
||||||
logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
|
logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from Solver.GenericSolver import GenericSolver
|
|||||||
from Solver.Mage import (
|
from Solver.Mage import (
|
||||||
Mage7, Mage8, MageFake7,
|
Mage7, Mage8, MageFake7,
|
||||||
)
|
)
|
||||||
|
from Solver.AdisTS import AdisTSwc
|
||||||
from Solver.RubarBE import Rubar3, RubarBE
|
from Solver.RubarBE import Rubar3, RubarBE
|
||||||
|
|
||||||
_translate = QCoreApplication.translate
|
_translate = QCoreApplication.translate
|
||||||
@@ -31,6 +32,7 @@ solver_long_name = {
|
|||||||
# "mage7": "Mage v7",
|
# "mage7": "Mage v7",
|
||||||
"mage8": "Mage v8",
|
"mage8": "Mage v8",
|
||||||
# "mage_fake7": "Mage fake v7",
|
# "mage_fake7": "Mage fake v7",
|
||||||
|
"adistswc": "Adis-TS_WC",
|
||||||
# "rubarbe": "RubarBE",
|
# "rubarbe": "RubarBE",
|
||||||
# "rubar3": "Rubar3",
|
# "rubar3": "Rubar3",
|
||||||
}
|
}
|
||||||
@@ -40,6 +42,7 @@ solver_type_list = {
|
|||||||
# "mage7": Mage7,
|
# "mage7": Mage7,
|
||||||
"mage8": Mage8,
|
"mage8": Mage8,
|
||||||
# "mage_fake7": MageFake7,
|
# "mage_fake7": MageFake7,
|
||||||
|
"adistswc": AdisTSwc,
|
||||||
# "rubarbe": RubarBE,
|
# "rubarbe": RubarBE,
|
||||||
# "rubar3": Rubar3,
|
# "rubar3": Rubar3,
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ class BoundaryConditionWindow(PamhyrWindow):
|
|||||||
def setup_table(self):
|
def setup_table(self):
|
||||||
self._table = {}
|
self._table = {}
|
||||||
|
|
||||||
for t in ["liquid", "solid", "suspenssion"]:
|
for t in ["liquid", "solid"]:
|
||||||
self._delegate_type = ComboBoxDelegate(
|
self._delegate_type = ComboBoxDelegate(
|
||||||
trad=self._trad,
|
trad=self._trad,
|
||||||
data=self._study.river,
|
data=self._study.river,
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Plot.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
QCoreApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class Plot(PamhyrPlot):
|
||||||
|
def __init__(self, mode="time", data=None,
|
||||||
|
trad=None, canvas=None, toolbar=None,
|
||||||
|
parent=None):
|
||||||
|
super(Plot, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
data=data,
|
||||||
|
toolbar=toolbar,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table_headers = self._trad.get_dict("table_headers")
|
||||||
|
|
||||||
|
header = self.data.header
|
||||||
|
|
||||||
|
self.label_x = self._table_headers[header[0]]
|
||||||
|
self.label_y = self._table_headers[header[1]]
|
||||||
|
|
||||||
|
self._mode = mode
|
||||||
|
self._isometric_axis = False
|
||||||
|
|
||||||
|
self._auto_relim_update = True
|
||||||
|
self._autoscale_update = True
|
||||||
|
|
||||||
|
def custom_ticks(self):
|
||||||
|
if self.data.header[0] != "time":
|
||||||
|
return
|
||||||
|
|
||||||
|
self.set_ticks_time_formater()
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def draw(self):
|
||||||
|
self.init_axes()
|
||||||
|
|
||||||
|
if len(self.data) == 0:
|
||||||
|
self._init = False
|
||||||
|
return
|
||||||
|
|
||||||
|
self.draw_data()
|
||||||
|
self.custom_ticks()
|
||||||
|
|
||||||
|
self.idle()
|
||||||
|
self._init = True
|
||||||
|
|
||||||
|
def draw_data(self):
|
||||||
|
# Plot data
|
||||||
|
x = list(map(lambda v: v[0], self.data.data))
|
||||||
|
y = list(map(lambda v: v[1], self.data.data))
|
||||||
|
|
||||||
|
self._line, = self.canvas.axes.plot(
|
||||||
|
x, y,
|
||||||
|
color=self.color_plot,
|
||||||
|
**self.plot_default_kargs
|
||||||
|
)
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def update(self, ind=None):
|
||||||
|
if not self._init:
|
||||||
|
self.draw()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.update_data()
|
||||||
|
|
||||||
|
self.update_idle()
|
||||||
|
|
||||||
|
def update_data(self):
|
||||||
|
x = list(map(lambda v: v[0], self.data.data))
|
||||||
|
y = list(map(lambda v: v[1], self.data.data))
|
||||||
|
|
||||||
|
self._line.set_data(x, y)
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from datetime import date, time, datetime, timedelta
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer,
|
||||||
|
timestamp_to_old_pamhyr_date,
|
||||||
|
old_pamhyr_date_to_timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect, QTime, QDateTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QTableView, QAbstractItemView, QSpinBox,
|
||||||
|
QTimeEdit, QDateTimeEdit, QItemDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.BoundaryConditionsAdisTS.Edit.UndoCommand import (
|
||||||
|
AddCommand, DelCommand, SetDataCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.TextAlignmentRole:
|
||||||
|
return Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
value = QVariant()
|
||||||
|
|
||||||
|
if 0 <= column < 2:
|
||||||
|
v = self._data._data[row][column]
|
||||||
|
if self._data._types[column] == float:
|
||||||
|
value = f"{v:.4f}"
|
||||||
|
elif self._data.header[column] == "time":
|
||||||
|
if self._opt_data == "time":
|
||||||
|
value = timestamp_to_old_pamhyr_date(int(v))
|
||||||
|
else:
|
||||||
|
value = str(datetime.fromtimestamp(v))
|
||||||
|
else:
|
||||||
|
value = f"{v}"
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._undo.push(
|
||||||
|
SetDataCommand(
|
||||||
|
self._data, row, column, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
||||||
|
import BoundaryConditionAdisTS
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SetDataCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._column = column
|
||||||
|
self._old = self._data._data[self._index][self._column]
|
||||||
|
_type = self._data._types[self._column]
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == 0:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._old, self._data._data[self._index][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._data._data[self._index][0], self._old
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == 0:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._new, self._data._data[self._index][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._data._data[self._index][0], self._new
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
del self._data._data[self._index]
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._data._data.insert(self._index, (
|
||||||
|
self._data._types[0](0), self._data._types[1](0.0)
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
self._data._data.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._bc = []
|
||||||
|
for row in rows:
|
||||||
|
self._bc.append((row, self._data._data[row]))
|
||||||
|
self._bc.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._bc:
|
||||||
|
self._data._data.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for row in self._rows:
|
||||||
|
del self._data._data[row]
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
from View.Tools.PamhyrWidget import PamhyrWidget
|
||||||
|
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
pyqtSlot, pyqtSignal,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView, QDoubleSpinBox, QVBoxLayout,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||||
|
|
||||||
|
from View.BoundaryConditionsAdisTS.Edit.translate import BCETranslate
|
||||||
|
from View.BoundaryConditionsAdisTS.Edit.Table import TableModel
|
||||||
|
from View.BoundaryConditionsAdisTS.Edit.Plot import Plot
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class EditBoundaryConditionWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "EditBoundaryConditionsAdisTS"
|
||||||
|
_pamhyr_name = "Edit Boundary Conditions AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, data=None, study=None, config=None, parent=None):
|
||||||
|
self._data = data
|
||||||
|
trad = BCETranslate()
|
||||||
|
|
||||||
|
name = trad[self._pamhyr_name]
|
||||||
|
|
||||||
|
super(EditBoundaryConditionWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._data is not None:
|
||||||
|
n = self._data.node
|
||||||
|
node_name = next(filter(
|
||||||
|
lambda x: x.id == n, self._study.river._nodes
|
||||||
|
)).name
|
||||||
|
name += (
|
||||||
|
f" - {study.name} " +
|
||||||
|
f"({node_name})"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_plot()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
if self._data.type == "Concentration":
|
||||||
|
self._data.header = ["time", "concentration"]
|
||||||
|
else:
|
||||||
|
self._data.header = ["time", "rate"]
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
table_headers = self._trad.get_dict("table_headers")
|
||||||
|
for h in self._data.header:
|
||||||
|
headers[h] = table_headers[h]
|
||||||
|
|
||||||
|
self._delegate_time = PamhyrExTimeDelegate(
|
||||||
|
data=self._data,
|
||||||
|
mode=self._study.time_system,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=headers,
|
||||||
|
editable_headers=self._data.header,
|
||||||
|
delegates={
|
||||||
|
# "time": self._delegate_time,
|
||||||
|
},
|
||||||
|
data=self._data,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
opt_data=self._study.time_system
|
||||||
|
)
|
||||||
|
|
||||||
|
table.setModel(self._table)
|
||||||
|
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
table.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
def setup_plot(self):
|
||||||
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas.setObjectName("canvas")
|
||||||
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
|
self.canvas, self
|
||||||
|
)
|
||||||
|
self.verticalLayout.addWidget(self.toolbar)
|
||||||
|
self.verticalLayout.addWidget(self.canvas)
|
||||||
|
|
||||||
|
self.plot = Plot(
|
||||||
|
canvas=self.canvas,
|
||||||
|
data=self._data,
|
||||||
|
mode=self._study.time_system,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar,
|
||||||
|
)
|
||||||
|
self.plot.draw()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self.update)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return table.selectionModel()\
|
||||||
|
.selectedRows()[0]\
|
||||||
|
.row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._data) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def sort(self):
|
||||||
|
self._table.sort(False)
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
table = []
|
||||||
|
table.append(self._data.header)
|
||||||
|
|
||||||
|
data = self._data.data
|
||||||
|
for row in rows:
|
||||||
|
table.append(list(data[row]))
|
||||||
|
|
||||||
|
self.copyTableIntoClipboard(table)
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
self.plot.update()
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
from View.BoundaryCondition.translate import BCTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class BCETranslate(BCTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(BCETranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Edit Boundary Conditions AdisTS"] = _translate(
|
||||||
|
"BoundaryConditionAdisTS", "Edit boundary conditions AdisTS"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"time": self._dict["time"],
|
||||||
|
"date": self._dict["date"],
|
||||||
|
"rate": _translate("BoundaryConditionAdisTS", "Rate"),
|
||||||
|
"concentration": _translate("BoundaryConditionAdisTS",
|
||||||
|
"Concentration"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||||
|
NotDefined, PonctualContribution,
|
||||||
|
TimeOverZ, TimeOverDischarge, ZOverDischarge
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.BoundaryConditionsAdisTS.UndoCommand import (
|
||||||
|
SetNodeCommand, SetTypeCommand,
|
||||||
|
AddCommand, DelCommand, SetPolCommand
|
||||||
|
)
|
||||||
|
from View.BoundaryCondition.translate import BC_types
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, mode="type", tab="",
|
||||||
|
trad=None, parent=None):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._mode = mode
|
||||||
|
self._tab = tab
|
||||||
|
self._trad = trad
|
||||||
|
|
||||||
|
self._long_types = {}
|
||||||
|
if self._trad is not None:
|
||||||
|
self._long_types = self._trad.get_dict("long_types")
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
if self._mode == "type":
|
||||||
|
lst = [self._trad["not_associated"], "Concentration", "Rate"]
|
||||||
|
self.editor.addItems(
|
||||||
|
lst
|
||||||
|
)
|
||||||
|
elif self._mode == "node":
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad["not_associated"]] +
|
||||||
|
self._data.nodes_names()
|
||||||
|
)
|
||||||
|
elif self._mode == "pol":
|
||||||
|
lst = [p.name for p in self._data._Pollutants.Pollutants_List]
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad["not_associated"]] +
|
||||||
|
lst
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(index.data(Qt.DisplayRole))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, pollutant=None, bc_list=None, trad=None, **kwargs):
|
||||||
|
self._trad = trad
|
||||||
|
self._bc_list = bc_list
|
||||||
|
self._pollutant = pollutant
|
||||||
|
|
||||||
|
super(TableModel, self).__init__(trad=trad, **kwargs)
|
||||||
|
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._bc_list.lst
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if len(self._lst) != 0:
|
||||||
|
data = self._lst
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] == "type":
|
||||||
|
n = data[row].type
|
||||||
|
if n is None or n == "":
|
||||||
|
return self._trad["not_associated"]
|
||||||
|
return n
|
||||||
|
elif self._headers[column] == "node":
|
||||||
|
n = data[row].node
|
||||||
|
if n is None:
|
||||||
|
return self._trad["not_associated"]
|
||||||
|
tmp = next(filter(lambda x: x.id == n, self._data._nodes),
|
||||||
|
None)
|
||||||
|
if tmp is not None:
|
||||||
|
return tmp.name
|
||||||
|
else:
|
||||||
|
return self._trad["not_associated"]
|
||||||
|
elif self._headers[column] == "pol":
|
||||||
|
n = data[row].pollutant
|
||||||
|
if n is None or n == "not_associated" or n == "":
|
||||||
|
return self._trad["not_associated"]
|
||||||
|
return self._data._Pollutants.Pollutants_List[n].name
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "type":
|
||||||
|
self._undo.push(
|
||||||
|
SetTypeCommand(
|
||||||
|
self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "node":
|
||||||
|
self._undo.push(
|
||||||
|
SetNodeCommand(
|
||||||
|
self._lst, row, self._data.node(value)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "pol":
|
||||||
|
pol = next(filter(lambda x: x.name == value,
|
||||||
|
self._data._Pollutants.Pollutants_List))
|
||||||
|
self._undo.push(
|
||||||
|
SetPolCommand(
|
||||||
|
self._lst, row, pol.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._pollutant, self._bc_list, self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
||||||
|
import BoundaryConditionAdisTS
|
||||||
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionsAdisTSList \
|
||||||
|
import BoundaryConditionsAdisTSList
|
||||||
|
|
||||||
|
|
||||||
|
class SetNodeCommand(QUndoCommand):
|
||||||
|
def __init__(self, bcs, index, node):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._bcs = bcs
|
||||||
|
self._index = index
|
||||||
|
self._old = self._bcs[self._index].node
|
||||||
|
self._new = node.id
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._bcs[self._index].node = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._bcs[self._index].node = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetTypeCommand(QUndoCommand):
|
||||||
|
def __init__(self, bcs, index, _type):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._bcs = bcs
|
||||||
|
self._index = index
|
||||||
|
self._type = _type
|
||||||
|
self._old = self._bcs[self._index].type
|
||||||
|
self._new = self._type
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._bcs[self._index].type = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._bcs[self._index].type = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetPolCommand(QUndoCommand):
|
||||||
|
def __init__(self, bcs, index, pollutant):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._bcs = bcs
|
||||||
|
self._index = index
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._old = self._bcs[self._index].pollutant
|
||||||
|
self._new = self._pollutant
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._bcs[self._index].pollutant = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._bcs[self._index].pollutant = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutant, bcs_list, bcs, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._bcs = bcs
|
||||||
|
self._bc_list = bcs_list
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
del self._bcs[self._index]
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._bc_list.new(self._index, self._pollutant)
|
||||||
|
else:
|
||||||
|
self._bcs.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, bcs, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._bcs = bcs
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._bc = []
|
||||||
|
for row in rows:
|
||||||
|
self._bc.append((row, self._bcs[row]))
|
||||||
|
self._bc.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._bc:
|
||||||
|
self._bcs.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for row in self._rows:
|
||||||
|
del self._bcs[row]
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer, logger_exception
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.BoundaryConditionsAdisTS.Table import (
|
||||||
|
TableModel, ComboBoxDelegate
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Network.GraphWidget import GraphWidget
|
||||||
|
from View.BoundaryConditionsAdisTS.translate import BCAdisTSTranslate
|
||||||
|
from View.BoundaryConditionsAdisTS.Edit.Window \
|
||||||
|
import EditBoundaryConditionWindow
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class BoundaryConditionAdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "BoundaryConditionsAdisTS"
|
||||||
|
_pamhyr_name = "Boundary conditions AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, study=None, config=None, parent=None):
|
||||||
|
trad = BCAdisTSTranslate()
|
||||||
|
name = (
|
||||||
|
trad[self._pamhyr_name] +
|
||||||
|
" - " + study.name
|
||||||
|
)
|
||||||
|
|
||||||
|
super(BoundaryConditionAdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._pollutants_lst = self._study._river._Pollutants
|
||||||
|
|
||||||
|
self._bcs = self._study.river.boundary_conditions_adists
|
||||||
|
|
||||||
|
self.setup_graph()
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
self.ui.setWindowTitle(self._title)
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._delegate_type = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
mode="type",
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self._delegate_node = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
mode="node",
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self._delegate_pol = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
mode="pol",
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["type", "node", "pol"],
|
||||||
|
delegates={
|
||||||
|
"type": self._delegate_type,
|
||||||
|
"node": self._delegate_node,
|
||||||
|
"pol": self._delegate_pol,
|
||||||
|
},
|
||||||
|
trad=self._trad,
|
||||||
|
bc_list=self._study.river.boundary_conditions_adists,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
data=self._study.river
|
||||||
|
)
|
||||||
|
table.setModel(self._table)
|
||||||
|
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
table.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
def setup_graph(self):
|
||||||
|
self.graph_widget = GraphWidget(
|
||||||
|
self._study.river,
|
||||||
|
min_size=None, size=(200, 200),
|
||||||
|
only_display=True,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self.graph_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||||
|
self.graph_layout.addWidget(self.graph_widget)
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||||
|
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
return table.selectionModel()\
|
||||||
|
.selectedRows()[0]\
|
||||||
|
.row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._bcs) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
||||||
|
def edit(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
for row in rows:
|
||||||
|
data = self._bcs.lst[row]
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
EditBoundaryConditionWindow,
|
||||||
|
data=[self._study, None, data]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
win = EditBoundaryConditionWindow(
|
||||||
|
data=data,
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
win.show()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class BCAdisTSTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(BCAdisTSTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Boundary conditions AdisTS"] = _translate(
|
||||||
|
"BoundaryConditionsAdisTS", "Boundary conditions AdisTS"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"type": self._dict["type"],
|
||||||
|
"node": _translate("BoundaryCondition", "Node"),
|
||||||
|
"pol": _translate("BoundaryCondition", "Pollutant")
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
# WindowAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, QRect, QThread,
|
||||||
|
pyqtSlot, pyqtSignal,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
QProgressBar, QLabel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.CheckList.Table import TableModel
|
||||||
|
from View.CheckList.Worker import Worker
|
||||||
|
from View.CheckList.Translate import CheckListTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class CheckListWindowAdisTS(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "CheckList"
|
||||||
|
_pamhyr_name = "Check list"
|
||||||
|
|
||||||
|
signalStatus = pyqtSignal(str)
|
||||||
|
|
||||||
|
def __init__(self, autorun: bool = True,
|
||||||
|
study=None, config=None,
|
||||||
|
solver=None, parent=None, mage_rep=None):
|
||||||
|
trad = CheckListTranslate()
|
||||||
|
|
||||||
|
self._autorun = autorun
|
||||||
|
self._solver = solver
|
||||||
|
self._mage_rep = mage_rep
|
||||||
|
|
||||||
|
name = trad[self._pamhyr_name] + " - " + study.name
|
||||||
|
|
||||||
|
super(CheckListWindowAdisTS, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
options=[],
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add solver to hash computation data
|
||||||
|
self._hash_data.append(self._solver)
|
||||||
|
|
||||||
|
self._checker_list = (
|
||||||
|
self._study.checkers() +
|
||||||
|
self._solver.checkers()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_progress_bar()
|
||||||
|
self.setup_connections()
|
||||||
|
self.setup_thread()
|
||||||
|
self.setup_statusbar()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
data=self._checker_list,
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup_progress_bar(self):
|
||||||
|
self._progress = self.find(QProgressBar, f"progressBar")
|
||||||
|
self._p = 0 # Progress current step
|
||||||
|
|
||||||
|
self._progress.setRange(0, len(self._checker_list))
|
||||||
|
self._progress.setValue(self._p)
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
|
||||||
|
self.find(QPushButton, "pushButton_retry").clicked.connect(self.retry)
|
||||||
|
self.find(QPushButton, "pushButton_cancel")\
|
||||||
|
.clicked.connect(self.reject)
|
||||||
|
|
||||||
|
def setup_thread(self):
|
||||||
|
self._worker = Worker(self._study, self._checker_list)
|
||||||
|
self._worker_thread = QThread()
|
||||||
|
self._worker.moveToThread(self._worker_thread)
|
||||||
|
|
||||||
|
# Connect any worker signals
|
||||||
|
self._worker.signalStatus.connect(self.update)
|
||||||
|
self._worker_thread.started.connect(self._worker.process)
|
||||||
|
|
||||||
|
self._worker_thread.start()
|
||||||
|
|
||||||
|
def retry(self):
|
||||||
|
self._worker_thread.terminate()
|
||||||
|
self._worker_thread.wait()
|
||||||
|
|
||||||
|
self.find(QPushButton, "pushButton_retry").setEnabled(False)
|
||||||
|
self.find(QPushButton, "pushButton_ok").setEnabled(False)
|
||||||
|
|
||||||
|
self.setup_thread()
|
||||||
|
|
||||||
|
def _compute_status(self):
|
||||||
|
ok = len(list(filter(lambda c: c.is_ok(), self._checker_list)))
|
||||||
|
warning = len(
|
||||||
|
list(filter(lambda c: c.is_warning(), self._checker_list)))
|
||||||
|
error = len(list(filter(lambda c: c.is_error(), self._checker_list)))
|
||||||
|
|
||||||
|
return ok, warning, error
|
||||||
|
|
||||||
|
def _compute_status_label(self):
|
||||||
|
ok, warning, error = self._compute_status()
|
||||||
|
return (f"<font color=\"Green\">Ok: {ok} </font> |" +
|
||||||
|
f"<font color=\"Orange\">Warning: {warning} </font> |" +
|
||||||
|
f"<font color=\"Red\">Error: {error}</font>")
|
||||||
|
|
||||||
|
def setup_statusbar(self):
|
||||||
|
txt = self._compute_status_label()
|
||||||
|
self._status_label = QLabel(txt)
|
||||||
|
self.statusbar.addPermanentWidget(self._status_label)
|
||||||
|
|
||||||
|
def update_statusbar(self):
|
||||||
|
txt = self._compute_status_label()
|
||||||
|
self._status_label.setText(txt)
|
||||||
|
|
||||||
|
def progress(self):
|
||||||
|
self._p += 1
|
||||||
|
self._progress.setValue(self._p)
|
||||||
|
self._table.update()
|
||||||
|
|
||||||
|
def start_compute(self):
|
||||||
|
self._p = 0
|
||||||
|
self._progress.setValue(self._p)
|
||||||
|
|
||||||
|
def info_compute(self, str):
|
||||||
|
self.statusbar.showMessage(str, 3000)
|
||||||
|
|
||||||
|
def end_compute(self):
|
||||||
|
self._table.layoutChanged.emit()
|
||||||
|
self.find(QPushButton, "pushButton_retry").setEnabled(True)
|
||||||
|
|
||||||
|
errors = any(filter(lambda c: c.is_error(), self._checker_list))
|
||||||
|
if not errors:
|
||||||
|
self.find(QPushButton, "pushButton_ok").setEnabled(True)
|
||||||
|
if self._autorun:
|
||||||
|
self._parent.solver_log_adists(self._solver, self._mage_rep)
|
||||||
|
self.end()
|
||||||
|
|
||||||
|
self.update_statusbar()
|
||||||
|
|
||||||
|
def update(self, key: str):
|
||||||
|
if key == "start":
|
||||||
|
self.start_compute()
|
||||||
|
self.info_compute("Starting ...")
|
||||||
|
elif key == "end":
|
||||||
|
self.info_compute("Finish")
|
||||||
|
self.end_compute()
|
||||||
|
elif key == "progress":
|
||||||
|
self.progress()
|
||||||
|
else:
|
||||||
|
self.info_compute(key)
|
||||||
|
|
||||||
|
self.update_statusbar()
|
||||||
|
|
||||||
|
def end(self):
|
||||||
|
# self._worker.join()b
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
self.end()
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
self._parent.solver_log_adists(self._solver, self._mage_rep)
|
||||||
|
# self.end()
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.D90AdisTS.UndoCommand import (
|
||||||
|
SetCommand, AddCommand, SetCommandSpec,
|
||||||
|
DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, ic_spec_lst=None,
|
||||||
|
trad=None, parent=None, mode="reaches"):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._mode = mode
|
||||||
|
self._trad = trad
|
||||||
|
self._ic_spec_lst = ic_spec_lst
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
val = []
|
||||||
|
if self._mode == "rk":
|
||||||
|
reach_id = self._ic_spec_lst[index.row()].reach
|
||||||
|
|
||||||
|
reach = next(filter(
|
||||||
|
lambda edge: edge.id == reach_id, self._data.edges()
|
||||||
|
))
|
||||||
|
|
||||||
|
if reach_id is not None:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda rk: str(rk), reach.reach.get_rk()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda n: n.name, self._data.edges()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad['not_associated']] +
|
||||||
|
val
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class D90TableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, river=None, data=None, **kwargs):
|
||||||
|
self._river = river
|
||||||
|
|
||||||
|
super(D90TableModel, self).__init__(data=data, **kwargs)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data._data
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] is "name":
|
||||||
|
n = self._lst[row].name
|
||||||
|
if n is None or n == "":
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "reach":
|
||||||
|
n = self._lst[row].reach
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return next(filter(
|
||||||
|
lambda edge: edge.id == n, self._river.edges()
|
||||||
|
)).name
|
||||||
|
elif self._headers[column] is "start_rk":
|
||||||
|
n = self._lst[row].start_rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "end_rk":
|
||||||
|
n = self._lst[row].end_rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "d90":
|
||||||
|
n = self._lst[row].d90
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] != "reach":
|
||||||
|
self._undo.push(
|
||||||
|
SetCommandSpec(
|
||||||
|
self._lst, row, self._headers[column], value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
print(self._river.edge(value).id)
|
||||||
|
self._undo.push(
|
||||||
|
SetCommandSpec(
|
||||||
|
self._lst, row, self._headers[column],
|
||||||
|
self._river.edge(value).id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.D90AdisTS.UndoCommand import (
|
||||||
|
SetCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class D90TableDefaultModel(PamhyrTableModel):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(D90TableDefaultModel, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] is "name":
|
||||||
|
return self._data[row].name
|
||||||
|
elif self._headers[column] is "d90":
|
||||||
|
n = self._data[row].d90
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] is not None:
|
||||||
|
self._undo.push(
|
||||||
|
SetCommand(
|
||||||
|
self._data, row, self._headers[column], value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.D90AdisTS.D90AdisTS import D90AdisTS
|
||||||
|
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
|
||||||
|
|
||||||
|
|
||||||
|
class SetCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, row, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._column = column
|
||||||
|
|
||||||
|
if self._column == "name":
|
||||||
|
self._old = self._data[self._row].name
|
||||||
|
elif self._column == "d90":
|
||||||
|
self._old = self._data[self._row].d90
|
||||||
|
|
||||||
|
_type = float
|
||||||
|
if column == "name":
|
||||||
|
_type = str
|
||||||
|
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._old
|
||||||
|
elif self._column == "d90":
|
||||||
|
self._data[self._row].d90 = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._new
|
||||||
|
elif self._column == "d90":
|
||||||
|
self._data[self._row].d90 = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetCommandSpec(QUndoCommand):
|
||||||
|
def __init__(self, data, row, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._column = column
|
||||||
|
|
||||||
|
if self._column == "name":
|
||||||
|
self._old = self._data[self._row].name
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._old = self._data[self._row].reach
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._old = self._data[self._row].start_rk
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._old = self._data[self._row].end_rk
|
||||||
|
elif self._column == "d90":
|
||||||
|
self._old = self._data[self._row].d90
|
||||||
|
|
||||||
|
_type = float
|
||||||
|
if column == "name":
|
||||||
|
_type = str
|
||||||
|
elif column == "reach":
|
||||||
|
_type = int
|
||||||
|
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._old
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._data[self._row].reach = self._old
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._data[self._row].start_rk = self._old
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._data[self._row].end_rk = self._old
|
||||||
|
elif self._column == "d90":
|
||||||
|
self._data[self._row].d90 = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._new
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._data[self._row].reach = self._new
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._data[self._row].start_rk = self._new
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._data[self._row].end_rk = self._new
|
||||||
|
elif self._column == "d90":
|
||||||
|
self._data[self._row].d90 = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, ics_spec, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._ics_spec = ics_spec
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._data.new(self._index)
|
||||||
|
else:
|
||||||
|
self._data.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, ics_spec, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._ics_spec = ics_spec
|
||||||
|
self._rows = rows
|
||||||
|
# self._data = data
|
||||||
|
|
||||||
|
self._ic = []
|
||||||
|
for row in rows:
|
||||||
|
self._ic.append((row, self._ics_spec[row]))
|
||||||
|
self._ic.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._ic:
|
||||||
|
self._data.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data.delete_i(self._rows)
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer, logger_exception
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence, QIcon,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
QVBoxLayout, QToolBar, QAction, QToolButton,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Modules import Modules
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.UndoCommand import (
|
||||||
|
SetCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.D90AdisTS.TableDefault import (
|
||||||
|
D90TableDefaultModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.D90AdisTS.Table import (
|
||||||
|
D90TableModel, ComboBoxDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.D90AdisTS.translate import D90AdisTSTranslate
|
||||||
|
|
||||||
|
from Solver.Mage import Mage8
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class D90AdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "D90AdisTS"
|
||||||
|
_pamhyr_name = "D90 AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, data=None, study=None, config=None, parent=None):
|
||||||
|
self._data = []
|
||||||
|
self._data.append(data)
|
||||||
|
trad = D90AdisTSTranslate()
|
||||||
|
|
||||||
|
name = (
|
||||||
|
trad[self._pamhyr_name] +
|
||||||
|
" - " + study.name +
|
||||||
|
" - " + self._data[0].name
|
||||||
|
)
|
||||||
|
|
||||||
|
super(D90AdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self._d90_adists_lst = study.river.d90_adists
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
|
||||||
|
self.ui.setWindowTitle(self._title)
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
|
||||||
|
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
|
||||||
|
|
||||||
|
table_default = self.find(QTableView, f"tableView")
|
||||||
|
|
||||||
|
self._table = D90TableDefaultModel(
|
||||||
|
table_view=table_default,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["name", "d90"],
|
||||||
|
delegates={},
|
||||||
|
data=self._data,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad
|
||||||
|
)
|
||||||
|
|
||||||
|
table_default.setModel(self._table)
|
||||||
|
table_default.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table_default.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
table_default.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
layout = self.find(QVBoxLayout, f"verticalLayout_1")
|
||||||
|
toolBar = QToolBar()
|
||||||
|
layout.addWidget(toolBar)
|
||||||
|
|
||||||
|
action_add = QAction(self)
|
||||||
|
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
|
||||||
|
action_add.triggered.connect(self.add)
|
||||||
|
action_delete = QAction(self)
|
||||||
|
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
|
||||||
|
action_delete.triggered.connect(self.delete)
|
||||||
|
|
||||||
|
toolBar.addAction(action_add)
|
||||||
|
toolBar.addAction(action_delete)
|
||||||
|
|
||||||
|
self.table_spec = QTableView()
|
||||||
|
layout.addWidget(self.table_spec)
|
||||||
|
|
||||||
|
self._delegate_reach = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="reaches"
|
||||||
|
)
|
||||||
|
self._delegate_rk = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="rk"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table_spec = D90TableModel(
|
||||||
|
table_view=self.table_spec,
|
||||||
|
table_headers=self._trad.get_dict("table_headers_spec"),
|
||||||
|
editable_headers=["name", "reach", "start_rk", "end_rk", "d90"],
|
||||||
|
delegates={
|
||||||
|
"reach": self._delegate_reach,
|
||||||
|
"start_rk": self._delegate_rk,
|
||||||
|
"end_rk": self._delegate_rk
|
||||||
|
},
|
||||||
|
data=self._data[0],
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad,
|
||||||
|
river=self._study.river
|
||||||
|
)
|
||||||
|
|
||||||
|
self.table_spec.setModel(self._table_spec)
|
||||||
|
self.table_spec.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
self.table_spec.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
self.table_spec.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
selectionModel = self.table_spec.selectionModel()
|
||||||
|
index = self.table_spec.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
self.table_spec.scrollTo(index)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
# table = self.find(QTableView, f"tableView")
|
||||||
|
table = self.table_spec
|
||||||
|
rows = table.selectionModel()\
|
||||||
|
.selectedRows()
|
||||||
|
|
||||||
|
if len(rows) == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return rows[0].row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
# table = self.find(QTableView, f"tableView")
|
||||||
|
table = self.table_spec
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def move_up(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_up(row)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def move_down(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_down(row)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
rows = list(
|
||||||
|
map(
|
||||||
|
lambda row: row.row(),
|
||||||
|
self.tableView.selectionModel().selectedRows()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
table = list(
|
||||||
|
map(
|
||||||
|
lambda eic: list(
|
||||||
|
map(
|
||||||
|
lambda k: eic[1][k],
|
||||||
|
["rk", "discharge", "elevation"]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
filter(
|
||||||
|
lambda eic: eic[0] in rows,
|
||||||
|
enumerate(self._ics.lst())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.copyTableIntoClipboard(table)
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
header, data = self.parseClipboardTable()
|
||||||
|
|
||||||
|
if len(data) + len(header) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"D90: Paste: " +
|
||||||
|
f"header = {header}, " +
|
||||||
|
f"data = {data}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
row = self.index_selected_row()
|
||||||
|
# self._table.paste(row, header, data)
|
||||||
|
self._table.paste(row, [], data)
|
||||||
|
except Exception as e:
|
||||||
|
logger_exception(e)
|
||||||
|
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._data[0]._data) == 0 or len(rows) == 0:
|
||||||
|
self._table_spec.add(0)
|
||||||
|
else:
|
||||||
|
self._table_spec.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
print("del")
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
print("len 0")
|
||||||
|
return
|
||||||
|
self._table_spec.delete(rows)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class D90AdisTSTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(D90AdisTSTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["D90 AdisTS"] = _translate(
|
||||||
|
"D90AdisTS", "D90 AdisTS")
|
||||||
|
|
||||||
|
self._dict["rk"] = self._dict["unit_rk"]
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"name": self._dict["name"],
|
||||||
|
"d90": _translate("Unit", "D90"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_spec"] = {
|
||||||
|
"name": self._dict["name"],
|
||||||
|
"reach": self._dict["reach"],
|
||||||
|
"start_rk": _translate("Unit", "Start_RK (m)"),
|
||||||
|
"end_rk": _translate("Unit", "End_RK (m)"),
|
||||||
|
"d90": _translate("Unit", "D90"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.DIFAdisTS.UndoCommand import (
|
||||||
|
SetCommand, AddCommand, SetCommandSpec,
|
||||||
|
DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, ic_spec_lst=None,
|
||||||
|
trad=None, parent=None, mode="reaches"):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._mode = mode
|
||||||
|
self._trad = trad
|
||||||
|
self._ic_spec_lst = ic_spec_lst
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
val = []
|
||||||
|
if self._mode == "rk":
|
||||||
|
reach_id = self._ic_spec_lst[index.row()].reach
|
||||||
|
|
||||||
|
reach = next(filter(
|
||||||
|
lambda edge: edge.id == reach_id, self._data.edges()
|
||||||
|
))
|
||||||
|
|
||||||
|
if reach_id is not None:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda rk: str(rk), reach.reach.get_rk()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._mode == "method":
|
||||||
|
val = self._data.dif_adists.lst[0].types
|
||||||
|
else:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda n: n.name, self._data.edges()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad['not_associated']] +
|
||||||
|
val
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class DIFTableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, river=None, data=None, **kwargs):
|
||||||
|
self._river = river
|
||||||
|
|
||||||
|
super(DIFTableModel, self).__init__(data=data, **kwargs)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data._data
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] is "method":
|
||||||
|
n = self._lst[row].method
|
||||||
|
if n is None or n == "":
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "reach":
|
||||||
|
n = self._lst[row].reach
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return next(filter(
|
||||||
|
lambda edge: edge.id == n, self._river.edges()
|
||||||
|
)).name
|
||||||
|
elif self._headers[column] is "start_rk":
|
||||||
|
n = self._lst[row].start_rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "end_rk":
|
||||||
|
n = self._lst[row].end_rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "dif":
|
||||||
|
n = self._lst[row].dif
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "b":
|
||||||
|
n = self._lst[row].b
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "c":
|
||||||
|
n = self._lst[row].c
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] != "reach":
|
||||||
|
self._undo.push(
|
||||||
|
SetCommandSpec(
|
||||||
|
self._lst, row, self._headers[column], value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
print(self._river.edge(value).id)
|
||||||
|
self._undo.push(
|
||||||
|
SetCommandSpec(
|
||||||
|
self._lst,
|
||||||
|
row,
|
||||||
|
self._headers[column],
|
||||||
|
self._river.edge(value).id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.DIFAdisTS.UndoCommand import (
|
||||||
|
SetCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class DIFTableDefaultModel(PamhyrTableModel):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(DIFTableDefaultModel, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] is "method":
|
||||||
|
n = self._data[row].method
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "dif":
|
||||||
|
n = self._data[row].dif
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "b":
|
||||||
|
n = self._data[row].b
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "c":
|
||||||
|
n = self._data[row].c
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] is not None:
|
||||||
|
self._undo.push(
|
||||||
|
SetCommand(
|
||||||
|
self._data, row, self._headers[column], value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
|
||||||
|
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
|
||||||
|
|
||||||
|
|
||||||
|
class SetCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, row, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._column = column
|
||||||
|
|
||||||
|
if self._column == "method":
|
||||||
|
self._old = self._data[self._row].method
|
||||||
|
elif self._column == "dif":
|
||||||
|
self._old = self._data[self._row].dif
|
||||||
|
elif self._column == "b":
|
||||||
|
self._old = self._data[self._row].b
|
||||||
|
elif self._column == "c":
|
||||||
|
self._old = self._data[self._row].c
|
||||||
|
|
||||||
|
_type = float
|
||||||
|
if column == "method":
|
||||||
|
_type = str
|
||||||
|
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == "method":
|
||||||
|
self._data[self._row].method = self._old
|
||||||
|
elif self._column == "dif":
|
||||||
|
self._data[self._row].dif = self._old
|
||||||
|
elif self._column == "b":
|
||||||
|
self._data[self._row].b = self._old
|
||||||
|
elif self._column == "c":
|
||||||
|
self._data[self._row].c = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == "method":
|
||||||
|
self._data[self._row].method = self._new
|
||||||
|
elif self._column == "dif":
|
||||||
|
self._data[self._row].dif = self._new
|
||||||
|
elif self._column == "b":
|
||||||
|
self._data[self._row].b = self._new
|
||||||
|
elif self._column == "c":
|
||||||
|
self._data[self._row].c = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetCommandSpec(QUndoCommand):
|
||||||
|
def __init__(self, data, row, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._column = column
|
||||||
|
|
||||||
|
if self._column == "method":
|
||||||
|
self._old = self._data[self._row].method
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._old = self._data[self._row].reach
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._old = self._data[self._row].start_rk
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._old = self._data[self._row].end_rk
|
||||||
|
elif self._column == "dif":
|
||||||
|
self._old = self._data[self._row].dif
|
||||||
|
elif self._column == "b":
|
||||||
|
self._old = self._data[self._row].b
|
||||||
|
elif self._column == "c":
|
||||||
|
self._old = self._data[self._row].c
|
||||||
|
|
||||||
|
_type = float
|
||||||
|
if column == "method":
|
||||||
|
_type = str
|
||||||
|
elif column == "reach":
|
||||||
|
_type = int
|
||||||
|
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == "method":
|
||||||
|
self._data[self._row].method = self._old
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._data[self._row].reach = self._old
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._data[self._row].start_rk = self._old
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._data[self._row].end_rk = self._old
|
||||||
|
elif self._column == "dif":
|
||||||
|
self._data[self._row].dif = self._old
|
||||||
|
elif self._column == "b":
|
||||||
|
self._data[self._row].b = self._old
|
||||||
|
elif self._column == "c":
|
||||||
|
self._data[self._row].c = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == "method":
|
||||||
|
self._data[self._row].method = self._new
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._data[self._row].reach = self._new
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._data[self._row].start_rk = self._new
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._data[self._row].end_rk = self._new
|
||||||
|
elif self._column == "dif":
|
||||||
|
self._data[self._row].dif = self._new
|
||||||
|
elif self._column == "b":
|
||||||
|
self._data[self._row].b = self._new
|
||||||
|
elif self._column == "c":
|
||||||
|
self._data[self._row].c = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, ics_spec, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._ics_spec = ics_spec
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._data.new(self._index)
|
||||||
|
else:
|
||||||
|
self._data.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, ics_spec, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._ics_spec = ics_spec
|
||||||
|
self._rows = rows
|
||||||
|
# self._data = data
|
||||||
|
|
||||||
|
self._ic = []
|
||||||
|
for row in rows:
|
||||||
|
self._ic.append((row, self._ics_spec[row]))
|
||||||
|
self._ic.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._ic:
|
||||||
|
self._data.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data.delete_i(self._rows)
|
||||||
@@ -0,0 +1,295 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer, logger_exception
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence, QIcon,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
QVBoxLayout, QToolBar, QAction, QToolButton,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Modules import Modules
|
||||||
|
|
||||||
|
from View.DIFAdisTS.TableDefault import (
|
||||||
|
DIFTableDefaultModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.DIFAdisTS.Table import (
|
||||||
|
DIFTableModel, ComboBoxDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.DIFAdisTS.translate import DIFAdisTSTranslate
|
||||||
|
|
||||||
|
from Solver.Mage import Mage8
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class DIFAdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "DIFAdisTS"
|
||||||
|
_pamhyr_name = "DIF AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, data=None, study=None, config=None, parent=None):
|
||||||
|
self._data = []
|
||||||
|
self._data.append(data)
|
||||||
|
trad = DIFAdisTSTranslate()
|
||||||
|
|
||||||
|
name = (
|
||||||
|
trad[self._pamhyr_name] +
|
||||||
|
" - " + study.name +
|
||||||
|
" - " + self._data[0].name
|
||||||
|
)
|
||||||
|
|
||||||
|
super(DIFAdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self._dif_adists_lst = study.river.dif_adists
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
|
||||||
|
self.ui.setWindowTitle(self._title)
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
|
||||||
|
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
|
||||||
|
|
||||||
|
table_default = self.find(QTableView, f"tableView")
|
||||||
|
|
||||||
|
self._delegate_method = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="method"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table = DIFTableDefaultModel(
|
||||||
|
table_view=table_default,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["method", "dif", "b", "c"],
|
||||||
|
delegates={
|
||||||
|
"method": self._delegate_method
|
||||||
|
},
|
||||||
|
data=self._data,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad
|
||||||
|
)
|
||||||
|
|
||||||
|
table_default.setModel(self._table)
|
||||||
|
table_default.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table_default.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
table_default.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
layout = self.find(QVBoxLayout, f"verticalLayout_1")
|
||||||
|
toolBar = QToolBar()
|
||||||
|
layout.addWidget(toolBar)
|
||||||
|
|
||||||
|
action_add = QAction(self)
|
||||||
|
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
|
||||||
|
action_add.triggered.connect(self.add)
|
||||||
|
action_delete = QAction(self)
|
||||||
|
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
|
||||||
|
action_delete.triggered.connect(self.delete)
|
||||||
|
|
||||||
|
toolBar.addAction(action_add)
|
||||||
|
toolBar.addAction(action_delete)
|
||||||
|
|
||||||
|
self.table_spec = QTableView()
|
||||||
|
layout.addWidget(self.table_spec)
|
||||||
|
|
||||||
|
self._delegate_reach = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="reaches"
|
||||||
|
)
|
||||||
|
self._delegate_rk = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="rk"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table_spec = DIFTableModel(
|
||||||
|
table_view=self.table_spec,
|
||||||
|
table_headers=self._trad.get_dict("table_headers_spec"),
|
||||||
|
editable_headers=["method", "reach", "start_rk",
|
||||||
|
"end_rk", "dif", "b", "c"],
|
||||||
|
delegates={
|
||||||
|
"method": self._delegate_method,
|
||||||
|
"reach": self._delegate_reach,
|
||||||
|
"start_rk": self._delegate_rk,
|
||||||
|
"end_rk": self._delegate_rk
|
||||||
|
},
|
||||||
|
data=self._data[0],
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad,
|
||||||
|
river=self._study.river
|
||||||
|
)
|
||||||
|
|
||||||
|
self.table_spec.setModel(self._table_spec)
|
||||||
|
self.table_spec.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
self.table_spec.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
self.table_spec.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
selectionModel = self.table_spec.selectionModel()
|
||||||
|
index = self.table_spec.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
self.table_spec.scrollTo(index)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
# table = self.find(QTableView, f"tableView")
|
||||||
|
table = self.table_spec
|
||||||
|
rows = table.selectionModel()\
|
||||||
|
.selectedRows()
|
||||||
|
|
||||||
|
if len(rows) == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return rows[0].row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
# table = self.find(QTableView, f"tableView")
|
||||||
|
table = self.table_spec
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def move_up(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_up(row)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def move_down(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_down(row)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
rows = list(
|
||||||
|
map(
|
||||||
|
lambda row: row.row(),
|
||||||
|
self.tableView.selectionModel().selectedRows()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
table = list(
|
||||||
|
map(
|
||||||
|
lambda eic: list(
|
||||||
|
map(
|
||||||
|
lambda k: eic[1][k],
|
||||||
|
["rk", "discharge", "elevation"]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
filter(
|
||||||
|
lambda eic: eic[0] in rows,
|
||||||
|
enumerate(self._ics.lst())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.copyTableIntoClipboard(table)
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
header, data = self.parseClipboardTable()
|
||||||
|
|
||||||
|
if len(data) + len(header) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"DIF: Paste: " +
|
||||||
|
f"header = {header}, " +
|
||||||
|
f"data = {data}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
row = self.index_selected_row()
|
||||||
|
# self._table.paste(row, header, data)
|
||||||
|
self._table.paste(row, [], data)
|
||||||
|
except Exception as e:
|
||||||
|
logger_exception(e)
|
||||||
|
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._data[0]._data) == 0 or len(rows) == 0:
|
||||||
|
self._table_spec.add(0)
|
||||||
|
else:
|
||||||
|
self._table_spec.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
print("del")
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
print("len 0")
|
||||||
|
return
|
||||||
|
self._table_spec.delete(rows)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class DIFAdisTSTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(DIFAdisTSTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["DIF AdisTS"] = _translate(
|
||||||
|
"DIFAdisTS", "DIF AdisTS")
|
||||||
|
|
||||||
|
self._dict["rk"] = self._dict["unit_rk"]
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"method": self._dict["method"],
|
||||||
|
"dif": _translate("Unit", "DIF"),
|
||||||
|
"b": _translate("Unit", "Coeff b"),
|
||||||
|
"c": _translate("Unit", "Coeff c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_spec"] = {
|
||||||
|
"method": self._dict["method"],
|
||||||
|
"reach": self._dict["reach"],
|
||||||
|
"start_rk": _translate("Unit", "Start_RK (m)"),
|
||||||
|
"end_rk": _translate("Unit", "End_RK (m)"),
|
||||||
|
"dif": _translate("Unit", "DIF"),
|
||||||
|
"b": _translate("Unit", "Coeff b"),
|
||||||
|
"c": _translate("Unit", "Coeff c"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.UndoCommand import (
|
||||||
|
SetCommand, AddCommand, SetCommandSpec,
|
||||||
|
DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, ic_spec_lst=None,
|
||||||
|
trad=None, parent=None, mode="reaches"):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._mode = mode
|
||||||
|
self._trad = trad
|
||||||
|
self._ic_spec_lst = ic_spec_lst
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
val = []
|
||||||
|
if self._mode == "rk":
|
||||||
|
reach_id = self._ic_spec_lst[index.row()].reach
|
||||||
|
|
||||||
|
reach = next(filter(lambda edge: edge.id == reach_id,
|
||||||
|
self._data.edges()))
|
||||||
|
|
||||||
|
if reach_id is not None:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda rk: str(rk), reach.reach.get_rk()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda n: n.name, self._data.edges()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad['not_associated']] +
|
||||||
|
val
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class InitialConditionTableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, river=None, data=None, **kwargs):
|
||||||
|
self._river = river
|
||||||
|
|
||||||
|
super(InitialConditionTableModel, self).__init__(data=data, **kwargs)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data._data
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] is "name":
|
||||||
|
n = self._lst[row].name
|
||||||
|
if n is None or n == "":
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "reach":
|
||||||
|
n = self._lst[row].reach
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return next(filter(
|
||||||
|
lambda edge: edge.id == n, self._river.edges()
|
||||||
|
)).name
|
||||||
|
elif self._headers[column] is "start_rk":
|
||||||
|
n = self._lst[row].start_rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "end_rk":
|
||||||
|
n = self._lst[row].end_rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "concentration":
|
||||||
|
n = self._lst[row].concentration
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "eg":
|
||||||
|
n = self._lst[row].eg
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "em":
|
||||||
|
n = self._lst[row].em
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "ed":
|
||||||
|
n = self._lst[row].ed
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "rate":
|
||||||
|
n = self._lst[row].rate
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] != "reach":
|
||||||
|
self._undo.push(
|
||||||
|
SetCommandSpec(
|
||||||
|
self._lst, row, self._headers[column], value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
print(self._river.edge(value).id)
|
||||||
|
self._undo.push(
|
||||||
|
SetCommandSpec(
|
||||||
|
self._lst, row, self._headers[column],
|
||||||
|
self._river.edge(value).id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.UndoCommand import (
|
||||||
|
SetCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class InitialConditionTableDefaultModel(PamhyrTableModel):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(InitialConditionTableDefaultModel, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] is "name":
|
||||||
|
return self._data[row].name
|
||||||
|
elif self._headers[column] is "concentration":
|
||||||
|
n = self._data[row].concentration
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "eg":
|
||||||
|
n = self._data[row].eg
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "em":
|
||||||
|
n = self._data[row].em
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
elif self._headers[column] is "ed":
|
||||||
|
n = self._data[row].ed
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] is not None:
|
||||||
|
self._undo.push(
|
||||||
|
SetCommand(
|
||||||
|
self._data, row, self._headers[column], value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTS \
|
||||||
|
import InitialConditionsAdisTS
|
||||||
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTSList \
|
||||||
|
import InitialConditionsAdisTSList
|
||||||
|
|
||||||
|
|
||||||
|
class SetCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, row, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._column = column
|
||||||
|
|
||||||
|
if self._column == "name":
|
||||||
|
self._old = self._data[self._row].name
|
||||||
|
elif self._column == "concentration":
|
||||||
|
self._old = self._data[self._row].concentration
|
||||||
|
elif self._column == "eg":
|
||||||
|
self._old = self._data[self._row].eg
|
||||||
|
elif self._column == "em":
|
||||||
|
self._old = self._data[self._row].em
|
||||||
|
elif self._column == "ed":
|
||||||
|
self._old = self._data[self._row].ed
|
||||||
|
|
||||||
|
_type = float
|
||||||
|
if column == "name":
|
||||||
|
_type = str
|
||||||
|
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._old
|
||||||
|
elif self._column == "concentration":
|
||||||
|
self._data[self._row].concentration = self._old
|
||||||
|
elif self._column == "eg":
|
||||||
|
self._data[self._row].eg = self._old
|
||||||
|
elif self._column == "em":
|
||||||
|
self._data[self._row].em = self._old
|
||||||
|
elif self._column == "ed":
|
||||||
|
self._data[self._row].ed = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._new
|
||||||
|
elif self._column == "concentration":
|
||||||
|
self._data[self._row].concentration = self._new
|
||||||
|
elif self._column == "eg":
|
||||||
|
self._data[self._row].eg = self._new
|
||||||
|
elif self._column == "em":
|
||||||
|
self._data[self._row].em = self._new
|
||||||
|
elif self._column == "ed":
|
||||||
|
self._data[self._row].ed = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetCommandSpec(QUndoCommand):
|
||||||
|
def __init__(self, data, row, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._column = column
|
||||||
|
|
||||||
|
if self._column == "name":
|
||||||
|
self._old = self._data[self._row].name
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._old = self._data[self._row].reach
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._old = self._data[self._row].start_rk
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._old = self._data[self._row].end_rk
|
||||||
|
elif self._column == "concentration":
|
||||||
|
self._old = self._data[self._row].concentration
|
||||||
|
elif self._column == "eg":
|
||||||
|
self._old = self._data[self._row].eg
|
||||||
|
elif self._column == "em":
|
||||||
|
self._old = self._data[self._row].em
|
||||||
|
elif self._column == "ed":
|
||||||
|
self._old = self._data[self._row].ed
|
||||||
|
elif self._column == "rate":
|
||||||
|
self._old = self._data[self._row].rate
|
||||||
|
|
||||||
|
_type = float
|
||||||
|
if column == "name":
|
||||||
|
_type = str
|
||||||
|
elif column == "reach":
|
||||||
|
_type = int
|
||||||
|
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._old
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._data[self._row].reach = self._old
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._data[self._row].start_rk = self._old
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._data[self._row].end_rk = self._old
|
||||||
|
elif self._column == "concentration":
|
||||||
|
self._data[self._row].concentration = self._old
|
||||||
|
elif self._column == "eg":
|
||||||
|
self._data[self._row].eg = self._old
|
||||||
|
elif self._column == "em":
|
||||||
|
self._data[self._row].em = self._old
|
||||||
|
elif self._column == "ed":
|
||||||
|
self._data[self._row].ed = self._old
|
||||||
|
elif self._column == "rate":
|
||||||
|
self._data[self._row].rate = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == "name":
|
||||||
|
self._data[self._row].name = self._new
|
||||||
|
elif self._column == "reach":
|
||||||
|
self._data[self._row].reach = self._new
|
||||||
|
elif self._column == "start_rk":
|
||||||
|
self._data[self._row].start_rk = self._new
|
||||||
|
elif self._column == "end_rk":
|
||||||
|
self._data[self._row].end_rk = self._new
|
||||||
|
elif self._column == "concentration":
|
||||||
|
self._data[self._row].concentration = self._new
|
||||||
|
elif self._column == "eg":
|
||||||
|
self._data[self._row].eg = self._new
|
||||||
|
elif self._column == "em":
|
||||||
|
self._data[self._row].em = self._new
|
||||||
|
elif self._column == "ed":
|
||||||
|
self._data[self._row].ed = self._new
|
||||||
|
elif self._column == "rate":
|
||||||
|
self._data[self._row].rate = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, ics_spec, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._ics_spec = ics_spec
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._data.new(self._index)
|
||||||
|
else:
|
||||||
|
self._data.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, ics_spec, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._ics_spec = ics_spec
|
||||||
|
self._rows = rows
|
||||||
|
# self._data = data
|
||||||
|
|
||||||
|
self._ic = []
|
||||||
|
for row in rows:
|
||||||
|
self._ic.append((row, self._ics_spec[row]))
|
||||||
|
self._ic.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._ic:
|
||||||
|
self._data.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data.delete_i(self._rows)
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer, logger_exception
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence, QIcon,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
QVBoxLayout, QToolBar, QAction, QToolButton,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Modules import Modules
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.UndoCommand import (
|
||||||
|
SetCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.TableDefault import (
|
||||||
|
InitialConditionTableDefaultModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.Table import (
|
||||||
|
InitialConditionTableModel, ComboBoxDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.translate import IcAdisTSTranslate
|
||||||
|
|
||||||
|
from Solver.Mage import Mage8
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class InitialConditionsAdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "InitialConditionsAdisTS"
|
||||||
|
_pamhyr_name = "Initial condition AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, data=None, study=None, config=None, parent=None):
|
||||||
|
self._data = []
|
||||||
|
self._data.append(data)
|
||||||
|
trad = IcAdisTSTranslate()
|
||||||
|
|
||||||
|
name = (
|
||||||
|
trad[self._pamhyr_name] +
|
||||||
|
" - " + study.name +
|
||||||
|
" - " + self._data[0].name
|
||||||
|
)
|
||||||
|
|
||||||
|
super(InitialConditionsAdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self._ics_adists_lst = study.river.ic_adists
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
|
||||||
|
self.ui.setWindowTitle(self._title)
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
|
||||||
|
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
|
||||||
|
|
||||||
|
table_default = self.find(QTableView, f"tableView")
|
||||||
|
|
||||||
|
self._table = InitialConditionTableDefaultModel(
|
||||||
|
table_view=table_default,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["name", "concentration", "eg", "em", "ed"],
|
||||||
|
delegates={},
|
||||||
|
data=self._data,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad
|
||||||
|
)
|
||||||
|
|
||||||
|
table_default.setModel(self._table)
|
||||||
|
table_default.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table_default.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
table_default.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
layout = self.find(QVBoxLayout, f"verticalLayout_1")
|
||||||
|
toolBar = QToolBar()
|
||||||
|
layout.addWidget(toolBar)
|
||||||
|
|
||||||
|
action_add = QAction(self)
|
||||||
|
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
|
||||||
|
action_add.triggered.connect(self.add)
|
||||||
|
action_delete = QAction(self)
|
||||||
|
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
|
||||||
|
action_delete.triggered.connect(self.delete)
|
||||||
|
|
||||||
|
toolBar.addAction(action_add)
|
||||||
|
toolBar.addAction(action_delete)
|
||||||
|
|
||||||
|
self.table_spec = QTableView()
|
||||||
|
layout.addWidget(self.table_spec)
|
||||||
|
|
||||||
|
self._delegate_reach = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="reaches"
|
||||||
|
)
|
||||||
|
self._delegate_rk = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
ic_spec_lst=self._data[0]._data,
|
||||||
|
parent=self,
|
||||||
|
mode="rk"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table_spec = InitialConditionTableModel(
|
||||||
|
table_view=self.table_spec,
|
||||||
|
table_headers=self._trad.get_dict("table_headers_spec"),
|
||||||
|
editable_headers=["name", "reach", "start_rk",
|
||||||
|
"end_rk", "concentration",
|
||||||
|
"eg", "em", "ed", "rate"],
|
||||||
|
delegates={
|
||||||
|
"reach": self._delegate_reach,
|
||||||
|
"start_rk": self._delegate_rk,
|
||||||
|
"end_rk": self._delegate_rk
|
||||||
|
},
|
||||||
|
data=self._data[0],
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad,
|
||||||
|
river=self._study.river
|
||||||
|
)
|
||||||
|
|
||||||
|
self.table_spec.setModel(self._table_spec)
|
||||||
|
self.table_spec.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
self.table_spec.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
self.table_spec.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
selectionModel = self.table_spec.selectionModel()
|
||||||
|
index = self.table_spec.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
self.table_spec.scrollTo(index)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
# table = self.find(QTableView, f"tableView")
|
||||||
|
table = self.table_spec
|
||||||
|
rows = table.selectionModel()\
|
||||||
|
.selectedRows()
|
||||||
|
|
||||||
|
if len(rows) == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return rows[0].row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
# table = self.find(QTableView, f"tableView")
|
||||||
|
table = self.table_spec
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def move_up(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_up(row)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def move_down(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_down(row)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
rows = list(
|
||||||
|
map(
|
||||||
|
lambda row: row.row(),
|
||||||
|
self.tableView.selectionModel().selectedRows()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
table = list(
|
||||||
|
map(
|
||||||
|
lambda eic: list(
|
||||||
|
map(
|
||||||
|
lambda k: eic[1][k],
|
||||||
|
["rk", "discharge", "elevation"]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
filter(
|
||||||
|
lambda eic: eic[0] in rows,
|
||||||
|
enumerate(self._ics.lst())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.copyTableIntoClipboard(table)
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
header, data = self.parseClipboardTable()
|
||||||
|
|
||||||
|
if len(data) + len(header) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"IC: Paste: " +
|
||||||
|
f"header = {header}, " +
|
||||||
|
f"data = {data}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
row = self.index_selected_row()
|
||||||
|
# self._table.paste(row, header, data)
|
||||||
|
self._table.paste(row, [], data)
|
||||||
|
except Exception as e:
|
||||||
|
logger_exception(e)
|
||||||
|
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._data[0]._data) == 0 or len(rows) == 0:
|
||||||
|
self._table_spec.add(0)
|
||||||
|
else:
|
||||||
|
self._table_spec.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
print("del")
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
print("len 0")
|
||||||
|
return
|
||||||
|
self._table_spec.delete(rows)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class IcAdisTSTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(IcAdisTSTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Initial condition AdisTS"] = _translate(
|
||||||
|
"InitialConditionAdisTS", "Initial condition AdisTS")
|
||||||
|
|
||||||
|
self._dict["rk"] = self._dict["unit_rk"]
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"name": self._dict["name"],
|
||||||
|
"concentration": self._dict["unit_concentration"],
|
||||||
|
"eg": _translate("Unit", "EG (m)"),
|
||||||
|
"em": _translate("Unit", "EM (m)"),
|
||||||
|
"ed": _translate("Unit", "ED (m)"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_spec"] = {
|
||||||
|
"name": self._dict["name"],
|
||||||
|
"reach": self._dict["reach"],
|
||||||
|
"start_rk": _translate("Unit", "Start_RK (m)"),
|
||||||
|
"end_rk": _translate("Unit", "End_RK (m)"),
|
||||||
|
"concentration": self._dict["unit_concentration"],
|
||||||
|
"eg": _translate("Unit", "EG (m)"),
|
||||||
|
"em": _translate("Unit", "EM (m)"),
|
||||||
|
"ed": _translate("Unit", "ED (m)"),
|
||||||
|
"rate": _translate("Unit", "Rate"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Plot.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
QCoreApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class Plot(PamhyrPlot):
|
||||||
|
def __init__(self, mode="time", data=None,
|
||||||
|
trad=None, canvas=None, toolbar=None,
|
||||||
|
parent=None):
|
||||||
|
super(Plot, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
data=data,
|
||||||
|
toolbar=toolbar,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table_headers = self._trad.get_dict("table_headers")
|
||||||
|
|
||||||
|
header = self.data.header
|
||||||
|
self.label_x = self._table_headers[header[0]]
|
||||||
|
self.label_y = self._table_headers[header[1]]
|
||||||
|
|
||||||
|
self._mode = mode
|
||||||
|
self._isometric_axis = False
|
||||||
|
|
||||||
|
self._auto_relim_update = True
|
||||||
|
self._autoscale_update = True
|
||||||
|
|
||||||
|
def custom_ticks(self):
|
||||||
|
if self.data.header[0] != "time":
|
||||||
|
return
|
||||||
|
|
||||||
|
self.set_ticks_time_formater()
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def draw(self):
|
||||||
|
self.init_axes()
|
||||||
|
|
||||||
|
if len(self.data) == 0:
|
||||||
|
self._init = False
|
||||||
|
return
|
||||||
|
|
||||||
|
self.draw_data()
|
||||||
|
self.custom_ticks()
|
||||||
|
|
||||||
|
self.idle()
|
||||||
|
self._init = True
|
||||||
|
|
||||||
|
def draw_data(self):
|
||||||
|
# Plot data
|
||||||
|
x = list(map(lambda v: v[0], self.data.data))
|
||||||
|
y = list(map(lambda v: v[1], self.data.data))
|
||||||
|
|
||||||
|
self._line, = self.canvas.axes.plot(
|
||||||
|
x, y,
|
||||||
|
color=self.color_plot,
|
||||||
|
**self.plot_default_kargs
|
||||||
|
)
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def update(self, ind=None):
|
||||||
|
if not self._init:
|
||||||
|
self.draw()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.update_data()
|
||||||
|
|
||||||
|
self.update_idle()
|
||||||
|
|
||||||
|
def update_data(self):
|
||||||
|
x = list(map(lambda v: v[0], self.data.data))
|
||||||
|
y = list(map(lambda v: v[1], self.data.data))
|
||||||
|
|
||||||
|
self._line.set_data(x, y)
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from datetime import date, time, datetime, timedelta
|
||||||
|
|
||||||
|
from tools import (
|
||||||
|
trace, timer,
|
||||||
|
timestamp_to_old_pamhyr_date,
|
||||||
|
old_pamhyr_date_to_timestamp,
|
||||||
|
timestamp_to_old_pamhyr_date_adists
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect, QTime, QDateTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QTableView, QAbstractItemView, QSpinBox,
|
||||||
|
QTimeEdit, QDateTimeEdit, QItemDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
from View.LateralContributionsAdisTS.Edit.UndoCommand import (
|
||||||
|
SetDataCommand, AddCommand, DelCommand,
|
||||||
|
)
|
||||||
|
from View.LateralContributionsAdisTS.Edit.translate import *
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.TextAlignmentRole:
|
||||||
|
return Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
value = QVariant()
|
||||||
|
|
||||||
|
if 0 <= column < 2:
|
||||||
|
v = self._data._data[row][column]
|
||||||
|
if self._data._types[column] == float:
|
||||||
|
value = f"{v:.4f}"
|
||||||
|
elif self._data.header[column] == "time":
|
||||||
|
if self._opt_data == "time":
|
||||||
|
value = timestamp_to_old_pamhyr_date_adists(int(v))
|
||||||
|
else:
|
||||||
|
value = str(datetime.fromtimestamp(v))
|
||||||
|
else:
|
||||||
|
value = f"{v}"
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._undo.push(
|
||||||
|
SetDataCommand(
|
||||||
|
self._data, row, column, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
||||||
|
import LateralContributionAdisTS
|
||||||
|
|
||||||
|
|
||||||
|
class SetDataCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._column = column
|
||||||
|
self._old = self._data._data[self._index][self._column]
|
||||||
|
_type = self._data._types[self._column]
|
||||||
|
self._new = _type(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._column == 0:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._old, self._data._data[self._index][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._data._data[self._index][0], self._old
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._column == 0:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._new, self._data._data[self._index][1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._data._data[self._index] = (
|
||||||
|
self._data._data[self._index][0], self._new
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
del self._data._data[self._index]
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._data._data.insert(
|
||||||
|
self._index, (
|
||||||
|
self._data._types[0](0), self._data._types[1](0.0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._data._data.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._lc = []
|
||||||
|
for row in rows:
|
||||||
|
self._lc.append((row, self._data._data[row]))
|
||||||
|
self._lc.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._lc:
|
||||||
|
self._data._data.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for row in self._rows:
|
||||||
|
del self._data._data[row]
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||||
|
|
||||||
|
from View.LateralContributionsAdisTS.Edit.translate import LCETranslate
|
||||||
|
from View.LateralContributionsAdisTS.Edit.Table import TableModel
|
||||||
|
from View.LateralContributionsAdisTS.Edit.Plot import Plot
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class EditLateralContributionAdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "EditLateralContributionAdisTS"
|
||||||
|
_pamhyr_name = "Edit lateral contribution AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, data=None,
|
||||||
|
study=None, config=None,
|
||||||
|
parent=None):
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
trad = LCETranslate()
|
||||||
|
name = trad[self._pamhyr_name]
|
||||||
|
|
||||||
|
super(EditLateralContributionAdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._data is not None:
|
||||||
|
if self._data.edge is not None:
|
||||||
|
edge_name = next(filter(
|
||||||
|
lambda edge: edge.id == self._data.edge,
|
||||||
|
self._study.river.edges()
|
||||||
|
)).name
|
||||||
|
else:
|
||||||
|
edge_name = trad['not_associated']
|
||||||
|
|
||||||
|
name += (
|
||||||
|
f"{study.name} - " +
|
||||||
|
f"{edge_name})"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_plot()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._delegate_time = PamhyrExTimeDelegate(
|
||||||
|
data=self._data,
|
||||||
|
mode=self._study.time_system,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
table_headers = self._trad.get_dict("table_headers")
|
||||||
|
for h in self._data.header:
|
||||||
|
headers[h] = table_headers[h]
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=headers,
|
||||||
|
editable_headers=self._data.header,
|
||||||
|
delegates={
|
||||||
|
# "time": self._delegate_time,
|
||||||
|
},
|
||||||
|
data=self._data,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad,
|
||||||
|
opt_data=self._study.time_system
|
||||||
|
)
|
||||||
|
|
||||||
|
table.setModel(self._table)
|
||||||
|
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
table.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
def setup_plot(self):
|
||||||
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas.setObjectName("canvas")
|
||||||
|
self.toolbar = PamhyrPlotToolbar(
|
||||||
|
self.canvas, self
|
||||||
|
)
|
||||||
|
self.verticalLayout.addWidget(self.toolbar)
|
||||||
|
self.verticalLayout.addWidget(self.canvas)
|
||||||
|
|
||||||
|
self.plot = Plot(
|
||||||
|
canvas=self.canvas,
|
||||||
|
data=self._data,
|
||||||
|
mode=self._study.time_system,
|
||||||
|
toolbar=self.toolbar,
|
||||||
|
trad=self._trad,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self.plot.draw()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self.update)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return table.selectionModel()\
|
||||||
|
.selectedRows()[0]\
|
||||||
|
.row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._data) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
self.plot.update()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
from View.LateralContributionsAdisTS.translate import LCTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class LCETranslate(LCTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(LCETranslate, self).__init__()
|
||||||
|
self._dict["Edit lateral contribution AdisTS"] = _translate(
|
||||||
|
"LateralContributionAdisTS", "Edit lateral contribution AdisTS"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"time": self._dict["time"],
|
||||||
|
"date": self._dict["date"],
|
||||||
|
"rate": _translate("LateralContributionAdisTS", "Mass Flow"),
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.LateralContributionsAdisTS.UndoCommand import (
|
||||||
|
SetEdgeCommand,
|
||||||
|
SetBeginCommand, SetEndCommand,
|
||||||
|
AddCommand, DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, mode="edge",
|
||||||
|
trad=None, parent=None):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._mode = mode
|
||||||
|
self._trad = trad
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
@data.setter
|
||||||
|
def data(self, data):
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
if self._mode == "rk":
|
||||||
|
if self._data is None:
|
||||||
|
self.editor.addItems(
|
||||||
|
["0"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.editor.addItems(
|
||||||
|
list(
|
||||||
|
map(str, self._data.reach.get_rk())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad['not_associated']] +
|
||||||
|
self._data.edges_names()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(index.data(Qt.DisplayRole))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def __init__(self, pollutant=None, lcs_list=None, trad=None, **kwargs):
|
||||||
|
self._trad = trad
|
||||||
|
self._lcs_list = lcs_list
|
||||||
|
self._pollutant = pollutant
|
||||||
|
|
||||||
|
super(TableModel, self).__init__(trad=trad, **kwargs)
|
||||||
|
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data.lateral_contributions_adists.lst
|
||||||
|
self._tab = self._opt_data
|
||||||
|
self._long_types = self._trad.get_dict("long_types")
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] == "edge":
|
||||||
|
n = self._lst[row].edge
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return next(filter(
|
||||||
|
lambda edge: edge.id == n, self._data.edges()
|
||||||
|
)).name
|
||||||
|
elif self._headers[column] == "begin_rk":
|
||||||
|
return str(self._lst[row].begin_rk)
|
||||||
|
elif self._headers[column] == "end_rk":
|
||||||
|
return str(self._lst[row].end_rk)
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "edge":
|
||||||
|
self._undo.push(
|
||||||
|
SetEdgeCommand(
|
||||||
|
self._lcs_list, self._lst,
|
||||||
|
row,
|
||||||
|
self._data.edge(value).id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "begin_rk":
|
||||||
|
self._undo.push(
|
||||||
|
SetBeginCommand(
|
||||||
|
self._lcs_list, self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "end_rk":
|
||||||
|
self._undo.push(
|
||||||
|
SetEndCommand(
|
||||||
|
self._lcs_list, self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._pollutant, self._lcs_list, self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
||||||
|
import LateralContributionAdisTS
|
||||||
|
from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList \
|
||||||
|
import LateralContributionsAdisTSList
|
||||||
|
|
||||||
|
|
||||||
|
class SetBeginCommand(QUndoCommand):
|
||||||
|
def __init__(self, lcs, lcs_lst, index, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._lcs = lcs
|
||||||
|
self._lcs_lst = lcs_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._lcs_lst[self._index].begin_rk
|
||||||
|
self._new = float(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._lcs_lst[self._index].begin_rk = float(self._old)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._lcs_lst[self._index].begin_rk = float(self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class SetEndCommand(QUndoCommand):
|
||||||
|
def __init__(self, lcs, lcs_lst, index, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._lcs = lcs
|
||||||
|
self._lcs_lst = lcs_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._lcs_lst[self._index].end_rk
|
||||||
|
self._new = float(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._lcs_lst[self._index].end_rk = float(self._old)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._lcs_lst[self._index].end_rk = float(self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class SetEdgeCommand(QUndoCommand):
|
||||||
|
def __init__(self, lcs, lcs_lst, index, edge):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._lcs = lcs
|
||||||
|
self._lcs_lst = lcs_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._lcs_lst[self._index].edge
|
||||||
|
self._new = edge
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._lcs_lst[self._index].edge = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._lcs_lst[self._index].edge = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutant, lcs, lcs_lst, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._pollutant = pollutant
|
||||||
|
self._lcs = lcs
|
||||||
|
self._lcs_lst = lcs_lst
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
del self._lcs[self._index]
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._lcs.new(self._index, self._pollutant)
|
||||||
|
else:
|
||||||
|
self._lcs_lst.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, lcs, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._lcs = lcs
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._bc = []
|
||||||
|
for row in rows:
|
||||||
|
self._bc.append((row, self._lcs[row]))
|
||||||
|
self._bc.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._bc:
|
||||||
|
self._lcs.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for row in self._rows:
|
||||||
|
del self._lcs[row]
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.LateralContributionsAdisTS.Table import (
|
||||||
|
TableModel, ComboBoxDelegate
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Geometry.PlotXY import PlotXY
|
||||||
|
from View.LateralContributionsAdisTS.translate \
|
||||||
|
import LCTranslate
|
||||||
|
from View.LateralContributionsAdisTS.Edit.Window \
|
||||||
|
import EditLateralContributionAdisTSWindow
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class LateralContributionAdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "LateralContributionsAdisTS"
|
||||||
|
_pamhyr_name = "Lateral contribution AdisTS"
|
||||||
|
|
||||||
|
def __init__(self, study=None, pollutant=None, config=None, parent=None):
|
||||||
|
trad = LCTranslate()
|
||||||
|
name = trad[self._pamhyr_name] + " - " + study.name
|
||||||
|
|
||||||
|
self._pollutant = pollutant
|
||||||
|
|
||||||
|
super(LateralContributionAdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
trad=trad,
|
||||||
|
config=config,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._lcs = self._study.river.lateral_contributions_adists
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_graph()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._table = {}
|
||||||
|
|
||||||
|
self._delegate_rk = []
|
||||||
|
|
||||||
|
delegate_rk = ComboBoxDelegate(
|
||||||
|
data=None,
|
||||||
|
mode="rk",
|
||||||
|
trad=self._trad,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self._delegate_rk.append(delegate_rk)
|
||||||
|
|
||||||
|
self._delegate_edge = ComboBoxDelegate(
|
||||||
|
data=self._study.river,
|
||||||
|
mode="edge",
|
||||||
|
trad=self._trad,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=self._trad.get_dict("table_headers"),
|
||||||
|
delegates={
|
||||||
|
"edge": self._delegate_edge,
|
||||||
|
"begin_rk": delegate_rk,
|
||||||
|
"end_rk": delegate_rk,
|
||||||
|
},
|
||||||
|
data=self._study.river,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
trad=self._trad,
|
||||||
|
opt_data="liquid",
|
||||||
|
pollutant=self._pollutant,
|
||||||
|
lcs_list=self._lcs,
|
||||||
|
)
|
||||||
|
table.setModel(self._table)
|
||||||
|
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
table.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
def setup_graph(self):
|
||||||
|
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas.setObjectName("canvas")
|
||||||
|
self.plot_layout = self.find(QVBoxLayout, "verticalLayout_2")
|
||||||
|
self.plot_layout.addWidget(self.canvas)
|
||||||
|
|
||||||
|
self.plot = PlotXY(
|
||||||
|
canvas=self.canvas,
|
||||||
|
data=None,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||||
|
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
table.selectionModel().selectionChanged.connect(
|
||||||
|
self._set_current_reach
|
||||||
|
)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self._set_current_reach)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
return table.selectionModel()\
|
||||||
|
.selectedRows()[0]\
|
||||||
|
.row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _set_current_reach(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
data = None
|
||||||
|
highlight = None
|
||||||
|
|
||||||
|
tab = "liquid"
|
||||||
|
|
||||||
|
if len(rows) > 0:
|
||||||
|
edge_id = self._study.river\
|
||||||
|
.lateral_contributions_adists.lst[rows[0]]\
|
||||||
|
.edge
|
||||||
|
|
||||||
|
if edge_id:
|
||||||
|
edge = next(filter(lambda e: e.id == edge_id,
|
||||||
|
self._study.river.edges()))
|
||||||
|
data = edge.reach
|
||||||
|
lc = self._lcs.lst[rows[0]]
|
||||||
|
highlight = (lc.begin_rk, lc.end_rk)
|
||||||
|
|
||||||
|
for delegate in self._delegate_rk:
|
||||||
|
delegate.data = edge
|
||||||
|
|
||||||
|
self.plot = PlotXY(
|
||||||
|
canvas=self.canvas,
|
||||||
|
data=data,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=None,
|
||||||
|
)
|
||||||
|
self.plot.highlight = highlight
|
||||||
|
self.plot.update()
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._lcs) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def sort(self):
|
||||||
|
self._table.sort(False)
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
self._set_current_reach()
|
||||||
|
|
||||||
|
def edit(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
for row in rows:
|
||||||
|
data = self._lcs.lst[row]
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
EditLateralContributionAdisTSWindow,
|
||||||
|
data=[self._study, None, data]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
win = EditLateralContributionAdisTSWindow(
|
||||||
|
data=data,
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
win.show()
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class LCTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(LCTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Lateral contribution AdisTS"] = _translate(
|
||||||
|
"LateralContribution1adisTS", "Lateral contribution AdisTS"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sub_dict["long_types"] = {
|
||||||
|
"ND": self._dict["not_defined"],
|
||||||
|
"LC": _translate("LateralContribution", "Lateral contribution"),
|
||||||
|
"RA": _translate("LateralContribution", "Rain"),
|
||||||
|
"EV": _translate("LateralContribution", "Evaporation"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._dict["x"] = _translate("Geometry", "X (m)")
|
||||||
|
self._dict["y"] = _translate("Geometry", "Y (m)")
|
||||||
|
self._dict["z"] = _translate("Geometry", "Z (m)")
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"edge": self._dict["reach"],
|
||||||
|
"begin_rk": _translate("LateralContribution", "Begin rk (m)"),
|
||||||
|
"end_rk": _translate("LateralContribution", "End rk (m)")
|
||||||
|
}
|
||||||
+392
-13
@@ -23,7 +23,13 @@ import logging
|
|||||||
import subprocess
|
import subprocess
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from time import process_time_ns
|
||||||
|
|
||||||
|
from numpy.core.multiarray import where
|
||||||
from platformdirs import user_cache_dir
|
from platformdirs import user_cache_dir
|
||||||
|
|
||||||
|
from Solver.AdisTS import AdisTS
|
||||||
|
from Solver.Mage import Mage8
|
||||||
from tools import logger_exception
|
from tools import logger_exception
|
||||||
|
|
||||||
from PyQt5 import QtGui
|
from PyQt5 import QtGui
|
||||||
@@ -39,6 +45,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,
|
||||||
)
|
)
|
||||||
from PyQt5.uic import loadUi
|
from PyQt5.uic import loadUi
|
||||||
|
|
||||||
@@ -70,11 +77,29 @@ from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
|
|||||||
from View.AdditionalFiles.Window import AddFileListWindow
|
from View.AdditionalFiles.Window import AddFileListWindow
|
||||||
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 SelectSolverWindow, SolverLogWindow
|
from View.RunSolver.Window import (
|
||||||
|
SelectSolverWindow,
|
||||||
|
SolverLogWindow,
|
||||||
|
CompareSolversWindow
|
||||||
|
)
|
||||||
|
from View.Results.Window import ResultsWindow # , CompareResultsWindow
|
||||||
|
from View.RunSolver.WindowAdisTS import (
|
||||||
|
SelectSolverWindowAdisTS,
|
||||||
|
SolverLogWindowAdisTS
|
||||||
|
)
|
||||||
from View.CheckList.Window import CheckListWindow
|
from View.CheckList.Window import CheckListWindow
|
||||||
from View.Results.Window import ResultsWindow
|
from View.CheckList.WindowAdisTS import CheckListWindowAdisTS
|
||||||
|
from View.Results.WindowAdisTS import ResultsWindowAdisTS
|
||||||
from View.Results.ReadingResultsDialog import ReadingResultsDialog
|
from View.Results.ReadingResultsDialog import ReadingResultsDialog
|
||||||
from View.Debug.Window import ReplWindow
|
from View.Debug.Window import ReplWindow
|
||||||
|
from View.OutputRKAdisTS.Window import OutputRKAdisTSWindow
|
||||||
|
from View.Pollutants.Window import PollutantsWindow
|
||||||
|
from View.D90AdisTS.Window import D90AdisTSWindow
|
||||||
|
from View.DIFAdisTS.Window import DIFAdisTSWindow
|
||||||
|
|
||||||
|
from Solver.Solvers import GenericSolver
|
||||||
|
|
||||||
|
from Model.Results.Results import Results
|
||||||
|
|
||||||
# Optional internal display of documentation for make the application
|
# Optional internal display of documentation for make the application
|
||||||
# package lighter...
|
# package lighter...
|
||||||
@@ -97,6 +122,7 @@ model_action = [
|
|||||||
"action_menu_close", "action_menu_edit", "action_menu_save",
|
"action_menu_close", "action_menu_edit", "action_menu_save",
|
||||||
"action_menu_save_as", "action_toolBar_close", "action_toolBar_save",
|
"action_menu_save_as", "action_toolBar_close", "action_toolBar_save",
|
||||||
"action_menu_numerical_parameter", "action_open_results_from_file",
|
"action_menu_numerical_parameter", "action_open_results_from_file",
|
||||||
|
"action_open_results_adists",
|
||||||
]
|
]
|
||||||
|
|
||||||
other_model_action = [
|
other_model_action = [
|
||||||
@@ -117,8 +143,10 @@ define_model_action = [
|
|||||||
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs",
|
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs",
|
||||||
"action_menu_edit_hydraulic_structures", "action_menu_additional_file",
|
"action_menu_edit_hydraulic_structures", "action_menu_additional_file",
|
||||||
"action_menu_results_last", "action_open_results_from_file",
|
"action_menu_results_last", "action_open_results_from_file",
|
||||||
"action_menu_boundary_conditions_sediment",
|
"action_compare_results", "action_menu_boundary_conditions_sediment",
|
||||||
"action_menu_rep_additional_lines",
|
"action_menu_rep_additional_lines", "action_menu_output_rk",
|
||||||
|
"action_menu_run_adists", "action_menu_pollutants",
|
||||||
|
"action_menu_d90", "action_menu_dif",
|
||||||
]
|
]
|
||||||
|
|
||||||
action = (
|
action = (
|
||||||
@@ -228,6 +256,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
"""
|
"""
|
||||||
actions = {
|
actions = {
|
||||||
# Menu action
|
# Menu action
|
||||||
|
"action_menu_dif": self.open_dif,
|
||||||
|
"action_menu_d90": self.open_d90,
|
||||||
|
"action_menu_pollutants": self.open_pollutants,
|
||||||
|
"action_menu_run_adists": self.select_run_solver_adists,
|
||||||
|
"action_menu_output_rk": self.open_output_rk_adists,
|
||||||
"action_menu_config": self.open_configure,
|
"action_menu_config": self.open_configure,
|
||||||
"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,
|
||||||
@@ -255,6 +288,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
"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_open_results_from_file": self.open_results_from_file,
|
"action_open_results_from_file": self.open_results_from_file,
|
||||||
|
"action_compare_results": self.compare_results,
|
||||||
|
"action_open_results_adists": self.open_results_adists,
|
||||||
# Help
|
# Help
|
||||||
"action_menu_pamhyr_users_wiki": self.open_doc_user,
|
"action_menu_pamhyr_users_wiki": self.open_doc_user,
|
||||||
"action_menu_pamhyr_developers_pdf":
|
"action_menu_pamhyr_developers_pdf":
|
||||||
@@ -760,14 +795,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
|
|
||||||
dlg.setWindowTitle(self._trad["mb_last_open_title"])
|
dlg.setWindowTitle(self._trad["mb_last_open_title"])
|
||||||
dlg.setText(self._trad["mb_last_open_msg"])
|
dlg.setText(self._trad["mb_last_open_msg"])
|
||||||
opt = QMessageBox.Cancel | QMessageBox.Ok # | QMessageBox.Open
|
opt = QMessageBox.No | QMessageBox.Yes # | QMessageBox.Open
|
||||||
|
|
||||||
dlg.setStandardButtons(opt)
|
dlg.setStandardButtons(opt)
|
||||||
dlg.setIcon(QMessageBox.Question)
|
dlg.setIcon(QMessageBox.Question)
|
||||||
|
dlg.button(QMessageBox.Yes).setText(self._trad["Yes"])
|
||||||
|
dlg.button(QMessageBox.No).setText(self._trad["No"])
|
||||||
|
|
||||||
res = dlg.exec()
|
res = dlg.exec()
|
||||||
|
|
||||||
if res == QMessageBox.Ok:
|
if res == QMessageBox.Yes:
|
||||||
old = self.conf.last_study
|
old = self.conf.last_study
|
||||||
file = self.dialog_reopen_study_select_backup(
|
file = self.dialog_reopen_study_select_backup(
|
||||||
self.conf.last_study
|
self.conf.last_study
|
||||||
@@ -780,7 +817,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
elif res == QMessageBox.Open:
|
elif res == QMessageBox.Open:
|
||||||
self.open_model()
|
self.open_model()
|
||||||
return True
|
return True
|
||||||
elif res == QMessageBox.Cancel:
|
elif res == QMessageBox.No:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def dialog_reopen_study_select_backup(self, filename):
|
def dialog_reopen_study_select_backup(self, filename):
|
||||||
@@ -807,6 +844,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
|
|
||||||
dlg.setStandardButtons(opt)
|
dlg.setStandardButtons(opt)
|
||||||
dlg.setIcon(QMessageBox.Warning)
|
dlg.setIcon(QMessageBox.Warning)
|
||||||
|
dlg.button(QMessageBox.Save).setText(self._trad["Save"])
|
||||||
|
dlg.button(QMessageBox.Close).setText(self._trad["Close"])
|
||||||
|
if cancel:
|
||||||
|
dlg.button(QMessageBox.Cancel).setText(self._trad["Cancel"])
|
||||||
|
|
||||||
res = dlg.exec()
|
res = dlg.exec()
|
||||||
|
|
||||||
@@ -881,6 +922,70 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
# SUB WINDOWS #
|
# SUB WINDOWS #
|
||||||
###############
|
###############
|
||||||
|
|
||||||
|
def open_d90(self):
|
||||||
|
if len(self._study.river.d90_adists.lst) != 0:
|
||||||
|
d90_default = self._study.river.d90_adists.lst[0]
|
||||||
|
else:
|
||||||
|
d90_default = self._study.river.d90_adists.new(0)
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
D90AdisTSWindow,
|
||||||
|
data=[self._study, None, d90_default]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
D90AdisTS = D90AdisTSWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self,
|
||||||
|
data=d90_default
|
||||||
|
)
|
||||||
|
D90AdisTS.show()
|
||||||
|
|
||||||
|
def open_dif(self):
|
||||||
|
if len(self._study.river.dif_adists.lst) != 0:
|
||||||
|
dif_default = self._study.river.dif_adists.lst[0]
|
||||||
|
else:
|
||||||
|
dif_default = self._study.river.dif_adists.new(0)
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
DIFAdisTSWindow,
|
||||||
|
data=[self._study, None, dif_default]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
DIFAdisTS = DIFAdisTSWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self,
|
||||||
|
data=dif_default
|
||||||
|
)
|
||||||
|
DIFAdisTS.show()
|
||||||
|
|
||||||
|
def open_pollutants(self):
|
||||||
|
if self.sub_window_exists(
|
||||||
|
PollutantsWindow,
|
||||||
|
data=[self._study, None]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
Pollutants = PollutantsWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
Pollutants.show()
|
||||||
|
|
||||||
|
def open_output_rk_adists(self):
|
||||||
|
if self.sub_window_exists(
|
||||||
|
OutputRKAdisTSWindow,
|
||||||
|
data=[self._study, None]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
Output_RK_AdisTS = OutputRKAdisTSWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
Output_RK_AdisTS.show()
|
||||||
|
|
||||||
def open_configure(self):
|
def open_configure(self):
|
||||||
"""Open configure window
|
"""Open configure window
|
||||||
|
|
||||||
@@ -1206,6 +1311,49 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
if run.exec():
|
if run.exec():
|
||||||
self.run_solver(run.solver)
|
self.run_solver(run.solver)
|
||||||
|
|
||||||
|
def select_run_solver_adists(self):
|
||||||
|
if self._study is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# solver = next(filter(lambda x: x._type == "adistswc",
|
||||||
|
# self.conf.solvers))
|
||||||
|
# solver = next(filter(lambda x: x.name == "AdisTS-WC",
|
||||||
|
# self.conf.solvers))
|
||||||
|
# print(solver._type)
|
||||||
|
# self.run_solver(solver)
|
||||||
|
|
||||||
|
run = SelectSolverWindowAdisTS(
|
||||||
|
study=self._study,
|
||||||
|
config=self.conf,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
if run.exec():
|
||||||
|
self.run_solver_adists(run.solver, run.mage_rep)
|
||||||
|
|
||||||
|
def run_solver_adists(self, solver, mage_rep):
|
||||||
|
if self._study is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
CheckListWindowAdisTS,
|
||||||
|
data=[
|
||||||
|
self._study,
|
||||||
|
self.conf,
|
||||||
|
solver,
|
||||||
|
mage_rep
|
||||||
|
]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
check = CheckListWindowAdisTS(
|
||||||
|
study=self._study,
|
||||||
|
config=self.conf,
|
||||||
|
solver=solver,
|
||||||
|
parent=self,
|
||||||
|
mage_rep=mage_rep,
|
||||||
|
)
|
||||||
|
check.show()
|
||||||
|
|
||||||
def run_solver(self, solver):
|
def run_solver(self, solver):
|
||||||
if self._study is None:
|
if self._study is None:
|
||||||
return
|
return
|
||||||
@@ -1228,6 +1376,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
)
|
)
|
||||||
check.show()
|
check.show()
|
||||||
|
|
||||||
|
def solver_log_adists(self, solver, mage_rep):
|
||||||
|
sol = SolverLogWindowAdisTS(
|
||||||
|
study=self._study,
|
||||||
|
config=self.conf,
|
||||||
|
solver=solver,
|
||||||
|
parent=self,
|
||||||
|
mage_rep=mage_rep,
|
||||||
|
)
|
||||||
|
sol.show()
|
||||||
|
|
||||||
def solver_log(self, solver):
|
def solver_log(self, solver):
|
||||||
sol = SolverLogWindow(
|
sol = SolverLogWindow(
|
||||||
study=self._study,
|
study=self._study,
|
||||||
@@ -1279,14 +1437,69 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
ResultsWindow,
|
ResultsWindow,
|
||||||
data=[
|
data=[
|
||||||
self._study,
|
self._study,
|
||||||
None, # No config
|
None, # No config
|
||||||
solver,
|
results]
|
||||||
results
|
|
||||||
]
|
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
res = ResultsWindow(
|
res = ResultsWindow(
|
||||||
|
study=self._study,
|
||||||
|
results=[results],
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
res.show()
|
||||||
|
|
||||||
|
def open_solver_results_adists(self, solver, results=None):
|
||||||
|
def reading_fn():
|
||||||
|
self._tmp_results = results
|
||||||
|
|
||||||
|
# If no specific results, get last results
|
||||||
|
if results is None:
|
||||||
|
def reading_fn():
|
||||||
|
self._tmp_results = self._last_results
|
||||||
|
|
||||||
|
if self._last_results is None:
|
||||||
|
def reading_fn():
|
||||||
|
self._tmp_results = solver.results(
|
||||||
|
self._study,
|
||||||
|
self._solver_workdir(solver),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Open from file
|
||||||
|
if type(results) is str:
|
||||||
|
logger.info(f"Open results from {os.path.dirname(results)}")
|
||||||
|
|
||||||
|
name = os.path.basename(results).replace(".BIN", "")
|
||||||
|
|
||||||
|
def reading_fn():
|
||||||
|
self._tmp_results = solver.results(
|
||||||
|
self._study,
|
||||||
|
os.path.dirname(results),
|
||||||
|
name=name
|
||||||
|
)
|
||||||
|
|
||||||
|
dlg = ReadingResultsDialog(reading_fn=reading_fn, parent=self)
|
||||||
|
dlg.exec_()
|
||||||
|
results = self._tmp_results
|
||||||
|
|
||||||
|
# No results available
|
||||||
|
if results is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Windows already opened
|
||||||
|
if self.sub_window_exists(
|
||||||
|
ResultsWindowAdisTS,
|
||||||
|
data=[
|
||||||
|
self._study,
|
||||||
|
None, # No config
|
||||||
|
solver,
|
||||||
|
results,
|
||||||
|
self
|
||||||
|
]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
res = ResultsWindowAdisTS(
|
||||||
study=self._study,
|
study=self._study,
|
||||||
solver=solver,
|
solver=solver,
|
||||||
results=results,
|
results=results,
|
||||||
@@ -1308,7 +1521,12 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
if self._last_solver is None:
|
if self._last_solver is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.open_solver_results(self._last_solver, self._last_results)
|
if self._last_solver._type == "mage8":
|
||||||
|
self.open_solver_results(self._last_solver,
|
||||||
|
self._last_results)
|
||||||
|
elif self._last_solver._type == "adistswc":
|
||||||
|
self.open_solver_results_adists(self._last_solver,
|
||||||
|
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:
|
||||||
@@ -1340,10 +1558,171 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
|||||||
file_name = dialog.selectedFiles()
|
file_name = dialog.selectedFiles()
|
||||||
logger.info(f"Select results: {file_name}")
|
logger.info(f"Select results: {file_name}")
|
||||||
self.open_solver_results(
|
self.open_solver_results(
|
||||||
self._last_solver,
|
Mage8("Mage"),
|
||||||
results=file_name[0]
|
results=file_name[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def compare_results(self):
|
||||||
|
if self._study is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
run = CompareSolversWindow(
|
||||||
|
study=self._study,
|
||||||
|
config=self.conf,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
if run.exec():
|
||||||
|
results = self.diff_results(run.solver1,
|
||||||
|
run.solver2)
|
||||||
|
|
||||||
|
# At least one result not available
|
||||||
|
if results is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Windows already opened
|
||||||
|
if self.sub_window_exists(
|
||||||
|
ResultsWindow,
|
||||||
|
data=[
|
||||||
|
self._study,
|
||||||
|
None] + # No config
|
||||||
|
results
|
||||||
|
):
|
||||||
|
#w = self.get_sub_window(
|
||||||
|
#ResultsWindow,
|
||||||
|
#data=[
|
||||||
|
#self._study,
|
||||||
|
#None, # No config
|
||||||
|
#results,
|
||||||
|
#parent=self
|
||||||
|
#])
|
||||||
|
#w.add_result(result[1], run.solver2)
|
||||||
|
return
|
||||||
|
|
||||||
|
res = ResultsWindow(
|
||||||
|
study=self._study,
|
||||||
|
results=results,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
res.show()
|
||||||
|
|
||||||
|
#res = CompareResultsWindow(
|
||||||
|
#study=self._study,
|
||||||
|
#solver=results._solver,
|
||||||
|
#results=results,
|
||||||
|
#parent=self
|
||||||
|
#)
|
||||||
|
#res.show()
|
||||||
|
|
||||||
|
|
||||||
|
def diff_results(self, solver1, solver2):
|
||||||
|
if solver1 is None:
|
||||||
|
# TODO message
|
||||||
|
return None
|
||||||
|
if solver2 is None:
|
||||||
|
# TODO message
|
||||||
|
return None
|
||||||
|
|
||||||
|
solver3 = GenericSolver(solver1.name+" - "+solver2.name)
|
||||||
|
|
||||||
|
result1 = solver1.results(
|
||||||
|
self._study,
|
||||||
|
self._solver_workdir(solver1),
|
||||||
|
)
|
||||||
|
|
||||||
|
if result1 is None:
|
||||||
|
# TODO message
|
||||||
|
return None
|
||||||
|
|
||||||
|
result2 = solver2.results(
|
||||||
|
self._study,
|
||||||
|
self._solver_workdir(solver2),
|
||||||
|
)
|
||||||
|
|
||||||
|
if result2 is None:
|
||||||
|
# TODO message
|
||||||
|
return None
|
||||||
|
|
||||||
|
if result2.get("nb_reach") != result1.get("nb_reach"):
|
||||||
|
# TODO message
|
||||||
|
return None
|
||||||
|
|
||||||
|
if result2.get("nb_profile") != result1.get("nb_profile"):
|
||||||
|
# TODO message
|
||||||
|
return None
|
||||||
|
|
||||||
|
return [result1, result2]
|
||||||
|
|
||||||
|
result3 = Results(self._study, solver3)
|
||||||
|
result3.set("nb_reach", result1.get("nb_reach"))
|
||||||
|
result3.set("nb_profile", result1.get("nb_profile"))
|
||||||
|
ts = list(result1.get("timestamps").intersection(result2.get("timestamps")))
|
||||||
|
result3.set("timestamps", ts)
|
||||||
|
|
||||||
|
for i in range(int(result1.get("nb_reach"))):
|
||||||
|
# Add reach to results reach list
|
||||||
|
r = result3.river.add(i)
|
||||||
|
|
||||||
|
for timestamp in result3.get("timestamps"):
|
||||||
|
for r in range(int(result1.get("nb_reach"))):
|
||||||
|
reach1 = result1.river.reach(r)
|
||||||
|
reach2 = result2.river.reach(r)
|
||||||
|
reach3 = result3.river.reach(r)
|
||||||
|
for p, (profile1, profile2) in enumerate(zip(reach1.profiles, reach2.profiles)):
|
||||||
|
for key in ["Z", "Q"]:
|
||||||
|
d1 = profile1.get_ts_key(timestamp, key)
|
||||||
|
d2 = profile2.get_ts_key(timestamp, key)
|
||||||
|
reach3.set(p, timestamp, key, d1-d2)
|
||||||
|
|
||||||
|
return result3
|
||||||
|
|
||||||
|
def open_results_adists(self):
|
||||||
|
if self._study is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._study.filename == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
options = QFileDialog.Options()
|
||||||
|
options |= QFileDialog.DontUseNativeDialog
|
||||||
|
|
||||||
|
dialog = QFileDialog(self, options=options)
|
||||||
|
dialog.setFileMode(QFileDialog.DirectoryOnly)
|
||||||
|
# QFileDialog.FileMode.ExistingFile)
|
||||||
|
|
||||||
|
if self._last_solver is None:
|
||||||
|
dialog.setDirectory(
|
||||||
|
os.path.dirname(self._study.filename)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
dialog.setDirectory(
|
||||||
|
self._solver_workdir(self._last_solver)
|
||||||
|
)
|
||||||
|
|
||||||
|
if dialog.exec_():
|
||||||
|
dir_path = dialog.selectedFiles()[0]
|
||||||
|
dir_list = os.listdir(dir_path)
|
||||||
|
bin_list = list(filter(lambda s: "bin" in s, dir_list))
|
||||||
|
solver_name = dir_path.split("/")[-2]
|
||||||
|
solver_results = next(filter(lambda x: x.name == solver_name,
|
||||||
|
self.conf.solvers))
|
||||||
|
solver_results_adists = solver_results.results(
|
||||||
|
self._study,
|
||||||
|
repertory=dir_path[:-10], qlog=None) # self._output)
|
||||||
|
logger.info(f"Select results: {dir_path}")
|
||||||
|
if len(bin_list) >= 2 and ("total_sediment.bin" in bin_list):
|
||||||
|
self.open_solver_results_adists(
|
||||||
|
solver_results,
|
||||||
|
results=solver_results_adists,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
dlg = QDialog(self)
|
||||||
|
dlg.setWindowTitle("AdisTS Results")
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
message = QLabel("AdisTS Results not found")
|
||||||
|
layout.addWidget(message)
|
||||||
|
dlg.setLayout(layout)
|
||||||
|
dlg.exec()
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# DOCUMENTATION #
|
# DOCUMENTATION #
|
||||||
#################
|
#################
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.OutputRKAdisTS.UndoCommand import (
|
||||||
|
SetTitleCommand, SetReachCommand, SetRKCommand,
|
||||||
|
SetEnabledCommand, AddCommand, DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class ComboBoxDelegate(QItemDelegate):
|
||||||
|
def __init__(self, data=None, trad=None, parent=None, mode="reaches"):
|
||||||
|
super(ComboBoxDelegate, self).__init__(parent)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._trad = trad
|
||||||
|
self._mode = mode
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QComboBox(parent)
|
||||||
|
|
||||||
|
val = []
|
||||||
|
if self._mode == "rk":
|
||||||
|
reach_id = self._data.Output_rk_adists \
|
||||||
|
.get(index.row()) \
|
||||||
|
.reach
|
||||||
|
|
||||||
|
reach = next(filter(
|
||||||
|
lambda edge: edge.id == reach_id, self._data.edges()
|
||||||
|
))
|
||||||
|
|
||||||
|
if reach_id is not None:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda rk: str(rk), reach.reach.get_rk()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
val = list(
|
||||||
|
map(
|
||||||
|
lambda n: n.name, self._data.edges()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.addItems(
|
||||||
|
[self._trad['not_associated']] +
|
||||||
|
val
|
||||||
|
)
|
||||||
|
|
||||||
|
self.editor.setCurrentText(str(index.data(Qt.DisplayRole)))
|
||||||
|
return self.editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
value = index.data(Qt.DisplayRole)
|
||||||
|
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
text = str(editor.currentText())
|
||||||
|
model.setData(index, text)
|
||||||
|
editor.close()
|
||||||
|
editor.deleteLater()
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
r = QRect(option.rect)
|
||||||
|
if self.editor.windowFlags() & Qt.Popup:
|
||||||
|
if editor.parent() is not None:
|
||||||
|
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
|
||||||
|
editor.setGeometry(r)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def currentItemChanged(self):
|
||||||
|
self.commitData.emit(self.sender())
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data._Output_rk_adists
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] == "title":
|
||||||
|
return self._lst.get(row).title
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
n = self._lst.get(row).reach
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return next(filter(
|
||||||
|
lambda edge: edge.id == n, self._data.edges())
|
||||||
|
).name
|
||||||
|
elif self._headers[column] == "rk":
|
||||||
|
n = self._lst.get(row).rk
|
||||||
|
if n is None:
|
||||||
|
return self._trad['not_associated']
|
||||||
|
return n
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
na = self._trad['not_associated']
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "title":
|
||||||
|
self._undo.push(
|
||||||
|
SetTitleCommand(
|
||||||
|
self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "reach":
|
||||||
|
if value == na:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SetReachCommand(
|
||||||
|
self._lst, row, self._data.edge(value)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif self._headers[column] == "rk":
|
||||||
|
if value == na:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SetRKCommand(
|
||||||
|
self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._lst, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._lst, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def enabled(self, row, enabled, parent=QModelIndex()):
|
||||||
|
self._undo.push(
|
||||||
|
SetEnabledCommand(
|
||||||
|
self._lst, row, enabled
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class OutputRKAdisTSTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(OutputRKAdisTSTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Output RK"] = _translate(
|
||||||
|
"OutputRKAdisTS", "Output RK"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._dict["x"] = _translate("OutputRKAdisTS", "X (m)")
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"title": self._dict["title"],
|
||||||
|
"reach": self._dict["reach"],
|
||||||
|
"rk": self._dict["unit_rk"],
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SetTitleCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, index, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._outputrk_lst.get(self._index).title
|
||||||
|
self._new = str(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._outputrk_lst.get(self._index).title = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._outputrk_lst.get(self._index).title = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetReachCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, index, reach):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._outputrk_lst.get(self._index).reach
|
||||||
|
self._new = reach.id
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
i = self._outputrk_lst.get(self._index)
|
||||||
|
i.reach = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
i = self._outputrk_lst.get(self._index)
|
||||||
|
i.reach = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetRKCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, index, rk):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._outputrk_lst.get(self._index).rk
|
||||||
|
self._new = rk
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._outputrk_lst.get(self._index).rk = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._outputrk_lst.get(self._index).rk = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetEnabledCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, index, enabled):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = not enabled
|
||||||
|
self._new = enabled
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._outputrk_lst.get(self._index).enabled = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._outputrk_lst.get(self._index).enabled = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._outputrk_lst.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._outputrk_lst.new(self._outputrk_lst, self._index)
|
||||||
|
else:
|
||||||
|
self._outputrk_lst.insert(self._index, self._new)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._outputrk = []
|
||||||
|
for row in rows:
|
||||||
|
self._outputrk.append((row, self._outputrk_lst.get(row)))
|
||||||
|
self._outputrk.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._outputrk:
|
||||||
|
self._outputrk_lst.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._outputrk_lst.delete_i(self._rows)
|
||||||
|
|
||||||
|
|
||||||
|
class PasteCommand(QUndoCommand):
|
||||||
|
def __init__(self, outputrk_lst, row, outputrk):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._outputrk_lst = outputrk_lst
|
||||||
|
|
||||||
|
self._row = row
|
||||||
|
self._outputrk = deepcopy(outputrk)
|
||||||
|
self._outputrk.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._outputrk_lst.delete_i(
|
||||||
|
self._tab,
|
||||||
|
range(self._row, self._row + len(self._outputrk))
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for r in self._outputrk:
|
||||||
|
self._outputrk_lst.insert(self._row, r)
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
pyqtSlot, pyqtSignal, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||||
|
|
||||||
|
from View.OutputRKAdisTS.Table import (
|
||||||
|
TableModel, ComboBoxDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Network.GraphWidget import GraphWidget
|
||||||
|
from View.OutputRKAdisTS.Translate import OutputRKAdisTSTranslate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class OutputRKAdisTSWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "OutputRKAdisTS"
|
||||||
|
_pamhyr_name = "Output RK"
|
||||||
|
|
||||||
|
def __init__(self, study=None, config=None, parent=None):
|
||||||
|
trad = OutputRKAdisTSTranslate()
|
||||||
|
name = trad[self._pamhyr_name] + " - " + study.name
|
||||||
|
|
||||||
|
super(OutputRKAdisTSWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._outputrk_lst = self._study.river._Output_rk_adists
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_checkbox()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._table = None
|
||||||
|
|
||||||
|
self._delegate_reach = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
parent=self,
|
||||||
|
mode="reaches"
|
||||||
|
)
|
||||||
|
self._delegate_rk = ComboBoxDelegate(
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
parent=self,
|
||||||
|
mode="rk"
|
||||||
|
)
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["title", "reach", "rk"],
|
||||||
|
delegates={
|
||||||
|
"reach": self._delegate_reach,
|
||||||
|
"rk": self._delegate_rk,
|
||||||
|
},
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
)
|
||||||
|
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
def setup_checkbox(self):
|
||||||
|
self._checkbox = self.find(QCheckBox, f"checkBox")
|
||||||
|
self._set_checkbox_state()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_delete").triggered.connect(self.delete)
|
||||||
|
self._checkbox.clicked.connect(self._set_outputrk_state)
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
table.selectionModel()\
|
||||||
|
.selectionChanged\
|
||||||
|
.connect(self.update)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self.update)
|
||||||
|
self._table.layoutChanged.connect(self.update)
|
||||||
|
|
||||||
|
def index_selected(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0].row()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._outputrk_lst) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
||||||
|
def _set_checkbox_state(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
if row is None:
|
||||||
|
self._checkbox.setEnabled(False)
|
||||||
|
self._checkbox.setChecked(True)
|
||||||
|
else:
|
||||||
|
self._checkbox.setEnabled(True)
|
||||||
|
self._checkbox.setChecked(self._outputrk_lst.get(row).enabled)
|
||||||
|
|
||||||
|
def _set_outputrk_state(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) != 0:
|
||||||
|
for row in rows:
|
||||||
|
if row is not None:
|
||||||
|
self._table.enabled(
|
||||||
|
row,
|
||||||
|
self._checkbox.isChecked()
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._set_checkbox_state()
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from datetime import date, time, datetime, timedelta
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect, QTime, QDateTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QTableView, QAbstractItemView, QSpinBox, QItemDelegate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Pollutants.Edit.UndoCommand import (
|
||||||
|
SetDataCommand, PasteCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.TextAlignmentRole:
|
||||||
|
return Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
return self._data.data[row][column]
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "type":
|
||||||
|
self._undo.push(
|
||||||
|
SetDataCommand(
|
||||||
|
self._data, row, column, int(value)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._undo.push(
|
||||||
|
SetDataCommand(
|
||||||
|
self._data, row, column, float(value)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def paste(self, row, data):
|
||||||
|
if len(data) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.layoutAboutToBeChanged.emit()
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
PasteCommand(
|
||||||
|
self._data, row,
|
||||||
|
list(
|
||||||
|
map(
|
||||||
|
lambda d: self._data.new_from_data(d),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.layoutAboutToBeChanged.emit()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
from View.Pollutants.Translate import PollutantsTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class EditPollutantTranslate(PollutantsTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(EditPollutantTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Edit Pollutant"] = _translate(
|
||||||
|
"Pollutants", "Edit Pollutant"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"type": self._dict["type"],
|
||||||
|
"diametre": self._dict["unit_area"],
|
||||||
|
"rho": self._dict["unit_rho"],
|
||||||
|
"porosity": self._dict["unit_porosity"],
|
||||||
|
"cdc_riv": self._dict["unit_cdc_riv"],
|
||||||
|
"cdc_cas": self._dict["unit_cdc_cas"],
|
||||||
|
"apd": self._dict["unit_apd"],
|
||||||
|
"ac": self._dict["unit_ac"],
|
||||||
|
"bc": self._dict["unit_bc"],
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.Pollutants.Pollutants import Pollutants
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SetDataCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._column = column
|
||||||
|
self._old = self._data.data[self._index][self._column]
|
||||||
|
self._new = new_value
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.data[self._index][self._column] = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data.data[self._index][self._column] = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class PasteCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, row, hs):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._h = hs
|
||||||
|
self._h.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete_i(
|
||||||
|
range(self._row, self._row + len(self._h))
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for h in self._h:
|
||||||
|
self._data.insert(self._row, h)
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
from View.Tools.PamhyrWidget import PamhyrWidget
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
pyqtSlot, pyqtSignal,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView, QDoubleSpinBox, QVBoxLayout,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Pollutants.Edit.Translate import EditPollutantTranslate
|
||||||
|
from View.Pollutants.Edit.Table import TableModel
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class EditPolluantWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "Pollutant"
|
||||||
|
_pamhyr_name = "Edit Pollutant"
|
||||||
|
|
||||||
|
def __init__(self, data=None, study=None, config=None, parent=None):
|
||||||
|
self._data = data
|
||||||
|
trad = EditPollutantTranslate()
|
||||||
|
|
||||||
|
name = trad[self._pamhyr_name]
|
||||||
|
if self._data is not None:
|
||||||
|
name += (
|
||||||
|
f" - {study.name} " +
|
||||||
|
f" - {self._data.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
super(EditPolluantWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(data)
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
headers = {}
|
||||||
|
table_headers = self._trad.get_dict("table_headers")
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=table_headers,
|
||||||
|
editable_headers=table_headers,
|
||||||
|
delegates={},
|
||||||
|
data=self._data,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
opt_data=self._study.time_system
|
||||||
|
)
|
||||||
|
|
||||||
|
table.setModel(self._table)
|
||||||
|
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||||
|
table.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return table.selectionModel()\
|
||||||
|
.selectedRows()[0]\
|
||||||
|
.row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
table = []
|
||||||
|
# table.append(self._data.header)
|
||||||
|
table.append(self._trad.get_dict("table_headers"))
|
||||||
|
|
||||||
|
data = self._data.data
|
||||||
|
for row in rows:
|
||||||
|
table.append(list(data[row]))
|
||||||
|
|
||||||
|
self.copyTableIntoClipboard(table)
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
header, data = self.parseClipboardTable()
|
||||||
|
|
||||||
|
logger.debug(f"paste: h:{header}, d:{data}")
|
||||||
|
|
||||||
|
if len(data) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
row = 0
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) != 0:
|
||||||
|
row = rows[0]
|
||||||
|
|
||||||
|
self._table.paste(row, data)
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Table.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
|
||||||
|
from View.Pollutants.UndoCommand import (
|
||||||
|
SetNameCommand,
|
||||||
|
SetEnabledCommand, AddCommand, DelCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def _setup_lst(self):
|
||||||
|
self._lst = self._data._Pollutants
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._lst)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
return self._lst.get(row).name
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
self._undo.push(
|
||||||
|
SetNameCommand(
|
||||||
|
self._lst, row, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(e)
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._lst, row, self._data.ic_adists
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._lst, rows, self._data.initial_conditions_adists
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def enabled(self, row, enabled, parent=QModelIndex()):
|
||||||
|
self._undo.push(
|
||||||
|
SetEnabledCommand(
|
||||||
|
self._lst, row, enabled
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# translate.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from View.Translate import MainTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class PollutantsTranslate(MainTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(PollutantsTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict["Pollutants"] = _translate(
|
||||||
|
"Pollutants", "Pollutants"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._dict["x"] = _translate("Pollutants", "X (m)")
|
||||||
|
|
||||||
|
self._sub_dict["table_headers"] = {
|
||||||
|
"name": self._dict["name"],
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
# UndoCommand.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SetNameCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutants_lst, index, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._pollutants_lst = pollutants_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = self._pollutants_lst.get(self._index).name
|
||||||
|
self._new = str(new_value)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._pollutants_lst.get(self._index).name = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._pollutants_lst.get(self._index).name = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class SetEnabledCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutants_lst, index, enabled):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._pollutants_lst = pollutants_lst
|
||||||
|
self._index = index
|
||||||
|
self._old = not enabled
|
||||||
|
self._new = enabled
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._pollutants_lst.get(self._index).enabled = self._old
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._pollutants_lst.get(self._index).enabled = self._new
|
||||||
|
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutants_lst, index, data):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._pollutants_lst = pollutants_lst
|
||||||
|
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
self._new_ic_adists = None
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._pollutants_lst.delete_i([self._index])
|
||||||
|
self._data.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._pollutants_lst.new(
|
||||||
|
self._pollutants_lst, self._index
|
||||||
|
)
|
||||||
|
self._new._data = [[0, 0., 0., 0., 0., 0., 0., 0., 0.]]
|
||||||
|
self._new_ic_adists = self._data.new(self._index, self._new.id)
|
||||||
|
else:
|
||||||
|
self._pollutants_lst.insert(self._index, self._new)
|
||||||
|
self._data.insert(self._index, self._new_ic_adists)
|
||||||
|
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutants_lst, rows, data):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._pollutants_lst = pollutants_lst
|
||||||
|
self._inc_pollutants_lst = data
|
||||||
|
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._pollutants = []
|
||||||
|
self._inc_pollutants = []
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
self._pollutants.append((row, self._pollutants_lst.get(row)))
|
||||||
|
self._inc_pollutants.append((row, self._inc_pollutants_lst))
|
||||||
|
self._pollutants.sort()
|
||||||
|
self._inc_pollutants.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._pollutants:
|
||||||
|
self._pollutants_lst.insert(row, el)
|
||||||
|
|
||||||
|
for row, el in self._inc_pollutants:
|
||||||
|
self._inc_pollutants_lst.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._pollutants_lst.delete_i(self._rows)
|
||||||
|
self._inc_pollutants_lst.delete_i(self._rows)
|
||||||
|
|
||||||
|
|
||||||
|
class PasteCommand(QUndoCommand):
|
||||||
|
def __init__(self, pollutants_lst, row, pollutant):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._pollutants_lst = pollutants_lst
|
||||||
|
|
||||||
|
self._row = row
|
||||||
|
self._pollutant = deepcopy(pollutant)
|
||||||
|
self._pollutant.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._pollutants_lst.delete_i(
|
||||||
|
self._tab,
|
||||||
|
range(self._row, self._row + len(self._pollutant))
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for r in self._pollutant:
|
||||||
|
self._pollutants_lst.insert(self._row, r)
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
# Window.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
|
pyqtSlot, pyqtSignal, QItemSelectionModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Pollutants.Table import (
|
||||||
|
TableModel
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Pollutants.Translate import PollutantsTranslate
|
||||||
|
|
||||||
|
from View.Pollutants.Edit.Window import EditPolluantWindow
|
||||||
|
|
||||||
|
from View.InitialConditionsAdisTS.Window import InitialConditionsAdisTSWindow
|
||||||
|
from View.BoundaryConditionsAdisTS.Window import BoundaryConditionAdisTSWindow
|
||||||
|
from View.LateralContributionsAdisTS.Window \
|
||||||
|
import LateralContributionAdisTSWindow
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class PollutantsWindow(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "Pollutants"
|
||||||
|
_pamhyr_name = "Pollutants"
|
||||||
|
|
||||||
|
def __init__(self, study=None, config=None, parent=None):
|
||||||
|
trad = PollutantsTranslate()
|
||||||
|
name = trad[self._pamhyr_name] + " - " + study.name
|
||||||
|
|
||||||
|
super(PollutantsWindow, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._pollutants_lst = self._study._river._Pollutants
|
||||||
|
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_checkbox()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._table = None
|
||||||
|
|
||||||
|
table = self.find(QTableView, f"tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict("table_headers"),
|
||||||
|
editable_headers=["name"],
|
||||||
|
trad=self._trad,
|
||||||
|
data=self._study.river,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
)
|
||||||
|
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(0, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
def setup_checkbox(self):
|
||||||
|
self._checkbox = self.find(QCheckBox, f"checkBox")
|
||||||
|
self._set_checkbox_state()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_delete").triggered.connect(self.delete)
|
||||||
|
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||||
|
self.find(QAction, "action_initial_conditions"
|
||||||
|
).triggered.connect(self.initial_conditions)
|
||||||
|
self.find(QAction, "action_boundary_conditions"
|
||||||
|
).triggered.connect(self.boundary_conditions)
|
||||||
|
self.find(QAction, "action_lateral_contributions"
|
||||||
|
).triggered.connect(self.lateral_contrib)
|
||||||
|
self._checkbox.clicked.connect(self._set_structure_state)
|
||||||
|
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
table.selectionModel()\
|
||||||
|
.selectionChanged\
|
||||||
|
.connect(self.update)
|
||||||
|
|
||||||
|
self._table.dataChanged.connect(self.update)
|
||||||
|
self._table.layoutChanged.connect(self.update)
|
||||||
|
|
||||||
|
def index_selected(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
r = table.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
if len(r) > 0:
|
||||||
|
return r[0].row()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._pollutants_lst) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[-1])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
||||||
|
def edit(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
for row in rows:
|
||||||
|
data = self._pollutants_lst.get(row)
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
EditPolluantWindow,
|
||||||
|
data=[self._study, None, data]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
win = EditPolluantWindow(
|
||||||
|
data=data,
|
||||||
|
study=self._study,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
win.show()
|
||||||
|
|
||||||
|
def initial_conditions(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
pollutant_id = self._pollutants_lst.get(row).id
|
||||||
|
|
||||||
|
ics_adists = next(filter(lambda x: x.pollutant == pollutant_id,
|
||||||
|
self._study.river.ic_adists.lst))
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
InitialConditionsAdisTSWindow,
|
||||||
|
data=[self._study, None, ics_adists]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
initial = InitialConditionsAdisTSWindow(
|
||||||
|
study=self._study,
|
||||||
|
parent=self,
|
||||||
|
data=ics_adists
|
||||||
|
)
|
||||||
|
initial.show()
|
||||||
|
|
||||||
|
def boundary_conditions(self, tab=0):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
pollutant_id = self._pollutants_lst.get(row).id
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
BoundaryConditionAdisTSWindow,
|
||||||
|
data=[self._study, None]
|
||||||
|
):
|
||||||
|
bound = self.get_sub_window(
|
||||||
|
BoundaryConditionAdisTSWindow,
|
||||||
|
data=[self._study, None]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
bound = BoundaryConditionAdisTSWindow(
|
||||||
|
study=self._study, parent=self
|
||||||
|
)
|
||||||
|
bound.show()
|
||||||
|
|
||||||
|
def lateral_contrib(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
pollutant_id = self._pollutants_lst.get(row).id
|
||||||
|
|
||||||
|
if self.sub_window_exists(
|
||||||
|
LateralContributionAdisTSWindow,
|
||||||
|
data=[self._study, pollutant_id, None]
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
lateral = LateralContributionAdisTSWindow(
|
||||||
|
study=self._study,
|
||||||
|
pollutant=pollutant_id,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
lateral.show()
|
||||||
|
|
||||||
|
def _set_checkbox_state(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
if row is None:
|
||||||
|
self._checkbox.setEnabled(False)
|
||||||
|
self._checkbox.setChecked(True)
|
||||||
|
else:
|
||||||
|
self._checkbox.setEnabled(True)
|
||||||
|
self._checkbox.setChecked(self._pollutants_lst.get(row).enabled)
|
||||||
|
|
||||||
|
def _set_structure_state(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) != 0:
|
||||||
|
for row in rows:
|
||||||
|
if row is not None:
|
||||||
|
self._table.enabled(
|
||||||
|
row,
|
||||||
|
self._checkbox.isChecked()
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._set_checkbox_state()
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# CustomPlotValuesSelectionDialog.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrDialog
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QRadioButton, QCheckBox, QVBoxLayout,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Results.translate import ResultsTranslate
|
||||||
|
|
||||||
|
|
||||||
|
class CustomExportAdisDialog(PamhyrDialog):
|
||||||
|
_pamhyr_ui = "CustomExportAdisDialog"
|
||||||
|
_pamhyr_name = "Custom Plot Selection"
|
||||||
|
|
||||||
|
def __init__(self, pollutants, parent=None):
|
||||||
|
trad = ResultsTranslate()
|
||||||
|
super(CustomExportAdisDialog, self).__init__(
|
||||||
|
title=trad[self._pamhyr_name],
|
||||||
|
options=[],
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
if pollutants is not None:
|
||||||
|
self.pollutants = pollutants
|
||||||
|
if "total_sediment" in self.pollutants:
|
||||||
|
self.pollutants.remove("total_sediment")
|
||||||
|
else:
|
||||||
|
self.pollutants = pollutants
|
||||||
|
|
||||||
|
self._available_values_x = self._trad.get_dict("values_x")
|
||||||
|
self._available_values_y = self._trad.get_dict("values_y_pol")
|
||||||
|
|
||||||
|
self.setup_radio_buttons_x()
|
||||||
|
self.setup_radio_buttons_pol()
|
||||||
|
self.setup_check_boxes()
|
||||||
|
|
||||||
|
self.value = None
|
||||||
|
|
||||||
|
def setup_radio_buttons_x(self):
|
||||||
|
self._radio = []
|
||||||
|
layout = self.find(QVBoxLayout, "verticalLayout_x")
|
||||||
|
|
||||||
|
for value in self._available_values_x:
|
||||||
|
btn = QRadioButton(
|
||||||
|
self._available_values_x[value],
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self._radio.append((value, btn))
|
||||||
|
layout.addWidget(btn)
|
||||||
|
|
||||||
|
self._radio[0][1].setChecked(True)
|
||||||
|
layout.addStretch()
|
||||||
|
|
||||||
|
def setup_radio_buttons_pol(self):
|
||||||
|
self._radio2 = []
|
||||||
|
layout = self.find(QVBoxLayout, "verticalLayout_pol")
|
||||||
|
|
||||||
|
for value in self.pollutants:
|
||||||
|
btn = QRadioButton(
|
||||||
|
value,
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self._radio2.append((value, btn))
|
||||||
|
layout.addWidget(btn)
|
||||||
|
|
||||||
|
self._radio2[0][1].setChecked(True)
|
||||||
|
layout.addStretch()
|
||||||
|
|
||||||
|
def setup_check_boxes(self):
|
||||||
|
self._check = []
|
||||||
|
layout = self.find(QVBoxLayout, "verticalLayout_y")
|
||||||
|
|
||||||
|
for value in self._available_values_y:
|
||||||
|
btn = QCheckBox(
|
||||||
|
self._available_values_y[value],
|
||||||
|
parent=self
|
||||||
|
)
|
||||||
|
self._check.append((value, btn))
|
||||||
|
layout.addWidget(btn)
|
||||||
|
|
||||||
|
self._check[0][1].setChecked(True)
|
||||||
|
layout.addStretch()
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
x = next(
|
||||||
|
filter(
|
||||||
|
lambda r: r[1].isChecked(),
|
||||||
|
self._radio
|
||||||
|
)
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
y = list(
|
||||||
|
map(
|
||||||
|
lambda b: b[0],
|
||||||
|
filter(
|
||||||
|
lambda b: b[1].isChecked(),
|
||||||
|
self._check
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
pol = next(
|
||||||
|
filter(
|
||||||
|
lambda r: r[1].isChecked(),
|
||||||
|
self._radio2
|
||||||
|
)
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
self.value = x, y, pol
|
||||||
|
|
||||||
|
super().accept()
|
||||||
@@ -49,7 +49,7 @@ unit = {
|
|||||||
|
|
||||||
class CustomPlot(PamhyrPlot):
|
class CustomPlot(PamhyrPlot):
|
||||||
def __init__(self, x, y, envelop, reach, profile, timestamp,
|
def __init__(self, x, y, envelop, reach, profile, timestamp,
|
||||||
data=None, canvas=None, trad=None,
|
data=None, canvas=None, trad=None, res_id=0,
|
||||||
toolbar=None, parent=None):
|
toolbar=None, parent=None):
|
||||||
super(CustomPlot, self).__init__(
|
super(CustomPlot, self).__init__(
|
||||||
canvas=canvas,
|
canvas=canvas,
|
||||||
@@ -65,6 +65,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
self._reach = reach
|
self._reach = reach
|
||||||
self._profile = profile
|
self._profile = profile
|
||||||
self._timestamp = timestamp
|
self._timestamp = timestamp
|
||||||
|
self._current_res_id = res_id
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Create custom plot for: " +
|
"Create custom plot for: " +
|
||||||
@@ -130,7 +131,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
"""
|
"""
|
||||||
Get SL list for profile p at initial time (initial data)
|
Get SL list for profile p at initial time (initial data)
|
||||||
"""
|
"""
|
||||||
t0 = min(list(self.data.get("timestamps")))
|
t0 = min(list(self.data[self._current_res_id].get("timestamps")))
|
||||||
return map(
|
return map(
|
||||||
lambda p: p.get_ts_key(t0, "sl")[0],
|
lambda p: p.get_ts_key(t0, "sl")[0],
|
||||||
reach.profiles
|
reach.profiles
|
||||||
@@ -146,7 +147,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_ts_zmin(self, profile):
|
def get_ts_zmin(self, profile):
|
||||||
results = self.data
|
results = self.data[self._current_res_id]
|
||||||
nt = len(list(results.get("timestamps")))
|
nt = len(list(results.get("timestamps")))
|
||||||
reach = results.river.reach(self._reach)
|
reach = results.river.reach(self._reach)
|
||||||
berdrock = self.sl_compute_bedrock(reach)
|
berdrock = self.sl_compute_bedrock(reach)
|
||||||
@@ -161,13 +162,13 @@ class CustomPlot(PamhyrPlot):
|
|||||||
sl, z
|
sl, z
|
||||||
),
|
),
|
||||||
ts_z_bedrock, # Bedrock elevations
|
ts_z_bedrock, # Bedrock elevations
|
||||||
asarray(sl)[:,0,:,0] # Sediment layers
|
asarray(sl)[:, 0, :, 0] # Sediment layers
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return ts_z_min
|
return ts_z_min
|
||||||
|
|
||||||
def _draw_rk(self):
|
def _draw_rk(self):
|
||||||
results = self.data
|
results = self.data[self._current_res_id]
|
||||||
reach = results.river.reach(self._reach)
|
reach = results.river.reach(self._reach)
|
||||||
rk = reach.geometry.get_rk()
|
rk = reach.geometry.get_rk()
|
||||||
if reach.has_sediment():
|
if reach.has_sediment():
|
||||||
@@ -485,7 +486,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
self.canvas.axes.legend(lns, labs, loc="best")
|
self.canvas.axes.legend(lns, labs, loc="best")
|
||||||
|
|
||||||
def _redraw_rk(self):
|
def _redraw_rk(self):
|
||||||
results = self.data
|
results = self.data[self._current_res_id]
|
||||||
reach = results.river.reach(self._reach)
|
reach = results.river.reach(self._reach)
|
||||||
rk = reach.geometry.get_rk()
|
rk = reach.geometry.get_rk()
|
||||||
z_min = reach.geometry.get_z_min()
|
z_min = reach.geometry.get_z_min()
|
||||||
@@ -623,7 +624,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
self.canvas.axes.set_xticks(ticks=fx, labels=xt, rotation=45)
|
self.canvas.axes.set_xticks(ticks=fx, labels=xt, rotation=45)
|
||||||
|
|
||||||
def _draw_time(self):
|
def _draw_time(self):
|
||||||
results = self.data
|
results = self.data[self._current_res_id]
|
||||||
reach = results.river.reach(self._reach)
|
reach = results.river.reach(self._reach)
|
||||||
profile = reach.profile(self._profile)
|
profile = reach.profile(self._profile)
|
||||||
|
|
||||||
@@ -774,7 +775,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
|
|
||||||
def _redraw_time(self):
|
def _redraw_time(self):
|
||||||
|
|
||||||
results = self.data
|
results = self.data[self._current_res_id]
|
||||||
reach = results.river.reach(self._reach)
|
reach = results.river.reach(self._reach)
|
||||||
profile = reach.profile(self._profile)
|
profile = reach.profile(self._profile)
|
||||||
ts = list(results.get("timestamps"))
|
ts = list(results.get("timestamps"))
|
||||||
@@ -895,7 +896,7 @@ class CustomPlot(PamhyrPlot):
|
|||||||
elif self._x == "time":
|
elif self._x == "time":
|
||||||
self._draw_time()
|
self._draw_time()
|
||||||
if self._x == "rk":
|
if self._x == "rk":
|
||||||
reach = self.data.river.reach(self._reach)
|
reach = self.data[self._current_res_id].river.reach(self._reach)
|
||||||
profile = reach.profile(self._profile)
|
profile = reach.profile(self._profile)
|
||||||
x = profile.rk
|
x = profile.rk
|
||||||
elif self._x == "time":
|
elif self._x == "time":
|
||||||
@@ -935,6 +936,10 @@ class CustomPlot(PamhyrPlot):
|
|||||||
else:
|
else:
|
||||||
self.draw_current()
|
self.draw_current()
|
||||||
|
|
||||||
|
def set_result(self, res_id):
|
||||||
|
self._current_res_id = res_id
|
||||||
|
self.draw()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._timestamp = timestamp
|
self._timestamp = timestamp
|
||||||
|
|
||||||
@@ -945,11 +950,10 @@ class CustomPlot(PamhyrPlot):
|
|||||||
|
|
||||||
def draw_current(self):
|
def draw_current(self):
|
||||||
if self._x == "rk":
|
if self._x == "rk":
|
||||||
reach = self.data.river.reach(self._reach)
|
reach = self.data[self._current_res_id].river.reach(self._reach)
|
||||||
profile = reach.profile(self._profile)
|
profile = reach.profile(self._profile)
|
||||||
x = profile.rk
|
x = profile.rk
|
||||||
elif self._x == "time":
|
elif self._x == "time":
|
||||||
x = self._timestamp
|
x = self._timestamp
|
||||||
self._current.set_data([x, x], self.canvas.axes.get_ylim())
|
self._current.set_data([x, x], self.canvas.axes.get_ylim())
|
||||||
self.canvas.draw_idle()
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ _translate = QCoreApplication.translate
|
|||||||
|
|
||||||
class PlotAC(PamhyrPlot):
|
class PlotAC(PamhyrPlot):
|
||||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
results=None, reach_id=0, profile_id=0,
|
results=None, reach_id=0, profile_id=0, res_id=0,
|
||||||
parent=None):
|
parent=None):
|
||||||
super(PlotAC, self).__init__(
|
super(PlotAC, self).__init__(
|
||||||
canvas=canvas,
|
canvas=canvas,
|
||||||
@@ -39,9 +39,10 @@ class PlotAC(PamhyrPlot):
|
|||||||
parent=parent
|
parent=parent
|
||||||
)
|
)
|
||||||
|
|
||||||
self._current_timestamp = max(results.get("timestamps"))
|
self._current_timestamp = max(results[res_id].get("timestamps"))
|
||||||
self._current_reach_id = reach_id
|
self._current_reach_id = reach_id
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
|
self._current_res_id = res_id
|
||||||
|
|
||||||
self.label_x = self._trad['x']
|
self.label_x = self._trad['x']
|
||||||
self.label_y = self._trad["unit_elevation"]
|
self.label_y = self._trad["unit_elevation"]
|
||||||
@@ -62,7 +63,7 @@ class PlotAC(PamhyrPlot):
|
|||||||
@results.setter
|
@results.setter
|
||||||
def results(self, results):
|
def results(self, results):
|
||||||
self.data = results
|
self.data = results
|
||||||
self._current_timestamp = max(results.get("timestamps"))
|
self._current_timestamp = max(results[self._current_res_id].get("timestamps"))
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def draw(self, highlight=None):
|
def draw(self, highlight=None):
|
||||||
@@ -71,7 +72,11 @@ class PlotAC(PamhyrPlot):
|
|||||||
if self.results is None:
|
if self.results is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
if self.results[self._current_res_id] is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
self.draw_profile(reach, profile)
|
self.draw_profile(reach, profile)
|
||||||
@@ -137,10 +142,14 @@ class PlotAC(PamhyrPlot):
|
|||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def set_result(self, res_id):
|
||||||
|
self._current_result_id = res_id
|
||||||
|
self.update()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
reach = self.results[self._current_res_id].river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
x = profile.geometry.get_station()
|
x = profile.geometry.get_station()
|
||||||
z = profile.geometry.z()
|
z = profile.geometry.z()
|
||||||
@@ -152,7 +161,7 @@ class PlotAC(PamhyrPlot):
|
|||||||
if not self._init:
|
if not self._init:
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
reach = self.results[self._current_res_id].river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
x = profile.geometry.get_station()
|
x = profile.geometry.get_station()
|
||||||
z = profile.geometry.z()
|
z = profile.geometry.z()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
class PlotH(PamhyrPlot):
|
class PlotH(PamhyrPlot):
|
||||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
results=None, reach_id=0, profile_id=0,
|
results=None, reach_id=0, profile_id=0, res_id=0,
|
||||||
parent=None):
|
parent=None):
|
||||||
super(PlotH, self).__init__(
|
super(PlotH, self).__init__(
|
||||||
canvas=canvas,
|
canvas=canvas,
|
||||||
@@ -47,9 +47,10 @@ class PlotH(PamhyrPlot):
|
|||||||
|
|
||||||
self._mode = "time"
|
self._mode = "time"
|
||||||
|
|
||||||
self._current_timestamp = max(results.get("timestamps"))
|
self._current_timestamp = max(results[res_id].get("timestamps"))
|
||||||
self._current_reach_id = reach_id
|
self._current_reach_id = reach_id
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
|
self._current_res_id = res_id
|
||||||
|
|
||||||
self.label_x = self._trad["unit_time_s"]
|
self.label_x = self._trad["unit_time_s"]
|
||||||
self.label_y = self._trad["unit_discharge"]
|
self.label_y = self._trad["unit_discharge"]
|
||||||
@@ -70,7 +71,7 @@ class PlotH(PamhyrPlot):
|
|||||||
@results.setter
|
@results.setter
|
||||||
def results(self, results):
|
def results(self, results):
|
||||||
self.data = results
|
self.data = results
|
||||||
self._current_timestamp = max(results.get("timestamps"))
|
self._current_timestamp = max(results[self._current_res_id].get("timestamps"))
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def draw(self, highlight=None):
|
def draw(self, highlight=None):
|
||||||
@@ -79,7 +80,11 @@ class PlotH(PamhyrPlot):
|
|||||||
if self.results is None:
|
if self.results is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
if self.results[self._current_res_id] is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
if reach.geometry.number_profiles == 0:
|
if reach.geometry.number_profiles == 0:
|
||||||
@@ -99,7 +104,7 @@ class PlotH(PamhyrPlot):
|
|||||||
self._init = True
|
self._init = True
|
||||||
|
|
||||||
def draw_data(self, reach, profile):
|
def draw_data(self, reach, profile):
|
||||||
self.ts = list(self.results.get("timestamps"))
|
self.ts = list(self.results[self._current_res_id].get("timestamps"))
|
||||||
self.ts.sort()
|
self.ts.sort()
|
||||||
|
|
||||||
x = self.ts
|
x = self.ts
|
||||||
@@ -123,7 +128,7 @@ class PlotH(PamhyrPlot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def draw_max(self, reach):
|
def draw_max(self, reach):
|
||||||
self.ts = list(self.results.get("timestamps"))
|
self.ts = list(self.results[self._current_res_id].get("timestamps"))
|
||||||
self.ts.sort()
|
self.ts.sort()
|
||||||
|
|
||||||
x = self.ts
|
x = self.ts
|
||||||
@@ -152,6 +157,10 @@ class PlotH(PamhyrPlot):
|
|||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def set_result(self, res_id):
|
||||||
|
self._current_result_id = res_id
|
||||||
|
self.update()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.update_current()
|
self.update_current()
|
||||||
@@ -165,7 +174,8 @@ class PlotH(PamhyrPlot):
|
|||||||
self.update_idle()
|
self.update_idle()
|
||||||
|
|
||||||
def update_data(self):
|
def update_data(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
x = self.ts
|
x = self.ts
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
class PlotRKC(PamhyrPlot):
|
class PlotRKC(PamhyrPlot):
|
||||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
results=None, reach_id=0, profile_id=0,
|
results=None, reach_id=0, profile_id=0, res_id=0,
|
||||||
parent=None):
|
parent=None):
|
||||||
super(PlotRKC, self).__init__(
|
super(PlotRKC, self).__init__(
|
||||||
canvas=canvas,
|
canvas=canvas,
|
||||||
@@ -42,10 +42,12 @@ class PlotRKC(PamhyrPlot):
|
|||||||
parent=parent
|
parent=parent
|
||||||
)
|
)
|
||||||
|
|
||||||
self._timestamps = results.get("timestamps")
|
self._timestamps = results[res_id].get("timestamps")
|
||||||
|
self._current_timestamp = max(results[res_id].get("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
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
|
self._current_res_id = res_id
|
||||||
|
|
||||||
self.label_x = self._trad["unit_rk"]
|
self.label_x = self._trad["unit_rk"]
|
||||||
self.label_y = self._trad["unit_elevation"]
|
self.label_y = self._trad["unit_elevation"]
|
||||||
@@ -63,7 +65,7 @@ class PlotRKC(PamhyrPlot):
|
|||||||
@results.setter
|
@results.setter
|
||||||
def results(self, results):
|
def results(self, results):
|
||||||
self.data = results
|
self.data = results
|
||||||
self._current_timestamp = max(results.get("timestamps"))
|
self._current_timestamp = max(results[self._current_res_id].get("timestamps"))
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def draw(self, highlight=None):
|
def draw(self, highlight=None):
|
||||||
@@ -72,7 +74,11 @@ class PlotRKC(PamhyrPlot):
|
|||||||
if self.results is None:
|
if self.results is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
if self.results[self._current_res_id] is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
|
|
||||||
self.draw_bottom(reach)
|
self.draw_bottom(reach)
|
||||||
self.draw_water_elevation(reach)
|
self.draw_water_elevation(reach)
|
||||||
@@ -111,7 +117,7 @@ class PlotRKC(PamhyrPlot):
|
|||||||
lambda hs: hs._input_reach.reach is reach.geometry,
|
lambda hs: hs._input_reach.reach is reach.geometry,
|
||||||
filter(
|
filter(
|
||||||
lambda hs: hs._input_reach is not None,
|
lambda hs: hs._input_reach is not None,
|
||||||
self.results.study.river.hydraulic_structures.lst
|
self.results[self._current_res_id].study.river.hydraulic_structures.lst
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -312,6 +318,10 @@ class PlotRKC(PamhyrPlot):
|
|||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
self.update_current()
|
self.update_current()
|
||||||
|
|
||||||
|
def set_result(self, res_id):
|
||||||
|
self._current_result_id = res_id
|
||||||
|
self.update()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.update()
|
self.update()
|
||||||
@@ -320,7 +330,8 @@ class PlotRKC(PamhyrPlot):
|
|||||||
if not self._init:
|
if not self._init:
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
if reach.has_sediment():
|
if reach.has_sediment():
|
||||||
self.update_bottom_with_bedload()
|
self.update_bottom_with_bedload()
|
||||||
|
|
||||||
@@ -329,7 +340,8 @@ class PlotRKC(PamhyrPlot):
|
|||||||
self.update_idle()
|
self.update_idle()
|
||||||
|
|
||||||
def update_water_elevation(self):
|
def update_water_elevation(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
rk = reach.geometry.get_rk()
|
rk = reach.geometry.get_rk()
|
||||||
z_min = reach.geometry.get_z_min()
|
z_min = reach.geometry.get_z_min()
|
||||||
|
|
||||||
@@ -354,7 +366,8 @@ class PlotRKC(PamhyrPlot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def update_current(self):
|
def update_current(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
rk = reach.geometry.get_rk()
|
rk = reach.geometry.get_rk()
|
||||||
z_min = reach.geometry.get_z_min()
|
z_min = reach.geometry.get_z_min()
|
||||||
z_max = reach.geometry.get_z_max()
|
z_max = reach.geometry.get_z_max()
|
||||||
@@ -367,7 +380,8 @@ class PlotRKC(PamhyrPlot):
|
|||||||
self.canvas.figure.canvas.draw_idle()
|
self.canvas.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def update_bottom_with_bedload(self):
|
def update_bottom_with_bedload(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
rk = reach.geometry.get_rk()
|
rk = reach.geometry.get_rk()
|
||||||
z = self.sl_compute_current_z(reach)
|
z = self.sl_compute_current_z(reach)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,422 @@
|
|||||||
|
# PlotSedAdisDx.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from functools import reduce
|
||||||
|
from datetime import datetime
|
||||||
|
from math import inf
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
QCoreApplication
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class PlotAdis(PamhyrPlot):
|
||||||
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
|
results=None, reach_id=0, profile_id=0,
|
||||||
|
pol_id=[0], key="C", type_pol=[7], parent=None):
|
||||||
|
super(PlotAdis, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
data=results,
|
||||||
|
toolbar=toolbar,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._current_timestamp = max(results.get("timestamps"))
|
||||||
|
self._current_reach_id = reach_id
|
||||||
|
self._current_profile_id = profile_id
|
||||||
|
self._current_pol_id = pol_id
|
||||||
|
self._key = key
|
||||||
|
self._type_pol = type_pol
|
||||||
|
|
||||||
|
self.label_x = self._trad["unit_rk"]
|
||||||
|
self._available_values_y = self._trad.get_dict("values_y_pol")
|
||||||
|
|
||||||
|
self.label = {}
|
||||||
|
self.label_max = {}
|
||||||
|
self.label_min = {}
|
||||||
|
self.label["C"] = _translate("Results", "Concentration")
|
||||||
|
self.label_max["C"] = _translate("Results", "Max Concentration")
|
||||||
|
self.label_min["C"] = _translate("Results", "Min Concentration")
|
||||||
|
if self._type_pol[pol_id[0]] == -1: # Total
|
||||||
|
self.label["M"] = _translate("Results", "Thickness")
|
||||||
|
self.label_max["M"] = _translate("Results", "Max Thickness")
|
||||||
|
self.label_min["M"] = _translate("Results", "Min Thickness")
|
||||||
|
self.label_y = self._available_values_y["unit_thickness"]
|
||||||
|
else:
|
||||||
|
self.label["M"] = _translate("Results", "Mass")
|
||||||
|
self.label_max["M"] = _translate("Results", "Max Mass")
|
||||||
|
self.label_min["M"] = _translate("Results", "Min Mass")
|
||||||
|
self.label_y = self._available_values_y["unit_"+self._key]
|
||||||
|
self.val_id = {}
|
||||||
|
self.val_id["C"] = 0
|
||||||
|
self.val_id["G"] = 1
|
||||||
|
self.val_id["M"] = 2
|
||||||
|
self.val_id["D"] = 3
|
||||||
|
|
||||||
|
self._lines = []
|
||||||
|
|
||||||
|
self._isometric_axis = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def results(self):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
@results.setter
|
||||||
|
def results(self, results):
|
||||||
|
self.data = results
|
||||||
|
self._current_timestamp = max(results.get("timestamps"))
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def draw(self, highlight=None):
|
||||||
|
self.init_axes()
|
||||||
|
|
||||||
|
if self.results is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
|
if reach.geometry.number_profiles == 0:
|
||||||
|
self._init = False
|
||||||
|
return
|
||||||
|
|
||||||
|
self.draw_min_and_max()
|
||||||
|
self.draw_data()
|
||||||
|
self.draw_current()
|
||||||
|
|
||||||
|
self.idle()
|
||||||
|
self._init = True
|
||||||
|
|
||||||
|
def draw_min_and_max(self):
|
||||||
|
x, y, z = self.get_min_and_max()
|
||||||
|
|
||||||
|
self._line_max, = self.canvas.axes.plot(
|
||||||
|
x, y,
|
||||||
|
label=self.label_max[self._key],
|
||||||
|
color=self.color_plot_highlight,
|
||||||
|
linestyle='dotted',
|
||||||
|
lw=1.,
|
||||||
|
markersize=0
|
||||||
|
)
|
||||||
|
|
||||||
|
self._line_min, = self.canvas.axes.plot(
|
||||||
|
x, z,
|
||||||
|
label=self.label_min[self._key],
|
||||||
|
color='g',
|
||||||
|
linestyle='dotted',
|
||||||
|
lw=1.,
|
||||||
|
markersize=0
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_min_and_max(self):
|
||||||
|
x, y, z = self.get_min_and_max()
|
||||||
|
|
||||||
|
self._line_max.set_data(x, y)
|
||||||
|
self._line_min.set_data(x, z)
|
||||||
|
|
||||||
|
def set_reach(self, reach_id):
|
||||||
|
self._current_reach_id = reach_id
|
||||||
|
self._current_profile_id = 0
|
||||||
|
self.draw()
|
||||||
|
self.update_current()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if not self._init:
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
self.update_min_and_max()
|
||||||
|
self.update_data()
|
||||||
|
self.update_idle()
|
||||||
|
|
||||||
|
def hide_current(self):
|
||||||
|
self._current.set_visible(False)
|
||||||
|
|
||||||
|
def show_current(self):
|
||||||
|
self._current.set_visible(True)
|
||||||
|
|
||||||
|
def set_pollutant(self, pol_id):
|
||||||
|
for j in self._lines:
|
||||||
|
j.remove()
|
||||||
|
self._lines = []
|
||||||
|
self._current_pol_id = pol_id
|
||||||
|
self.update_min_and_max()
|
||||||
|
self.draw_data()
|
||||||
|
self.update_current()
|
||||||
|
self.update_idle()
|
||||||
|
|
||||||
|
|
||||||
|
class PlotAdis_dx(PlotAdis):
|
||||||
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
|
results=None, reach_id=0, profile_id=0,
|
||||||
|
pol_id=[0], key="C", type_pol=[7], parent=None):
|
||||||
|
super(PlotAdis_dx, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
toolbar=toolbar,
|
||||||
|
results=results,
|
||||||
|
reach_id=reach_id,
|
||||||
|
profile_id=profile_id,
|
||||||
|
pol_id=pol_id,
|
||||||
|
key=key,
|
||||||
|
type_pol=type_pol,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_data(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
x = reach.geometry.get_rk()
|
||||||
|
|
||||||
|
self._lines = []
|
||||||
|
for i in range(len(self._current_pol_id)):
|
||||||
|
pol_id = self._current_pol_id[i]
|
||||||
|
name = self.data.pollutants_list[pol_id]
|
||||||
|
if self.val_id[self._key] > 0 and self._type_pol[pol_id] == 1:
|
||||||
|
y = [0.0]*len(x)
|
||||||
|
else:
|
||||||
|
y = list(map(lambda p:
|
||||||
|
p.get_ts_key(
|
||||||
|
self._current_timestamp, "pols"
|
||||||
|
)[pol_id][self.val_id[self._key]],
|
||||||
|
reach.profiles))
|
||||||
|
self._lines.append(self.canvas.axes.plot(
|
||||||
|
x, y,
|
||||||
|
label=self.label[self._key]+" "+name,
|
||||||
|
color=self.colors[pol_id % len(self.colors)],
|
||||||
|
**self.plot_default_kargs
|
||||||
|
)[0])
|
||||||
|
|
||||||
|
self.enable_legend()
|
||||||
|
|
||||||
|
def update_data(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
|
x = reach.geometry.get_rk()
|
||||||
|
|
||||||
|
for i in range(len(self._current_pol_id)):
|
||||||
|
pol_id = self._current_pol_id[i]
|
||||||
|
if self.val_id[self._key] > 0 and self._type_pol[pol_id] == 1:
|
||||||
|
continue # no mass
|
||||||
|
else:
|
||||||
|
y = list(map(
|
||||||
|
lambda p: p.get_ts_key(
|
||||||
|
self._current_timestamp, "pols"
|
||||||
|
)[pol_id][self.val_id[self._key]],
|
||||||
|
reach.profiles))
|
||||||
|
|
||||||
|
self._lines[i].set_data(x, y)
|
||||||
|
|
||||||
|
self.update_current()
|
||||||
|
|
||||||
|
def draw_current(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
rk = reach.geometry.get_rk()
|
||||||
|
|
||||||
|
self._current, = self.canvas.axes.plot(
|
||||||
|
[rk[self._current_profile_id], rk[self._current_profile_id]],
|
||||||
|
self.canvas.axes.get_ylim(),
|
||||||
|
color="grey",
|
||||||
|
linestyle="dashed",
|
||||||
|
lw=1.,
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_current(self):
|
||||||
|
self.hide_current()
|
||||||
|
self.canvas.axes.relim(visible_only=True)
|
||||||
|
self.canvas.axes.autoscale_view()
|
||||||
|
self.show_current()
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
rk = reach.geometry.get_rk()
|
||||||
|
self._current.set_data(
|
||||||
|
[rk[self._current_profile_id], rk[self._current_profile_id]],
|
||||||
|
self.canvas.axes.get_ylim()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_min_and_max(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
x = reach.geometry.get_rk()
|
||||||
|
|
||||||
|
y = [-inf]*reach.geometry.number_profiles
|
||||||
|
z = [inf]*reach.geometry.number_profiles
|
||||||
|
for pol_id in self._current_pol_id:
|
||||||
|
if self.val_id[self._key] > 0 and self._type_pol[pol_id] == 1:
|
||||||
|
for i in range(reach.geometry.number_profiles):
|
||||||
|
y[i] = max(0.0, y[i])
|
||||||
|
z[i] = min(0.0, z[i])
|
||||||
|
else:
|
||||||
|
val_id = self.val_id[self._key]
|
||||||
|
for i, p in enumerate(reach.profiles):
|
||||||
|
rk_y = list(map(lambda data_el:
|
||||||
|
data_el[pol_id][val_id],
|
||||||
|
p.get_key("pols")
|
||||||
|
))
|
||||||
|
y[i] = max(np.max(rk_y), y[i])
|
||||||
|
z[i] = min(np.min(rk_y), z[i])
|
||||||
|
|
||||||
|
return x, y, z
|
||||||
|
|
||||||
|
def set_profile(self, profile_id):
|
||||||
|
self._current_profile_id = profile_id
|
||||||
|
self.update_current()
|
||||||
|
self.update_idle()
|
||||||
|
|
||||||
|
def set_timestamp(self, timestamp):
|
||||||
|
self._current_timestamp = timestamp
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
|
||||||
|
class PlotAdis_dt(PlotAdis):
|
||||||
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
|
results=None, reach_id=0, profile_id=0,
|
||||||
|
pol_id=[0], key="C", type_pol=[7], parent=None):
|
||||||
|
super(PlotAdis_dt, self).__init__(
|
||||||
|
canvas=canvas,
|
||||||
|
trad=trad,
|
||||||
|
toolbar=toolbar,
|
||||||
|
results=results,
|
||||||
|
reach_id=reach_id,
|
||||||
|
profile_id=profile_id,
|
||||||
|
pol_id=pol_id,
|
||||||
|
key=key,
|
||||||
|
type_pol=type_pol,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
self.label_x = self._trad["unit_time_s"]
|
||||||
|
|
||||||
|
def draw_data(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
self.ts = list(self.results.get("timestamps"))
|
||||||
|
self.ts.sort()
|
||||||
|
|
||||||
|
self._lines = []
|
||||||
|
x = self.ts
|
||||||
|
for i in range(len(self._current_pol_id)):
|
||||||
|
pol_id = self._current_pol_id[i]
|
||||||
|
name = self.data.pollutants_list[pol_id]
|
||||||
|
if self.val_id[self._key] > 0 and self._type_pol[pol_id] == 1:
|
||||||
|
y = [0.0]*len(x)
|
||||||
|
else:
|
||||||
|
val_id = self.val_id[self._key]
|
||||||
|
y = list(map(lambda data_el:
|
||||||
|
data_el[pol_id][val_id],
|
||||||
|
profile.get_key("pols")
|
||||||
|
))
|
||||||
|
|
||||||
|
self._lines.append(self.canvas.axes.plot(
|
||||||
|
x, y,
|
||||||
|
label=self.label[self._key]+" "+name,
|
||||||
|
color=self.colors[pol_id % len(self.colors)],
|
||||||
|
**self.plot_default_kargs
|
||||||
|
)[0])
|
||||||
|
|
||||||
|
self.set_ticks_time_formater()
|
||||||
|
self.enable_legend()
|
||||||
|
|
||||||
|
def update_data(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
|
x = self.ts
|
||||||
|
for i in range(len(self._current_pol_id)):
|
||||||
|
pol_id = self._current_pol_id[i]
|
||||||
|
if self.val_id[self._key] > 0 and self._type_pol[pol_id] == 1:
|
||||||
|
continue # no mass
|
||||||
|
else:
|
||||||
|
y = list(map(lambda data_el:
|
||||||
|
data_el[pol_id][self.val_id[self._key]],
|
||||||
|
profile.get_key("pols")
|
||||||
|
))
|
||||||
|
|
||||||
|
self._lines[i].set_data(x, y)
|
||||||
|
|
||||||
|
self._current.set_data(
|
||||||
|
[self._current_timestamp, self._current_timestamp],
|
||||||
|
self.canvas.axes.get_ylim()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.update_current()
|
||||||
|
|
||||||
|
def draw_current(self):
|
||||||
|
self._current, = self.canvas.axes.plot(
|
||||||
|
[self._current_timestamp, self._current_timestamp],
|
||||||
|
self.canvas.axes.get_ylim(),
|
||||||
|
color="grey",
|
||||||
|
linestyle="dashed",
|
||||||
|
lw=1.,
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_current(self):
|
||||||
|
self.hide_current()
|
||||||
|
self.canvas.axes.relim(visible_only=True)
|
||||||
|
self.canvas.axes.autoscale_view()
|
||||||
|
self.show_current()
|
||||||
|
self._current.set_data(
|
||||||
|
[self._current_timestamp, self._current_timestamp],
|
||||||
|
self.canvas.axes.get_ylim()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_min_and_max(self):
|
||||||
|
reach = self.results.river.reach(self._current_reach_id)
|
||||||
|
self.ts = list(self.results.get("timestamps"))
|
||||||
|
self.ts.sort()
|
||||||
|
|
||||||
|
x = self.ts
|
||||||
|
|
||||||
|
y = [-inf]*len(x)
|
||||||
|
z = [inf]*len(x)
|
||||||
|
for pol_id in self._current_pol_id:
|
||||||
|
if self.val_id[self._key] > 0 and self._type_pol[pol_id] == 1:
|
||||||
|
for i in range(len(x)):
|
||||||
|
y[i] = max(0.0, y[i])
|
||||||
|
z[i] = min(0.0, z[i])
|
||||||
|
else:
|
||||||
|
val_id = self.val_id[self._key]
|
||||||
|
for i, ts in enumerate(x):
|
||||||
|
ts_y = list(map(lambda p:
|
||||||
|
p.get_ts_key(
|
||||||
|
ts, "pols"
|
||||||
|
)[pol_id][val_id],
|
||||||
|
reach.profiles
|
||||||
|
))
|
||||||
|
y[i] = max(np.max(ts_y), y[i])
|
||||||
|
z[i] = min(np.min(ts_y), z[i])
|
||||||
|
|
||||||
|
return x, y, z
|
||||||
|
|
||||||
|
def set_profile(self, profile_id):
|
||||||
|
self._current_profile_id = profile_id
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def set_timestamp(self, timestamp):
|
||||||
|
self._current_timestamp = timestamp
|
||||||
|
self.update_current()
|
||||||
|
self.update_idle()
|
||||||
@@ -38,7 +38,7 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
class PlotXY(PamhyrPlot):
|
class PlotXY(PamhyrPlot):
|
||||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||||
results=None, reach_id=0, profile_id=0,
|
results=None, reach_id=0, profile_id=0, res_id=0,
|
||||||
display_current=True, parent=None):
|
display_current=True, parent=None):
|
||||||
super(PlotXY, self).__init__(
|
super(PlotXY, self).__init__(
|
||||||
canvas=canvas,
|
canvas=canvas,
|
||||||
@@ -52,10 +52,11 @@ class PlotXY(PamhyrPlot):
|
|||||||
self.line_gl = []
|
self.line_gl = []
|
||||||
self.overflow = []
|
self.overflow = []
|
||||||
|
|
||||||
self._timestamps = results.get("timestamps")
|
self._timestamps = results[res_id].get("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
|
||||||
self._current_profile_id = profile_id
|
self._current_profile_id = profile_id
|
||||||
|
self._current_res_id = res_id
|
||||||
|
|
||||||
self.label_x = _translate("Results", "X (m)")
|
self.label_x = _translate("Results", "X (m)")
|
||||||
self.label_y = _translate("Results", "Y (m)")
|
self.label_y = _translate("Results", "Y (m)")
|
||||||
@@ -136,7 +137,7 @@ class PlotXY(PamhyrPlot):
|
|||||||
@results.setter
|
@results.setter
|
||||||
def results(self, results):
|
def results(self, results):
|
||||||
self.data = results
|
self.data = results
|
||||||
self._current_timestamp = max(results.get("timestamps"))
|
self._current_timestamp = max(results[self._current_res_id].get("timestamps"))
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def draw(self, highlight=None):
|
def draw(self, highlight=None):
|
||||||
@@ -145,8 +146,12 @@ class PlotXY(PamhyrPlot):
|
|||||||
if self.results is None:
|
if self.results is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
if self.results[self._current_res_id] is None:
|
||||||
reaches = self.results.river.reachs
|
return
|
||||||
|
|
||||||
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
|
reaches = results.river.reachs
|
||||||
|
|
||||||
self.draw_profiles(reach, reaches)
|
self.draw_profiles(reach, reaches)
|
||||||
self.draw_water_elevation(reach)
|
self.draw_water_elevation(reach)
|
||||||
@@ -272,7 +277,8 @@ class PlotXY(PamhyrPlot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def draw_water_elevation(self, reach):
|
def draw_water_elevation(self, reach):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], []
|
poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], []
|
||||||
|
|
||||||
for profile in reach.profiles:
|
for profile in reach.profiles:
|
||||||
@@ -308,6 +314,11 @@ class PlotXY(PamhyrPlot):
|
|||||||
self.update_profile()
|
self.update_profile()
|
||||||
self.update_idle()
|
self.update_idle()
|
||||||
|
|
||||||
|
def set_result(self, res_id):
|
||||||
|
self._current_result_id = res_id
|
||||||
|
self.update_profile()
|
||||||
|
self.update_idle()
|
||||||
|
|
||||||
def set_timestamp(self, timestamp):
|
def set_timestamp(self, timestamp):
|
||||||
self._current_timestamp = timestamp
|
self._current_timestamp = timestamp
|
||||||
self.update()
|
self.update()
|
||||||
@@ -322,7 +333,8 @@ class PlotXY(PamhyrPlot):
|
|||||||
self.update_idle()
|
self.update_idle()
|
||||||
|
|
||||||
def update_profile(self):
|
def update_profile(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
self.plot_selected.set_data(
|
self.plot_selected.set_data(
|
||||||
@@ -331,7 +343,8 @@ class PlotXY(PamhyrPlot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def update_water_elevation(self):
|
def update_water_elevation(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], []
|
poly_l_x, poly_l_y, poly_r_x, poly_r_y = [], [], [], []
|
||||||
|
|
||||||
for profile in reach.profiles:
|
for profile in reach.profiles:
|
||||||
@@ -358,7 +371,8 @@ class PlotXY(PamhyrPlot):
|
|||||||
self.water_fill[0].set_xy(poly)
|
self.water_fill[0].set_xy(poly)
|
||||||
|
|
||||||
def update_water_elevation_overflow(self):
|
def update_water_elevation_overflow(self):
|
||||||
reach = self.results.river.reach(self._current_reach_id)
|
results = self.results[self._current_res_id]
|
||||||
|
reach = results.river.reach(self._current_reach_id)
|
||||||
profile = reach.profile(self._current_profile_id)
|
profile = reach.profile(self._current_profile_id)
|
||||||
|
|
||||||
overflow = []
|
overflow = []
|
||||||
|
|||||||
@@ -46,13 +46,15 @@ _translate = QCoreApplication.translate
|
|||||||
|
|
||||||
class TableModel(PamhyrTableModel):
|
class TableModel(PamhyrTableModel):
|
||||||
def _setup_lst(self):
|
def _setup_lst(self):
|
||||||
_river = self._data.river
|
_river = self._data[0].river
|
||||||
if self._opt_data == "reach":
|
if self._opt_data == "reach":
|
||||||
self._lst = _river.reachs
|
self._lst = _river.reachs
|
||||||
elif self._opt_data == "profile":
|
elif self._opt_data == "profile":
|
||||||
self._lst = _river.reach(0).profiles
|
self._lst = _river.reach(0).profiles
|
||||||
elif self._opt_data == "raw_data":
|
elif self._opt_data == "raw_data":
|
||||||
self._lst = _river.reach(0).profiles
|
self._lst = _river.reach(0).profiles
|
||||||
|
elif self._opt_data == "solver":
|
||||||
|
self._lst = self._parent._solvers
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self._timestamp = 0.0
|
self._timestamp = 0.0
|
||||||
@@ -76,6 +78,10 @@ class TableModel(PamhyrTableModel):
|
|||||||
elif self._headers[column] == "rk":
|
elif self._headers[column] == "rk":
|
||||||
v = self._lst[row].rk
|
v = self._lst[row].rk
|
||||||
return f"{v:.4f}"
|
return f"{v:.4f}"
|
||||||
|
elif self._opt_data == "solver":
|
||||||
|
if self._headers[column] == "solver":
|
||||||
|
v = self._lst[row].name
|
||||||
|
return str(v)
|
||||||
elif self._opt_data == "raw_data":
|
elif self._opt_data == "raw_data":
|
||||||
p = self._lst[row]
|
p = self._lst[row]
|
||||||
if self._headers[column] == "name":
|
if self._headers[column] == "name":
|
||||||
@@ -123,7 +129,10 @@ class TableModel(PamhyrTableModel):
|
|||||||
v = self._lst[row].geometry.speed(q, z)
|
v = self._lst[row].geometry.speed(q, z)
|
||||||
a = self._lst[row].geometry.wet_area(z)
|
a = self._lst[row].geometry.wet_area(z)
|
||||||
b = self._lst[row].geometry.wet_width(z)
|
b = self._lst[row].geometry.wet_width(z)
|
||||||
froude = v / sqrt(9.81 * (a / b))
|
if b == 0.0 or a == 0.0:
|
||||||
|
froude = 0.0
|
||||||
|
else:
|
||||||
|
froude = v / sqrt(9.81 * (a / b))
|
||||||
return f"{froude:.4f}"
|
return f"{froude:.4f}"
|
||||||
else:
|
else:
|
||||||
v = 0.0
|
v = 0.0
|
||||||
@@ -132,12 +141,14 @@ class TableModel(PamhyrTableModel):
|
|||||||
return QVariant()
|
return QVariant()
|
||||||
|
|
||||||
def update(self, reach):
|
def update(self, reach):
|
||||||
_river = self._data.river
|
_river = self._data[0].river
|
||||||
|
|
||||||
if self._opt_data == "reach":
|
if self._opt_data == "reach":
|
||||||
self._lst = _river.reachs
|
self._lst = _river.reachs
|
||||||
elif self._opt_data == "profile" or self._opt_data == "raw_data":
|
elif self._opt_data == "profile" or self._opt_data == "raw_data":
|
||||||
self._lst = _river.reach(reach).profiles
|
self._lst = _river.reach(reach).profiles
|
||||||
|
elif self._opt_data == "solver":
|
||||||
|
self._lst = self._parent._solvers
|
||||||
|
|
||||||
self.layoutChanged.emit()
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# TableAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from tools import timer, trace
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence, QColor
|
||||||
|
)
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QAbstractTableModel, QModelIndex,
|
||||||
|
QVariant, pyqtSlot, QCoreApplication,
|
||||||
|
)
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
QStyledItemDelegate, QLineEdit, QAbstractItemView,
|
||||||
|
QComboBox,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||||
|
from View.Results.translate import *
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(PamhyrTableModel):
|
||||||
|
def _setup_lst(self):
|
||||||
|
_river = self._data.river
|
||||||
|
self._timestamp = min(list(self._data.get("timestamps")))
|
||||||
|
if self._opt_data == "reach":
|
||||||
|
self._lst = _river.reachs
|
||||||
|
elif self._opt_data == "profile":
|
||||||
|
self._lst = _river.reach(0).profiles
|
||||||
|
elif self._opt_data == "raw_data":
|
||||||
|
self._lst = _river.reach(0).profiles
|
||||||
|
elif self._opt_data == "pollutants":
|
||||||
|
tmp_list = self._data.pollutants_list.copy()
|
||||||
|
tmp_list.remove("total_sediment")
|
||||||
|
self._lst = tmp_list
|
||||||
|
|
||||||
|
def __init__(self, type_pol, **kwargs):
|
||||||
|
self._type_pol = type_pol
|
||||||
|
super(TableModel, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def data(self, index, role=Qt.DisplayRole):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if self._opt_data == "reach":
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
v = self._lst[row].name
|
||||||
|
return str(v)
|
||||||
|
elif self._opt_data == "pollutants":
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
v = self._lst[row]
|
||||||
|
return str(v)
|
||||||
|
elif self._opt_data == "profile":
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
v = self._lst[row].name
|
||||||
|
return str(v)
|
||||||
|
elif self._headers[column] == "rk":
|
||||||
|
v = self._lst[row].rk
|
||||||
|
return f"{v:.4f}"
|
||||||
|
elif self._opt_data == "raw_data":
|
||||||
|
p = self._lst[row]
|
||||||
|
if self._headers[column] == "name":
|
||||||
|
if p.name == "":
|
||||||
|
return f"{p.rk:.4f}"
|
||||||
|
return f"{p.name}"
|
||||||
|
tmp_list = self._data.pollutants_list.copy()
|
||||||
|
tmp_list.remove("total_sediment")
|
||||||
|
tmp_list2 = self._data.pollutants_list.copy()
|
||||||
|
for pol in tmp_list:
|
||||||
|
pol_index = tmp_list2.index(pol)
|
||||||
|
header_name = pol + " Concentration"
|
||||||
|
if self._headers[column] == header_name:
|
||||||
|
v = self._lst[row].get_ts_key(
|
||||||
|
self._timestamp, "pols")[pol_index][0]
|
||||||
|
return f"{v:.4f}"
|
||||||
|
if (self._headers[column] == pol + " Mass"
|
||||||
|
and self._type_pol[pol_index] == 7):
|
||||||
|
m1 = self._lst[row].get_ts_key(
|
||||||
|
self._timestamp, "pols")[pol_index][1]
|
||||||
|
m2 = self._lst[row].get_ts_key(
|
||||||
|
self._timestamp, "pols")[pol_index][2]
|
||||||
|
m3 = self._lst[row].get_ts_key(
|
||||||
|
self._timestamp, "pols")[pol_index][3]
|
||||||
|
v = m1 + m2 + m3
|
||||||
|
return f"{v:.4f}"
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def update(self, reach):
|
||||||
|
_river = self._data.river
|
||||||
|
|
||||||
|
if self._opt_data == "reach":
|
||||||
|
self._lst = _river.reachs
|
||||||
|
elif self._opt_data == "profile" or self._opt_data == "raw_data":
|
||||||
|
self._lst = _river.reach(reach).profiles
|
||||||
|
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def timestamp(self):
|
||||||
|
return self._timestamp
|
||||||
|
|
||||||
|
def set_timestamp(self, timestamp):
|
||||||
|
self._timestamp = timestamp
|
||||||
|
self.layoutChanged.emit()
|
||||||
+105
-19
@@ -61,7 +61,10 @@ from View.Results.CustomPlot.CustomPlotValuesSelectionDialog import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from View.Results.Table import TableModel
|
from View.Results.Table import TableModel
|
||||||
from View.Results.translate import ResultsTranslate
|
from View.Results.translate import (
|
||||||
|
ResultsTranslate,
|
||||||
|
CompareResultsTranslate
|
||||||
|
)
|
||||||
from View.Stricklers.Window import StricklersWindow
|
from View.Stricklers.Window import StricklersWindow
|
||||||
|
|
||||||
_translate = QCoreApplication.translate
|
_translate = QCoreApplication.translate
|
||||||
@@ -82,16 +85,17 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, study=None, config=None,
|
def __init__(self, study=None, config=None,
|
||||||
solver=None, results=None,
|
results=None, parent=None, trad=None):
|
||||||
parent=None):
|
self._solvers = [r._solver for r in results]
|
||||||
self._solver = solver
|
|
||||||
self._results = results
|
self._results = results
|
||||||
|
self._current_results = 0
|
||||||
|
|
||||||
trad = ResultsTranslate()
|
if trad is None:
|
||||||
|
trad = ResultsTranslate()
|
||||||
name = (
|
name = (
|
||||||
trad[self._pamhyr_name] + " - "
|
trad[self._pamhyr_name] + " - "
|
||||||
+ study.name + " - "
|
+ study.name + " - "
|
||||||
+ self._solver.name
|
+ " - ".join([s.name for s in self._solvers])
|
||||||
)
|
)
|
||||||
|
|
||||||
super(ResultsWindow, self).__init__(
|
super(ResultsWindow, self).__init__(
|
||||||
@@ -102,13 +106,13 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
parent=parent
|
parent=parent
|
||||||
)
|
)
|
||||||
|
|
||||||
self._hash_data.append(self._solver)
|
for r in results:
|
||||||
self._hash_data.append(self._results)
|
self._hash_data.append(r)
|
||||||
|
|
||||||
self._additional_plot = {}
|
self._additional_plot = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._timestamps = sorted(list(self._results.get("timestamps")))
|
self._timestamps = sorted(list(self._results[self._current_results].get("timestamps")))
|
||||||
|
|
||||||
self.setup_slider()
|
self.setup_slider()
|
||||||
self.setup_table()
|
self.setup_table()
|
||||||
@@ -121,20 +125,24 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
|
|
||||||
def setup_table(self):
|
def setup_table(self):
|
||||||
self._table = {}
|
self._table = {}
|
||||||
for t in ["reach", "profile", "raw_data"]:
|
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}"),
|
||||||
data=self._results,
|
data=self._results,
|
||||||
undo=self._undo_stack,
|
undo=self._undo_stack,
|
||||||
opt_data=t
|
opt_data=t,
|
||||||
|
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:
|
||||||
|
table = self.find(QTableView, f"tableView_solver")
|
||||||
|
table.hide()
|
||||||
|
|
||||||
def setup_slider(self):
|
def setup_slider(self):
|
||||||
default_reach = self._results.river.reach(0)
|
default_reach = self._results[self._current_results].river.reach(0)
|
||||||
|
|
||||||
self._slider_time = self.find(QSlider, f"horizontalSlider_time")
|
self._slider_time = self.find(QSlider, f"horizontalSlider_time")
|
||||||
self._slider_time.setMaximum(len(self._timestamps) - 1)
|
self._slider_time.setMaximum(len(self._timestamps) - 1)
|
||||||
@@ -181,6 +189,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
results=self._results,
|
results=self._results,
|
||||||
reach_id=0,
|
reach_id=0,
|
||||||
profile_id=0,
|
profile_id=0,
|
||||||
|
res_id=self._current_results,
|
||||||
trad=self._trad,
|
trad=self._trad,
|
||||||
toolbar=self.toolbar,
|
toolbar=self.toolbar,
|
||||||
display_current=True,
|
display_current=True,
|
||||||
@@ -205,6 +214,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
results=self._results,
|
results=self._results,
|
||||||
reach_id=0,
|
reach_id=0,
|
||||||
profile_id=0,
|
profile_id=0,
|
||||||
|
res_id=self._current_results,
|
||||||
trad=self._trad,
|
trad=self._trad,
|
||||||
toolbar=self.toolbar_2
|
toolbar=self.toolbar_2
|
||||||
)
|
)
|
||||||
@@ -227,6 +237,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
results=self._results,
|
results=self._results,
|
||||||
reach_id=0,
|
reach_id=0,
|
||||||
profile_id=0,
|
profile_id=0,
|
||||||
|
res_id=self._current_results,
|
||||||
trad=self._trad,
|
trad=self._trad,
|
||||||
toolbar=self.toolbar_3
|
toolbar=self.toolbar_3
|
||||||
)
|
)
|
||||||
@@ -250,6 +261,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
results=self._results,
|
results=self._results,
|
||||||
reach_id=0,
|
reach_id=0,
|
||||||
profile_id=0,
|
profile_id=0,
|
||||||
|
res_id=self._current_results,
|
||||||
trad=self._trad,
|
trad=self._trad,
|
||||||
toolbar=self.toolbar_4
|
toolbar=self.toolbar_4
|
||||||
)
|
)
|
||||||
@@ -324,9 +336,10 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
"reach": self._set_current_reach,
|
"reach": self._set_current_reach,
|
||||||
"profile": self._set_current_profile,
|
"profile": self._set_current_profile,
|
||||||
"raw_data": self._set_current_profile_raw_data,
|
"raw_data": self._set_current_profile_raw_data,
|
||||||
|
"solver": self._set_current_results,
|
||||||
}
|
}
|
||||||
|
|
||||||
for t in ["reach", "profile", "raw_data"]:
|
for t in ["reach", "profile", "raw_data", "solver"]:
|
||||||
table = self.find(QTableView, f"tableView_{t}")
|
table = self.find(QTableView, f"tableView_{t}")
|
||||||
|
|
||||||
table.selectionModel()\
|
table.selectionModel()\
|
||||||
@@ -374,7 +387,28 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
table.scrollTo(index)
|
table.scrollTo(index)
|
||||||
|
|
||||||
def update(self, reach_id=None, profile_id=None, timestamp=None):
|
def update_table_selection_solver(self, ind):
|
||||||
|
table = self.find(QTableView, f"tableView_solver")
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(ind, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
self._table["profile"].update(ind)
|
||||||
|
self._table["raw_data"].update(ind)
|
||||||
|
|
||||||
|
def update(self,
|
||||||
|
reach_id=None,
|
||||||
|
profile_id=None,
|
||||||
|
solver_id=None,
|
||||||
|
timestamp=None):
|
||||||
|
|
||||||
if reach_id is not None:
|
if reach_id is not None:
|
||||||
self.plot_xy.set_reach(reach_id)
|
self.plot_xy.set_reach(reach_id)
|
||||||
self.plot_ac.set_reach(reach_id)
|
self.plot_ac.set_reach(reach_id)
|
||||||
@@ -398,6 +432,15 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
|
|
||||||
self.update_table_selection_profile(profile_id)
|
self.update_table_selection_profile(profile_id)
|
||||||
|
|
||||||
|
if solver_id is not None:
|
||||||
|
self.plot_xy.set_result(solver_id)
|
||||||
|
self.plot_ac.set_result(solver_id)
|
||||||
|
self.plot_rkc.set_result(solver_id)
|
||||||
|
self.plot_h.set_result(solver_id)
|
||||||
|
|
||||||
|
for plot in self._additional_plot:
|
||||||
|
self._additional_plot[plot].set_result(solver_id)
|
||||||
|
|
||||||
if timestamp is not None:
|
if timestamp is not None:
|
||||||
self.plot_xy.set_timestamp(timestamp)
|
self.plot_xy.set_timestamp(timestamp)
|
||||||
self.plot_ac.set_timestamp(timestamp)
|
self.plot_ac.set_timestamp(timestamp)
|
||||||
@@ -458,6 +501,14 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
ind = indexes[0].row()
|
ind = indexes[0].row()
|
||||||
self.update(profile_id=ind)
|
self.update(profile_id=ind)
|
||||||
|
|
||||||
|
def _set_current_results(self):
|
||||||
|
table = self.find(QTableView, f"tableView_solver")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.update(solver_id=indexes[0].row())
|
||||||
|
|
||||||
def _set_current_timestamp(self):
|
def _set_current_timestamp(self):
|
||||||
timestamp = self._timestamps[self._slider_time.value()]
|
timestamp = self._timestamps[self._slider_time.value()]
|
||||||
self.update(timestamp=timestamp)
|
self.update(timestamp=timestamp)
|
||||||
@@ -614,13 +665,13 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
if x == "rk":
|
if x == "rk":
|
||||||
timestamp = self._get_current_timestamp()
|
timestamp = self._get_current_timestamp()
|
||||||
first_line.append(f"Time: {timestamp}s")
|
first_line.append(f"Time: {timestamp}s")
|
||||||
val_dict = self._export_rk(timestamp, y, envelop, filename)
|
val_dict = self._export_rk(timestamp, y, envelop)
|
||||||
elif x == "time":
|
elif x == "time":
|
||||||
profile_id = self._get_current_profile()
|
profile_id = self._get_current_profile()
|
||||||
profile = reach.profile(profile_id)
|
profile = reach.profile(profile_id)
|
||||||
pname = profile.name if profile.name != "" else profile.rk
|
pname = profile.name if profile.name != "" else profile.rk
|
||||||
first_line.append(f"Profile: {pname}")
|
first_line.append(f"Profile: {pname}")
|
||||||
val_dict = self._export_time(profile_id, y, filename)
|
val_dict = self._export_time(profile_id, y)
|
||||||
|
|
||||||
with open(filename, 'w', newline='') as csvfile:
|
with open(filename, 'w', newline='') as csvfile:
|
||||||
writer = csv.writer(csvfile, delimiter=',',
|
writer = csv.writer(csvfile, delimiter=',',
|
||||||
@@ -675,7 +726,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
self._additional_plot.pop(tab_widget.tabText(index))
|
self._additional_plot.pop(tab_widget.tabText(index))
|
||||||
tab_widget.removeTab(index)
|
tab_widget.removeTab(index)
|
||||||
|
|
||||||
def _export_rk(self, timestamp, y, envelop, filename):
|
def _export_rk(self, timestamp, y, envelop):
|
||||||
reach = self._results.river.reachs[self._get_current_reach()]
|
reach = self._results.river.reachs[self._get_current_reach()]
|
||||||
dict_x = self._trad.get_dict("values_x")
|
dict_x = self._trad.get_dict("values_x")
|
||||||
dict_y = self._trad.get_dict("values_y")
|
dict_y = self._trad.get_dict("values_y")
|
||||||
@@ -685,7 +736,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
my_dict[dict_x["rk"]] = reach.geometry.get_rk()
|
my_dict[dict_x["rk"]] = reach.geometry.get_rk()
|
||||||
if "bed_elevation" in y:
|
if "bed_elevation" in y:
|
||||||
my_dict[dict_y["bed_elevation"]] = reach.geometry.get_z_min()
|
my_dict[dict_y["bed_elevation"]] = reach.geometry.get_z_min()
|
||||||
#if envelop and reach.has_sediment():
|
# if envelop and reach.has_sediment():
|
||||||
if "discharge" in y:
|
if "discharge" in y:
|
||||||
my_dict[dict_y["discharge"]] = list(
|
my_dict[dict_y["discharge"]] = list(
|
||||||
map(
|
map(
|
||||||
@@ -807,7 +858,7 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
|
|
||||||
return my_dict
|
return my_dict
|
||||||
|
|
||||||
def _export_time(self, profile, y, filename):
|
def _export_time(self, profile, y):
|
||||||
reach = self._results.river.reachs[self._get_current_reach()]
|
reach = self._results.river.reachs[self._get_current_reach()]
|
||||||
profile = reach.profile(profile)
|
profile = reach.profile(profile)
|
||||||
ts = list(self._results.get("timestamps"))
|
ts = list(self._results.get("timestamps"))
|
||||||
@@ -855,3 +906,38 @@ class ResultsWindow(PamhyrWindow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return my_dict
|
return my_dict
|
||||||
|
|
||||||
|
|
||||||
|
#class CompareResultsWindow(ResultsWindow):
|
||||||
|
#_pamhyr_ui = "CompareResults"
|
||||||
|
#_pamhyr_name = "CompareResults"
|
||||||
|
|
||||||
|
#def _path_file(self, filename):
|
||||||
|
#return os.path.abspath(
|
||||||
|
#os.path.join(
|
||||||
|
#os.path.dirname(__file__),
|
||||||
|
#"..", "ui", "ressources", filename
|
||||||
|
#)
|
||||||
|
#)
|
||||||
|
|
||||||
|
#def __init__(self, study=None, config=None,
|
||||||
|
#solver=None, results=None,
|
||||||
|
#parent=None):
|
||||||
|
#self._solvers[0] = solver
|
||||||
|
#self._results = results
|
||||||
|
|
||||||
|
#trad = CompareResultsTranslate()
|
||||||
|
#name = (
|
||||||
|
#trad[self._pamhyr_name] + " - "
|
||||||
|
#+ study.name + " - "
|
||||||
|
#+ self._solvers[0].name
|
||||||
|
#)
|
||||||
|
|
||||||
|
#super(CompareResultsWindow, self).__init__(
|
||||||
|
#study=study,
|
||||||
|
#config=config,
|
||||||
|
#solver=solver,
|
||||||
|
#results=results,
|
||||||
|
#parent=parent,
|
||||||
|
#trad=trad
|
||||||
|
#)
|
||||||
@@ -0,0 +1,744 @@
|
|||||||
|
# WindowAdisTS.py -- Pamhyr
|
||||||
|
# Copyright (C) 2023-2024 INRAE
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import csv
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from tools import trace, timer, logger_exception
|
||||||
|
|
||||||
|
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence, QIcon, QPixmap,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QItemSelectionModel, QTimer,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
|
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
|
||||||
|
QSlider, QLabel, QWidget, QGridLayout,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||||
|
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||||
|
|
||||||
|
from View.Results.PlotSedAdis import PlotAdis_dx, PlotAdis_dt
|
||||||
|
|
||||||
|
from View.Results.CustomPlot.Plot import CustomPlot
|
||||||
|
from View.Results.CustomExportAdis import (
|
||||||
|
CustomExportAdisDialog,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.Results.TableAdisTS import TableModel
|
||||||
|
from View.Results.translate import ResultsTranslate
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class ResultsWindowAdisTS(PamhyrWindow):
|
||||||
|
_pamhyr_ui = "ResultsAdisTS"
|
||||||
|
_pamhyr_name = "Results"
|
||||||
|
|
||||||
|
def _path_file(self, filename):
|
||||||
|
return os.path.abspath(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
"..", "ui", "ressources", filename
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, study=None, config=None,
|
||||||
|
solver=None, results=None,
|
||||||
|
parent=None):
|
||||||
|
self._solver = solver
|
||||||
|
self._results = results
|
||||||
|
|
||||||
|
pollutants_headers = self._results.pollutants_list.copy()
|
||||||
|
|
||||||
|
trad = ResultsTranslate(pollutants_headers)
|
||||||
|
name = (
|
||||||
|
trad[self._pamhyr_name] + " - "
|
||||||
|
+ study.name + " - "
|
||||||
|
+ self._solver.name
|
||||||
|
)
|
||||||
|
|
||||||
|
super(ResultsWindowAdisTS, self).__init__(
|
||||||
|
title=name,
|
||||||
|
study=study,
|
||||||
|
config=config,
|
||||||
|
trad=trad,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
self._hash_data.append(self._solver)
|
||||||
|
self._hash_data.append(self._results)
|
||||||
|
|
||||||
|
self._additional_plot = {}
|
||||||
|
self._current_pol_id = [1]
|
||||||
|
self._reach_id = 0
|
||||||
|
self._profile_id = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._timestamps = sorted(list(self._results.get("timestamps")))
|
||||||
|
self.set_type_pol()
|
||||||
|
self.setup_slider()
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_plots()
|
||||||
|
self.setup_statusbar()
|
||||||
|
self.setup_connections()
|
||||||
|
self.update_table_selection_reach(self._reach_id)
|
||||||
|
self.update_table_selection_profile(self._profile_id)
|
||||||
|
self.update_table_selection_pol(self._current_pol_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger_exception(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
def set_type_pol(self):
|
||||||
|
self._type_pol = []
|
||||||
|
self._pol_id_dict = {}
|
||||||
|
tmp_list = self._results.river.reach(0).profiles
|
||||||
|
for pol_index in range(self._results.nb_pollutants):
|
||||||
|
pol_name = self._results.pollutants_list[pol_index]
|
||||||
|
if pol_name == "total_sediment":
|
||||||
|
self._type_pol.append(-1)
|
||||||
|
else:
|
||||||
|
self._type_pol.append(len(
|
||||||
|
tmp_list[0].get_ts_key(
|
||||||
|
self._timestamps[0], "pols")[pol_index])
|
||||||
|
)
|
||||||
|
self._pol_id_dict[pol_name] = pol_index
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
self._table = {}
|
||||||
|
for t in ["reach", "profile", "pollutants", "raw_data"]:
|
||||||
|
table = self.find(QTableView, f"tableView_{t}")
|
||||||
|
self._table[t] = TableModel(
|
||||||
|
self._type_pol,
|
||||||
|
table_view=table,
|
||||||
|
table_headers=self._trad.get_dict(f"table_headers_{t}"),
|
||||||
|
data=self._results,
|
||||||
|
undo=self._undo_stack,
|
||||||
|
opt_data=t,
|
||||||
|
)
|
||||||
|
self._table[t]._timestamp = self._timestamps[
|
||||||
|
self._slider_time.value()]
|
||||||
|
|
||||||
|
def setup_slider(self):
|
||||||
|
self._slider_time = self.find(QSlider, f"horizontalSlider_time")
|
||||||
|
self._slider_time.setMaximum(len(self._timestamps) - 1)
|
||||||
|
self._slider_time.setValue(len(self._timestamps) - 1)
|
||||||
|
|
||||||
|
self._icon_start = QIcon()
|
||||||
|
self._icon_start.addPixmap(
|
||||||
|
QPixmap(self._path_file("media-playback-start.png"))
|
||||||
|
)
|
||||||
|
|
||||||
|
self._icon_pause = QIcon()
|
||||||
|
self._icon_pause.addPixmap(
|
||||||
|
QPixmap(self._path_file("media-playback-pause.png"))
|
||||||
|
)
|
||||||
|
self._button_play = self.find(QPushButton, f"playButton")
|
||||||
|
self._button_play.setIcon(self._icon_start)
|
||||||
|
self._button_back = self.find(QPushButton, f"backButton")
|
||||||
|
self._button_next = self.find(QPushButton, f"nextButton")
|
||||||
|
self._button_first = self.find(QPushButton, f"firstButton")
|
||||||
|
self._button_last = self.find(QPushButton, f"lastButton")
|
||||||
|
self._timer = QTimer(self)
|
||||||
|
|
||||||
|
def setup_plots(self):
|
||||||
|
self.canvas_cdt = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_cdt.setObjectName("canvas_cdt")
|
||||||
|
self.toolbar_cdt = PamhyrPlotToolbar(
|
||||||
|
self.canvas_cdt, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.plot_layout_cdt = self.find(
|
||||||
|
QVBoxLayout, "verticalLayout_concentration_dt")
|
||||||
|
self.plot_layout_cdt.addWidget(self.toolbar_cdt)
|
||||||
|
self.plot_layout_cdt.addWidget(self.canvas_cdt)
|
||||||
|
|
||||||
|
self.plot_cdt = PlotAdis_dt(
|
||||||
|
canvas=self.canvas_cdt,
|
||||||
|
results=self._results,
|
||||||
|
reach_id=self._reach_id,
|
||||||
|
profile_id=self._profile_id,
|
||||||
|
pol_id=self._current_pol_id,
|
||||||
|
key="C",
|
||||||
|
type_pol=self._type_pol,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar_cdt
|
||||||
|
)
|
||||||
|
self.plot_cdt.draw()
|
||||||
|
|
||||||
|
self.canvas_cdx = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_cdx.setObjectName("canvas_cdx")
|
||||||
|
self.toolbar_cdx = PamhyrPlotToolbar(
|
||||||
|
self.canvas_cdx, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.plot_layout_cdx = self.find(
|
||||||
|
QVBoxLayout, "verticalLayout_concentration_dx")
|
||||||
|
self.plot_layout_cdx.addWidget(self.toolbar_cdx)
|
||||||
|
self.plot_layout_cdx.addWidget(self.canvas_cdx)
|
||||||
|
|
||||||
|
self.plot_cdx = PlotAdis_dx(
|
||||||
|
canvas=self.canvas_cdx,
|
||||||
|
results=self._results,
|
||||||
|
reach_id=self._reach_id,
|
||||||
|
profile_id=self._profile_id,
|
||||||
|
pol_id=self._current_pol_id,
|
||||||
|
key="C",
|
||||||
|
type_pol=self._type_pol,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar_cdx
|
||||||
|
)
|
||||||
|
self.plot_cdx.draw()
|
||||||
|
|
||||||
|
self.canvas_mdx = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_mdx.setObjectName("canvas_mdx")
|
||||||
|
self.toolbar_mdx = PamhyrPlotToolbar(
|
||||||
|
self.canvas_mdx, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.plot_layout_mdx = self.find(
|
||||||
|
QVBoxLayout, "verticalLayout_mass_dx2")
|
||||||
|
self.plot_layout_mdx.addWidget(self.toolbar_mdx)
|
||||||
|
self.plot_layout_mdx.addWidget(self.canvas_mdx)
|
||||||
|
|
||||||
|
self.plot_mdx = PlotAdis_dx(
|
||||||
|
canvas=self.canvas_mdx,
|
||||||
|
results=self._results,
|
||||||
|
reach_id=self._reach_id,
|
||||||
|
profile_id=self._profile_id,
|
||||||
|
pol_id=self._current_pol_id,
|
||||||
|
key="M",
|
||||||
|
type_pol=self._type_pol,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar_mdx
|
||||||
|
)
|
||||||
|
|
||||||
|
self.plot_mdx.draw()
|
||||||
|
|
||||||
|
self.canvas_mdt = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_mdt.setObjectName("canvas_mdt")
|
||||||
|
self.toolbar_mdt = PamhyrPlotToolbar(
|
||||||
|
self.canvas_mdt, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.plot_layout_mdt = self.find(
|
||||||
|
QVBoxLayout, "verticalLayout_mass_dt2")
|
||||||
|
self.plot_layout_mdt.addWidget(self.toolbar_mdt)
|
||||||
|
self.plot_layout_mdt.addWidget(self.canvas_mdt)
|
||||||
|
|
||||||
|
self.plot_mdt = PlotAdis_dt(
|
||||||
|
canvas=self.canvas_mdt,
|
||||||
|
results=self._results,
|
||||||
|
reach_id=self._reach_id,
|
||||||
|
profile_id=self._profile_id,
|
||||||
|
pol_id=self._current_pol_id,
|
||||||
|
key="M",
|
||||||
|
type_pol=self._type_pol,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar_mdt
|
||||||
|
)
|
||||||
|
|
||||||
|
self.plot_mdt.draw()
|
||||||
|
|
||||||
|
# Thickness
|
||||||
|
|
||||||
|
self.canvas_tdx = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_tdx.setObjectName("canvas_tdx")
|
||||||
|
self.toolbar_tdx = PamhyrPlotToolbar(
|
||||||
|
self.canvas_tdx, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.plot_layout_tdx = self.find(
|
||||||
|
QVBoxLayout, "verticalLayout_tdx")
|
||||||
|
self.plot_layout_tdx.addWidget(self.toolbar_tdx)
|
||||||
|
self.plot_layout_tdx.addWidget(self.canvas_tdx)
|
||||||
|
|
||||||
|
self.plot_tdx = PlotAdis_dx(
|
||||||
|
canvas=self.canvas_tdx,
|
||||||
|
results=self._results,
|
||||||
|
reach_id=self._reach_id,
|
||||||
|
profile_id=self._profile_id,
|
||||||
|
pol_id=[0],
|
||||||
|
key="M",
|
||||||
|
type_pol=self._type_pol,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar_tdx
|
||||||
|
)
|
||||||
|
|
||||||
|
self.plot_tdx.draw()
|
||||||
|
|
||||||
|
self.canvas_tdt = MplCanvas(width=5, height=4, dpi=100)
|
||||||
|
self.canvas_tdt.setObjectName("canvas_tdt")
|
||||||
|
self.toolbar_tdt = PamhyrPlotToolbar(
|
||||||
|
self.canvas_tdt, self, items=[
|
||||||
|
"home", "move", "zoom", "save",
|
||||||
|
"iso", "back/forward"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.plot_layout_tdt = self.find(
|
||||||
|
QVBoxLayout, "verticalLayout_tdt")
|
||||||
|
self.plot_layout_tdt.addWidget(self.toolbar_tdt)
|
||||||
|
self.plot_layout_tdt.addWidget(self.canvas_tdt)
|
||||||
|
|
||||||
|
self.plot_tdt = PlotAdis_dt(
|
||||||
|
canvas=self.canvas_tdt,
|
||||||
|
results=self._results,
|
||||||
|
reach_id=self._reach_id,
|
||||||
|
profile_id=self._profile_id,
|
||||||
|
pol_id=[0],
|
||||||
|
key="M",
|
||||||
|
type_pol=self._type_pol,
|
||||||
|
trad=self._trad,
|
||||||
|
toolbar=self.toolbar_tdt
|
||||||
|
)
|
||||||
|
|
||||||
|
self.plot_tdt.draw()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
try:
|
||||||
|
self._timer.stop()
|
||||||
|
except Exception as e:
|
||||||
|
logger_exception(e)
|
||||||
|
|
||||||
|
super(ResultsWindowAdisTS, self).closeEvent(event)
|
||||||
|
|
||||||
|
def _compute_status_label(self):
|
||||||
|
# Timestamp
|
||||||
|
ts = self._timestamps[self._slider_time.value()]
|
||||||
|
|
||||||
|
t0 = datetime.fromtimestamp(0)
|
||||||
|
fts = str(
|
||||||
|
datetime.fromtimestamp(ts) - t0
|
||||||
|
)
|
||||||
|
fts.replace("days", _translate("Results", "days"))\
|
||||||
|
.replace("day", _translate("Results", "day"))
|
||||||
|
|
||||||
|
# Reach
|
||||||
|
table = self.find(QTableView, f"tableView_reach")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
reach = self._study.river.edges()[0]
|
||||||
|
else:
|
||||||
|
reach = self._study.river.edges()[indexes[0].row()]
|
||||||
|
|
||||||
|
# Profile
|
||||||
|
table = self.find(QTableView, f"tableView_profile")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
profile = reach.reach.profile(0)
|
||||||
|
else:
|
||||||
|
profile = reach.reach.profile(indexes[0].row())
|
||||||
|
|
||||||
|
pname = profile.name if profile.name != "" else profile.rk
|
||||||
|
|
||||||
|
# Pollutant
|
||||||
|
table = self.find(QTableView, f"tableView_pollutants")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
pollutant = self._results.pollutants_list[0]
|
||||||
|
else:
|
||||||
|
pollutant = self._results.pollutants_list[indexes[0].row()]
|
||||||
|
|
||||||
|
#return (f"Reach: {reach.name} | " +
|
||||||
|
#f"Profile: {pname} | " +
|
||||||
|
#f"Pollutant: {pollutant}")
|
||||||
|
return (f"{self._trad['reach']}: {reach.name} | " +
|
||||||
|
f"{self._trad['cross_section']}: {pname} | " +
|
||||||
|
f"Pollutant: {pollutant} | " +
|
||||||
|
f"{self._trad['unit_time_s']} : {fts} ({ts} sec)")
|
||||||
|
|
||||||
|
def setup_statusbar(self):
|
||||||
|
txt = self._compute_status_label()
|
||||||
|
self._status_label = QLabel(txt)
|
||||||
|
self.statusbar.addPermanentWidget(self._status_label)
|
||||||
|
|
||||||
|
def update_statusbar(self):
|
||||||
|
txt = self._compute_status_label()
|
||||||
|
self._status_label.setText(txt)
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
# Action
|
||||||
|
actions = {
|
||||||
|
"action_reload": self._reload,
|
||||||
|
# TODO "action_add": self._add_custom_plot,
|
||||||
|
"action_export": self.export,
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in actions:
|
||||||
|
self.find(QAction, action).triggered.connect(
|
||||||
|
actions[action]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table and Plot
|
||||||
|
fun = {
|
||||||
|
"reach": self._set_current_reach,
|
||||||
|
"profile": self._set_current_profile,
|
||||||
|
"pollutants": self._set_current_pol,
|
||||||
|
"raw_data": self._set_current_profile_raw_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
for t in ["reach", "profile", "pollutants"]:
|
||||||
|
table = self.find(QTableView, f"tableView_{t}")
|
||||||
|
|
||||||
|
table.selectionModel()\
|
||||||
|
.selectionChanged\
|
||||||
|
.connect(fun[t])
|
||||||
|
|
||||||
|
self._table[t].dataChanged.connect(fun[t])
|
||||||
|
|
||||||
|
self._slider_time.valueChanged.connect(self._set_current_timestamp)
|
||||||
|
self._button_play.setChecked(False)
|
||||||
|
self._button_play.clicked.connect(self._pause)
|
||||||
|
self._button_back.clicked.connect(self._back)
|
||||||
|
self._button_next.clicked.connect(self._next)
|
||||||
|
self._button_first.clicked.connect(self._first)
|
||||||
|
self._button_last.clicked.connect(self._last)
|
||||||
|
self._timer.timeout.connect(self._update_slider)
|
||||||
|
|
||||||
|
def update_table_selection_reach(self, ind):
|
||||||
|
table = self.find(QTableView, f"tableView_reach")
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(ind, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
self._table["profile"].update(ind)
|
||||||
|
self._table["raw_data"].update(ind)
|
||||||
|
|
||||||
|
def update_table_selection_profile(self, ind):
|
||||||
|
for t in ["profile"]:
|
||||||
|
table = self.find(QTableView, f"tableView_{t}")
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(ind, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
def update_table_selection_pol(self, ind):
|
||||||
|
for t in ["pollutants"]:
|
||||||
|
table = self.find(QTableView, f"tableView_{t}")
|
||||||
|
selectionModel = table.selectionModel()
|
||||||
|
index = table.model().index(ind[0] - 1, 0)
|
||||||
|
|
||||||
|
selectionModel.select(
|
||||||
|
index,
|
||||||
|
QItemSelectionModel.Rows |
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Select
|
||||||
|
)
|
||||||
|
table.scrollTo(index)
|
||||||
|
|
||||||
|
def update(self, reach_id=None, profile_id=None,
|
||||||
|
pol_id=None, timestamp=None):
|
||||||
|
if reach_id is not None:
|
||||||
|
self._reach_id = reach_id
|
||||||
|
self.plot_cdt.set_reach(reach_id)
|
||||||
|
self.plot_cdx.set_reach(reach_id)
|
||||||
|
self.plot_mdx.set_reach(reach_id)
|
||||||
|
self.plot_mdt.set_reach(reach_id)
|
||||||
|
self.plot_tdt.set_reach(reach_id)
|
||||||
|
self.plot_tdx.set_reach(reach_id)
|
||||||
|
|
||||||
|
self.update_table_selection_reach(reach_id)
|
||||||
|
self.update_table_selection_profile(0)
|
||||||
|
|
||||||
|
if profile_id is not None:
|
||||||
|
self._profile_id = profile_id
|
||||||
|
self.plot_cdt.set_profile(profile_id)
|
||||||
|
self.plot_cdx.set_profile(profile_id)
|
||||||
|
self.plot_mdx.set_profile(profile_id)
|
||||||
|
self.plot_mdt.set_profile(profile_id)
|
||||||
|
self.plot_tdt.set_profile(profile_id)
|
||||||
|
self.plot_tdx.set_profile(profile_id)
|
||||||
|
|
||||||
|
self.update_table_selection_profile(profile_id)
|
||||||
|
|
||||||
|
if pol_id is not None:
|
||||||
|
self._current_pol_id = [p+1 for p in pol_id] # rm total_sediment
|
||||||
|
self.plot_cdt.set_pollutant(self._current_pol_id)
|
||||||
|
self.plot_cdx.set_pollutant(self._current_pol_id)
|
||||||
|
self.plot_mdx.set_pollutant(self._current_pol_id)
|
||||||
|
self.plot_mdt.set_pollutant(self._current_pol_id)
|
||||||
|
|
||||||
|
if timestamp is not None:
|
||||||
|
self.plot_cdt.set_timestamp(timestamp)
|
||||||
|
self.plot_cdx.set_timestamp(timestamp)
|
||||||
|
self.plot_mdx.set_timestamp(timestamp)
|
||||||
|
self.plot_mdt.set_timestamp(timestamp)
|
||||||
|
self.plot_tdt.set_timestamp(timestamp)
|
||||||
|
self.plot_tdx.set_timestamp(timestamp)
|
||||||
|
|
||||||
|
self._table["raw_data"].set_timestamp(timestamp)
|
||||||
|
|
||||||
|
self.update_statusbar()
|
||||||
|
|
||||||
|
def _get_current_timestamp(self):
|
||||||
|
return self._timestamps[
|
||||||
|
self._slider_time.value()
|
||||||
|
]
|
||||||
|
|
||||||
|
def _set_current_reach(self):
|
||||||
|
table = self.find(QTableView, f"tableView_reach")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.update(reach_id=indexes[0].row())
|
||||||
|
|
||||||
|
def _set_current_profile(self):
|
||||||
|
table = self.find(QTableView, f"tableView_profile")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.update(profile_id=indexes[0].row())
|
||||||
|
|
||||||
|
def _set_current_pol(self):
|
||||||
|
table = self.find(QTableView, f"tableView_pollutants")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
rows = [i.row() for i in indexes]
|
||||||
|
if len(indexes) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.update(pol_id=rows)
|
||||||
|
|
||||||
|
def _set_current_profile_raw_data(self):
|
||||||
|
table = self.find(QTableView, f"tableView_raw_data")
|
||||||
|
indexes = table.selectedIndexes()
|
||||||
|
if len(indexes) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
ind = indexes[0].row()
|
||||||
|
self.update(profile_id=ind)
|
||||||
|
self._slider_profile.setValue(ind)
|
||||||
|
|
||||||
|
def _set_current_timestamp(self):
|
||||||
|
timestamp = self._timestamps[self._slider_time.value()]
|
||||||
|
self.update(timestamp=timestamp)
|
||||||
|
|
||||||
|
def _reload_plots(self):
|
||||||
|
self.plot_cdt.results = self._results
|
||||||
|
self.plot_cdx.results = self._results
|
||||||
|
self.plot_mdx.results = self._results
|
||||||
|
self.plot_mdt.results = self._results
|
||||||
|
self.plot_tdt.results = self._results
|
||||||
|
self.plot_tdx.results = self._results
|
||||||
|
|
||||||
|
self.plot_cdt.draw()
|
||||||
|
self.plot_cdx.draw()
|
||||||
|
self.plot_mdx.draw()
|
||||||
|
self.plot_mdt.draw()
|
||||||
|
self.plot_tdt.draw()
|
||||||
|
self.plot_tdx.draw()
|
||||||
|
|
||||||
|
def _reload_slider(self):
|
||||||
|
self._slider_time = self.find(QSlider, f"horizontalSlider_time")
|
||||||
|
self._slider_time.setMaximum(len(self._timestamps) - 1)
|
||||||
|
self._slider_time.setValue(len(self._timestamps) - 1)
|
||||||
|
|
||||||
|
def _reload(self):
|
||||||
|
logger.debug("Reload results...")
|
||||||
|
self._results = self._results.reload()
|
||||||
|
|
||||||
|
self._timestamps = sorted(list(self._results.get("timestamps")))
|
||||||
|
|
||||||
|
self._reload_plots()
|
||||||
|
self._reload_slider()
|
||||||
|
|
||||||
|
def _copy(self):
|
||||||
|
logger.info("TODO: copy")
|
||||||
|
|
||||||
|
def _paste(self):
|
||||||
|
logger.info("TODO: paste")
|
||||||
|
|
||||||
|
def _undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def _redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
||||||
|
# play / pause buttons
|
||||||
|
def _update_slider(self):
|
||||||
|
if self._slider_time.value() == self._slider_time.maximum():
|
||||||
|
self._slider_time.setValue(self._slider_time.minimum())
|
||||||
|
else:
|
||||||
|
self._slider_time.setValue(self._slider_time.value()+1)
|
||||||
|
|
||||||
|
def _next(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.value()+1)
|
||||||
|
|
||||||
|
def _back(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.value()-1)
|
||||||
|
|
||||||
|
def _first(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.minimum())
|
||||||
|
|
||||||
|
def _last(self):
|
||||||
|
self._slider_time.setValue(self._slider_time.maximum())
|
||||||
|
|
||||||
|
def _pause(self):
|
||||||
|
if self._button_play.isChecked():
|
||||||
|
self._button_next.setEnabled(False)
|
||||||
|
self._button_back.setEnabled(False)
|
||||||
|
self._button_first.setEnabled(False)
|
||||||
|
self._button_last.setEnabled(False)
|
||||||
|
self._timer.start(100)
|
||||||
|
self._button_play.setIcon(self._icon_pause)
|
||||||
|
else:
|
||||||
|
self._timer.stop()
|
||||||
|
self._button_next.setEnabled(True)
|
||||||
|
self._button_back.setEnabled(True)
|
||||||
|
self._button_first.setEnabled(True)
|
||||||
|
self._button_last.setEnabled(True)
|
||||||
|
self._button_play.setIcon(self._icon_start)
|
||||||
|
|
||||||
|
def export(self):
|
||||||
|
|
||||||
|
pols = self._results.pollutants_list.copy()
|
||||||
|
dlg = CustomExportAdisDialog(pollutants=pols,
|
||||||
|
parent=self)
|
||||||
|
if dlg.exec():
|
||||||
|
x, y, pol = dlg.value
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.file_dialog(
|
||||||
|
select_file="AnyFile",
|
||||||
|
callback=lambda f: self.export_to(f[0], x, y, pol),
|
||||||
|
default_suffix=".csv",
|
||||||
|
file_filter=["CSV (*.csv)"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def export_to(self, filename, x, y, pol):
|
||||||
|
timestamps = sorted(self._results.get("timestamps"))
|
||||||
|
reach = self._results.river.reachs[self._reach_id]
|
||||||
|
first_line = [f"Study: {self._results.study.name}",
|
||||||
|
f"Reach: {reach.name}"]
|
||||||
|
if x == "rk":
|
||||||
|
timestamp = self._get_current_timestamp()
|
||||||
|
first_line.append(f"Time: {timestamp}s")
|
||||||
|
val_dict = self._export_rk(timestamp, y, pol)
|
||||||
|
elif x == "time":
|
||||||
|
profile = reach.profile(self._profile_id)
|
||||||
|
pname = profile.name if profile.name != "" else profile.rk
|
||||||
|
first_line.append(f"Profile: {pname}")
|
||||||
|
val_dict = self._export_time(self._profile_id, y, pol)
|
||||||
|
with open(filename, 'w', newline='') as csvfile:
|
||||||
|
writer = csv.writer(csvfile, delimiter=',',
|
||||||
|
quotechar='|', quoting=csv.QUOTE_MINIMAL)
|
||||||
|
dict_x = self._trad.get_dict("values_x")
|
||||||
|
header = []
|
||||||
|
writer.writerow(first_line)
|
||||||
|
for text in val_dict.keys():
|
||||||
|
header.append(text)
|
||||||
|
writer.writerow(header)
|
||||||
|
for row in range(len(val_dict[dict_x[x]])):
|
||||||
|
line = []
|
||||||
|
for var in val_dict.keys():
|
||||||
|
line.append(val_dict[var][row])
|
||||||
|
writer.writerow(line)
|
||||||
|
|
||||||
|
def _export_rk(self, timestamp, y, pol):
|
||||||
|
reach = self._results.river.reachs[self._reach_id]
|
||||||
|
dict_x = self._trad.get_dict("values_x")
|
||||||
|
dict_y = self._trad.get_dict("values_y_pol")
|
||||||
|
my_dict = {}
|
||||||
|
my_dict[dict_x["rk"]] = reach.geometry.get_rk()
|
||||||
|
val_id = {}
|
||||||
|
val_id["unit_C"] = 0
|
||||||
|
val_id["unit_M"] = 2
|
||||||
|
val_id["unit_thickness"] = 2
|
||||||
|
for unit in y:
|
||||||
|
if unit == "unit_thickness":
|
||||||
|
pol_id = 0
|
||||||
|
else:
|
||||||
|
pol_id = self._pol_id_dict[pol]
|
||||||
|
if unit == "unit_M" and self._type_pol[pol_id] == 1:
|
||||||
|
my_dict[dict_y[unit]] = [0.0]*len(reach.profiles)
|
||||||
|
else:
|
||||||
|
my_dict[dict_y[unit]] = list(map(lambda p:
|
||||||
|
p.get_ts_key(
|
||||||
|
timestamp, "pols"
|
||||||
|
)[pol_id][val_id[unit]],
|
||||||
|
reach.profiles))
|
||||||
|
|
||||||
|
return my_dict
|
||||||
|
|
||||||
|
def _export_time(self, profile, y, pol):
|
||||||
|
reach = self._results.river.reachs[self._reach_id]
|
||||||
|
profile = reach.profile(profile)
|
||||||
|
ts = list(self._results.get("timestamps"))
|
||||||
|
ts.sort()
|
||||||
|
dict_x = self._trad.get_dict("values_x")
|
||||||
|
dict_y = self._trad.get_dict("values_y_pol")
|
||||||
|
my_dict = {}
|
||||||
|
my_dict[dict_x["time"]] = ts
|
||||||
|
val_id = {}
|
||||||
|
val_id["unit_C"] = 0
|
||||||
|
val_id["unit_M"] = 2
|
||||||
|
val_id["unit_thickness"] = 2
|
||||||
|
for unit in y:
|
||||||
|
if unit == "unit_thickness":
|
||||||
|
pol_id = 0
|
||||||
|
else:
|
||||||
|
pol_id = self._pol_id_dict[pol]
|
||||||
|
if unit == "unit_M" and self._type_pol[pol_id] == 1:
|
||||||
|
my_dict[dict_y[unit]] = [0.0]*len(ts)
|
||||||
|
else:
|
||||||
|
my_dict[dict_y[unit]] = list(map(lambda data_el:
|
||||||
|
data_el[pol_id][val_id[unit]],
|
||||||
|
profile.get_key("pols")
|
||||||
|
))
|
||||||
|
|
||||||
|
return my_dict
|
||||||
@@ -24,7 +24,14 @@ _translate = QCoreApplication.translate
|
|||||||
|
|
||||||
|
|
||||||
class ResultsTranslate(MainTranslate):
|
class ResultsTranslate(MainTranslate):
|
||||||
def __init__(self):
|
def __init__(self, pollutants=None):
|
||||||
|
if pollutants is not None:
|
||||||
|
self.pollutants = pollutants
|
||||||
|
if "total_sediment" in self.pollutants:
|
||||||
|
self.pollutants.remove("total_sediment")
|
||||||
|
else:
|
||||||
|
self.pollutants = pollutants
|
||||||
|
|
||||||
super(ResultsTranslate, self).__init__()
|
super(ResultsTranslate, self).__init__()
|
||||||
|
|
||||||
self._dict["Results"] = _translate("Results", "Results")
|
self._dict["Results"] = _translate("Results", "Results")
|
||||||
@@ -48,11 +55,23 @@ class ResultsTranslate(MainTranslate):
|
|||||||
"name": _translate("Results", "Reach name"),
|
"name": _translate("Results", "Reach name"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_phys_var"] = {
|
||||||
|
"name": _translate("Results", "Variables names"),
|
||||||
|
}
|
||||||
|
|
||||||
self._sub_dict["table_headers_profile"] = {
|
self._sub_dict["table_headers_profile"] = {
|
||||||
"name": self._dict["name"],
|
"name": _translate("Results", "Profile name"),
|
||||||
"rk": self._dict["unit_rk"],
|
"rk": self._dict["unit_rk"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_solver"] = {
|
||||||
|
"solver": _translate("Results", "Solver"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_pollutants"] = {
|
||||||
|
"name": _translate("Results", "Pollutant name"),
|
||||||
|
}
|
||||||
|
|
||||||
self._sub_dict["table_headers_raw_data"] = {
|
self._sub_dict["table_headers_raw_data"] = {
|
||||||
"name": _translate("Results", "Profile"),
|
"name": _translate("Results", "Profile"),
|
||||||
"water_elevation": self._dict["unit_water_elevation"],
|
"water_elevation": self._dict["unit_water_elevation"],
|
||||||
@@ -83,6 +102,22 @@ class ResultsTranslate(MainTranslate):
|
|||||||
"wet_area": self._dict["unit_wet_area"],
|
"wet_area": self._dict["unit_wet_area"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.pollutants is not None:
|
||||||
|
self._sub_dict["table_headers_raw_data"] = {
|
||||||
|
"name": _translate("Results", "Profile"),
|
||||||
|
}
|
||||||
|
tmp = self._sub_dict["table_headers_raw_data"]
|
||||||
|
for pol in self.pollutants:
|
||||||
|
tmp[pol + " Concentration"] = pol + "\n Concentration"
|
||||||
|
tmp[pol + " Mass"] = pol + "\n Mass"
|
||||||
|
else:
|
||||||
|
self._sub_dict["table_headers_raw_data"] = {
|
||||||
|
"name": _translate("Results", "Profile"),
|
||||||
|
"water_elevation": self._dict["unit_water_elevation"],
|
||||||
|
"discharge": self._dict["unit_discharge"],
|
||||||
|
"discharge": self._dict["unit_discharge"],
|
||||||
|
}
|
||||||
|
|
||||||
self._sub_dict["values_y_envelop"] = {
|
self._sub_dict["values_y_envelop"] = {
|
||||||
"min_bed_elevation": self._dict["unit_min_bed_elevation"],
|
"min_bed_elevation": self._dict["unit_min_bed_elevation"],
|
||||||
"max_bed_elevation": self._dict["unit_max_bed_elevation"],
|
"max_bed_elevation": self._dict["unit_max_bed_elevation"],
|
||||||
@@ -95,3 +130,41 @@ class ResultsTranslate(MainTranslate):
|
|||||||
"min_depth": self._dict["unit_min_depth"],
|
"min_depth": self._dict["unit_min_depth"],
|
||||||
"max_depth": self._dict["unit_max_depth"],
|
"max_depth": self._dict["unit_max_depth"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._sub_dict["values_y_pol"] = {
|
||||||
|
"unit_C": self._dict["unit_concentration"],
|
||||||
|
"unit_M": self._dict["unit_mass"],
|
||||||
|
"unit_thickness": self._dict["unit_thickness"],
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompareResultsTranslate(ResultsTranslate):
|
||||||
|
def __init__(self):
|
||||||
|
super(CompareResultsTranslate, self).__init__()
|
||||||
|
|
||||||
|
self._dict['label_water'] = u"Δ "+_translate("Results", "Water elevation")
|
||||||
|
self._dict['unit_elevation'] = u"Δ "+self._dict["unit_elevation"]
|
||||||
|
|
||||||
|
self._sub_dict["table_headers_raw_data"] = {
|
||||||
|
"name": _translate("Results", "Profile"),
|
||||||
|
"water_elevation": u"Δ "+self._dict["unit_water_elevation"],
|
||||||
|
"discharge": u"Δ "+self._dict["unit_discharge"],
|
||||||
|
"velocity": u"Δ "+self._dict["unit_velocity"],
|
||||||
|
"width": u"Δ "+self._dict["unit_width"],
|
||||||
|
"depth": u"Δ "+self._dict["unit_depth"],
|
||||||
|
"mean_depth": u"Δ "+self._dict["unit_mean_depth"],
|
||||||
|
"wet_area": u"Δ "+self._dict["unit_wet_area"],
|
||||||
|
"wet_perimeter": u"Δ "+self._dict["unit_wet_perimeter"],
|
||||||
|
"hydraulic_radius": u"Δ "+self._dict["unit_hydraulic_radius"],
|
||||||
|
"froude": u"Δ "+self._dict["unit_froude"],
|
||||||
|
}
|
||||||
|
|
||||||
|
self._sub_dict["values_y"] = {
|
||||||
|
"bed_elevation": u"Δ "+self._dict["unit_bed_elevation"],
|
||||||
|
"water_elevation": u"Δ "+self._dict["unit_water_elevation"],
|
||||||
|
"discharge": u"Δ "+self._dict["unit_discharge"],
|
||||||
|
"velocity": u"Δ "+self._dict["unit_velocity"],
|
||||||
|
"depth": u"Δ "+self._dict["unit_depth"],
|
||||||
|
"mean_depth": u"Δ "+self._dict["unit_mean_depth"],
|
||||||
|
"froude": u"Δ "+self._dict["unit_froude"],
|
||||||
|
"wet_area": u"Δ "+self._dict["unit_wet_area"],
|
||||||
|
}
|
||||||
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user