Compare commits

...
142 Commits
Author SHA1 Message Date
Pierre-Antoine Rouby a603c3a63f git: Update gitignore. 2023-10-10 09:51:57 +02:00
Pierre-Antoine Rouby d0ea629c10 pamhyr: Fix execution without argument. 2023-10-10 09:36:45 +02:00
Pierre-Antoine Rouby b35be16268 ci: Fix windows exe generator script. 2023-10-10 09:30:40 +02:00
Pierre-Antoine Rouby 7644927de9 pamhyr: Allow Pamhyr2 script run on tty mode. 2023-10-09 17:03:23 +02:00
Pierre-Antoine Rouby 242b1a4e50 pamhyr: Put debug info as logger debug handler. 2023-10-09 16:55:54 +02:00
Pierre-Antoine Rouby 397b5b0cab debug: Display qt5 path in terminal. 2023-10-09 16:22:16 +02:00
Pierre-Antoine Rouby 8da1cb622f ci: Install and update pip packages. 2023-10-09 15:22:28 +02:00
Pierre-Antoine Rouby 7365bf70b6 doc: Add mage documentation open action. 2023-10-09 14:51:19 +02:00
Pierre-Antoine Rouby 2825d02cb2 doc: Fix path for build version. 2023-10-09 14:29:40 +02:00
Pierre-Antoine Rouby 24cb32c96c ci: Fix linux ci. 2023-10-09 13:44:15 +02:00
Pierre-Antoine Rouby 820adf2bb7 ci: Fix linux ci. 2023-10-09 12:08:58 +02:00
Pierre-Antoine Rouby 4211855732 ci: Update ci build instruction. 2023-10-09 12:05:29 +02:00
Pierre-Antoine Rouby 44b057c522 Merge branch 'doc-dev' with refactoring and user/dev documentation. 2023-10-09 11:17:13 +02:00
Pierre-Antoine Rouby f4d580a000 doc: Doc in format html or pdf, back/forward button and firefox button. 2023-10-09 11:11:23 +02:00
Pierre-Antoine Rouby a77c342e69 doc: Fix window display for version PyQt5 > 5.5 2023-10-06 16:21:23 +02:00
Pierre-Antoine Rouby 559d7dc006 doc: Keep doc html and add doc window in Pamhyr2. 2023-10-06 15:54:27 +02:00
Pierre-Antoine Rouby 1a47bbe19d Results: Allow to open last results from mainwindow. 2023-10-06 14:54:17 +02:00
Pierre-Antoine Rouby b1c658d711 refactoring: Delete deprecated unused files. 2023-10-06 14:34:15 +02:00
Pierre-Antoine Rouby 2ff4f63d23 refactoring: refactor Results. 2023-10-06 14:25:44 +02:00
Pierre-Antoine Rouby ba087f12a5 refactoring: Apply table refacto on checklist. 2023-10-06 10:40:18 +02:00
Pierre-Antoine Rouby a64a9867ef refactoring: refacto solver parameters 2023-10-04 17:01:31 +02:00
Pierre-Antoine Rouby 63d6c114c0 refactoring: refacto SL reach and profile window. 2023-10-04 16:23:06 +02:00
Pierre-Antoine Rouby 1d45ccb8b8 refactoring: refacto SL list & edit. 2023-10-04 15:19:27 +02:00
Pierre-Antoine Rouby 4ce597a2a0 refactoring: refacto frictions & stricklers. 2023-10-04 14:34:07 +02:00
Pierre-Antoine Rouby b430cbb62b refactoring: refacto IC. 2023-10-04 13:51:01 +02:00
Pierre-Antoine Rouby 500885ec4d refactoring: refacto LC. 2023-10-04 11:26:43 +02:00
Pierre-Antoine Rouby ea3654f1f8 refactoring: refacto BC. 2023-10-04 10:35:54 +02:00
Pierre-Antoine Rouby 7a5b4779e9 refactoring: Minor fix in geometry. 2023-10-03 17:11:06 +02:00
Pierre-Antoine Rouby c744797c6f refactoring: Minor fix in geometry. 2023-10-03 16:39:31 +02:00
Pierre-Antoine Rouby 11a7486a9c Refactoring: Use ui file in Geometry profile edit window. 2023-10-03 16:26:50 +02:00
Pierre-Antoine Rouby 44ccf99d48 Refactoring: Use ui file for geometry reach window. 2023-10-03 15:10:46 +02:00
Pierre-Antoine Rouby ef672798ef refactoring: Start refactoring plot. 2023-09-29 11:17:08 +02:00
Pierre-Antoine Rouby 245a5057c3 refactoring: Update BC. 2023-09-29 10:14:40 +02:00
Pierre-Antoine Rouby e06610756a model: geometry: Fix 'sl' setter. 2023-09-29 08:33:23 +02:00
Pierre-Antoine Rouby dbc256be1a ui, doc: Update Pamhyr2 debug repl. 2023-09-27 16:49:00 +02:00
Pierre-Antoine Rouby e589fcdd3d doc: dev: Complete dev documentation. 2023-09-27 16:42:42 +02:00
Pierre-Antoine Rouby d20cdfc47f doc: dev: Complete Architecture section. 2023-09-27 15:55:45 +02:00
Pierre-Antoine Rouby ab2e307e3d doc: dev: Update graphs. 2023-09-27 12:10:47 +02:00
Pierre-Antoine Rouby a81564e582 doc: dev: Write Plot section. 2023-09-27 09:20:17 +02:00
Pierre-Antoine Rouby f93f3fe122 doc: dev: Update Table and UndoCommand. 2023-09-27 09:00:18 +02:00
Pierre-Antoine Rouby 2e94348bb8 refactoring: Add PamhyrTranslate class for GUI translation and document it. 2023-09-26 17:16:53 +02:00
Pierre-Antoine Rouby e7fd2077bd doc: dev: Update with PamhyrWindow, and minor change in hash computation. 2023-09-26 14:19:22 +02:00
Pierre-Antoine Rouby 624e9b6f71 refactoring: Apply PamhyrWindow on Debug window. 2023-09-26 12:07:10 +02:00
Pierre-Antoine Rouby ca10f19fe5 refactoring: Continue apply PamhyrWindow. 2023-09-26 12:03:41 +02:00
Pierre-Antoine Rouby 1a36d78ee2 refactoring: Apply PamhyrWindow to SL. 2023-09-26 09:34:26 +02:00
Pierre-Antoine Rouby 809a0dd9cb refarctoring: Apply PamhyrWindow to frictions. 2023-09-25 17:01:59 +02:00
Pierre-Antoine Rouby b2b5c971c3 refactoring: Apply new Pamhyr tools on IC. 2023-09-25 16:38:14 +02:00
Pierre-Antoine Rouby 7c4ba10871 refactoring: Continue refacto using PamhyrWindow. 2023-09-25 15:57:53 +02:00
Pierre-Antoine Rouby cc9f853a34 refactoring: Add PamhyrWidget and continue refacto using PamhyrWindow. 2023-09-25 15:31:41 +02:00
Pierre-Antoine Rouby fa32e42933 refactoring: Add PamhyrWindow and PamhyrDialog (WIP). 2023-09-25 15:10:30 +02:00
Pierre-Antoine Rouby 9c7f1f7ba9 doc: Try with system package. 2023-09-22 11:51:00 +02:00
Pierre-Antoine Rouby 3842e689ac doc: Fix build package missing. 2023-09-22 11:45:12 +02:00
Pierre-Antoine Rouby d461cb8001 doc: Fix htmlize into html build. 2023-09-22 11:29:04 +02:00
Pierre-Antoine Rouby 2bb783b5d4 ci: Add HTML documentation. 2023-09-22 11:09:55 +02:00
Pierre-Antoine Rouby edc5b65234 Scripts: Convert plot_3DST.py to Pamhyr2 script. 2023-09-22 11:03:07 +02:00
Pierre-Antoine Rouby 0237c5a80a MainWindow: Connect menu close model. 2023-09-22 09:25:47 +02:00
Pierre-Antoine Rouby 1201184669 packages: windows: Add Desktop shortcuts and update wine script. 2023-09-22 09:05:35 +02:00
Pierre-Antoine Rouby 36adba84e7 config: Fix arguments in default mage solver. 2023-09-20 17:25:34 +02:00
Pierre-Antoine Rouby afa21c9057 doc: dev: View section (UI, window). 2023-09-20 16:46:37 +02:00
Pierre-Antoine Rouby 42dfdcc3b1 doc: dev: Complete Model section. 2023-09-20 15:32:04 +02:00
Pierre-Antoine Rouby 6c9481296b doc: dev: Add SQL section with examples. 2023-09-20 15:21:46 +02:00
Pierre-Antoine Rouby 89353b656f doc: dev: Some minor change. 2023-09-20 11:52:53 +02:00
Pierre-Antoine Rouby 48851c7d71 ci: Fix documentations dependencies. 2023-09-20 10:10:41 +02:00
Pierre-Antoine Rouby d30028c57d ci: Disable package building at master branch. 2023-09-20 09:34:12 +02:00
Pierre-Antoine Rouby 23e36ca3f7 ci: Fix pipeline release job. 2023-09-20 09:31:54 +02:00
Pierre-Antoine Rouby e92e00f5c8 ci: Disable tests. 2023-09-19 17:37:49 +02:00
Pierre-Antoine Rouby 0d547cc469 Scripts: Fix return value. 2023-09-19 17:25:30 +02:00
Pierre-Antoine Rouby 973e6c8080 ci: Fix dependencies. 2023-09-19 17:08:20 +02:00
Pierre-Antoine Rouby 5f6154c510 ci: tests: Add test-linux and test-windows. 2023-09-19 17:05:15 +02:00
Pierre-Antoine Rouby 2cad6f21ab ci: Add running script for GNU/Linux. 2023-09-19 16:46:02 +02:00
Pierre-Antoine Rouby 41a56c6e77 ci: Fix Linux build. 2023-09-19 16:17:19 +02:00
Pierre-Antoine Rouby 487045dbad ci: Add doc. 2023-09-19 16:07:48 +02:00
Pierre-Antoine Rouby c12c06a604 Merge branch 'doc' into refactoring. 2023-09-19 15:56:53 +02:00
Pierre-Antoine Rouby 6380e3110f doc: Add temporary water mark. 2023-09-19 15:55:37 +02:00
Pierre-Antoine Rouby 3fdc0349da SL: Fix tables translate. 2023-09-19 15:29:42 +02:00
Pierre-Antoine Rouby 0691c64ca9 IC: Fix table translate. 2023-09-19 15:23:04 +02:00
Pierre-Antoine Rouby f4545b0075 LC: Fix table translate. 2023-09-19 15:19:06 +02:00
Pierre-Antoine Rouby ecefb39265 BC: Fix table translate. 2023-09-19 15:13:21 +02:00
Pierre-Antoine Rouby 95288e6eef Friction: Fix talbe translate. 2023-09-19 15:02:33 +02:00
Pierre-Antoine Rouby ffebfbdddc Stricklers: Switch stricklers table to PamhyrTableModel and fix translate. 2023-09-19 14:59:47 +02:00
Pierre-Antoine Rouby 4f471d4d6f Script: Add app in script parameters. 2023-09-19 14:41:05 +02:00
Pierre-Antoine Rouby f5046858c8 trans: Fix Network and Results table translate. 2023-09-19 14:40:04 +02:00
Pierre-Antoine Rouby 47bcf8a748 trans: Update name PAMHYR to Pamhyr2 and minor change. 2023-09-19 14:06:14 +02:00
Pierre-Antoine Rouby 694c84a526 Merge branch 'master' into refactoring. 2023-09-19 13:55:16 +02:00
Pierre-Antoine Rouby c567a0b370 Network: Fix edge selection crash. 2023-09-19 11:56:08 +02:00
Pierre-Antoine Rouby a14dfba6b4 doc: dev: Add solver pipeline graph. 2023-09-18 17:25:21 +02:00
Pierre-Antoine Rouby ed006fe1c3 doc: dev: Add archi and model graph. 2023-09-18 16:49:51 +02:00
Pierre-Antoine Rouby a733e148e2 doc: dev: Complete contribution. 2023-09-18 15:03:30 +02:00
Pierre-Antoine Rouby 77c0dddee3 doc: tools: LaTeX class minor change. 2023-09-18 14:19:18 +02:00
Pierre-Antoine Rouby 8429d04b3a doc: dev: Continue contribution section. 2023-09-18 11:58:03 +02:00
Pierre-Antoine Rouby 4c953d29fe doc: dev: Minor change. 2023-09-18 10:39:03 +02:00
Pierre-Antoine Rouby 129e3f3f88 doc: dev: Add abstract. 2023-09-18 10:36:50 +02:00
Pierre-Antoine Rouby 436f4adaf6 Doc: Add GPL logo and minor change. 2023-09-18 09:55:53 +02:00
Pierre-Antoine Rouby 9f9dd508b1 doc: dev: Add install command for texlive. 2023-09-15 16:24:26 +02:00
Pierre-Antoine Rouby d772a44c67 doc: tools, dev: Add caption and box for verbatim. 2023-09-15 16:19:46 +02:00
Pierre-Antoine Rouby 100f98c56b doc: dev: Finish the documentations files section. 2023-09-15 16:09:10 +02:00
Pierre-Antoine Rouby 445de3b777 doc: dev: Introduction + minor change. 2023-09-15 15:29:03 +02:00
Pierre-Antoine Rouby 1c7ddea417 ci: Fix pipeline. 2023-09-13 15:40:40 +02:00
Pierre-Antoine Rouby ac756ec41e ci: Fix linux package. 2023-09-13 15:36:39 +02:00
Pierre-Antoine Rouby d33d6b166f ci: Some minor fix. 2023-09-13 14:05:13 +02:00
Pierre-Antoine Rouby 1384634aa1 ci: Fix windows exe script. 2023-09-13 13:24:58 +02:00
Pierre-Antoine Rouby 906b7f2d39 ci: Minor fix. 2023-09-13 13:02:50 +02:00
Pierre-Antoine Rouby bb662346fa ci: Minor change. 2023-09-13 12:21:08 +02:00
Pierre-Antoine Rouby efff8fe4dc ci: Minor fix. 2023-09-13 12:00:28 +02:00
Pierre-Antoine Rouby fe6cafba47 ci: Minor change. 2023-09-13 11:51:35 +02:00
Pierre-Antoine Rouby ab3a006e65 ci: Add windows script. 2023-09-13 11:45:29 +02:00
Pierre-Antoine Rouby 5197bce872 ci: Minor change of debug ci. 2023-09-13 11:37:45 +02:00
Pierre-Antoine Rouby 00b4292306 ci: Minor change. 2023-09-13 11:12:04 +02:00
Pierre-Antoine Rouby ace5053c1b ci: Fix circular dependencies. 2023-09-13 10:28:31 +02:00
Pierre-Antoine Rouby 24aebfbcf8 ci: Merge build and dir creation. 2023-09-13 10:26:47 +02:00
Pierre-Antoine Rouby 5d6ae31ee9 ci: Refactoring CI pipeline. 2023-09-13 09:42:47 +02:00
Pierre-Antoine Rouby c59edf4661 Script: Help: Minor change. 2023-09-12 16:16:47 +02:00
Pierre-Antoine Rouby 029e8bb210 Script: Help: Specify the debug mode is for developers. 2023-09-12 16:14:30 +02:00
Pierre-Antoine Rouby 1fc8540bf8 Mainwindow: Display debug in main window title. 2023-09-12 16:11:27 +02:00
Pierre-Antoine Rouby 0c0ac93061 Script: Add gui, help and debug commands. 2023-09-12 16:02:51 +02:00
Pierre-Antoine Rouby 4debe830b6 Script: Add config in script init parameter. 2023-09-12 15:44:42 +02:00
Pierre-Antoine Rouby 4de6784bec Pamhyr: Add 'gui' as dummy script. 2023-09-12 15:39:20 +02:00
Pierre-Antoine Rouby d4568ddbb5 Script: Add pamhyr2 script with exemple hello. 2023-09-12 13:04:27 +02:00
Pierre-Antoine Rouby 75066fa61a Plot: Fix missin import. 2023-09-12 12:57:28 +02:00
Pierre-Antoine Rouby e887df9aac Pamhyr: Add init.py. 2023-09-12 11:24:06 +02:00
Pierre-Antoine Rouby 1662ff1e24 git: Update the README with sections 'features' and 'install'. 2023-09-12 11:00:01 +02:00
Pierre-Antoine Rouby cbc7296132 git: Add editorconfig. 2023-09-12 10:43:08 +02:00
Pierre-Antoine Rouby 8004fbcdcc Solver, View: Add some comment. 2023-09-12 10:42:51 +02:00
Pierre-Antoine Rouby bb6292ce90 View: Plot: Minor change. 2023-09-11 16:27:29 +02:00
Pierre-Antoine Rouby af72e59830 Plot: Toolbar: Rename file and class. 2023-09-11 16:23:33 +02:00
Pierre-Antoine Rouby 45690690d1 View: BC, LC: Factorise time delegate and widget. 2023-09-11 15:49:10 +02:00
Pierre-Antoine Rouby 602593a6cb View: LC: Use PamhyrTableModel. 2023-09-11 15:20:59 +02:00
Pierre-Antoine Rouby 650df2b56c View: BC: Use PamhyrTableModel. 2023-09-11 14:57:55 +02:00
Pierre-Antoine Rouby c986674306 View: Network: Add missing tranlate file. 2023-09-11 14:43:07 +02:00
Pierre-Antoine Rouby 3eeaea14fa View: Tools: Add PamhyrTableModel and switch Network tables on it. 2023-09-11 14:13:40 +02:00
Pierre-Antoine Rouby 4b9f75b8d1 Tools: SQL: Minor change. 2023-09-11 14:13:19 +02:00
Pierre-Antoine Rouby 2139b42b77 Model: Tools: List, Dict: Minor change. 2023-09-11 14:12:49 +02:00
Pierre-Antoine Rouby 8c9478f403 Model: Delete unsed file. 2023-09-11 11:41:32 +02:00
Pierre-Antoine Rouby 973ea74622 Model: Move Model.DB into Model.Tools.PamhyrDB. 2023-09-11 11:40:26 +02:00
Pierre-Antoine Rouby e63b931dfb Model: Add PamhyrModelDict and use it in IC. 2023-09-11 11:29:18 +02:00
Pierre-Antoine Rouby 0b0c402baf Model: Add PamhyrModelListWithTab. Use it in BC and LC. 2023-09-11 11:20:08 +02:00
Pierre-Antoine Rouby 3aea790829 Model: Switch SedimentLayerList to PamhyrModelList. 2023-09-11 11:06:54 +02:00
Pierre-Antoine Rouby 460683703e Model: Switch SolverParametersList to PamhyrModelList. 2023-09-11 10:58:29 +02:00
Pierre-Antoine Rouby 89b9fd21ff Model: Add a Pamhyr List Abstract implementation and use it for
Stricklers and friction.
2023-09-11 10:53:58 +02:00
Pierre-Antoine Rouby 187135e62b doc: dev: minor change. 2023-09-11 09:31:11 +02:00
Pierre-Antoine Rouby f3ec60500d doc: Update dev doc with some org-mode tips. 2023-09-08 18:43:42 +02:00
Pierre-Antoine Rouby ae857a098a doc: Add user and developers documentation scheme. 2023-09-08 17:41:40 +02:00
170 changed files with 6752 additions and 4282 deletions

