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 ### ### CUSTOM ###
*.exe *.exe
*.msi
*.tar.xz *.tar.xz
*.zip *.zip
*.spec *.spec
@@ -9,15 +10,33 @@
tmp tmp
*.tar
*.tar.* *.tar.*
*.tgz
*.zip *.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 pamhyrBuild
wine-pamhyr-build* wine-pamhyr-build*
_PAMHYR_ _PAMHYR_
__old__ __old__
*-venv
### END CUSTOM ### ### END CUSTOM ###
# Created by https://www.toptal.com/developers/gitignore/api/python # Created by https://www.toptal.com/developers/gitignore/api/python
@@ -309,3 +328,318 @@ compile_commands.json
*_qmlcache.qrc *_qmlcache.qrc
# End of https://www.toptal.com/developers/gitignore/api/qt # 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/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
stages: stages:
- downloads
- build - build
- test - test
- package - package
- release - 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 #
######### #########
build-mage-linux: set-version:
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:
stage: build stage: build
tags: tags:
- linux - linux
@@ -126,60 +85,240 @@ build-lang:
paths: paths:
- src/lang/*.qm - 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 # # TESTS #
######### #########
test: # test-windows:
stage: test # stage: test
script: # tags:
- echo "TODO PAMHYR tests" # - 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 # # PACKAGES #
############ ############
linux-package: linux-package-tar:
stage: package stage: package
tags: tags:
- release - release
- linux - linux
needs: needs:
- job: build-lang # - job: test-linux
artifacts: true - job: build-linux
- job: build
artifacts: true
- job: build-mage-linux
artifacts: true artifacts: true
rules: rules:
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG - if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_TAG
artifacts: artifacts:
paths: paths:
- packages/pamhyr-gnulinux-amd64.tar.xz - linux/pamhyr-gnulinux.tar.xz
script: script:
- cd packages - cd linux
- ./linux.sh - 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 stage: package
tags: tags:
- release - release
- wine - wine
needs: needs:
- job: build-lang # - job: test-windows
artifacts: true - job: build-windows
- job: build
artifacts: true
- job: build-mage-windows
artifacts: true artifacts: true
rules: rules:
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG - if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_TAG
artifacts: artifacts:
paths: paths:
- packages/pamhyr-win-amd64.exe - windows/pamhyr-windows.exe
script: script:
- cd packages - cd windows
- ./windows.bat - copy ..\packages\make-windows-exe.bat make-windows-exe.bat
- copy ..\packages\pamhyr.nsi pamhyr.nsi
- .\make-windows-exe.bat
########### ###########
# RELEASE # # RELEASE #
@@ -191,16 +330,25 @@ tag-release:
- release - release
- linux - linux
needs: needs:
- job: linux-package - job: linux-package-tar
artifacts: true 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 artifacts: true
rules: rules:
- if: $CI_COMMIT_TAG - if: $CI_COMMIT_TAG
artifacts: artifacts:
paths: paths:
- packages/pamhyr-gnulinux-amd64.tar.xz - linux/pamhyr-gnulinux.tar.xz
- packages/pamhyr-win-amd64.exe - windows/pamhyr-windows.zip
- windows/pamhyr-windows.exe
- doc/dev/documentation.pdf
- doc/users/documentation.pdf
script: script:
- cd packages - cd packages
release: release:
@@ -210,11 +358,23 @@ tag-release:
ref: '$CI_COMMIT_SHA' ref: '$CI_COMMIT_SHA'
assets: assets:
links: links:
- name: 'GNU/Linux amd64 (tar.xz)' - name: 'GNU/Linux (tar.xz)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-gnulinux-amd64.tar.xz' url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/linux/pamhyr-gnulinux.tar.xz'
filepath: '/packages/pamhyr-gnulinux-amd64.tar.xz' filepath: '/linux/pamhyr-gnulinux.tar.xz'
link_type: 'package' link_type: 'package'
- name: 'Windows amd64 (exe)' - name: 'Windows (exe)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-win-amd64.exe' url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/windows/pamhyr-windows.exe'
filepath: '/packages/pamhyr-win-amd64.exe' filepath: '/windows/pamhyr-windows.exe'
link_type: 'package' 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 # Run pamhyr
./src/pamhyr.py ./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" echo " *** COPY DATA"
mkdir -p dist/pamhyr/View/ui mkdir -p dist/pamhyr/_internal/View/ui
mkdir -p dist/pamhyr/View/ui/Widgets mkdir -p dist/pamhyr/_internal/View/ui/Widgets
cp -r ../src/View/ui/ressources/ dist/pamhyr/View/ui/ cp -r ../src/View/ui/ressources/ dist/pamhyr/_internal/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/View/ui/Widgets/ cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/_internal/View/ui/Widgets/
cp -r ../src/View/ui/*.ui dist/pamhyr/View/ui/ cp -r ../src/View/ui/*.ui dist/pamhyr/_internal/View/ui/
mkdir -p dist/pamhyr/lang mkdir -p dist/pamhyr/lang
cp -r ../src/lang/*.qm dist/pamhyr/lang/ cp -r ../src/lang/*.qm dist/pamhyr/lang/
cp ../VERSION dist/pamhyr/ mkdir dist/pamhyr/_internal/
cp ../AUTHORS dist/pamhyr/ #cp ../VERSION dist/pamhyr/_internal/
cp ../LICENSE dist/pamhyr/ 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/ mkdir -p dist/pamhyr/mage/
cp ../mage/mage 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 mkdir -p dist/pamhyr/tests_cases/Saar
cp ../tests_cases/Saar/Saar.pamhyr 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" echo " *** MAKE SRC PACKAGE"
OLD_PWD=$PWD OLD_PWD=$PWD
@@ -76,11 +91,11 @@ mv ../pamhyr-src.tar.gz ./
echo " *** MAKE BIN PACKAGE" echo " *** MAKE BIN PACKAGE"
OLD_PWD=$PWD # OLD_PWD=$PWD
cd dist/ # cd dist/
tar --xz -cf pamhyr-gnulinux-amd64.tar.xz pamhyr --checkpoint=.100 # tar --xz -cf pamhyr-gnulinux-amd64.tar.xz pamhyr --checkpoint=.100
cd $OLD_PWD # cd $OLD_PWD
mv dist/pamhyr-gnulinux-amd64.tar.xz ./ # mv dist/pamhyr-gnulinux-amd64.tar.xz ./
echo " *** DONE" 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 LIC_NAME "LICENSE"
!define APP_NAME "PAMHYR" !define APP_NAME "PAMHYR"
Name "PAMHYR" Name "Pamhyr2"
OutFile "pamhyr-win-amd64.exe" OutFile "pamhyr-windows.exe"
LicenseData "..\LICENSE" LicenseData "pamhyr\pamhyr\_internal\LICENSE"
LicenseText "I Agree" LicenseText "I Agree"
RequestExecutionLevel admin RequestExecutionLevel admin
Unicode True Unicode True
InstallDir $PROGRAMFILES\PAMHYR InstallDir $PROGRAMFILES\Pamhyr2
InstallDirRegKey HKLM "Software\PAMHYR" "Install_Dir" InstallDirRegKey HKLM "Software\Pamhyr2" "Install_Dir"
Page license Page license
Page components Page components
@@ -23,35 +23,39 @@ Page instfiles
UninstPage uninstConfirm UninstPage uninstConfirm
UninstPage instfiles UninstPage instfiles
Section "PAMHYR" Section "Pamhyr2"
SectionIn RO SectionIn RO
SetOutPath $INSTDIR 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\Pamhyr2" "DisplayName" "NSIS Pamhyr2"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "UninstallString" "$INSTDIR\uninstall.exe" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "NoRepair" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "NoRepair" 1
WriteUninstaller "$INSTDIR\uninstall.exe" WriteUninstaller "$INSTDIR\uninstall.exe"
SectionEnd SectionEnd
Section "Start Menu Shortcuts" 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" Section "Desktop Shortcuts"
CreateShortcut "$SMPROGRAMS\PAMHYR\Uninstall.lnk" "$INSTDIR\uninstall.exe" SetShellVarContext all
CreateShortcut "$SMPROGRAMS\PAMHYR\PAMHYR.lnk" "$INSTDIR\pamhyr.exe" CreateShortcut "$desktop\Pamhyr2.lnk" "$instdir\pamhyr\pamhyr.exe"
SectionEnd SectionEnd
Section "Uninstall" Section "Uninstall"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2"
DeleteRegKey HKLM SOFTWARE\NSIS_PAMHYR 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" RMDir /R "$INSTDIR"
SectionEnd SectionEnd
+45 -16
View File
@@ -81,13 +81,42 @@ wine pyinstaller ../src/pamhyr.py -y
echo " *** COPY DATA" echo " *** COPY DATA"
mkdir -p dist/pamhyr/View/ui mkdir -p pamhyr
cp -r ../src/View/ui/ressources/ dist/pamhyr/View/ui/ mkdir -p pamhyr/pamhyr
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/View/ui/
cp -r ../src/View/ui/*.ui dist/pamhyr/View/ui/
cp ../VERSION dist/pamhyr/ # Copy Pamhyr info
cp ../AUTHORS dist/pamhyr/ 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 # Update TS and build QM files
OLD_PWD=$PWD OLD_PWD=$PWD
@@ -102,19 +131,19 @@ echo " *** MAKE PACKAGES"
echo " TAR pamhyr-win-amd64.tar.xz" echo " TAR pamhyr-win-amd64.tar.xz"
OLD_PWD=$PWD # OLD_PWD=$PWD
cd dist/ # cd dist/
tar --xz -cf pamhyr-win-amd64.tar.xz pamhyr --checkpoint=.100 # tar --xz -cf pamhyr-win-amd64.tar.xz pamhyr --checkpoint=.100
cd $OLD_PWD # cd $OLD_PWD
mv dist/pamhyr-win-amd64.tar.xz ./ # mv dist/pamhyr-win-amd64.tar.xz ./
echo " ZIP pamhyr-win-amd64.zip" echo " ZIP pamhyr-win-amd64.zip"
OLD_PWD=$PWD # OLD_PWD=$PWD
cd dist/ # cd dist/
zip -r pamhyr-win-amd64.zip pamhyr/ # zip -r pamhyr-win-amd64.zip pamhyr/
cd $OLD_PWD # cd $OLD_PWD
mv dist/pamhyr-win-amd64.zip ./ # mv dist/pamhyr-win-amd64.zip ./
echo " *** MAKE INSTALLER" echo " *** MAKE INSTALLER"
+2 -1
View File
@@ -1,6 +1,7 @@
PyQt5==5.15.4 PyQt5==5.15.4
PyQt5-Qt5==5.15.2 PyQt5-Qt5==5.15.2
PyQt5-sip==12.8.1 PyQt5-sip==12.12.2
PyQtWebEngine==5.15.6
pyqtgraph>=0.12.1 pyqtgraph>=0.12.1
matplotlib>=3.4.1 matplotlib>=3.4.1
numpy>=1.24.2 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 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 from Model.Except import NotImplementedMethodeError
logger = logging.getLogger() logger = logging.getLogger()
@@ -19,7 +19,7 @@
from copy import copy from copy import copy
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrList import PamhyrModelListWithTab
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
@@ -30,30 +30,12 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
Solid, Solid,
) )
class BoundaryConditionList(SQLSubModel): class BoundaryConditionList(PamhyrModelListWithTab):
_tabs_list = ["liquid", "solid", "suspenssion"]
_sub_classes = [ _sub_classes = [
BoundaryCondition, 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 @classmethod
def _sql_load(cls, execute, data = None): def _sql_load(cls, execute, data = None):
new = cls(status = data['status']) new = cls(status = data['status'])
@@ -82,66 +64,12 @@ class BoundaryConditionList(SQLSubModel):
return True 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): def new(self, lst, index):
n = NotDefined(status=self._status) n = NotDefined(status=self._status)
self._tabs[lst].insert(index, n) self._tabs[lst].insert(index, n)
self._status.modified() self._status.modified()
return n 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): def __copy__(self):
new = BoundaryConditionList() new = BoundaryConditionList()
+1 -1
View File
@@ -18,7 +18,7 @@
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
class Friction(SQLSubModel): class Friction(SQLSubModel):
def __init__(self, name:str = "", status = None): def __init__(self, name:str = "", status = None):
+8 -67
View File
@@ -21,26 +21,18 @@ import logging
from copy import copy from copy import copy
from tools import trace, timer 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 from Model.Friction.Friction import Friction
logger = logging.getLogger() logger = logging.getLogger()
class FrictionList(SQLSubModel): class FrictionList(PamhyrModelList):
_sub_classes = [ _sub_classes = [
Friction 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 @classmethod
def _sql_update_0_0_1(cls, execute, version): def _sql_update_0_0_1(cls, execute, version):
execute("ALTER TABLE `section` RENAME TO `friction`") execute("ALTER TABLE `section` RENAME TO `friction`")
@@ -57,7 +49,7 @@ class FrictionList(SQLSubModel):
def _sql_load(cls, execute, data = None): def _sql_load(cls, execute, data = None):
new = cls(status = data['status']) new = cls(status = data['status'])
new._frictions = Friction._sql_load( new._lst = Friction._sql_load(
execute, data execute, data
) )
@@ -69,70 +61,19 @@ class FrictionList(SQLSubModel):
ok = True ok = True
ind = 0 ind = 0
for friction in self._frictions: for friction in self._lst:
data["ind"] = ind data["ind"] = ind
ok &= friction._sql_save(execute, data = data) ok &= friction._sql_save(execute, data = data)
ind += 1 ind += 1
return ok return ok
def __len__(self):
return len(self._frictions)
@property @property
def frictions(self): def frictions(self):
return self._frictions.copy() return self.lst
def get(self, row):
return self._frictions[row]
def set(self, row, new):
self._frictions[row] = new
self._status.modified()
def new(self, index): def new(self, index):
n = Friction(status = self._status) n = Friction(status = self._status)
self._frictions.insert(index, n) self._lst.insert(index, n)
self._status.modified() self._status.modified()
return n 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): def sl(self, sl):
if sl == self._profile.sl: if sl == self._profile.sl:
self._sl = None self._sl = None
else:
self._sl = sl self._sl = sl
@property @property
def name(self): def name(self):
+1 -1
View File
@@ -19,7 +19,7 @@
from math import dist from math import dist
import numpy as np import numpy as np
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
from Model.Geometry.Point import Point from Model.Geometry.Point import Point
class PointXYZ(Point, SQLSubModel): class PointXYZ(Point, SQLSubModel):
+3
View File
@@ -56,6 +56,9 @@ class Profile(object):
self._profile_type = _type self._profile_type = _type
def __len__(self):
return len(self._points)
@property @property
def number_points(self): def number_points(self):
return len(self._points) return len(self._points)
+1 -1
View File
@@ -21,7 +21,7 @@ from typing import List
from tools import timer from tools import timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import ClipboardFormatError from Model.Except import ClipboardFormatError
from Model.Geometry.Profile import Profile from Model.Geometry.Profile import Profile
from Model.Geometry.PointXYZ import PointXYZ 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 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.Profile import Profile
from Model.Geometry.ProfileXYZ import ProfileXYZ from Model.Geometry.ProfileXYZ import ProfileXYZ
@@ -42,7 +42,7 @@ class Reach(SQLSubModel):
] ]
def __init__(self, status=None, parent=None): 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._status = status
self._parent = parent self._parent = parent
self._profiles: List[Profile] = [] self._profiles: List[Profile] = []
@@ -106,6 +106,9 @@ class Reach(SQLSubModel):
return self._parent.name return self._parent.name
def __len__(self):
return len(self._profiles)
@property @property
def profiles(self): def profiles(self):
return self._profiles.copy() return self._profiles.copy()
@@ -20,7 +20,7 @@ from copy import copy, deepcopy
from tools import trace, timer from tools import trace, timer
from functools import reduce from functools import reduce
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
class Data(SQLSubModel): class Data(SQLSubModel):
def __init__(self, name:str = "", def __init__(self, name:str = "",
@@ -19,29 +19,14 @@
from copy import copy from copy import copy
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrDict import PamhyrModelDict
from Model.InitialConditions.InitialConditions import InitialConditions from Model.InitialConditions.InitialConditions import InitialConditions
class InitialConditionsDict(SQLSubModel): class InitialConditionsDict(PamhyrModelDict):
_sub_classes = [ _sub_classes = [
InitialConditions, 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 @classmethod
def _sql_load(cls, execute, data = None): def _sql_load(cls, execute, data = None):
new = cls(status = data["status"]) new = cls(status = data["status"])
@@ -55,7 +40,7 @@ class InitialConditionsDict(SQLSubModel):
) )
if ic is not None: if ic is not None:
new._reach[reach] = ic new._dict[reach] = ic
return new return new
@@ -66,30 +51,12 @@ class InitialConditionsDict(SQLSubModel):
execute("DELETE FROM initial_conditions") execute("DELETE FROM initial_conditions")
for reach in self._reach: for reach in self._dict:
data["reach"] = reach data["reach"] = reach
ok &= self._reach[reach]._sql_save(execute, data) ok &= self._dict[reach]._sql_save(execute, data)
return ok 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): def new(self, reach):
new = InitialConditions(reach = reach, status = self._status) new = InitialConditions(reach = reach, status = self._status)
self.set(reach, new) self.set(reach, new)
@@ -20,7 +20,7 @@ import logging
from tools import trace, timer, old_pamhyr_date_to_timestamp 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 from Model.Except import NotImplementedMethodeError
logger = logging.getLogger() logger = logging.getLogger()
@@ -19,7 +19,7 @@
from copy import copy from copy import copy
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrList import PamhyrModelListWithTab
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.LateralContribution.LateralContribution import LateralContribution from Model.LateralContribution.LateralContribution import LateralContribution
@@ -27,30 +27,12 @@ from Model.LateralContribution.LateralContributionTypes import (
NotDefined, LateralContrib, Rain, Evaporation, NotDefined, LateralContrib, Rain, Evaporation,
) )
class LateralContributionList(SQLSubModel): class LateralContributionList(PamhyrModelListWithTab):
_tabs_list = ["liquid", "solid", "suspenssion"]
_sub_classes = [ _sub_classes = [
LateralContribution, 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 @classmethod
def _sql_load(cls, execute, data = None): def _sql_load(cls, execute, data = None):
new = cls(status = data['status']) new = cls(status = data['status'])
@@ -79,66 +61,12 @@ class LateralContributionList(SQLSubModel):
return True 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): def new(self, lst, index):
n = NotDefined(status=self._status) n = NotDefined(status=self._status)
self._tabs[lst].insert(index, n) self._tabs[lst].insert(index, n)
self._status.modified() self._status.modified()
return n 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): def __copy__(self):
new = LateralContributionList() new = LateralContributionList()
+4
View File
@@ -43,6 +43,10 @@ class Results(object):
def river(self): def river(self):
return self._river return self._river
@property
def study(self):
return self._study
def set(self, key, value): def set(self, key, value):
self._meta_data[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._profile = profile # Source profile in the study
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...} self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
def __len__(self):
return len(self._data)
@property @property
def name(self): def name(self):
return self._profile.name return self._profile.name
@@ -66,6 +69,9 @@ class Reach(object):
) )
) )
def __len__(self):
return len(self._profiles)
@property @property
def name(self): def name(self):
return self._reach.name return self._reach.name
@@ -91,6 +97,9 @@ class River(object):
# Dict with timestamps as key # Dict with timestamps as key
self._reachs = [] self._reachs = []
def __len__(self):
return len(self._reachs)
@property @property
def reachs(self): def reachs(self):
return self._reachs.copy() return self._reachs.copy()
+1 -5
View File
@@ -18,7 +18,7 @@
from tools import flatten from tools import flatten
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
from Model.Network.Node import Node from Model.Network.Node import Node
from Model.Network.Edge import Edge from Model.Network.Edge import Edge
@@ -311,10 +311,6 @@ class River(Graph, SQLSubModel):
self._save_submodel(execute, objs, data) self._save_submodel(execute, objs, data)
return True return True
@property
def frictions(self):
return self._frictions
@property @property
def boundary_condition(self): def boundary_condition(self):
return self._boundary_condition return self._boundary_condition
+1 -1
View File
@@ -2,7 +2,7 @@
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
class Layer(SQLSubModel): class Layer(SQLSubModel):
+5 -66
View File
@@ -2,35 +2,18 @@
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrList import PamhyrModelList
from Model.SedimentLayer.SedimentLayer import SedimentLayer from Model.SedimentLayer.SedimentLayer import SedimentLayer
class SedimentLayerList(SQLSubModel): class SedimentLayerList(PamhyrModelList):
_sub_classes = [SedimentLayer] _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 @classmethod
def _sql_load(cls, execute, data = None): def _sql_load(cls, execute, data = None):
new = cls(status = data["status"]) new = cls(status = data["status"])
new._sl = SedimentLayer._sql_load(execute, data) new._lst = SedimentLayer._sql_load(execute, data)
return new return new
@@ -41,61 +24,17 @@ class SedimentLayerList(SQLSubModel):
execute("DELETE FROM sedimentary_layer") execute("DELETE FROM sedimentary_layer")
execute("DELETE FROM sedimentary_layer_layer") execute("DELETE FROM sedimentary_layer_layer")
for sl in self._sl: for sl in self._lst:
ok &= sl._sql_save(execute, data) ok &= sl._sql_save(execute, data)
return ok return ok
@property @property
def sediment_layers(self): def sediment_layers(self):
return self._sl.copy() return self.lst
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()
def new(self, index): def new(self, index):
n = SedimentLayer(status = self._status) n = SedimentLayer(status = self._status)
self.insert(index, n) self.insert(index, n)
self._status.modified() self._status.modified()
return n 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 tools import trace, timer
from Solver.Solvers import solver_type_list from Solver.Solvers import solver_type_list
from Model.DB import SQLSubModel from Model.Tools.PamhyrList import PamhyrModelList
class Parameter(): class Parameter():
def __init__(self, def __init__(self,
@@ -63,13 +63,13 @@ class Parameter():
return new return new
class SolverParametersList(SQLSubModel): class SolverParametersList(PamhyrModelList):
def __init__(self, solver_type = None, status = None): def __init__(self, solver_type = None, status = None):
super(SolverParametersList, self).__init__() super(SolverParametersList, self).__init__()
self._status = status self._status = status
self._solver = solver_type self._solver = solver_type
self._parameters = list( self._lst = list(
map( map(
lambda t: Parameter.from_tuple(t, self._status), lambda t: Parameter.from_tuple(t, self._status),
solver_type.default_parameters() solver_type.default_parameters()
@@ -194,7 +194,7 @@ class SolverParametersList(SQLSubModel):
) )
ind = 0 ind = 0
for param in self._parameters: for param in self._lst:
sql = ( sql = (
"INSERT INTO " + "INSERT INTO " +
"solver_parameter(ind, name, value, solver) "+ "solver_parameter(ind, name, value, solver) "+
@@ -210,39 +210,36 @@ class SolverParametersList(SQLSubModel):
return True return True
def __len__(self):
return len(self._parameters)
@property @property
def parameters(self): def parameters(self):
return self._parameters.copy() return self.lst
def get(self, index): def get(self, index):
return self._parameters[index] return self._lst[index]
def get_by_key(self, key): def get_by_key(self, key):
try: try:
return next( return next(
filter( filter(
lambda p: p["name"] == key, lambda p: p["name"] == key,
self._parameters self._lst
) )
)["value"] )["value"]
except: except:
return None return None
def set(self, index, new): def set(self, index, new):
self._parameters[index] = new self._lst[index] = new
self._status.modified() self._status.modified()
def set_value(self, key, value): def set_value(self, key, value):
for p in self._parameters: for p in self._lst:
if p["name"] == key: if p["name"] == key:
p["value"] = value p["value"] = value
self._status.modified() self._status.modified()
return return
self._parameters.append( self._lst.append(
Parameter( Parameter(
name = key, name = key,
value = value, value = value,
@@ -253,47 +250,6 @@ class SolverParametersList(SQLSubModel):
def new(self, index): def new(self, index):
n = Parameter(status = self._status) n = Parameter(status = self._status)
self._parameters.insert(index, n) self._lst.insert(index, n)
self._status.modified() self._status.modified()
return n 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 tools import trace, timer
from Model.DB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
class Stricklers(SQLSubModel): class Stricklers(SQLSubModel):
_id_cnt = 0 _id_cnt = 0
+8 -49
View File
@@ -18,22 +18,15 @@
from tools import trace, timer from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Saved import SavedStatus from Model.Saved import SavedStatus
from Model.Tools.PamhyrList import PamhyrModelList
from Model.Stricklers.Stricklers import Stricklers from Model.Stricklers.Stricklers import Stricklers
class StricklersList(SQLSubModel): class StricklersList(PamhyrModelList):
_sub_classes = [ _sub_classes = [
Stricklers, Stricklers,
] ]
def __init__(self, status = None):
if status is None:
status = SavedStatus()
self._status = status
self._stricks = []
@classmethod @classmethod
def _sql_create(cls, execute): def _sql_create(cls, execute):
return cls._create_submodel(execute) return cls._create_submodel(execute)
@@ -46,7 +39,7 @@ class StricklersList(SQLSubModel):
def _sql_load(cls, execute, data = None): def _sql_load(cls, execute, data = None):
new = cls(status = data["status"]) new = cls(status = data["status"])
new._stricks = Stricklers._sql_load( new._lst = Stricklers._sql_load(
execute, execute,
data = data data = data
) )
@@ -56,60 +49,26 @@ class StricklersList(SQLSubModel):
def _sql_save(self, execute, data = None): def _sql_save(self, execute, data = None):
execute("DELETE FROM stricklers") execute("DELETE FROM stricklers")
objs = self._stricks objs = self._lst
return self._save_submodel(execute, objs, data) return self._save_submodel(execute, objs, data)
def __len__(self):
return len(self._stricks)
@property @property
def stricklers(self): def stricklers(self):
return self._stricks.copy() return self.lst
def get(self, index): def new(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):
s = Stricklers(status = self._status) s = Stricklers(status = self._status)
self.insert(index, s) self.insert(index, s)
return 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 @timer
def sort(self, reverse:bool = False, key=None): def sort(self, reverse:bool = False, key=None):
f = lambda st: st.name f = lambda st: st.name
if key is not None: if key is not None:
f = key f = key
self._stricks = sorted( self._lst = sorted(
self._stricks, self._lst,
key = f, key = f,
reverse = reverse, reverse = reverse,
) )
+1 -1
View File
@@ -23,7 +23,7 @@ from datetime import datetime
from tools import timer, timestamp from tools import timer, timestamp
from Model.DB import SQLModel from Model.Tools.PamhyrDB import SQLModel
from Model.Saved import SavedStatus from Model.Saved import SavedStatus
from Model.Serializable import Serializable from Model.Serializable import Serializable
from Model.Except import NotImplementedMethodeError 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 # Copyright (C) 2023 INRAE
# #
# This program is free software: you can redistribute it and/or modify # 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 from tools import timer
try: try:
# Installation allow Unix-like signal
from signal import SIGTERM, SIGSTOP, SIGCONT from signal import SIGTERM, SIGSTOP, SIGCONT
_signal = True _signal = True
except: except:
@@ -201,7 +202,7 @@ class AbstractSolver(object):
return os.path.abspath( return os.path.abspath(
os.path.join( os.path.join(
os.path.dirname(__file__), os.path.dirname(__file__),
".." "..", ".."
) )
) )
@@ -216,7 +217,6 @@ class AbstractSolver(object):
The executable and list of arguments The executable and list of arguments
""" """
# HACK: Works in most case... Trust me i'm an engineer # HACK: Works in most case... Trust me i'm an engineer
cmd = cmd.replace("@install_dir", self._install_dir()) cmd = cmd.replace("@install_dir", self._install_dir())
cmd = cmd.replace("@path", path.replace(" ", "\ ")) cmd = cmd.replace("@path", path.replace(" ", "\ "))
cmd = cmd.replace("@input", self.input_param()) cmd = cmd.replace("@input", self.input_param())
@@ -358,6 +358,7 @@ class AbstractSolver(object):
def start(self, study, process = None): def start(self, study, process = None):
if _signal: if _signal:
# Solver is PAUSED, so continue execution
if self._status == STATUS.PAUSED: if self._status == STATUS.PAUSED:
os.kill(self._process.pid(), SIGCONT) os.kill(self._process.pid(), SIGCONT)
self._status = STATUS.RUNNING self._status = STATUS.RUNNING
@@ -371,6 +372,7 @@ class AbstractSolver(object):
if self._process is None: if self._process is None:
return False return False
# Send SIGSTOP to PAUSED solver
os.kill(self._process.pid(), SIGSTOP) os.kill(self._process.pid(), SIGSTOP)
self._status = STATUS.PAUSED self._status = STATUS.PAUSED
return True return True
+3 -3
View File
@@ -24,9 +24,9 @@ from Solver.Mage import Mage7, Mage8
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
solver_long_name = { solver_long_name = {
"generic": _translate("Solvers", "Generic"), "generic": "Generic",
# "mage7": _translate("Solvers", "Mage version 7"), # "mage7": "Mage v7",
"mage8": _translate("Solvers", "Mage version 8"), "mage8": "Mage v8",
} }
solver_type_list = { solver_type_list = {
+13 -6
View File
@@ -19,14 +19,17 @@
import os import os
import logging import logging
from View.ASubWindow import ASubWindow from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class AboutWindow(ASubWindow): class AboutWindow(PamhyrDialog):
_pamhyr_ui = "about"
_pamhyr_name = "About"
def _path_file(self, filename): def _path_file(self, filename):
return os.path.abspath( return os.path.abspath(
os.path.join( os.path.join(
@@ -35,10 +38,14 @@ class AboutWindow(ASubWindow):
) )
) )
def __init__(self, study = None, config = None, parent=None):
def __init__(self, title="About", parent=None): super(AboutWindow, self).__init__(
super(AboutWindow, self).__init__(name=title, ui="about", parent=parent) title = _translate("About", "About"),
self.ui.setWindowTitle(title) study = study,
config = config,
options = [],
parent = parent
)
# Version # Version
with open(self._path_file("VERSION"), "r") as f: with open(self._path_file("VERSION"), "r") as f:
+14 -10
View File
@@ -21,27 +21,31 @@ import logging
from datetime import datetime from datetime import datetime
from tools import timer, trace from tools import timer, trace
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
) )
from View.BoundaryCondition.Edit.translate import * from View.BoundaryCondition.Edit.translate import BCETranslate
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class Plot(APlot): class Plot(PamhyrPlot):
def __init__(self, canvas=None, data=None, def __init__(self, mode = "time", data=None,
mode = "time", toolbar=None): trad=None, canvas=None, toolbar=None,
parent=None):
super(Plot, self).__init__( super(Plot, self).__init__(
canvas=canvas, canvas = canvas,
data=data, trad = trad,
toolbar=toolbar data = data,
toolbar = toolbar,
parent = parent
) )
self._table_headers = self._trad.get_dict("table_headers")
self._mode = mode self._mode = mode
def custom_ticks(self): def custom_ticks(self):
@@ -109,10 +113,10 @@ class Plot(APlot):
# Plot label # Plot label
header = self.data.header header = self.data.header
self.canvas.axes.set_xlabel( 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( 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) 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 tools import trace, timer
from View.ASubWindow import ASubMainWindow, AWidget from View.Tools.PamhyrTable import PamhyrTableModel
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, Qt, QVariant, QAbstractTableModel,
@@ -47,131 +46,12 @@ from View.BoundaryCondition.Edit.UndoCommand import (
SortCommand, MoveCommand, PasteCommand, SortCommand, MoveCommand, PasteCommand,
DuplicateCommand, DuplicateCommand,
) )
from View.BoundaryCondition.Edit.translate import *
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class ExtendedTimeEdit(AWidget): class TableModel(PamhyrTableModel):
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)
def data(self, index, role): def data(self, index, role):
if role == Qt.TextAlignmentRole: if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter return Qt.AlignHCenter | Qt.AlignVCenter
@@ -189,7 +69,7 @@ class TableModel(QAbstractTableModel):
if self._data.get_type_column(column) == float: if self._data.get_type_column(column) == float:
value = f"{v:.4f}" value = f"{v:.4f}"
elif self._data.header[column] == "time": elif self._data.header[column] == "time":
if self._mode == "time": if self._opt_data == "time":
t0 = datetime.fromtimestamp(0) t0 = datetime.fromtimestamp(0)
t = datetime.fromtimestamp(v) t = datetime.fromtimestamp(v)
value = str(t - t0) value = str(t - t0)
@@ -200,12 +80,6 @@ class TableModel(QAbstractTableModel):
return value 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): def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
@@ -315,12 +189,3 @@ class TableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.layoutChanged.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 -*- # -*- coding: utf-8 -*-
import logging
from tools import timer, trace from tools import timer, trace
from View.ASubWindow import ASubMainWindow, AWidget from View.Tools.PamhyrWindow import PamhyrWindow
from View.ListedSubWindow import ListedSubWindow from View.Tools.PamhyrWidget import PamhyrWidget
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -38,26 +41,28 @@ from PyQt5.QtWidgets import (
QHeaderView, QDoubleSpinBox, QVBoxLayout, QHeaderView, QDoubleSpinBox, QVBoxLayout,
) )
from View.Plot.MplCanvas import MplCanvas from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT 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.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 from View.BoundaryCondition.Edit.Plot import Plot
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class WD50Sigma(AWidget): logger = logging.getLogger()
class WD50Sigma(PamhyrWidget):
_pamhyr_ui = "d50sigma"
d50Changed = pyqtSignal(float) d50Changed = pyqtSignal(float)
sigmaChanged = pyqtSignal(float) sigmaChanged = pyqtSignal(float)
def __init__(self, parent=None): def __init__(self, parent=None):
super(WD50Sigma, self).__init__( super(WD50Sigma, self).__init__(
ui="d50sigma",
parent=parent parent=parent
) )
self.parent = parent
self.spinBox_d50 = self.find(QDoubleSpinBox, "doubleSpinBox_d50") self.spinBox_d50 = self.find(QDoubleSpinBox, "doubleSpinBox_d50")
self.spinBox_sigma = self.find(QDoubleSpinBox, "doubleSpinBox_sigma") self.spinBox_sigma = self.find(QDoubleSpinBox, "doubleSpinBox_sigma")
@@ -89,46 +94,39 @@ class WD50Sigma(AWidget):
def valueChangedSigma(self, value): def valueChangedSigma(self, value):
self.sigmaChanged.emit(value) self.sigmaChanged.emit(value)
class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow): class EditBoundaryConditionWindow(PamhyrWindow):
def __init__(self, title="Edit boundary condition", _pamhyr_ui = "EditBoundaryConditions"
data=None, study=None, parent=None): _pamhyr_name = "Edit Boundary Conditions"
self._data = data
self._study = study
self._title = title
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__( 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_table()
self.setup_plot() self.setup_plot()
self.setup_data() self.setup_data()
self.setup_connections() 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): def setup_data(self):
self._is_solid = self._data.bctype == "SL" self._is_solid = self._data.bctype == "SL"
@@ -141,23 +139,29 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self._d50sigma.set_sigma(self._data.sigma) self._d50sigma.set_sigma(self._data.sigma)
def setup_table(self): def setup_table(self):
table = self.find(QTableView, "tableView") headers = {}
self._table = TableModel( 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, 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": table = self.find(QTableView, "tableView")
self._delegate_time = ExTimeDelegate( self._table = TableModel(
data = self._data, table_view = table,
mode = self._study.time_system, table_headers = headers,
parent = self editable_headers = self._data.header,
) delegates = {
"time": self._delegate_time,
table.setItemDelegateForColumn( },
0, self._delegate_time data = self._data,
) undo = self._undo_stack,
opt_data = self._study.time_system
)
table.setModel(self._table) table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows) table.setSelectionBehavior(QAbstractItemView.SelectRows)
@@ -167,7 +171,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def setup_plot(self): def setup_plot(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100) self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas") self.canvas.setObjectName("canvas")
self.toolbar = PamHyrNavigationToolbar2QT( self.toolbar = PamhyrPlotToolbar(
self.canvas, self self.canvas, self
) )
self.verticalLayout.addWidget(self.toolbar) self.verticalLayout.addWidget(self.toolbar)
@@ -177,21 +181,16 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
canvas = self.canvas, canvas = self.canvas,
data = self._data, data = self._data,
mode = self._study.time_system, mode = self._study.time_system,
trad = self._trad,
toolbar = self.toolbar, toolbar = self.toolbar,
) )
self.plot.draw() self.plot.draw()
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort) 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) self._table.dataChanged.connect(self.update)
if self._is_solid: if self._is_solid:
@@ -240,7 +239,6 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
) )
) )
def add(self): def add(self):
rows = self.index_selected_rows() rows = self.index_selected_rows()
if len(self._data) == 0 or len(rows) == 0: if len(self._data) == 0 or len(rows) == 0:
@@ -273,7 +271,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.plot.update() self.plot.update()
def copy(self): def _copy(self):
rows = self.index_selected_rows() rows = self.index_selected_rows()
table = [] table = []
@@ -285,7 +283,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.copyTableIntoClipboard(table) self.copyTableIntoClipboard(table)
def paste(self): def _paste(self):
header, data = self.parseClipboardTable() header, data = self.parseClipboardTable()
if len(data) == 0: if len(data) == 0:
@@ -299,12 +297,12 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self._table.paste(row, header, data) self._table.paste(row, header, data)
self.plot.update() self.plot.update()
def undo(self): def _undo(self):
self._table.undo() self._table.undo()
self.plot.update() self.plot.update()
self.widget_update() self.widget_update()
def redo(self): def _redo(self):
self._table.redo() self._table.redo()
self.plot.update() self.plot.update()
self.widget_update() self.widget_update()
+17 -9
View File
@@ -18,14 +18,22 @@
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from View.BoundaryCondition.translate import BCTranslate
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
table_headers = { class BCETranslate(BCTranslate):
"x": _translate("BoundaryCondition", "X"), def __init__(self):
"y": _translate("BoundaryCondition", "Y"), super(BCETranslate, self).__init__()
"time": _translate("BoundaryCondition", "Time"),
"date": _translate("BoundaryCondition", "Date"), self._sub_dict["table_headers"] = {
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"), "x": _translate("BoundaryCondition", "X"),
"z": _translate("BoundaryCondition", "Z (m)"), "y": _translate("BoundaryCondition", "Y"),
"solid": _translate("BoundaryCondition", "Solid (kg/s)"), "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 TimeOverZ, TimeOverDischarge, ZOverDischarge
) )
from View.Tools.PamhyrTable import PamhyrTableModel
from View.BoundaryCondition.UndoCommand import ( from View.BoundaryCondition.UndoCommand import (
SetNameCommand, SetNodeCommand, SetTypeCommand, SetNameCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SortCommand, AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand, MoveCommand, PasteCommand, DuplicateCommand,
) )
from View.BoundaryCondition.translate import * from View.BoundaryCondition.translate import BC_types
logger = logging.getLogger() logger = logging.getLogger()
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate): 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) super(ComboBoxDelegate, self).__init__(parent)
self._data = data self._data = data
self._mode = mode self._mode = mode
self._tab = tab 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): def createEditor(self, parent, option, index):
self.editor = QComboBox(parent) self.editor = QComboBox(parent)
@@ -64,7 +71,7 @@ class ComboBoxDelegate(QItemDelegate):
if self._mode == "type": if self._mode == "type":
lst = list( lst = list(
map( map(
lambda k: long_types[k], lambda k: self._long_types[k],
filter( filter(
lambda k: self._tab in BC_types[k].compatibility(), lambda k: self._tab in BC_types[k].compatibility(),
BC_types.keys() BC_types.keys()
@@ -104,26 +111,20 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender()) self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel): class TableModel(PamhyrTableModel):
def __init__(self, data=None, undo=None, tab=""): def __init__(self, **kwargs):
super(QAbstractTableModel, self).__init__() super(TableModel, self).__init__(**kwargs)
self._headers = list(table_headers.keys())
self._data = data
self._undo = undo
self._tab = tab
self._bcs = self._data.boundary_condition
def flags(self, index): self._long_types = {}
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable if self._trad is not None:
options |= Qt.ItemIsEditable 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): def rowCount(self, parent):
return self._bcs.len(self._tab) return self._lst.len(self._tab)
def columnCount(self, parent):
return len(self._headers)
def data(self, index, role): def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole: if role != Qt.ItemDataRole.DisplayRole:
@@ -133,24 +134,18 @@ class TableModel(QAbstractTableModel):
column = index.column() column = index.column()
if self._headers[column] == "name": 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": elif self._headers[column] == "type":
t = self._bcs.get(self._tab, row).bctype t = self._lst.get(self._tab, row).bctype
return long_types[t] return self._long_types[t]
elif self._headers[column] == "node": 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: if n is None:
return _translate("BoundaryCondition", "Not associate") return _translate("BoundaryCondition", "Not associate")
return n.name return n.name
return QVariant() 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): def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
@@ -162,20 +157,20 @@ class TableModel(QAbstractTableModel):
if self._headers[column] == "name": if self._headers[column] == "name":
self._undo.push( self._undo.push(
SetNameCommand( SetNameCommand(
self._bcs, self._tab,row, value self._lst, self._tab,row, value
) )
) )
elif self._headers[column] == "type": 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( self._undo.push(
SetTypeCommand( SetTypeCommand(
self._bcs, self._tab,row, BC_types[key] self._lst, self._tab,row, BC_types[key]
) )
) )
elif self._headers[column] == "node": elif self._headers[column] == "node":
self._undo.push( self._undo.push(
SetNodeCommand( SetNodeCommand(
self._bcs, self._tab,row, self._data.node(value) self._lst, self._tab,row, self._data.node(value)
) )
) )
except Exception as e: except Exception as e:
@@ -190,7 +185,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
AddCommand( AddCommand(
self._bcs, self._tab,row self._lst, self._tab,row
) )
) )
@@ -202,7 +197,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._bcs, self._tab,rows self._lst, self._tab, rows
) )
) )
@@ -214,7 +209,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
SortCommand( SortCommand(
self._bcs, self._tab,False self._lst, self._tab, False
) )
) )
@@ -231,7 +226,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push( self._undo_stack.push(
MoveCommand( MoveCommand(
self._bcs, self._tab,"up", row self._lst, self._tab, "up", row
) )
) )
@@ -239,7 +234,7 @@ class TableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()): def move_down(self, index, parent=QModelIndex()):
if row > len(self._bcs): if row > len(self._lst):
return return
target = row target = row
@@ -248,7 +243,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push( self._undo_stack.push(
MoveCommand( 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 tools import trace, timer
from View.ASubWindow import ASubMainWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -56,71 +55,70 @@ from View.BoundaryCondition.Table import (
) )
from View.Network.GraphWidget import GraphWidget from View.Network.GraphWidget import GraphWidget
from View.BoundaryCondition.translate import * from View.BoundaryCondition.translate import BCTranslate
from View.BoundaryCondition.Edit.Window import EditBoundaryConditionWindow from View.BoundaryCondition.Edit.Window import EditBoundaryConditionWindow
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow): class BoundaryConditionWindow(PamhyrWindow):
def __init__(self, title="Boundary conditions", study=None, parent=None): _pamhyr_ui = "BoundaryConditions"
self._title = title + " - " + study.name _pamhyr_name = "Boundary conditions"
def __init__(self, study=None, config=None, parent=None):
name = self._pamhyr_name + " - " + study.name
super(BoundaryConditionWindow, self).__init__( 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._bcs = self._study.river.boundary_condition
self.setup_sc()
self.setup_table() self.setup_table()
self.setup_graph() self.setup_graph()
self.setup_connections() self.setup_connections()
self.ui.setWindowTitle(self._title) 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): def setup_table(self):
self._table = {} self._table = {}
for t in ["liquid", "solid", "suspenssion"]: 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( self._delegate_type = ComboBoxDelegate(
trad = self._trad,
data = self._study.river, data = self._study.river,
mode = "type", mode = "type",
tab = t, tab = t,
parent=self parent=self
) )
self._delegate_node = ComboBoxDelegate( self._delegate_node = ComboBoxDelegate(
trad = self._trad,
data = self._study.river, data = self._study.river,
mode = "node", mode = "node",
tab = t, tab = t,
parent=self parent=self
) )
table.setItemDelegateForColumn( table = self.find(QTableView, f"tableView_{t}")
1, self._delegate_type 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( table.setModel(self._table[t])
2, self._delegate_node
)
table.setSelectionBehavior(QAbstractItemView.SelectRows) table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True) table.setAlternatingRowColors(True)
@@ -141,11 +139,6 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.find(QAction, "action_edit").triggered.connect(self.edit) self.find(QAction, "action_edit").triggered.connect(self.edit)
self.find(QAction, "action_sort").triggered.connect(self.sort) 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): def current_tab(self):
return self.find(QTabWidget, "tabWidget")\ return self.find(QTabWidget, "tabWidget")\
.currentWidget()\ .currentWidget()\
@@ -202,17 +195,17 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
row = self.index_selected_row() row = self.index_selected_row()
self._table[tab].move_down(row) self._table[tab].move_down(row)
def copy(self): def _copy(self):
logger.info("TODO: copy") logger.info("TODO: copy")
def paste(self): def _paste(self):
logger.info("TODO: paste") logger.info("TODO: paste")
def undo(self): def _undo(self):
tab = self.current_tab() tab = self.current_tab()
self._table[tab].undo() self._table[tab].undo()
def redo(self): def _redo(self):
tab = self.current_tab() tab = self.current_tab()
self._table[tab].redo() self._table[tab].redo()
+21 -15
View File
@@ -18,6 +18,8 @@
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from Model.BoundaryCondition.BoundaryConditionTypes import ( from Model.BoundaryCondition.BoundaryConditionTypes import (
NotDefined, PonctualContribution, NotDefined, PonctualContribution,
TimeOverZ, TimeOverDischarge, ZOverDischarge, TimeOverZ, TimeOverDischarge, ZOverDischarge,
@@ -26,21 +28,6 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
_translate = QCoreApplication.translate _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 = { BC_types = {
"ND": NotDefined, "ND": NotDefined,
"PC": PonctualContribution, "PC": PonctualContribution,
@@ -49,3 +36,22 @@ BC_types = {
"ZD": ZOverDischarge, "ZD": ZOverDischarge,
"SL": Solid, "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, QComboBox,
) )
from View.Tools.PamhyrTable import PamhyrTableModel
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class TableModel(QAbstractTableModel): class TableModel(PamhyrTableModel):
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)
def data(self, index, role): def data(self, index, role):
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -81,12 +68,3 @@ class TableModel(QAbstractTableModel):
return self._data[row].summary return self._data[row].summary
return QVariant() 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 tools import trace, timer
from View.ASubWindow import ASubMainWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -41,33 +40,36 @@ from PyQt5.QtWidgets import (
from View.CheckList.Table import TableModel from View.CheckList.Table import TableModel
from View.CheckList.Worker import Worker from View.CheckList.Worker import Worker
from View.CheckList.Translate import CheckListTranslate
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class CheckListWindow(ASubMainWindow, ListedSubWindow): class CheckListWindow(PamhyrWindow):
_pamhyr_ui = "CheckList"
_pamhyr_name = "Check list"
signalStatus = pyqtSignal(str) signalStatus = pyqtSignal(str)
def __init__(self, title="Check list", def __init__(self, autorun:bool=True,
autorun:bool=True,
study=None, config=None, study=None, config=None,
solver=None, parent=None): solver=None, parent=None):
self._title = title + " - " + study.name
self._autorun = autorun self._autorun = autorun
self._study = study
self._config = config
self._solver = solver self._solver = solver
self._parent = parent
name = self._pamhyr_name + " - " + study.name
super(CheckListWindow, self).__init__( 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._checker_list = (
self._study.checkers() + self._solver.checkers() self._study.checkers() +
self._solver.checkers()
) )
self.setup_table() self.setup_table()
@@ -78,16 +80,12 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
def setup_table(self): def setup_table(self):
table = self.find(QTableView, f"tableView") table = self.find(QTableView, f"tableView")
self._table = TableModel( self._table = TableModel(
table_view = table,
table_headers = self._trad.get_dict("table_headers"),
data = self._checker_list, 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): def setup_progress_bar(self):
self._progress = self.find(QProgressBar, f"progressBar") self._progress = self.find(QProgressBar, f"progressBar")
self._p = 0 # Progress current step self._p = 0 # Progress current step
+2 -1
View File
@@ -37,10 +37,11 @@ class Worker(QObject):
self.signalStatus.emit('end') self.signalStatus.emit('end')
def _compute(self): def _compute(self):
# The computation loop
for checker in self._checker_list: for checker in self._checker_list:
self.signalStatus.emit(checker.name) self.signalStatus.emit(checker.name)
# time.sleep(1) # Run checker
checker.run(self._study) checker.run(self._study)
self.signalStatus.emit("progress") self.signalStatus.emit("progress")
+18 -8
View File
@@ -16,7 +16,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow from View.Tools.PamhyrWindow import PamhyrDialog
from Solver.Solvers import solver_type_list from Solver.Solvers import solver_type_list
from Solver.GenericSolver import GenericSolver from Solver.GenericSolver import GenericSolver
@@ -24,12 +24,22 @@ from PyQt5.QtWidgets import (
QPushButton, QPushButton,
) )
class ConfigureSolverWindow(ASubWindow): class ConfigureSolverWindow(PamhyrDialog):
def __init__(self, data=None, title="Configuration : Add Solver", parent=None): _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__( super(ConfigureSolverWindow, self).__init__(
name=title, ui="ConfigureAddSolverDialog", parent=parent title = name,
config = config,
options = [],
parent=parent
) )
self.ui.setWindowTitle(title)
# Combo box item # Combo box item
for solver in solver_type_list: for solver in solver_type_list:
@@ -37,10 +47,10 @@ class ConfigureSolverWindow(ASubWindow):
# Data to return # Data to return
self.data = data self.data = data
if not self.data is None: if self.data is not None:
self.copy_data() self.copy_data()
self.connect() self.setup_connection()
def copy_data(self): def copy_data(self):
self.set_combobox_text("comboBox_solver", self.data.type) 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", self.data._path_output)
self.set_line_edit_text("lineEdit_output_cmd", self.data._cmd_output) self.set_line_edit_text("lineEdit_output_cmd", self.data._cmd_output)
def connect(self): def setup_connection(self):
# File button # File button
buttons = { buttons = {
"pushButton_input": (lambda: self.file_dialog( "pushButton_input": (lambda: self.file_dialog(
+58 -59
View File
@@ -21,8 +21,7 @@ import logging
from copy import deepcopy from copy import deepcopy
from config import Config from config import Config
from View.ASubWindow import ASubWindow from View.Tools.PamhyrWindow import PamhyrDialog
from View.ListedSubWindow import ListedSubWindow
from View.Stricklers.Table import TableModel from View.Stricklers.Table import TableModel
from View.Stricklers.translate import * from View.Stricklers.translate import *
@@ -95,50 +94,42 @@ class SolverTableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
class ConfigureWindow(ASubWindow, ListedSubWindow): class ConfigureWindow(PamhyrDialog):
def __init__(self, conf=None, title="Configure", parent=None): _pamhyr_ui = "ConfigureDialog"
_pamhyr_name = "Configure"
def __init__(self, config=None, parent=None):
if config is None:
config = Config()
super(ConfigureWindow, self).__init__( 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.setup_custom_sc()
self.conf = Config() self.setup_solver()
else: self.setup_stricklers()
self.conf = conf self.setup_data()
self.setup_connection()
self.setup_sc() def setup_solver(self):
# Solver
table = self.find(QTableView, "tableView_solver") table = self.find(QTableView, "tableView_solver")
self.solver_table_model = SolverTableModel( self.solver_table_model = SolverTableModel(
headers = ["name", "type", "description"], headers = ["name", "type", "description"],
rows = conf.solvers rows = self._config.solvers
) )
table.setModel(self.solver_table_model) table.setModel(self.solver_table_model)
table.setSelectionBehavior(QAbstractItemView.SelectRows) table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True) table.setAlternatingRowColors(True)
table.resizeColumnsToContents()
# Meshing_Tool def setup_stricklers(self):
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
table = self.find(QTableView, f"tableView_stricklers") table = self.find(QTableView, f"tableView_stricklers")
self._stricklers = deepcopy(self.conf.stricklers) self._stricklers = deepcopy(self._config.stricklers)
self._stricklers_table = TableModel( self._stricklers_table = TableModel(
data = self._stricklers, data = self._stricklers,
undo = self._undo_stack, undo = self._undo_stack,
@@ -148,26 +139,35 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True) 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 # 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 # Language
languages = Config.languages() languages = Config.languages()
for lang in languages: for lang in languages:
self.combobox_add_item("comboBox_language", lang) 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) self.set_combobox_text("comboBox_language", lang)
def setup_sc(self): def setup_custom_sc(self):
self._undo_stack = QUndoStack() self._debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
self._debug_sc.activated.connect(self.set_debug)
self.debug_sc = QShortcut(QKeySequence("Ctrl+G"), self) def setup_connection(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):
buttons = { buttons = {
# Solvers # Solvers
"pushButton_solver_add": self.add_solver, "pushButton_solver_add": self.add_solver,
@@ -195,33 +195,31 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
for button in buttons: for button in buttons:
self.find(QPushButton, button).clicked.connect(buttons[button]) self.find(QPushButton, button).clicked.connect(buttons[button])
self.debug_sc.activated.connect(self.set_debug)
def accept(self): def accept(self):
# Solvers # Solvers
self.conf.solvers = self.solver_table_model.rows.copy() self._config.solvers = self.solver_table_model.rows.copy()
# Meshing_Tool # 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 # Const
self.conf.segment = self.get_line_edit_text("lineEdit_segment") self._config.segment = self.get_line_edit_text("lineEdit_segment")
self.conf.max_listing = self.get_line_edit_text("lineEdit_max_listing") self._config.max_listing = self.get_line_edit_text("lineEdit_max_listing")
# Backup # Backup
self.conf.backup_enable = self.get_check_box("checkBox_backup") self._config.backup_enable = self.get_check_box("checkBox_backup")
self.conf.backup_path = self.get_line_edit_text("lineEdit_backup_path") self._config.backup_path = self.get_line_edit_text("lineEdit_backup_path")
self.conf.backup_frequence = self.get_time_edit("timeEdit_backup_frequence") self._config.backup_frequence = self.get_time_edit("timeEdit_backup_frequence")
self.conf.backup_max = self.get_spin_box("spinBox_backup_max") self._config.backup_max = self.get_spin_box("spinBox_backup_max")
# Stricklers # Stricklers
self.conf.stricklers = deepcopy(self._stricklers) self._config.stricklers = deepcopy(self._stricklers)
# Editor # Editor
self.conf.editor = self.get_line_edit_text("lineEdit_editor_cmd") self._config.editor = self.get_line_edit_text("lineEdit_editor_cmd")
# Language # 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() self.end()
@@ -230,13 +228,13 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
self.end() self.end()
def end(self): def end(self):
self.conf.save() self._config.save()
self.close() self.close()
# Debug # Debug
def set_debug(self): def set_debug(self):
self.conf.debug = not self.conf.debug self._config.debug = not self._config.debug
logger.info(f"Debug mode set : {self.conf.debug}") logger.info(f"Debug mode set : {self._config.debug}")
self.parent.setup_debug_mode() self.parent.setup_debug_mode()
# Solvers # Solvers
@@ -246,6 +244,7 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
for index in indexes: for index in indexes:
self.edit_solver = ConfigureSolverWindow( self.edit_solver = ConfigureSolverWindow(
data=self.solver_table_model.rows[index.row()], data=self.solver_table_model.rows[index.row()],
config=self._config,
parent=self parent=self
) )
if self.edit_solver.exec_(): if self.edit_solver.exec_():
+15 -13
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer from tools import trace, timer
from View.ASubWindow import ASubMainWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -45,20 +44,19 @@ logger = logging.getLogger()
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class ReplWindow(ASubMainWindow, ListedSubWindow): class ReplWindow(PamhyrWindow):
def __init__(self, title="Debug REPL", _pamhyr_ui = "DebugRepl"
study=None, config=None, _pamhyr_name = "Debug REPL"
def __init__(self, study=None, config=None,
solver=None, parent=None): solver=None, parent=None):
self._title = title
self._study = study
self._config = config
self._parent = parent
super(ReplWindow, self).__init__( 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.__debug_exec_result__ = None
self._history = [] self._history = []
@@ -92,7 +90,9 @@ class ReplWindow(ASubMainWindow, ListedSubWindow):
self.set_plaintext_edit_text("plainTextEdit", "") self.set_plaintext_edit_text("plainTextEdit", "")
self._history_ind = 0 self._history_ind = 0
# Code to rich_code
rich_code = code.strip().split("\n") 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[-1] = "self.__debug_exec_result__ = " + rich_code[-1]
rich_code = "\n".join(rich_code) rich_code = "\n".join(rich_code)
logger.debug(f"User debug command : {rich_code}") logger.debug(f"User debug command : {rich_code}")
@@ -102,6 +102,8 @@ class ReplWindow(ASubMainWindow, ListedSubWindow):
except Exception as e: except Exception as e:
value = f"<font color=\"red\">" + str(e) + "</font>" value = f"<font color=\"red\">" + str(e) + "</font>"
# Display code
msg = f"<font color=\"grey\"> # " + code + " #</font>" msg = f"<font color=\"grey\"> # " + code + " #</font>"
self.find(QTextEdit, "textEdit").append(msg) self.find(QTextEdit, "textEdit").append(msg)
# Display results
self.find(QTextEdit, "textEdit").append(str(value)) 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 -*- # -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow from View.Tools.PamhyrWindow import PamhyrWindow
class DummyWindow(ASubWindow): class DummyWindow(PamhyrWindow):
def __init__(self, title="Dummy", parent=None): __ui = "Dummy"
super(DummyWindow, self).__init__(name=title, ui="dummy", parent=parent) __name = "Pamhyr Dummy Window"
self.ui.setWindowTitle(title)
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 -*- # -*- coding: utf-8 -*-
from tools import timer, flatten from tools import timer, flatten
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -25,14 +25,7 @@ from PyQt5.QtCore import (
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class PlotStricklers(APlot): class PlotStricklers(PamhyrPlot):
def __init__(self, canvas=None, data=None, toolbar=None):
super(PlotStricklers, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
def draw_frictions(self, frictions, color="r"): def draw_frictions(self, frictions, color="r"):
lst = frictions lst = frictions
lst.sort(key = lambda s: s.begin_kp) lst.sort(key = lambda s: s.begin_kp)
+22 -42
View File
@@ -41,6 +41,8 @@ from View.Frictions.UndoCommand import (
MoveCommand, PasteCommand, DuplicateCommand, MoveCommand, PasteCommand, DuplicateCommand,
) )
from View.Tools.PamhyrTable import PamhyrTableModel
from View.Frictions.translate import * from View.Frictions.translate import *
logger = logging.getLogger() logger = logging.getLogger()
@@ -93,26 +95,10 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender()) self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel): class TableModel(PamhyrTableModel):
def __init__(self, data=None, study=None, undo=None): def _setup_lst(self):
super(QAbstractTableModel, self).__init__() self._lst = self._data.frictions
self._headers = list(table_headers.keys()) self._study = self._opt_data
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)
def data(self, index, role): def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole: if role != Qt.ItemDataRole.DisplayRole:
@@ -122,30 +108,24 @@ class TableModel(QAbstractTableModel):
column = index.column() column = index.column()
if self._headers[column] == "name": if self._headers[column] == "name":
return self._frictions.get(row).name return self._lst.get(row).name
elif self._headers[column] == "begin_kp": 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": 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": elif self._headers[column] == "begin_strickler":
value = self._frictions.get(row).begin_strickler value = self._lst.get(row).begin_strickler
if value == None: if value == None:
return _translate("Frictions", "Not defined") return _translate("Frictions", "Not defined")
return str(value) return str(value)
elif self._headers[column] == "end_strickler": elif self._headers[column] == "end_strickler":
value = self._frictions.get(row).end_strickler value = self._lst.get(row).end_strickler
if value == None: if value == None:
return _translate("Frictions", "Not defined") return _translate("Frictions", "Not defined")
return str(value) return str(value)
return QVariant() 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): def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
@@ -157,31 +137,31 @@ class TableModel(QAbstractTableModel):
if self._headers[column] == "name": if self._headers[column] == "name":
self._undo.push( self._undo.push(
SetNameCommand( SetNameCommand(
self._frictions, row, value self._lst, row, value
) )
) )
elif self._headers[column] == "begin_kp": elif self._headers[column] == "begin_kp":
self._undo.push( self._undo.push(
SetBeginCommand( SetBeginCommand(
self._frictions, row, value self._lst, row, value
) )
) )
elif self._headers[column] == "end_kp": elif self._headers[column] == "end_kp":
self._undo.push( self._undo.push(
SetEndCommand( SetEndCommand(
self._frictions, row, value self._lst, row, value
) )
) )
elif self._headers[column] == "begin_strickler": elif self._headers[column] == "begin_strickler":
self._undo.push( self._undo.push(
SetBeginStricklerCommand( SetBeginStricklerCommand(
self._frictions, row, self._study.river.strickler(value) self._lst, row, self._study.river.strickler(value)
) )
) )
elif self._headers[column] == "end_strickler": elif self._headers[column] == "end_strickler":
self._undo.push( self._undo.push(
SetEndStricklerCommand( SetEndStricklerCommand(
self._frictions, row, self._study.river.strickler(value) self._lst, row, self._study.river.strickler(value)
) )
) )
except Exception as e: except Exception as e:
@@ -196,7 +176,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
AddCommand( AddCommand(
self._frictions, row, self._data self._lst, row, self._data
) )
) )
@@ -208,7 +188,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._frictions, rows self._lst, rows
) )
) )
@@ -220,7 +200,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
SortCommand( SortCommand(
self._frictions, False self._lst, False
) )
) )
@@ -237,7 +217,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push( self._undo_stack.push(
MoveCommand( MoveCommand(
self._frictions, "up", row self._lst, "up", row
) )
) )
@@ -245,7 +225,7 @@ class TableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()): def move_down(self, index, parent=QModelIndex()):
if row > len(self._frictions): if row > len(self._lst):
return return
target = row target = row
@@ -254,7 +234,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push( self._undo_stack.push(
MoveCommand( 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 tools import trace, timer
from View.ASubWindow import ASubMainWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -48,62 +47,49 @@ from View.Frictions.Table import (
TableModel, ComboBoxDelegate TableModel, ComboBoxDelegate
) )
from View.Plot.MplCanvas import MplCanvas from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Geometry.PlotKPZ import PlotKPZ from View.Geometry.PlotKPZ import PlotKPZ
from View.Frictions.PlotStricklers import PlotStricklers from View.Frictions.PlotStricklers import PlotStricklers
from View.Frictions.translate import * from View.Frictions.translate import FrictionsTranslate
from View.Stricklers.Window import StricklersWindow from View.Stricklers.Window import StricklersWindow
_translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class FrictionsWindow(ASubMainWindow, ListedSubWindow): class FrictionsWindow(PamhyrWindow):
def __init__(self, title="Frictions", study=None, parent=None): _pamhyr_ui = "Frictions"
self._study = study _pamhyr_name = "Edit frictions"
self._reach = self._study.river.current_reach()
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._frictions = self._reach.frictions
self.setup_title(title) name = (
self._pamhyr_name + " - "
super(FrictionsWindow, self).__init__( + study.name + " - "
name=title, ui="Frictions", parent=parent + 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_table()
self.setup_graph() self.setup_graph()
self.setup_connections() 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): def setup_table(self):
self._table = {} 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( self._delegate_stricklers = ComboBoxDelegate(
data = self._reach, data = self._reach,
study = self._study, study = self._study,
@@ -111,13 +97,23 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
parent=self parent=self
) )
table.setItemDelegateForColumn( table = self.find(QTableView, f"tableView")
3, self._delegate_stricklers 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( table.setModel(self._table)
4, self._delegate_stricklers
)
table.setSelectionBehavior(QAbstractItemView.SelectRows) table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True) table.setAlternatingRowColors(True)
@@ -155,11 +151,6 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
self.find(QAction, "action_sort").triggered.connect(self.sort) self.find(QAction, "action_sort").triggered.connect(self.sort)
self.find(QAction, "action_edit_stricklers").triggered.connect(self.edit_stricklers) 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 = self.find(QTableView, f"tableView")
table.selectionModel()\ table.selectionModel()\
.selectionChanged\ .selectionChanged\
@@ -233,16 +224,16 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow):
row = self.index_selected_row() row = self.index_selected_row()
self._table.move_down(row) self._table.move_down(row)
def copy(self): def _copy(self):
logger.info("TODO: copy") logger.info("TODO: copy")
def paste(self): def _paste(self):
logger.info("TODO: paste") logger.info("TODO: paste")
def undo(self): def _undo(self):
self._table.undo() self._table.undo()
def redo(self): def _redo(self):
self._table.redo() self._table.redo()
def edit_stricklers(self): def edit_stricklers(self):
+14 -8
View File
@@ -18,13 +18,19 @@
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
table_headers = { class FrictionsTranslate(PamhyrTranslate):
"name": _translate("Frictions", "Name"), def __init__(self):
# "edge": _translate("Frictions", "Reach"), super(FrictionsTranslate, self).__init__()
"begin_kp": _translate("Frictions", "Begin kp (m)"),
"end_kp": _translate("Frictions", "End kp (m)"), self._sub_dict["table_headers"] = {
"begin_strickler": _translate("Frictions", "Begin strickler"), "name": _translate("Frictions", "Name"),
"end_strickler": _translate("Frictions", "End strickler"), # "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 import logging
from tools import timer from tools import timer
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -29,12 +29,15 @@ _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class PlotAC(APlot): class PlotAC(PamhyrPlot):
def __init__(self, canvas=None, data=None, toolbar=None, plot_xy=None): def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
plot_xy=None, parent=None):
super(PlotAC, self).__init__( super(PlotAC, self).__init__(
canvas=canvas, canvas = canvas,
data=data, trad = trad,
toolbar=toolbar data = data,
toolbar = toolbar,
parent = parent
) )
self.plot_xy = plot_xy self.plot_xy = plot_xy
+9 -7
View File
@@ -19,7 +19,7 @@
import logging import logging
from tools import timer from tools import timer
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -29,13 +29,15 @@ _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class PlotKPZ(APlot): class PlotKPZ(PamhyrPlot):
def __init__(self, canvas=None, data=None, toolbar=None, def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
display_current=True): display_current=True, parent=None):
super(PlotKPZ, self).__init__( super(PlotKPZ, self).__init__(
canvas=canvas, canvas = canvas,
data=data, trad = trad,
toolbar=toolbar data = data,
toolbar = toolbar,
parent = parent
) )
self.display_current = display_current self.display_current = display_current
+11 -8
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from tools import timer, trace from tools import timer, trace
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -25,12 +25,15 @@ from PyQt5.QtCore import (
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class PlotXY(APlot): class PlotXY(PamhyrPlot):
def __init__(self, canvas=None, data=None, toolbar=None, display_current=True): def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
display_current=True, parent=None):
super(PlotXY, self).__init__( super(PlotXY, self).__init__(
canvas=canvas, canvas = canvas,
data=data, trad = trad,
toolbar=toolbar data = data,
toolbar = toolbar,
parent = parent
) )
self.display_current = display_current self.display_current = display_current
@@ -60,11 +63,11 @@ class PlotXY(APlot):
# Axes # Axes
self.canvas.axes.set_xlabel( self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"), _translate("Geometry", "X (m)"),
color='green', fontsize=12 color='green', fontsize=12
) )
self.canvas.axes.set_ylabel( self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Y (m)"), _translate("Geometry", "Y (m)"),
color='green', fontsize=12 color='green', fontsize=12
) )
self.canvas.axes.axis("equal") self.canvas.axes.axis("equal")
+10 -7
View File
@@ -19,8 +19,8 @@
import logging import logging
from tools import timer, trace from tools import timer, trace
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from View.Plot.mpl_canvas_onpick_event import OnpickEvent from View.Tools.Plot.OnPickEvent import OnpickEvent
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -30,12 +30,15 @@ _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class Plot(APlot): class Plot(PamhyrPlot):
def __init__(self, canvas=None, data=None, toolbar=None, table=None): def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
table=None, parent=None):
super(Plot, self).__init__( super(Plot, self).__init__(
canvas=canvas, canvas = canvas,
data=data, trad = trad,
toolbar=toolbar data = data,
toolbar = toolbar,
parent = parent
) )
self._table = table self._table = table
+46 -104
View File
@@ -29,10 +29,11 @@ from PyQt5.QtWidgets import (
QMessageBox, QStyledItemDelegate, QLineEdit QMessageBox, QStyledItemDelegate, QLineEdit
) )
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QModelIndex, Qt, QAbstractTableModel, QModelIndex, Qt, QVariant, QCoreApplication
QVariant, QCoreApplication
) )
from View.Tools.PamhyrTable import PamhyrTableModel
from Model.Geometry.PointXYZ import PointXYZ from Model.Geometry.PointXYZ import PointXYZ
from Model.Geometry.ProfileXYZ import ProfileXYZ from Model.Geometry.ProfileXYZ import ProfileXYZ
@@ -43,43 +44,22 @@ logger = logging.getLogger()
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class TableEditableModel(QAbstractTableModel): class GeometryProfileTableModel(PamhyrTableModel):
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)
def data(self, index, role=Qt.DisplayRole): def data(self, index, role=Qt.DisplayRole):
if index.isValid(): if index.isValid():
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
value = "" value = ""
if index.column() == 0: if index.column() == 0:
value = self._profile.point(index.row()).x value = self._data.point(index.row()).x
elif index.column() == 1: elif index.column() == 1:
value = self._profile.point(index.row()).y value = self._data.point(index.row()).y
elif index.column() == 2: elif index.column() == 2:
value = self._profile.point(index.row()).z value = self._data.point(index.row()).z
elif index.column() == 3: elif index.column() == 3:
value = self._profile.point(index.row()).name value = self._data.point(index.row()).name
elif index.column() == 4: elif index.column() == 4:
station = self._profile.get_station() station = self._data.get_station()
if station is None: if station is None:
return "-" return "-"
else: else:
@@ -95,25 +75,25 @@ class TableEditableModel(QAbstractTableModel):
return Qt.AlignHCenter | Qt.AlignVCenter return Qt.AlignHCenter | Qt.AlignVCenter
if index.column() == 2: if index.column() == 2:
value = self._profile.point(index.row()).z value = self._data.point(index.row()).z
if role == Qt.ForegroundRole: if role == Qt.ForegroundRole:
if value == self._profile.z_min(): if value == self._data.z_min():
return QColor("red") return QColor("red")
elif value == self._profile.z_max(): elif value == self._data.z_max():
return QColor("blue") return QColor("blue")
if role == Qt.ToolTipRole: if role == Qt.ToolTipRole:
if value == self._profile.z_min(): if value == self._data.z_min():
return _translate("MainWindowProfile", return _translate("MainWindowProfile",
"La cote du fond", "La cote du fond",
"Z minimale") "Z minimale")
elif value == self._profile.z_max(): elif value == self._data.z_max():
return _translate("MainWindowProfile", return _translate("MainWindowProfile",
"La cote maximale", "La cote maximale",
"Z maximale") "Z maximale")
if index.column() == 3: 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 value.strip().upper() in ["RG", "RD"]:
if role == Qt.FontRole: if role == Qt.FontRole:
@@ -132,21 +112,6 @@ class TableEditableModel(QAbstractTableModel):
return QVariant() 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): def setData(self, index, value, role=Qt.EditRole):
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -154,34 +119,34 @@ class TableEditableModel(QAbstractTableModel):
if role == Qt.EditRole: if role == Qt.EditRole:
try: try:
if column == 0: if column == 0:
self._undo_stack.push( self._undo.push(
SetXCommand( SetXCommand(
self._profile, row, self._data, row,
self._profile.point(row).x, self._data.point(row).x,
value value
) )
) )
elif column == 1: elif column == 1:
self._undo_stack.push( self._undo.push(
SetYCommand( SetYCommand(
self._profile, row, self._data, row,
self._profile.point(row).y, self._data.point(row).y,
value value
) )
) )
elif column == 2: elif column == 2:
self._undo_stack.push( self._undo.push(
SetZCommand( SetZCommand(
self._profile, row, self._data, row,
self._profile.point(row).z, self._data.point(row).z,
value value
) )
) )
elif column == 3: elif column == 3:
self._undo_stack.push( self._undo.push(
SetNameCommand( SetNameCommand(
self._profile, row, self._data, row,
self._profile.point(row).name, self._data.point(row).name,
value value
) )
) )
@@ -213,9 +178,9 @@ class TableEditableModel(QAbstractTableModel):
def insert_row(self, row, parent=QModelIndex()): def insert_row(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1) self.beginInsertRows(parent, row, row - 1)
self._undo_stack.push( self._undo.push(
AddCommand( AddCommand(
self._profile, row self._data, row
) )
) )
@@ -225,9 +190,9 @@ class TableEditableModel(QAbstractTableModel):
def remove_rows(self, rows, parent=QModelIndex()): def remove_rows(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo_stack.push( self._undo.push(
DelCommand( DelCommand(
self._profile, rows self._data, rows
) )
) )
@@ -239,15 +204,15 @@ class TableEditableModel(QAbstractTableModel):
reverse = (order != Qt.AscendingOrder) reverse = (order != Qt.AscendingOrder)
self._undo_stack.push( self._undo.push(
SortCommand( SortCommand(
self._profile, column, reverse self._data, column, reverse
) )
) )
self.layoutChanged.emit() self.layoutChanged.emit()
def move_row_up(self, row, parent=QModelIndex()): def move_up(self, row, parent=QModelIndex()):
if row <= 0: if row <= 0:
return return
@@ -255,26 +220,26 @@ class TableEditableModel(QAbstractTableModel):
self.beginMoveRows(parent, row - 1, row - 1, parent, target) self.beginMoveRows(parent, row - 1, row - 1, parent, target)
self._undo_stack.push( self._undo.push(
MoveCommand( MoveCommand(
self._profile, "up", row self._data, "up", row
) )
) )
self.endMoveRows() self.endMoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def move_row_down(self, row_to_move, parent=QModelIndex()): def move_down(self, row_to_move, parent=QModelIndex()):
if row > self._profile.number_points: if row > self._data.number_points:
return return
target = row target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target) self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push( self._undo.push(
MoveCommand( MoveCommand(
self._profile, "down", row self._data, "down", row
) )
) )
@@ -282,7 +247,7 @@ class TableEditableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def paste(self, row, header, data): def paste(self, row, header, data):
if row > self._profile.number_points: if row > self._data.number_points:
return return
if len(data) == 0: if len(data) == 0:
@@ -290,12 +255,12 @@ class TableEditableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo_stack.push( self._undo.push(
PasteCommand( PasteCommand(
self._profile, row, self._data, row,
list( list(
map( map(
lambda d: self._profile.point_from_data(header, d), lambda d: self._data.point_from_data(header, d),
data data
) )
) )
@@ -306,32 +271,9 @@ class TableEditableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def undo(self): def undo(self):
self._undo_stack.undo() self._undo.undo()
self.layoutChanged.emit() self.layoutChanged.emit()
def redo(self): def redo(self):
self._undo_stack.redo() self._undo.redo()
self.layoutChanged.emit() 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 ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QFileDialog, QCheckBox, QApplication, QMainWindow, QFileDialog, QCheckBox,
QUndoStack, QShortcut, QUndoStack, QShortcut, QTableView, QAbstractItemView,
QHeaderView, QVBoxLayout, QAction,
) )
from Model.Geometry.Reach import Reach from Model.Geometry.Reach import Reach
from Model.Geometry.ProfileXYZ import ProfileXYZ from Model.Geometry.ProfileXYZ import ProfileXYZ
from View.ASubWindow import ASubMainWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.Geometry.Profile.mainwindow_ui_profile import Ui_MainWindow 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.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 _translate = QCoreApplication.translate
class ProfileWindow(ASubMainWindow): class ProfileWindow(PamhyrWindow):
def __init__(self, profile=None, title="Profile", parent=None): _pamhyr_ui = "GeometryCrossSection"
self._title = title _pamhyr_name = "Geometry cross-section"
self.parent = parent
super(ProfileWindow, self).__init__(
name=self._title,
parent=self.parent
)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def __init__(self, profile=None, study=None, config=None, parent=None):
self._profile = profile self._profile = profile
self._model = None
self.setup_window() name = f"{self._pamhyr_name} - {self._profile.name} {self._profile.kp}"
self.setup_sc() super(ProfileWindow, self).__init__(
self.setup_model() title = name,
self.setup_connections() study = study,
self.plot() config = config,
trad = GeometryProfileTranslate(),
self._model.dataChanged.connect(self.update_plot) parent = parent
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})"
) )
self.setWindowTitle(self._title) self.setup_table()
self.setup_plot()
self.setup_connections()
def setup_sc(self): def setup_table(self):
self._undo_stack = QUndoStack() table_headers = self._trad.get_dict("table_headers")
self.undo_sc = QShortcut(QKeySequence.Undo, self) table = self.find(QTableView, "tableView")
self.redo_sc = QShortcut(QKeySequence.Redo, self) self._tablemodel = GeometryProfileTableModel(
self.copy_sc = QShortcut(QKeySequence.Copy, self) table_view = table,
self.paste_sc = QShortcut(QKeySequence.Paste, self) table_headers = table_headers,
editable_headers = ["name", "x", "y", "z"],
def setup_model(self): data = self._profile,
self._model = TableEditableModel(
profile = self._profile,
undo = self._undo_stack 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) def setup_plot(self):
self.ui.tableView.setItemDelegate(Delegate()) self._tablemodel.blockSignals(True)
def setup_connections(self): self._canvas = MplCanvas(width=3, height=4, dpi=100)
self.ui.btn_sort_asc_x.triggered.connect(self.sort_X_ascending) self._canvas.setObjectName("canvas")
self.ui.btn_sort_desc_x.triggered.connect(self.sort_X_descending) self._toolbar = PamhyrPlotToolbar(
self.ui.btn_sort_asc_y.triggered.connect(self.sort_Y_ascending) self._canvas, self,
self.ui.btn_sort_desc_y.triggered.connect(self.sort_Y_descending) items = ["home", "zoom", "save", "iso", "back/forward", "move"]
self.ui.btn_move_up.triggered.connect(self.move_row_up) )
self.ui.btn_move_down.triggered.connect(self.move_row_down) self._plot_layout = self.find(QVBoxLayout, "verticalLayout")
self.ui.btn_export.triggered.connect(self.handleSave) self._plot_layout.addWidget(self._toolbar)
self.ui.btn_add.triggered.connect(self.insert_row) self._plot_layout.addWidget(self._canvas)
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._plot = Plot( self._plot = Plot(
canvas = self.ui.canvas, canvas = self._canvas,
data = self._profile, data = self._profile,
toolbar = None, toolbar = self._toolbar,
table = self.ui.tableView, table = self.find(QTableView, "tableView")
) )
self._plot.draw() 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): def update_plot(self):
self.ui.tableView.model().blockSignals(True) self._tablemodel.blockSignals(True)
# TODO: Do not rebuild all graph # TODO: Do not rebuild all graph
self._plot.update() self._plot.update()
self.ui.tableView.model().blockSignals(False) self._tablemodel.blockSignals(False)
def index_selected_row(self): def index_selected_row(self):
rows = self.ui.tableView\ rows = self._tablemodel\
.selectionModel()\ .selectionModel()\
.selectedRows() .selectedRows()
if len(rows) == 0: if len(rows) == 0:
return 0 return 0
return self.ui.tableView\ return self._tablemodel\
.selectionModel()\ .selectionModel()\
.selectedRows()[0]\ .selectedRows()[0]\
.row() .row()
def insert_row(self): def add(self):
if len(self.ui.tableView.selectedIndexes()) == 0: table = self.find(QTableView, "tableView")
self._model.insert_row(self._model.rowCount()) if len(table.selectedIndexes()) == 0:
self._tablemodel.insert_row(self._tablemodel.rowCount())
else: else:
row = self.index_selected_row() row = self.index_selected_row()
self._model.insert_row(row + 1) self._tablemodel.insert_row(row + 1)
self.update_plot() self.update_plot()
def delete_row(self): def delete(self):
table = self.find(QTableView, "tableView")
rows = sorted( rows = sorted(
list( list(
set( set(
[index.row() for index in self.ui.tableView.selectedIndexes()] [index.row() for index in table.selectedIndexes()]
) )
) )
) )
if len(rows) > 0: if len(rows) > 0:
self._model.remove_rows(rows) self._tablemodel.remove_rows(rows)
self.update_plot() self.update_plot()
def sort_X_ascending(self): def sort_X_ascending(self):
self._model.sort('x', order=Qt.AscendingOrder) self._tablemodel.sort('x', order=Qt.AscendingOrder)
self.update_plot() self.update_plot()
def sort_X_descending(self): def sort_X_descending(self):
self._model.sort('x', order=Qt.DescendingOrder) self._tablemodel.sort('x', order=Qt.DescendingOrder)
self.update_plot() self.update_plot()
def sort_Y_ascending(self): def sort_Y_ascending(self):
self._model.sort('y', order=Qt.AscendingOrder) self._tablemodel.sort('y', order=Qt.AscendingOrder)
self.update_plot() self.update_plot()
def sort_Y_descending(self): def sort_Y_descending(self):
self._model.sort('y', order=Qt.DescendingOrder) self._tablemodel.sort('y', order=Qt.DescendingOrder)
self.update_plot() self.update_plot()
def move_row_down(self): def move_down(self):
rows = list( rows = list(
set( set(
[index.row() for index in self.ui.tableView.selectedIndexes()] [index.row() for index in
self.find(QTableView, "tableView").selectedIndexes()]
) )
) )
for row in rows: for row in rows:
if row < self._model.rowCount() - 1: if row < self._tablemodel.rowCount() - 1:
self._model.move_row_down(row) self._tablemodel.move_down(row)
self.update_plot() self.update_plot()
def move_row_up(self): def move_up(self):
rows = list( rows = list(
set( set(
[index.row() for index in self.ui.tableView.selectedIndexes()] [index.row() for index in
self.find(QTableView, "tableView").selectedIndexes()]
) )
) )
for row in rows: for row in rows:
if 0 < row: if 0 < row:
self._model.move_row_up(row) self._tablemodel.move_up(row)
self.update_plot() self.update_plot()
def copy(self): def copy(self):
rows = self.ui.tableView\ rows = self._tablemodel\
.selectionModel()\ .selectionModel()\
.selectedRows() .selectedRows()
table = [] table = []
@@ -257,136 +240,13 @@ class ProfileWindow(ASubMainWindow):
row.append(self._profile) row.append(self._profile)
row = self.index_selected_row() row = self.index_selected_row()
self._model.paste(row, header, data) self._tablemodel.paste(row, header, data)
self.update_plot() self.update_plot()
def undo(self): def undo(self):
self._model.undo() self._tablemodel.undo()
self.update_plot() self.update_plot()
def redo(self): def redo(self):
self._model.redo() self._tablemodel.redo()
self.update_plot() 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, QComboBox,
) )
from View.Tools.PamhyrTable import PamhyrTableModel
from Model.Geometry import Reach from Model.Geometry import Reach
from Model.Geometry.ProfileXYZ import ProfileXYZ from Model.Geometry.ProfileXYZ import ProfileXYZ
from View.Geometry.UndoCommand import * from View.Geometry.UndoCommand import *
@@ -44,53 +46,26 @@ logger = logging.getLogger()
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class TableEditableModel(QAbstractTableModel): class GeometryReachTableModel(PamhyrTableModel):
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)
def data(self, index, role=Qt.DisplayRole): def data(self, index, role=Qt.DisplayRole):
if not index.isValid(): if not index.isValid():
return QVariant() return QVariant()
if role == Qt.DisplayRole and index.column() == 0: 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: 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}" return f"{kp:.4f}"
if role == Qt.DisplayRole and index.column() == 2: 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: if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter return Qt.AlignHCenter | Qt.AlignVCenter
if role == Qt.ForegroundRole and index.column() == 0: if role == Qt.ForegroundRole and index.column() == 0:
name = self._reach.profile(index.row()).name\ name = self._data.profile(index.row()).name\
.strip()\ .strip()\
.lower() .lower()
if (name == "upstream" or name == "up" or if (name == "upstream" or name == "up" or
@@ -102,16 +77,6 @@ class TableEditableModel(QAbstractTableModel):
return QVariant() 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): def setData(self, index, value, role=Qt.EditRole):
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -119,19 +84,19 @@ class TableEditableModel(QAbstractTableModel):
if role == Qt.EditRole and index.column() != 2: if role == Qt.EditRole and index.column() != 2:
try: try:
if index.column() == 0: if index.column() == 0:
self._undo_stack.push( self._undo.push(
SetNameCommand( SetNameCommand(
self._reach, index.row(), self._data, index.row(),
self._reach.profile(index.row()).name, self._data.profile(index.row()).name,
value value
) )
) )
if index.column() == 1: if index.column() == 1:
self._undo_stack.push( self._undo.push(
SetKPCommand( SetKPCommand(
self._reach, index.row(), self._data, index.row(),
self._reach.profile(index.row()).kp, self._data.profile(index.row()).kp,
value value
) )
) )
@@ -149,39 +114,25 @@ class TableEditableModel(QAbstractTableModel):
return False 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() # @QtCore.pyqtSlot()
def insert_row(self, row, parent=QModelIndex()): def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1) self.beginInsertRows(parent, row, row - 1)
self._undo_stack.push( self._undo.push(
AddCommand( AddCommand(
self._reach, row self._data, row
) )
) )
self.endInsertRows() self.endInsertRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def remove_rows(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo_stack.push( self._undo.push(
DelCommand( DelCommand(
self._reach, rows self._data, rows
) )
) )
@@ -191,9 +142,9 @@ class TableEditableModel(QAbstractTableModel):
def sort_profiles(self, _reverse): def sort_profiles(self, _reverse):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo_stack.push( self._undo.push(
SortCommand( SortCommand(
self._reach, _reverse self._data, _reverse
) )
) )
@@ -201,7 +152,7 @@ class TableEditableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def move_row_up(self, row, parent=QModelIndex()): def move_up(self, row, parent=QModelIndex()):
if row <= 0: if row <= 0:
return return
@@ -209,26 +160,26 @@ class TableEditableModel(QAbstractTableModel):
self.beginMoveRows(parent, row - 1, row - 1, parent, target) self.beginMoveRows(parent, row - 1, row - 1, parent, target)
self._undo_stack.push( self._undo.push(
MoveCommand( MoveCommand(
self._reach, "up", row self._data, "up", row
) )
) )
self.endMoveRows() self.endMoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def move_row_down(self, row, parent=QModelIndex()): def move_down(self, row, parent=QModelIndex()):
if row > self._reach.number_profiles: if row > self._data.number_profiles:
return return
target = row target = row
self.beginMoveRows(parent, row + 1, row + 1, parent, target) self.beginMoveRows(parent, row + 1, row + 1, parent, target)
self._undo_stack.push( self._undo.push(
MoveCommand( MoveCommand(
self._reach, "down", row self._data, "down", row
) )
) )
@@ -238,9 +189,9 @@ class TableEditableModel(QAbstractTableModel):
def duplicate(self, rows, profiles): def duplicate(self, rows, profiles):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo_stack.push( self._undo.push(
DuplicateCommand( DuplicateCommand(
self._reach, rows, self._data, rows,
profiles profiles
) )
) )
@@ -250,7 +201,7 @@ class TableEditableModel(QAbstractTableModel):
def paste(self, row, header, data): def paste(self, row, header, data):
if row > self._reach.number_profiles: if row > self._data.number_profiles:
return return
if len(data) == 0: if len(data) == 0:
@@ -258,9 +209,9 @@ class TableEditableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo_stack.push( self._undo.push(
PasteCommand( PasteCommand(
self._reach, row, self._data, row,
list( list(
map( map(
lambda d: ProfileXYZ.from_data(header, d), lambda d: ProfileXYZ.from_data(header, d),
@@ -272,71 +223,3 @@ class TableEditableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.layoutChanged.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 ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QFileDialog, QCheckBox, QApplication, QMainWindow, QFileDialog, QCheckBox,
QUndoStack, QShortcut, QUndoStack, QShortcut, QTableView, QHeaderView,
QAction, QSlider, QPushButton, QVBoxLayout,
QLabel, QAbstractItemView,
) )
from View.Geometry.PlotXY import PlotXY from View.Tools.PamhyrWindow import PamhyrWindow
from View.Geometry.PlotKPZ import PlotKPZ from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Geometry.PlotAC import PlotAC from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.ASubWindow import ASubMainWindow, WindowToolKit from View.Geometry.Table import GeometryReachTableModel
from View.ListedSubWindow import ListedSubWindow from View.Geometry.PlotXY import PlotXY
from View.Geometry.mainwindow_ui_reach import Ui_MainWindow from View.Geometry.PlotAC import PlotAC
from View.Geometry.Table import * from View.Geometry.PlotKPZ import PlotKPZ
from View.Geometry.Translate import GeometryTranslate
from View.Geometry.Profile.Window import ProfileWindow from View.Geometry.Profile.Window import ProfileWindow
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class GeometryWindow(ASubMainWindow, ListedSubWindow): class GeometryWindow(PamhyrWindow):
def __init__(self, model=None, title="Geometry", parent=None): _pamhyr_ui = "GeometryReach"
self._title = title _pamhyr_name = "Geometry"
self.parent = parent
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__( super(GeometryWindow, self).__init__(
name=self._title, title = name,
parent=parent 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._tablemodel = None
self._profile_window = [] self._profile_window = []
self._clipboard = None
self.setup_window() self.setup_table()
self.setup_sc()
self.setup_model()
self.setup_plots() self.setup_plots()
self.setup_statusbar()
self.setup_connections() self.setup_connections()
self.changed_slider_value() self.changed_slider_value()
def setup_window(self): def setup_table(self):
self._title = f"{self.ui.mainwindow_title} - {self._reach.name}" table_headers = self._trad.get_dict("table_headers")
self.setWindowTitle(self._title)
def setup_sc(self): table = self.find(QTableView, "tableView")
self._undo_stack = QUndoStack() self._tablemodel = GeometryReachTableModel(
table_view = table,
self.undo_sc = QShortcut(QKeySequence.Undo, self) table_headers = table_headers,
self.redo_sc = QShortcut(QKeySequence.Redo, self) editable_headers = ["name", "kp"],
self.copy_sc = QShortcut(QKeySequence.Copy, self) data = self._reach,
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_model(self):
self._tablemodel = TableEditableModel(
headers = self.ui.tableView_header,
reach = self._reach,
undo = self._undo_stack undo = self._undo_stack
) )
self.tableView.setModel(self._tablemodel) table.setModel(self._tablemodel)
self.tableView.setItemDelegate(Delegate()) table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_plots(self): 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.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.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() 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): def setup_connections(self):
self.ui.btn_open.triggered.connect(self.open_file_dialog) actions = {
self.ui.btn_sort_asc.triggered.connect(self.sort_ascending) "action_import": self.import_from_file,
self.ui.btn_sort_desc.triggered.connect(self.sort_descending) # "action_export": self.export_to_file,
self.ui.btn_move_up.triggered.connect(self.move_row_up) "action_sort_asc": self.sort_ascending,
self.ui.btn_move_down.triggered.connect(self.move_row_down) "action_sort_des": self.sort_descending,
# self.ui.btn_end_editing.triggered.connect(self.handleSave) "action_up": self.move_up,
self.ui.btn_add.triggered.connect(self.insert_row) "action_down": self.move_down,
self.ui.btn_delete.triggered.connect(self.delete_rows) "action_add": self.add,
self.ui.btn_edit.triggered.connect(self.edit_profile) "action_delete": self.delete,
self.ui.verticalSlider.valueChanged.connect(self.changed_slider_value) "action_edit": self.edit_profile,
}
self.ui.btn_slider_up.clicked.connect(self.decrement_value_slider) for action in actions:
self.ui.btn_slider_down.clicked.connect(self.increment_value_slider) self.find(QAction, action)\
self.ui.btn_move_up.triggered.connect(self.changed_profile_slot) .triggered.connect(actions[action])
self.undo_sc.activated.connect(self.undo) self.find(QSlider, "verticalSlider").valueChanged.connect(self.changed_slider_value)
self.redo_sc.activated.connect(self.redo) self.find(QPushButton, "pushButton_up").clicked.connect(self.decrement_value_slider)
self.copy_sc.activated.connect(self.copy) self.find(QPushButton, "pushButton_down").clicked.connect(self.increment_value_slider)
self.paste_sc.activated.connect(self.paste)
# Profile selection when line change in table # Profile selection when line change in table
self.tableView.selectionModel()\ self.find(QTableView, "tableView").selectionModel()\
.selectionChanged\ .selectionChanged\
.connect(self.select_current_profile) .connect(self.select_current_profile)
def open_file_dialog(self):
def import_from_file(self):
options = QFileDialog.Options() options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', ) settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_st"],
self._trad["file_m"],
self._trad["file_all"],
]
filename, _ = QtWidgets.QFileDialog.getOpenFileName( filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self, self,
_translate("MainWindow_reach", "Ouvrir un fichier"), self._trad["open_file"],
"", "",
_translate("MainWindow_reach", "Fichiers .ST (*.ST)") + ";; ".join(file_types),
";; " +
_translate("MainWindow_reach", "Fichiers .M (*.M)") +
";; " +
_translate("MainWindow_reach", "Tous les fichiers (*)"),
options=options options=options
) )
@@ -158,20 +208,6 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.plot_kpc() self.plot_kpc()
self.plot_ac() 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): def edit_profile(self):
self.tableView.model().blockSignals(True) self.tableView.model().blockSignals(True)
@@ -214,9 +250,9 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True) self.tableView.model().blockSignals(True)
self._plot_xy = PlotXY( self._plot_xy = PlotXY(
canvas = self.ui.canvas_1, canvas = self._canvas_xy,
data = self._reach, data = self._reach,
toolbar = self.ui.toolbar_1 toolbar = self._toolbar_xy
) )
self._plot_xy.draw() self._plot_xy.draw()
@@ -231,9 +267,9 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True) self.tableView.model().blockSignals(True)
self._plot_kpc = PlotKPZ( self._plot_kpc = PlotKPZ(
canvas = self.ui.canvas_2, canvas = self._canvas_kpc,
data = self._reach, data = self._reach,
toolbar = self.ui.toolbar_2 toolbar = self._toolbar_kpc
) )
self._plot_kpc.draw() self._plot_kpc.draw()
@@ -248,9 +284,9 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True) self.tableView.model().blockSignals(True)
self._plot_ac = PlotAC( self._plot_ac = PlotAC(
canvas = self.ui.canvas_3, canvas = self._canvas_ac,
data = self._reach, data = self._reach,
toolbar = self.ui.toolbar_3, toolbar = self._toolbar_ac,
plot_xy = self._plot_xy plot_xy = self._plot_xy
) )
self._plot_ac.draw() self._plot_ac.draw()
@@ -303,7 +339,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
if len(self.tableView.selectedIndexes()) > 0: if len(self.tableView.selectedIndexes()) > 0:
row = self.index_selected_row() row = self.index_selected_row()
self.ui.verticalSlider.setValue(row) self.find(QSlider, "verticalSlider").setValue(row)
self.select_plot_xy(row) self.select_plot_xy(row)
self.select_plot_kpc(row) self.select_plot_kpc(row)
self.select_plot_ac(row) self.select_plot_ac(row)
@@ -314,41 +350,37 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.tableView.model().blockSignals(True) self.tableView.model().blockSignals(True)
if self._tablemodel.rowCount() != 0: 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 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_xy(slider_value)
self.select_plot_kpc(slider_value) self.select_plot_kpc(slider_value)
self.select_row_profile_slider(slider_value) self.select_row_profile_slider(slider_value)
self.update_statusbar()
self.tableView.model().blockSignals(False) self.tableView.model().blockSignals(False)
def increment_value_slider(self): def increment_value_slider(self):
if 0 <= self.ui.verticalSlider.value() < self._tablemodel.rowCount() - 1: slider = self.find(QSlider, "verticalSlider")
self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() + 1) if 0 <= slider.value() < self._tablemodel.rowCount() - 1:
slider.setValue(slider.value() + 1)
def decrement_value_slider(self): def decrement_value_slider(self):
if 0 < self.ui.verticalSlider.value() < self._tablemodel.rowCount(): slider = self.find(QSlider, "verticalSlider")
self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() - 1) 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: if len(self.tableView.selectedIndexes()) == 0:
self._tablemodel.insert_row(self._tablemodel.rowCount()) self._tablemodel.add(self._tablemodel.rowCount())
else: else:
row = self.index_selected_row() 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( rows = sorted(
list( list(
set( set(
@@ -358,7 +390,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
) )
if len(rows) > 0: if len(rows) > 0:
self._tablemodel.remove_rows(rows) self._tablemodel.delete(rows)
self.update_plot_xy() self.update_plot_xy()
self.select_current_profile() self.select_current_profile()
@@ -373,24 +405,24 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
.row() .row()
def sort_ascending(self): def sort_ascending(self):
self._tablemodel.sort_profiles(True) self._tablemodel.sort_profiles(False)
self.select_current_profile() self.select_current_profile()
self.changed_slider_value() self.changed_slider_value()
def sort_descending(self): def sort_descending(self):
self._tablemodel.sort_profiles(False) self._tablemodel.sort_profiles(True)
self.select_current_profile() self.select_current_profile()
self.changed_slider_value() self.changed_slider_value()
def move_row_up(self): def move_up(self):
row = self.index_selected_row() row = self.index_selected_row()
self._tablemodel.move_row_up(row) self._tablemodel.move_up(row)
self.select_current_profile() self.select_current_profile()
def move_row_down(self): def move_down(self):
row = self.index_selected_row() row = self.index_selected_row()
self._tablemodel.move_row_down(row) self._tablemodel.move_down(row)
self.select_current_profile() self.select_current_profile()
def duplicate(self): def duplicate(self):
@@ -411,7 +443,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self._tablemodel.duplicate(rows, profiles) self._tablemodel.duplicate(rows, profiles)
self.select_current_profile() self.select_current_profile()
def copy(self): def _copy(self):
rows = self.tableView\ rows = self.tableView\
.selectionModel()\ .selectionModel()\
.selectedRows() .selectedRows()
@@ -427,7 +459,7 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self.copyTableIntoClipboard(table) self.copyTableIntoClipboard(table)
def paste(self): def _paste(self):
header, data = self.parseClipboardTable() header, data = self.parseClipboardTable()
if len(data) == 0: if len(data) == 0:
@@ -442,19 +474,19 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
self._tablemodel.paste(row, header, data) self._tablemodel.paste(row, header, data)
self.select_current_profile() self.select_current_profile()
def undo(self): def _undo(self):
self._tablemodel.undo() self._tablemodel.undo()
self.select_current_profile() self.select_current_profile()
self.update_plot_xy() self.update_plot_xy()
self.update_plot_kpc() self.update_plot_kpc()
def redo(self): def _redo(self):
self._tablemodel.redo() self._tablemodel.redo()
self.select_current_profile() self.select_current_profile()
self.update_plot_xy() self.update_plot_xy()
self.update_plot_kpc() self.update_plot_kpc()
def handleSave(self): def export_to_file(self):
options = QFileDialog.Options() options = QFileDialog.Options()
DEFAULT_DIRECTORY = '/home/' DEFAULT_DIRECTORY = '/home/'
settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', ) settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
@@ -463,32 +495,11 @@ class GeometryWindow(ASubMainWindow, ListedSubWindow):
filename, filters = QFileDialog.getSaveFileName( filename, filters = QFileDialog.getSaveFileName(
self, self,
filter=_translate("MainWindow_reach", filter = self._trad["file_st"] + ";; " + self._trad["file_all"],
"Files .ST(*.ST or *.st)") options = options
+ ";; " +
_translate("MainWindow_reach", "All files "
"(*)"),
options=options
) )
current_dir = os.path.split(filename)[0] or DEFAULT_DIRECTORY current_dir = os.path.split(filename)[0] or DEFAULT_DIRECTORY
if filename != '': if filename != '':
self._tablemodel.export_reach(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 -*- # -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow from View.Tools.PamhyrWindow import PamhyrDialog
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -32,10 +31,15 @@ from PyQt5.QtWidgets import (
QDoubleSpinBox, QDoubleSpinBox,
) )
class DischargeDialog(ASubWindow, ListedSubWindow): class DischargeDialog(PamhyrDialog):
_pamhyr_ui = "InitialConditions_Dialog_Generator_Discharge"
_pamhyr_name = "Discharge"
def __init__(self, title="Discharge", parent=None): def __init__(self, title="Discharge", parent=None):
super(DischargeDialog, self).__init__( super(DischargeDialog, self).__init__(
name=title, ui="InitialConditions_Dialog_Generator_Discharge", parent=parent title = self._pamhyr_name,
options = [],
parent = parent
) )
self.value = None self.value = None
+9 -5
View File
@@ -16,8 +16,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow from View.Tools.PamhyrWindow import PamhyrDialog
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -32,10 +31,15 @@ from PyQt5.QtWidgets import (
QDoubleSpinBox, QDoubleSpinBox,
) )
class HeightDialog(ASubWindow, ListedSubWindow): class HeightDialog(PamhyrDialog):
def __init__(self, title="Height", parent=None): _pamhyr_ui = "InitialConditions_Dialog_Generator_Height"
_pamhyr_name = "Height"
def __init__(self, parent=None):
super(HeightDialog, self).__init__( super(HeightDialog, self).__init__(
name=title, ui="InitialConditions_Dialog_Generator_Height", parent=parent title = self._pamhyr_name,
options = [],
parent = parent
) )
self.value = None self.value = None
+4 -11
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from tools import timer from tools import timer
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -25,14 +25,7 @@ from PyQt5.QtCore import (
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class PlotDKP(APlot): class PlotDKP(PamhyrPlot):
def __init__(self, canvas=None, data=None, toolbar=None):
super(PlotDKP, self).__init__(
canvas=canvas,
data=data,
toolbar=toolbar
)
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
self.canvas.axes.cla() self.canvas.axes.cla()
@@ -42,11 +35,11 @@ class PlotDKP(APlot):
return return
self.canvas.axes.set_ylabel( self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Elevation (m)"), self._trad["elevation"],
color='green', fontsize=11 color='green', fontsize=11
) )
self.canvas.axes.set_xlabel( self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "KP (m)"), self._trad["kp"],
color='green', fontsize=11 color='green', fontsize=11
) )
+4 -17
View File
@@ -17,22 +17,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from tools import timer from tools import timer
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
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
)
class PlotDischarge(PamhyrPlot):
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
self.canvas.axes.cla() self.canvas.axes.cla()
@@ -42,11 +29,11 @@ class PlotDischarge(APlot):
return return
self.canvas.axes.set_ylabel( self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Discharge (m^3/s)"), self._trad["discharge"],
color='green', fontsize=11 color='green', fontsize=11
) )
self.canvas.axes.set_xlabel( self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "KP (m)"), self._trad["kp"],
color='green', fontsize=11 color='green', fontsize=11
) )
+17 -39
View File
@@ -33,14 +33,14 @@ from PyQt5.QtWidgets import (
QComboBox, QComboBox,
) )
from View.Tools.PamhyrTable import PamhyrTableModel
from View.InitialConditions.UndoCommand import ( from View.InitialConditions.UndoCommand import (
SetCommand, AddCommand, DelCommand, SetCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand, SortCommand, MoveCommand, PasteCommand,
DuplicateCommand, GenerateCommand, DuplicateCommand, GenerateCommand,
) )
from View.InitialConditions.translate import *
logger = logging.getLogger() logger = logging.getLogger()
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
@@ -87,29 +87,13 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender()) self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel): class InitialConditionTableModel(PamhyrTableModel):
def __init__(self, river=None, reach=None, undo=None): def __init__(self, reach=None, **kwargs):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._river = river
self._reach = reach self._reach = reach
self._undo = undo super(InitialConditionTableModel, self).__init__(**kwargs)
self._ics = self._river.initial_conditions.get(reach)
def flags(self, index): def _setup_lst(self):
column = index.column() self._lst = self._data.river.initial_conditions.get(self._reach)
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 data(self, index, role): def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole: if role != Qt.ItemDataRole.DisplayRole:
@@ -119,16 +103,10 @@ class TableModel(QAbstractTableModel):
column = index.column() column = index.column()
if self._headers[column] not in ["name", "comment"]: 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}" return f"{v:.4f}"
else: else:
return self._ics.get(row)[self._headers[column]] return self._lst.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 QVariant() return QVariant()
@@ -143,7 +121,7 @@ class TableModel(QAbstractTableModel):
if self._headers[column] is not None: if self._headers[column] is not None:
self._undo.push( self._undo.push(
SetCommand( SetCommand(
self._ics, row, self._headers[column], value self._lst, row, self._headers[column], value
) )
) )
except Exception as e: except Exception as e:
@@ -158,7 +136,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
AddCommand( AddCommand(
self._ics, row self._lst, row
) )
) )
@@ -170,7 +148,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._ics, rows self._lst, rows
) )
) )
@@ -182,7 +160,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
SortCommand( SortCommand(
self._ics, False self._lst, False
) )
) )
@@ -199,7 +177,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
MoveCommand( MoveCommand(
self._ics, "up", row self._lst, "up", row
) )
) )
@@ -207,7 +185,7 @@ class TableModel(QAbstractTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()): def move_down(self, index, parent=QModelIndex()):
if row > len(self._ics): if row > len(self._lst):
return return
target = row target = row
@@ -216,7 +194,7 @@ class TableModel(QAbstractTableModel):
self._undo.push( self._undo.push(
MoveCommand( MoveCommand(
self._ics, "down", row self._lst, "down", row
) )
) )
@@ -234,7 +212,7 @@ class TableModel(QAbstractTableModel):
def generate(self, generator, param): def generate(self, generator, param):
self._undo.push( self._undo.push(
GenerateCommand( GenerateCommand(
self._ics, generator, param self._lst, generator, param
) )
) )
self.layoutChanged.emit() self.layoutChanged.emit()
+50 -55
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer from tools import trace, timer
from View.ASubWindow import ASubMainWindow from View.Tools.PamhyrWindow import PamhyrWindow
from View.ListedSubWindow import ListedSubWindow
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QKeySequence, QKeySequence,
@@ -46,14 +45,14 @@ from View.InitialConditions.UndoCommand import (
DuplicateCommand, DuplicateCommand,
) )
from View.InitialConditions.Table import TableModel, ComboBoxDelegate from View.InitialConditions.Table import InitialConditionTableModel, ComboBoxDelegate
from View.Plot.MplCanvas import MplCanvas from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.InitialConditions.PlotDKP import PlotDKP from View.InitialConditions.PlotDKP import PlotDKP
from View.InitialConditions.PlotDischarge import PlotDischarge 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.DialogHeight import HeightDialog
from View.InitialConditions.DialogDischarge import DischargeDialog from View.InitialConditions.DialogDischarge import DischargeDialog
@@ -61,68 +60,65 @@ _translate = QCoreApplication.translate
logger = logging.getLogger() logger = logging.getLogger()
class InitialConditionsWindow(ASubMainWindow, ListedSubWindow): class InitialConditionsWindow(PamhyrWindow):
def __init__(self, title="Initial condition", _pamhyr_ui = "InitialConditions"
study=None, parent=None): _pamhyr_name = "Initial condition"
self._study = study
self._reach = study.river.current_reach()
self._ics = self._study.river.initial_conditions.get(self._reach)
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 = (
name=self._title, ui="InitialConditions", parent=parent 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_table()
self.setup_graph() self.setup_plot()
self.setup_connections() self.setup_connections()
self.ui.setWindowTitle(self._title) 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): def setup_table(self):
table = self.find(QTableView, f"tableView") 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( self._delegate_kp = ComboBoxDelegate(
reach = self._reach, reach = self._reach,
parent=self parent = self
) )
table.setItemDelegateForColumn( self._table = InitialConditionTableModel(
list(table_headers).index("kp"), reach = self._reach,
self._delegate_kp 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.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True) table.setAlternatingRowColors(True)
def setup_graph(self): def setup_plot(self):
self.canvas_1 = MplCanvas(width=5, height=4, dpi=100) self.canvas_1 = MplCanvas(width=5, height=4, dpi=100)
self.canvas_1.setObjectName("canvas_1") self.canvas_1.setObjectName("canvas_1")
self.toolbar_1 = PamHyrNavigationToolbar2QT( self.toolbar_1 = PamhyrPlotToolbar(
self.canvas_1, self self.canvas_1, self
) )
self.plot_layout_1 = self.find(QVBoxLayout, "verticalLayout_1") self.plot_layout_1 = self.find(QVBoxLayout, "verticalLayout_1")
@@ -132,13 +128,15 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self.plot_1 = PlotDKP( self.plot_1 = PlotDKP(
canvas = self.canvas_1, canvas = self.canvas_1,
data = self._ics, data = self._ics,
trad = self._trad,
toolbar = self.toolbar_1, toolbar = self.toolbar_1,
parent = self
) )
self.plot_1.draw() self.plot_1.draw()
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100) self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
self.canvas_2.setObjectName("canvas_2") self.canvas_2.setObjectName("canvas_2")
self.toolbar_2 = PamHyrNavigationToolbar2QT( self.toolbar_2 = PamhyrPlotToolbar(
self.canvas_2, self self.canvas_2, self
) )
self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2") self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2")
@@ -148,7 +146,9 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self.plot_2 = PlotDischarge( self.plot_2 = PlotDischarge(
canvas = self.canvas_2, canvas = self.canvas_2,
data = self._ics, data = self._ics,
trad = self._trad,
toolbar = self.toolbar_2, toolbar = self.toolbar_2,
parent = self
) )
self.plot_2.draw() self.plot_2.draw()
@@ -165,11 +165,6 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self.generate_discharge 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) self._table.dataChanged.connect(self._update_plot)
def index_selected_row(self): def index_selected_row(self):
@@ -225,19 +220,19 @@ class InitialConditionsWindow(ASubMainWindow, ListedSubWindow):
self._table.move_down(row) self._table.move_down(row)
self._update_plot() self._update_plot()
def copy(self): def _copy(self):
logger.info("TODO: copy") logger.info("TODO: copy")
self._update_plot() self._update_plot()
def paste(self): def _paste(self):
logger.info("TODO: paste") logger.info("TODO: paste")
self._update_plot() self._update_plot()
def undo(self): def _undo(self):
self._table.undo() self._table.undo()
self._update_plot() self._update_plot()
def redo(self): def _redo(self):
self._table.redo() self._table.redo()
self._update_plot() self._update_plot()
+19 -9
View File
@@ -18,14 +18,24 @@
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
table_headers = { class ICTranslate(PamhyrTranslate):
# "name": _translate("LateralContribution", "Name"), def __init__(self):
"kp": _translate("LateralContribution", "KP (m)"), super(ICTranslate, self).__init__()
# "speed": _translate("LateralContribution", "Speed (m/s)"),
"discharge": _translate("LateralContribution", "Discharge (m³/s)"), self._dict["elevation"] = _translate("InitialCondition", "Elevation (m)")
"elevation": _translate("LateralContribution", "Elevation (m)"), self._dict["discharge"] = _translate("InitialCondition", "Discharge (m³/s)")
"height": _translate("LateralContribution", "Height (m)"), self._dict["kp"] = _translate("InitialCondition", "KP (m)")
# "comment": _translate("LateralContribution", "Comment"),
} 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 datetime import datetime
from tools import timer, trace from tools import timer, trace
from View.Plot.APlot import APlot from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import ( from PyQt5.QtCore import (
QCoreApplication QCoreApplication
@@ -29,15 +29,19 @@ from View.LateralContribution.Edit.translate import *
_translate = QCoreApplication.translate _translate = QCoreApplication.translate
class Plot(APlot): class Plot(PamhyrPlot):
def __init__(self, canvas=None, data=None, def __init__(self, mode = "time", data=None,
mode = "time", toolbar=None): canvas=None, trad=None, toolbar=None,
parent=None):
super(Plot, self).__init__( super(Plot, self).__init__(
canvas=canvas, canvas = canvas,
data=data, trad = trad,
toolbar=toolbar data = data,
toolbar = toolbar,
parent = parent
) )
self._table_headers = self._trad.get_dict("table_headers")
self._mode = mode self._mode = mode
def custom_ticks(self): def custom_ticks(self):
@@ -105,10 +109,10 @@ class Plot(APlot):
# Plot label # Plot label
header = self.data.header header = self.data.header
self.canvas.axes.set_xlabel( 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( 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) 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