mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
No files matched your search
@@ -0,0 +1,12 @@
|
||||
# Pamhyr .editorconfig
|
||||
|
||||
root = true
|
||||
|
||||
[*.py]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
+249
-115
@@ -15,96 +15,55 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
stages:
|
||||
- downloads
|
||||
- build
|
||||
- test
|
||||
- package
|
||||
- release
|
||||
|
||||
#############
|
||||
# DOWNLOADS #
|
||||
#############
|
||||
|
||||
dl-mage-linux:
|
||||
stage: downloads
|
||||
tags:
|
||||
- linux
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
script:
|
||||
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test6/downloads/packages/mage_linux.tgz
|
||||
- mkdir -p mage-linux
|
||||
- cd mage-linux
|
||||
- tar xvf ../mage.tgz
|
||||
artifacts:
|
||||
paths:
|
||||
- mage-linux/mage
|
||||
- mage-linux/mage_extraire
|
||||
- mage-linux/mailleurPF
|
||||
|
||||
dl-mage-windows:
|
||||
stage: downloads
|
||||
tags:
|
||||
- linux
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
script:
|
||||
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test6/downloads/packages/mage_windows.tgz
|
||||
- mkdir -p mage-windows
|
||||
- cd mage-windows
|
||||
- tar xvf ../mage.tgz
|
||||
artifacts:
|
||||
paths:
|
||||
- mage-windows/mage.exe
|
||||
- mage-windows/mage_extraire.exe
|
||||
- mage-windows/mailleurPF.exe
|
||||
|
||||
#########
|
||||
# BUILD #
|
||||
#########
|
||||
|
||||
build-mage-linux:
|
||||
stage: build
|
||||
tags:
|
||||
- linux
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
script:
|
||||
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test6/downloads/packages/mage_linux.tgz
|
||||
- mkdir -p mage
|
||||
- cd mage
|
||||
- tar xvf ../mage.tgz
|
||||
artifacts:
|
||||
paths:
|
||||
- mage/mage
|
||||
- mage/mage_extraire
|
||||
- mage/mailleurPF
|
||||
|
||||
# build-mage-linux:
|
||||
# stage: build
|
||||
# tags:
|
||||
# - linux
|
||||
# rules:
|
||||
# - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
# variables:
|
||||
# GIT_SUBMODULE_STRATEGY: recursive
|
||||
# GIT_SUBMODULE_DEPTH: 20
|
||||
# script:
|
||||
# - cd mage/src/
|
||||
# - mkdir -p build
|
||||
# - cd build
|
||||
# - cmake ..
|
||||
# - make
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - mage/src/build/mage
|
||||
# - mage/src/build/mage_extraire
|
||||
# - mage/src/build/mailleurPF
|
||||
|
||||
build-mage-windows:
|
||||
stage: build
|
||||
tags:
|
||||
- linux
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
script:
|
||||
- curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test6/downloads/packages/mage_windows.tgz
|
||||
- mkdir -p mage
|
||||
- cd mage
|
||||
- tar xvf ../mage.tgz
|
||||
artifacts:
|
||||
paths:
|
||||
- mage/mage.exe
|
||||
- mage/mage_extraire.exe
|
||||
- mage/mailleurPF.exe
|
||||
|
||||
# build-mage-windows:
|
||||
# stage: build
|
||||
# tags:
|
||||
# - wine
|
||||
# rules:
|
||||
# - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
# variables:
|
||||
# GIT_SUBMODULE_STRATEGY: recursive
|
||||
# GIT_SUBMODULE_DEPTH: 20
|
||||
# script:
|
||||
# - cd mage/src/
|
||||
# - sed s/add_compile_definitions(win)/add_compile_definitions(windows)/ CMakeLists.txt > __tmp__
|
||||
# - echo set(CMAKE_Fortran_COMPILER "C:/Program\ Files/gcc/bin/gfortran.exe") > CMakeLists.txt
|
||||
# - echo set(CMAKE_MAKE_PROGRAM "C:/Program Files\ \(x86\)/GnuWin32/bin/make.exe") >> CMakeLists.txt
|
||||
# - type __tmp__ >> CMakeLists.txt
|
||||
# - mkdir build
|
||||
# - cd build
|
||||
# - cmake -G "MinGW Makefiles" ..
|
||||
# - make
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - mage/src/build/mage.exe
|
||||
# - mage/src/build/mage_extraire.exe
|
||||
# - mage/src/build/mailleurPF.exe
|
||||
|
||||
build:
|
||||
set-version:
|
||||
stage: build
|
||||
tags:
|
||||
- linux
|
||||
@@ -126,60 +85,214 @@ build-lang:
|
||||
paths:
|
||||
- src/lang/*.qm
|
||||
|
||||
build-users-doc:
|
||||
stage: build
|
||||
tags:
|
||||
- linux
|
||||
script:
|
||||
- cd ./doc/users/
|
||||
- ./build.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- doc/users/documentation.pdf
|
||||
|
||||
build-developers-doc:
|
||||
stage: build
|
||||
tags:
|
||||
- linux
|
||||
script:
|
||||
- cd ./doc/dev/
|
||||
- ./build.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- doc/dev/documentation.pdf
|
||||
|
||||
build-linux:
|
||||
stage: build
|
||||
tags:
|
||||
- linux
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
needs:
|
||||
- job: dl-mage-linux
|
||||
artifacts: true
|
||||
- job: set-version
|
||||
artifacts: true
|
||||
- job: build-lang
|
||||
artifacts: true
|
||||
- job: build-developers-doc
|
||||
artifacts: true
|
||||
- job: build-users-doc
|
||||
artifacts: true
|
||||
script:
|
||||
- mkdir -p linux
|
||||
- cd linux
|
||||
# Setup virtual env
|
||||
- python3 -m venv linux-venv
|
||||
- . linux-venv/bin/activate
|
||||
- pip3 install -r ../requirements.txt
|
||||
# Run Pyinstaller
|
||||
- pyinstaller -y --paths linux-venv/lib/python3.8/site-packages ../src/pamhyr.py
|
||||
# Create directory
|
||||
- mkdir -p pamhyr
|
||||
- mkdir -p pamhyr/pamhyr
|
||||
# Copy Pamhyr info
|
||||
- cp -v ../VERSION pamhyr/pamhyr/
|
||||
- cp -v ../AUTHORS pamhyr/pamhyr/
|
||||
- cp -v ../LICENSE pamhyr/pamhyr/
|
||||
# Copy mage
|
||||
- mkdir -p pamhyr/mage
|
||||
- cp -v ../mage-linux/* pamhyr/mage/
|
||||
# Copy Pamhyr
|
||||
- cp -r dist/pamhyr pamhyr/
|
||||
# Copy Pamhyr resources
|
||||
- mkdir -p pamhyr/pamhyr/View
|
||||
- mkdir -p pamhyr/pamhyr/View/ui
|
||||
- mkdir -p pamhyr/pamhyr/View/ui/Widgets
|
||||
- cp -r ../src/View/ui/*.ui pamhyr/pamhyr/View/ui/
|
||||
- cp -r ../src/View/ui/ressources/ pamhyr/pamhyr/View/ui
|
||||
- cp -r ../src/View/ui/Widgets/*.ui pamhyr/pamhyr/View/ui/Widgets
|
||||
# Copy lang
|
||||
- mkdir -p pamhyr/pamhyr/lang
|
||||
- cp -r ../src/lang/*.qm pamhyr/pamhyr/lang
|
||||
# Copy tests cases
|
||||
- mkdir -p pamhyr/tests_cases/
|
||||
- mkdir -p pamhyr/tests_cases/Saar
|
||||
- cp ../tests_cases/Saar/Saar.pamhyr pamhyr/tests_cases/Saar/
|
||||
# Copy documentations
|
||||
- mkdir -p pamhyr/doc/
|
||||
- cp ../doc/users/documentation.pdf pamhyr/doc/Pamhyr2-users.pdf
|
||||
- cp ../doc/dev/documentation.pdf pamhyr/doc/Pamhyr2-dev.pdf
|
||||
# Create running script
|
||||
- echo "#!/bin/sh" > pamhyr/Pamhyr2
|
||||
- echo "./pamhyr/pamhyr $@" >> pamhyr/Pamhyr2
|
||||
- chmod u+x pamhyr/Pamhyr2
|
||||
artifacts:
|
||||
paths:
|
||||
- linux/pamhyr
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
tags:
|
||||
- wine
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
needs:
|
||||
- job: dl-mage-windows
|
||||
artifacts: true
|
||||
- job: set-version
|
||||
artifacts: true
|
||||
- job: build-lang
|
||||
artifacts: true
|
||||
- job: build-developers-doc
|
||||
artifacts: true
|
||||
- job: build-users-doc
|
||||
artifacts: true
|
||||
script:
|
||||
- mkdir windows
|
||||
- cd windows
|
||||
# Setup env
|
||||
- python -m pip install -r ..\requirements.txt
|
||||
# Run Pyinstaller
|
||||
- pyinstaller -y ..\src\pamhyr.py
|
||||
# Create directory
|
||||
- mkdir pamhyr
|
||||
- dir
|
||||
- copy ..\packages\make-windows-dir.bat .\make-windows-dir.bat
|
||||
- .\make-windows-dir.bat
|
||||
artifacts:
|
||||
paths:
|
||||
- windows/pamhyr
|
||||
|
||||
#########
|
||||
# TESTS #
|
||||
#########
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- echo "TODO PAMHYR tests"
|
||||
# test-windows:
|
||||
# stage: test
|
||||
# tags:
|
||||
# - wine
|
||||
# needs:
|
||||
# - job: set-version
|
||||
# artifacts: true
|
||||
# - job: build-windows
|
||||
# artifacts: true
|
||||
# script:
|
||||
# - cd windows\pamhyr
|
||||
# - pamhyr\pamhyr.exe hello
|
||||
|
||||
# test-linux:
|
||||
# stage: test
|
||||
# tags:
|
||||
# - linux
|
||||
# needs:
|
||||
# - job: set-version
|
||||
# artifacts: true
|
||||
# - job: build-linux
|
||||
# artifacts: true
|
||||
# script:
|
||||
# - cd linux/pamhyr
|
||||
# - ./Pamhyr2 hello
|
||||
|
||||
############
|
||||
# PACKAGES #
|
||||
############
|
||||
|
||||
linux-package:
|
||||
linux-package-tar:
|
||||
stage: package
|
||||
tags:
|
||||
- release
|
||||
- linux
|
||||
needs:
|
||||
- job: build-lang
|
||||
artifacts: true
|
||||
- job: build
|
||||
artifacts: true
|
||||
- job: build-mage-linux
|
||||
# - job: test-linux
|
||||
- job: build-linux
|
||||
artifacts: true
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
artifacts:
|
||||
paths:
|
||||
- packages/pamhyr-gnulinux-amd64.tar.xz
|
||||
- linux/pamhyr-gnulinux.tar.xz
|
||||
script:
|
||||
- cd packages
|
||||
- ./linux.sh
|
||||
- cd linux
|
||||
- tar --xz -cf pamhyr-gnulinux.tar.xz ./pamhyr --checkpoint=.100
|
||||
|
||||
windows-package:
|
||||
windows-package-zip:
|
||||
stage: package
|
||||
tags:
|
||||
- release
|
||||
- linux
|
||||
needs:
|
||||
# - job: test-windows
|
||||
- job: build-windows
|
||||
artifacts: true
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
artifacts:
|
||||
paths:
|
||||
- windows/pamhyr-windows.zip
|
||||
script:
|
||||
- cd windows
|
||||
- zip -r pamhyr-windows.zip pamhyr
|
||||
|
||||
windows-package-exe:
|
||||
stage: package
|
||||
tags:
|
||||
- release
|
||||
- wine
|
||||
needs:
|
||||
- job: build-lang
|
||||
artifacts: true
|
||||
- job: build
|
||||
artifacts: true
|
||||
- job: build-mage-windows
|
||||
# - job: test-windows
|
||||
- job: build-windows
|
||||
artifacts: true
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||
artifacts:
|
||||
paths:
|
||||
- packages/pamhyr-win-amd64.exe
|
||||
- windows/pamhyr-windows.exe
|
||||
script:
|
||||
- cd packages
|
||||
- ./windows.bat
|
||||
- cd windows
|
||||
- copy ..\packages\make-windows-exe.bat make-windows-exe.bat
|
||||
- copy ..\packages\pamhyr.nsi pamhyr.nsi
|
||||
- .\make-windows-exe.bat
|
||||
|
||||
###########
|
||||
# RELEASE #
|
||||
@@ -191,16 +304,25 @@ tag-release:
|
||||
- release
|
||||
- linux
|
||||
needs:
|
||||
- job: linux-package
|
||||
- job: linux-package-tar
|
||||
artifacts: true
|
||||
- job: windows-package
|
||||
- job: windows-package-zip
|
||||
artifacts: true
|
||||
- job: windows-package-exe
|
||||
artifacts: true
|
||||
- job: build-users-doc
|
||||
artifacts: true
|
||||
- job: build-developers-doc
|
||||
artifacts: true
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
artifacts:
|
||||
paths:
|
||||
- packages/pamhyr-gnulinux-amd64.tar.xz
|
||||
- packages/pamhyr-win-amd64.exe
|
||||
- linux/pamhyr-gnulinux.tar.xz
|
||||
- windows/pamhyr-windows.zip
|
||||
- windows/pamhyr-windows.exe
|
||||
- doc/dev/documentation.pdf
|
||||
- doc/users/documentation.pdf
|
||||
script:
|
||||
- cd packages
|
||||
release:
|
||||
@@ -210,11 +332,23 @@ tag-release:
|
||||
ref: '$CI_COMMIT_SHA'
|
||||
assets:
|
||||
links:
|
||||
- name: 'GNU/Linux amd64 (tar.xz)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-gnulinux-amd64.tar.xz'
|
||||
filepath: '/packages/pamhyr-gnulinux-amd64.tar.xz'
|
||||
- name: 'GNU/Linux (tar.xz)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/linux/pamhyr-gnulinux.tar.xz'
|
||||
filepath: '/linux/pamhyr-gnulinux.tar.xz'
|
||||
link_type: 'package'
|
||||
- name: 'Windows amd64 (exe)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-win-amd64.exe'
|
||||
filepath: '/packages/pamhyr-win-amd64.exe'
|
||||
- name: 'Windows (exe)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/windows/pamhyr-windows.exe'
|
||||
filepath: '/windows/pamhyr-windows.exe'
|
||||
link_type: 'package'
|
||||
- name: 'Windows (zip)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/windows/pamhyr-windows.zip'
|
||||
filepath: '/windows/pamhyr-windows.zip'
|
||||
link_type: 'package'
|
||||
- name: 'User documentations (pdf)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/doc/users/documentation.pdf'
|
||||
filepath: '/doc/users/documentation.pdf'
|
||||
link_type: 'other'
|
||||
- name: 'Dev documentations (pdf)'
|
||||
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/doc/dev/documentation.pdf'
|
||||
filepath: '/doc/dev/documentation.pdf'
|
||||
link_type: 'other'
|
||||
@@ -47,5 +47,4 @@ pip3 install -r requirements.txt
|
||||
|
||||
# Run pamhyr
|
||||
./src/pamhyr.py
|
||||
|
||||
```
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Build the PDF
|
||||
|
||||
emacs -Q -q -nw --batch \
|
||||
--eval "(require 'org)" \
|
||||
--eval "(require 'ox-latex)" \
|
||||
--eval "(load-file \"../tools/setup.el\")" \
|
||||
--file ./documentation.org \
|
||||
-f org-latex-export-to-pdf
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../tools/ref.bib
|
||||
@@ -0,0 +1,551 @@
|
||||
# documentation.org -- Pamhyr developers documentation
|
||||
# Copyright (C) 2023 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 -*-
|
||||
|
||||
#+STARTUP: indent
|
||||
|
||||
#+INCLUDE: ../tools/macro.org
|
||||
#+INCLUDE: ../tools/latex.org
|
||||
|
||||
#+TITLE: Developers documentation
|
||||
#+SUBTITLE: Version: {{{version}}}
|
||||
#+AUTHOR: {{{INRAE}}}
|
||||
|
||||
#+OPTIONS: toc:t
|
||||
#+LANGUAGE: UKenglish
|
||||
|
||||
#+NAME: attr_wrap
|
||||
#+HEADER: :var width="\\textwidth"
|
||||
#+HEADER: :var caption=""
|
||||
#+HEADER: :var smallcaption=""
|
||||
#+HEADER: :var name=""
|
||||
#+HEADER: :var data=""
|
||||
#+HEADER: :var float="nil"
|
||||
#+BEGIN_SRC sh :results output :exports none
|
||||
echo "#+CAPTION[$smallcaption]: $caption"
|
||||
echo "#+NAME: $name"
|
||||
echo "#+ATTR_LATEX: :width $width :float $float"
|
||||
echo "$data"
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_abstract
|
||||
This document is for the use of developers. It describes the project
|
||||
architecture, the tools available to assist development and
|
||||
debugging. It also describes the procedures for creating packages and
|
||||
the configurations required to set up the gitlab runners. Finally,
|
||||
this document explains how documentation is written and modified, and
|
||||
how to contribute to the project by modifying, improving or adding
|
||||
documentation, translations or code.
|
||||
#+END_abstract
|
||||
|
||||
* Introduction
|
||||
|
||||
Pamhyr2 is free and open source software (FOSS) graphical user
|
||||
interface (GUI) for 1D hydro-sedimentary modelling of rivers developed
|
||||
in Python (with version 3.8). It use PyQt at version 5 and matplotlib
|
||||
in version 3.4.1 or later for the user insterface (see
|
||||
{{{file(/requirements.txt)}}} for details). The architecture of
|
||||
project code follow the Qt Model/View architecture [fn:qt-arch] (see
|
||||
details in section [[Architecture]]). Pamhyr2 packages can be build
|
||||
manually (see section [[Building packages]]), but there are automatically
|
||||
build with the gitlab-ci (see the section [[Setup the CI
|
||||
environment]]). Documentation files are written with org-mode[fn:org],
|
||||
let see section [[Documentation files]]. Finally, to see the contribution
|
||||
rules, see the section [[How to contribute?]].
|
||||
|
||||
[fn:qt-arch] Qt Model/View documentation:
|
||||
https://doc.qt.io/qt-5/model-view-programming.html (last access
|
||||
2023-09-15)
|
||||
[fn:org] The org-mode website: https://orgmode.org/ (last access
|
||||
2023-09-15)
|
||||
|
||||
* TODO Architecture
|
||||
|
||||
Pamhyr2 architecture is based on Qt Model/View programming[fn:qt-mv] architecture.
|
||||
|
||||
#+name: graph-pipeline
|
||||
#+header: :results drawer
|
||||
#+header: :exports results
|
||||
#+header: :post attr_wrap(width="12cm", data=*this*, name="graph-pipeline", caption="Pamhyr2 solver execution pipeline architecture scheme", float="t")
|
||||
#+begin_src dot :file "images/graph-pipeline.png" :cache no
|
||||
digraph {
|
||||
node[colorscheme=set19,shape=box,style="filled",fillcolor=9];
|
||||
|
||||
subgraph cluster0 {
|
||||
label="Pamhyr2"
|
||||
config[label="Config",fillcolor=5];
|
||||
model[label="Model",fillcolor=2];
|
||||
obj[label="Solver",fillcolor=6];
|
||||
results[label="Results",fillcolor=2];
|
||||
view[label="View",fillcolor=1];
|
||||
|
||||
results -> model[style="dotted"];
|
||||
results -> obj[style="dotted"];
|
||||
}
|
||||
|
||||
config -> obj[label=""];
|
||||
obj -> model;
|
||||
|
||||
subgraph cluster1{
|
||||
label="System";
|
||||
in[label="Solver input files",shape=note];
|
||||
out[label="Solver output files",shape=note];
|
||||
bin[label="Solver binary",shape=note];
|
||||
}
|
||||
|
||||
obj -> in[label="Write"];
|
||||
bin -> in[label="Read"];
|
||||
obj -> bin[label="Execute"];
|
||||
bin -> out[label="Write"];
|
||||
obj -> results[label="Create"];
|
||||
results -> out[label="Read"];
|
||||
view -> model;
|
||||
view -> results;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
#+name: graph-architecture
|
||||
#+header: :results drawer
|
||||
#+header: :exports results
|
||||
#+header: :post attr_wrap(width="7cm", data=*this*, name="graph-architecture", caption="Pamhyr2 Model/View architecture scheme (inspired by Qt Model/View architecture [[https://doc.qt.io/qt-5/model-view-programming.html]])", float="t")
|
||||
#+begin_src dot :file "images/graph-architecture.png" :cache no
|
||||
digraph {
|
||||
node[colorscheme=set19,shape=box,style="filled",fillcolor=white];
|
||||
|
||||
save[label="Pamhyr save",fillcolor="9",shape=note];
|
||||
model[label="Model",fillcolor="2"];
|
||||
view[label="View",fillcolor="1"];
|
||||
delegate[label="Delegate",fillcolor="3"];
|
||||
undocommand[label="UndoCommand",fillcolor="4"];
|
||||
user[label="User",shape=ellipse];
|
||||
|
||||
model -> save[label="Save"];
|
||||
save -> model[label="Load"];
|
||||
|
||||
model -> view[label="Rendering"];
|
||||
view -> delegate[label="Rendering"];
|
||||
delegate -> undocommand[label="Create"];
|
||||
undocommand -> model[label="Modify"]
|
||||
|
||||
view -> user[label="Vizualize"];
|
||||
user -> delegate[label="Modify"];
|
||||
}
|
||||
#+end_src
|
||||
|
||||
#+name: graph-model
|
||||
#+header: :results drawer
|
||||
#+header: :exports results
|
||||
#+header: :post attr_wrap(width="16cm", data=*this*, name="graph-model", caption="Pamhyr2 Model architecture scheme", float="t")
|
||||
#+begin_src dot :file "images/graph-model.png" :cache no
|
||||
digraph {
|
||||
node[colorscheme=set19,shape=box,style="filled",fillcolor="2"];
|
||||
|
||||
study[label="Study"];
|
||||
river[label="River"];
|
||||
|
||||
subgraph cluster00 {
|
||||
label="Network"
|
||||
rnode[label="RiverNode"];
|
||||
redge[label="RiverReach"];
|
||||
}
|
||||
|
||||
frictionlist[label="FrictionList"];
|
||||
|
||||
subgraph cluster01 {
|
||||
label="Stricklers";
|
||||
stricklers[label="Stricklers"];
|
||||
stricklerslist[label="StricklersList"];
|
||||
}
|
||||
subgraph cluster02 {
|
||||
label="BoundaryCondition";
|
||||
boundaryconditionlist[label="BoundaryConditionList"];
|
||||
boundarycondition[label="BoundaryCondition"];
|
||||
}
|
||||
subgraph cluster03 {
|
||||
label="LateralContribution";
|
||||
lateralcontributionlist[label="LateralContributionList"];
|
||||
lateralcontribution[label="LateralContribution"];
|
||||
}
|
||||
subgraph cluster04 {
|
||||
label="InitialConditions";
|
||||
initialconditionsdict[label="InitialConditionsDict"];
|
||||
initialconditions[label="InitialConditions"];
|
||||
}
|
||||
|
||||
solverparameterslist[label="SolverParametersList"];
|
||||
|
||||
subgraph cluster05 {
|
||||
label="Sediment";
|
||||
sedimentlayerlist[label="SedimentLayerList"];
|
||||
sedimentlayer[label="SedimentLayer"];
|
||||
layer[label="Layer"];
|
||||
}
|
||||
|
||||
subgraph cluster06 {
|
||||
label="Greometry"
|
||||
georeach[label="Reach"];
|
||||
geocrosssection[label="Cross-section"];
|
||||
geopoint[label="Point"];
|
||||
}
|
||||
|
||||
study -> river;
|
||||
river -> rnode;
|
||||
river -> redge;
|
||||
redge -> rnode;
|
||||
river -> boundaryconditionlist -> boundarycondition -> rnode;
|
||||
river -> lateralcontributionlist -> lateralcontribution -> redge;
|
||||
river -> initialconditionsdict -> initialconditions;
|
||||
initialconditions -> redge;
|
||||
river -> stricklerslist -> stricklers;
|
||||
river -> solverparameterslist;
|
||||
river -> sedimentlayerlist -> sedimentlayer -> layer;
|
||||
redge -> frictionlist -> stricklers;
|
||||
redge -> georeach -> geocrosssection -> geopoint;
|
||||
geocrosssection -> sedimentlayer;
|
||||
geopoint -> sedimentlayer;
|
||||
|
||||
}
|
||||
#+end_src
|
||||
|
||||
[fn:qt-mv] The Qt Model/View documentation web page:
|
||||
https://doc.qt.io/qt-5/model-view-programming.html
|
||||
|
||||
** TODO Model
|
||||
** TODO View
|
||||
*** TODO UI file
|
||||
*** TODO Window class
|
||||
** TODO UndoCommand
|
||||
** TODO Table
|
||||
** TODO Plot
|
||||
** TODO Solver
|
||||
** TODO Unit tests
|
||||
** TODO The debug mode
|
||||
* TODO Build the project
|
||||
|
||||
The project uses gitlab-ci runners to build packages, but it is possible
|
||||
to build packages manually.
|
||||
|
||||
** Building packages
|
||||
|
||||
If you need an hand made package, you can script available in
|
||||
{{{file(packages)}}} directory.
|
||||
|
||||
*** GNU/Linux
|
||||
|
||||
On GNU/Linux building GNU/Linux packages is easy, you just need python
|
||||
in version 3.8 must be installed with venv and pyinstaller packages
|
||||
(see Listing [[linux-env-deb]] for Debian and derived system). Finally,
|
||||
run the {{{file(linux.sh)}}} script (see Listing [[linux-pkg]]).
|
||||
|
||||
#+NAME: linux-env-deb
|
||||
#+CAPTION: Install environment on GNU/Linux
|
||||
#+begin_src shell
|
||||
sudo apt install python3.8
|
||||
python3 -m pip install venv
|
||||
python3 -m pip install pyinstaller
|
||||
#+end_src
|
||||
|
||||
#+NAME: linux-pkg
|
||||
#+CAPTION: Build GNU/Linux package
|
||||
#+begin_src shell
|
||||
cd packages
|
||||
./linux.sh
|
||||
#+end_src
|
||||
|
||||
*** Windows
|
||||
|
||||
To make the Windows packages you have two choice: If you use Windows
|
||||
you can use the script {{{file(packages/windows.bat)}}}, other else
|
||||
you can use the script {{{file(packages/wine.sh)}}}. Each script need
|
||||
a specific software environment.
|
||||
|
||||
On windows, you needs python on version 3.8, pyinstaller and
|
||||
NSIS[fn:nsis] installed. On GNU/Linux you need wget, wine and
|
||||
winetricks installed.
|
||||
|
||||
[fn:nsis] The NSIS web site: https://sourceforge.net/projects/nsis/
|
||||
|
||||
** TODO Setup the CI environment
|
||||
* Documentation files
|
||||
|
||||
This document and the user documentation are org files. This text file
|
||||
format is formatted so that it can be exported in different formats:
|
||||
PDF (with latex), ODT, HTML, etc. It was originally designed for the
|
||||
GNUEmacs[fn:emacs] text editor, but can be edited with any text editor. Here we
|
||||
take a look at the different features used in these documents.
|
||||
|
||||
[fn:org] The org-mode website: https://orgmode.org/ (last access
|
||||
2023-09-15)
|
||||
[fn:emacs] The GNUEmacs project website: https://gnu.org/s/emacs/
|
||||
(last access 2023-09-15)
|
||||
|
||||
** Org-mode
|
||||
*** Document structure
|
||||
|
||||
Org uses the =*= character to define a new document section. To add a
|
||||
sub-section, you can add an additional =*= to the current section[fn::
|
||||
See document structure documentation:
|
||||
https://orgmode.org/org.html#Headlines (last access 2023-09-15)].
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
* Top level headline
|
||||
** Second level
|
||||
*** Third level
|
||||
some text
|
||||
*** Third level
|
||||
more text
|
||||
* Another top level headline
|
||||
#+END_EXAMPLE
|
||||
|
||||
*** Format
|
||||
|
||||
Org-mode is a markup file, using markup in the text to modify the
|
||||
appearance of a portion of text[fn:: See markup documentation:
|
||||
https://orgmode.org/org.html#Emphasis-and-Monospace (last access
|
||||
2023-09-15)].
|
||||
|
||||
| Markup | Results |
|
||||
|--------------------+------------------|
|
||||
| =*Bolt*= | *Bolt* |
|
||||
| =/Italic/= | /Italic/ |
|
||||
| =_underline_= | _underline_ |
|
||||
| ==verbatim== | =verbatim= |
|
||||
| =~code~= | ~code~ |
|
||||
| =+strike-through+= | +strike-through+ |
|
||||
|
||||
*** Source code blocks
|
||||
|
||||
You can add some code blocks[fn:: See org-mode documentation for
|
||||
source code: https://orgmode.org/org.html#Working-with-Source-Code
|
||||
(last access 2023-09-15)] in the document.
|
||||
|
||||
Here is an example for python source code:
|
||||
#+BEGIN_EXAMPLE
|
||||
#+CAPTION: Get os type name in Python code
|
||||
#+begin_src python
|
||||
import os
|
||||
|
||||
print(f"Document build on system: {os.name}")
|
||||
#+end_src
|
||||
#+END_EXAMPLE
|
||||
|
||||
If you use GNUEmacs, it is also possible to run the code inside a
|
||||
block and export (or not) the reuslts in the document.
|
||||
|
||||
#+OPTIONS: float:nil
|
||||
#+CAPTION: Get os type name in Python code
|
||||
#+begin_src python :python python3 :results output :exports both :noweb yes
|
||||
import os
|
||||
|
||||
print(f"Document build on system: {os.name}")
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
: Document build on system: posix
|
||||
|
||||
*** LaTeX
|
||||
|
||||
If we export the file to PDF, org-mode use {{{latex}}}. So we can add
|
||||
some piece of {{{latex}}} into the document[fn:: See {{{latex}}} part
|
||||
in documentation: https://orgmode.org/org.html#Embedded-LaTeX (last
|
||||
access 2023-09-15)]. For exemple, we can add math formula like
|
||||
=$E=mc^2$= ($E=mc^2$) or =\[E=mc^2\]=:
|
||||
|
||||
\[E=mc^2\]
|
||||
|
||||
But we can also add every type of {{{latex}}}:
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
# Add latex in line
|
||||
#+LATEX: <my line of latex>
|
||||
|
||||
# Add multiple line of LaTeX
|
||||
#+BEGIN_EXPORT latex
|
||||
<my latex here>
|
||||
#+END_EXPORT
|
||||
#+END_EXAMPLE
|
||||
|
||||
It is also possible to add specific {{{latex}}} file header with
|
||||
=#+LATEX_HEADER=. In this document we use the file
|
||||
{{{file(doc/tools/latex.org)}}} for all {{{latex}}} headers.
|
||||
|
||||
*** Macro
|
||||
|
||||
In this document, we use a few macros[fn:: See marcos documentation
|
||||
https://orgmode.org/org.html#Macro-Replacement (last access
|
||||
2023-09-15)] to simplify writing. They allow you to define sequences
|
||||
of text to be replaced, so that the macro name is replaced by its
|
||||
value. They are defined in the {{{file(doc/tools/macro.org)}}}
|
||||
file. Once defined, they can be used in the document as follows:
|
||||
={{{<macro-name>}}}=. You can also have macros with arguments, in this
|
||||
case: ={{{<macro-name>(arg1,...)}}}=. Les macros peuvent aussi
|
||||
utiliser du code emacs-lisp.
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
# Exemple of macro définition
|
||||
|
||||
#+MACRO: toto tata
|
||||
#+MACRO: add \(($1 + $2)\)
|
||||
#+MACRO: emacs-version (eval (nth 2 (split-string (emacs-version))))
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+MACRO: toto tata
|
||||
#+MACRO: add \(($1 + $2)\)
|
||||
#+MACRO: emacs-version (eval (nth 2 (split-string (emacs-version))))
|
||||
|
||||
Macro apply:
|
||||
- Marco ={{{toto}}}=: {{{toto}}}
|
||||
- Marco ={{{add(x,y)}}}=: {{{add(x,y)}}}
|
||||
- Marco ={{{emacs-version}}}=: {{{emacs-version}}}
|
||||
|
||||
*** Footnotes
|
||||
|
||||
Footnote in org-mode is define with marker =[fn:...]=[fn:: Create
|
||||
footnotes in org-mode documentation
|
||||
https://orgmode.org/org.html#Creating-Footnotes (last access
|
||||
2023-09-15)]:
|
||||
#+BEGIN_EXAMPLE
|
||||
The Org website[fn:1] now looks a lot better than it used to.
|
||||
...
|
||||
[fn:1] The link is: https://orgmode.org
|
||||
#+END_EXAMPLE
|
||||
or:
|
||||
#+BEGIN_EXAMPLE
|
||||
The Org website[fn:: The link is: https://orgmode.org] now looks
|
||||
a lot better than it used to.
|
||||
...
|
||||
#+END_EXAMPLE
|
||||
|
||||
*** References
|
||||
|
||||
The references use the {{{latex}}} bibtex tools. The bib file is in
|
||||
{{{file(/doc/tools/ref.bib)}}} and use for developers and user
|
||||
documentation. In document, use ={{{cite(<name>)}}}= to cite a paper.
|
||||
|
||||
** Export
|
||||
|
||||
To export the files, a {{{file(build.sh)}}} script is available in the org
|
||||
files directories. On GNU/Linux system you can build the documentation
|
||||
PDF file with the command =./build.sh=. Texlive package must be
|
||||
installed, you can install only needed packages or all texlive
|
||||
packages, for example on Debian (and some derived system) use command
|
||||
Listing [[texlive-install]].
|
||||
|
||||
#+NAME: texlive-install
|
||||
#+CAPTION: Installation command for texlive full on Debian system
|
||||
#+begin_src shell
|
||||
sudo apt install texlive-full
|
||||
#+end_src
|
||||
|
||||
Some org-mode configuration used in documentations files are define in
|
||||
=/doc/tools/=:
|
||||
- {{{file(PamhyrDoc.cls)}}}: The {{{latex}}} document class
|
||||
- {{{file(macro.org)}}}: Available macro
|
||||
- {{{file(latex.org)}}}: {{{latex}}} configutation for documentations
|
||||
files
|
||||
- {{{file(setup.el)}}}: GNUEmacs configuration to build documentations
|
||||
- {{{file(ref.bib)}}}: Bibtex files for documentations files
|
||||
|
||||
* How to contribute?
|
||||
|
||||
Pamhyr2 is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License[fn:license], either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
[fn:license] The GPLv3 web page:
|
||||
https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
** Guidelines
|
||||
|
||||
To contribute to Pamhyr2, we expect a minimum of respect between
|
||||
contributors. We therefore ask you to respect the following rules
|
||||
regarding communication and contribution content:
|
||||
+ No gender, racial, religious or social discrimination
|
||||
+ No insults, personal attacks or potentially offensive remarks
|
||||
+ Pamhyr2 is free software, and intended to remain so, so take care
|
||||
with the licensing of libraries and external content you want to add
|
||||
to the project
|
||||
+ Humour or hidden easter eggs are welcome if they respect the
|
||||
previous rules
|
||||
|
||||
** Make a contribution
|
||||
|
||||
There are several ways to contribute: you can report a bug by creating
|
||||
an issue on the project's gitlab page[fn:p2-gitlab], or you can create
|
||||
a merge request on the same page with the changes you have made to the
|
||||
code, translation or documentation.
|
||||
|
||||
The Pamhyr2 copyright is owned by INRAE[fn:inrae], but we keep a
|
||||
record of each contributors. If you made a modification to pamhyr2
|
||||
software, please add your name at the end of {{{file(AUTHORS)}}} file
|
||||
and respect the Listing [[auth-format]] format. You can update this file
|
||||
information for following contribution.
|
||||
|
||||
#+NAME: auth-format
|
||||
#+CAPTION: =AUTHORS= file format
|
||||
#+begin_src text
|
||||
<first name> <last name> [(optional) email], <organisation>, <years>
|
||||
#+end_src
|
||||
|
||||
#+CAPTION: Current =AUTHORS= file
|
||||
#+INCLUDE: "../../AUTHORS" src text
|
||||
|
||||
[fn:p2-gitlab] The Pamhyr2 Gitlab project page:
|
||||
https://gitlab.irstea.fr/theophile.terraz/pamhyr
|
||||
[fn:inrae] The INRAE web site: https://www.inrae.fr/
|
||||
|
||||
** Translate
|
||||
|
||||
You can improve or add translation for the project. To contribute to
|
||||
Pamhyr2 translate, you need to use Qt Linguist[fn:qt-linguist]. Open
|
||||
Qt-linguist and edite the translation ({{{file(.ts)}}}) file, finally,
|
||||
commit the new version of file and make a merge request.
|
||||
|
||||
If you want add a new language, edit the script
|
||||
{{{file(src/lang/create_ts.sh)}}} like Listing [[ts-it]]. Run the script
|
||||
and open the new file with Qt-linguist, setup target language (Figure
|
||||
[[qt-linguist-setup]]) and complete translation. Finally, commit the new
|
||||
file and make a merge request.
|
||||
|
||||
#+NAME: ts-it
|
||||
#+CAPTION: Example of modified {{{file(src/lang/create_ts.sh)}}} to add italian (it) translate for Pamhyr2
|
||||
#+begin_src shell
|
||||
...
|
||||
LANG="fr it"
|
||||
...
|
||||
#+end_src
|
||||
|
||||
#+NAME: qt-linguist-setup
|
||||
#+ATTR_HTML: :width 8cm
|
||||
#+ATTR_LATEX: :width 8cm
|
||||
#+CAPTION: Qt linguist lang setup example with italian.
|
||||
[[../images/Qt-linguist-setup-lang.png]]
|
||||
|
||||
[fn:qt-linguist] The Qt linguist documentation web page:
|
||||
https://doc.qt.io/qt-5/qtlinguist-index.html (last access 2023-09-18)
|
||||
|
||||
** Code contribution
|
||||
|
||||
If you are developper you can improve and/or add features to
|
||||
Pamhyr2. Please, follow the architecture described in section
|
||||
[[Architecture]] as closely as possible. Keep the code simple, clear and
|
||||
efficient as possible. The master branch is reserved for the project
|
||||
maintainer; you can create a new branch or fork the project before the
|
||||
request.
|
||||
|
||||
|
||||
{{{biblio}}}
|
||||
Whitespace-only changes.
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,163 @@
|
||||
\ProvidesClass{Pamhyr}[Pamhyr class]
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProcessOptions\relax
|
||||
\LoadClass[11pt,a4paper]{article}
|
||||
|
||||
\usepackage[hmargin=2cm, vmargin=3cm]{geometry}
|
||||
\usepackage[british,UKenglish,USenglish,english,american]{babel}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{times}
|
||||
\usepackage[usenames,dvipsnames]{xcolor}
|
||||
\usepackage[absolute]{textpos}
|
||||
\usepackage{titlesec}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{tcolorbox}
|
||||
|
||||
\usepackage{enumitem}
|
||||
\setlist[enumerate]{itemsep=-0.5em}
|
||||
\setlist[itemize]{itemsep=-0.5em}
|
||||
|
||||
%%
|
||||
%% Bibtex
|
||||
%%
|
||||
|
||||
\bibliographystyle{plain}
|
||||
|
||||
%%
|
||||
%% Colors
|
||||
%%
|
||||
|
||||
\definecolor{primary}{HTML}{212121}
|
||||
\definecolor{lightheadings}{HTML}{5c5c5c}
|
||||
\definecolor{headings}{HTML}{212121}
|
||||
\definecolor{links}{HTML}{e12121}
|
||||
|
||||
%%
|
||||
%% Title
|
||||
%%
|
||||
|
||||
\setlength{\columnsep}{20pt}
|
||||
\setlength{\parskip}{0pt} %% inter paragraph space
|
||||
|
||||
\renewcommand{\maketitle}{
|
||||
{
|
||||
\centering
|
||||
{
|
||||
\fontsize{35}{40}
|
||||
\selectfont
|
||||
\scshape
|
||||
Pamhyr2 \\
|
||||
\medskip
|
||||
}
|
||||
{
|
||||
%\centering
|
||||
\scshape
|
||||
\fontsize{20pt}{24pt}
|
||||
\selectfont
|
||||
A graphical user interface for 1D hydro-sedimentary modelling
|
||||
of rivers \\
|
||||
\medskip
|
||||
}
|
||||
\rule{3cm}{0.4pt}\\
|
||||
\vspace{0.3cm}
|
||||
{
|
||||
%\centering
|
||||
\scshape
|
||||
\fontsize{26pt}{24pt}
|
||||
\selectfont
|
||||
\@title \\
|
||||
Document licence: GPLv3 \\
|
||||
\medskip
|
||||
\includegraphics[width=3cm]{../images/GPLv3_Logo.png} \\
|
||||
\medskip
|
||||
\fontsize{14pt}{16pt}
|
||||
\selectfont
|
||||
© \@author \\
|
||||
\medskip
|
||||
}
|
||||
{
|
||||
%\centering
|
||||
%\scshape
|
||||
\fontsize{12pt}{14pt}
|
||||
\selectfont
|
||||
\@date
|
||||
%\medskip
|
||||
}\\
|
||||
\vspace{0.3cm}
|
||||
\rule{3cm}{0.4pt}\\
|
||||
\vspace{0.3cm}
|
||||
{
|
||||
\begin{minipage}[h]{0.24\textwidth}
|
||||
\includegraphics[width=4cm]{../../src/View/ui/ressources/Logo-INRAE.png}
|
||||
\end{minipage}
|
||||
\begin{minipage}[h]{0.24\textwidth}
|
||||
\includegraphics[width=4cm]{../images/riverly.png}
|
||||
\end{minipage}
|
||||
}\\
|
||||
\vspace{0.3cm}
|
||||
%%\rule{\textwidth}{0.4pt}
|
||||
%%\newpage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
%%
|
||||
%% Texttt
|
||||
%%
|
||||
|
||||
\usepackage[htt]{hyphenat} % Make texttt split at end of line
|
||||
|
||||
%%
|
||||
%% Verbatim
|
||||
%%
|
||||
|
||||
\AtBeginEnvironment{verbatim}{\footnotesize}
|
||||
|
||||
%% Add box arrond of verbatim
|
||||
\BeforeBeginEnvironment{verbatim}{\begin{tcolorbox}[boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}%
|
||||
\AfterEndEnvironment{verbatim}{\end{tcolorbox}}%
|
||||
|
||||
\makeatletter
|
||||
\AtBeginEnvironment{verbatim}{\dontdofcolorbox}
|
||||
|
||||
\def\dontdofcolorbox{\renewcommand\fcolorbox[4][]{##4}}
|
||||
\makeatother
|
||||
|
||||
%%
|
||||
%% Source code blocks
|
||||
%%
|
||||
|
||||
%% Use minted
|
||||
\usepackage{minted}
|
||||
\usemintedstyle{tango}
|
||||
\setminted[c]{fontsize=\footnotesize,encoding=utf8,linenos}
|
||||
\setminted[c++]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos}
|
||||
\setminted[python]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos}
|
||||
\setminted[shell]{breaklines,fontsize=\footnotesize,encoding=utf8}
|
||||
\setminted[scheme]{breaklines,fontsize=\footnotesize,encoding=utf8}
|
||||
\setminted[commun-lisp]{breaklines,fontsize=\footnotesize,encoding=utf8}
|
||||
\setminted[text]{breaklines,fontsize=\footnotesize,encoding=utf8}
|
||||
\setminted[llvm]{breaklines,fontsize=\footnotesize,encoding=utf8}
|
||||
|
||||
%% Add box arrond of minted
|
||||
\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}%
|
||||
\AfterEndEnvironment{minted}{\end{tcolorbox}}%
|
||||
|
||||
\makeatletter
|
||||
\AtBeginEnvironment{minted}{\dontdofcolorbox}
|
||||
|
||||
\def\dontdofcolorbox{\renewcommand\fcolorbox[4][]{##4}}
|
||||
\makeatother
|
||||
|
||||
|
||||
%%
|
||||
%% Water mark
|
||||
%%
|
||||
|
||||
%% FIXME: Delete me for the first realease
|
||||
\usepackage{draftwatermark}
|
||||
\SetWatermarkLightness{0.8}
|
||||
\SetWatermarkAngle{25}
|
||||
\SetWatermarkScale{3}
|
||||
\SetWatermarkFontSize{1cm}
|
||||
\SetWatermarkText{Work in progress}
|
||||
@@ -0,0 +1,19 @@
|
||||
# latex.org -- Pamhyr documentation latex config
|
||||
# Copyright (C) 2023 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 -*-
|
||||
|
||||
#+LaTeX_CLASS: PamhyrDoc
|
||||
@@ -0,0 +1,51 @@
|
||||
# macro.org -- Pamhyr documentation macro
|
||||
# Copyright (C) 2023 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 -*-
|
||||
|
||||
# Constant name
|
||||
#+MACRO: oldPamhyr PAMHyR
|
||||
#+MACRO: Pamhyr Pamhyr2
|
||||
#+MACRO: Mage Mage
|
||||
#+MACRO: Rubarbe RubarBE
|
||||
#+MACRO: Inrae INRAE
|
||||
#+MACRO: latex \LaTeX
|
||||
|
||||
# Information
|
||||
#+MACRO: version (eval (pamhyr-version))
|
||||
#+MACRO: current-file (eval (pamhyr-current-file))
|
||||
|
||||
# Special format
|
||||
#+MACRO: file =$1=
|
||||
|
||||
# Biblio
|
||||
#+MACRO: cite [cite:$1]
|
||||
#+MACRO: biblio \bibliography{documentation}
|
||||
|
||||
# Wrapper
|
||||
#+NAME: attr_wrap
|
||||
#+HEADER: :var width="\\textwidth"
|
||||
#+HEADER: :var caption=""
|
||||
#+HEADER: :var smallcaption=""
|
||||
#+HEADER: :var name=""
|
||||
#+HEADER: :var data=""
|
||||
#+HEADER: :var float="nil"
|
||||
#+BEGIN_SRC sh :results output :exports none
|
||||
echo "#+CAPTION[$smallcaption]: $caption"
|
||||
echo "#+NAME: $name"
|
||||
echo "#+ATTR_LATEX: :width $width :float $float"
|
||||
echo "$data"
|
||||
#+END_SRC
|
||||
Whitespace-only changes.
@@ -0,0 +1,66 @@
|
||||
(require 'org)
|
||||
(require 'subr-x)
|
||||
|
||||
;; LaTeX config
|
||||
|
||||
(add-to-list
|
||||
'org-latex-classes
|
||||
'("PamhyrDoc"
|
||||
"\\documentclass{../tools/PamhyrDoc}"
|
||||
("\\section{%s}" . "\\section*{%s}")
|
||||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||||
("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
|
||||
|
||||
(setq org-confirm-babel-evaluate nil)
|
||||
(setq org-latex-caption-above nil)
|
||||
|
||||
(org-babel-do-load-languages
|
||||
'org-babel-load-languages
|
||||
(add-to-list 'org-babel-load-languages '(shell . t)))
|
||||
|
||||
(org-babel-do-load-languages
|
||||
'org-babel-load-languages
|
||||
(add-to-list 'org-babel-load-languages '(dot . t)))
|
||||
|
||||
(org-babel-do-load-languages
|
||||
'org-babel-load-languages
|
||||
(add-to-list 'org-babel-load-languages '(python . t)))
|
||||
|
||||
(add-to-list 'org-latex-packages-alist '("" "minted"))
|
||||
(setq org-latex-listings 'minted)
|
||||
(setq org-src-fontify-natively t)
|
||||
|
||||
(setq org-latex-pdf-process
|
||||
'("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||||
"bibtex documentation"
|
||||
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||||
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
|
||||
|
||||
;; Citations
|
||||
|
||||
(org-add-link-type
|
||||
"cite" 'ebib
|
||||
(lambda (path desc format)
|
||||
(cond
|
||||
((eq format 'html)
|
||||
(format "(<cite>%s</cite>)" path))
|
||||
((eq format 'latex)
|
||||
(if (or (not desc) (equal 0 (search "cite:" desc)))
|
||||
(format "\\cite{%s}" path)
|
||||
(format "\\cite[%s][%s]{%s}"
|
||||
(cadr (split-string desc ";"))
|
||||
(car (split-string desc ";")) path))))))
|
||||
|
||||
;; Functools
|
||||
|
||||
(defun pamhyr-version ()
|
||||
"Return the contents of the pamhyr version file."
|
||||
(with-temp-buffer
|
||||
(insert-file-contents "../../VERSION")
|
||||
(buffer-string)))
|
||||
|
||||
(defun pamhyr-current-file ()
|
||||
(let ((lst (split-string (buffer-file-name) "/")))
|
||||
(string-join (nthcdr (- (length lst) 3) lst) "/")))
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Build the PDF
|
||||
|
||||
emacs -Q -q -nw --batch \
|
||||
--eval "(require 'org)" \
|
||||
--eval "(require 'ox-latex)" \
|
||||
--eval "(load-file \"../tools/setup.el\")" \
|
||||
--file ./documentation.org \
|
||||
-f org-latex-export-to-pdf
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../tools/ref.bib
|
||||
@@ -0,0 +1,20 @@
|
||||
#+STARTUP: indent
|
||||
|
||||
#+INCLUDE: ../tools/macro.org
|
||||
#+INCLUDE: ../tools/latex.org
|
||||
|
||||
#+TITLE: User documentation
|
||||
#+SUBTITLE: Version: {{{version}}}
|
||||
#+AUTHOR: {{{INRAE}}}
|
||||
|
||||
#+OPTIONS: toc:t
|
||||
#+LANGUAGE: UKenglish
|
||||
|
||||
* TODO Over view
|
||||
* TODO Get started
|
||||
** Installation
|
||||
*** Linux
|
||||
*** Windows
|
||||
* TODO Usage
|
||||
|
||||
{{{biblio}}}
|
||||
@@ -0,0 +1,54 @@
|
||||
rem windows.bat -- Pamhyr Windows batch for windows version building
|
||||
rem Copyright (C) 2023 INRAE
|
||||
rem
|
||||
rem This program is free software: you can redistribute it and/or modify
|
||||
rem it under the terms of the GNU General Public License as published by
|
||||
rem the Free Software Foundation, either version 3 of the License, or
|
||||
rem (at your option) any later version.
|
||||
rem
|
||||
rem This program is distributed in the hope that it will be useful,
|
||||
rem but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
rem GNU General Public License for more details.
|
||||
rem
|
||||
rem You should have received a copy of the GNU General Public License
|
||||
rem along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
@ECHO ON
|
||||
|
||||
mkdir pamhyr\pamhyr
|
||||
xcopy /s /e /y dist\pamhyr pamhyr\pamhyr
|
||||
|
||||
rem Copy data
|
||||
mkdir pamhyr\pamhyr\lang
|
||||
mkdir pamhyr\pamhyr\View\ui\ressources
|
||||
mkdir pamhyr\pamhyr\View\ui\Widgets
|
||||
|
||||
rem UI
|
||||
copy /y ..\src\View\ui\ressources\ pamhyr\pamhyr\View\ui\ressources
|
||||
copy /y ..\src\View\ui\Widgets\*.ui pamhyr\pamhyr\View\ui\Widgets
|
||||
copy /y ..\src\View\ui\*.ui pamhyr\pamhyr\View\ui\
|
||||
|
||||
rem Lang
|
||||
copy /y ..\src\lang\*.qm pamhyr\pamhyr\lang\
|
||||
|
||||
rem Information
|
||||
copy /y ..\VERSION pamhyr\pamhyr
|
||||
copy /y ..\AUTHORS pamhyr\pamhyr
|
||||
copy /y ..\LICENSE pamhyr\pamhyr
|
||||
|
||||
rem MAGE
|
||||
mkdir pamhyr\mage
|
||||
copy /y ..\mage-windows\mage.exe pamhyr\mage\
|
||||
copy /y ..\mage-windows\mage_extraire.exe pamhyr\mage\
|
||||
copy /y ..\mage-windows\mailleurPF.exe pamhyr\mage\
|
||||
|
||||
rem Copy tests_cases
|
||||
mkdir pamhyr\tests_cases
|
||||
mkdir pamhyr\tests_cases\Saar
|
||||
copy /y ..\tests_cases\Saar\Saar.pamhyr pamhyr\tests_cases\Saar\
|
||||
|
||||
rem Documentations
|
||||
mkdir pamhyr\doc
|
||||
copy /y ..\doc\users\documentation.pdf pamhyr\doc\Pamhyr2-users.pdf
|
||||
copy /y ..\doc\dev\documentation.pdf pamhyr\doc\Pamhyr2-dev.pdf
|
||||
@@ -0,0 +1,20 @@
|
||||
rem windows.bat -- Pamhyr Windows batch for windows version building
|
||||
rem Copyright (C) 2023 INRAE
|
||||
rem
|
||||
rem This program is free software: you can redistribute it and/or modify
|
||||
rem it under the terms of the GNU General Public License as published by
|
||||
rem the Free Software Foundation, either version 3 of the License, or
|
||||
rem (at your option) any later version.
|
||||
rem
|
||||
rem This program is distributed in the hope that it will be useful,
|
||||
rem but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
rem GNU General Public License for more details.
|
||||
rem
|
||||
rem You should have received a copy of the GNU General Public License
|
||||
rem along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
@ECHO ON
|
||||
|
||||
rem Make installer
|
||||
"C:\Program Files (x86)\NSIS\makensis.exe" pamhyr.nsi
|
||||
+18
-18
@@ -3,17 +3,17 @@
|
||||
!define LIC_NAME "LICENSE"
|
||||
!define APP_NAME "PAMHYR"
|
||||
|
||||
Name "PAMHYR"
|
||||
OutFile "pamhyr-win-amd64.exe"
|
||||
LicenseData "..\LICENSE"
|
||||
Name "Pamhyr2"
|
||||
OutFile "pamhyr-windows.exe"
|
||||
LicenseData "pamhyr\pamhyr\LICENSE"
|
||||
LicenseText "I Agree"
|
||||
|
||||
RequestExecutionLevel admin
|
||||
|
||||
Unicode True
|
||||
InstallDir $PROGRAMFILES\PAMHYR
|
||||
InstallDir $PROGRAMFILES\Pamhyr2
|
||||
|
||||
InstallDirRegKey HKLM "Software\PAMHYR" "Install_Dir"
|
||||
InstallDirRegKey HKLM "Software\Pamhyr2" "Install_Dir"
|
||||
|
||||
Page license
|
||||
Page components
|
||||
@@ -23,35 +23,35 @@ Page instfiles
|
||||
UninstPage uninstConfirm
|
||||
UninstPage instfiles
|
||||
|
||||
Section "PAMHYR"
|
||||
Section "Pamhyr2"
|
||||
SectionIn RO
|
||||
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
File /r "dist\pamhyr\*"
|
||||
File /r "pamhyr\*"
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "DisplayName" "NSIS PAMHYR"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "UninstallString" "$INSTDIR\uninstall.exe"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "NoRepair" 1
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "DisplayName" "NSIS Pamhyr2"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "UninstallString" "$INSTDIR\uninstall.exe"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "NoRepair" 1
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Start Menu Shortcuts"
|
||||
|
||||
CreateDirectory "$SMPROGRAMS\PAMHYR"
|
||||
CreateShortcut "$SMPROGRAMS\PAMHYR\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
CreateShortcut "$SMPROGRAMS\PAMHYR\PAMHYR.lnk" "$INSTDIR\pamhyr.exe"
|
||||
CreateDirectory "$SMPROGRAMS\Pamhyr2"
|
||||
CreateShortcut "$SMPROGRAMS\Pamhyr2\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
CreateShortcut "$SMPROGRAMS\Pamhyr2\Pamhyr2.lnk" "$INSTDIR\pamhyr\pamhyr.exe"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR"
|
||||
DeleteRegKey HKLM SOFTWARE\NSIS_PAMHYR
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2"
|
||||
DeleteRegKey HKLM SOFTWARE\NSIS_Pamhyr2
|
||||
|
||||
Delete "$SMPROGRAMS\PAMHYR\*.lnk"
|
||||
Delete "$SMPROGRAMS\Pamhyr2\*.lnk"
|
||||
|
||||
RMDir "$SMPROGRAMS\PAMHYR"
|
||||
RMDir "$SMPROGRAMS\Pamhyr2"
|
||||
RMDir /R "$INSTDIR"
|
||||
SectionEnd
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class AbstractModelComponent(object):
|
||||
def __init__(self):
|
||||
super(AbstractModelComponent, self).__init__()
|
||||
|
||||
def new(self, filename):
|
||||
|
||||
|
||||
def export(self, filename):
|
||||
@@ -20,7 +20,7 @@ import logging
|
||||
|
||||
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
from copy import copy
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrList import PamhyrModelListWithTab
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
||||
@@ -30,30 +30,12 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||
Solid,
|
||||
)
|
||||
|
||||
class BoundaryConditionList(SQLSubModel):
|
||||
class BoundaryConditionList(PamhyrModelListWithTab):
|
||||
_tabs_list = ["liquid", "solid", "suspenssion"]
|
||||
_sub_classes = [
|
||||
BoundaryCondition,
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(BoundaryConditionList, self).__init__()
|
||||
|
||||
self._status = status
|
||||
|
||||
self._tabs = {
|
||||
"liquid" : [],
|
||||
"solid" : [],
|
||||
"suspenssion" : []
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
new = cls(status = data['status'])
|
||||
@@ -82,66 +64,12 @@ class BoundaryConditionList(SQLSubModel):
|
||||
|
||||
return True
|
||||
|
||||
def len(self, lst):
|
||||
return len(self._tabs[lst])
|
||||
|
||||
def get_tab(self, lst):
|
||||
return self._tabs[lst].copy()
|
||||
|
||||
def get(self, lst, row):
|
||||
return self._tabs[lst][row]
|
||||
|
||||
def set(self, lst, row, new):
|
||||
self._tabs[lst][row] = new
|
||||
self._status.modified()
|
||||
|
||||
def new(self, lst, index):
|
||||
n = NotDefined(status=self._status)
|
||||
self._tabs[lst].insert(index, n)
|
||||
self._status.modified()
|
||||
return n
|
||||
|
||||
def insert(self, lst, index, new):
|
||||
self._tabs[lst].insert(index, new)
|
||||
self._status.modified()
|
||||
|
||||
def delete(self, lst, bcs):
|
||||
for bc in bcs:
|
||||
self._tabs[lst].remove(bc)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, lst, indexes):
|
||||
bcs = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._tabs[lst])
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(lst, bcs)
|
||||
|
||||
def sort(self, lst, reverse=False, key=None):
|
||||
self._tabs[lst].sort(reverse=reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def move_up(self, lst, index):
|
||||
if index < len(self._tabs[lst]):
|
||||
next = index - 1
|
||||
|
||||
l = self._tabs[lst]
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, lst, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._tabs[lst]
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def __copy__(self):
|
||||
new = BoundaryConditionList()
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
|
||||
class Friction(SQLSubModel):
|
||||
def __init__(self, name:str = "", status = None):
|
||||
|
||||
@@ -21,26 +21,18 @@ import logging
|
||||
from copy import copy
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Tools.PamhyrList import PamhyrModelList
|
||||
|
||||
from Model.Friction.Friction import Friction
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class FrictionList(SQLSubModel):
|
||||
class FrictionList(PamhyrModelList):
|
||||
_sub_classes = [
|
||||
Friction
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(FrictionList, self).__init__()
|
||||
|
||||
self._status = status
|
||||
self._frictions = []
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update_0_0_1(cls, execute, version):
|
||||
execute("ALTER TABLE `section` RENAME TO `friction`")
|
||||
@@ -57,7 +49,7 @@ class FrictionList(SQLSubModel):
|
||||
def _sql_load(cls, execute, data = None):
|
||||
new = cls(status = data['status'])
|
||||
|
||||
new._frictions = Friction._sql_load(
|
||||
new._lst = Friction._sql_load(
|
||||
execute, data
|
||||
)
|
||||
|
||||
@@ -69,70 +61,19 @@ class FrictionList(SQLSubModel):
|
||||
|
||||
ok = True
|
||||
ind = 0
|
||||
for friction in self._frictions:
|
||||
for friction in self._lst:
|
||||
data["ind"] = ind
|
||||
ok &= friction._sql_save(execute, data = data)
|
||||
ind += 1
|
||||
|
||||
return ok
|
||||
|
||||
def __len__(self):
|
||||
return len(self._frictions)
|
||||
|
||||
@property
|
||||
def frictions(self):
|
||||
return self._frictions.copy()
|
||||
|
||||
def get(self, row):
|
||||
return self._frictions[row]
|
||||
|
||||
def set(self, row, new):
|
||||
self._frictions[row] = new
|
||||
self._status.modified()
|
||||
return self.lst
|
||||
|
||||
def new(self, index):
|
||||
n = Friction(status = self._status)
|
||||
self._frictions.insert(index, n)
|
||||
self._lst.insert(index, n)
|
||||
self._status.modified()
|
||||
return n
|
||||
|
||||
def insert(self, index, new):
|
||||
self._frictions.insert(index, new)
|
||||
self._status.modified()
|
||||
|
||||
def delete(self, frictions):
|
||||
for friction in frictions:
|
||||
self._frictions.remove(friction)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, indexes):
|
||||
frictions = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._frictions)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(frictions)
|
||||
|
||||
def sort(self, reverse=False, key=None):
|
||||
self._frictions.sort(reverse=reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def move_up(self, index):
|
||||
if index < len(self._frictions):
|
||||
next = index - 1
|
||||
|
||||
l = self._frictions
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._frictions
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
@@ -19,7 +19,7 @@
|
||||
from math import dist
|
||||
import numpy as np
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Geometry.Point import Point
|
||||
|
||||
class PointXYZ(Point, SQLSubModel):
|
||||
|
||||
@@ -21,7 +21,7 @@ from typing import List
|
||||
|
||||
from tools import timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import ClipboardFormatError
|
||||
from Model.Geometry.Profile import Profile
|
||||
from Model.Geometry.PointXYZ import PointXYZ
|
||||
|
||||
@@ -27,7 +27,7 @@ from functools import reduce
|
||||
|
||||
from tools import flatten, timer, trace
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
|
||||
from Model.Geometry.Profile import Profile
|
||||
from Model.Geometry.ProfileXYZ import ProfileXYZ
|
||||
|
||||
@@ -20,7 +20,7 @@ from copy import copy, deepcopy
|
||||
from tools import trace, timer
|
||||
from functools import reduce
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
|
||||
class Data(SQLSubModel):
|
||||
def __init__(self, name:str = "",
|
||||
|
||||
@@ -19,29 +19,14 @@
|
||||
from copy import copy
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDict import PamhyrModelDict
|
||||
from Model.InitialConditions.InitialConditions import InitialConditions
|
||||
|
||||
class InitialConditionsDict(SQLSubModel):
|
||||
class InitialConditionsDict(PamhyrModelDict):
|
||||
_sub_classes = [
|
||||
InitialConditions,
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(InitialConditionsDict, self).__init__()
|
||||
|
||||
self._status = status
|
||||
|
||||
self._reach = {}
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
new = cls(status = data["status"])
|
||||
@@ -55,7 +40,7 @@ class InitialConditionsDict(SQLSubModel):
|
||||
)
|
||||
|
||||
if ic is not None:
|
||||
new._reach[reach] = ic
|
||||
new._dict[reach] = ic
|
||||
|
||||
return new
|
||||
|
||||
@@ -66,30 +51,12 @@ class InitialConditionsDict(SQLSubModel):
|
||||
|
||||
execute("DELETE FROM initial_conditions")
|
||||
|
||||
for reach in self._reach:
|
||||
for reach in self._dict:
|
||||
data["reach"] = reach
|
||||
ok &= self._reach[reach]._sql_save(execute, data)
|
||||
ok &= self._dict[reach]._sql_save(execute, data)
|
||||
|
||||
return ok
|
||||
|
||||
def __len__(self):
|
||||
return len(self._reach)
|
||||
|
||||
def is_defined(self, reach):
|
||||
return reach in self._reach
|
||||
|
||||
def get(self, reach):
|
||||
if reach in self._reach:
|
||||
return self._reach[reach]
|
||||
|
||||
new = self.new(reach)
|
||||
self.set(reach, new)
|
||||
return new
|
||||
|
||||
def set(self, reach, new):
|
||||
self._reach[reach] = new
|
||||
self._status.modified()
|
||||
|
||||
def new(self, reach):
|
||||
new = InitialConditions(reach = reach, status = self._status)
|
||||
self.set(reach, new)
|
||||
|
||||
@@ -20,7 +20,7 @@ import logging
|
||||
|
||||
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
from copy import copy
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrList import PamhyrModelListWithTab
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
from Model.LateralContribution.LateralContribution import LateralContribution
|
||||
@@ -27,30 +27,12 @@ from Model.LateralContribution.LateralContributionTypes import (
|
||||
NotDefined, LateralContrib, Rain, Evaporation,
|
||||
)
|
||||
|
||||
class LateralContributionList(SQLSubModel):
|
||||
class LateralContributionList(PamhyrModelListWithTab):
|
||||
_tabs_list = ["liquid", "solid", "suspenssion"]
|
||||
_sub_classes = [
|
||||
LateralContribution,
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(LateralContributionList, self).__init__()
|
||||
|
||||
self._status = status
|
||||
|
||||
self._tabs = {
|
||||
"liquid" : [],
|
||||
"solid" : [],
|
||||
"suspenssion" : []
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
new = cls(status = data['status'])
|
||||
@@ -79,66 +61,12 @@ class LateralContributionList(SQLSubModel):
|
||||
|
||||
return True
|
||||
|
||||
def len(self, lst):
|
||||
return len(self._tabs[lst])
|
||||
|
||||
def get_tab(self, lst):
|
||||
return self._tabs[lst].copy()
|
||||
|
||||
def get(self, lst, row):
|
||||
return self._tabs[lst][row]
|
||||
|
||||
def set(self, lst, row, new):
|
||||
self._tabs[lst][row] = new
|
||||
self._status.modified()
|
||||
|
||||
def new(self, lst, index):
|
||||
n = NotDefined(status=self._status)
|
||||
self._tabs[lst].insert(index, n)
|
||||
self._status.modified()
|
||||
return n
|
||||
|
||||
def insert(self, lst, index, new):
|
||||
self._tabs[lst].insert(index, new)
|
||||
self._status.modified()
|
||||
|
||||
def delete(self, lst, bcs):
|
||||
for bc in bcs:
|
||||
self._tabs[lst].remove(bc)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, lst, indexes):
|
||||
bcs = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._tabs[lst])
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(lst, bcs)
|
||||
|
||||
def sort(self, lst, reverse=False, key=None):
|
||||
self._tabs[lst].sort(reverse=reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def move_up(self, lst, index):
|
||||
if index < len(self._tabs[lst]):
|
||||
next = index - 1
|
||||
|
||||
l = self._tabs[lst]
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, lst, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._tabs[lst]
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def __copy__(self):
|
||||
new = LateralContributionList()
|
||||
|
||||
|
||||
+1
-5
@@ -18,7 +18,7 @@
|
||||
|
||||
from tools import flatten
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
|
||||
from Model.Network.Node import Node
|
||||
from Model.Network.Edge import Edge
|
||||
@@ -311,10 +311,6 @@ class River(Graph, SQLSubModel):
|
||||
self._save_submodel(execute, objs, data)
|
||||
return True
|
||||
|
||||
@property
|
||||
def frictions(self):
|
||||
return self._frictions
|
||||
|
||||
@property
|
||||
def boundary_condition(self):
|
||||
return self._boundary_condition
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
class Layer(SQLSubModel):
|
||||
|
||||
@@ -2,35 +2,18 @@
|
||||
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
from Model.Tools.PamhyrList import PamhyrModelList
|
||||
from Model.SedimentLayer.SedimentLayer import SedimentLayer
|
||||
|
||||
class SedimentLayerList(SQLSubModel):
|
||||
class SedimentLayerList(PamhyrModelList):
|
||||
_sub_classes = [SedimentLayer]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(SedimentLayerList, self).__init__()
|
||||
|
||||
self._status = status
|
||||
self._sl = []
|
||||
|
||||
def __len__(self):
|
||||
return len(self._sl)
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
new = cls(status = data["status"])
|
||||
|
||||
new._sl = SedimentLayer._sql_load(execute, data)
|
||||
new._lst = SedimentLayer._sql_load(execute, data)
|
||||
|
||||
return new
|
||||
|
||||
@@ -41,61 +24,17 @@ class SedimentLayerList(SQLSubModel):
|
||||
execute("DELETE FROM sedimentary_layer")
|
||||
execute("DELETE FROM sedimentary_layer_layer")
|
||||
|
||||
for sl in self._sl:
|
||||
for sl in self._lst:
|
||||
ok &= sl._sql_save(execute, data)
|
||||
|
||||
return ok
|
||||
|
||||
@property
|
||||
def sediment_layers(self):
|
||||
return self._sl.copy()
|
||||
|
||||
def get(self, index):
|
||||
return self._sl[index]
|
||||
|
||||
def set(self, index, new):
|
||||
self._sl[index] = new
|
||||
self._status.modified()
|
||||
|
||||
def insert(self, index, new):
|
||||
self._sl.insert(index, new)
|
||||
self._status.modified()
|
||||
return self.lst
|
||||
|
||||
def new(self, index):
|
||||
n = SedimentLayer(status = self._status)
|
||||
self.insert(index, n)
|
||||
self._status.modified()
|
||||
return n
|
||||
|
||||
def delete(self, els):
|
||||
for el in els:
|
||||
self._sl.remove(el)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, indexes):
|
||||
els = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._sl)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(els)
|
||||
|
||||
def move_up(self, index):
|
||||
if index < len(self._sl):
|
||||
next = index - 1
|
||||
|
||||
l = self._sl
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._sl
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
@@ -20,7 +20,7 @@ from copy import copy
|
||||
from tools import trace, timer
|
||||
from Solver.Solvers import solver_type_list
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrList import PamhyrModelList
|
||||
|
||||
class Parameter():
|
||||
def __init__(self,
|
||||
@@ -63,13 +63,13 @@ class Parameter():
|
||||
|
||||
return new
|
||||
|
||||
class SolverParametersList(SQLSubModel):
|
||||
class SolverParametersList(PamhyrModelList):
|
||||
def __init__(self, solver_type = None, status = None):
|
||||
super(SolverParametersList, self).__init__()
|
||||
|
||||
self._status = status
|
||||
self._solver = solver_type
|
||||
self._parameters = list(
|
||||
self._lst = list(
|
||||
map(
|
||||
lambda t: Parameter.from_tuple(t, self._status),
|
||||
solver_type.default_parameters()
|
||||
@@ -194,7 +194,7 @@ class SolverParametersList(SQLSubModel):
|
||||
)
|
||||
|
||||
ind = 0
|
||||
for param in self._parameters:
|
||||
for param in self._lst:
|
||||
sql = (
|
||||
"INSERT INTO " +
|
||||
"solver_parameter(ind, name, value, solver) "+
|
||||
@@ -210,39 +210,36 @@ class SolverParametersList(SQLSubModel):
|
||||
|
||||
return True
|
||||
|
||||
def __len__(self):
|
||||
return len(self._parameters)
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return self._parameters.copy()
|
||||
return self.lst
|
||||
|
||||
def get(self, index):
|
||||
return self._parameters[index]
|
||||
return self._lst[index]
|
||||
|
||||
def get_by_key(self, key):
|
||||
try:
|
||||
return next(
|
||||
filter(
|
||||
lambda p: p["name"] == key,
|
||||
self._parameters
|
||||
self._lst
|
||||
)
|
||||
)["value"]
|
||||
except:
|
||||
return None
|
||||
|
||||
def set(self, index, new):
|
||||
self._parameters[index] = new
|
||||
self._lst[index] = new
|
||||
self._status.modified()
|
||||
|
||||
def set_value(self, key, value):
|
||||
for p in self._parameters:
|
||||
for p in self._lst:
|
||||
if p["name"] == key:
|
||||
p["value"] = value
|
||||
self._status.modified()
|
||||
return
|
||||
|
||||
self._parameters.append(
|
||||
self._lst.append(
|
||||
Parameter(
|
||||
name = key,
|
||||
value = value,
|
||||
@@ -253,47 +250,6 @@ class SolverParametersList(SQLSubModel):
|
||||
|
||||
def new(self, index):
|
||||
n = Parameter(status = self._status)
|
||||
self._parameters.insert(index, n)
|
||||
self._lst.insert(index, n)
|
||||
self._status.modified()
|
||||
return n
|
||||
|
||||
def insert(self, index, new):
|
||||
self._parameters.insert(index, new)
|
||||
self._status.modified()
|
||||
|
||||
def delete(self, parameters):
|
||||
for parameter in parameters:
|
||||
self._parameters.remove(parameter)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, indexes):
|
||||
parameters = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._parameters)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(parameters)
|
||||
|
||||
def sort(self, reverse=False, key=None):
|
||||
self._parameters.sort(reverse=reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def move_up(self, index):
|
||||
if index < len(self._parameters):
|
||||
next = index - 1
|
||||
|
||||
l = self._parameters
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._parameters
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
|
||||
class Stricklers(SQLSubModel):
|
||||
_id_cnt = 0
|
||||
|
||||
@@ -18,22 +18,15 @@
|
||||
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.DB import SQLSubModel
|
||||
from Model.Saved import SavedStatus
|
||||
from Model.Tools.PamhyrList import PamhyrModelList
|
||||
from Model.Stricklers.Stricklers import Stricklers
|
||||
|
||||
class StricklersList(SQLSubModel):
|
||||
class StricklersList(PamhyrModelList):
|
||||
_sub_classes = [
|
||||
Stricklers,
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
if status is None:
|
||||
status = SavedStatus()
|
||||
self._status = status
|
||||
|
||||
self._stricks = []
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
@@ -46,7 +39,7 @@ class StricklersList(SQLSubModel):
|
||||
def _sql_load(cls, execute, data = None):
|
||||
new = cls(status = data["status"])
|
||||
|
||||
new._stricks = Stricklers._sql_load(
|
||||
new._lst = Stricklers._sql_load(
|
||||
execute,
|
||||
data = data
|
||||
)
|
||||
@@ -56,60 +49,26 @@ class StricklersList(SQLSubModel):
|
||||
def _sql_save(self, execute, data = None):
|
||||
execute("DELETE FROM stricklers")
|
||||
|
||||
objs = self._stricks
|
||||
objs = self._lst
|
||||
return self._save_submodel(execute, objs, data)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._stricks)
|
||||
|
||||
@property
|
||||
def stricklers(self):
|
||||
return self._stricks.copy()
|
||||
return self.lst
|
||||
|
||||
def get(self, index):
|
||||
if 0 <= index < len(self._stricks):
|
||||
return self._stricks[index]
|
||||
|
||||
return None
|
||||
|
||||
def insert(self, index, strick):
|
||||
self._stricks.insert(index, strick)
|
||||
self._status.modified()
|
||||
|
||||
def add(self, index):
|
||||
def new(self, index):
|
||||
s = Stricklers(status = self._status)
|
||||
self.insert(index, s)
|
||||
return s
|
||||
|
||||
def delete(self, stricks):
|
||||
self._stricks = list(
|
||||
filter(
|
||||
lambda s: s not in stricks,
|
||||
self._stricks
|
||||
)
|
||||
)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, indexes):
|
||||
stricks = set(
|
||||
map(
|
||||
lambda e: e[1],
|
||||
filter(
|
||||
lambda e: e[0] in indexes,
|
||||
enumerate(self._stricks)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(stricks)
|
||||
|
||||
@timer
|
||||
def sort(self, reverse:bool = False, key=None):
|
||||
f = lambda st: st.name
|
||||
if key is not None:
|
||||
f = key
|
||||
|
||||
self._stricks = sorted(
|
||||
self._stricks,
|
||||
self._lst = sorted(
|
||||
self._lst,
|
||||
key = f,
|
||||
reverse = reverse,
|
||||
)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ from datetime import datetime
|
||||
|
||||
from tools import timer, timestamp
|
||||
|
||||
from Model.DB import SQLModel
|
||||
from Model.Tools.PamhyrDB import SQLModel
|
||||
from Model.Saved import SavedStatus
|
||||
from Model.Serializable import Serializable
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# DB.py -- Pamhyr abstract model database classes
|
||||
# PamhyrDB.py -- Pamhyr abstract model database classes
|
||||
# Copyright (C) 2023 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -0,0 +1,74 @@
|
||||
# PamhyrDict.py -- Pamhyr abstract dict for model classes
|
||||
# Copyright (C) 2023 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 copy
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class PamhyrModelDict(SQLSubModel):
|
||||
_sub_classes = []
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(PamhyrModelDict, self).__init__()
|
||||
|
||||
self._status = status
|
||||
|
||||
self._dict = {}
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
raise NotImplementedMethodeError(cls, cls._sql_load)
|
||||
|
||||
def _sql_save(self, execute, data = None):
|
||||
raise NotImplementedMethodeError(self, self._sql_save)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._dict)
|
||||
|
||||
def is_defined(self, key):
|
||||
return key in self._dict
|
||||
|
||||
def set(self, key, new):
|
||||
self._dict[key] = new
|
||||
self._status.modified()
|
||||
|
||||
def get(self, key):
|
||||
if key in self._dict:
|
||||
return self._dict[key]
|
||||
|
||||
new = self.new(key)
|
||||
self.set(key, new)
|
||||
return new
|
||||
|
||||
def new(self, key):
|
||||
raise NotImplementedMethodeError(self, self.new)
|
||||
@@ -0,0 +1,229 @@
|
||||
# PamhyrList.py -- Pamhyr Abstract List object for the Model
|
||||
# Copyright (C) 2023 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 copy
|
||||
from tools import trace, timer
|
||||
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class PamhyrModelList(SQLSubModel):
|
||||
_sub_classes = [
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(PamhyrModelList, self).__init__()
|
||||
|
||||
self._status = status
|
||||
self._lst = []
|
||||
|
||||
#######
|
||||
# SQL #
|
||||
#######
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
raise NotImplementedMethodeError(cls, cls._sql_load)
|
||||
|
||||
def _sql_save(self, execute, data = None):
|
||||
raise NotImplementedMethodeError(self, self._sql_save)
|
||||
|
||||
################
|
||||
# MODEL METHOD #
|
||||
################
|
||||
|
||||
def __len__(self):
|
||||
return len(self._lst)
|
||||
|
||||
@property
|
||||
def lst(self):
|
||||
return self._lst.copy()
|
||||
|
||||
def get(self, row):
|
||||
return self._lst[row]
|
||||
|
||||
def set(self, row, new):
|
||||
self._lst[row] = new
|
||||
self._status.modified()
|
||||
|
||||
def new(self, index):
|
||||
"""Create new elements and add it to list
|
||||
|
||||
Args:
|
||||
index: The index of new elements
|
||||
|
||||
Returns:
|
||||
The new elements
|
||||
"""
|
||||
raise NotImplementedMethodeError(self, self.new)
|
||||
|
||||
def insert(self, index, new):
|
||||
self._lst.insert(index, new)
|
||||
self._status.modified()
|
||||
|
||||
def delete(self, lst):
|
||||
for el in lst:
|
||||
self._lst.remove(el)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, indexes):
|
||||
lst = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._lst)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(lst)
|
||||
|
||||
def sort(self, reverse=False, key=None):
|
||||
self._lst.sort(reverse=reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def move_up(self, index):
|
||||
if index < len(self._lst):
|
||||
next = index - 1
|
||||
|
||||
l = self._lst
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._lst
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
|
||||
|
||||
class PamhyrModelListWithTab(SQLSubModel):
|
||||
_tabs_list = []
|
||||
_sub_classes = [
|
||||
]
|
||||
|
||||
def __init__(self, status = None):
|
||||
super(PamhyrModelListWithTab, self).__init__()
|
||||
|
||||
self._status = status
|
||||
self._tabs = {}
|
||||
for tab in self._tabs_list:
|
||||
self._tabs[tab] = []
|
||||
|
||||
#######
|
||||
# SQL #
|
||||
#######
|
||||
|
||||
@classmethod
|
||||
def _sql_create(cls, execute):
|
||||
return cls._create_submodel(execute)
|
||||
|
||||
@classmethod
|
||||
def _sql_update(cls, execute, version):
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _sql_load(cls, execute, data = None):
|
||||
raise NotImplementedMethodeError(cls, cls._sql_load)
|
||||
|
||||
def _sql_save(self, execute, data = None):
|
||||
raise NotImplementedMethodeError(self, self._sql_save)
|
||||
|
||||
################
|
||||
# MODEL METHOD #
|
||||
################
|
||||
|
||||
def len(self, lst):
|
||||
return len(self._tabs[lst])
|
||||
|
||||
def get_tab(self, lst):
|
||||
return self._tabs[lst].copy()
|
||||
|
||||
def get(self, lst, row):
|
||||
return self._tabs[lst][row]
|
||||
|
||||
def set(self, lst, row, new):
|
||||
self._tabs[lst][row] = new
|
||||
self._status.modified()
|
||||
|
||||
def new(self, lst, index):
|
||||
"""Create new elements and add it to list
|
||||
|
||||
Args:
|
||||
lst: The tab name
|
||||
index: The index of new elements
|
||||
|
||||
Returns:
|
||||
The new elements
|
||||
"""
|
||||
raise NotImplementedMethodeError(self, self.new)
|
||||
|
||||
def insert(self, lst, index, new):
|
||||
self._tabs[lst].insert(index, new)
|
||||
self._status.modified()
|
||||
|
||||
def delete(self, lst, els):
|
||||
for el in els:
|
||||
self._tabs[lst].remove(el)
|
||||
self._status.modified()
|
||||
|
||||
def delete_i(self, lst, indexes):
|
||||
els = list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(self._tabs[lst])
|
||||
)
|
||||
)
|
||||
)
|
||||
self.delete(lst, els)
|
||||
|
||||
def sort(self, lst, reverse=False, key=None):
|
||||
self._tabs[lst].sort(reverse=reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def move_up(self, lst, index):
|
||||
if index < len(self._tabs[lst]):
|
||||
next = index - 1
|
||||
|
||||
l = self._tabs[lst]
|
||||
l[index], l[next] = l[next], l[index]
|
||||
self._status.modified()
|
||||
|
||||
def move_down(self, lst, index):
|
||||
if index >= 0:
|
||||
prev = index + 1
|
||||
|
||||
l = self._tabs[lst]
|
||||
l[index], l[prev] = l[prev], l[index]
|
||||
self._status.modified()
|
||||
@@ -0,0 +1,36 @@
|
||||
# AScript.py -- Pamhyr abstract script class
|
||||
# Copyright (C) 2023 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 Model.Except import NotImplementedMethodeError
|
||||
|
||||
class AScript(object):
|
||||
name = ""
|
||||
description = ""
|
||||
|
||||
def __init__(self, app, conf, args):
|
||||
super(AScript, self).__init__()
|
||||
|
||||
self._app = app
|
||||
self._conf = conf
|
||||
self._args = args.copy()
|
||||
|
||||
def usage(self):
|
||||
raise NotImplementedMethodeError(self, self.usage)
|
||||
|
||||
def run(self):
|
||||
raise NotImplementedMethodeError(self, self.run)
|
||||
@@ -0,0 +1,34 @@
|
||||
# help.py -- Pamhyr
|
||||
# Copyright (C) 2023 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 Scripts.AScript import AScript
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class ScriptHello(AScript):
|
||||
name = "Hello"
|
||||
description = "Display hello"
|
||||
|
||||
def usage(self):
|
||||
logger.info(f"Usage : {self._args[0]} hello")
|
||||
|
||||
def run(self):
|
||||
logger.info("Hello from Pamhyr2 script !")
|
||||
return 0
|
||||
@@ -22,6 +22,7 @@ import logging
|
||||
from tools import timer
|
||||
|
||||
try:
|
||||
# Installation allow Unix-like signal
|
||||
from signal import SIGTERM, SIGSTOP, SIGCONT
|
||||
_signal = True
|
||||
except:
|
||||
@@ -201,7 +202,7 @@ class AbstractSolver(object):
|
||||
return os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
".."
|
||||
"..", ".."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -216,7 +217,6 @@ class AbstractSolver(object):
|
||||
The executable and list of arguments
|
||||
"""
|
||||
# HACK: Works in most case... Trust me i'm an engineer
|
||||
|
||||
cmd = cmd.replace("@install_dir", self._install_dir())
|
||||
cmd = cmd.replace("@path", path.replace(" ", "\ "))
|
||||
cmd = cmd.replace("@input", self.input_param())
|
||||
@@ -358,6 +358,7 @@ class AbstractSolver(object):
|
||||
|
||||
def start(self, study, process = None):
|
||||
if _signal:
|
||||
# Solver is PAUSED, so continue execution
|
||||
if self._status == STATUS.PAUSED:
|
||||
os.kill(self._process.pid(), SIGCONT)
|
||||
self._status = STATUS.RUNNING
|
||||
@@ -371,6 +372,7 @@ class AbstractSolver(object):
|
||||
if self._process is None:
|
||||
return False
|
||||
|
||||
# Send SIGSTOP to PAUSED solver
|
||||
os.kill(self._process.pid(), SIGSTOP)
|
||||
self._status = STATUS.PAUSED
|
||||
return True
|
||||
|
||||
@@ -25,6 +25,7 @@ from tools import trace, timer
|
||||
|
||||
from View.ASubWindow import ASubMainWindow, AWidget
|
||||
from View.ListedSubWindow import ListedSubWindow
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QVariant, QAbstractTableModel,
|
||||
@@ -53,125 +54,7 @@ _translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class ExtendedTimeEdit(AWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(ExtendedTimeEdit, self).__init__(
|
||||
ui="extendedTimeEdit",
|
||||
parent=parent
|
||||
)
|
||||
self.parent = parent
|
||||
|
||||
self.spinBox_days = self.find(QSpinBox, "spinBox_days")
|
||||
self.timeEdit = self.find(QTimeEdit, "timeEdit")
|
||||
|
||||
def set_time(self, time):
|
||||
days = 0
|
||||
stime = time
|
||||
|
||||
# if ',' in time, time format is 'DD days, HH:MM:SS',
|
||||
# otherelse is 'HH:MM:SS'
|
||||
if "," in time:
|
||||
s = time.strip().split(" ")
|
||||
days = int(s[0])
|
||||
stime = s[-1]
|
||||
|
||||
qtime = QTime.fromString(
|
||||
stime,
|
||||
"h:mm:ss"
|
||||
)
|
||||
self.spinBox_days.setValue(days)
|
||||
self.timeEdit.setTime(qtime)
|
||||
|
||||
def get_time(self):
|
||||
days = self.spinBox_days.value()
|
||||
time = self.timeEdit.time().toPyTime()
|
||||
secs = (
|
||||
(time.hour * 3600) +
|
||||
(time.minute * 60) +
|
||||
time.second
|
||||
)
|
||||
|
||||
return timedelta(days=days, seconds=secs)
|
||||
|
||||
class ExtendedDateTimeEdit(AWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(ExtendedDateTimeEdit, self).__init__(
|
||||
ui="extendedDateTimeEdit",
|
||||
parent=parent
|
||||
)
|
||||
self.parent = parent
|
||||
|
||||
self.dateTimeEdit = self.find(QDateTimeEdit, "dateTimeEdit")
|
||||
|
||||
def set_time(self, time):
|
||||
qdatetime = QDateTime.fromString(
|
||||
time,
|
||||
"yyyy-MM-dd hh:mm:ss"
|
||||
)
|
||||
self.dateTimeEdit.setDateTime(qdatetime)
|
||||
|
||||
def get_time(self):
|
||||
time = self.dateTimeEdit.dateTime().toPyDateTime()
|
||||
return time
|
||||
|
||||
class ExTimeDelegate(QItemDelegate):
|
||||
def __init__(self, data=None, mode="time", parent=None):
|
||||
super(ExTimeDelegate, self).__init__(parent)
|
||||
|
||||
self._data = data
|
||||
self._mode = mode
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
if self._mode == "time":
|
||||
self.editor = ExtendedTimeEdit(parent=parent)
|
||||
else:
|
||||
self.editor = ExtendedDateTimeEdit(parent=parent)
|
||||
value = index.data(Qt.DisplayRole)
|
||||
self.editor.set_time(value)
|
||||
logger.debug(str(value))
|
||||
return self.editor
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
time = editor.get_time()
|
||||
if self._mode == "time":
|
||||
model.setData(index, int(time.total_seconds()))
|
||||
else:
|
||||
logger.debug(str(time.timestamp()))
|
||||
model.setData(index, int(time.timestamp()))
|
||||
editor.close()
|
||||
editor.deleteLater()
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
r = QRect(option.rect)
|
||||
if self.editor.windowFlags() & Qt.Popup and 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(QAbstractTableModel):
|
||||
def __init__(self, data=None, mode="time", undo=None):
|
||||
super(QAbstractTableModel, self).__init__()
|
||||
self._headers = data.header
|
||||
self._data = data
|
||||
self._mode = mode
|
||||
self._undo = undo
|
||||
|
||||
def flags(self, index):
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self._data)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._headers)
|
||||
|
||||
class TableModel(PamhyrTableModel):
|
||||
def data(self, index, role):
|
||||
if role == Qt.TextAlignmentRole:
|
||||
return Qt.AlignHCenter | Qt.AlignVCenter
|
||||
@@ -189,7 +72,7 @@ class TableModel(QAbstractTableModel):
|
||||
if self._data.get_type_column(column) == float:
|
||||
value = f"{v:.4f}"
|
||||
elif self._data.header[column] == "time":
|
||||
if self._mode == "time":
|
||||
if self._opt_data == "time":
|
||||
t0 = datetime.fromtimestamp(0)
|
||||
t = datetime.fromtimestamp(v)
|
||||
value = str(t - t0)
|
||||
@@ -200,12 +83,6 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
return value
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||
return table_headers[self._headers[section]]
|
||||
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if not index.isValid() or role != Qt.EditRole:
|
||||
return False
|
||||
@@ -315,12 +192,3 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
|
||||
def undo(self):
|
||||
self._undo.undo()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def redo(self):
|
||||
self._undo.redo()
|
||||
self.layoutChanged.emit()
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
from tools import timer, trace
|
||||
|
||||
from View.ASubWindow import ASubMainWindow, AWidget
|
||||
from View.ListedSubWindow import ListedSubWindow
|
||||
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence,
|
||||
@@ -39,15 +42,18 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.BoundaryCondition.translate import long_types
|
||||
from View.BoundaryCondition.Edit.translate import table_headers, retranslate
|
||||
from View.BoundaryCondition.Edit.UndoCommand import SetMetaDataCommand
|
||||
from View.BoundaryCondition.Edit.Table import TableModel, ExTimeDelegate
|
||||
from View.BoundaryCondition.Edit.Table import TableModel
|
||||
from View.BoundaryCondition.Edit.Plot import Plot
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class WD50Sigma(AWidget):
|
||||
d50Changed = pyqtSignal(float)
|
||||
sigmaChanged = pyqtSignal(float)
|
||||
@@ -141,23 +147,29 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||
self._d50sigma.set_sigma(self._data.sigma)
|
||||
|
||||
def setup_table(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
self._table = TableModel(
|
||||
retranslate()
|
||||
headers = {}
|
||||
for h in self._data.header:
|
||||
headers[h] = table_headers[h]
|
||||
|
||||
self._delegate_time = PamhyrExTimeDelegate(
|
||||
data = self._data,
|
||||
undo = self._undo_stack,
|
||||
mode = self._study.time_system
|
||||
mode = self._study.time_system,
|
||||
parent = self
|
||||
)
|
||||
|
||||
if self._data.header[0] == "time":
|
||||
self._delegate_time = ExTimeDelegate(
|
||||
data = self._data,
|
||||
mode = self._study.time_system,
|
||||
parent = self
|
||||
)
|
||||
|
||||
table.setItemDelegateForColumn(
|
||||
0, self._delegate_time
|
||||
)
|
||||
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)
|
||||
@@ -167,7 +179,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_plot(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
)
|
||||
self.verticalLayout.addWidget(self.toolbar)
|
||||
|
||||
@@ -29,3 +29,12 @@ table_headers = {
|
||||
"z": _translate("BoundaryCondition", "Z (m)"),
|
||||
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["x"] = _translate("BoundaryCondition", "X")
|
||||
table_headers["y"] = _translate("BoundaryCondition", "Y")
|
||||
table_headers["time"] = _translate("BoundaryCondition", "Time")
|
||||
table_headers["date"] = _translate("BoundaryCondition", "Date")
|
||||
table_headers["discharge"] = _translate("BoundaryCondition", "Discharge (m³/s)")
|
||||
table_headers["z"] = _translate("BoundaryCondition", "Z (m)")
|
||||
table_headers["solid"] = _translate("BoundaryCondition", "Solid (kg/s)")
|
||||
@@ -39,6 +39,8 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||
TimeOverZ, TimeOverDischarge, ZOverDischarge
|
||||
)
|
||||
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from View.BoundaryCondition.UndoCommand import (
|
||||
SetNameCommand, SetNodeCommand, SetTypeCommand,
|
||||
AddCommand, DelCommand, SortCommand,
|
||||
@@ -104,26 +106,13 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
self.commitData.emit(self.sender())
|
||||
|
||||
|
||||
class TableModel(QAbstractTableModel):
|
||||
def __init__(self, data=None, undo=None, tab=""):
|
||||
super(QAbstractTableModel, self).__init__()
|
||||
self._headers = list(table_headers.keys())
|
||||
self._data = data
|
||||
self._undo = undo
|
||||
self._tab = tab
|
||||
self._bcs = self._data.boundary_condition
|
||||
|
||||
def flags(self, index):
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
class TableModel(PamhyrTableModel):
|
||||
def _setup_lst(self):
|
||||
self._lst = self._data.boundary_condition
|
||||
self._tab = self._opt_data
|
||||
|
||||
def rowCount(self, parent):
|
||||
return self._bcs.len(self._tab)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._headers)
|
||||
return self._lst.len(self._tab)
|
||||
|
||||
def data(self, index, role):
|
||||
if role != Qt.ItemDataRole.DisplayRole:
|
||||
@@ -133,24 +122,18 @@ class TableModel(QAbstractTableModel):
|
||||
column = index.column()
|
||||
|
||||
if self._headers[column] == "name":
|
||||
return self._bcs.get(self._tab, row).name
|
||||
return self._lst.get(self._tab, row).name
|
||||
elif self._headers[column] == "type":
|
||||
t = self._bcs.get(self._tab, row).bctype
|
||||
t = self._lst.get(self._tab, row).bctype
|
||||
return long_types[t]
|
||||
elif self._headers[column] == "node":
|
||||
n = self._bcs.get(self._tab, row).node
|
||||
n = self._lst.get(self._tab, row).node
|
||||
if n is None:
|
||||
return _translate("BoundaryCondition", "Not associate")
|
||||
return n.name
|
||||
|
||||
return QVariant()
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||
return table_headers[self._headers[section]]
|
||||
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if not index.isValid() or role != Qt.EditRole:
|
||||
return False
|
||||
@@ -162,20 +145,20 @@ class TableModel(QAbstractTableModel):
|
||||
if self._headers[column] == "name":
|
||||
self._undo.push(
|
||||
SetNameCommand(
|
||||
self._bcs, self._tab,row, value
|
||||
self._lst, self._tab,row, value
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "type":
|
||||
key = next(k for k, v in long_types.items() if v == value)
|
||||
self._undo.push(
|
||||
SetTypeCommand(
|
||||
self._bcs, self._tab,row, BC_types[key]
|
||||
self._lst, self._tab,row, BC_types[key]
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "node":
|
||||
self._undo.push(
|
||||
SetNodeCommand(
|
||||
self._bcs, self._tab,row, self._data.node(value)
|
||||
self._lst, self._tab,row, self._data.node(value)
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -190,7 +173,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo.push(
|
||||
AddCommand(
|
||||
self._bcs, self._tab,row
|
||||
self._lst, self._tab,row
|
||||
)
|
||||
)
|
||||
|
||||
@@ -202,7 +185,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo.push(
|
||||
DelCommand(
|
||||
self._bcs, self._tab,rows
|
||||
self._lst, self._tab, rows
|
||||
)
|
||||
)
|
||||
|
||||
@@ -214,7 +197,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo.push(
|
||||
SortCommand(
|
||||
self._bcs, self._tab,False
|
||||
self._lst, self._tab, False
|
||||
)
|
||||
)
|
||||
|
||||
@@ -231,7 +214,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo_stack.push(
|
||||
MoveCommand(
|
||||
self._bcs, self._tab,"up", row
|
||||
self._lst, self._tab, "up", row
|
||||
)
|
||||
)
|
||||
|
||||
@@ -239,7 +222,7 @@ class TableModel(QAbstractTableModel):
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def move_down(self, index, parent=QModelIndex()):
|
||||
if row > len(self._bcs):
|
||||
if row > len(self._lst):
|
||||
return
|
||||
|
||||
target = row
|
||||
@@ -248,7 +231,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo_stack.push(
|
||||
MoveCommand(
|
||||
self._bcs, self._tab,"down", row
|
||||
self._lst, self._tab, "down", row
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ from View.BoundaryCondition.Table import (
|
||||
)
|
||||
|
||||
from View.Network.GraphWidget import GraphWidget
|
||||
from View.BoundaryCondition.translate import *
|
||||
from View.BoundaryCondition.translate import table_headers, retranslate
|
||||
from View.BoundaryCondition.Edit.Window import EditBoundaryConditionWindow
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -90,17 +90,10 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
self._table = {}
|
||||
|
||||
for t in ["liquid", "solid", "suspenssion"]:
|
||||
table = self.find(QTableView, f"tableView_{t}")
|
||||
self._table[t] = TableModel(
|
||||
data = self._study.river,
|
||||
undo = self._undo_stack,
|
||||
tab = t,
|
||||
)
|
||||
table.setModel(self._table[t])
|
||||
|
||||
self._delegate_type = ComboBoxDelegate(
|
||||
data = self._study.river,
|
||||
mode = "type",
|
||||
@@ -114,13 +107,20 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||
parent=self
|
||||
)
|
||||
|
||||
table.setItemDelegateForColumn(
|
||||
1, self._delegate_type
|
||||
table = self.find(QTableView, f"tableView_{t}")
|
||||
self._table[t] = TableModel(
|
||||
table_view = table,
|
||||
table_headers = table_headers,
|
||||
editable_headers = ["name", "type", "node"],
|
||||
delegates = {
|
||||
"type": self._delegate_type,
|
||||
"node": self._delegate_node,
|
||||
},
|
||||
data = self._study.river,
|
||||
undo = self._undo_stack,
|
||||
opt_data = t,
|
||||
)
|
||||
table.setItemDelegateForColumn(
|
||||
2, self._delegate_node
|
||||
)
|
||||
|
||||
table.setModel(self._table[t])
|
||||
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
table.setAlternatingRowColors(True)
|
||||
|
||||
@@ -49,3 +49,8 @@ BC_types = {
|
||||
"ZD": ZOverDischarge,
|
||||
"SL": Solid,
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("BoundaryCondition", "Name")
|
||||
table_headers["type"] = _translate("BoundaryCondition", "Type")
|
||||
table_headers["node"] = _translate("BoundaryCondition", "Node")
|
||||
@@ -37,10 +37,11 @@ class Worker(QObject):
|
||||
self.signalStatus.emit('end')
|
||||
|
||||
def _compute(self):
|
||||
# The computation loop
|
||||
for checker in self._checker_list:
|
||||
self.signalStatus.emit(checker.name)
|
||||
|
||||
# time.sleep(1)
|
||||
# Run checker
|
||||
checker.run(self._study)
|
||||
|
||||
self.signalStatus.emit("progress")
|
||||
@@ -92,7 +92,9 @@ class ReplWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.set_plaintext_edit_text("plainTextEdit", "")
|
||||
self._history_ind = 0
|
||||
|
||||
# Code to rich_code
|
||||
rich_code = code.strip().split("\n")
|
||||
# Add return variable for results display at last row
|
||||
rich_code[-1] = "self.__debug_exec_result__ = " + rich_code[-1]
|
||||
rich_code = "\n".join(rich_code)
|
||||
logger.debug(f"User debug command : {rich_code}")
|
||||
@@ -102,6 +104,8 @@ class ReplWindow(ASubMainWindow, ListedSubWindow):
|
||||
except Exception as e:
|
||||
value = f"<font color=\"red\">" + str(e) + "</font>"
|
||||
|
||||
# Display code
|
||||
msg = f"<font color=\"grey\"> # " + code + " #</font>"
|
||||
self.find(QTextEdit, "textEdit").append(msg)
|
||||
# Display results
|
||||
self.find(QTextEdit, "textEdit").append(str(value))
|
||||
@@ -51,7 +51,7 @@ from View.Frictions.Table import (
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Geometry.PlotKPZ import PlotKPZ
|
||||
from View.Frictions.PlotStricklers import PlotStricklers
|
||||
from View.Frictions.translate import *
|
||||
from View.Frictions.translate import table_headers, retranslate
|
||||
|
||||
from View.Stricklers.Window import StricklersWindow
|
||||
|
||||
@@ -94,6 +94,7 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
self._table = {}
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
|
||||
@@ -28,3 +28,11 @@ table_headers = {
|
||||
"begin_strickler": _translate("Frictions", "Begin strickler"),
|
||||
"end_strickler": _translate("Frictions", "End strickler"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("Frictions", "Name")
|
||||
# table_headers["edge"] = _translate("Frictions", "Reach")
|
||||
table_headers["begin_kp"] = _translate("Frictions", "Begin kp (m)")
|
||||
table_headers["end_kp"] = _translate("Frictions", "End kp (m)")
|
||||
table_headers["begin_strickler"] = _translate("Frictions", "Begin strickler")
|
||||
table_headers["end_strickler"] = _translate("Frictions", "End strickler")
|
||||
@@ -25,7 +25,8 @@ from PyQt5.QtWidgets import (
|
||||
QToolBar,
|
||||
)
|
||||
|
||||
from View.Plot import MplCanvas, navigation_toolbar_2qt
|
||||
from View.Plot import MplCanvas
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
file_path = os.path.abspath(os.path.dirname(__file__))
|
||||
icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../View/ui/ressources/"
|
||||
@@ -197,7 +198,7 @@ class Ui_MainWindow(object):
|
||||
self.canvas = MplCanvas.MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
|
||||
self.my_toolbar = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(
|
||||
self.my_toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self.widget1
|
||||
)
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QHeaderView, QToolBar,
|
||||
)
|
||||
|
||||
from View.Plot import (
|
||||
MplCanvas, navigation_toolbar_2qt
|
||||
)
|
||||
from View.Plot import MplCanvas
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
|
||||
file_path = os.path.abspath(os.path.dirname(__file__))
|
||||
icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/View/ui/ressources/"
|
||||
@@ -190,7 +190,7 @@ class Ui_MainWindow(object):
|
||||
self.canvas_1 = MplCanvas.MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_1.setObjectName("canvas_1")
|
||||
|
||||
self.toolbar_1 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_1 = PamhyrPlotToolbar(
|
||||
self.canvas_1, self.centralwidget,
|
||||
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
|
||||
)
|
||||
@@ -207,7 +207,7 @@ class Ui_MainWindow(object):
|
||||
self.canvas_2 = MplCanvas.MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_2.setObjectName("canvas_2")
|
||||
|
||||
self.toolbar_2 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_2 = PamhyrPlotToolbar(
|
||||
self.canvas_2, self.centralwidget,
|
||||
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
|
||||
)
|
||||
@@ -223,7 +223,7 @@ class Ui_MainWindow(object):
|
||||
self.canvas_3 = MplCanvas.MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_3.setObjectName("canvas_3")
|
||||
|
||||
self.toolbar_3 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_3 = PamhyrPlotToolbar(
|
||||
self.canvas_3, self.centralwidget,
|
||||
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
|
||||
)
|
||||
|
||||
@@ -49,11 +49,11 @@ from View.InitialConditions.UndoCommand import (
|
||||
from View.InitialConditions.Table import TableModel, ComboBoxDelegate
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.InitialConditions.PlotDKP import PlotDKP
|
||||
from View.InitialConditions.PlotDischarge import PlotDischarge
|
||||
from View.InitialConditions.translate import *
|
||||
from View.InitialConditions.translate import table_headers, retranslate
|
||||
from View.InitialConditions.DialogHeight import HeightDialog
|
||||
from View.InitialConditions.DialogDischarge import DischargeDialog
|
||||
|
||||
@@ -97,6 +97,8 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
self._table = TableModel(
|
||||
river = self._study.river,
|
||||
@@ -122,7 +124,7 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_graph(self):
|
||||
self.canvas_1 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_1.setObjectName("canvas_1")
|
||||
self.toolbar_1 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_1 = PamhyrPlotToolbar(
|
||||
self.canvas_1, self
|
||||
)
|
||||
self.plot_layout_1 = self.find(QVBoxLayout, "verticalLayout_1")
|
||||
@@ -138,7 +140,7 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_2.setObjectName("canvas_2")
|
||||
self.toolbar_2 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_2 = PamhyrPlotToolbar(
|
||||
self.canvas_2, self
|
||||
)
|
||||
self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
|
||||
|
||||
@@ -29,3 +29,12 @@ table_headers = {
|
||||
"height": _translate("LateralContribution", "Height (m)"),
|
||||
# "comment": _translate("LateralContribution", "Comment"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
# table_headers["name"] = _translate("LateralContribution", "Name")
|
||||
table_headers["kp"] = _translate("LateralContribution", "KP (m)")
|
||||
# table_headers["speed"] = _translate("LateralContribution", "Speed (m/s)")
|
||||
table_headers["discharge"] = _translate("LateralContribution", "Discharge (m³/s)")
|
||||
table_headers["elevation"] = _translate("LateralContribution", "Elevation (m)")
|
||||
table_headers["height"] = _translate("LateralContribution", "Height (m)")
|
||||
# table_headers["comment"] = _translate("LateralContribution", "Comment")
|
||||
@@ -24,6 +24,7 @@ from tools import trace, timer
|
||||
|
||||
from View.ASubWindow import ASubMainWindow, AWidget
|
||||
from View.ListedSubWindow import ListedSubWindow
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QVariant, QAbstractTableModel,
|
||||
@@ -51,125 +52,8 @@ _translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class ExtendedTimeEdit(AWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(ExtendedTimeEdit, self).__init__(
|
||||
ui="extendedTimeEdit",
|
||||
parent=parent
|
||||
)
|
||||
self.parent = parent
|
||||
|
||||
self.spinBox_days = self.find(QSpinBox, "spinBox_days")
|
||||
self.timeEdit = self.find(QTimeEdit, "timeEdit")
|
||||
|
||||
def set_time(self, time):
|
||||
days = 0
|
||||
stime = time
|
||||
|
||||
# if ',' in time, time format is 'DD days, HH:MM:SS',
|
||||
# otherelse is 'HH:MM:SS'
|
||||
if "," in time:
|
||||
s = time.strip().split(" ")
|
||||
days = int(s[0])
|
||||
stime = s[-1]
|
||||
|
||||
qtime = QTime.fromString(
|
||||
stime,
|
||||
"h:mm:ss"
|
||||
)
|
||||
self.spinBox_days.setValue(days)
|
||||
self.timeEdit.setTime(qtime)
|
||||
|
||||
def get_time(self):
|
||||
days = self.spinBox_days.value()
|
||||
time = self.timeEdit.time().toPyTime()
|
||||
secs = (
|
||||
(time.hour * 3600) +
|
||||
(time.minute * 60) +
|
||||
time.second
|
||||
)
|
||||
|
||||
return timedelta(days=days, seconds=secs)
|
||||
|
||||
class ExtendedDateTimeEdit(AWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(ExtendedDateTimeEdit, self).__init__(
|
||||
ui="extendedDateTimeEdit",
|
||||
parent=parent
|
||||
)
|
||||
self.parent = parent
|
||||
|
||||
self.dateTimeEdit = self.find(QDateTimeEdit, "dateTimeEdit")
|
||||
|
||||
def set_time(self, time):
|
||||
qdatetime = QDateTime.fromString(
|
||||
time,
|
||||
"yyyy-MM-dd hh:mm:ss"
|
||||
)
|
||||
self.dateTimeEdit.setDateTime(qdatetime)
|
||||
|
||||
def get_time(self):
|
||||
time = self.dateTimeEdit.dateTime().toPyDateTime()
|
||||
return time
|
||||
|
||||
class ExTimeDelegate(QItemDelegate):
|
||||
def __init__(self, data=None, mode="time", parent=None):
|
||||
super(ExTimeDelegate, self).__init__(parent)
|
||||
|
||||
self._data = data
|
||||
self._mode = mode
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
if self._mode == "time":
|
||||
self.editor = ExtendedTimeEdit(parent=parent)
|
||||
else:
|
||||
self.editor = ExtendedDateTimeEdit(parent=parent)
|
||||
value = index.data(Qt.DisplayRole)
|
||||
self.editor.set_time(value)
|
||||
logger.debug(str(value))
|
||||
return self.editor
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
time = editor.get_time()
|
||||
if self._mode == "time":
|
||||
model.setData(index, int(time.total_seconds()))
|
||||
else:
|
||||
logger.debug(str(time.timestamp()))
|
||||
model.setData(index, int(time.timestamp()))
|
||||
editor.close()
|
||||
editor.deleteLater()
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
r = QRect(option.rect)
|
||||
if self.editor.windowFlags() & Qt.Popup and 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(QAbstractTableModel):
|
||||
def __init__(self, data=None, mode="time", undo=None):
|
||||
super(QAbstractTableModel, self).__init__()
|
||||
self._headers = data.header
|
||||
self._data = data
|
||||
self._mode = mode
|
||||
self._undo = undo
|
||||
|
||||
def flags(self, index):
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self._data)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._headers)
|
||||
|
||||
class TableModel(PamhyrTableModel):
|
||||
def data(self, index, role):
|
||||
if role == Qt.TextAlignmentRole:
|
||||
return Qt.AlignHCenter | Qt.AlignVCenter
|
||||
@@ -187,7 +71,7 @@ class TableModel(QAbstractTableModel):
|
||||
if self._data.get_type_column(column) == float:
|
||||
value = f"{v:.4f}"
|
||||
elif self._data.header[column] == "time":
|
||||
if self._mode == "time":
|
||||
if self._opt_data == "time":
|
||||
t0 = datetime.fromtimestamp(0)
|
||||
t = datetime.fromtimestamp(v)
|
||||
value = str(t - t0)
|
||||
@@ -198,12 +82,6 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
return value
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||
return table_headers[self._headers[section]]
|
||||
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if not index.isValid() or role != Qt.EditRole:
|
||||
return False
|
||||
@@ -313,12 +191,3 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
|
||||
def undo(self):
|
||||
self._undo.undo()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def redo(self):
|
||||
self._undo.redo()
|
||||
self.layoutChanged.emit()
|
||||
@@ -20,6 +20,7 @@ from tools import timer, trace
|
||||
|
||||
from View.ASubWindow import ASubMainWindow
|
||||
from View.ListedSubWindow import ListedSubWindow
|
||||
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence,
|
||||
@@ -37,10 +38,11 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.LateralContribution.translate import long_types
|
||||
from View.LateralContribution.Edit.Table import TableModel, ExTimeDelegate
|
||||
from View.LateralContribution.Edit.translate import table_headers, retranslate
|
||||
from View.LateralContribution.Edit.Table import TableModel
|
||||
from View.LateralContribution.Edit.Plot import Plot
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -85,23 +87,29 @@ class EditLateralContributionWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
self._table = TableModel(
|
||||
retranslate()
|
||||
headers = {}
|
||||
for h in self._data.header:
|
||||
headers[h] = table_headers[h]
|
||||
|
||||
self._delegate_time = PamhyrExTimeDelegate(
|
||||
data = self._data,
|
||||
undo = self._undo_stack,
|
||||
mode = self._study.time_system
|
||||
mode = self._study.time_system,
|
||||
parent = self
|
||||
)
|
||||
|
||||
if self._data.header[0] == "time":
|
||||
self._delegate_time = ExTimeDelegate(
|
||||
data = self._data,
|
||||
mode = self._study.time_system,
|
||||
parent = self
|
||||
)
|
||||
|
||||
table.setItemDelegateForColumn(
|
||||
0, self._delegate_time
|
||||
)
|
||||
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)
|
||||
@@ -111,7 +119,7 @@ class EditLateralContributionWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_plot(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
)
|
||||
self.verticalLayout.addWidget(self.toolbar)
|
||||
|
||||
@@ -28,3 +28,11 @@ table_headers = {
|
||||
"discharge": _translate("LateralContribution", "Discharge (m³/s)"),
|
||||
"z": _translate("LateralContribution", "Z (m)")
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["x"] = _translate("LateralContribution", "X")
|
||||
table_headers["y"] = _translate("LateralContribution", "Y")
|
||||
table_headers["time"] = _translate("LateralContribution", "Time")
|
||||
table_headers["date"] = _translate("LateralContribution", "Date")
|
||||
table_headers["discharge"] = _translate("LateralContribution", "Discharge (m³/s)")
|
||||
table_headers["z"] = _translate("LateralContribution", "Z (m)")
|
||||
@@ -44,6 +44,8 @@ from View.LateralContribution.UndoCommand import (
|
||||
from Model.LateralContribution.LateralContributionTypes import (
|
||||
NotDefined, LateralContrib, Rain, Evaporation,
|
||||
)
|
||||
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
from View.LateralContribution.translate import *
|
||||
|
||||
logger = logging.getLogger()
|
||||
@@ -102,26 +104,13 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
self.commitData.emit(self.sender())
|
||||
|
||||
|
||||
class TableModel(QAbstractTableModel):
|
||||
def __init__(self, data=None, undo=None, tab=""):
|
||||
super(QAbstractTableModel, self).__init__()
|
||||
self._headers = list(table_headers.keys())
|
||||
self._data = data
|
||||
self._undo = undo
|
||||
self._tab = tab
|
||||
self._lcs = self._data.lateral_contribution
|
||||
|
||||
def flags(self, index):
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
class TableModel(PamhyrTableModel):
|
||||
def _setup_lst(self):
|
||||
self._lst = self._data.lateral_contribution
|
||||
self._tab = self._opt_data
|
||||
|
||||
def rowCount(self, parent):
|
||||
return self._lcs.len(self._tab)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._headers)
|
||||
return self._lst.len(self._tab)
|
||||
|
||||
def data(self, index, role):
|
||||
if role != Qt.ItemDataRole.DisplayRole:
|
||||
@@ -131,26 +120,19 @@ class TableModel(QAbstractTableModel):
|
||||
column = index.column()
|
||||
|
||||
if self._headers[column] == "name":
|
||||
return self._lcs.get(self._tab, row).name
|
||||
return self._lst.get(self._tab, row).name
|
||||
elif self._headers[column] == "type":
|
||||
t = self._lcs.get(self._tab, row).lctype
|
||||
t = self._lst.get(self._tab, row).lctype
|
||||
return long_types[t]
|
||||
elif self._headers[column] == "edge":
|
||||
n = self._lcs.get(self._tab, row).edge
|
||||
n = self._lst.get(self._tab, row).edge
|
||||
if n is None:
|
||||
return _translate("LateralContribution", "Not associate")
|
||||
return n.name
|
||||
elif self._headers[column] == "begin_kp":
|
||||
return self._lcs.get(self._tab, row).begin_kp
|
||||
return self._lst.get(self._tab, row).begin_kp
|
||||
elif self._headers[column] == "end_kp":
|
||||
return self._lcs.get(self._tab, row).end_kp
|
||||
|
||||
|
||||
return QVariant()
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||
return table_headers[self._headers[section]]
|
||||
return self._lst.get(self._tab, row).end_kp
|
||||
|
||||
return QVariant()
|
||||
|
||||
@@ -165,32 +147,32 @@ class TableModel(QAbstractTableModel):
|
||||
if self._headers[column] == "name":
|
||||
self._undo.push(
|
||||
SetNameCommand(
|
||||
self._lcs, self._tab, row, value
|
||||
self._lst, self._tab, row, value
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "type":
|
||||
key = next(k for k, v in long_types.items() if v == value)
|
||||
self._undo.push(
|
||||
SetTypeCommand(
|
||||
self._lcs, self._tab, row, LC_types[key]
|
||||
self._lst, self._tab, row, LC_types[key]
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "edge":
|
||||
self._undo.push(
|
||||
SetEdgeCommand(
|
||||
self._lcs, self._tab, row, self._data.edge(value)
|
||||
self._lst, self._tab, row, self._data.edge(value)
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "begin_kp":
|
||||
self._undo.push(
|
||||
SetBeginCommand(
|
||||
self._lcs, self._tab, row, value
|
||||
self._lst, self._tab, row, value
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "end_kp":
|
||||
self._undo.push(
|
||||
SetEndCommand(
|
||||
self._lcs, self._tab, row, value
|
||||
self._lst, self._tab, row, value
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -205,7 +187,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo.push(
|
||||
AddCommand(
|
||||
self._lcs, self._tab, row
|
||||
self._lst, self._tab, row
|
||||
)
|
||||
)
|
||||
|
||||
@@ -217,7 +199,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo.push(
|
||||
DelCommand(
|
||||
self._lcs, self._tab, rows
|
||||
self._lst, self._tab, rows
|
||||
)
|
||||
)
|
||||
|
||||
@@ -229,7 +211,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo.push(
|
||||
SortCommand(
|
||||
self._lcs, self._tab, False
|
||||
self._lst, self._tab, False
|
||||
)
|
||||
)
|
||||
|
||||
@@ -246,7 +228,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo_stack.push(
|
||||
MoveCommand(
|
||||
self._lcs, self._tab,"up", row
|
||||
self._lst, self._tab, "up", row
|
||||
)
|
||||
)
|
||||
|
||||
@@ -254,7 +236,7 @@ class TableModel(QAbstractTableModel):
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def move_down(self, index, parent=QModelIndex()):
|
||||
if row > len(self._lcs):
|
||||
if row > len(self._lst):
|
||||
return
|
||||
|
||||
target = row
|
||||
@@ -263,17 +245,9 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
self._undo_stack.push(
|
||||
MoveCommand(
|
||||
self._lcs, self._tab,"down", row
|
||||
self._lst, self._tab, "down", row
|
||||
)
|
||||
)
|
||||
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def undo(self):
|
||||
self._undo.undo()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def redo(self):
|
||||
self._undo.redo()
|
||||
self.layoutChanged.emit()
|
||||
@@ -56,7 +56,10 @@ from View.LateralContribution.Table import (
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Geometry.PlotXY import PlotXY
|
||||
from View.LateralContribution.translate import *
|
||||
from View.LateralContribution.translate import (
|
||||
long_types, table_headers, LC_types,
|
||||
retranslate,
|
||||
)
|
||||
from View.LateralContribution.Edit.Window import EditLateralContributionWindow
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -90,17 +93,10 @@ class LateralContributionWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
self._table = {}
|
||||
|
||||
for t in ["liquid", "solid", "suspenssion"]:
|
||||
table = self.find(QTableView, f"tableView_{t}")
|
||||
self._table[t] = TableModel(
|
||||
data = self._study.river,
|
||||
undo = self._undo_stack,
|
||||
tab = t,
|
||||
)
|
||||
table.setModel(self._table[t])
|
||||
|
||||
self._delegate_type = ComboBoxDelegate(
|
||||
data = self._study.river,
|
||||
mode = "type",
|
||||
@@ -114,13 +110,20 @@ class LateralContributionWindow(ASubMainWindow, ListedSubWindow):
|
||||
parent=self
|
||||
)
|
||||
|
||||
table.setItemDelegateForColumn(
|
||||
1, self._delegate_type
|
||||
table = self.find(QTableView, f"tableView_{t}")
|
||||
self._table[t] = TableModel(
|
||||
table_view = table,
|
||||
table_headers = table_headers,
|
||||
editable_headers = True,
|
||||
delegates = {
|
||||
"type": self._delegate_type,
|
||||
"edge": self._delegate_edge,
|
||||
},
|
||||
data = self._study.river,
|
||||
undo = self._undo_stack,
|
||||
opt_data = t,
|
||||
)
|
||||
table.setItemDelegateForColumn(
|
||||
2, self._delegate_edge
|
||||
)
|
||||
|
||||
table.setModel(self._table[t])
|
||||
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
table.setAlternatingRowColors(True)
|
||||
|
||||
@@ -45,3 +45,15 @@ LC_types = {
|
||||
"RA": Rain,
|
||||
"EV": Evaporation,
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("LateralContribution", "Name")
|
||||
table_headers["type"] = _translate("LateralContribution", "Type")
|
||||
table_headers["edge"] = _translate("LateralContribution", "Reach")
|
||||
table_headers["begin_kp"] = _translate("LateralContribution", "Begin kp (m)")
|
||||
table_headers["end_kp"] = _translate("LateralContribution", "End kp (m)")
|
||||
|
||||
long_types["ND"] = _translate("LateralContribution", "Not defined")
|
||||
long_types["LC"] = _translate("LateralContribution", "Lateral contribution")
|
||||
long_types["RA"] = _translate("LateralContribution", "Rain")
|
||||
long_types["EV"] = _translate("LateralContribution", "Evaporation")
|
||||
@@ -131,10 +131,14 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
self.dialog_reopen_study()
|
||||
|
||||
def set_title(self):
|
||||
title = "(dbg) " if self.conf.debug else ""
|
||||
|
||||
if self.model is not None:
|
||||
self.setWindowTitle(f"PAMHYR - {self.model.name}")
|
||||
title += f"Pamhyr2 - {self.model.name}"
|
||||
self.setWindowTitle(title)
|
||||
else:
|
||||
self.setWindowTitle("PAMHYR")
|
||||
title += "Pamhyr2"
|
||||
self.setWindowTitle(title)
|
||||
|
||||
def enable_actions(self, action:str, enable:bool):
|
||||
"""Enable of disable an action componant
|
||||
@@ -255,9 +259,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.info("Set logging level to INFO")
|
||||
|
||||
|
||||
def setup_debug_mode(self, init = False):
|
||||
menu = self.findChild(QMenu, "menu_help")
|
||||
self.set_title()
|
||||
|
||||
if init:
|
||||
self.debug_action = QAction("Debug", self)
|
||||
|
||||
@@ -584,7 +584,7 @@ class GraphWidget(QGraphicsView):
|
||||
if item:
|
||||
item.update()
|
||||
except Exception as e:
|
||||
logger.debug(str(e))
|
||||
logger.warning(str(e))
|
||||
|
||||
def selected_new_edge_src_node(self):
|
||||
"""The current node item selected to add new edge
|
||||
@@ -626,12 +626,15 @@ class GraphWidget(QGraphicsView):
|
||||
Returns:
|
||||
Nothing
|
||||
"""
|
||||
previous_edge = self._current_edge
|
||||
self._current_edge = edge
|
||||
self.graph.set_current_reach(edge.edge)
|
||||
try:
|
||||
previous_edge = self._current_edge
|
||||
self._current_edge = edge
|
||||
self.graph.set_current_reach(edge.edge)
|
||||
|
||||
if previous_edge:
|
||||
previous_edge.update()
|
||||
if previous_edge:
|
||||
previous_edge.update()
|
||||
except Exception as e:
|
||||
logger.warning(str(e))
|
||||
|
||||
def reset_selection(self):
|
||||
"""Reset all the selected items
|
||||
|
||||
+72
-117
@@ -25,6 +25,7 @@ from Model.Network.Graph import Graph
|
||||
from View.ASubWindow import ASubWindow
|
||||
from View.Network.GraphWidget import GraphWidget
|
||||
from View.Network.UndoCommand import *
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QRect, QVariant, QAbstractTableModel, pyqtSlot, pyqtSignal,
|
||||
@@ -69,151 +70,105 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
def currentItemChanged(self):
|
||||
self.commitData.emit(self.sender())
|
||||
|
||||
class TrueFalseComboBoxDelegate(QItemDelegate):
|
||||
def __init__(self, parent=None):
|
||||
super(TrueFalseComboBoxDelegate, self).__init__(parent)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
self.editor = QComboBox(parent)
|
||||
self.editor.addItems(["true", "false"])
|
||||
self.editor.setCurrentText("true" if index.data(Qt.DisplayRole) else "false")
|
||||
return self.editor
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
value = str(index.data(Qt.DisplayRole))
|
||||
self.editor.currentTextChanged.connect(self.currentItemChanged)
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
value = str(editor.currentText()) == "true"
|
||||
model.setData(index, value)
|
||||
editor.close()
|
||||
editor.deleteLater()
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
r = QRect(option.rect)
|
||||
if self.editor.windowFlags() & Qt.Popup and 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 GraphTableModel(QAbstractTableModel):
|
||||
def __init__(self, headers=[], graph=None, undo = None, rows_type="nodes"):
|
||||
super(QAbstractTableModel, self).__init__()
|
||||
self.headers = headers
|
||||
self.graph = graph
|
||||
self._type = rows_type
|
||||
self._undo = undo
|
||||
|
||||
if self._type == "nodes":
|
||||
self.rows = graph.nodes()
|
||||
elif self._type == "edges":
|
||||
self.rows = graph.edges()
|
||||
|
||||
def flags(self, index):
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
|
||||
if self.headers[index.column()] != "type":
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.rows)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self.headers)
|
||||
class NodeTableModel(PamhyrTableModel):
|
||||
def _setup_lst(self):
|
||||
self._lst = self._data.nodes()
|
||||
|
||||
def data(self, index, role):
|
||||
if role != Qt.ItemDataRole.DisplayRole:
|
||||
return QVariant()
|
||||
|
||||
if self.headers[index.column()] == "type":
|
||||
node = self.rows[index.row()]
|
||||
if self._headers[index.column()] == "type":
|
||||
node = self._lst[index.row()]
|
||||
ret = "internal"
|
||||
|
||||
if not self.graph.is_enable_node(node):
|
||||
if not self._data.is_enable_node(node):
|
||||
ret = "disable"
|
||||
elif self.graph.is_upstream_node(node):
|
||||
elif self._data.is_upstream_node(node):
|
||||
ret = "upstream"
|
||||
elif self.graph.is_downstream_node(node):
|
||||
elif self._data.is_downstream_node(node):
|
||||
ret = "downstream"
|
||||
|
||||
return ret
|
||||
|
||||
return self.rows[index.row()][self.headers[index.column()]]
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if (role == Qt.ItemDataRole.DisplayRole and
|
||||
orientation == Qt.Orientation.Horizontal):
|
||||
return self.headers[section].capitalize()
|
||||
|
||||
return QVariant()
|
||||
return self._lst[index.row()][self._headers[index.column()]]
|
||||
|
||||
@pyqtSlot()
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if index.isValid():
|
||||
if role == Qt.EditRole:
|
||||
try:
|
||||
if (self.headers[index.column()] == "node1" or
|
||||
self.headers[index.column()] == "node2"):
|
||||
node = self.graph.node(value)
|
||||
self._undo.push(
|
||||
SetNodeCommand(
|
||||
self.graph,
|
||||
self.rows[index.row()],
|
||||
self.headers[index.column()],
|
||||
node
|
||||
)
|
||||
)
|
||||
# elif self.headers[index.column()] == "enable":
|
||||
# self._undo.push(
|
||||
# EnableEdgeCommand(
|
||||
# self.rows[index.row()], value
|
||||
# )
|
||||
# )
|
||||
else:
|
||||
self._undo.push(
|
||||
SetCommand(
|
||||
self.rows[index.row()],
|
||||
self.headers[index.column()],
|
||||
value
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
logger.debug(traceback.format_exc())
|
||||
if not index.isValid():
|
||||
return False
|
||||
|
||||
if role == Qt.EditRole:
|
||||
try:
|
||||
self._undo.push(
|
||||
SetCommand(
|
||||
self._lst[index.row()],
|
||||
self._headers[index.column()],
|
||||
value
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
self.dataChanged.emit(index, index, [Qt.DisplayRole])
|
||||
self.layoutChanged.emit()
|
||||
return True
|
||||
|
||||
self.dataChanged.emit(index, index)
|
||||
else:
|
||||
return False
|
||||
|
||||
def update(self):
|
||||
if self._type == "nodes":
|
||||
self.rows = self.graph.nodes()
|
||||
elif self._type == "edges":
|
||||
self.rows = self.graph.edges()
|
||||
|
||||
self._lst = self._data.nodes()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def reverse_edge(self, index):
|
||||
if self._type == "edges":
|
||||
tmp = self.rows[index.row()].node1
|
||||
self.rows[index.row()].node1 = self.rows[index.row()].node2
|
||||
self.rows[index.row()].node2 = tmp
|
||||
class EdgeTableModel(PamhyrTableModel):
|
||||
def _setup_lst(self):
|
||||
self._lst = self._data.edges()
|
||||
|
||||
def data(self, index, role):
|
||||
if role != Qt.ItemDataRole.DisplayRole:
|
||||
return QVariant()
|
||||
|
||||
return self._lst[index.row()][self._headers[index.column()]]
|
||||
|
||||
@pyqtSlot()
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if not index.isValid():
|
||||
return False
|
||||
|
||||
if role != Qt.EditRole:
|
||||
return QVariant()
|
||||
|
||||
try:
|
||||
if (self._headers[index.column()] == "node1" or
|
||||
self._headers[index.column()] == "node2"):
|
||||
node = self.graph.node(value)
|
||||
self._undo.push(
|
||||
SetNodeCommand(
|
||||
self._data,
|
||||
self._lst[index.row()],
|
||||
self._headers[index.column()],
|
||||
node
|
||||
)
|
||||
)
|
||||
else:
|
||||
self._undo.push(
|
||||
SetCommand(
|
||||
self._lst[index.row()],
|
||||
self._headers[index.column()],
|
||||
value
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
self.dataChanged.emit(index, index, [Qt.DisplayRole])
|
||||
self.layoutChanged.emit()
|
||||
return True
|
||||
|
||||
def undo(self):
|
||||
self._undo.undo()
|
||||
self.layoutChanged.emit()
|
||||
self.dataChanged.emit(index, index)
|
||||
|
||||
def redo(self):
|
||||
self._undo.redo()
|
||||
def update(self):
|
||||
self._lst = self._data.edges()
|
||||
self.layoutChanged.emit()
|
||||
+31
-24
@@ -16,6 +16,8 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence,
|
||||
)
|
||||
@@ -37,9 +39,16 @@ from Model.River import RiverNode, RiverReach, River
|
||||
from View.ASubWindow import ASubMainWindow
|
||||
from View.Network.GraphWidget import GraphWidget
|
||||
from View.Network.UndoCommand import *
|
||||
from View.Network.Table import (
|
||||
GraphTableModel, ComboBoxDelegate, TrueFalseComboBoxDelegate,
|
||||
from View.Network.translate import (
|
||||
table_headers_node, table_headers_edge,
|
||||
retranslate,
|
||||
)
|
||||
from View.Network.Table import (
|
||||
ComboBoxDelegate, NodeTableModel, EdgeTableModel,
|
||||
)
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
|
||||
class NetworkWindow(ASubMainWindow):
|
||||
def __init__(self, model=None, title="River network", parent=None):
|
||||
@@ -69,43 +78,41 @@ class NetworkWindow(ASubMainWindow):
|
||||
self.redo_sc = QShortcut(QKeySequence.Redo, self)
|
||||
|
||||
def setup_table(self):
|
||||
# Nodes table
|
||||
retranslate()
|
||||
|
||||
self._nodes_model = GraphTableModel(
|
||||
headers = ["name", "type"],
|
||||
graph = self._graph,
|
||||
rows_type = "nodes",
|
||||
# Nodes table
|
||||
table = self.find(QTableView, "tableView_nodes")
|
||||
self._nodes_model = NodeTableModel(
|
||||
table_view = table,
|
||||
table_headers = table_headers_node,
|
||||
editable_headers = ["name"],
|
||||
data = self._graph,
|
||||
undo = self._undo_stack,
|
||||
)
|
||||
table = self.find(QTableView, "tableView_nodes")
|
||||
table.setModel(self._nodes_model)
|
||||
#table.resizeColumnsToContents()
|
||||
|
||||
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
|
||||
# Edges table
|
||||
|
||||
self._reachs_model = GraphTableModel(
|
||||
headers = ["name", # "enable",
|
||||
"node1", "node2"],
|
||||
graph = self._graph,
|
||||
rows_type = "edges",
|
||||
undo = self._undo_stack,
|
||||
)
|
||||
table = self.find(QTableView, "tableView_reachs")
|
||||
self.delegate_combobox = ComboBoxDelegate(
|
||||
graph = self._graph,
|
||||
parent = self,
|
||||
)
|
||||
self.delegate_true_false_combobox = TrueFalseComboBoxDelegate(
|
||||
parent = self,
|
||||
)
|
||||
|
||||
table = self.find(QTableView, "tableView_reachs")
|
||||
self._reachs_model = EdgeTableModel(
|
||||
table_view = table,
|
||||
table_headers = table_headers_edge,
|
||||
editable_headers = ["name", "node1", "node2"],
|
||||
delegates = {
|
||||
"node1": self.delegate_combobox,
|
||||
"node2": self.delegate_combobox,
|
||||
},
|
||||
data = self._graph,
|
||||
undo = self._undo_stack,
|
||||
)
|
||||
table.setModel(self._reachs_model)
|
||||
# table.setItemDelegateForColumn(1, self.delegate_true_false_combobox)
|
||||
table.setItemDelegateForColumn(1, self.delegate_combobox)
|
||||
table.setItemDelegateForColumn(2, self.delegate_combobox)
|
||||
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
#table.resizeColumnsToContents()
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# translate.py -- Pamhyr
|
||||
# Copyright (C) 2023 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
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
table_headers_node = {
|
||||
"name": _translate("Network", "Name"),
|
||||
"type": _translate("Network", "Type"),
|
||||
}
|
||||
|
||||
table_headers_edge = {
|
||||
"name": _translate("Network", "Name"),
|
||||
"node1": _translate("Network", "Source node"),
|
||||
"node2": _translate("Network", "Destination node"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers_node["name"] = _translate("Network", "Name")
|
||||
table_headers_node["type"] = _translate("Network", "Type")
|
||||
|
||||
table_headers_edge["name"] = _translate("Network", "Name")
|
||||
table_headers_edge["node1"] = _translate("Network", "Source node")
|
||||
table_headers_edge["node2"] = _translate("Network", "Destination node")
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
|
||||
class APlot(object):
|
||||
def __init__(self, canvas=None, data=None, toolbar=None):
|
||||
super(APlot, self).__init__()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# navigation_toolbar_2qt.py -- Pamhyr
|
||||
# PamhyrToolbar.py -- Pamhyr
|
||||
# Copyright (C) 2023 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -19,10 +19,12 @@
|
||||
import os
|
||||
import logging
|
||||
import matplotlib as mpl
|
||||
|
||||
from matplotlib.backends import qt_compat
|
||||
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtWidgets import QAction
|
||||
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT
|
||||
from PyQt5.QtCore import pyqtSignal, QSize
|
||||
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
@@ -31,21 +33,21 @@ logger = logging.getLogger()
|
||||
file_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
class PamHyrNavigationToolbar2QT(NavigationToolbar2QT):
|
||||
class PamhyrPlotToolbar(NavigationToolbar2QT):
|
||||
"""
|
||||
Cette classe est une personnalisation de la toolbar (NavigationToolbar2QT). Elle permet l'internationnalisation et
|
||||
l'ajout d'autres boutons à la toolbar initiale
|
||||
MatPlotLib plot toolbar for Pamhyr
|
||||
"""
|
||||
isometric_signal = pyqtSignal(str)
|
||||
|
||||
def __init__(self, canvas, parent, items = ["home", "move", "zoom", "save"]):
|
||||
"""
|
||||
Args:
|
||||
canvas: canvas de matplotlib
|
||||
parent: parent du canvas
|
||||
"""
|
||||
self.my_canvas = canvas
|
||||
"""PamhyrPlotToolbar
|
||||
|
||||
Args:
|
||||
canvas: MatPlotLib canvas
|
||||
parent: parent Qt object
|
||||
items: Enables items (default: "home", "move", "zoom", "save")
|
||||
"""
|
||||
self._canvas = canvas
|
||||
self._items = items
|
||||
self.toolitems = [
|
||||
(None, None, None, None),
|
||||
@@ -118,7 +120,7 @@ class PamHyrNavigationToolbar2QT(NavigationToolbar2QT):
|
||||
self.toolitems.append((None, None, None, None))
|
||||
|
||||
NavigationToolbar2QT.__init__(self, canvas, parent)
|
||||
btn_size = QSize(30, 30)
|
||||
btn_size = QSize(40, 28)
|
||||
actions = self.findChildren(QAction)
|
||||
|
||||
for a, i in icons:
|
||||
@@ -161,18 +163,15 @@ class PamHyrNavigationToolbar2QT(NavigationToolbar2QT):
|
||||
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton
|
||||
)
|
||||
|
||||
def cursor1(self):
|
||||
logger.info("TODO: cursor1")
|
||||
|
||||
def isometric_view(self):
|
||||
self.my_canvas.axes.axis("equal")
|
||||
self.my_canvas.figure.canvas.draw_idle()
|
||||
self._canvas.axes.axis("equal")
|
||||
self._canvas.figure.canvas.draw_idle()
|
||||
self.isometric_signal[str].emit("vue iso")
|
||||
|
||||
def non_isometric_view(self):
|
||||
self.my_canvas.axes.axis("tight")
|
||||
self.my_canvas.toolbar.update()
|
||||
self.my_canvas.figure.canvas.draw_idle()
|
||||
self._canvas.axes.axis("tight")
|
||||
self._canvas.toolbar.update()
|
||||
self._canvas.figure.canvas.draw_idle()
|
||||
|
||||
def toolitems_translation(self):
|
||||
self._actions['home'].setToolTip(_translate("Toolbar", "Default view"))
|
||||
@@ -43,7 +43,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.Results.PlotXY import PlotXY
|
||||
from View.Results.PlotAC import PlotAC
|
||||
@@ -53,7 +53,10 @@ from View.Results.PlotSedReach import PlotSedReach
|
||||
from View.Results.PlotSedProfile import PlotSedProfile
|
||||
|
||||
from View.Results.Table import TableModel
|
||||
from View.Results.translate import *
|
||||
from View.Results.translate import (
|
||||
table_headers_reach, table_headers_profile,
|
||||
retranslate,
|
||||
)
|
||||
from View.Stricklers.Window import StricklersWindow
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -102,6 +105,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
self._table = {}
|
||||
|
||||
for t in ["reach", "profile"]:
|
||||
@@ -131,7 +135,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_graph(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
)
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||
@@ -150,7 +154,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_2.setObjectName("canvas_2")
|
||||
self.toolbar_2 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_2 = PamhyrPlotToolbar(
|
||||
self.canvas_2, self
|
||||
)
|
||||
self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
|
||||
@@ -168,7 +172,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self.canvas_3 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_3.setObjectName("canvas_3")
|
||||
self.toolbar_3 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_3 = PamhyrPlotToolbar(
|
||||
self.canvas_3, self
|
||||
)
|
||||
self.plot_layout_3 = self.find(QVBoxLayout, "verticalLayout_3")
|
||||
@@ -186,7 +190,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self.canvas_4 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_4.setObjectName("canvas_4")
|
||||
self.toolbar_4 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_4 = PamhyrPlotToolbar(
|
||||
self.canvas_4, self
|
||||
)
|
||||
self.plot_layout_4 = self.find(QVBoxLayout, "verticalLayout_hydrograph")
|
||||
@@ -204,7 +208,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self.canvas_5 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_5.setObjectName("canvas_5")
|
||||
self.toolbar_5 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_5 = PamhyrPlotToolbar(
|
||||
self.canvas_5, self
|
||||
)
|
||||
self.plot_layout_5 = self.find(QVBoxLayout, "verticalLayout_sed_reach")
|
||||
@@ -224,7 +228,7 @@ class ResultsWindow(ASubMainWindow, ListedSubWindow):
|
||||
|
||||
self.canvas_6 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_6.setObjectName("canvas_6")
|
||||
self.toolbar_6 = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar_6 = PamhyrPlotToolbar(
|
||||
self.canvas_6, self
|
||||
)
|
||||
self.plot_layout_6 = self.find(QVBoxLayout, "verticalLayout_sed_profile")
|
||||
|
||||
@@ -28,3 +28,9 @@ table_headers_profile = {
|
||||
"name": _translate("Results", "Name"),
|
||||
"kp": _translate("Results", "KP (m)"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers_reach["name"] = _translate("Results", "Reach name")
|
||||
|
||||
table_headers_profile["name"] = _translate("Results", "Name")
|
||||
table_headers_profile["kp"] = _translate("Results", "KP (m)")
|
||||
@@ -41,12 +41,12 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.SedimentLayers.Edit.UndoCommand import *
|
||||
from View.SedimentLayers.Edit.Table import *
|
||||
from View.SedimentLayers.Edit.Plot import Plot
|
||||
from View.SedimentLayers.Edit.translate import *
|
||||
from View.SedimentLayers.Edit.translate import table_headers, retranslate
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
@@ -89,6 +89,7 @@ class EditSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
self._table = {}
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
@@ -106,7 +107,7 @@ class EditSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_graph(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
)
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||
|
||||
@@ -12,3 +12,11 @@ table_headers = {
|
||||
"sigma": _translate("SedimentLayers", "Sigma"),
|
||||
"critical_constraint": _translate("SedimentLayers", "Critical constraint"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("SedimentLayers", "Name")
|
||||
# table_headers["type"] = _translate("SedimentLayers", "Type")
|
||||
table_headers["height"] = _translate("SedimentLayers", "Height")
|
||||
table_headers["d50"] = _translate("SedimentLayers", "D50")
|
||||
table_headers["sigma"] = _translate("SedimentLayers", "Sigma")
|
||||
table_headers["critical_constraint"] = _translate("SedimentLayers", "Critical constraint")
|
||||
@@ -29,9 +29,9 @@ from View.SedimentLayers.Reach.Profile.Table import *
|
||||
from View.SedimentLayers.Reach.Profile.Plot import Plot
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.SedimentLayers.Reach.Profile.translate import *
|
||||
from View.SedimentLayers.Reach.Profile.translate import table_headers, retranslate
|
||||
from View.SedimentLayers.Window import SedimentLayersWindow
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -85,6 +85,8 @@ class ProfileSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
self._table = TableModel(
|
||||
study = self._study,
|
||||
@@ -110,7 +112,7 @@ class ProfileSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_graph(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
)
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||
|
||||
@@ -11,3 +11,10 @@ table_headers = {
|
||||
"name": _translate("SedimentLayers", "Name"),
|
||||
"sl": _translate("SedimentLayers", "Sediment layers"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["x"] = _translate("SedimentLayers", "X (m)")
|
||||
table_headers["y"] = _translate("SedimentLayers", "Y (m)")
|
||||
table_headers["z"] = _translate("SedimentLayers", "Z (m)")
|
||||
table_headers["name"] = _translate("SedimentLayers", "Name")
|
||||
table_headers["sl"] = _translate("SedimentLayers", "Sediment layers")
|
||||
@@ -30,9 +30,9 @@ from View.SedimentLayers.Reach.Plot import Plot
|
||||
from View.SedimentLayers.Reach.SLDialog import SLDialog
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
|
||||
from View.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.SedimentLayers.Reach.translate import *
|
||||
from View.SedimentLayers.Reach.translate import table_headers, retranslate
|
||||
from View.SedimentLayers.Window import SedimentLayersWindow
|
||||
from View.SedimentLayers.Reach.Profile.Window import ProfileSedimentLayersWindow
|
||||
|
||||
@@ -73,6 +73,8 @@ class ReachSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
self._table = TableModel(
|
||||
study = self._study,
|
||||
@@ -98,7 +100,7 @@ class ReachSedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
def setup_graph(self):
|
||||
self.canvas = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas.setObjectName("canvas")
|
||||
self.toolbar = PamHyrNavigationToolbar2QT(
|
||||
self.toolbar = PamhyrPlotToolbar(
|
||||
self.canvas, self
|
||||
)
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout_2")
|
||||
|
||||
@@ -9,3 +9,8 @@ table_headers = {
|
||||
"kp": _translate("SedimentLayers", "KP (m)"),
|
||||
"sl": _translate("SedimentLayers", "Sediment layers"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("SedimentLayers", "Name")
|
||||
table_headers["kp"] = _translate("SedimentLayers", "KP (m)")
|
||||
table_headers["sl"] = _translate("SedimentLayers", "Sediment layers")
|
||||
@@ -30,7 +30,7 @@ from View.SedimentLayers.Table import *
|
||||
from View.SedimentLayers.Edit.Plot import Plot
|
||||
|
||||
from View.Plot.MplCanvas import MplCanvas
|
||||
from View.SedimentLayers.translate import *
|
||||
from View.SedimentLayers.translate import table_headers, retranslate
|
||||
|
||||
from View.SedimentLayers.Edit.Window import EditSedimentLayersWindow
|
||||
|
||||
@@ -70,6 +70,8 @@ class SedimentLayersWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
self._table = TableModel(
|
||||
study = self._study,
|
||||
|
||||
@@ -8,3 +8,7 @@ table_headers = {
|
||||
"name": _translate("SedimentLayers", "Name"),
|
||||
"comment": _translate("SedimentLayers", "Comment"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("SedimentLayers", "Name")
|
||||
table_headers["comment"] = _translate("SedimentLayers", "Comment")
|
||||
@@ -34,37 +34,19 @@ from PyQt5.QtWidgets import (
|
||||
QComboBox,
|
||||
)
|
||||
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from View.Stricklers.UndoCommand import (
|
||||
SetNameCommand, SetCommentCommand,
|
||||
SetMinorCommand, SetMediumCommand,
|
||||
AddCommand, DelCommand, SortCommand,
|
||||
)
|
||||
|
||||
from View.Stricklers.translate import *
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
class TableModel(QAbstractTableModel):
|
||||
def __init__(self, data=None, undo=None, tab=""):
|
||||
super(QAbstractTableModel, self).__init__()
|
||||
self._headers = list(table_headers.keys())
|
||||
self._data = data
|
||||
self._undo = undo
|
||||
|
||||
def flags(self, index):
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self._data)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._headers)
|
||||
|
||||
class TableModel(PamhyrTableModel):
|
||||
def data(self, index, role):
|
||||
if role != Qt.ItemDataRole.DisplayRole:
|
||||
return QVariant()
|
||||
@@ -83,12 +65,7 @@ class TableModel(QAbstractTableModel):
|
||||
|
||||
return QVariant()
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||
return table_headers[self._headers[section]]
|
||||
|
||||
return QVariant()
|
||||
|
||||
@pyqtSlot()
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if not index.isValid() or role != Qt.EditRole:
|
||||
return False
|
||||
|
||||
@@ -100,7 +100,7 @@ class AddCommand(QUndoCommand):
|
||||
|
||||
def redo(self):
|
||||
if self._new is None:
|
||||
self._new = self._data.add(self._index)
|
||||
self._new = self._data.new(self._index)
|
||||
else:
|
||||
self._data.insert(self._index, self._new)
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ from Model.Stricklers.Stricklers import Stricklers
|
||||
|
||||
from View.Stricklers.UndoCommand import PasteCommand
|
||||
from View.Stricklers.Table import TableModel
|
||||
from View.Stricklers.translate import table_headers, retranslate
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
@@ -74,7 +75,9 @@ class StricklersWindow(ASubMainWindow, ListedSubWindow):
|
||||
self.copy_sc = QShortcut(QKeySequence.Copy, self)
|
||||
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||
|
||||
|
||||
def setup_table(self):
|
||||
retranslate()
|
||||
self._table = {}
|
||||
|
||||
for t in ["app", "study"]:
|
||||
@@ -85,6 +88,9 @@ class StricklersWindow(ASubMainWindow, ListedSubWindow):
|
||||
data = self._config.stricklers
|
||||
|
||||
self._table[t] = TableModel(
|
||||
table_view = table,
|
||||
table_headers = table_headers,
|
||||
editable_headers = ["name", "comment", "minor", "medium"],
|
||||
data = data,
|
||||
undo = self._undo_stack,
|
||||
)
|
||||
|
||||
@@ -30,3 +30,9 @@ table_headers = {
|
||||
"medium": _translate("LateralContribution", "Medium bed"),
|
||||
"comment": _translate("LateralContribution", "Comment"),
|
||||
}
|
||||
|
||||
def retranslate():
|
||||
table_headers["name"] = _translate("LateralContribution", "Name")
|
||||
table_headers["minor"] = _translate("LateralContribution", "Minor bed")
|
||||
table_headers["medium"] = _translate("LateralContribution", "Medium bed")
|
||||
table_headers["comment"] = _translate("LateralContribution", "Comment")
|
||||
@@ -0,0 +1,70 @@
|
||||
# PamhyrDelegate.py -- Pamhyr
|
||||
# Copyright (C) 2023 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 PyQt5.QtCore import (
|
||||
Qt, QRect, QTime, QDateTime, pyqtSlot,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QSpinBox, QTimeEdit, QDateTimeEdit, QItemDelegate,
|
||||
)
|
||||
|
||||
from View.Tools.PamhyrWidget import (
|
||||
PamhyrExtendedTimeEdit, PamhyrExtendedDateTimeEdit
|
||||
)
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class PamhyrExTimeDelegate(QItemDelegate):
|
||||
def __init__(self, data=None, mode="time", parent=None):
|
||||
super(PamhyrExTimeDelegate, self).__init__(parent)
|
||||
|
||||
self._data = data
|
||||
self._mode = mode
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
if self._mode == "time":
|
||||
self.editor = PamhyrExtendedTimeEdit(parent=parent)
|
||||
else:
|
||||
self.editor = PamhyrExtendedDateTimeEdit(parent=parent)
|
||||
value = index.data(Qt.DisplayRole)
|
||||
self.editor.set_time(value)
|
||||
logger.debug(str(value))
|
||||
return self.editor
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
time = editor.get_time()
|
||||
if self._mode == "time":
|
||||
model.setData(index, int(time.total_seconds()))
|
||||
else:
|
||||
logger.debug(str(time.timestamp()))
|
||||
model.setData(index, int(time.timestamp()))
|
||||
editor.close()
|
||||
editor.deleteLater()
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
r = QRect(option.rect)
|
||||
if self.editor.windowFlags() & Qt.Popup and 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())
|
||||
@@ -0,0 +1,140 @@
|
||||
# PamhyrTable.py -- Pamhyr abstract table model
|
||||
# Copyright (C) 2023 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 Model.Except import NotImplementedMethodeError
|
||||
|
||||
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, QStyledItemDelegate,
|
||||
)
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class PamhyrTextDelegate(QStyledItemDelegate):
|
||||
def __init__(self, parent=None):
|
||||
super(PamhyrTextDelegate, self).__init__(parent)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
index.model().data(index, Qt.DisplayRole)
|
||||
return QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
value = index.model().data(index, Qt.DisplayRole)
|
||||
editor.setText(str(value))
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
model.setData(index, editor.text())
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
editor.setGeometry(option.rect)
|
||||
|
||||
class PamhyrTableModel(QAbstractTableModel):
|
||||
def _setup_delegates(self):
|
||||
if self._table_view is None:
|
||||
return
|
||||
|
||||
for h in self._headers:
|
||||
if h in self._delegates:
|
||||
self._table_view.setItemDelegateForColumn(
|
||||
self._headers.index(h), self._delegates[h]
|
||||
)
|
||||
else:
|
||||
self._table_view.setItemDelegateForColumn(
|
||||
self._headers.index(h), PamhyrTextDelegate(
|
||||
parent=self
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self,
|
||||
table_view=None,
|
||||
table_headers={},
|
||||
editable_headers=[],
|
||||
delegates = {},
|
||||
data=None,
|
||||
undo=None,
|
||||
opt_data=None):
|
||||
super(PamhyrTableModel, self).__init__()
|
||||
|
||||
self._table_view = table_view
|
||||
|
||||
self._table_headers = table_headers
|
||||
self._headers = list(table_headers.keys())
|
||||
self._editable_headers = editable_headers
|
||||
self._delegates = delegates
|
||||
|
||||
self._data = data
|
||||
self._opt_data = opt_data
|
||||
self._undo = undo
|
||||
self._lst = []
|
||||
|
||||
self._setup_delegates()
|
||||
self._setup_lst()
|
||||
|
||||
def _setup_lst(self):
|
||||
self._lst = self._data
|
||||
|
||||
def flags(self, index):
|
||||
column = index.column()
|
||||
|
||||
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
|
||||
if (self._editable_headers or
|
||||
self._headers[column] in self._editable_headers):
|
||||
options |= Qt.ItemIsEditable
|
||||
|
||||
return options
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self._lst)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._headers)
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||
return self._table_headers[self._headers[section]]
|
||||
|
||||
return QVariant()
|
||||
|
||||
def data(self, index, role):
|
||||
raise NotImplementedMethodeError(self, self.data)
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
raise NotImplementedMethodeError(self, self.setData)
|
||||
|
||||
def undo(self):
|
||||
self._undo.undo()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def redo(self):
|
||||
self._undo.redo()
|
||||
self.layoutChanged.emit()
|
||||
@@ -0,0 +1,92 @@
|
||||
# PamhyrWidget.py -- Pamhyr
|
||||
# Copyright (C) 2023 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 PyQt5.QtCore import (
|
||||
QModelIndex, QRect, QTime, QDateTime,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QSpinBox, QTimeEdit, QDateTimeEdit, QItemDelegate,
|
||||
)
|
||||
|
||||
from View.ASubWindow import AWidget
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
class PamhyrExtendedTimeEdit(AWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(PamhyrExtendedTimeEdit, self).__init__(
|
||||
ui="extendedTimeEdit",
|
||||
parent=parent
|
||||
)
|
||||
self.parent = parent
|
||||
|
||||
self.spinBox_days = self.find(QSpinBox, "spinBox_days")
|
||||
self.timeEdit = self.find(QTimeEdit, "timeEdit")
|
||||
|
||||
def set_time(self, time):
|
||||
days = 0
|
||||
stime = time
|
||||
|
||||
# if ',' in time, time format is 'DD days, HH:MM:SS',
|
||||
# otherelse is 'HH:MM:SS'
|
||||
if "," in time:
|
||||
s = time.strip().split(" ")
|
||||
days = int(s[0])
|
||||
stime = s[-1]
|
||||
|
||||
qtime = QTime.fromString(
|
||||
stime,
|
||||
"h:mm:ss"
|
||||
)
|
||||
self.spinBox_days.setValue(days)
|
||||
self.timeEdit.setTime(qtime)
|
||||
|
||||
def get_time(self):
|
||||
days = self.spinBox_days.value()
|
||||
time = self.timeEdit.time().toPyTime()
|
||||
secs = (
|
||||
(time.hour * 3600) +
|
||||
(time.minute * 60) +
|
||||
time.second
|
||||
)
|
||||
|
||||
return timedelta(days=days, seconds=secs)
|
||||
|
||||
class PamhyrExtendedDateTimeEdit(AWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(ExtendedDateTimeEdit, self).__init__(
|
||||
ui="extendedDateTimeEdit",
|
||||
parent=parent
|
||||
)
|
||||
self.parent = parent
|
||||
|
||||
self.dateTimeEdit = self.find(QDateTimeEdit, "dateTimeEdit")
|
||||
|
||||
def set_time(self, time):
|
||||
qdatetime = QDateTime.fromString(
|
||||
time,
|
||||
"yyyy-MM-dd hh:mm:ss"
|
||||
)
|
||||
self.dateTimeEdit.setDateTime(qdatetime)
|
||||
|
||||
def get_time(self):
|
||||
time = self.dateTimeEdit.dateTime().toPyDateTime()
|
||||
return time
|
||||
@@ -39,7 +39,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About PAMHYR</string>
|
||||
<string>About Pamhyr2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+2
-2
@@ -96,7 +96,7 @@ class Config(SQL):
|
||||
INSERT INTO solver VALUES (
|
||||
'mage8',
|
||||
'default-mage',
|
||||
'Default PAMHYR mage 8 version',
|
||||
'Default Pamhyr2 mage 8 version',
|
||||
|
||||
'', '', '',
|
||||
|
||||
@@ -265,7 +265,7 @@ class Config(SQL):
|
||||
posix = os.name == 'posix'
|
||||
ctor = solver_type_list["mage8"]
|
||||
new = ctor("default-mage")
|
||||
new._description = "Default PAMHYR mage 8 version"
|
||||
new._description = "Default Pamhyr2 mage 8 version"
|
||||
new._cmd_solver = f""""@install_dir/mage/mage{"" if posix else ".exe"}" -fp=1 @input"""
|
||||
self._solvers.append(new)
|
||||
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
# init.py -- Pamhyr
|
||||
# Copyright (C) 2023 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 sys, os
|
||||
import locale
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import QTranslator
|
||||
|
||||
from tools import (
|
||||
reset_timers, display_timers, timer,
|
||||
logger_color_blue, logger_color_red, logger_color_green, logger_color_reset
|
||||
)
|
||||
from config import Config
|
||||
|
||||
from View.MainWindow import ApplicationWindow
|
||||
from Model.Study import Study
|
||||
|
||||
|
||||
# Configure logger
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format=(f'[{logger_color_blue()}PAMHYR{logger_color_reset()}]' +
|
||||
f'[{logger_color_green()}%(levelname)s{logger_color_reset()}]' +
|
||||
' %(message)s')
|
||||
)
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
try:
|
||||
log = os.path.join(
|
||||
os.path.dirname(Config.filename()), "log.txt"
|
||||
)
|
||||
logfile = open(log, "w+")
|
||||
handler = logging.StreamHandler(logfile)
|
||||
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
logger.addHandler(handler)
|
||||
except:
|
||||
logger.error("Failed to create logfile...")
|
||||
|
||||
|
||||
def license():
|
||||
"Display in stdout Pamhyr infomation about licence and version"
|
||||
blue = lambda s: logger.info(f"{logger_color_blue()}{s}{logger_color_reset()}")
|
||||
|
||||
blue("""`7MM\"""Mq. db `7MMM. ,MMF'`7MMF' `7MMF'`YMM' `MM'`7MM\"""Mq.""")
|
||||
blue(""" MM `MM. ;MM: MMMb dPMM MM MM VMA ,V MM `MM.""")
|
||||
blue(""" MM ,M9 ,V^MM. M YM ,M MM MM MM VMA ,V MM ,M9 pd*"*b.""")
|
||||
blue(""" MMmmdM9 ,M `MM M Mb M' MM MMmmmmmmMM VMMP MMmmdM9 (O) j8""")
|
||||
blue(""" MM AbmmmqMA M YM.P' MM MM MM MM MM YM. ,;j9""")
|
||||
blue(""" MM A' VML M `YM' MM MM MM MM MM `Mb. ,-='""")
|
||||
blue(""".JMML. .AMA. .AMMA..JML. `' .JMML..JMML. .JMML. .JMML. .JMML. .JMM. Ammmmmmm""")
|
||||
|
||||
with open(os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"VERSION"
|
||||
)
|
||||
), "r") as f:
|
||||
version = f.readline().strip()
|
||||
logger.info(f"version: {logger_color_green()}{version}{logger_color_reset()}")
|
||||
|
||||
logger.info("license: pamhyr Copyright (C) 2023 INRAE")
|
||||
logger.info("license: This program comes with ABSOLUTELY NO WARRANTY.")
|
||||
logger.info("license: This is free software, and you are welcome to redistribute it")
|
||||
logger.info("license: under certain conditions.")
|
||||
|
||||
def setup_lang(app, conf:Config):
|
||||
"Return QTranslator configured for current language"
|
||||
lang_file = ""
|
||||
translator = QTranslator()
|
||||
|
||||
if conf.lang == "":
|
||||
# System language
|
||||
sys_lang = locale.getdefaultlocale()
|
||||
if "fr" in sys_lang[0]:
|
||||
lang_file = os.path.dirname(__file__) + "/lang/fr.qm"
|
||||
elif conf.lang == "fr":
|
||||
# French
|
||||
lang_file = os.path.dirname(__file__) + "/lang/fr.qm"
|
||||
else:
|
||||
# English default language
|
||||
lang_file = ""
|
||||
|
||||
if lang_file != "":
|
||||
logger.info(f"Load lang file: {lang_file}")
|
||||
ok = translator.load(lang_file)
|
||||
if not ok:
|
||||
logger.error("Failed to load translate file")
|
||||
|
||||
return translator
|
||||
+122
-83
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="fr_FR" sourcelanguage="en_150">
|
||||
<!DOCTYPE TS><TS version="2.0" language="fr_FR" sourcelanguage="en_150">
|
||||
<context>
|
||||
<name>About</name>
|
||||
<message>
|
||||
@@ -12,7 +11,7 @@
|
||||
<context>
|
||||
<name>BoundaryCondition</name>
|
||||
<message>
|
||||
<location filename="../View/BoundaryCondition/Table.py" line="143"/>
|
||||
<location filename="../View/BoundaryCondition/Table.py" line="132"/>
|
||||
<source>Not associate</source>
|
||||
<translation>Non associer</translation>
|
||||
</message>
|
||||
@@ -78,7 +77,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/BoundaryCondition/Edit/translate.py" line="12"/>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<translation type="obsolete">Débit (m³/s)</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -106,6 +105,11 @@
|
||||
<source>ZDischarge</source>
|
||||
<translation>HauteurDébit</translation>
|
||||
</message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/BoundaryCondition/Edit/translate.py" line="28"/>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Checker</name>
|
||||
@@ -486,12 +490,12 @@
|
||||
<message>
|
||||
<location filename="../View/ui/about.ui" line="42"/>
|
||||
<source>About PAMHYR</source>
|
||||
<translation>À propos de PAMHYR</translation>
|
||||
<translation type="obsolete">À propos de PAMHYR</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/ui/about.ui" line="56"/>
|
||||
<source>Version: @version</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Version: @version</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/ui/about.ui" line="63"/>
|
||||
@@ -518,6 +522,11 @@
|
||||
<source>Sigma</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/ui/about.ui" line="42"/>
|
||||
<source>About Pamhyr2</source>
|
||||
<translation>À propos de Pamhyr2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Frictions</name>
|
||||
@@ -603,7 +612,7 @@
|
||||
<translation>Commentaire</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/LateralContribution/Table.py" line="141"/>
|
||||
<location filename="../View/LateralContribution/Table.py" line="130"/>
|
||||
<source>Not associate</source>
|
||||
<translation>Non associer</translation>
|
||||
</message>
|
||||
@@ -699,9 +708,14 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/InitialConditions/translate.py" line="11"/>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<translation type="obsolete">Débit (m³/s)</translation>
|
||||
</message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/InitialConditions/translate.py" line="27"/>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
@@ -1408,7 +1422,7 @@
|
||||
<message>
|
||||
<location filename="../View/ui/SolverLog.ui" line="106"/>
|
||||
<source>results</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Résultats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/ui/EditSedimentLayers.ui" line="63"/>
|
||||
@@ -1510,28 +1524,28 @@
|
||||
<source>Edit sediment layers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/ui/MainWindow.ui" line="553"/>
|
||||
<source>Édition des Tronçons</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/ui/MainWindow.ui" line="667"/>
|
||||
<source>Ouvrir une étude</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/ui/MainWindow.ui" line="679"/>
|
||||
<source>Enrégistrer étude en cours (Ctrl+S)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/ui/MainWindow.ui" line="694"/>
|
||||
<source>Fermer étude en cours (Ctrl+F)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<context encoding="UTF-8">
|
||||
<name>MainWindowProfile</name>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/Window.py" line="83"/>
|
||||
@@ -1549,17 +1563,17 @@
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="223"/>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="224"/>
|
||||
<source>MainWindowProfile</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="227"/>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="228"/>
|
||||
<source>Trier les points par ordre croissant de X</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="231"/>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="232"/>
|
||||
<source>Trier les points par ordre croissant de Y</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -1605,108 +1619,108 @@
|
||||
<source>Cote (m)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/Window.py" line="328"/>
|
||||
<source>Suppression les lignes incomplètes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/Window.py" line="328"/>
|
||||
<source>Supprimer les lignes des cellules non renseignées ?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/Window.py" line="371"/>
|
||||
<source>Suppression des noms répétés</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/Window.py" line="382"/>
|
||||
<source>Etes-vous sûr de vouloir quitter ?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="224"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="225"/>
|
||||
<source>Insérer un point</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="225"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="226"/>
|
||||
<source>Supprimer le/les point(s) sélectionnés</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="229"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="230"/>
|
||||
<source>Trier les points par ordre décroissant de X</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="234"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="235"/>
|
||||
<source>Trier les points par ordre décroissant de Y</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="236"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="237"/>
|
||||
<source>Décaler le point sélectionné vers le haut</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="238"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="239"/>
|
||||
<source>Décaler le point sélectionné vers le bas</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="240"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="241"/>
|
||||
<source>Exporter (dans un fichier) les points du profil au format tabulé</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="242"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="243"/>
|
||||
<source>Copier la sélection au format tabulé</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="244"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="245"/>
|
||||
<source>Coller la sélection depuis le presse-papier au format tabulé</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="246"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="247"/>
|
||||
<source>Vérifier la validité de la saisie et garder ou pas les modifications apportées</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="249"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="250"/>
|
||||
<source>Annuler toutes les modifications depuis la dernière validation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="251"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="252"/>
|
||||
<source>Annuler toutes les modifications et revenir à l'état initial</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="253"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="254"/>
|
||||
<source>Ligne d'eau :
|
||||
Z : Cote (m)
|
||||
A : Aire mouillée (mu00B2)
|
||||
p : Périmètre mouillé (m)
|
||||
L : Largeur au miroir (m)</source>
|
||||
<byte value="x9"/>Z : Cote (m)
|
||||
<byte value="x9"/>A : Aire mouillée (mu00B2)
|
||||
<byte value="x9"/>p : Périmètre mouillé (m)
|
||||
<byte value="x9"/>L : Largeur au miroir (m)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="257"/>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="258"/>
|
||||
<source>'Maj + Clic' : Ligne d'eau & 'Ctrl + Clic' : Sélectionner des points</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Profile/Table.py" line="142"/>
|
||||
<source>Abscisse en travers calculée en projétant les points
|
||||
sur le plan défini par les deux points nommés extrêmes </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<context encoding="UTF-8">
|
||||
<name>MainWindow_reach</name>
|
||||
<message>
|
||||
<location filename="../View/Frictions/PlotStricklers.py" line="76"/>
|
||||
@@ -1844,7 +1858,7 @@ sur le plan défini par les deux points nommés extrêmes </source>
|
||||
<translation type="unfinished">Hauteur (m)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="146"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="148"/>
|
||||
<source>Select destination file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -1898,58 +1912,58 @@ sur le plan défini par les deux points nommés extrêmes </source>
|
||||
<source> Editer selected profile</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Window.py" line="164"/>
|
||||
<source>Édition des profils sélectionnés</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Window.py" line="166"/>
|
||||
<source>Vous avez sélectionné plus de 5 profils.
|
||||
Seuls les 5 premiers seront édités.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/Window.py" line="322"/>
|
||||
<source>Profil N° : </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/PlotAC.py" line="179"/>
|
||||
<source>Profil précédent</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/PlotAC.py" line="180"/>
|
||||
<source>Profil sélectionné</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/mainwindow_ui_reach.py" line="328"/>
|
||||
<source> Trier les profils par ordre décroissant des Pk</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/mainwindow_ui_reach.py" line="329"/>
|
||||
<source> Changer l'ordre des profils (en décalant le profil sélectionné vers le haut)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/mainwindow_ui_reach.py" line="331"/>
|
||||
<source> Changer l'ordre des profils (en décalant le profil sélectionné vers le bas)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/mainwindow_ui_reach.py" line="339"/>
|
||||
<source>Vue isométrique (Alt+Z)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/mainwindow_ui_reach.py" line="342"/>
|
||||
<source>Vue isométrique (Alt+E)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Geometry/mainwindow_ui_reach.py" line="345"/>
|
||||
<source>Vue isométrique (Alt+R)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@@ -1958,40 +1972,60 @@ Seuls les 5 premiers seront édités.</source>
|
||||
<context>
|
||||
<name>Network</name>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="809"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="812"/>
|
||||
<source>Add node</source>
|
||||
<translation>Ajouter un nœud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="817"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="820"/>
|
||||
<source>Delete the node</source>
|
||||
<translation>Supprimer un nœud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="818"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="821"/>
|
||||
<source>Disable the node</source>
|
||||
<translation>Déactiver un nœud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="826"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="829"/>
|
||||
<source>Delete the reach</source>
|
||||
<translation>Supprimer un bief</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="829"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="832"/>
|
||||
<source>Disable the reach</source>
|
||||
<translation>Déactiver un bief</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="832"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="835"/>
|
||||
<source>Enable the reach</source>
|
||||
<translation>Activer un bief</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/GraphWidget.py" line="835"/>
|
||||
<location filename="../View/Network/GraphWidget.py" line="838"/>
|
||||
<source>Reverse the reach orientation</source>
|
||||
<translation>Inverser l'orientation du bief</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/translate.py" line="29"/>
|
||||
<source>Name</source>
|
||||
<translation>Nom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/translate.py" line="25"/>
|
||||
<source>Type</source>
|
||||
<translation>Type</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/translate.py" line="30"/>
|
||||
<source>Source node</source>
|
||||
<translation>Nœud source</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Network/translate.py" line="31"/>
|
||||
<source>Destination node</source>
|
||||
<translation>Nœud destination</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Results</name>
|
||||
@@ -2037,9 +2071,14 @@ Seuls les 5 premiers seront édités.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Results/PlotH.py" line="79"/>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<translation type="obsolete">Débit (m³/s)</translation>
|
||||
</message>
|
||||
<message encoding="UTF-8">
|
||||
<location filename="../View/Results/PlotH.py" line="79"/>
|
||||
<source>Discharge (m³/s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SedimentLayers</name>
|
||||
@@ -2303,42 +2342,42 @@ Seuls les 5 premiers seront édités.</source>
|
||||
<context>
|
||||
<name>Toolbar</name>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="182"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="181"/>
|
||||
<source>Zoom</source>
|
||||
<translation>Zoom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="178"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="177"/>
|
||||
<source>Default view</source>
|
||||
<translation>Vue par défaut</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="179"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="178"/>
|
||||
<source>Back to previous view</source>
|
||||
<translation>Retour a la vue précédente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="180"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="179"/>
|
||||
<source>Return to next view</source>
|
||||
<translation>Retour à la vue suivante</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="181"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="180"/>
|
||||
<source>Axes panoramic</source>
|
||||
<translation>Axes panoramique</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="184"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="183"/>
|
||||
<source>Isometric view (Shift+W)</source>
|
||||
<translation>Vue isométrique (Shift+W)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="185"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="184"/>
|
||||
<source>Auto scale view (Shift+X)</source>
|
||||
<translation>Vue automatique (Shift+X)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../View/Plot/navigation_toolbar_2qt.py" line="183"/>
|
||||
<location filename="../View/Plot/PamhyrToolbar.py" line="182"/>
|
||||
<source>Save the figure</source>
|
||||
<translation>Sauvegarder la figure</translation>
|
||||
</message>
|
||||
|
||||
+47
-64
@@ -34,85 +34,68 @@ from tools import (
|
||||
from View.MainWindow import ApplicationWindow
|
||||
from Model.Study import Study
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format=(f'[{logger_color_blue()}PAMHYR{logger_color_reset()}]' +
|
||||
f'[{logger_color_green()}%(levelname)s{logger_color_reset()}]' +
|
||||
' %(message)s')
|
||||
)
|
||||
from Scripts.Hello import ScriptHello
|
||||
|
||||
from init import license, setup_lang
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
try:
|
||||
log = os.path.join(
|
||||
os.path.dirname(Config.filename()), "log.txt"
|
||||
)
|
||||
logfile = open(log, "w+")
|
||||
handler = logging.StreamHandler(logfile)
|
||||
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
logger.addHandler(handler)
|
||||
except:
|
||||
logger.error("Failed to create logfile...")
|
||||
scripts = {
|
||||
"hello": ScriptHello,
|
||||
}
|
||||
|
||||
def license():
|
||||
blue = lambda s: logger.info(f"{logger_color_blue()}{s}{logger_color_reset()}")
|
||||
def usage(argv):
|
||||
logger.info("")
|
||||
logger.info(f"Usage: {argv[0]} <script> <args...>")
|
||||
logger.info("")
|
||||
logger.info(f"Available scripts:")
|
||||
logger.info(f"\thelp\t\tDisplay this message")
|
||||
logger.info(f"\tgui\t\tRun Pamhyr graphics user interface (by default)")
|
||||
logger.info(f"\tdebug\t\tRun Pamhyr graphics user interface as debug mode (for developers)")
|
||||
for s in scripts:
|
||||
logger.info(f"\t{s}\t\t{scripts[s].description}")
|
||||
logger.info("")
|
||||
|
||||
blue("""`7MM\"""Mq. db `7MMM. ,MMF'`7MMF' `7MMF'`YMM' `MM'`7MM\"""Mq.""")
|
||||
blue(""" MM `MM. ;MM: MMMb dPMM MM MM VMA ,V MM `MM.""")
|
||||
blue(""" MM ,M9 ,V^MM. M YM ,M MM MM MM VMA ,V MM ,M9 pd*"*b.""")
|
||||
blue(""" MMmmdM9 ,M `MM M Mb M' MM MMmmmmmmMM VMMP MMmmdM9 (O) j8""")
|
||||
blue(""" MM AbmmmqMA M YM.P' MM MM MM MM MM YM. ,;j9""")
|
||||
blue(""" MM A' VML M `YM' MM MM MM MM MM `Mb. ,-='""")
|
||||
blue(""".JMML. .AMA. .AMMA..JML. `' .JMML..JMML. .JMML. .JMML. .JMML. .JMM. Ammmmmmm""")
|
||||
def gui(app:QApplication, conf:Config):
|
||||
application = ApplicationWindow(conf=conf)
|
||||
application.show()
|
||||
|
||||
with open(os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"VERSION"
|
||||
)
|
||||
), "r") as f:
|
||||
version = f.readline().strip()
|
||||
logger.info(f"version: {logger_color_green()}{version}{logger_color_reset()}")
|
||||
|
||||
logger.info("license: pamhyr Copyright (C) 2023 INRAE")
|
||||
logger.info("license: This program comes with ABSOLUTELY NO WARRANTY.")
|
||||
logger.info("license: This is free software, and you are welcome to redistribute it")
|
||||
logger.info("license: under certain conditions.")
|
||||
return app.exec_()
|
||||
|
||||
def main():
|
||||
conf = Config.load()
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
translator = QTranslator()
|
||||
tr = setup_lang(app, conf)
|
||||
app.installTranslator(tr)
|
||||
|
||||
license()
|
||||
|
||||
lang_file = ""
|
||||
if conf.lang == "":
|
||||
# System language
|
||||
sys_lang = locale.getdefaultlocale()
|
||||
if "fr" in sys_lang[0]:
|
||||
lang_file = os.path.dirname(__file__) + "/lang/fr.qm"
|
||||
elif conf.lang == "fr":
|
||||
# French
|
||||
lang_file = os.path.dirname(__file__) + "/lang/fr.qm"
|
||||
if len(sys.argv) > 1:
|
||||
# Run a script
|
||||
script = sys.argv[1]
|
||||
|
||||
if script == "help":
|
||||
ret = usage(sys.argv)
|
||||
elif script == "gui":
|
||||
ret = gui(app, conf)
|
||||
elif script == "debug":
|
||||
conf.debug = True
|
||||
ret = gui(app, conf)
|
||||
else:
|
||||
if script not in scripts:
|
||||
logger.error(f"{logger_color_red()}Invalid script name '{sys.argv[1]}'{logger_color_reset()}")
|
||||
usage(sys.argv)
|
||||
sys.exit(-1)
|
||||
|
||||
application = scripts[script](app, conf, sys.argv)
|
||||
ret = application.run()
|
||||
if ret != 0:
|
||||
application.usage()
|
||||
else:
|
||||
# English default language
|
||||
lang_file = ""
|
||||
# No args, run Pamhyr2 interface
|
||||
ret = gui(app, conf)
|
||||
|
||||
if lang_file != "":
|
||||
ok = translator.load(lang_file)
|
||||
if not ok:
|
||||
logger.error("Failed to load translate file")
|
||||
|
||||
app.installTranslator(translator)
|
||||
|
||||
application = ApplicationWindow(conf=conf)
|
||||
application.show()
|
||||
|
||||
ret = app.exec_()
|
||||
display_timers()
|
||||
sys.exit(ret)
|
||||
|
||||
|
||||
@@ -247,6 +247,7 @@ class SQL(object):
|
||||
self._init_db_file(filename)
|
||||
|
||||
def commit(self):
|
||||
logger.debug("SQL - commit")
|
||||
self._db.commit()
|
||||
|
||||
def _fetch_string(self, s):
|
||||
|
||||
Reference in new issue
Block a user