No files matched your search

+12
View File
@@ -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
+334
View File
@@ -1,6 +1,7 @@
### CUSTOM ###
*.exe
*.msi
*.tar.xz
*.zip
*.spec
@@ -9,15 +10,33 @@
tmp
*.tar
*.tar.*
*.tgz
*.zip
*.html
*.pdf
*.odt
doc/dev/images/auto_*
doc/dev/documentation.*
!doc/dev/*.org
doc/dev/images/auto_*
doc/users/documentation.*
!doc/users/*.org
TAGS
pamhyrBuild
wine-pamhyr-build*
_PAMHYR_
__old__
*-venv
### END CUSTOM ###
# Created by https://www.toptal.com/developers/gitignore/api/python
@@ -309,3 +328,318 @@ compile_commands.json
*_qmlcache.qrc
# End of https://www.toptal.com/developers/gitignore/api/qt
# Created by https://www.toptal.com/developers/gitignore/api/latex
# Edit at https://www.toptal.com/developers/gitignore?templates=latex
### LaTeX ###
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
*.lzo
*.lzs
*.slg
*.slo
*.sls
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplot
*.gnuplot
*.table
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.glog
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
# *.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# newpax
*.newpax
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# svg
svg-inkscape/
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# titletoc
*.ptc
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices and outlines
*.xyc
*.xyd
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# gummi
.*.swp
# KBibTeX
*~[0-9]*
# TeXnicCenter
*.tps
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
# Makeindex log files
*.lpz
# xwatermark package
*.xwm
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
# Uncomment the next line to have this generated file ignored.
#*Notes.bib
### LaTeX Patch ###
# LIPIcs / OASIcs
*.vtc
# glossaries
*.glstex
# End of https://www.toptal.com/developers/gitignore/api/latex
+275 -115
View File
@@ -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,240 @@ build-lang:
paths:
- src/lang/*.qm
build-users-doc:
stage: build
tags:
- linux
needs:
- job: set-version
artifacts: true
script:
- cd ./doc/users/
- ./build.sh
artifacts:
paths:
- doc/users/documentation.pdf
- doc/users/documentation.html
- doc/users/images/
build-developers-doc:
stage: build
tags:
- linux
needs:
- job: set-version
artifacts: true
script:
- cd ./doc/dev/
- ./build.sh
# Create html documentation
- cd ../../src
- pdoc --html .
# Create archive
- tar cf html.tar ./html
- cd ../doc/dev
- mv ../../src/html.tar ./
artifacts:
paths:
- doc/dev/documentation.pdf
- doc/dev/documentation.html
- doc/dev/html.tar
- doc/dev/images/
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 -U -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
- mkdir -p pamhyr/pamhyr/_internal
# Copy Pamhyr info
- cp -v ../VERSION pamhyr/pamhyr/_internal/
- cp -v ../AUTHORS pamhyr/pamhyr/_internal/
- cp -v ../LICENSE pamhyr/pamhyr/_internal/
# 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/_internal/View
- mkdir -p pamhyr/pamhyr/_internal/View/ui
- mkdir -p pamhyr/pamhyr/_internal/View/ui/Widgets
- cp -r ../src/View/ui/*.ui pamhyr/pamhyr/_internal/View/ui/
- cp -r ../src/View/ui/ressources/ pamhyr/pamhyr/_internal/View/ui
- cp -r ../src/View/ui/Widgets/*.ui pamhyr/pamhyr/_internal/View/ui/Widgets
# Copy lang
- mkdir -p pamhyr/pamhyr/_internal/lang
- cp -r ../src/lang/*.qm pamhyr/pamhyr/_internal/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/
- mkdir -p pamhyr/doc/images/
- mkdir -p pamhyr/doc/html/
- cp ../doc/users/documentation.pdf pamhyr/doc/Pamhyr2-users.pdf
- cp ../doc/users/documentation.html pamhyr/doc/Pamhyr2-users.html
- cp ../doc/dev/documentation.pdf pamhyr/doc/Pamhyr2-dev.pdf
- cp ../doc/dev/documentation.html pamhyr/doc/Pamhyr2-dev.html
#- cp ../doc/users/images/* pamhyr/doc/images
- cp ../doc/dev/images/* pamhyr/doc/images
- cp ../doc/images/* pamhyr/doc/images
# 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 -U -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_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_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_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 +330,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 +358,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'
-1
View File
@@ -47,5 +47,4 @@ pip3 install -r requirements.txt
# Run pamhyr
./src/pamhyr.py
```
+18
View File
@@ -0,0 +1,18 @@
#! /bin/sh
echo " PDF documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-latex-export-to-pdf
echo " HTML documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(require 'ox-html)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-html-export-to-html
+1
View File
@@ -0,0 +1 @@
../tools/ref.bib
File diff suppressed because it is too large. Load diff
View File
Whitespace-only changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+163
View File
@@ -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}
+19
View File
@@ -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
+51
View File
@@ -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
View File
Whitespace-only changes.
+66
View File
@@ -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) "/")))
+18
View File
@@ -0,0 +1,18 @@
#! /bin/sh
echo " PDF documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-latex-export-to-pdf
echo " HTML documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(require 'ox-html)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-html-export-to-html
+1
View File
@@ -0,0 +1 @@
../tools/ref.bib
+20
View File
@@ -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}}}
View File
Whitespace-only changes.
+28 -13
View File
@@ -43,18 +43,21 @@ cd $OLD_PWD
echo " *** COPY DATA"
mkdir -p dist/pamhyr/View/ui
mkdir -p dist/pamhyr/View/ui/Widgets
cp -r ../src/View/ui/ressources/ dist/pamhyr/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/View/ui/Widgets/
cp -r ../src/View/ui/*.ui dist/pamhyr/View/ui/
mkdir -p dist/pamhyr/_internal/View/ui
mkdir -p dist/pamhyr/_internal/View/ui/Widgets
cp -r ../src/View/ui/ressources/ dist/pamhyr/_internal/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/_internal/View/ui/Widgets/
cp -r ../src/View/ui/*.ui dist/pamhyr/_internal/View/ui/
mkdir -p dist/pamhyr/lang
cp -r ../src/lang/*.qm dist/pamhyr/lang/
cp ../VERSION dist/pamhyr/
cp ../AUTHORS dist/pamhyr/
cp ../LICENSE dist/pamhyr/
mkdir dist/pamhyr/_internal/
#cp ../VERSION dist/pamhyr/_internal/
VERSION=$(cat ../VERSION)
echo "local-build-$VERSION" > dist/pamhyr/_internal/VERSION
cp ../AUTHORS dist/pamhyr/_internal/
cp ../LICENSE dist/pamhyr/_internal/
mkdir -p dist/pamhyr/mage/
cp ../mage/mage dist/pamhyr/mage/
@@ -65,6 +68,18 @@ mkdir -p dist/pamhyr/tests_cases/
mkdir -p dist/pamhyr/tests_cases/Saar
cp ../tests_cases/Saar/Saar.pamhyr dist/pamhyr/tests_cases/Saar/
mkdir -p dist/pamhyr/doc/
cp ../doc/dev/documentation.pdf dist/pamhyr/doc/Pamhyr2-dev.pdf
cp ../doc/dev/documentation.html dist/pamhyr/doc/Pamhyr2-dev.html
cp ../doc/users/documentation.pdf dist/pamhyr/doc/Pamhyr2-users.pdf
cp ../doc/users/documentation.html dist/pamhyr/doc/Pamhyr2-users.html
mkdir -p dist/pamhyr/doc/images
cp ../doc/users/images/* dist/pamhyr/doc/images/
cp ../doc/dev/images/* dist/pamhyr/doc/images/
cp ../doc/images/* dist/pamhyr/doc/images/
echo " *** MAKE SRC PACKAGE"
OLD_PWD=$PWD
@@ -76,11 +91,11 @@ mv ../pamhyr-src.tar.gz ./
echo " *** MAKE BIN PACKAGE"
OLD_PWD=$PWD
cd dist/
tar --xz -cf pamhyr-gnulinux-amd64.tar.xz pamhyr --checkpoint=.100
cd $OLD_PWD
# OLD_PWD=$PWD
# cd dist/
# tar --xz -cf pamhyr-gnulinux-amd64.tar.xz pamhyr --checkpoint=.100
# cd $OLD_PWD
mv dist/pamhyr-gnulinux-amd64.tar.xz ./
# mv dist/pamhyr-gnulinux-amd64.tar.xz ./
echo " *** DONE"
+60
View File
@@ -0,0 +1,60 @@
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\_internal
xcopy /s /e /y dist\pamhyr pamhyr\pamhyr
rem Copy data
mkdir pamhyr\pamhyr\_internal\lang
mkdir pamhyr\pamhyr\_internal\View\ui\ressources
mkdir pamhyr\pamhyr\_internal\View\ui\Widgets
rem UI
copy /y ..\src\View\ui\ressources\ pamhyr\pamhyr\_internal\View\ui\ressources
copy /y ..\src\View\ui\Widgets\*.ui pamhyr\pamhyr\_internal\View\ui\Widgets
copy /y ..\src\View\ui\*.ui pamhyr\pamhyr\_internal\View\ui\
rem Lang
copy /y ..\src\lang\*.qm pamhyr\pamhyr\_internal\lang\
rem Information
copy /y ..\VERSION pamhyr\pamhyr\_internal
copy /y ..\AUTHORS pamhyr\pamhyr\_internal
copy /y ..\LICENSE pamhyr\pamhyr\_internal
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
mkdir pamhyr\doc\images
copy /y ..\doc\users\documentation.pdf pamhyr\doc\Pamhyr2-users.pdf
copy /y ..\doc\dev\documentation.pdf pamhyr\doc\Pamhyr2-dev.pdf
copy /y ..\doc\users\documentation.html pamhyr\doc\Pamhyr2-users.html
copy /y ..\doc\dev\documentation.html pamhyr\doc\Pamhyr2-dev.html
copy /y ..\doc\users\images\* pamhyr\doc\images
copy /y ..\doc\dev\images\* pamhyr\doc\images
copy /y ..\doc\images\* pamhyr\doc\images
+20
View File
@@ -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
+23 -19
View File
@@ -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\_internal\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,39 @@ 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\Pamhyr2"
CreateShortcut "$SMPROGRAMS\Pamhyr2\Uninstall.lnk" "$INSTDIR\uninstall.exe"
CreateShortcut "$SMPROGRAMS\Pamhyr2\Pamhyr2.lnk" "$INSTDIR\pamhyr\pamhyr.exe"
SectionEnd
CreateDirectory "$SMPROGRAMS\PAMHYR"
CreateShortcut "$SMPROGRAMS\PAMHYR\Uninstall.lnk" "$INSTDIR\uninstall.exe"
CreateShortcut "$SMPROGRAMS\PAMHYR\PAMHYR.lnk" "$INSTDIR\pamhyr.exe"
Section "Desktop Shortcuts"
SetShellVarContext all
CreateShortcut "$desktop\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"
Delete "$desktop\Pamhyr2.lnk"
RMDir "$SMPROGRAMS\PAMHYR"
RMDir "$SMPROGRAMS\Pamhyr2"
RMDir /R "$INSTDIR"
SectionEnd
+45 -16
View File
@@ -81,13 +81,42 @@ wine pyinstaller ../src/pamhyr.py -y
echo " *** COPY DATA"
mkdir -p dist/pamhyr/View/ui
cp -r ../src/View/ui/ressources/ dist/pamhyr/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/View/ui/
cp -r ../src/View/ui/*.ui dist/pamhyr/View/ui/
mkdir -p pamhyr
mkdir -p pamhyr/pamhyr
cp ../VERSION dist/pamhyr/
cp ../AUTHORS dist/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/* 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
# Update TS and build QM files
OLD_PWD=$PWD
@@ -102,19 +131,19 @@ echo " *** MAKE PACKAGES"
echo " TAR pamhyr-win-amd64.tar.xz"
OLD_PWD=$PWD
cd dist/
tar --xz -cf pamhyr-win-amd64.tar.xz pamhyr --checkpoint=.100
cd $OLD_PWD
mv dist/pamhyr-win-amd64.tar.xz ./
# OLD_PWD=$PWD
# cd dist/
# tar --xz -cf pamhyr-win-amd64.tar.xz pamhyr --checkpoint=.100
# cd $OLD_PWD
# mv dist/pamhyr-win-amd64.tar.xz ./
echo " ZIP pamhyr-win-amd64.zip"
OLD_PWD=$PWD
cd dist/
zip -r pamhyr-win-amd64.zip pamhyr/
cd $OLD_PWD
mv dist/pamhyr-win-amd64.zip ./
# OLD_PWD=$PWD
# cd dist/
# zip -r pamhyr-win-amd64.zip pamhyr/
# cd $OLD_PWD
# mv dist/pamhyr-win-amd64.zip ./
echo " *** MAKE INSTALLER"
+2 -1
View File
@@ -1,6 +1,7 @@
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.8.1
PyQt5-sip==12.12.2
PyQtWebEngine==5.15.6
pyqtgraph>=0.12.1
matplotlib>=3.4.1
numpy>=1.24.2
-10
View File
@@ -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()
+1 -1
View File
@@ -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):
+8 -67
View File
@@ -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()
+2 -2
View File
@@ -40,8 +40,8 @@ class Point(object):
def sl(self, sl):
if sl == self._profile.sl:
self._sl = None
self._sl = sl
else:
self._sl = sl
@property
def name(self):
+1 -1
View File
@@ -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):
+3
View File
@@ -56,6 +56,9 @@ class Profile(object):
self._profile_type = _type
def __len__(self):
return len(self._points)
@property
def number_points(self):
return len(self._points)
+1 -1
View File
@@ -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
+5 -2
View File
@@ -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
@@ -42,7 +42,7 @@ class Reach(SQLSubModel):
]
def __init__(self, status=None, parent=None):
self.id = parent.id
self.id = parent.id if parent is not None else 0
self._status = status
self._parent = parent
self._profiles: List[Profile] = []
@@ -106,6 +106,9 @@ class Reach(SQLSubModel):
return self._parent.name
def __len__(self):
return len(self._profiles)
@property
def profiles(self):
return self._profiles.copy()
@@ -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()
+4
View File
@@ -43,6 +43,10 @@ class Results(object):
def river(self):
return self._river
@property
def study(self):
return self._study
def set(self, key, value):
self._meta_data[key] = value
+9
View File
@@ -26,6 +26,9 @@ class Profile(object):
self._profile = profile # Source profile in the study
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
def __len__(self):
return len(self._data)
@property
def name(self):
return self._profile.name
@@ -66,6 +69,9 @@ class Reach(object):
)
)
def __len__(self):
return len(self._profiles)
@property
def name(self):
return self._reach.name
@@ -91,6 +97,9 @@ class River(object):
# Dict with timestamps as key
self._reachs = []
def __len__(self):
return len(self._reachs)
@property
def reachs(self):
return self._reachs.copy()
+1 -5
View File
@@ -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
+1 -1
View File
@@ -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):
+5 -66
View File
@@ -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()
+1 -1
View File
@@ -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
+8 -49
View File
@@ -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
View File
@@ -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
+74
View File
@@ -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)
+229
View File
@@ -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()
+36
View File
@@ -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)
+34
View File
@@ -0,0 +1,34 @@
# Hello.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
+115
View File
@@ -0,0 +1,115 @@
# P3DST.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
import logging
from matplotlib import pyplot as plt
from numpy import mean
from Scripts.AScript import AScript
from Model.Saved import SavedStatus
from Model.Geometry.Reach import Reach
logger = logging.getLogger()
class Script3DST(AScript):
name = "3DST"
description = "Display a 3D plot of a river reach from ST file"
def usage(self):
logger.info(f"Usage : {self._args[0]} 3DST <INPUT_ST_FILE>")
def set_axes_equal(self, ax):
"""Make axes of 3D plot have equal scale
Make axes of 3D plot have equal scale so that spheres appear
as spheres, cubes as cubes, etc.. This is one possible
solution to Matplotlib's ax.set_aspect('equal') and
ax.axis('equal') not working for 3D.
Args:
ax: a matplotlib axis, e.g., as output from plt.gca()
Returns:
The input axis
"""
x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()
x_range = abs(x_limits[1] - x_limits[0])
x_middle = mean(x_limits)
y_range = abs(y_limits[1] - y_limits[0])
y_middle = mean(y_limits)
z_range = abs(z_limits[1] - z_limits[0])
z_middle = mean(z_limits)
# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.
plot_radius = 0.5*max([x_range, y_range, z_range])
ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
return ax
def run(self):
try:
st_file = self._args[2]
logger.info(f"Use ST file: {st_file}")
except Exception as e:
logger.error(f"Arguments parcing: {str(e)}")
return 1
try:
status = SavedStatus()
my_reach = Reach(status = status)
my_reach.import_geometry(st_file)
my_reach.compute_guidelines()
ax = plt.figure().add_subplot(projection="3d")
logger.info(my_reach.get_x())
for x, y, z in zip(
my_reach.get_x(),
my_reach.get_y(),
my_reach.get_z()
):
ax.plot(x, y, z, color='r', lw=1.)
for x, y, z in zip(
my_reach.get_guidelines_x(),
my_reach.get_guidelines_y(),
my_reach.get_guidelines_z()
):
ax.plot(x, y, z, color='b', lw=1.)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.tight_layout()
self.set_axes_equal(ax)
plt.show()
return 0
except Exception as e:
logger.error(str(e))
return 1
-68
View File
@@ -1,68 +0,0 @@
# plot_3DST.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 -*-
# a lancer depuis src
import sys
from matplotlib import pyplot as plt
from Model.Geometry.Reach import Reach
from numpy import mean
def set_axes_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca().
'''
x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()
x_range = abs(x_limits[1] - x_limits[0])
x_middle = mean(x_limits)
y_range = abs(y_limits[1] - y_limits[0])
y_middle = mean(y_limits)
z_range = abs(z_limits[1] - z_limits[0])
z_middle = mean(z_limits)
# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.
plot_radius = 0.5*max([x_range, y_range, z_range])
ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
st_file = sys.argv[1]
my_reach = Reach(None)
my_reach.import_geometry(st_file)
my_reach.compute_guidelines()
ax = plt.figure().add_subplot(projection="3d")
for x, y, z in zip(my_reach.get_x(), my_reach.get_y(), my_reach.get_z()):
ax.plot(x, y, z, color='r', lw=1.)
for x, y, z in zip(my_reach.get_guidelines_x(), my_reach.get_guidelines_y(), my_reach.get_guidelines_z()):
ax.plot(x, y, z, color='b', lw=1.)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.tight_layout()
set_axes_equal(ax)
plt.show()
+4 -2
View File
@@ -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
+3 -3
View File
@@ -24,9 +24,9 @@ from Solver.Mage import Mage7, Mage8
_translate = QCoreApplication.translate
solver_long_name = {
"generic": _translate("Solvers", "Generic"),
# "mage7": _translate("Solvers", "Mage version 7"),
"mage8": _translate("Solvers", "Mage version 8"),
"generic": "Generic",
# "mage7": "Mage v7",
"mage8": "Mage v8",
}
solver_type_list = {
+13 -6
View File
@@ -19,14 +19,17 @@
import os
import logging
from View.ASubWindow import ASubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
logger = logging.getLogger()
class AboutWindow(ASubWindow):
class AboutWindow(PamhyrDialog):
_pamhyr_ui = "about"
_pamhyr_name = "About"
def _path_file(self, filename):
return os.path.abspath(
os.path.join(
@@ -35,10 +38,14 @@ class AboutWindow(ASubWindow):
)
)
def __init__(self, title="About", parent=None):
super(AboutWindow, self).__init__(name=title, ui="about", parent=parent)
self.ui.setWindowTitle(title)
def __init__(self, study = None, config = None, parent=None):
super(AboutWindow, self).__init__(
title = _translate("About", "About"),
study = study,
config = config,
options = [],
parent = parent
)
# Version
with open(self._path_file("VERSION"), "r") as f:
+14 -10
View File
@@ -21,27 +21,31 @@ import logging
from datetime import datetime
from tools import timer, trace
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
)
from View.BoundaryCondition.Edit.translate import *
from View.BoundaryCondition.Edit.translate import BCETranslate
_translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(APlot):
def __init__(self, canvas=None, data=None,
mode = "time", toolbar=None):
class Plot(PamhyrPlot):
def __init__(self, mode = "time", data=None,
trad=None, canvas=None, toolbar=None,
parent=None):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
canvas = canvas,
trad = trad,
data = data,
toolbar = toolbar,
parent = parent
)
self._table_headers = self._trad.get_dict("table_headers")
self._mode = mode
def custom_ticks(self):
@@ -109,10 +113,10 @@ class Plot(APlot):
# Plot label
header = self.data.header
self.canvas.axes.set_xlabel(
table_headers[header[0]], color='black', fontsize=10
self._table_headers[header[0]], color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
table_headers[header[1]], color='black', fontsize=10
self._table_headers[header[1]], color='black', fontsize=10
)
self.canvas.axes.autoscale_view(True, True, True)
+3 -138
View File
@@ -23,8 +23,7 @@ from datetime import date, time, datetime, timedelta
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,
@@ -47,131 +46,12 @@ from View.BoundaryCondition.Edit.UndoCommand import (
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand,
)
from View.BoundaryCondition.Edit.translate import *
_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 +69,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 +80,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 +189,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()
+63 -65
View File
@@ -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.PamhyrWindow import PamhyrWindow
from View.Tools.PamhyrWidget import PamhyrWidget
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
from PyQt5.QtGui import (
QKeySequence,
@@ -38,26 +41,28 @@ from PyQt5.QtWidgets import (
QHeaderView, QDoubleSpinBox, QVBoxLayout,
)
from View.Plot.MplCanvas import MplCanvas
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.BoundaryCondition.translate import long_types
from View.BoundaryCondition.Edit.translate import BCETranslate
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
class WD50Sigma(AWidget):
logger = logging.getLogger()
class WD50Sigma(PamhyrWidget):
_pamhyr_ui = "d50sigma"
d50Changed = pyqtSignal(float)
sigmaChanged = pyqtSignal(float)
def __init__(self, parent=None):
super(WD50Sigma, self).__init__(
ui="d50sigma",
parent=parent
)
self.parent = parent
self.spinBox_d50 = self.find(QDoubleSpinBox, "doubleSpinBox_d50")
self.spinBox_sigma = self.find(QDoubleSpinBox, "doubleSpinBox_sigma")
@@ -89,46 +94,39 @@ class WD50Sigma(AWidget):
def valueChangedSigma(self, value):
self.sigmaChanged.emit(value)
class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Edit boundary condition",
data=None, study=None, parent=None):
self._data = data
self._study = study
self._title = title
class EditBoundaryConditionWindow(PamhyrWindow):
_pamhyr_ui = "EditBoundaryConditions"
_pamhyr_name = "Edit Boundary Conditions"
self.compute_title()
def __init__(self, data=None, study=None, config=None, parent=None):
self._data = data
trad = BCETranslate()
self._long_types = trad.get_dict("long_types")
name = self._pamhyr_name
if self._data is not None:
node_name = (self._data.node.name if self._data.node is not None
else _translate("BoundaryCondition", "Not associate"))
name = (
_translate("Edit boundary condition", self._pamhyr_name) +
f" - {study.name} " +
f" - {self._data.name} ({self._data.id}) " +
f"({self._long_types[self._data.bctype]} - {node_name})"
)
super(EditBoundaryConditionWindow, self).__init__(
name=self._title, ui="EditBoundaryConditions", parent=parent
title = name,
study = study,
config = config,
trad = trad,
parent = parent
)
self.ui.setWindowTitle(self._title)
self.setup_sc()
self.setup_table()
self.setup_plot()
self.setup_data()
self.setup_connections()
def compute_title(self):
if self._data is not None:
node_name = (self._data.node.name if self._data.node is not None
else _translate("BoundaryCondition", "Not associate"))
self._title = (
_translate("Edit boundary condition", self._title) +
f" - {self._study.name} " +
f" - {self._data.name} ({self._data.id}) " +
f"({long_types[self._data.bctype]} - {node_name})"
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_data(self):
self._is_solid = self._data.bctype == "SL"
@@ -141,23 +139,29 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self._d50sigma.set_sigma(self._data.sigma)
def setup_table(self):
table = self.find(QTableView, "tableView")
self._table = TableModel(
headers = {}
table_headers = self._trad.get_dict("table_headers")
for h in self._data.header:
headers[h] = table_headers[h]
self._delegate_time = PamhyrExTimeDelegate(
data = self._data,
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 +171,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)
@@ -177,21 +181,16 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
canvas = self.canvas,
data = self._data,
mode = self._study.time_system,
trad = self._trad,
toolbar = self.toolbar,
)
self.plot.draw()
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self._table.dataChanged.connect(self.update)
if self._is_solid:
@@ -240,7 +239,6 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._data) == 0 or len(rows) == 0:
@@ -273,7 +271,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.plot.update()
def copy(self):
def _copy(self):
rows = self.index_selected_rows()
table = []
@@ -285,7 +283,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.copyTableIntoClipboard(table)
def paste(self):
def _paste(self):
header, data = self.parseClipboardTable()
if len(data) == 0:
@@ -299,12 +297,12 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self._table.paste(row, header, data)
self.plot.update()
def undo(self):
def _undo(self):
self._table.undo()
self.plot.update()
self.widget_update()
def redo(self):
def _redo(self):
self._table.redo()
self.plot.update()
self.widget_update()
+17 -9
View File
@@ -18,14 +18,22 @@
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from View.BoundaryCondition.translate import BCTranslate
_translate = QCoreApplication.translate
table_headers = {
"x": _translate("BoundaryCondition", "X"),
"y": _translate("BoundaryCondition", "Y"),
"time": _translate("BoundaryCondition", "Time"),
"date": _translate("BoundaryCondition", "Date"),
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"),
"z": _translate("BoundaryCondition", "Z (m)"),
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
}
class BCETranslate(BCTranslate):
def __init__(self):
super(BCETranslate, self).__init__()
self._sub_dict["table_headers"] = {
"x": _translate("BoundaryCondition", "X"),
"y": _translate("BoundaryCondition", "Y"),
"time": _translate("BoundaryCondition", "Time"),
"date": _translate("BoundaryCondition", "Date"),
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"),
"z": _translate("BoundaryCondition", "Z (m)"),
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
}
+34 -39
View File
@@ -39,24 +39,31 @@ 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,
MoveCommand, PasteCommand, DuplicateCommand,
)
from View.BoundaryCondition.translate import *
from View.BoundaryCondition.translate import BC_types
logger = logging.getLogger()
_translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, mode="type", tab="", parent=None):
def __init__(self, data=None, mode="type", tab="", trad=None, parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._mode = mode
self._tab = tab
self._trad = trad
self._long_types = {}
if self._trad is not None:
self._long_types = self._trad.get_dict("long_types")
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
@@ -64,7 +71,7 @@ class ComboBoxDelegate(QItemDelegate):
if self._mode == "type":
lst = list(
map(
lambda k: long_types[k],
lambda k: self._long_types[k],
filter(
lambda k: self._tab in BC_types[k].compatibility(),
BC_types.keys()
@@ -104,26 +111,20 @@ 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
class TableModel(PamhyrTableModel):
def __init__(self, **kwargs):
super(TableModel, self).__init__(**kwargs)
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
self._long_types = {}
if self._trad is not None:
self._long_types = self._trad.get_dict("long_types")
return options
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 +134,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
return long_types[t]
t = self._lst.get(self._tab, row).bctype
return self._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 +157,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)
key = next(k for k, v in self._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 +185,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
AddCommand(
self._bcs, self._tab,row
self._lst, self._tab,row
)
)
@@ -202,7 +197,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
DelCommand(
self._bcs, self._tab,rows
self._lst, self._tab, rows
)
)
@@ -214,7 +209,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
SortCommand(
self._bcs, self._tab,False
self._lst, self._tab, False
)
)
@@ -231,7 +226,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push(
MoveCommand(
self._bcs, self._tab,"up", row
self._lst, self._tab, "up", row
)
)
@@ -239,7 +234,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 +243,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push(
MoveCommand(
self._bcs, self._tab,"down", row
self._lst, self._tab, "down", row
)
)
+33 -40
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -56,71 +55,70 @@ from View.BoundaryCondition.Table import (
)
from View.Network.GraphWidget import GraphWidget
from View.BoundaryCondition.translate import *
from View.BoundaryCondition.translate import BCTranslate
from View.BoundaryCondition.Edit.Window import EditBoundaryConditionWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Boundary conditions", study=None, parent=None):
self._title = title + " - " + study.name
class BoundaryConditionWindow(PamhyrWindow):
_pamhyr_ui = "BoundaryConditions"
_pamhyr_name = "Boundary conditions"
def __init__(self, study=None, config=None, parent=None):
name = self._pamhyr_name + " - " + study.name
super(BoundaryConditionWindow, self).__init__(
name=title, ui="BoundaryConditions", parent=parent
title = name,
study = study,
config = config,
trad = BCTranslate(),
parent=parent
)
self._study = study
self._bcs = self._study.river.boundary_condition
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
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(
trad = self._trad,
data = self._study.river,
mode = "type",
tab = t,
parent=self
)
self._delegate_node = ComboBoxDelegate(
trad = self._trad,
data = self._study.river,
mode = "node",
tab = t,
parent=self
)
table.setItemDelegateForColumn(
1, self._delegate_type
table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel(
table_view = table,
table_headers = self._trad.get_dict("table_headers"),
editable_headers = ["name", "type", "node"],
delegates = {
"type": self._delegate_type,
"node": self._delegate_node,
},
trad = self._trad,
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)
@@ -141,11 +139,6 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.find(QAction, "action_edit").triggered.connect(self.edit)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
def current_tab(self):
return self.find(QTabWidget, "tabWidget")\
.currentWidget()\
@@ -202,17 +195,17 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
row = self.index_selected_row()
self._table[tab].move_down(row)
def copy(self):
def _copy(self):
logger.info("TODO: copy")
def paste(self):
def _paste(self):
logger.info("TODO: paste")
def undo(self):
def _undo(self):
tab = self.current_tab()
self._table[tab].undo()
def redo(self):
def _redo(self):
tab = self.current_tab()
self._table[tab].redo()
+21 -15
View File
@@ -18,6 +18,8 @@
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from Model.BoundaryCondition.BoundaryConditionTypes import (
NotDefined, PonctualContribution,
TimeOverZ, TimeOverDischarge, ZOverDischarge,
@@ -26,21 +28,6 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
_translate = QCoreApplication.translate
long_types = {
"ND": _translate("BoundaryCondition", "Not defined"),
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
"TZ": _translate("BoundaryCondition", "TimeZ"),
"TD": _translate("BoundaryCondition", "TimeDischarge"),
"ZD": _translate("BoundaryCondition", "ZDischarge"),
"SL": _translate("BoundaryCondition", "Solid"),
}
table_headers = {
"name": _translate("BoundaryCondition", "Name"),
"type": _translate("BoundaryCondition", "Type"),
"node": _translate("BoundaryCondition", "Node")
}
BC_types = {
"ND": NotDefined,
"PC": PonctualContribution,
@@ -49,3 +36,22 @@ BC_types = {
"ZD": ZOverDischarge,
"SL": Solid,
}
class BCTranslate(PamhyrTranslate):
def __init__(self):
super(BCTranslate, self).__init__()
self._sub_dict["long_types"] = {
"ND": _translate("BoundaryCondition", "Not defined"),
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
"TZ": _translate("BoundaryCondition", "TimeZ"),
"TD": _translate("BoundaryCondition", "TimeDischarge"),
"ZD": _translate("BoundaryCondition", "ZDischarge"),
"SL": _translate("BoundaryCondition", "Solid"),
}
self._sub_dict["table_headers"] = {
"name": _translate("BoundaryCondition", "Name"),
"type": _translate("BoundaryCondition", "Type"),
"node": _translate("BoundaryCondition", "Node")
}
+3 -25
View File
@@ -35,25 +35,12 @@ from PyQt5.QtWidgets import (
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
_translate = QCoreApplication.translate
class TableModel(QAbstractTableModel):
def __init__(self, data=None):
super(QAbstractTableModel, self).__init__()
self._headers = ["name", "status"]
self._data = data
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
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):
row = index.row()
column = index.column()
@@ -81,12 +68,3 @@ class TableModel(QAbstractTableModel):
return self._data[row].summary
return QVariant()
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return self._headers[section]
return QVariant()
def update(self):
self.layoutChanged.emit()
+32
View File
@@ -0,0 +1,32 @@
# 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
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate
class CheckListTranslate(PamhyrTranslate):
def __init__(self):
super(CheckListTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"name": _translate("CheckList", "Name"),
"status": _translate("CheckList", "Status"),
}
+18 -20
View File
@@ -18,8 +18,7 @@
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -41,33 +40,36 @@ from PyQt5.QtWidgets import (
from View.CheckList.Table import TableModel
from View.CheckList.Worker import Worker
from View.CheckList.Translate import CheckListTranslate
_translate = QCoreApplication.translate
class CheckListWindow(ASubMainWindow, ListedSubWindow):
class CheckListWindow(PamhyrWindow):
_pamhyr_ui = "CheckList"
_pamhyr_name = "Check list"
signalStatus = pyqtSignal(str)
def __init__(self, title="Check list",
autorun:bool=True,
def __init__(self, autorun:bool=True,
study=None, config=None,
solver=None, parent=None):
self._title = title + " - " + study.name
self._autorun = autorun
self._study = study
self._config = config
self._solver = solver
self._parent = parent
name = self._pamhyr_name + " - " + study.name
super(CheckListWindow, self).__init__(
name=self._title, ui="CheckList", parent=parent
title = name,
study = study,
config = config,
trad = CheckListTranslate(),
options = [],
parent = parent
)
self.ui.setWindowTitle(self._title)
self._checker_list = (
self._study.checkers() + self._solver.checkers()
self._study.checkers() +
self._solver.checkers()
)
self.setup_table()
@@ -78,16 +80,12 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
table_view = table,
table_headers = self._trad.get_dict("table_headers"),
data = self._checker_list,
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_progress_bar(self):
self._progress = self.find(QProgressBar, f"progressBar")
self._p = 0 # Progress current step
+2 -1
View File
@@ -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")
+18 -8
View File
@@ -16,7 +16,7 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from Solver.Solvers import solver_type_list
from Solver.GenericSolver import GenericSolver
@@ -24,12 +24,22 @@ from PyQt5.QtWidgets import (
QPushButton,
)
class ConfigureSolverWindow(ASubWindow):
def __init__(self, data=None, title="Configuration : Add Solver", parent=None):
class ConfigureSolverWindow(PamhyrDialog):
_pamhyr_ui = "ConfigureAddSolverDialog"
_pamhyr_name = "Add/Edit Solver"
def __init__(self, data=None, config=None, parent=None):
if data is not None:
name = f"Edit Solver - {data.name}"
else:
name = "Add a new Solver"
super(ConfigureSolverWindow, self).__init__(
name=title, ui="ConfigureAddSolverDialog", parent=parent
title = name,
config = config,
options = [],
parent=parent
)
self.ui.setWindowTitle(title)
# Combo box item
for solver in solver_type_list:
@@ -37,10 +47,10 @@ class ConfigureSolverWindow(ASubWindow):
# Data to return
self.data = data
if not self.data is None:
if self.data is not None:
self.copy_data()
self.connect()
self.setup_connection()
def copy_data(self):
self.set_combobox_text("comboBox_solver", self.data.type)
@@ -53,7 +63,7 @@ class ConfigureSolverWindow(ASubWindow):
self.set_line_edit_text("lineEdit_output", self.data._path_output)
self.set_line_edit_text("lineEdit_output_cmd", self.data._cmd_output)
def connect(self):
def setup_connection(self):
# File button
buttons = {
"pushButton_input": (lambda: self.file_dialog(
+58 -59
View File
@@ -21,8 +21,7 @@ import logging
from copy import deepcopy
from config import Config
from View.ASubWindow import ASubWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from View.Stricklers.Table import TableModel
from View.Stricklers.translate import *
@@ -95,50 +94,42 @@ class SolverTableModel(QAbstractTableModel):
self.layoutChanged.emit()
class ConfigureWindow(ASubWindow, ListedSubWindow):
def __init__(self, conf=None, title="Configure", parent=None):
class ConfigureWindow(PamhyrDialog):
_pamhyr_ui = "ConfigureDialog"
_pamhyr_name = "Configure"
def __init__(self, config=None, parent=None):
if config is None:
config = Config()
super(ConfigureWindow, self).__init__(
name=title, ui="ConfigureDialog", parent=parent
title = self._pamhyr_name,
config = config,
options = [],
parent=parent
)
self.ui.setWindowTitle(title)
if conf is None:
self.conf = Config()
else:
self.conf = conf
self.setup_custom_sc()
self.setup_solver()
self.setup_stricklers()
self.setup_data()
self.setup_connection()
self.setup_sc()
# Solver
def setup_solver(self):
table = self.find(QTableView, "tableView_solver")
self.solver_table_model = SolverTableModel(
headers = ["name", "type", "description"],
rows = conf.solvers
rows = self._config.solvers
)
table.setModel(self.solver_table_model)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
table.resizeColumnsToContents()
# Meshing_Tool
self.set_line_edit_text("lineEdit_meshing_tool", self.conf.meshing_tool)
# Const
self.set_line_edit_text("lineEdit_segment", str(self.conf.segment))
self.set_line_edit_text("lineEdit_max_listing", str(self.conf.max_listing))
# Backup
self.set_check_box("checkBox_backup", self.conf.backup_enable)
self.set_line_edit_text("lineEdit_backup_path", self.conf.backup_path)
self.set_time_edit("timeEdit_backup_frequence", self.conf.backup_frequence)
self.set_spin_box("spinBox_backup_max", self.conf.backup_max)
self.find(QTableView, "tableView_solver").resizeColumnsToContents()
self.connect()
# Stricklers
def setup_stricklers(self):
table = self.find(QTableView, f"tableView_stricklers")
self._stricklers = deepcopy(self.conf.stricklers)
self._stricklers = deepcopy(self._config.stricklers)
self._stricklers_table = TableModel(
data = self._stricklers,
undo = self._undo_stack,
@@ -148,26 +139,35 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_data(self):
# Meshing_Tool
self.set_line_edit_text("lineEdit_meshing_tool", self._config.meshing_tool)
# Const
self.set_line_edit_text("lineEdit_segment", str(self._config.segment))
self.set_line_edit_text("lineEdit_max_listing", str(self._config.max_listing))
# Backup
self.set_check_box("checkBox_backup", self._config.backup_enable)
self.set_line_edit_text("lineEdit_backup_path", self._config.backup_path)
self.set_time_edit("timeEdit_backup_frequence", self._config.backup_frequence)
self.set_spin_box("spinBox_backup_max", self._config.backup_max)
# Editor
self.set_line_edit_text("lineEdit_editor_cmd", str(self.conf.editor))
self.set_line_edit_text("lineEdit_editor_cmd", str(self._config.editor))
# Language
languages = Config.languages()
for lang in languages:
self.combobox_add_item("comboBox_language", lang)
if self.conf.lang == languages[lang]:
if self._config.lang == languages[lang]:
self.set_combobox_text("comboBox_language", lang)
def setup_sc(self):
self._undo_stack = QUndoStack()
def setup_custom_sc(self):
self._debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
self._debug_sc.activated.connect(self.set_debug)
self.debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def connect(self):
def setup_connection(self):
buttons = {
# Solvers
"pushButton_solver_add": self.add_solver,
@@ -195,33 +195,31 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
for button in buttons:
self.find(QPushButton, button).clicked.connect(buttons[button])
self.debug_sc.activated.connect(self.set_debug)
def accept(self):
# Solvers
self.conf.solvers = self.solver_table_model.rows.copy()
self._config.solvers = self.solver_table_model.rows.copy()
# Meshing_Tool
self.conf.meshing_tool = self.get_line_edit_text("lineEdit_meshing_tool")
self._config.meshing_tool = self.get_line_edit_text("lineEdit_meshing_tool")
# Const
self.conf.segment = self.get_line_edit_text("lineEdit_segment")
self.conf.max_listing = self.get_line_edit_text("lineEdit_max_listing")
self._config.segment = self.get_line_edit_text("lineEdit_segment")
self._config.max_listing = self.get_line_edit_text("lineEdit_max_listing")
# Backup
self.conf.backup_enable = self.get_check_box("checkBox_backup")
self.conf.backup_path = self.get_line_edit_text("lineEdit_backup_path")
self.conf.backup_frequence = self.get_time_edit("timeEdit_backup_frequence")
self.conf.backup_max = self.get_spin_box("spinBox_backup_max")
self._config.backup_enable = self.get_check_box("checkBox_backup")
self._config.backup_path = self.get_line_edit_text("lineEdit_backup_path")
self._config.backup_frequence = self.get_time_edit("timeEdit_backup_frequence")
self._config.backup_max = self.get_spin_box("spinBox_backup_max")
# Stricklers
self.conf.stricklers = deepcopy(self._stricklers)
self._config.stricklers = deepcopy(self._stricklers)
# Editor
self.conf.editor = self.get_line_edit_text("lineEdit_editor_cmd")
self._config.editor = self.get_line_edit_text("lineEdit_editor_cmd")
# Language
self.conf.lang = Config.languages()[self.get_combobox_text("comboBox_language")]
self._config.lang = Config.languages()[self.get_combobox_text("comboBox_language")]
self.end()
@@ -230,13 +228,13 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
self.end()
def end(self):
self.conf.save()
self._config.save()
self.close()
# Debug
def set_debug(self):
self.conf.debug = not self.conf.debug
logger.info(f"Debug mode set : {self.conf.debug}")
self._config.debug = not self._config.debug
logger.info(f"Debug mode set : {self._config.debug}")
self.parent.setup_debug_mode()
# Solvers
@@ -246,6 +244,7 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
for index in indexes:
self.edit_solver = ConfigureSolverWindow(
data=self.solver_table_model.rows[index.row()],
config=self._config,
parent=self
)
if self.edit_solver.exec_():
+15 -13
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -45,20 +44,19 @@ logger = logging.getLogger()
_translate = QCoreApplication.translate
class ReplWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Debug REPL",
study=None, config=None,
class ReplWindow(PamhyrWindow):
_pamhyr_ui = "DebugRepl"
_pamhyr_name = "Debug REPL"
def __init__(self, study=None, config=None,
solver=None, parent=None):
self._title = title
self._study = study
self._config = config
self._parent = parent
super(ReplWindow, self).__init__(
name=self._title, ui="DebugRepl", parent=parent
title = self._pamhyr_name,
study = study,
config = config,
options = [],
parent = parent
)
self.ui.setWindowTitle(self._title)
self.__debug_exec_result__ = None
self._history = []
@@ -92,7 +90,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 +102,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))
+106
View File
@@ -0,0 +1,106 @@
# Window.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 os, sys
import logging
import subprocess
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
logger = logging.getLogger()
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QAction
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class DocWindow(PamhyrWindow):
_pamhyr_ui = "WebView"
_pamhyr_name = "Doc"
@classmethod
def _path_file(cls, filename):
if ".py" in sys.argv[0]:
return os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"..", "..", "..", "doc", filename
)
)
return os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"..", "..", "..", "..", "doc", filename
)
)
def __init__(self, filename=None,
study=None, config=None,
parent=None):
super(DocWindow, self).__init__(
title = self._pamhyr_name,
study = study,
config = config,
options = [],
parent = parent
)
self._path = self._path_file(filename)
self.setup_web_engine()
self.setup_url(self._path)
self.setup_connection()
def setup_connection(self):
self.findChild(QAction, "action_back").triggered.connect(self.back)
self.findChild(QAction, "action_forward").triggered.connect(self.forward)
self.findChild(QAction, "action_firefox").triggered.connect(self.open_in_firefox)
def setup_web_engine(self):
vl = self.find(QVBoxLayout, "verticalLayout")
self._web_view = QWebEngineView()
settings = self._web_view.settings()
settings.setAttribute(QWebEngineSettings.PluginsEnabled, True)
settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
settings.setAttribute(QWebEngineSettings.PdfViewerEnabled, True)
vl.addWidget(self._web_view)
def setup_url(self, filename):
logger.info(f"Open documentation : {filename}")
self._web_view.setUrl(QUrl(f"file://{self._path_file(filename)}"))
def back(self):
self._web_view.back()
def forward(self):
self._web_view.forward()
def open_in_firefox(self):
_ = subprocess.Popen(
f"firefox {self._path}",
shell = True
)
+10 -5
View File
@@ -16,9 +16,14 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
class DummyWindow(ASubWindow):
def __init__(self, title="Dummy", parent=None):
super(DummyWindow, self).__init__(name=title, ui="dummy", parent=parent)
self.ui.setWindowTitle(title)
class DummyWindow(PamhyrWindow):
__ui = "Dummy"
__name = "Pamhyr Dummy Window"
def __init__(self, parent=None):
super(DummyWindow, self).__init__(
title = self.__name,
parent = parent,
)
+2 -9
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*-
from tools import timer, flatten
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
@@ -25,14 +25,7 @@ from PyQt5.QtCore import (
_translate = QCoreApplication.translate
class PlotStricklers(APlot):
def __init__(self, canvas=None, data=None, toolbar=None):
super(PlotStricklers, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
class PlotStricklers(PamhyrPlot):
def draw_frictions(self, frictions, color="r"):
lst = frictions
lst.sort(key = lambda s: s.begin_kp)
+22 -42
View File
@@ -41,6 +41,8 @@ from View.Frictions.UndoCommand import (
MoveCommand, PasteCommand, DuplicateCommand,
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.Frictions.translate import *
logger = logging.getLogger()
@@ -93,26 +95,10 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel):
def __init__(self, data=None, study=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._data = data
self._study = study
self._undo = undo
self._frictions = self._data.frictions
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._frictions)
def columnCount(self, parent):
return len(self._headers)
class TableModel(PamhyrTableModel):
def _setup_lst(self):
self._lst = self._data.frictions
self._study = self._opt_data
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
@@ -122,30 +108,24 @@ class TableModel(QAbstractTableModel):
column = index.column()
if self._headers[column] == "name":
return self._frictions.get(row).name
return self._lst.get(row).name
elif self._headers[column] == "begin_kp":
return self._frictions.get(row).begin_kp
return self._lst.get(row).begin_kp
elif self._headers[column] == "end_kp":
return self._frictions.get(row).end_kp
return self._lst.get(row).end_kp
elif self._headers[column] == "begin_strickler":
value = self._frictions.get(row).begin_strickler
value = self._lst.get(row).begin_strickler
if value == None:
return _translate("Frictions", "Not defined")
return str(value)
elif self._headers[column] == "end_strickler":
value = self._frictions.get(row).end_strickler
value = self._lst.get(row).end_strickler
if value == None:
return _translate("Frictions", "Not defined")
return str(value)
return QVariant()
def headerData(self, friction, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[friction]]
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
@@ -157,31 +137,31 @@ class TableModel(QAbstractTableModel):
if self._headers[column] == "name":
self._undo.push(
SetNameCommand(
self._frictions, row, value
self._lst, row, value
)
)
elif self._headers[column] == "begin_kp":
self._undo.push(
SetBeginCommand(
self._frictions, row, value
self._lst, row, value
)
)
elif self._headers[column] == "end_kp":
self._undo.push(
SetEndCommand(
self._frictions, row, value
self._lst, row, value
)
)
elif self._headers[column] == "begin_strickler":
self._undo.push(
SetBeginStricklerCommand(
self._frictions, row, self._study.river.strickler(value)
self._lst, row, self._study.river.strickler(value)
)
)
elif self._headers[column] == "end_strickler":
self._undo.push(
SetEndStricklerCommand(
self._frictions, row, self._study.river.strickler(value)
self._lst, row, self._study.river.strickler(value)
)
)
except Exception as e:
@@ -196,7 +176,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
AddCommand(
self._frictions, row, self._data
self._lst, row, self._data
)
)
@@ -208,7 +188,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
DelCommand(
self._frictions, rows
self._lst, rows
)
)
@@ -220,7 +200,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
SortCommand(
self._frictions, False
self._lst, False
)
)
@@ -237,7 +217,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push(
MoveCommand(
self._frictions, "up", row
self._lst, "up", row
)
)
@@ -245,7 +225,7 @@ class TableModel(QAbstractTableModel):
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._frictions):
if row > len(self._lst):
return
target = row
@@ -254,7 +234,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push(
MoveCommand(
self._frictions, "down", row
self._lst, "down", row
)
)
+45 -54
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -48,62 +47,49 @@ from View.Frictions.Table import (
TableModel, ComboBoxDelegate
)
from View.Plot.MplCanvas import MplCanvas
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Geometry.PlotKPZ import PlotKPZ
from View.Frictions.PlotStricklers import PlotStricklers
from View.Frictions.translate import *
from View.Frictions.translate import FrictionsTranslate
from View.Stricklers.Window import StricklersWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class FrictionsWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Frictions", study=None, parent=None):
self._study = study
self._reach = self._study.river.current_reach()
class FrictionsWindow(PamhyrWindow):
_pamhyr_ui = "Frictions"
_pamhyr_name = "Edit frictions"
def __init__(self, reach=None, study=None, config=None, parent=None):
if reach is not None:
self._reach = reach
else:
self._reach = study.river.current_reach()
self._frictions = self._reach.frictions
self.setup_title(title)
super(FrictionsWindow, self).__init__(
name=title, ui="Frictions", parent=parent
name = (
self._pamhyr_name + " - "
+ study.name + " - "
+ self._reach.name
)
super(FrictionsWindow, self).__init__(
title = name,
study = study,
config = config,
trad = FrictionsTranslate(),
parent = parent
)
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_title(self, title):
self._title = (
title + " - "
+ self._study.name + " - "
+ self._reach.name
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
self._table = {}
table = self.find(QTableView, f"tableView")
self._table = TableModel(
data = self._reach,
study = self._study,
undo = self._undo_stack,
)
table.setModel(self._table)
self._delegate_stricklers = ComboBoxDelegate(
data = self._reach,
study = self._study,
@@ -111,13 +97,23 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
parent=self
)
table.setItemDelegateForColumn(
3, self._delegate_stricklers
table = self.find(QTableView, f"tableView")
self._table = TableModel(
table_view = table,
table_headers = self._trad.get_dict("table_headers"),
editable_headers = [
"name", "begin_kp", "end_kp",
"begin_strickler", "end_strickler"
],
delegates = {
"begin_strickler": self._delegate_stricklers,
"end_strickler": self._delegate_stricklers,
},
data = self._reach,
undo = self._undo_stack,
opt_data = self._study
)
table.setItemDelegateForColumn(
4, self._delegate_stricklers
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
@@ -155,11 +151,6 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
self.find(QAction, "action_sort").triggered.connect(self.sort)
self.find(QAction, "action_edit_stricklers").triggered.connect(self.edit_stricklers)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
table = self.find(QTableView, f"tableView")
table.selectionModel()\
.selectionChanged\
@@ -233,16 +224,16 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
row = self.index_selected_row()
self._table.move_down(row)
def copy(self):
def _copy(self):
logger.info("TODO: copy")
def paste(self):
def _paste(self):
logger.info("TODO: paste")
def undo(self):
def _undo(self):
self._table.undo()
def redo(self):
def _redo(self):
self._table.redo()
def edit_stricklers(self):
+14 -8
View File
@@ -18,13 +18,19 @@
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate
table_headers = {
"name": _translate("Frictions", "Name"),
# "edge": _translate("Frictions", "Reach"),
"begin_kp": _translate("Frictions", "Begin kp (m)"),
"end_kp": _translate("Frictions", "End kp (m)"),
"begin_strickler": _translate("Frictions", "Begin strickler"),
"end_strickler": _translate("Frictions", "End strickler"),
}
class FrictionsTranslate(PamhyrTranslate):
def __init__(self):
super(FrictionsTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"name": _translate("Frictions", "Name"),
# "edge": _translate("Frictions", "Reach"),
"begin_kp": _translate("Frictions", "Begin kp (m)"),
"end_kp": _translate("Frictions", "End kp (m)"),
"begin_strickler": _translate("Frictions", "Begin strickler"),
"end_strickler": _translate("Frictions", "End strickler"),
}
+9 -6
View File
@@ -19,7 +19,7 @@
import logging
from tools import timer
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
@@ -29,12 +29,15 @@ _translate = QCoreApplication.translate
logger = logging.getLogger()
class PlotAC(APlot):
def __init__(self, canvas=None, data=None, toolbar=None, plot_xy=None):
class PlotAC(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
plot_xy=None, parent=None):
super(PlotAC, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
canvas = canvas,
trad = trad,
data = data,
toolbar = toolbar,
parent = parent
)
self.plot_xy = plot_xy
+9 -7
View File
@@ -19,7 +19,7 @@
import logging
from tools import timer
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
@@ -29,13 +29,15 @@ _translate = QCoreApplication.translate
logger = logging.getLogger()
class PlotKPZ(APlot):
def __init__(self, canvas=None, data=None, toolbar=None,
display_current=True):
class PlotKPZ(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
display_current=True, parent=None):
super(PlotKPZ, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
canvas = canvas,
trad = trad,
data = data,
toolbar = toolbar,
parent = parent
)
self.display_current = display_current
+11 -8
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*-
from tools import timer, trace
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
@@ -25,12 +25,15 @@ from PyQt5.QtCore import (
_translate = QCoreApplication.translate
class PlotXY(APlot):
def __init__(self, canvas=None, data=None, toolbar=None, display_current=True):
class PlotXY(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
display_current=True, parent=None):
super(PlotXY, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
canvas = canvas,
trad = trad,
data = data,
toolbar = toolbar,
parent = parent
)
self.display_current = display_current
@@ -60,11 +63,11 @@ class PlotXY(APlot):
# Axes
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"),
_translate("Geometry", "X (m)"),
color='green', fontsize=12
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Y (m)"),
_translate("Geometry", "Y (m)"),
color='green', fontsize=12
)
self.canvas.axes.axis("equal")
+10 -7
View File
@@ -19,8 +19,8 @@
import logging
from tools import timer, trace
from View.Plot.APlot import APlot
from View.Plot.mpl_canvas_onpick_event import OnpickEvent
from View.Tools.PamhyrPlot import PamhyrPlot
from View.Tools.Plot.OnPickEvent import OnpickEvent
from PyQt5.QtCore import (
QCoreApplication
@@ -30,12 +30,15 @@ _translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(APlot):
def __init__(self, canvas=None, data=None, toolbar=None, table=None):
class Plot(PamhyrPlot):
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
table=None, parent=None):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
canvas = canvas,
trad = trad,
data = data,
toolbar = toolbar,
parent = parent
)
self._table = table
+46 -104
View File
@@ -29,10 +29,11 @@ from PyQt5.QtWidgets import (
QMessageBox, QStyledItemDelegate, QLineEdit
)
from PyQt5.QtCore import (
QModelIndex, Qt, QAbstractTableModel,
QVariant, QCoreApplication
QModelIndex, Qt, QVariant, QCoreApplication
)
from View.Tools.PamhyrTable import PamhyrTableModel
from Model.Geometry.PointXYZ import PointXYZ
from Model.Geometry.ProfileXYZ import ProfileXYZ
@@ -43,43 +44,22 @@ logger = logging.getLogger()
_translate = QCoreApplication.translate
class TableEditableModel(QAbstractTableModel):
def __init__(self, profile: ProfileXYZ, header=None, undo=None):
QAbstractTableModel.__init__(self)
self._undo_stack = undo
self._profile = profile
if header is None:
self._header = [
"X (m)", "Y (m)", "Z (m)",
_translate("MainWindowProfile", "Nom"),
_translate("MainWindowProfile", "Abs en travers (m)")
]
else:
self._header = header
def rowCount(self, parent=QModelIndex()):
return self._profile.number_points
def columnCount(self, parent=QModelIndex()):
return len(self._header)
class GeometryProfileTableModel(PamhyrTableModel):
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole:
value = ""
if index.column() == 0:
value = self._profile.point(index.row()).x
value = self._data.point(index.row()).x
elif index.column() == 1:
value = self._profile.point(index.row()).y
value = self._data.point(index.row()).y
elif index.column() == 2:
value = self._profile.point(index.row()).z
value = self._data.point(index.row()).z
elif index.column() == 3:
value = self._profile.point(index.row()).name
value = self._data.point(index.row()).name
elif index.column() == 4:
station = self._profile.get_station()
station = self._data.get_station()
if station is None:
return "-"
else:
@@ -95,25 +75,25 @@ class TableEditableModel(QAbstractTableModel):
return Qt.AlignHCenter | Qt.AlignVCenter
if index.column() == 2:
value = self._profile.point(index.row()).z
value = self._data.point(index.row()).z
if role == Qt.ForegroundRole:
if value == self._profile.z_min():
if value == self._data.z_min():
return QColor("red")
elif value == self._profile.z_max():
elif value == self._data.z_max():
return QColor("blue")
if role == Qt.ToolTipRole:
if value == self._profile.z_min():
if value == self._data.z_min():
return _translate("MainWindowProfile",
"La cote du fond",
"Z minimale")
elif value == self._profile.z_max():
elif value == self._data.z_max():
return _translate("MainWindowProfile",
"La cote maximale",
"Z maximale")
if index.column() == 3:
value = self._profile.point(index.row()).name
value = self._data.point(index.row()).name
if value.strip().upper() in ["RG", "RD"]:
if role == Qt.FontRole:
@@ -132,21 +112,6 @@ class TableEditableModel(QAbstractTableModel):
return QVariant()
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._header[section]
elif orientation == Qt.Vertical and role == Qt.DisplayRole:
return str(section + 1)
if role == Qt.ToolTipRole and section == 4:
return _translate(
"MainWindowProfile",
"Abscisse en travers calculée en projétant les points"
" \nsur le plan défini par les deux points nommés extrêmes "
)
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
row = index.row()
column = index.column()
@@ -154,34 +119,34 @@ class TableEditableModel(QAbstractTableModel):
if role == Qt.EditRole:
try:
if column == 0:
self._undo_stack.push(
self._undo.push(
SetXCommand(
self._profile, row,
self._profile.point(row).x,
self._data, row,
self._data.point(row).x,
value
)
)
elif column == 1:
self._undo_stack.push(
self._undo.push(
SetYCommand(
self._profile, row,
self._profile.point(row).y,
self._data, row,
self._data.point(row).y,
value
)
)
elif column == 2:
self._undo_stack.push(
self._undo.push(
SetZCommand(
self._profile, row,
self._profile.point(row).z,
self._data, row,
self._data.point(row).z,
value
)
)
elif column == 3:
self._undo_stack.push(
self._undo.push(
SetNameCommand(
self._profile, row,
self._profile.point(row).name,
self._data, row,
self._data.point(row).name,
value
)
)
@@ -213,9 +178,9 @@ class TableEditableModel(QAbstractTableModel):
def insert_row(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo_stack.push(
self._undo.push(
AddCommand(
self._profile, row
self._data, row
)
)
@@ -225,9 +190,9 @@ class TableEditableModel(QAbstractTableModel):
def remove_rows(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo_stack.push(
self._undo.push(
DelCommand(
self._profile, rows
self._data, rows
)
)
@@ -239,15 +204,15 @@ class TableEditableModel(QAbstractTableModel):
reverse = (order != Qt.AscendingOrder)
self._undo_stack.push(
self._undo.push(
SortCommand(
self._profile, column, reverse
self._data, column, reverse
)
)
self.layoutChanged.emit()
def move_row_up(self, row, parent=QModelIndex()):
def move_up(self, row, parent=QModelIndex()):
if row <= 0:
return
@@ -255,26 +220,26 @@ class TableEditableModel(QAbstractTableModel):
self.beginMoveRows(parent, row - 1, row - 1, parent, target)
self._undo_stack.push(
self._undo.push(
MoveCommand(
self._profile, "up", row
self._data, "up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_row_down(self, row_to_move, parent=QModelIndex()):
if row > self._profile.number_points:
def move_down(self, row_to_move, parent=QModelIndex()):
if row > self._data.number_points:
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push(
self._undo.push(
MoveCommand(
self._profile, "down", row
self._data, "down", row
)
)
@@ -282,7 +247,7 @@ class TableEditableModel(QAbstractTableModel):
self.layoutChanged.emit()
def paste(self, row, header, data):
if row > self._profile.number_points:
if row > self._data.number_points:
return
if len(data) == 0:
@@ -290,12 +255,12 @@ class TableEditableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit()
self._undo_stack.push(
self._undo.push(
PasteCommand(
self._profile, row,
self._data, row,
list(
map(
lambda d: self._profile.point_from_data(header, d),
lambda d: self._data.point_from_data(header, d),
data
)
)
@@ -306,32 +271,9 @@ class TableEditableModel(QAbstractTableModel):
self.layoutChanged.emit()
def undo(self):
self._undo_stack.undo()
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo_stack.redo()
self._undo.redo()
self.layoutChanged.emit()
class Delegate(QStyledItemDelegate):
def __init__(self, parent=None, setModelDataEvent=None):
super(Delegate, self).__init__(parent)
self.setModelDataEvent = setModelDataEvent
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())
if not self.setModelDataEvent is None:
self.setModelDataEvent()
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
+36
View File
@@ -0,0 +1,36 @@
# 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
from View.Tools.PamhyrTranslate import PamhyrTranslate
from View.Geometry.Translate import GeometryTranslate
_translate = QCoreApplication.translate
class GeometryProfileTranslate(GeometryTranslate):
def __init__(self):
super(GeometryProfileTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"x": _translate("Geometry", "X (m)"),
"y": _translate("Geometry", "Y (m)"),
"z": _translate("Geometry", "Z (m)"),
"name": _translate("Geometry", "Name"),
"abs": _translate("Geometry", "Traversal abs (m)"),
}
+95 -235
View File
@@ -31,205 +31,188 @@ from PyQt5.QtCore import (
)
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QFileDialog, QCheckBox,
QUndoStack, QShortcut,
QUndoStack, QShortcut, QTableView, QAbstractItemView,
QHeaderView, QVBoxLayout, QAction,
)
from Model.Geometry.Reach import Reach
from Model.Geometry.ProfileXYZ import ProfileXYZ
from View.ASubWindow import ASubMainWindow
from View.Geometry.Profile.mainwindow_ui_profile import Ui_MainWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Geometry.Profile.Plot import Plot
from View.Geometry.Profile.Table import *
from View.Geometry.Profile.Table import GeometryProfileTableModel
from View.Geometry.Profile.Translate import GeometryProfileTranslate
_translate = QCoreApplication.translate
class ProfileWindow(ASubMainWindow):
def __init__(self, profile=None, title="Profile", parent=None):
self._title = title
self.parent = parent
super(ProfileWindow, self).__init__(
name=self._title,
parent=self.parent
)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
class ProfileWindow(PamhyrWindow):
_pamhyr_ui = "GeometryCrossSection"
_pamhyr_name = "Geometry cross-section"
def __init__(self, profile=None, study=None, config=None, parent=None):
self._profile = profile
self._model = None
self.setup_window()
self.setup_sc()
self.setup_model()
self.setup_connections()
self.plot()
self._model.dataChanged.connect(self.update_plot)
self.fileName = None
# self.ui.tableView.installEventFilter(self)
# self._model.dataChanged.connect(self.tableview_is_modified)
# self.ui.btn_go_back.setEnabled(False)
# self.ui.btn_check.setEnabled(False)
# self._model.dataChanged.connect(self.set_enable_cancel_btn)
# self._model.dataChanged.connect(self.set_enable_validate_changes_btn)
# self.ui.btn_reset.setEnabled(False)
# self._model.dataChanged.connect(self.set_enable_go_back_initial_state_btn)
def setup_window(self):
header = _translate("MainWindowProfile", "Profile")
name = self._profile.name
if (name is None) or (name == ""):
name = _translate("MainWindowProfile", "(no name)")
self._title = (
header + " - " +
f"{self._profile.reach.name}" + " - " +
f"{name} ({self._profile.kp})"
name = f"{self._pamhyr_name} - {self._profile.name} {self._profile.kp}"
super(ProfileWindow, self).__init__(
title = name,
study = study,
config = config,
trad = GeometryProfileTranslate(),
parent = parent
)
self.setWindowTitle(self._title)
self.setup_table()
self.setup_plot()
self.setup_connections()
def setup_sc(self):
self._undo_stack = QUndoStack()
def setup_table(self):
table_headers = self._trad.get_dict("table_headers")
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_model(self):
self._model = TableEditableModel(
profile = self._profile,
table = self.find(QTableView, "tableView")
self._tablemodel = GeometryProfileTableModel(
table_view = table,
table_headers = table_headers,
editable_headers = ["name", "x", "y", "z"],
data = self._profile,
undo = self._undo_stack
)
table.setModel(self._tablemodel)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
self.ui.tableView.setModel(self._model)
self.ui.tableView.setItemDelegate(Delegate())
def setup_plot(self):
self._tablemodel.blockSignals(True)
def setup_connections(self):
self.ui.btn_sort_asc_x.triggered.connect(self.sort_X_ascending)
self.ui.btn_sort_desc_x.triggered.connect(self.sort_X_descending)
self.ui.btn_sort_asc_y.triggered.connect(self.sort_Y_ascending)
self.ui.btn_sort_desc_y.triggered.connect(self.sort_Y_descending)
self.ui.btn_move_up.triggered.connect(self.move_row_up)
self.ui.btn_move_down.triggered.connect(self.move_row_down)
self.ui.btn_export.triggered.connect(self.handleSave)
self.ui.btn_add.triggered.connect(self.insert_row)
self.ui.btn_delete.triggered.connect(self.delete_row)
# self.ui.btn_copy.clicked.connect(self.copyTable)
# self.ui.btn_paste.clicked.connect(self.pasteTable)
# self.ui.btn_check.clicked.connect(self.validate_changes)
# self.ui.btn_go_back.clicked.connect(self.cancel_validate_changes)
# self.ui.btn_reset.clicked.connect(self.go_back_to_initial_state)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
def plot(self):
self.ui.tableView.model().blockSignals(True)
self._canvas = MplCanvas(width=3, height=4, dpi=100)
self._canvas.setObjectName("canvas")
self._toolbar = PamhyrPlotToolbar(
self._canvas, self,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout = self.find(QVBoxLayout, "verticalLayout")
self._plot_layout.addWidget(self._toolbar)
self._plot_layout.addWidget(self._canvas)
self._plot = Plot(
canvas = self.ui.canvas,
canvas = self._canvas,
data = self._profile,
toolbar = None,
table = self.ui.tableView,
toolbar = self._toolbar,
table = self.find(QTableView, "tableView")
)
self._plot.draw()
self.ui.tableView.model().blockSignals(False)
self._tablemodel.blockSignals(False)
def setup_connections(self):
actions = {
"action_sort_asc": self.sort_X_ascending,
"action_sort_des": self.sort_X_descending,
"action_up": self.move_up,
"action_down": self.move_down,
"action_add": self.add,
"action_delete": self.delete,
}
for action in actions:
self.find(QAction, action)\
.triggered.connect(actions[action])
self._tablemodel.dataChanged.connect(self.update_plot)
def update_plot(self):
self.ui.tableView.model().blockSignals(True)
self._tablemodel.blockSignals(True)
# TODO: Do not rebuild all graph
self._plot.update()
self.ui.tableView.model().blockSignals(False)
self._tablemodel.blockSignals(False)
def index_selected_row(self):
rows = self.ui.tableView\
rows = self._tablemodel\
.selectionModel()\
.selectedRows()
if len(rows) == 0:
return 0
return self.ui.tableView\
return self._tablemodel\
.selectionModel()\
.selectedRows()[0]\
.row()
def insert_row(self):
if len(self.ui.tableView.selectedIndexes()) == 0:
self._model.insert_row(self._model.rowCount())
def add(self):
table = self.find(QTableView, "tableView")
if len(table.selectedIndexes()) == 0:
self._tablemodel.insert_row(self._tablemodel.rowCount())
else:
row = self.index_selected_row()
self._model.insert_row(row + 1)
self._tablemodel.insert_row(row + 1)
self.update_plot()
def delete_row(self):
def delete(self):
table = self.find(QTableView, "tableView")
rows = sorted(
list(
set(
[index.row() for index in self.ui.tableView.selectedIndexes()]
[index.row() for index in table.selectedIndexes()]
)
)
)
if len(rows) > 0:
self._model.remove_rows(rows)
self._tablemodel.remove_rows(rows)
self.update_plot()
def sort_X_ascending(self):
self._model.sort('x', order=Qt.AscendingOrder)
self._tablemodel.sort('x', order=Qt.AscendingOrder)
self.update_plot()
def sort_X_descending(self):
self._model.sort('x', order=Qt.DescendingOrder)
self._tablemodel.sort('x', order=Qt.DescendingOrder)
self.update_plot()
def sort_Y_ascending(self):
self._model.sort('y', order=Qt.AscendingOrder)
self._tablemodel.sort('y', order=Qt.AscendingOrder)
self.update_plot()
def sort_Y_descending(self):
self._model.sort('y', order=Qt.DescendingOrder)
self._tablemodel.sort('y', order=Qt.DescendingOrder)
self.update_plot()
def move_row_down(self):
def move_down(self):
rows = list(
set(
[index.row() for index in self.ui.tableView.selectedIndexes()]
[index.row() for index in
self.find(QTableView, "tableView").selectedIndexes()]
)
)
for row in rows:
if row < self._model.rowCount() - 1:
self._model.move_row_down(row)
if row < self._tablemodel.rowCount() - 1:
self._tablemodel.move_down(row)
self.update_plot()
def move_row_up(self):
def move_up(self):
rows = list(
set(
[index.row() for index in self.ui.tableView.selectedIndexes()]
[index.row() for index in
self.find(QTableView, "tableView").selectedIndexes()]
)
)
for row in rows:
if 0 < row:
self._model.move_row_up(row)
self._tablemodel.move_up(row)
self.update_plot()
def copy(self):
rows = self.ui.tableView\
rows = self._tablemodel\
.selectionModel()\
.selectedRows()
table = []
@@ -257,136 +240,13 @@ class ProfileWindow(ASubMainWindow):
row.append(self._profile)
row = self.index_selected_row()
self._model.paste(row, header, data)
self._tablemodel.paste(row, header, data)
self.update_plot()
def undo(self):
self._model.undo()
self._tablemodel.undo()
self.update_plot()
def redo(self):
self._model.redo()
self._tablemodel.redo()
self.update_plot()
def handleSave(self):
if self.fileName is None or self.fileName == '':
self.fileName, self.filters = QFileDialog.getSaveFileName(
self, filter="CSV files (*.csv)"
)
if self.fileName != '':
with open(self.fileName, 'w') as stream:
csvout = csv.writer(stream, delimiter='\t', quotechar=' ',
escapechar=None,
quoting=csv.QUOTE_NONNUMERIC,
lineterminator='\n')
for row in range(self._model.rowCount(QModelIndex())):
rowdata = []
for column in range(self._model.columnCount(QModelIndex())):
item = self._model.index(
row, column, QModelIndex()
).data(Qt.DisplayRole)
if item is not None:
rowdata.append(item)
if item == 'nan':
rowdata.remove(item)
csvout.writerow(rowdata)
def handleOpen(self):
self.fileName, self.filterName = QFileDialog.getOpenFileName(self)
if self.fileName != '':
with open(self.fileName, 'r') as f:
reader = csv.reader(f, delimiter='\t')
header = next(reader)
buf = []
for row in reader:
row[0] = QCheckBox("-")
buf.append(row)
self._model = None
self._model = TableEditableModel(buf)
self.ui.tableView.setModel(self._model)
self.fileName = ''
def set_enable_validate_changes_btn(self):
self.ui.btn_check.setEnabled(True)
def set_enable_cancel_btn(self):
self.ui.btn_go_back.setEnabled(True)
def set_enable_go_back_initial_state_btn(self):
self.ui.btn_reset.setEnabled(True)
def delete_empty_rows(self):
if self._model.data_contains_nan():
buttonReply = QtWidgets.QMessageBox.question(
self,
_translate("MainWindowProfile",
"Suppression les lignes incomplètes"),
_translate("MainWindowProfile",
"Supprimer les lignes des cellules"
" non renseignées ?"),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if buttonReply == QtWidgets.QMessageBox.Yes:
self._model.delete_empty_rows()
if buttonReply == QtWidgets.QMessageBox.No:
pass
def remove_duplicates_point_names(self):
counter_list = []
list_deleted_names = []
for ind, name_point in enumerate(self._model.name):
if name_point not in counter_list:
counter_list.append(name_point)
elif len(name_point.strip()) > 0 and name_point in counter_list:
if name_point not in list_deleted_names:
list_deleted_names.append(name_point)
if list_deleted_names:
self.msg_box_check_duplication_names(list_deleted_names)
def msg_box_check_duplication_names(self, list_deleted_names):
if len(list_deleted_names) == 1:
text = _translate("MainWindowProfile",
"Le nom ''{}'' est dupliqué."
" \n\nYes : Ne garder que la première occurrence. \nNo :"
" Annuler la suppression.".format(list_deleted_names[0]))
else:
text = _translate("MainWindowProfile",
"Les noms suivants : \n{} sont dupliqués"
" \n\nYes : Ne garder que la première occurrence de "
"chaque nom. \nNo :"
" Annuler la suppression.".format(list_deleted_names))
buttonReply = QtWidgets.QMessageBox.question(
self, _translate("MainWindowProfile",
"Suppression des noms répétés"),
text,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if buttonReply == QtWidgets.QMessageBox.Yes:
self._model.remove_duplicates_names()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(
self,
_translate("MainWindowProfile", "Quittez ?"),
_translate("MainWindowProfile",
"Etes-vous sûr de vouloir quitter ?"),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
return choice == QtWidgets.QMessageBox.Yes
def initial_state_model(self, profile_initial_state):
return profile_initial_state
@@ -1,259 +0,0 @@
# mainwindow_ui_profile.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 os.path
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import (
QTableView, QAbstractItemView, QHeaderView,
QToolBar,
)
from View.Plot import MplCanvas, navigation_toolbar_2qt
file_path = os.path.abspath(os.path.dirname(__file__))
icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../View/ui/ressources/"
class Ui_MainWindow(object):
def setupUi(self, MainWindowProfile):
MainWindowProfile.setObjectName("MainWindowProfile")
MainWindowProfile.resize(1089, 699)
self.centralwidget = QtWidgets.QWidget(MainWindowProfile)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.splitter = QtWidgets.QSplitter(self.centralwidget)
self.splitter.setOrientation(Qt.Horizontal)
self.splitter.setObjectName("splitter")
self.widget = QtWidgets.QWidget(self.splitter)
self.widget.setObjectName("widget")
self.verticalLayout_left = QtWidgets.QVBoxLayout(self.widget)
self.verticalLayout_left.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_left.setObjectName("verticalLayout_left")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.toolBar = QToolBar(MainWindowProfile)
self.toolBar.setOrientation(Qt.Vertical)
self.toolBar.setEnabled(True)
MainWindowProfile.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
btn_size = QSize(40, 28)
self.btn_add = QtWidgets.QAction(self.widget)
self.btn_add.setObjectName("btn_add")
icon_btn_add = QtGui.QIcon()
icon_btn_add.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-add.png"))
self.btn_add.setIcon(icon_btn_add)
self.toolBar.addAction(self.btn_add)
self.btn_delete = QtWidgets.QAction(self.widget)
self.btn_delete.setObjectName("btn_delete")
icon_btn_delete = QtGui.QIcon()
icon_btn_delete.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-remove.png"))
self.btn_delete.setIcon(icon_btn_delete)
self.toolBar.addAction(self.btn_delete)
self.btn_sort_asc_x = QtWidgets.QAction(self.widget)
self.btn_sort_asc_x.setObjectName("btn_sort_asc_x")
icon_btn_sort_asc_x = QtGui.QIcon()
icon_btn_sort_asc_x.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png"))
self.btn_sort_asc_x.setIcon(icon_btn_sort_asc_x)
self.toolBar.addAction(self.btn_sort_asc_x)
self.btn_sort_desc_x = QtWidgets.QAction(self.widget)
self.btn_sort_desc_x.setObjectName("btn_sort_desc_x")
icon_btn_sort_desc_x = QtGui.QIcon()
icon_btn_sort_desc_x.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png"))
self.btn_sort_desc_x.setIcon(icon_btn_sort_desc_x)
self.toolBar.addAction(self.btn_sort_desc_x)
self.btn_sort_asc_y = QtWidgets.QAction(self.widget)
self.btn_sort_asc_y.setObjectName("btn_sort_asc_y")
icon_btn_sort_asc_y = QtGui.QIcon()
icon_btn_sort_asc_y.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png"))
self.btn_sort_asc_y.setIcon(icon_btn_sort_asc_y)
self.toolBar.addAction(self.btn_sort_asc_y)
self.btn_sort_desc_y = QtWidgets.QAction(self.widget)
self.btn_sort_desc_y.setObjectName("btn_sort_desc_y")
icon_btn_sort_desc_y = QtGui.QIcon()
icon_btn_sort_desc_y.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png"))
self.btn_sort_desc_y.setIcon(icon_btn_sort_desc_y)
self.toolBar.addAction(self.btn_sort_desc_y)
self.btn_move_up = QtWidgets.QAction(self.widget)
self.btn_move_up.setObjectName("btn_move_up")
icon_btn_move_up = QtGui.QIcon()
icon_btn_move_up.addPixmap(
QtGui.QPixmap(f"{icon_path}up.png"))
self.btn_move_up.setIcon(icon_btn_move_up)
self.toolBar.addAction(self.btn_move_up)
self.btn_move_down = QtWidgets.QAction(self.widget)
self.btn_move_down.setObjectName("btn_move_down")
icon_btn_move_down = QtGui.QIcon()
icon_btn_move_down.addPixmap(
QtGui.QPixmap(f"{icon_path}down.png"))
self.btn_move_down.setIcon(icon_btn_move_down)
self.toolBar.addAction(self.btn_move_down)
self.btn_export = QtWidgets.QAction(self.widget)
self.btn_export.setObjectName("btn_export")
icon_btn_export = QtGui.QIcon()
icon_btn_export.addPixmap(
QtGui.QPixmap(f"{icon_path}export.png"))
self.btn_export.setIcon(icon_btn_export)
self.toolBar.addAction(self.btn_export)
self.btn_copy = QtWidgets.QAction(self.widget)
self.btn_copy.setObjectName("btn_copy")
icon_btn_copy = QtGui.QIcon()
icon_btn_copy.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-copy.png"))
self.btn_copy.setIcon(icon_btn_copy)
self.toolBar.addAction(self.btn_copy)
self.btn_paste = QtWidgets.QAction(self.widget)
self.btn_paste.setObjectName("btn_paste")
icon_btn_paste = QtGui.QIcon()
icon_btn_paste.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-paste.png"))
self.btn_paste.setIcon(icon_btn_paste)
self.toolBar.addAction(self.btn_paste)
self.btn_check = QtWidgets.QAction(self.widget)
self.btn_check.setObjectName("btn_check")
icon_btn_check = QtGui.QIcon()
icon_btn_check.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-apply.png"))
self.btn_check.setIcon(icon_btn_check)
self.toolBar.addAction(self.btn_check)
self.btn_go_back = QtWidgets.QAction(self.widget)
self.btn_go_back.setObjectName("btn_go_back")
icon_btn_go_back = QtGui.QIcon()
icon_btn_go_back.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-undo.png"))
self.btn_go_back.setIcon(icon_btn_go_back)
self.toolBar.addAction(self.btn_go_back)
self.btn_reset = QtWidgets.QAction(self.widget)
self.btn_reset.setObjectName("btn_reset")
icon_btn_reset = QtGui.QIcon()
icon_btn_reset.addPixmap(
QtGui.QPixmap(f"{icon_path}cancel.png"))
self.btn_reset.setIcon(icon_btn_reset)
self.toolBar.addAction(self.btn_reset)
self.verticalLayout_left.addLayout(self.horizontalLayout)
self.tableView = QTableView(self.widget)
self.tableView.setObjectName("tableView")
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.tableView.resizeColumnsToContents()
self.tableView.resizeRowsToContents()
self.tableView.setAlternatingRowColors(True)
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.verticalLayout_left.addWidget(self.tableView)
self.widget1 = QtWidgets.QWidget(self.splitter)
self.widget1.setObjectName("widget1")
self.verticalLayout_right = QtWidgets.QVBoxLayout(self.widget1)
self.verticalLayout_right.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_right.setObjectName("verticalLayout_right")
self.label_graphic_help = QtWidgets.QLabel(self.widget1)
self.label_graphic_help.setMaximumSize(QtCore.QSize(16777215, 25))
self.label_graphic_help.setBaseSize(QtCore.QSize(0, 30))
self.label_graphic_help.setObjectName("label_graphic_help")
self.verticalLayout_right.addWidget(self.label_graphic_help)
self.canvas = MplCanvas.MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.my_toolbar = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(
self.canvas, self.widget1
)
self.verticalLayout_right.addWidget(self.my_toolbar)
self.verticalLayout_right.addWidget(self.canvas)
self.horizontalLayout_2.addWidget(self.splitter)
MainWindowProfile.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindowProfile)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1089, 20))
self.menubar.setObjectName("menubar")
MainWindowProfile.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindowProfile)
self.statusbar.setObjectName("statusbar")
MainWindowProfile.setStatusBar(self.statusbar)
self.retranslateUi(MainWindowProfile)
QtCore.QMetaObject.connectSlotsByName(MainWindowProfile)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindowProfile", "MainWindowProfile"))
self.btn_add.setToolTip(_translate("MainWindowProfile", "Insérer un point"))
self.btn_delete.setToolTip(
_translate("MainWindowProfile", "Supprimer le/les point(s) sélectionnés"))
self.btn_sort_asc_x.setToolTip(
_translate("MainWindowProfile", "Trier les points par ordre croissant de X"))
self.btn_sort_desc_x.setToolTip(
_translate("MainWindowProfile", "Trier les points par ordre décroissant de X"))
self.btn_sort_asc_y.setToolTip(_translate("MainWindowProfile",
"Trier les points par ordre croissant de Y"
))
self.btn_sort_desc_y.setToolTip(_translate("MainWindowProfile",
"Trier les points par ordre décroissant de Y"))
self.btn_move_up.setToolTip(_translate("MainWindowProfile",
"Décaler le point sélectionné vers le haut"))
self.btn_move_down.setToolTip(_translate("MainWindowProfile",
"Décaler le point sélectionné vers le bas"))
self.btn_export.setToolTip(_translate("MainWindowProfile",
"Exporter (dans un fichier) les points du profil au format tabulé"))
self.btn_copy.setToolTip(
_translate("MainWindowProfile", "Copier la sélection au format tabulé"))
self.btn_paste.setToolTip(_translate("MainWindowProfile",
"Coller la sélection depuis le presse-papier au format tabulé"))
self.btn_check.setToolTip(_translate("MainWindowProfile",
"Vérifier la validité de la saisie et garder ou pas les modifications "
"apportées"))
self.btn_go_back.setToolTip(_translate("MainWindowProfile",
"Annuler toutes les modifications depuis la dernière validation"))
self.btn_reset.setToolTip(_translate("MainWindowProfile",
"Annuler toutes les modifications et revenir à l\'état initial"))
self.label_graphic_help.setToolTip(_translate("MainWindowProfile",
"Ligne d'eau : \n \tZ : Cote (m) \n \tA : Aire mouillée ("
"m\u00B2) \n \tp : "
"Périmètre mouillé (m) \n \tL : Largeur au miroir (m)"))
self.label_graphic_help.setText(
_translate("MainWindowProfile", "\'Maj + Clic\' : Ligne d\'eau & \'Ctrl + Clic\' : "
"Sélectionner des points"))
+33 -150
View File
@@ -35,6 +35,8 @@ from PyQt5.QtWidgets import (
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
from Model.Geometry import Reach
from Model.Geometry.ProfileXYZ import ProfileXYZ
from View.Geometry.UndoCommand import *
@@ -44,53 +46,26 @@ logger = logging.getLogger()
_translate = QCoreApplication.translate
class TableEditableModel(QAbstractTableModel):
def __init__(self, reach, headers=None, undo=None):
QAbstractTableModel.__init__(self)
data_list = []
self._undo_stack = undo
self._reach = reach
# Hack for qtlinguist
_ = _translate("Geometry", "Name")
_ = _translate("Geometry", "Kp (m)")
_ = _translate("Geometry", "Type")
if headers is None:
self.headers = [
"Name",
"Kp (m)",
"Type"
]
else:
self.headers = headers
def rowCount(self, parent=QModelIndex()):
return self._reach.number_profiles
def columnCount(self, parent=QModelIndex()):
return len(self.headers)
class GeometryReachTableModel(PamhyrTableModel):
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
if role == Qt.DisplayRole and index.column() == 0:
return self._reach.profile(index.row()).name
return self._data.profile(index.row()).name
if role == Qt.DisplayRole and index.column() == 1:
kp = self._reach.profile(index.row()).kp
kp = self._data.profile(index.row()).kp
return f"{kp:.4f}"
if role == Qt.DisplayRole and index.column() == 2:
return self._reach.profile(index.row()).profile_type
return self._data.profile(index.row()).profile_type
if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter
if role == Qt.ForegroundRole and index.column() == 0:
name = self._reach.profile(index.row()).name\
name = self._data.profile(index.row()).name\
.strip()\
.lower()
if (name == "upstream" or name == "up" or
@@ -102,16 +77,6 @@ class TableEditableModel(QAbstractTableModel):
return QVariant()
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
if section < len(self.headers):
return _translate("Geometry", self.headers[section])
else:
return str(section + 1)
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
row = index.row()
column = index.column()
@@ -119,19 +84,19 @@ class TableEditableModel(QAbstractTableModel):
if role == Qt.EditRole and index.column() != 2:
try:
if index.column() == 0:
self._undo_stack.push(
self._undo.push(
SetNameCommand(
self._reach, index.row(),
self._reach.profile(index.row()).name,
self._data, index.row(),
self._data.profile(index.row()).name,
value
)
)
if index.column() == 1:
self._undo_stack.push(
self._undo.push(
SetKPCommand(
self._reach, index.row(),
self._reach.profile(index.row()).kp,
self._data, index.row(),
self._data.profile(index.row()).kp,
value
)
)
@@ -149,39 +114,25 @@ class TableEditableModel(QAbstractTableModel):
return False
def index(self, row, column, parent=QModelIndex()):
if not self.hasIndex(row, column, parent):
return QModelIndex()
return self.createIndex(row, column, QModelIndex())
def flags(self, index):
flg = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if index.column() == 2:
return flg
else:
return Qt.ItemIsEditable | flg
# @QtCore.pyqtSlot()
def insert_row(self, row, parent=QModelIndex()):
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo_stack.push(
self._undo.push(
AddCommand(
self._reach, row
self._data, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def remove_rows(self, rows, parent=QModelIndex()):
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo_stack.push(
self._undo.push(
DelCommand(
self._reach, rows
self._data, rows
)
)
@@ -191,9 +142,9 @@ class TableEditableModel(QAbstractTableModel):
def sort_profiles(self, _reverse):
self.layoutAboutToBeChanged.emit()
self._undo_stack.push(
self._undo.push(
SortCommand(
self._reach, _reverse
self._data, _reverse
)
)
@@ -201,7 +152,7 @@ class TableEditableModel(QAbstractTableModel):
self.layoutChanged.emit()
def move_row_up(self, row, parent=QModelIndex()):
def move_up(self, row, parent=QModelIndex()):
if row <= 0:
return
@@ -209,26 +160,26 @@ class TableEditableModel(QAbstractTableModel):
self.beginMoveRows(parent, row - 1, row - 1, parent, target)
self._undo_stack.push(
self._undo.push(
MoveCommand(
self._reach, "up", row
self._data, "up", row
)
)
self.endMoveRows()
self.layoutChanged.emit()
def move_row_down(self, row, parent=QModelIndex()):
if row > self._reach.number_profiles:
def move_down(self, row, parent=QModelIndex()):
if row > self._data.number_profiles:
return
target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push(
self._undo.push(
MoveCommand(
self._reach, "down", row
self._data, "down", row
)
)
@@ -238,9 +189,9 @@ class TableEditableModel(QAbstractTableModel):
def duplicate(self, rows, profiles):
self.layoutAboutToBeChanged.emit()
self._undo_stack.push(
self._undo.push(
DuplicateCommand(
self._reach, rows,
self._data, rows,
profiles
)
)
@@ -250,7 +201,7 @@ class TableEditableModel(QAbstractTableModel):
def paste(self, row, header, data):
if row > self._reach.number_profiles:
if row > self._data.number_profiles:
return
if len(data) == 0:
@@ -258,9 +209,9 @@ class TableEditableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit()
self._undo_stack.push(
self._undo.push(
PasteCommand(
self._reach, row,
self._data, row,
list(
map(
lambda d: ProfileXYZ.from_data(header, d),
@@ -272,71 +223,3 @@ class TableEditableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def undo(self):
self._undo_stack.undo()
self.layoutChanged.emit()
def redo(self):
self._undo_stack.redo()
self.layoutChanged.emit()
class Delegate(QStyledItemDelegate):
def __init__(self, parent=None, setModelDataEvent=None):
super(Delegate, self).__init__(parent)
self.setModelDataEvent = setModelDataEvent
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())
if not self.setModelDataEvent is None:
self.setModelDataEvent()
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class Delegate1(QStyledItemDelegate):
def __init__(self, owner, choices):
super().__init__(owner)
self.items = choices
def paint(self, painter, option, index):
if isinstance(self.parent(), QAbstractItemView):
self.parent().openPersistentEditor(index)
super(Delegate1, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
editor = QComboBox(parent)
# editor.currentIndexChanged.connect(self.commit_editor)
editor.addItems(self.items)
return editor
def setEditorData(self, editor, index):
editor.blockSignals(True)
text = index.model().data(index, Qt.DisplayRole)
try:
i = self.items.index(text)
except ValueError:
i = 0
editor.setCurrentIndex(i)
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentText(), Qt.DisplayRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
@pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())
+45
View File
@@ -0,0 +1,45 @@
# 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
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate
class GeometryTranslate(PamhyrTranslate):
def __init__(self):
super(GeometryTranslate, self).__init__()
self._dict["open_file"] = _translate("Geometry", "Open a file")
self._dict["file_st"] = _translate("Geometry", "File mage geometry (*.ST)")
self._dict["file_m"] = _translate("Geometry", "File mage meshed geometry (*.M)")
self._dict["file_all"] = _translate("Geometry", "All file (*)")
self._dict["reach"] = _translate("Geometry", "reach")
self._dict["reachs"] = _translate("Geometry", "reachs")
self._dict["cross_section"] = _translate("Geometry", "cross-section")
self._dict["cross_sections"] = _translate("Geometry", "cross-sections")
self._dict["profile"] = _translate("Geometry", "cross-section")
self._dict["profiles"] = _translate("Geometry", "cross-sections")
self._sub_dict["table_headers"] = {
"name": _translate("Geometry", "Name"),
"kp": _translate("Geometry", "KP"),
}
+156 -145
View File
@@ -34,117 +34,167 @@ from PyQt5.QtCore import (
)
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QFileDialog, QCheckBox,
QUndoStack, QShortcut,
QUndoStack, QShortcut, QTableView, QHeaderView,
QAction, QSlider, QPushButton, QVBoxLayout,
QLabel, QAbstractItemView,
)
from View.Geometry.PlotXY import PlotXY
from View.Geometry.PlotKPZ import PlotKPZ
from View.Geometry.PlotAC import PlotAC
from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.ASubWindow import ASubMainWindow, WindowToolKit
from View.ListedSubWindow import ListedSubWindow
from View.Geometry.mainwindow_ui_reach import Ui_MainWindow
from View.Geometry.Table import *
from View.Geometry.Table import GeometryReachTableModel
from View.Geometry.PlotXY import PlotXY
from View.Geometry.PlotAC import PlotAC
from View.Geometry.PlotKPZ import PlotKPZ
from View.Geometry.Translate import GeometryTranslate
from View.Geometry.Profile.Window import ProfileWindow
_translate = QCoreApplication.translate
class GeometryWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, model=None, title="Geometry", parent=None):
self._title = title
self.parent = parent
class GeometryWindow(PamhyrWindow):
_pamhyr_ui = "GeometryReach"
_pamhyr_name = "Geometry"
def __init__(self, reach=None, study=None, config=None, parent=None):
if reach is None:
self._reach = study.river.current_reach().reach
else:
self._reach = reach
name = f"{self._pamhyr_name} - {self._reach.name}"
super(GeometryWindow, self).__init__(
name=self._title,
parent=parent
title = name,
study = study,
config = config,
trad = GeometryTranslate(),
parent = parent
)
self._model = model
self._reach = model.river.current_reach().reach
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.tableView = self.ui.tableView
self.tableView_header = self.ui.tableView_header
self._tablemodel = None
self._profile_window = []
self._clipboard = None
self.setup_window()
self.setup_sc()
self.setup_model()
self.setup_table()
self.setup_plots()
self.setup_statusbar()
self.setup_connections()
self.changed_slider_value()
def setup_window(self):
self._title = f"{self.ui.mainwindow_title} - {self._reach.name}"
self.setWindowTitle(self._title)
def setup_table(self):
table_headers = self._trad.get_dict("table_headers")
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_model(self):
self._tablemodel = TableEditableModel(
headers = self.ui.tableView_header,
reach = self._reach,
table = self.find(QTableView, "tableView")
self._tablemodel = GeometryReachTableModel(
table_view = table,
table_headers = table_headers,
editable_headers = ["name", "kp"],
data = self._reach,
undo = self._undo_stack
)
self.tableView.setModel(self._tablemodel)
self.tableView.setItemDelegate(Delegate())
table.setModel(self._tablemodel)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_plots(self):
self._canvas_xy = MplCanvas(width=3, height=4, dpi=100)
self._canvas_xy.setObjectName("canvas_xy")
self._toolbar_xy = PamhyrPlotToolbar(
self._canvas_xy, self,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout_xy = self.find(QVBoxLayout, "verticalLayout")
self._plot_layout_xy.addWidget(self._toolbar_xy)
self._plot_layout_xy.addWidget(self._canvas_xy)
self.plot_xy()
self._canvas_kpc = MplCanvas(width=6, height=4, dpi=100)
self._canvas_kpc.setObjectName("canvas_kpc")
self._toolbar_kpc = PamhyrPlotToolbar(
self._canvas_kpc, self,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout_kpc = self.find(QVBoxLayout, "verticalLayout_2")
self._plot_layout_kpc.addWidget(self._toolbar_kpc)
self._plot_layout_kpc.addWidget(self._canvas_kpc)
self.plot_kpc()
self._canvas_ac = MplCanvas(width=9, height=4, dpi=100)
self._canvas_ac.setObjectName("canvas_ac")
self._toolbar_ac = PamhyrPlotToolbar(
self._canvas_ac, self,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout_ac = self.find(QVBoxLayout, "verticalLayout_3")
self._plot_layout_ac.addWidget(self._toolbar_ac)
self._plot_layout_ac.addWidget(self._canvas_ac)
self.plot_ac()
def _compute_status_label(self):
row = self.index_selected_row()
profile = self._reach.profile(row)
name = profile.name + " " + str(profile.kp)
return (
f"<font color=\"Grey\">{self._trad['reach']}: {self._reach.name}" + " - "
f"{self._trad['cross_section']}:</font> {name}"
)
def setup_statusbar(self):
txt = ""
self._status_label = QLabel(txt)
self.statusbar.addPermanentWidget(self._status_label)
def update_statusbar(self):
txt = self._compute_status_label()
self._status_label.setText(txt)
def setup_connections(self):
self.ui.btn_open.triggered.connect(self.open_file_dialog)
self.ui.btn_sort_asc.triggered.connect(self.sort_ascending)
self.ui.btn_sort_desc.triggered.connect(self.sort_descending)
self.ui.btn_move_up.triggered.connect(self.move_row_up)
self.ui.btn_move_down.triggered.connect(self.move_row_down)
# self.ui.btn_end_editing.triggered.connect(self.handleSave)
self.ui.btn_add.triggered.connect(self.insert_row)
self.ui.btn_delete.triggered.connect(self.delete_rows)
self.ui.btn_edit.triggered.connect(self.edit_profile)
self.ui.verticalSlider.valueChanged.connect(self.changed_slider_value)
actions = {
"action_import": self.import_from_file,
# "action_export": self.export_to_file,
"action_sort_asc": self.sort_ascending,
"action_sort_des": self.sort_descending,
"action_up": self.move_up,
"action_down": self.move_down,
"action_add": self.add,
"action_delete": self.delete,
"action_edit": self.edit_profile,
}
self.ui.btn_slider_up.clicked.connect(self.decrement_value_slider)
self.ui.btn_slider_down.clicked.connect(self.increment_value_slider)
self.ui.btn_move_up.triggered.connect(self.changed_profile_slot)
for action in actions:
self.find(QAction, action)\
.triggered.connect(actions[action])
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self.find(QSlider, "verticalSlider").valueChanged.connect(self.changed_slider_value)
self.find(QPushButton, "pushButton_up").clicked.connect(self.decrement_value_slider)
self.find(QPushButton, "pushButton_down").clicked.connect(self.increment_value_slider)
# Profile selection when line change in table
self.tableView.selectionModel()\
.selectionChanged\
.connect(self.select_current_profile)
self.find(QTableView, "tableView").selectionModel()\
.selectionChanged\
.connect(self.select_current_profile)
def open_file_dialog(self):
def import_from_file(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_st"],
self._trad["file_m"],
self._trad["file_all"],
]
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
_translate("MainWindow_reach", "Ouvrir un fichier"),
self._trad["open_file"],
"",
_translate("MainWindow_reach", "Fichiers .ST (*.ST)") +
";; " +
_translate("MainWindow_reach", "Fichiers .M (*.M)") +
";; " +
_translate("MainWindow_reach", "Tous les fichiers (*)"),
";; ".join(file_types),
options=options
)
@@ -158,20 +208,6 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.plot_kpc()
self.plot_ac()
def messagebox_profile_editing(self):
msg_box = QtWidgets.QMessageBox()
msg_box.setIcon(QtWidgets.QMessageBox.Information)
msg_box.setWindowTitle(_translate("MainWindow_reach",
"Édition des profils sélectionnés"))
msg_box.setText(_translate("MainWindow_reach",
"Vous avez sélectionné plus de 5 profils."
" \nSeuls les 5 premiers seront édités."))
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
return_value = msg_box.exec()
# if return_value == QtWidgets.QMessageBox.Ok:
# print('OK clicked')
def edit_profile(self):
self.tableView.model().blockSignals(True)
@@ -214,9 +250,9 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True)
self._plot_xy = PlotXY(
canvas = self.ui.canvas_1,
canvas = self._canvas_xy,
data = self._reach,
toolbar = self.ui.toolbar_1
toolbar = self._toolbar_xy
)
self._plot_xy.draw()
@@ -231,9 +267,9 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True)
self._plot_kpc = PlotKPZ(
canvas = self.ui.canvas_2,
canvas = self._canvas_kpc,
data = self._reach,
toolbar = self.ui.toolbar_2
toolbar = self._toolbar_kpc
)
self._plot_kpc.draw()
@@ -248,9 +284,9 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True)
self._plot_ac = PlotAC(
canvas = self.ui.canvas_3,
canvas = self._canvas_ac,
data = self._reach,
toolbar = self.ui.toolbar_3,
toolbar = self._toolbar_ac,
plot_xy = self._plot_xy
)
self._plot_ac.draw()
@@ -303,7 +339,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
if len(self.tableView.selectedIndexes()) > 0:
row = self.index_selected_row()
self.ui.verticalSlider.setValue(row)
self.find(QSlider, "verticalSlider").setValue(row)
self.select_plot_xy(row)
self.select_plot_kpc(row)
self.select_plot_ac(row)
@@ -314,41 +350,37 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True)
if self._tablemodel.rowCount() != 0:
self.ui.verticalSlider.setMaximum(self._tablemodel.rowCount() - 1)
slider = self.find(QSlider, "verticalSlider")
slider.setMaximum(self._tablemodel.rowCount() - 1)
slider_value = self.ui.verticalSlider.value()
slider_value = slider.value()
kp = self._reach.profile(slider_value).kp
self.ui.vertical_slider_label.setText(
_translate("MainWindow_reach", "Kp : ") +
f"{kp}" + "\n" +
_translate("MainWindow_reach",
"Profil N° : ") +
f"{slider_value + 1}"
)
self.select_plot_xy(slider_value)
self.select_plot_kpc(slider_value)
self.select_row_profile_slider(slider_value)
self.update_statusbar()
self.tableView.model().blockSignals(False)
def increment_value_slider(self):
if 0 <= self.ui.verticalSlider.value() < self._tablemodel.rowCount() - 1:
self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() + 1)
slider = self.find(QSlider, "verticalSlider")
if 0 <= slider.value() < self._tablemodel.rowCount() - 1:
slider.setValue(slider.value() + 1)
def decrement_value_slider(self):
if 0 < self.ui.verticalSlider.value() < self._tablemodel.rowCount():
self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() - 1)
slider = self.find(QSlider, "verticalSlider")
if 0 < slider.value() < self._tablemodel.rowCount():
slider.setValue(slider.value() - 1)
def insert_row(self):
def add(self):
if len(self.tableView.selectedIndexes()) == 0:
self._tablemodel.insert_row(self._tablemodel.rowCount())
self._tablemodel.add(self._tablemodel.rowCount())
else:
row = self.index_selected_row()
self._tablemodel.insert_row(row + 1)
self._tablemodel.add(row + 1)
def delete_rows(self):
def delete(self):
rows = sorted(
list(
set(
@@ -358,7 +390,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
)
if len(rows) > 0:
self._tablemodel.remove_rows(rows)
self._tablemodel.delete(rows)
self.update_plot_xy()
self.select_current_profile()
@@ -373,24 +405,24 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
.row()
def sort_ascending(self):
self._tablemodel.sort_profiles(True)
self._tablemodel.sort_profiles(False)
self.select_current_profile()
self.changed_slider_value()
def sort_descending(self):
self._tablemodel.sort_profiles(False)
self._tablemodel.sort_profiles(True)
self.select_current_profile()
self.changed_slider_value()
def move_row_up(self):
def move_up(self):
row = self.index_selected_row()
self._tablemodel.move_row_up(row)
self._tablemodel.move_up(row)
self.select_current_profile()
def move_row_down(self):
def move_down(self):
row = self.index_selected_row()
self._tablemodel.move_row_down(row)
self._tablemodel.move_down(row)
self.select_current_profile()
def duplicate(self):
@@ -411,7 +443,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self._tablemodel.duplicate(rows, profiles)
self.select_current_profile()
def copy(self):
def _copy(self):
rows = self.tableView\
.selectionModel()\
.selectedRows()
@@ -427,7 +459,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.copyTableIntoClipboard(table)
def paste(self):
def _paste(self):
header, data = self.parseClipboardTable()
if len(data) == 0:
@@ -442,19 +474,19 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self._tablemodel.paste(row, header, data)
self.select_current_profile()
def undo(self):
def _undo(self):
self._tablemodel.undo()
self.select_current_profile()
self.update_plot_xy()
self.update_plot_kpc()
def redo(self):
def _redo(self):
self._tablemodel.redo()
self.select_current_profile()
self.update_plot_xy()
self.update_plot_kpc()
def handleSave(self):
def export_to_file(self):
options = QFileDialog.Options()
DEFAULT_DIRECTORY = '/home/'
settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
@@ -463,32 +495,11 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
filename, filters = QFileDialog.getSaveFileName(
self,
filter=_translate("MainWindow_reach",
"Files .ST(*.ST or *.st)")
+ ";; " +
_translate("MainWindow_reach", "All files "
"(*)"),
options=options
filter = self._trad["file_st"] + ";; " + self._trad["file_all"],
options = options
)
current_dir = os.path.split(filename)[0] or DEFAULT_DIRECTORY
if filename != '':
self._tablemodel.export_reach(filename)
def handleOpen(self):
filename, filterName = QFileDialog.getOpenFileName(self)
if filename != '':
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter='\t')
header = next(reader)
buf = []
for row in reader:
row[0] = QCheckBox("-")
buf.append(row)
self._tablemodel = None
self._tablemodel = TableEditableModel(buf)
self.tableView.setModel(self._tablemodel)
filename = ''
-357
View File
@@ -1,357 +0,0 @@
# mainwindow_ui_reach.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
import os.path
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtWidgets import (
QAbstractItemView, QHeaderView, QToolBar,
)
from View.Plot import (
MplCanvas, navigation_toolbar_2qt
)
file_path = os.path.abspath(os.path.dirname(__file__))
icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/View/ui/ressources/"
class Ui_MainWindow(object):
def setupUi(self, MainWindow_reach):
MainWindow_reach.setObjectName("MainWindow_reach")
MainWindow_reach.resize(1400, 800)
self.centralwidget = QtWidgets.QWidget(MainWindow_reach)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.splitter_3 = QtWidgets.QSplitter(self.centralwidget)
self.splitter_3.setOrientation(Qt.Horizontal)
self.splitter_3.setObjectName("splitter_3")
self.widget = QtWidgets.QWidget(self.splitter_3)
self.widget.setObjectName("widget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
# self.label_section_order = QtWidgets.QLabel(self.widget)
# self.label_section_order.setLayoutDirection(Qt.LeftToRight)
# self.label_section_order.setObjectName("label_section_order")
# self.verticalLayout.addWidget(self.label_section_order)
self.toolBar = QToolBar(MainWindow_reach)
self.toolBar.setEnabled(True)
MainWindow_reach.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
btn_size = QSize(40, 28)
self.btn_open = QtWidgets.QAction(self.widget)
self.btn_open.setObjectName("btn_open")
icon_btn_open = QtGui.QIcon()
icon_btn_open.addPixmap(
QtGui.QPixmap(f"{icon_path}open.png"))
self.btn_open.setIcon(icon_btn_open)
self.toolBar.addAction(self.btn_open)
self.btn_add = QtWidgets.QAction(self.widget)
self.btn_add.setObjectName("btn_add")
icon_btn_add = QtGui.QIcon()
icon_btn_add.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk_add.png"))
self.btn_add.setIcon(icon_btn_add)
self.toolBar.addAction(self.btn_add)
self.btn_delete = QtWidgets.QAction(self.widget)
self.btn_delete.setObjectName("btn_delete")
icon_btn_delete = QtGui.QIcon()
icon_btn_delete.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-remove.png"))
self.btn_delete.setIcon(icon_btn_delete)
self.toolBar.addAction(self.btn_delete)
self.btn_edit = QtWidgets.QAction(self.widget)
self.btn_edit.setObjectName("btn_edit")
icon_btn_edit = QtGui.QIcon()
icon_btn_edit.addPixmap(
QtGui.QPixmap(f"{icon_path}gnome-stock-edit.png"))
self.btn_edit.setIcon(icon_btn_edit)
self.toolBar.addAction(self.btn_edit)
# self.btn_copy_selected_profile = QtWidgets.QAction(self.widget)
# self.btn_copy_selected_profile.setObjectName("btn_copy_selected_profile")
# icon_btn_copy_selected_profile = QtGui.QIcon()
# icon_btn_copy_selected_profile.addPixmap(
# QtGui.QPixmap(f"{icon_path}gtk-copy.png"))
# self.btn_copy_selected_profile.setIcon(icon_btn_copy_selected_profile)
# self.toolBar.addAction(self.btn_copy_selected_profile)
# self.btn_paste_selected_profile = QtWidgets.QAction(self.widget)
# self.btn_paste_selected_profile.setObjectName("btn_paste_selected_profile")
# icon_btn_paste_selected_profile = QtGui.QIcon()
# icon_btn_paste_selected_profile.addPixmap(
# QtGui.QPixmap(f"{icon_path}gtk-paste.png"))
# self.btn_paste_selected_profile.setIcon(icon_btn_paste_selected_profile)
# self.toolBar.addAction(self.btn_paste_selected_profile)
# self.btn_duplicate_selected_profile = QtWidgets.QAction(self.widget)
# self.btn_duplicate_selected_profile.setObjectName("btn_duplicate_selected_profile")
# icon_btn_duplicate_selected_profile = QtGui.QIcon()
# icon_btn_duplicate_selected_profile.addPixmap(
# QtGui.QPixmap(f"{icon_path}gtk-dnd-multiple.png"))
# self.btn_duplicate_selected_profile.setIcon(icon_btn_duplicate_selected_profile)
# self.toolBar.addAction(self.btn_duplicate_selected_profile)
self.btn_sort_asc = QtWidgets.QAction(self.widget)
self.btn_sort_asc.setObjectName("btn_sort_asc")
icon_btn_sort_asc = QtGui.QIcon()
icon_btn_sort_asc.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png"))
self.btn_sort_asc.setIcon(icon_btn_sort_asc)
self.toolBar.addAction(self.btn_sort_asc)
self.btn_sort_desc = QtWidgets.QAction(self.widget)
self.btn_sort_desc.setObjectName("btn_sort_desc")
icon_btn_sort_desc = QtGui.QIcon()
icon_btn_sort_desc.addPixmap(
QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png"))
self.btn_sort_desc.setIcon(icon_btn_sort_desc)
self.toolBar.addAction(self.btn_sort_desc)
self.btn_move_up = QtWidgets.QAction(self.widget)
self.btn_move_up.setObjectName("btn_move_up")
icon_btn_move_up = QtGui.QIcon()
icon_btn_move_up.addPixmap(
QtGui.QPixmap(f"{icon_path}up.png"))
self.btn_move_up.setIcon(icon_btn_move_up)
self.toolBar.addAction(self.btn_move_up)
self.btn_move_down = QtWidgets.QAction(self.widget)
self.btn_move_down.setObjectName("btn_move_down")
icon_btn_move_down = QtGui.QIcon()
icon_btn_move_down.addPixmap(
QtGui.QPixmap(f"{icon_path}down.png"))
self.btn_move_down.setIcon(icon_btn_move_down)
self.toolBar.addAction(self.btn_move_down)
# self.btn_end_editing = QtWidgets.QAction(self.widget)
# self.btn_end_editing.setObjectName("btn_end_editing")
# icon_btn_save = QtGui.QIcon()
# icon_btn_save.addPixmap(
# QtGui.QPixmap(f"{icon_path}gtk-apply.png"))
# self.btn_end_editing.setIcon(icon_btn_save)
# self.toolBar.addAction(self.btn_end_editing)
self.verticalLayout.addLayout(self.horizontalLayout)
self.tableView = QtWidgets.QTableView(self.widget)
self.tableView.setObjectName("tableView")
self.verticalLayout.addWidget(self.tableView)
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.tableView.setAlternatingRowColors(True)
self.tableView.verticalHeader().hide()
self.tableView.resizeColumnsToContents()
self.tableView.resizeRowsToContents()
self.widget1 = QtWidgets.QWidget(self.splitter_3)
self.widget1.setObjectName("widget1")
self.gridLayout = QtWidgets.QGridLayout(self.widget1)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.splitter_2 = QtWidgets.QSplitter(self.widget1)
self.splitter_2.setOrientation(Qt.Vertical)
self.splitter_2.setObjectName("splitter_2")
self.splitter = QtWidgets.QSplitter(self.splitter_2)
self.splitter.setOrientation(Qt.Horizontal)
self.splitter.setObjectName("splitter")
self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.splitter)
self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
self.verticalLayout_canvas_1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
self.verticalLayout_canvas_1.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_canvas_1.setObjectName("verticalLayout_canvas_1")
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.canvas_1, self.centralwidget,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.verticalLayout_canvas_1.addWidget(self.toolbar_1)
self.verticalLayout_canvas_1.addWidget(self.canvas_1)
self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.splitter)
self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
self.verticalLayout_canvas_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
self.verticalLayout_canvas_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_canvas_2.setObjectName("verticalLayout_canvas_2")
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.canvas_2, self.centralwidget,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.verticalLayout_canvas_2.addWidget(self.toolbar_2)
self.verticalLayout_canvas_2.addWidget(self.canvas_2)
self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter_2)
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout_canvas_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout_canvas_3.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_canvas_3.setObjectName("verticalLayout_canvas_3")
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.canvas_3, self.centralwidget,
items = ["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.verticalLayout_canvas_3.addWidget(self.toolbar_3)
self.verticalLayout_canvas_3.addWidget(self.canvas_3)
self.gridLayout.addWidget(self.splitter_2, 0, 0, 3, 3)
self.verticalSlider = QtWidgets.QSlider(self.widget1)
self.verticalSlider.setMinimumSize(QtCore.QSize(30, 0))
self.verticalSlider.setOrientation(Qt.Vertical)
self.verticalSlider.setInvertedAppearance(True)
self.verticalSlider.setInvertedControls(False)
self.verticalSlider.setObjectName("verticalSlider")
self.gridLayout.addWidget(self.verticalSlider, 0, 3, 1, 1)
self.btn_slider_up = QtWidgets.QPushButton(self.widget1)
self.btn_slider_up.setMaximumSize(btn_size)
self.btn_slider_up.setText("")
self.btn_slider_up.setObjectName("btn_slider_up")
icon_btn_slider_up = QtGui.QIcon()
icon_btn_slider_up.addPixmap(
QtGui.QPixmap(f"{icon_path}go-up2.png"))
self.btn_slider_up.setIcon(icon_btn_slider_up)
#self.btn_slider_up.setIconSize(btn_size)
self.gridLayout.addWidget(self.btn_slider_up, 1, 3, 1, 1)
self.btn_slider_down = QtWidgets.QPushButton(self.widget1)
self.btn_slider_down.setMaximumSize(btn_size)
self.btn_slider_down.setText("")
self.btn_slider_down.setObjectName("btn_slider_down")
icon_btn_slider_down = QtGui.QIcon()
icon_btn_slider_down.addPixmap(
QtGui.QPixmap(f"{icon_path}go-down1.png"))
self.btn_slider_down.setIcon(icon_btn_slider_down)
#self.btn_slider_down.setIconSize(btn_size)
self.gridLayout.addWidget(self.btn_slider_down, 2, 3, 1, 1)
self.vertical_slider_label = QtWidgets.QLabel(self.widget1)
self.vertical_slider_label.setObjectName("vertical_slider_label")
self.gridLayout.addWidget(self.vertical_slider_label, 3, 2, 2, 1)
self.vertical_slider_label.setAlignment(Qt.AlignRight)
# self.btn_slider_go_back = QtWidgets.QPushButton(self.widget1)
# self.btn_slider_go_back.setMaximumSize(btn_size)
# self.btn_slider_go_back.setText("")
# self.btn_slider_go_back.setObjectName("btn_slider_go_back")
# icon_btn_slider_go_back = QtGui.QIcon()
# icon_btn_slider_go_back.addPixmap(
# QtGui.QPixmap(f"{icon_path}gtk-go-back.png"))
# self.btn_slider_go_back.setIcon(icon_btn_slider_go_back)
# #self.btn_slider_go_back.setIconSize(btn_size)
# self.gridLayout.addWidget(self.btn_slider_go_back, 5, 0, 1, 1)
# self.btn_slider_go_back.setEnabled(False)
# self.btn_slider_go_forward = QtWidgets.QPushButton(self.widget1)
# self.btn_slider_go_forward.setMaximumSize(btn_size)
# self.btn_slider_go_forward.setText("")
# self.btn_slider_go_forward.setObjectName("btn_slider_go_forward")
# icon_btn_slider_go_forward = QtGui.QIcon()
# icon_btn_slider_go_forward.addPixmap(
# QtGui.QPixmap(f"{icon_path}gtk-go-forward.png"))
# self.btn_slider_go_forward.setIcon(icon_btn_slider_go_forward)
# #self.btn_slider_go_forward.setIconSize(btn_size)
# self.gridLayout.addWidget(self.btn_slider_go_forward, 5, 1, 1, 1)
# self.btn_slider_go_forward.setEnabled(False)
# self.horizontalSlider = QtWidgets.QSlider(self.widget1)
# self.horizontalSlider.setMinimumSize(QtCore.QSize(0, 30))
# self.horizontalSlider.setOrientation(Qt.Horizontal)
# self.horizontalSlider.setObjectName("horizontalSlider")
# self.gridLayout.addWidget(self.horizontalSlider, 5, 2, 1, 1)
# self.horizontalSlider.setEnabled(False)
self.horizontalLayout_2.addWidget(self.splitter_3)
MainWindow_reach.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow_reach)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1123, 20))
self.menubar.setObjectName("menubar")
MainWindow_reach.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow_reach)
self.statusbar.setObjectName("statusbar")
MainWindow_reach.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow_reach)
QtCore.QMetaObject.connectSlotsByName(MainWindow_reach)
def retranslateUi(self, MainWindow_reach):
_translate = QtCore.QCoreApplication.translate
self.name_reach = ""
self.mainwindow_title = _translate("MainWindow_reach", "Geometry reach edit")
MainWindow_reach.setWindowTitle(self.mainwindow_title + f"{self.name_reach}")
# self.label_section_order.setText(f"{self.text_section_order}")
self.vertical_slider_value = ""
self.vertical_slider_label.setText(_translate("MainWindow_reach", "Kp = ") + f"{self.vertical_slider_value}")
self.btn_open.setToolTip(_translate("MainWindow_reach", " Import geometry"))
self.btn_add.setToolTip(_translate("MainWindow_reach", " New profile"))
self.btn_delete.setToolTip(_translate("MainWindow_reach", " Delete selected profile"))
self.btn_edit.setToolTip(_translate("MainWindow_reach", " Editer selected profile"))
self.btn_sort_asc.setToolTip(_translate("MainWindow_reach", " Trier les profils par ordre croissant des Pk"))
self.btn_sort_desc.setToolTip(_translate("MainWindow_reach", " Trier les profils par ordre décroissant des Pk"))
self.btn_move_up.setToolTip(_translate("MainWindow_reach", " Changer l'ordre des profils (en décalant le "
"profil sélectionné vers le haut)"))
self.btn_move_down.setToolTip(_translate("MainWindow_reach", " Changer l'ordre des profils (en décalant le "
"profil sélectionné vers le bas)"))
# self.btn_end_editing.setToolTip(_translate("MainWindow_reach", " Terminer l'édition"))
self.tableView_header = [_translate("MainWindow_reach", "Name"),
_translate("MainWindow_reach", "Kp (m)"),
_translate("MainWindow_reach", "Type")]
self.toolbar_1._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+Z"))
self.toolbar_1._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+Z)"))
self.toolbar_2._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+E"))
self.toolbar_2._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+E)"))
self.toolbar_3._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+R"))
self.toolbar_3._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+R)"))
self.toolbar_1._actions["non_isometric_view"].setShortcut("Alt+S")
self.toolbar_1._actions["non_isometric_view"].setToolTip(
_translate("MainWindow_reach", "Vue globale automatique (Alt+S)"))
self.toolbar_2._actions["non_isometric_view"].setShortcut("Alt+D")
self.toolbar_2._actions["non_isometric_view"].setToolTip(
_translate("MainWindow_reach", "Vue globale automatique (Alt+D)"))
self.toolbar_3._actions["non_isometric_view"].setShortcut("Alt+F")
self.toolbar_3._actions["non_isometric_view"].setToolTip(
_translate("MainWindow_reach", "Vue globale automatique (Alt+F)"))
@@ -16,8 +16,7 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtGui import (
QKeySequence,
@@ -32,10 +31,15 @@ from PyQt5.QtWidgets import (
QDoubleSpinBox,
)
class DischargeDialog(ASubWindow, ListedSubWindow):
class DischargeDialog(PamhyrDialog):
_pamhyr_ui = "InitialConditions_Dialog_Generator_Discharge"
_pamhyr_name = "Discharge"
def __init__(self, title="Discharge", parent=None):
super(DischargeDialog, self).__init__(
name=title, ui="InitialConditions_Dialog_Generator_Discharge", parent=parent
title = self._pamhyr_name,
options = [],
parent = parent
)
self.value = None
+9 -5
View File
@@ -16,8 +16,7 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtGui import (
QKeySequence,
@@ -32,10 +31,15 @@ from PyQt5.QtWidgets import (
QDoubleSpinBox,
)
class HeightDialog(ASubWindow, ListedSubWindow):
def __init__(self, title="Height", parent=None):
class HeightDialog(PamhyrDialog):
_pamhyr_ui = "InitialConditions_Dialog_Generator_Height"
_pamhyr_name = "Height"
def __init__(self, parent=None):
super(HeightDialog, self).__init__(
name=title, ui="InitialConditions_Dialog_Generator_Height", parent=parent
title = self._pamhyr_name,
options = [],
parent = parent
)
self.value = None
+4 -11
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*-
from tools import timer
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
@@ -25,14 +25,7 @@ from PyQt5.QtCore import (
_translate = QCoreApplication.translate
class PlotDKP(APlot):
def __init__(self, canvas=None, data=None, toolbar=None):
super(PlotDKP, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
class PlotDKP(PamhyrPlot):
@timer
def draw(self, highlight=None):
self.canvas.axes.cla()
@@ -42,11 +35,11 @@ class PlotDKP(APlot):
return
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Elevation (m)"),
self._trad["elevation"],
color='green', fontsize=11
)
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "KP (m)"),
self._trad["kp"],
color='green', fontsize=11
)
+4 -17
View File
@@ -17,22 +17,9 @@
# -*- coding: utf-8 -*-
from tools import timer
from View.Plot.APlot import APlot
from PyQt5.QtCore import (
QCoreApplication
)
_translate = QCoreApplication.translate
class PlotDischarge(APlot):
def __init__(self, canvas=None, data=None, toolbar=None):
super(PlotDischarge, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
from View.Tools.PamhyrPlot import PamhyrPlot
class PlotDischarge(PamhyrPlot):
@timer
def draw(self, highlight=None):
self.canvas.axes.cla()
@@ -42,11 +29,11 @@ class PlotDischarge(APlot):
return
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Discharge (m^3/s)"),
self._trad["discharge"],
color='green', fontsize=11
)
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "KP (m)"),
self._trad["kp"],
color='green', fontsize=11
)
+17 -39
View File
@@ -33,14 +33,14 @@ from PyQt5.QtWidgets import (
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.InitialConditions.UndoCommand import (
SetCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand, GenerateCommand,
)
from View.InitialConditions.translate import *
logger = logging.getLogger()
_translate = QCoreApplication.translate
@@ -87,29 +87,13 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel):
def __init__(self, river=None, reach=None, undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._river = river
class InitialConditionTableModel(PamhyrTableModel):
def __init__(self, reach=None, **kwargs):
self._reach = reach
self._undo = undo
self._ics = self._river.initial_conditions.get(reach)
super(InitialConditionTableModel, self).__init__(**kwargs)
def flags(self, index):
column = index.column()
options = Qt.ItemIsSelectable
if self._headers[column] != "speed":
options |= Qt.ItemIsEnabled | Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._ics)
def columnCount(self, parent):
return len(self._headers)
def _setup_lst(self):
self._lst = self._data.river.initial_conditions.get(self._reach)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
@@ -119,16 +103,10 @@ class TableModel(QAbstractTableModel):
column = index.column()
if self._headers[column] not in ["name", "comment"]:
v = self._ics.get(row)[self._headers[column]]
v = self._lst.get(row)[self._headers[column]]
return f"{v:.4f}"
else:
return self._ics.get(row)[self._headers[column]]
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(row)[self._headers[column]]
return QVariant()
@@ -143,7 +121,7 @@ class TableModel(QAbstractTableModel):
if self._headers[column] is not None:
self._undo.push(
SetCommand(
self._ics, row, self._headers[column], value
self._lst, row, self._headers[column], value
)
)
except Exception as e:
@@ -158,7 +136,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
AddCommand(
self._ics, row
self._lst, row
)
)
@@ -170,7 +148,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
DelCommand(
self._ics, rows
self._lst, rows
)
)
@@ -182,7 +160,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
SortCommand(
self._ics, False
self._lst, False
)
)
@@ -199,7 +177,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
MoveCommand(
self._ics, "up", row
self._lst, "up", row
)
)
@@ -207,7 +185,7 @@ class TableModel(QAbstractTableModel):
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._ics):
if row > len(self._lst):
return
target = row
@@ -216,7 +194,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
MoveCommand(
self._ics, "down", row
self._lst, "down", row
)
)
@@ -234,7 +212,7 @@ class TableModel(QAbstractTableModel):
def generate(self, generator, param):
self._undo.push(
GenerateCommand(
self._ics, generator, param
self._lst, generator, param
)
)
self.layoutChanged.emit()
+50 -55
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -46,14 +45,14 @@ from View.InitialConditions.UndoCommand import (
DuplicateCommand,
)
from View.InitialConditions.Table import TableModel, ComboBoxDelegate
from View.InitialConditions.Table import InitialConditionTableModel, ComboBoxDelegate
from View.Plot.MplCanvas import MplCanvas
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.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 ICTranslate
from View.InitialConditions.DialogHeight import HeightDialog
from View.InitialConditions.DialogDischarge import DischargeDialog
@@ -61,68 +60,65 @@ _translate = QCoreApplication.translate
logger = logging.getLogger()
class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Initial condition",
study=None, parent=None):
self._study = study
self._reach = study.river.current_reach()
self._ics = self._study.river.initial_conditions.get(self._reach)
class InitialConditionsWindow(PamhyrWindow):
_pamhyr_ui = "InitialConditions"
_pamhyr_name = "Initial condition"
self.setup_title(title)
def __init__(self, study=None, config=None, reach=None, parent=None):
if reach is not None:
self._reach = reach
else:
self._reach = study.river.current_reach()
super(InitialConditionsWindow, self).__init__(
name=self._title, ui="InitialConditions", parent=parent
name = (
self._pamhyr_name +
" - " + study.name +
" - " + self._reach.name
)
self.setup_sc()
super(InitialConditionsWindow, self).__init__(
title = name,
study = study,
config = config,
trad = ICTranslate(),
parent = parent
)
self._ics = study.river.initial_conditions.get(self._reach)
self.setup_table()
self.setup_graph()
self.setup_plot()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_title(self, title):
self._title = (
title + " - "
+ self._study.name + " - "
+ self._reach.name
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
river = self._study.river,
reach = self._reach,
undo = self._undo_stack,
)
table.setModel(self._table)
self._delegate_kp = ComboBoxDelegate(
reach = self._reach,
parent=self
parent = self
)
table.setItemDelegateForColumn(
list(table_headers).index("kp"),
self._delegate_kp
self._table = InitialConditionTableModel(
reach = self._reach,
table_view = table,
table_headers = self._trad.get_dict("table_headers"),
editable_headers = ["kp", "discharge", "elevation", "height"],
delegates = {"kp": self._delegate_kp},
data = self._study,
undo = self._undo_stack,
trad = self._trad
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_graph(self):
def setup_plot(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")
@@ -132,13 +128,15 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self.plot_1 = PlotDKP(
canvas = self.canvas_1,
data = self._ics,
trad = self._trad,
toolbar = self.toolbar_1,
parent = self
)
self.plot_1.draw()
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")
@@ -148,7 +146,9 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self.plot_2 = PlotDischarge(
canvas = self.canvas_2,
data = self._ics,
trad = self._trad,
toolbar = self.toolbar_2,
parent = self
)
self.plot_2.draw()
@@ -165,11 +165,6 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self.generate_discharge
)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self._table.dataChanged.connect(self._update_plot)
def index_selected_row(self):
@@ -225,19 +220,19 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self._table.move_down(row)
self._update_plot()
def copy(self):
def _copy(self):
logger.info("TODO: copy")
self._update_plot()
def paste(self):
def _paste(self):
logger.info("TODO: paste")
self._update_plot()
def undo(self):
def _undo(self):
self._table.undo()
self._update_plot()
def redo(self):
def _redo(self):
self._table.redo()
self._update_plot()
+19 -9
View File
@@ -18,14 +18,24 @@
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate
table_headers = {
# "name": _translate("LateralContribution", "Name"),
"kp": _translate("LateralContribution", "KP (m)"),
# "speed": _translate("LateralContribution", "Speed (m/s)"),
"discharge": _translate("LateralContribution", "Discharge (m³/s)"),
"elevation": _translate("LateralContribution", "Elevation (m)"),
"height": _translate("LateralContribution", "Height (m)"),
# "comment": _translate("LateralContribution", "Comment"),
}
class ICTranslate(PamhyrTranslate):
def __init__(self):
super(ICTranslate, self).__init__()
self._dict["elevation"] = _translate("InitialCondition", "Elevation (m)")
self._dict["discharge"] = _translate("InitialCondition", "Discharge (m³/s)")
self._dict["kp"] = _translate("InitialCondition", "KP (m)")
self._sub_dict["table_headers"] = {
# "name": _translate("InitialCondition", "Name"),
"kp": _translate("InitialCondition", "KP (m)"),
# "speed": _translate("InitialCondition", "Speed (m/s)"),
"discharge": _translate("InitialCondition", "Discharge (m³/s)"),
"elevation": _translate("InitialCondition", "Elevation (m)"),
"height": _translate("InitialCondition", "Height (m)"),
# "comment": _translate("InitialCondition", "Comment"),
}
+13 -9
View File
@@ -19,7 +19,7 @@
from datetime import datetime
from tools import timer, trace
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
@@ -29,15 +29,19 @@ from View.LateralContribution.Edit.translate import *
_translate = QCoreApplication.translate
class Plot(APlot):
def __init__(self, canvas=None, data=None,
mode = "time", toolbar=None):
class Plot(PamhyrPlot):
def __init__(self, mode = "time", data=None,
canvas=None, trad=None, toolbar=None,
parent=None):
super(Plot, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
canvas = canvas,
trad = trad,
data = data,
toolbar = toolbar,
parent = parent
)
self._table_headers = self._trad.get_dict("table_headers")
self._mode = mode
def custom_ticks(self):
@@ -105,10 +109,10 @@ class Plot(APlot):
# Plot label
header = self.data.header
self.canvas.axes.set_xlabel(
table_headers[header[0]], color='black', fontsize=10
self._table_headers[header[0]], color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
table_headers[header[1]], color='black', fontsize=10
self._table_headers[header[1]], color='black', fontsize=10
)
self.canvas.axes.autoscale_view(True, True, True)
Loaded 100 of 170 files, more files were not shown because too many files have changed in this diff. Show more