Compare commits

...
198 Commits
Author SHA1 Message Date
Pierre-Antoine Rouby 76e90f417e Script: Fix pep8. 2023-10-24 15:26:55 +02:00
Pierre-Antoine Rouby e556bcff54 Script: Add script command list solver. 2023-10-24 15:13:35 +02:00
Pierre-Antoine Rouby 4833685e2e Script: Add run/export script. 2023-10-24 15:05:05 +02:00
Pierre-Antoine Rouby 2d7681086d ci: Add configure stage. 2023-10-24 11:11:34 +02:00
Pierre-Antoine Rouby f44ac69d68 ci: Change stages order (tests before build). 2023-10-24 10:58:22 +02:00
Pierre-Antoine Rouby 32684af292 tests: Minor change. 2023-10-24 10:56:23 +02:00
Pierre-Antoine Rouby 862ec88191 tests: Add test script and fix pep8 in unittest. 2023-10-24 10:48:54 +02:00
Pierre-Antoine Rouby d36a9cd340 tests: Fix test env path. 2023-10-24 10:43:33 +02:00
Pierre-Antoine Rouby afafbb0c5e tests: Add unittest for Model and minor change. 2023-10-24 10:38:34 +02:00
Pierre-Antoine Rouby e3f01b575a Solver: Mage: Fix minor in ST file export. 2023-10-23 10:31:59 +02:00
Pierre-Antoine Rouby d6924998d9 Results: CustomPlot: Fix pep8. 2023-10-20 17:25:36 +02:00
Pierre-Antoine Rouby 0ae5c02ea0 Friction: Fix open stricker edit. 2023-10-20 17:20:03 +02:00
Pierre-Antoine Rouby e395f9f575 BC, LC: Fix sub window open. 2023-10-20 17:16:37 +02:00
Pierre-Antoine Rouby f074e1fdf2 Geometry: Fix profile sub window open. 2023-10-20 17:01:28 +02:00
Pierre-Antoine Rouby 4d1eb0fecc View: Fix results window open. 2023-10-20 16:57:32 +02:00
Pierre-Antoine Rouby f5fdde9a5d Merge branch 'results-custom-plot'. 2023-10-20 16:51:26 +02:00
Pierre-Antoine Rouby 32aa5bece5 Results: CustomPlot: Add legend and change axes label fontsize for each plot. 2023-10-20 16:48:21 +02:00
Pierre-Antoine Rouby cdfb867037 Results: CustomPlot: Add custom time axes. 2023-10-20 16:27:07 +02:00
Pierre-Antoine Rouby 021971816a Results: CustomPlot: Implement plot with time on y axes. 2023-10-20 15:09:24 +02:00
Pierre-Antoine Rouby e4386e20a3 Network: Fix item selection. 2023-10-20 11:45:28 +02:00
Pierre-Antoine Rouby 8760cd364d Pamhyr: Implement window duplication deny mechanism with hash (#7). 2023-10-20 10:48:07 +02:00
Pierre-Antoine Rouby 4c40f675b5 IC: Fix #14. 2023-10-20 09:25:19 +02:00
Pierre-Antoine Rouby 325bb9ed02 Solver: Mage: Fix warning. 2023-10-18 16:43:01 +02:00
Pierre-Antoine Rouby 505d1aeb48 Solver: Add @output command line parameters. 2023-10-18 16:40:28 +02:00
Pierre-Antoine Rouby 1dd189cbd4 Results: Implement results reload (for same solver) #16. 2023-10-18 16:00:32 +02:00
Pierre-Antoine Rouby fa7490dee8 pep8: Minor fix. 2023-10-18 14:55:32 +02:00
Pierre-Antoine Rouby 7fda5b6b4a Mage: Fix #17 ST file export format. 2023-10-18 14:45:38 +02:00
Pierre-Antoine Rouby 3ff0883eca RunSolver: Fix #15 and minor change. 2023-10-18 14:24:14 +02:00
Pierre-Antoine Rouby 04c8f1ae5f Results: CustomPlot: Minor change to fix warnings. 2023-10-18 14:02:55 +02:00
Pierre-Antoine Rouby f73000056c Results: CustomPlot: Fix kp / elevation, water_elevation and discharge drawing. 2023-10-18 10:53:57 +02:00
Pierre-Antoine Rouby 4605950b94 Results: CustomPlot: Minor change. 2023-10-17 17:10:58 +02:00
Pierre-Antoine Rouby ae40097b51 Results: Fix pep8 format. 2023-10-17 11:27:00 +02:00
Pierre-Antoine Rouby daaa9b193e Resutls: CustomPlot: Prepare ploting mecanisme. 2023-10-17 11:25:30 +02:00
Pierre-Antoine Rouby 98ee3ee6ec CustomPlot: Add translation. 2023-10-16 17:25:08 +02:00
Pierre-Antoine Rouby b77aed2395 results: Add custom plot dialog scheme. 2023-10-16 15:52:29 +02:00
Pierre-Antoine Rouby 45afdc25f9 packages: Fix motd.txt missing and pip version in linux.sh. 2023-10-16 11:41:59 +02:00
Pierre-Antoine Rouby 2ae681cebc ci: Add a full requirements for doc build. 2023-10-11 16:52:09 +02:00
Pierre-Antoine Rouby cc465a050c ci: Allow doc build failed. 2023-10-11 16:50:14 +02:00
Pierre-Antoine Rouby 1fd840d33f ci: Update pip. 2023-10-11 16:47:52 +02:00
Pierre-Antoine Rouby cc237fffa8 pamhyr: Fix package requirements list. 2023-10-11 16:37:57 +02:00
Pierre-Antoine Rouby bb27d4ec20 ci: Fix env install package. 2023-10-11 16:26:43 +02:00
Pierre-Antoine Rouby 255f7302ea ci: Fix remove option -U from pip install 2023-10-11 16:24:45 +02:00
Pierre-Antoine Rouby 9e0b4fd243 ci: Fix missing package for pdoc, fix requirements and pep8. 2023-10-11 16:22:45 +02:00
Pierre-Antoine Rouby 2e926c9ece debug: Switch from QPlainTextEdit to PamhyrPythonEditor. 2023-10-11 16:17:21 +02:00
Pierre-Antoine Rouby bcc2d2f4d4 ci: Allow pep8 failed. 2023-10-11 15:16:04 +02:00
Pierre-Antoine Rouby e3555b2c51 pep8: Fix all pep8 format error. 2023-10-11 15:08:33 +02:00
Pierre-Antoine Rouby e99037223b pep8: Fix some View code format. 2023-10-11 11:56:48 +02:00
Pierre-Antoine Rouby 9f2b81d385 pep8: Fix code format for model. 2023-10-11 11:47:09 +02:00
Pierre-Antoine Rouby 2c592b25b9 pep8: Fix code format. 2023-10-11 11:23:46 +02:00
Pierre-Antoine Rouby bf05b5ddc1 ci: Add pep8 test job. 2023-10-11 11:12:32 +02:00
Pierre-Antoine Rouby fa2454d079 pep8: Some modifications to respect pep8 standard code. 2023-10-11 11:06:45 +02:00
Pierre-Antoine Rouby 8544dd1144 doc: Disactive documentation display in Pamhyr2 for lighter packages. 2023-10-11 10:40:58 +02:00
Pierre-Antoine Rouby 1f8b0bddb4 ui: Fix geometry cross section window lang. 2023-10-11 08:52:05 +02:00
Pierre-Antoine Rouby 5b2ee80329 geometry, results, configure: Some fixes. 2023-10-11 08:40:54 +02:00
Pierre-Antoine Rouby acfb876da0 doc: Change the Firefox button to an open button. 2023-10-10 15:28:59 +02:00
Pierre-Antoine Rouby ab9ebc6400 pamhyr: Add just-in-time database geometry loading. 2023-10-10 15:17:53 +02:00
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
215 changed files with 10014 additions and 5506 deletions

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
# Pamhyr .editorconfig
root = true
[*.py]
charset = utf-8
indent_style = space
indent_size = 4
tab_width = 8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
+334
View File
@@ -1,6 +1,7 @@
### CUSTOM ###
*.exe
*.msi
*.tar.xz
*.zip
*.spec
@@ -9,15 +10,33 @@
tmp
*.tar
*.tar.*
*.tgz
*.zip
*.html
*.pdf
*.odt
doc/dev/images/auto_*
doc/dev/documentation.*
!doc/dev/*.org
doc/dev/images/auto_*
doc/users/documentation.*
!doc/users/*.org
TAGS
pamhyrBuild
wine-pamhyr-build*
_PAMHYR_
__old__
*-venv
### END CUSTOM ###
# Created by https://www.toptal.com/developers/gitignore/api/python
@@ -309,3 +328,318 @@ compile_commands.json
*_qmlcache.qrc
# End of https://www.toptal.com/developers/gitignore/api/qt
# Created by https://www.toptal.com/developers/gitignore/api/latex
# Edit at https://www.toptal.com/developers/gitignore?templates=latex
### LaTeX ###
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
*.lzo
*.lzs
*.slg
*.slo
*.sls
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplot
*.gnuplot
*.table
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.glog
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
# *.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# newpax
*.newpax
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# svg
svg-inkscape/
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# titletoc
*.ptc
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices and outlines
*.xyc
*.xyd
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# gummi
.*.swp
# KBibTeX
*~[0-9]*
# TeXnicCenter
*.tps
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
# Makeindex log files
*.lpz
# xwatermark package
*.xwm
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
# Uncomment the next line to have this generated file ignored.
#*Notes.bib
### LaTeX Patch ###
# LIPIcs / OASIcs
*.vtc
# glossaries
*.glstex
# End of https://www.toptal.com/developers/gitignore/api/latex
+286 -100
View File
@@ -15,97 +15,57 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
stages:
- build
- downloads
- configure
- test
- build
- package
- release
#########
# BUILD #
#########
#############
# DOWNLOADS #
#############
build-mage-linux:
stage: build
dl-mage-linux:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
- 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
- cd mage
- mkdir -p mage-linux
- cd mage-linux
- tar xvf ../mage.tgz
artifacts:
paths:
- mage/mage
- mage/mage_extraire
- mage/mailleurPF
- mage-linux/mage
- mage-linux/mage_extraire
- mage-linux/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
dl-mage-windows:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
- 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
- cd mage
- mkdir -p mage-windows
- cd mage-windows
- tar xvf ../mage.tgz
artifacts:
paths:
- mage/mage.exe
- mage/mage_extraire.exe
- mage/mailleurPF.exe
- mage-windows/mage.exe
- mage-windows/mage_extraire.exe
- mage-windows/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
#############
# CONFIGURE #
#############
build:
stage: build
set-version:
stage: configure
tags:
- linux
script:
@@ -116,7 +76,7 @@ build:
- VERSION
build-lang:
stage: build
stage: configure
tags:
- linux
script:
@@ -130,56 +90,261 @@ build-lang:
# TESTS #
#########
test:
unittest:
stage: test
tags:
- linux
needs:
- job: set-version
artifacts: true
script:
- echo "TODO PAMHYR tests"
- python3 -m venv venv
- . venv/bin/activate
- pip3 install -U pip
- pip3 install -r ./full-requirements.txt
- pip3 install -U -r ./full-requirements.txt
- cd src
- python3 -m unittest discover -t .
test-pep8:
stage: test
tags:
- linux
needs:
- job: set-version
artifacts: true
- job: unittest
script:
# Setup virtual env
- python3 -m venv venv
- . venv/bin/activate
- pip3 install -U pip
- pip3 install -r ./requirements.txt
- pip3 install -U -r ./requirements.txt
- pip3 install pycodestyle
- pycodestyle ./src
allow_failure: true
#########
# BUILD #
#########
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/
allow_failure: true
build-developers-doc:
stage: build
tags:
- linux
needs:
- job: set-version
artifacts: true
script:
- cd ./doc/dev/
- ./build.sh
# Create html documentation
- cd ../../src
- python3 -m venv doc-venv
- . doc-venv/bin/activate
- pip3 install -U pip
- pip3 install -r ../full-requirements.txt
- pip3 install -U -r ../full-requirements.txt
- pip3 install pdoc3
- 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/
allow_failure: true
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 pip
- pip3 install -r ../requirements.txt
- 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/
- cp -v ../src/motd.txt 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 -r ..\requirements.txt
- 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
############
# PACKAGES #
############
linux-package:
linux-package-tar:
stage: package
tags:
- release
- linux
needs:
- job: build-lang
artifacts: true
- job: build
artifacts: true
- job: build-mage-linux
# - job: test-linux
- job: build-linux
artifacts: true
rules:
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_TAG
artifacts:
paths:
- packages/pamhyr-gnulinux-amd64.tar.xz
- linux/pamhyr-gnulinux.tar.xz
script:
- cd packages
- ./linux.sh
- cd linux
- tar --xz -cf pamhyr-gnulinux.tar.xz ./pamhyr --checkpoint=.100
windows-package:
windows-package-zip:
stage: package
tags:
- release
- linux
needs:
# - job: test-windows
- job: build-windows
artifacts: true
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_TAG
artifacts:
paths:
- windows/pamhyr-windows.zip
script:
- cd windows
- zip -r pamhyr-windows.zip pamhyr
windows-package-exe:
stage: package
tags:
- release
- wine
needs:
- job: build-lang
artifacts: true
- job: build
artifacts: true
- job: build-mage-windows
# - job: test-windows
- job: build-windows
artifacts: true
rules:
- if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_TAG
artifacts:
paths:
- packages/pamhyr-win-amd64.exe
- windows/pamhyr-windows.exe
script:
- cd packages
- ./windows.bat
- cd windows
- copy ..\packages\make-windows-exe.bat make-windows-exe.bat
- copy ..\packages\pamhyr.nsi pamhyr.nsi
- .\make-windows-exe.bat
###########
# RELEASE #
@@ -191,16 +356,25 @@ tag-release:
- release
- linux
needs:
- job: linux-package
- job: linux-package-tar
artifacts: true
- job: windows-package
- job: windows-package-zip
artifacts: true
- job: windows-package-exe
artifacts: true
- job: build-users-doc
artifacts: true
- job: build-developers-doc
artifacts: true
rules:
- if: $CI_COMMIT_TAG
artifacts:
paths:
- packages/pamhyr-gnulinux-amd64.tar.xz
- packages/pamhyr-win-amd64.exe
- linux/pamhyr-gnulinux.tar.xz
- windows/pamhyr-windows.zip
- windows/pamhyr-windows.exe
- doc/dev/documentation.pdf
- doc/users/documentation.pdf
script:
- cd packages
release:
@@ -210,11 +384,23 @@ tag-release:
ref: '$CI_COMMIT_SHA'
assets:
links:
- name: 'GNU/Linux amd64 (tar.xz)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-gnulinux-amd64.tar.xz'
filepath: '/packages/pamhyr-gnulinux-amd64.tar.xz'
- name: 'GNU/Linux (tar.xz)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/linux/pamhyr-gnulinux.tar.xz'
filepath: '/linux/pamhyr-gnulinux.tar.xz'
link_type: 'package'
- name: 'Windows amd64 (exe)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-win-amd64.exe'
filepath: '/packages/pamhyr-win-amd64.exe'
- name: 'Windows (exe)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/windows/pamhyr-windows.exe'
filepath: '/windows/pamhyr-windows.exe'
link_type: 'package'
- name: 'Windows (zip)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/windows/pamhyr-windows.zip'
filepath: '/windows/pamhyr-windows.zip'
link_type: 'package'
- name: 'User documentations (pdf)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/doc/users/documentation.pdf'
filepath: '/doc/users/documentation.pdf'
link_type: 'other'
- name: 'Dev documentations (pdf)'
url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/doc/dev/documentation.pdf'
filepath: '/doc/dev/documentation.pdf'
link_type: 'other'
-1
View File
@@ -47,5 +47,4 @@ pip3 install -r requirements.txt
# Run pamhyr
./src/pamhyr.py
```
+18
View File
@@ -0,0 +1,18 @@
#! /bin/sh
echo " PDF documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-latex-export-to-pdf
echo " HTML documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(require 'ox-html)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-html-export-to-html
+1
View File
@@ -0,0 +1 @@
../tools/ref.bib
File diff suppressed because it is too large. Load diff
View File
Whitespace-only changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+163
View File
@@ -0,0 +1,163 @@
\ProvidesClass{Pamhyr}[Pamhyr class]
\NeedsTeXFormat{LaTeX2e}
\ProcessOptions\relax
\LoadClass[11pt,a4paper]{article}
\usepackage[hmargin=2cm, vmargin=3cm]{geometry}
\usepackage[british,UKenglish,USenglish,english,american]{babel}
\usepackage[T1]{fontenc}
\usepackage{times}
\usepackage[usenames,dvipsnames]{xcolor}
\usepackage[absolute]{textpos}
\usepackage{titlesec}
\usepackage{amssymb}
\usepackage{tcolorbox}
\usepackage{enumitem}
\setlist[enumerate]{itemsep=-0.5em}
\setlist[itemize]{itemsep=-0.5em}
%%
%% Bibtex
%%
\bibliographystyle{plain}
%%
%% Colors
%%
\definecolor{primary}{HTML}{212121}
\definecolor{lightheadings}{HTML}{5c5c5c}
\definecolor{headings}{HTML}{212121}
\definecolor{links}{HTML}{e12121}
%%
%% Title
%%
\setlength{\columnsep}{20pt}
\setlength{\parskip}{0pt} %% inter paragraph space
\renewcommand{\maketitle}{
{
\centering
{
\fontsize{35}{40}
\selectfont
\scshape
Pamhyr2 \\
\medskip
}
{
%\centering
\scshape
\fontsize{20pt}{24pt}
\selectfont
A graphical user interface for 1D hydro-sedimentary modelling
of rivers \\
\medskip
}
\rule{3cm}{0.4pt}\\
\vspace{0.3cm}
{
%\centering
\scshape
\fontsize{26pt}{24pt}
\selectfont
\@title \\
Document licence: GPLv3 \\
\medskip
\includegraphics[width=3cm]{../images/GPLv3_Logo.png} \\
\medskip
\fontsize{14pt}{16pt}
\selectfont
© \@author \\
\medskip
}
{
%\centering
%\scshape
\fontsize{12pt}{14pt}
\selectfont
\@date
%\medskip
}\\
\vspace{0.3cm}
\rule{3cm}{0.4pt}\\
\vspace{0.3cm}
{
\begin{minipage}[h]{0.24\textwidth}
\includegraphics[width=4cm]{../../src/View/ui/ressources/Logo-INRAE.png}
\end{minipage}
\begin{minipage}[h]{0.24\textwidth}
\includegraphics[width=4cm]{../images/riverly.png}
\end{minipage}
}\\
\vspace{0.3cm}
%%\rule{\textwidth}{0.4pt}
%%\newpage
}
}
%%
%% Texttt
%%
\usepackage[htt]{hyphenat} % Make texttt split at end of line
%%
%% Verbatim
%%
\AtBeginEnvironment{verbatim}{\footnotesize}
%% Add box arrond of verbatim
\BeforeBeginEnvironment{verbatim}{\begin{tcolorbox}[boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}%
\AfterEndEnvironment{verbatim}{\end{tcolorbox}}%
\makeatletter
\AtBeginEnvironment{verbatim}{\dontdofcolorbox}
\def\dontdofcolorbox{\renewcommand\fcolorbox[4][]{##4}}
\makeatother
%%
%% Source code blocks
%%
%% Use minted
\usepackage{minted}
\usemintedstyle{tango}
\setminted[c]{fontsize=\footnotesize,encoding=utf8,linenos}
\setminted[c++]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos}
\setminted[python]{breaklines,fontsize=\footnotesize,encoding=utf8,linenos}
\setminted[shell]{breaklines,fontsize=\footnotesize,encoding=utf8}
\setminted[scheme]{breaklines,fontsize=\footnotesize,encoding=utf8}
\setminted[commun-lisp]{breaklines,fontsize=\footnotesize,encoding=utf8}
\setminted[text]{breaklines,fontsize=\footnotesize,encoding=utf8}
\setminted[llvm]{breaklines,fontsize=\footnotesize,encoding=utf8}
%% Add box arrond of minted
\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}%
\AfterEndEnvironment{minted}{\end{tcolorbox}}%
\makeatletter
\AtBeginEnvironment{minted}{\dontdofcolorbox}
\def\dontdofcolorbox{\renewcommand\fcolorbox[4][]{##4}}
\makeatother
%%
%% Water mark
%%
%% FIXME: Delete me for the first realease
\usepackage{draftwatermark}
\SetWatermarkLightness{0.8}
\SetWatermarkAngle{25}
\SetWatermarkScale{3}
\SetWatermarkFontSize{1cm}
\SetWatermarkText{Work in progress}
+19
View File
@@ -0,0 +1,19 @@
# latex.org -- Pamhyr documentation latex config
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
#+LaTeX_CLASS: PamhyrDoc
+51
View File
@@ -0,0 +1,51 @@
# macro.org -- Pamhyr documentation macro
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
# Constant name
#+MACRO: oldPamhyr PAMHyR
#+MACRO: Pamhyr Pamhyr2
#+MACRO: Mage Mage
#+MACRO: Rubarbe RubarBE
#+MACRO: Inrae INRAE
#+MACRO: latex \LaTeX
# Information
#+MACRO: version (eval (pamhyr-version))
#+MACRO: current-file (eval (pamhyr-current-file))
# Special format
#+MACRO: file =$1=
# Biblio
#+MACRO: cite [cite:$1]
#+MACRO: biblio \bibliography{documentation}
# Wrapper
#+NAME: attr_wrap
#+HEADER: :var width="\\textwidth"
#+HEADER: :var caption=""
#+HEADER: :var smallcaption=""
#+HEADER: :var name=""
#+HEADER: :var data=""
#+HEADER: :var float="nil"
#+BEGIN_SRC sh :results output :exports none
echo "#+CAPTION[$smallcaption]: $caption"
echo "#+NAME: $name"
echo "#+ATTR_LATEX: :width $width :float $float"
echo "$data"
#+END_SRC
View File
Whitespace-only changes.
+66
View File
@@ -0,0 +1,66 @@
(require 'org)
(require 'subr-x)
;; LaTeX config
(add-to-list
'org-latex-classes
'("PamhyrDoc"
"\\documentclass{../tools/PamhyrDoc}"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
(setq org-confirm-babel-evaluate nil)
(setq org-latex-caption-above nil)
(org-babel-do-load-languages
'org-babel-load-languages
(add-to-list 'org-babel-load-languages '(shell . t)))
(org-babel-do-load-languages
'org-babel-load-languages
(add-to-list 'org-babel-load-languages '(dot . t)))
(org-babel-do-load-languages
'org-babel-load-languages
(add-to-list 'org-babel-load-languages '(python . t)))
(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)
(setq org-src-fontify-natively t)
(setq org-latex-pdf-process
'("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"bibtex documentation"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
;; Citations
(org-add-link-type
"cite" 'ebib
(lambda (path desc format)
(cond
((eq format 'html)
(format "(<cite>%s</cite>)" path))
((eq format 'latex)
(if (or (not desc) (equal 0 (search "cite:" desc)))
(format "\\cite{%s}" path)
(format "\\cite[%s][%s]{%s}"
(cadr (split-string desc ";"))
(car (split-string desc ";")) path))))))
;; Functools
(defun pamhyr-version ()
"Return the contents of the pamhyr version file."
(with-temp-buffer
(insert-file-contents "../../VERSION")
(buffer-string)))
(defun pamhyr-current-file ()
(let ((lst (split-string (buffer-file-name) "/")))
(string-join (nthcdr (- (length lst) 3) lst) "/")))
+18
View File
@@ -0,0 +1,18 @@
#! /bin/sh
echo " PDF documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-latex-export-to-pdf
echo " HTML documentation.org"
emacs -nw --batch \
--eval "(require 'org)" \
--eval "(require 'ox-latex)" \
--eval "(require 'ox-html)" \
--eval "(load-file \"../tools/setup.el\")" \
--file ./documentation.org \
-f org-html-export-to-html
+1
View File
@@ -0,0 +1 @@
../tools/ref.bib
+20
View File
@@ -0,0 +1,20 @@
#+STARTUP: indent
#+INCLUDE: ../tools/macro.org
#+INCLUDE: ../tools/latex.org
#+TITLE: User documentation
#+SUBTITLE: Version: {{{version}}}
#+AUTHOR: {{{INRAE}}}
#+OPTIONS: toc:t
#+LANGUAGE: UKenglish
* TODO Over view
* TODO Get started
** Installation
*** Linux
*** Windows
* TODO Usage
{{{biblio}}}
View File
Whitespace-only changes.
+11
View File
@@ -0,0 +1,11 @@
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.12.2
PyQtWebEngine==5.15.6
QScintilla>=2.14.1
pyqtgraph>=0.12.1
matplotlib>=3.4.1
numpy>=1.24.2
colorama>=0.4.3
pyinstaller>=5.11.0
shapely>=2.0.1
+30 -13
View File
@@ -25,6 +25,7 @@ echo " *** SETUP ENV"
python3 -m venv linux-pamhyr-venv
. linux-pamhyr-venv/bin/activate
pip3 install -U pip
pip3 install -r ../requirements.txt
echo " *** PYINSTALL"
@@ -43,18 +44,22 @@ cd $OLD_PWD
echo " *** COPY DATA"
mkdir -p dist/pamhyr/View/ui
mkdir -p dist/pamhyr/View/ui/Widgets
cp -r ../src/View/ui/ressources/ dist/pamhyr/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/View/ui/Widgets/
cp -r ../src/View/ui/*.ui dist/pamhyr/View/ui/
mkdir -p dist/pamhyr/_internal/View/ui
mkdir -p dist/pamhyr/_internal/View/ui/Widgets
cp -r ../src/View/ui/ressources/ dist/pamhyr/_internal/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/_internal/View/ui/Widgets/
cp -r ../src/View/ui/*.ui dist/pamhyr/_internal/View/ui/
mkdir -p dist/pamhyr/lang
cp -r ../src/lang/*.qm dist/pamhyr/lang/
cp ../VERSION dist/pamhyr/
cp ../AUTHORS dist/pamhyr/
cp ../LICENSE dist/pamhyr/
mkdir dist/pamhyr/_internal/
#cp ../VERSION dist/pamhyr/_internal/
VERSION=$(cat ../VERSION)
echo "local-build-$VERSION" > dist/pamhyr/_internal/VERSION
cp ../AUTHORS dist/pamhyr/_internal/
cp ../LICENSE dist/pamhyr/_internal/
cp ../src/motd.txt dist/pamhyr/_internal/
mkdir -p dist/pamhyr/mage/
cp ../mage/mage dist/pamhyr/mage/
@@ -65,6 +70,18 @@ mkdir -p dist/pamhyr/tests_cases/
mkdir -p dist/pamhyr/tests_cases/Saar
cp ../tests_cases/Saar/Saar.pamhyr dist/pamhyr/tests_cases/Saar/
mkdir -p dist/pamhyr/doc/
cp ../doc/dev/documentation.pdf dist/pamhyr/doc/Pamhyr2-dev.pdf
cp ../doc/dev/documentation.html dist/pamhyr/doc/Pamhyr2-dev.html
cp ../doc/users/documentation.pdf dist/pamhyr/doc/Pamhyr2-users.pdf
cp ../doc/users/documentation.html dist/pamhyr/doc/Pamhyr2-users.html
mkdir -p dist/pamhyr/doc/images
cp ../doc/users/images/* dist/pamhyr/doc/images/
cp ../doc/dev/images/* dist/pamhyr/doc/images/
cp ../doc/images/* dist/pamhyr/doc/images/
echo " *** MAKE SRC PACKAGE"
OLD_PWD=$PWD
@@ -76,11 +93,11 @@ mv ../pamhyr-src.tar.gz ./
echo " *** MAKE BIN PACKAGE"
OLD_PWD=$PWD
cd dist/
tar --xz -cf pamhyr-gnulinux-amd64.tar.xz pamhyr --checkpoint=.100
cd $OLD_PWD
# OLD_PWD=$PWD
# cd dist/
# tar --xz -cf pamhyr-gnulinux-amd64.tar.xz pamhyr --checkpoint=.100
# cd $OLD_PWD
mv dist/pamhyr-gnulinux-amd64.tar.xz ./
# mv dist/pamhyr-gnulinux-amd64.tar.xz ./
echo " *** DONE"
+61
View File
@@ -0,0 +1,61 @@
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
copy /y ..\src\motd.txt pamhyr\pamhyr\_internal
rem MAGE
mkdir pamhyr\mage
copy /y ..\mage-windows\mage.exe pamhyr\mage\
copy /y ..\mage-windows\mage_extraire.exe pamhyr\mage\
copy /y ..\mage-windows\mailleurPF.exe pamhyr\mage\
rem Copy tests_cases
mkdir pamhyr\tests_cases
mkdir pamhyr\tests_cases\Saar
copy /y ..\tests_cases\Saar\Saar.pamhyr pamhyr\tests_cases\Saar\
rem Documentations
mkdir pamhyr\doc
mkdir pamhyr\doc\images
copy /y ..\doc\users\documentation.pdf pamhyr\doc\Pamhyr2-users.pdf
copy /y ..\doc\dev\documentation.pdf pamhyr\doc\Pamhyr2-dev.pdf
copy /y ..\doc\users\documentation.html pamhyr\doc\Pamhyr2-users.html
copy /y ..\doc\dev\documentation.html pamhyr\doc\Pamhyr2-dev.html
copy /y ..\doc\users\images\* pamhyr\doc\images
copy /y ..\doc\dev\images\* pamhyr\doc\images
copy /y ..\doc\images\* pamhyr\doc\images
+20
View File
@@ -0,0 +1,20 @@
rem windows.bat -- Pamhyr Windows batch for windows version building
rem Copyright (C) 2023 INRAE
rem
rem This program is free software: you can redistribute it and/or modify
rem it under the terms of the GNU General Public License as published by
rem the Free Software Foundation, either version 3 of the License, or
rem (at your option) any later version.
rem
rem This program is distributed in the hope that it will be useful,
rem but WITHOUT ANY WARRANTY; without even the implied warranty of
rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
rem GNU General Public License for more details.
rem
rem You should have received a copy of the GNU General Public License
rem along with this program. If not, see <https://www.gnu.org/licenses/>.
@ECHO ON
rem Make installer
"C:\Program Files (x86)\NSIS\makensis.exe" pamhyr.nsi
+23 -19
View File
@@ -3,17 +3,17 @@
!define LIC_NAME "LICENSE"
!define APP_NAME "PAMHYR"
Name "PAMHYR"
OutFile "pamhyr-win-amd64.exe"
LicenseData "..\LICENSE"
Name "Pamhyr2"
OutFile "pamhyr-windows.exe"
LicenseData "pamhyr\pamhyr\_internal\LICENSE"
LicenseText "I Agree"
RequestExecutionLevel admin
Unicode True
InstallDir $PROGRAMFILES\PAMHYR
InstallDir $PROGRAMFILES\Pamhyr2
InstallDirRegKey HKLM "Software\PAMHYR" "Install_Dir"
InstallDirRegKey HKLM "Software\Pamhyr2" "Install_Dir"
Page license
Page components
@@ -23,35 +23,39 @@ Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
Section "PAMHYR"
Section "Pamhyr2"
SectionIn RO
SetOutPath $INSTDIR
File /r "dist\pamhyr\*"
File /r "pamhyr\*"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "DisplayName" "NSIS PAMHYR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR" "NoRepair" 1
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "DisplayName" "NSIS Pamhyr2"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2" "NoRepair" 1
WriteUninstaller "$INSTDIR\uninstall.exe"
SectionEnd
Section "Start Menu Shortcuts"
CreateDirectory "$SMPROGRAMS\Pamhyr2"
CreateShortcut "$SMPROGRAMS\Pamhyr2\Uninstall.lnk" "$INSTDIR\uninstall.exe"
CreateShortcut "$SMPROGRAMS\Pamhyr2\Pamhyr2.lnk" "$INSTDIR\pamhyr\pamhyr.exe"
SectionEnd
CreateDirectory "$SMPROGRAMS\PAMHYR"
CreateShortcut "$SMPROGRAMS\PAMHYR\Uninstall.lnk" "$INSTDIR\uninstall.exe"
CreateShortcut "$SMPROGRAMS\PAMHYR\PAMHYR.lnk" "$INSTDIR\pamhyr.exe"
Section "Desktop Shortcuts"
SetShellVarContext all
CreateShortcut "$desktop\Pamhyr2.lnk" "$instdir\pamhyr\pamhyr.exe"
SectionEnd
Section "Uninstall"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PAMHYR"
DeleteRegKey HKLM SOFTWARE\NSIS_PAMHYR
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pamhyr2"
DeleteRegKey HKLM SOFTWARE\NSIS_Pamhyr2
Delete "$SMPROGRAMS\PAMHYR\*.lnk"
Delete "$SMPROGRAMS\Pamhyr2\*.lnk"
Delete "$desktop\Pamhyr2.lnk"
RMDir "$SMPROGRAMS\PAMHYR"
RMDir "$SMPROGRAMS\Pamhyr2"
RMDir /R "$INSTDIR"
SectionEnd
+45 -16
View File
@@ -81,13 +81,42 @@ wine pyinstaller ../src/pamhyr.py -y
echo " *** COPY DATA"
mkdir -p dist/pamhyr/View/ui
cp -r ../src/View/ui/ressources/ dist/pamhyr/View/ui/
cp -r ../src/View/ui/Widgets/*.ui dist/pamhyr/View/ui/
cp -r ../src/View/ui/*.ui dist/pamhyr/View/ui/
mkdir -p pamhyr
mkdir -p pamhyr/pamhyr
cp ../VERSION dist/pamhyr/
cp ../AUTHORS dist/pamhyr/
# Copy Pamhyr info
cp -v ../VERSION pamhyr/pamhyr/
cp -v ../AUTHORS pamhyr/pamhyr/
cp -v ../LICENSE pamhyr/pamhyr/
# Copy mage
mkdir -p pamhyr/mage
cp -v ../mage/* pamhyr/mage/
# Copy Pamhyr
cp -r dist/pamhyr pamhyr/
# Copy Pamhyr resources
mkdir -p pamhyr/pamhyr/View
mkdir -p pamhyr/pamhyr/View/ui
mkdir -p pamhyr/pamhyr/View/ui/Widgets
cp -r ../src/View/ui/*.ui pamhyr/pamhyr/View/ui/
cp -r ../src/View/ui/ressources/ pamhyr/pamhyr/View/ui
cp -r ../src/View/ui/Widgets/*.ui pamhyr/pamhyr/View/ui/Widgets
# Copy lang
mkdir -p pamhyr/pamhyr/lang
cp -r ../src/lang/*.qm pamhyr/pamhyr/lang
# Copy tests cases
mkdir -p pamhyr/tests_cases/
mkdir -p pamhyr/tests_cases/Saar
cp ../tests_cases/Saar/Saar.pamhyr pamhyr/tests_cases/Saar/
# Copy documentations
mkdir -p pamhyr/doc/
cp ../doc/users/documentation.pdf pamhyr/doc/Pamhyr2-users.pdf
cp ../doc/dev/documentation.pdf pamhyr/doc/Pamhyr2-dev.pdf
# Update TS and build QM files
OLD_PWD=$PWD
@@ -102,19 +131,19 @@ echo " *** MAKE PACKAGES"
echo " TAR pamhyr-win-amd64.tar.xz"
OLD_PWD=$PWD
cd dist/
tar --xz -cf pamhyr-win-amd64.tar.xz pamhyr --checkpoint=.100
cd $OLD_PWD
mv dist/pamhyr-win-amd64.tar.xz ./
# OLD_PWD=$PWD
# cd dist/
# tar --xz -cf pamhyr-win-amd64.tar.xz pamhyr --checkpoint=.100
# cd $OLD_PWD
# mv dist/pamhyr-win-amd64.tar.xz ./
echo " ZIP pamhyr-win-amd64.zip"
OLD_PWD=$PWD
cd dist/
zip -r pamhyr-win-amd64.zip pamhyr/
cd $OLD_PWD
mv dist/pamhyr-win-amd64.zip ./
# OLD_PWD=$PWD
# cd dist/
# zip -r pamhyr-win-amd64.zip pamhyr/
# cd $OLD_PWD
# mv dist/pamhyr-win-amd64.zip ./
echo " *** MAKE INSTALLER"
+3 -1
View File
@@ -1,6 +1,8 @@
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.8.1
PyQt5-sip==12.12.2
#PyQtWebEngine==5.15.6
QScintilla>=2.14.1
pyqtgraph>=0.12.1
matplotlib>=3.4.1
numpy>=1.24.2
+8
View File
@@ -20,12 +20,14 @@ from enum import Enum
from Model.Except import NotImplementedMethodeError
class STATUS(Enum):
UNKNOWN = -1
OK = 0
WARNING = 1
ERROR = 2
class AbstractModelChecker(object):
def __init__(self):
self._name = ""
@@ -62,6 +64,12 @@ class AbstractModelChecker(object):
# Abstract function
def _run(self, study):
from Model.Study import Study
thread_study = Study.open(study._filename)
self.run(thread_study)
def run(self, study):
"""Run checker function
+6 -3
View File
@@ -27,8 +27,9 @@ from Checker.Checker import AbstractModelChecker, STATUS
_translate = QCoreApplication.translate
class MageNetworkGraphChecker(AbstractModelChecker):
def __init__(self, connectivity = True):
def __init__(self, connectivity=True):
super(MageNetworkGraphChecker, self).__init__()
self._mode_conn = connectivity
@@ -38,8 +39,10 @@ class MageNetworkGraphChecker(AbstractModelChecker):
else:
mode = "cycle"
self._name = _translate("Checker", f"Mage network graph {mode} checker")
self._description = _translate("Checker", "Check if the network graph is valid")
self._name = _translate(
"Checker", f"Mage network graph {mode} checker")
self._description = _translate(
"Checker", "Check if the network graph is valid")
@timer
def _connectivity(self, summary, status, graph):
+6 -2
View File
@@ -30,7 +30,8 @@ class StudyNetworkReachChecker(AbstractModelChecker):
super(StudyNetworkReachChecker, self).__init__()
self._name = _translate("Checker", "Study network reach checker")
self._description = _translate("Checker", "Check if exists at least one reach for study")
self._description = _translate(
"Checker", "Check if exists at least one reach for study")
def run(self, study):
if study is None:
@@ -54,12 +55,14 @@ class StudyNetworkReachChecker(AbstractModelChecker):
self._status = STATUS.OK
return True
class StudyGeometryChecker(AbstractModelChecker):
def __init__(self):
super(StudyGeometryChecker, self).__init__()
self._name = _translate("Checker", "Study geometry checker")
self._description = _translate("Checker", "Check if exists geometry for each reach of study")
self._description = _translate(
"Checker", "Check if exists geometry for each reach of study")
self._reachs = []
def run(self, study):
@@ -128,6 +131,7 @@ class DummyWARNING(AbstractModelChecker):
return True
class DummyERROR(AbstractModelChecker):
def __init__(self):
super(DummyERROR, self).__init__()
-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,17 +20,18 @@ import logging
from tools import trace, timer, old_pamhyr_date_to_timestamp
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
class BoundaryCondition(SQLSubModel):
_sub_classes = []
_id_cnt = 0
def __init__(self, id:int = -1, name:str = "",
status = None):
def __init__(self, id: int = -1, name: str = "",
status=None):
super(BoundaryCondition, self).__init__()
self._status = status
@@ -101,7 +102,7 @@ class BoundaryCondition(SQLSubModel):
return res
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = []
tab = data["tab"]
@@ -115,9 +116,9 @@ class BoundaryCondition(SQLSubModel):
t = row[2]
ctor = cls._get_ctor_from_type(t)
bc = ctor(
id = row[0],
name = row[1],
status = data['status']
id=row[0],
name=row[1],
status=data['status']
)
bc.node = None
@@ -143,7 +144,7 @@ class BoundaryCondition(SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
tab = data["tab"]
execute(f"DELETE FROM boundary_condition WHERE id = {self.id}")
@@ -155,7 +156,7 @@ class BoundaryCondition(SQLSubModel):
sql = (
"INSERT INTO " +
"boundary_condition(id, name, type, tab, node) "+
"boundary_condition(id, name, type, tab, node) " +
"VALUES (" +
f"{self.id}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', '{tab}', {node}" +
@@ -170,7 +171,7 @@ class BoundaryCondition(SQLSubModel):
sql = (
"INSERT INTO " +
"boundary_condition_data(ind, data0, data1, bc) "+
"boundary_condition_data(ind, data0, data1, bc) " +
f"VALUES ({ind}, '{data0}', {data1}, {self.id})"
)
execute(sql)
@@ -187,7 +188,7 @@ class BoundaryCondition(SQLSubModel):
@classmethod
def time_convert(cls, data):
if type(data) == str and data.count(":") == 3:
if data is str and data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
return int(data)
@@ -246,7 +247,7 @@ class BoundaryCondition(SQLSubModel):
new_1 = self._default_1
if len(header) != 0:
for i in [0,1]:
for i in [0, 1]:
for j in range(len(header)):
if self._header[i] == header[j]:
if i == 0:
@@ -259,13 +260,13 @@ class BoundaryCondition(SQLSubModel):
return (new_0, new_1)
def add(self, index:int):
def add(self, index: int):
value = (self._default_0, self._default_1)
self._data.insert(index, value)
self._status.modified()
return value
def insert(self, index:int, value):
def insert(self, index: int, value):
self._data.insert(index, value)
self._status.modified()
@@ -301,11 +302,10 @@ class BoundaryCondition(SQLSubModel):
return self.data[index]
def get_range(self, _range):
l = []
lst = []
for r in _range:
l.append(r)
return l
lst.append(r)
return lst
def _set_i_c_v(self, index, column, value):
v = list(self._data[index])
@@ -313,22 +313,22 @@ class BoundaryCondition(SQLSubModel):
self._data[index] = tuple(v)
self._status.modified()
def set_i_0(self, index:int, value):
def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value)
def set_i_1(self, index:int, value):
def set_i_1(self, index: int, value):
self._set_i_c_v(index, 1, value)
@timer
def convert(self, cls):
new = cls(name = self.name, status = self._status)
new = cls(name=self.name, status=self._status)
new.node = self.node
for i, _ in enumerate(self.data):
new.add(i)
for i in [0,1]:
for j in [0,1]:
for i in [0, 1]:
for j in [0, 1]:
if self._header[i] == new.header[j]:
for ind, v in self.data:
try:
@@ -19,7 +19,7 @@
from copy import copy
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrList import PamhyrModelListWithTab
from Model.Except import NotImplementedMethodeError
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
@@ -30,33 +30,16 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
Solid,
)
class BoundaryConditionList(SQLSubModel):
class BoundaryConditionList(PamhyrModelListWithTab):
_tabs_list = ["liquid", "solid", "suspenssion"]
_sub_classes = [
BoundaryCondition,
]
def __init__(self, status = None):
super(BoundaryConditionList, self).__init__()
self._status = status
self._tabs = {
"liquid" : [],
"solid" : [],
"suspenssion" : []
}
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
return True
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data['status'])
def _sql_load(cls, execute, data=None):
new = cls(status=data['status'])
if data is None:
data = {}
@@ -69,7 +52,7 @@ class BoundaryConditionList(SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
execute("DELETE FROM boundary_condition")
if data is None:
@@ -78,83 +61,29 @@ class BoundaryConditionList(SQLSubModel):
for tab in self._tabs:
data["tab"] = tab
for bc in self._tabs[tab]:
bc._sql_save(execute, data = data)
bc._sql_save(execute, data=data)
return True
def len(self, lst):
return len(self._tabs[lst])
def get_tab(self, lst):
return self._tabs[lst].copy()
def get(self, lst, row):
return self._tabs[lst][row]
def set(self, lst, row, new):
self._tabs[lst][row] = new
self._status.modified()
def new(self, lst, index):
n = NotDefined(status=self._status)
self._tabs[lst].insert(index, n)
self._status.modified()
return n
def insert(self, lst, index, new):
self._tabs[lst].insert(index, new)
self._status.modified()
def delete(self, lst, bcs):
for bc in bcs:
self._tabs[lst].remove(bc)
self._status.modified()
def delete_i(self, lst, indexes):
bcs = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._tabs[lst])
)
)
)
self.delete(lst, bcs)
def sort(self, lst, reverse=False, key=None):
self._tabs[lst].sort(reverse=reverse, key=key)
self._status.modified()
def move_up(self, lst, index):
if index < len(self._tabs[lst]):
next = index - 1
l = self._tabs[lst]
l[index], l[next] = l[next], l[index]
self._status.modified()
def move_down(self, lst, index):
if index >= 0:
prev = index + 1
l = self._tabs[lst]
l[index], l[prev] = l[prev], l[index]
self._status.modified()
def __copy__(self):
new = BoundaryConditionList()
for l in self._tabs:
new.tabs[l] = self._tabs[l].copy()
for lst in self._tabs:
new.tabs[lst] = self._tabs[lst].copy()
return new
def __deepcopy__(self):
new = BoundaryConditionList()
for l in self._tabs:
new.tabs[l] = self._tabs[l].deepcopy()
for lst in self._tabs:
new.tabs[lst] = self._tabs[lst].deepcopy()
return new
@@ -22,7 +22,7 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
class NotDefined(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(NotDefined, self).__init__(id=id, name=name, status=status)
self._type = "ND"
@@ -32,9 +32,11 @@ class NotDefined(BoundaryCondition):
def _default_0(self):
return 0.0
class PonctualContribution(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
super(PonctualContribution, self).__init__(id=id, name=name, status=status)
def __init__(self, id: int = -1, name: str = "", status=None):
super(PonctualContribution, self).__init__(
id=id, name=name, status=status)
self._type = "PC"
self._header = ["time", "discharge"]
@@ -44,8 +46,9 @@ class PonctualContribution(BoundaryCondition):
def compatibility(cls):
return ["liquid"]
class TimeOverZ(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(TimeOverZ, self).__init__(id=id, name=name, status=status)
self._type = "TZ"
@@ -56,9 +59,11 @@ class TimeOverZ(BoundaryCondition):
def compatibility(cls):
return ["liquid"]
class TimeOverDischarge(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
super(TimeOverDischarge, self).__init__(id=id, name=name, status=status)
def __init__(self, id: int = -1, name: str = "", status=None):
super(TimeOverDischarge, self).__init__(
id=id, name=name, status=status)
self._type = "TD"
self._header = ["time", "discharge"]
@@ -68,8 +73,9 @@ class TimeOverDischarge(BoundaryCondition):
def compatibility(cls):
return ["liquid"]
class ZOverDischarge(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(ZOverDischarge, self).__init__(id=id, name=name, status=status)
self._type = "ZD"
@@ -86,7 +92,7 @@ class ZOverDischarge(BoundaryCondition):
class Solid(BoundaryCondition):
def __init__(self, id:int = -1, name:str = "", status = None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(Solid, self).__init__(id=id, name=name, status=status)
self.d50 = 0.002
+11 -5
View File
@@ -45,8 +45,9 @@ def exception_message_box(exception):
# Custom error #
################
class ExeceptionWithMessageBox(Exception):
def __init__(self, title = "Exeception"):
def __init__(self, title="Exeception"):
self.title = title
def header(self):
@@ -72,7 +73,7 @@ class ExeceptionWithMessageBox(Exception):
class NotImplementedMethodeError(ExeceptionWithMessageBox):
def __init__(self, obj, func):
super(NotImplementedMethodeError, self).__init__(
title = _translate("Exception", "Method not implemented")
title=_translate("Exception", "Method not implemented")
)
self.obj = obj
@@ -81,12 +82,16 @@ class NotImplementedMethodeError(ExeceptionWithMessageBox):
self.alert()
def __str__(self):
class_type = self.obj
if self.obj.__class__ != type:
class_type = self.obj.__class__
return (
_translate("Exception", "Method") +
f" '{self.func.__name__}' " +
_translate("Exception", "not implemented") +
_translate("Exception", "for class") +
f" '{self.obj.__class__ if self.obj.__class__ != type else self.obj}'"
f" '{class_type}'"
)
def header(self):
@@ -104,10 +109,11 @@ class NotImplementedMethodeError(ExeceptionWithMessageBox):
f" '{self.obj.__class__}'"
)
class FileFormatError(ExeceptionWithMessageBox):
def __init__(self, filename, reason):
super(FileFormatError, self).__init__(
title = _translate("Exception", "FileFormatError")
title=_translate("Exception", "FileFormatError")
)
self.reason = reason
@@ -137,7 +143,7 @@ class FileFormatError(ExeceptionWithMessageBox):
class ClipboardFormatError(ExeceptionWithMessageBox):
def __init__(self, mime=None, header=None, data=None):
super(ClipboardFormatError, self).__init__(
title = _translate("Exception", "Clipboard format error")
title=_translate("Exception", "Clipboard format error")
)
self._mime = mime
+10 -10
View File
@@ -18,10 +18,11 @@
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
class Friction(SQLSubModel):
def __init__(self, name:str = "", status = None):
def __init__(self, name: str = "", status=None):
super(Friction, self).__init__()
self._status = status
@@ -57,7 +58,7 @@ class Friction(SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = []
reach = data["parent"] # Reach object
status = data["status"]
@@ -78,22 +79,21 @@ class Friction(SQLSubModel):
es = next(filter(lambda s: s.id == row[4], stricklers))
# Create friction
sec = cls(status = status)
sec = cls(status=status)
sec.edge = reach
sec.begin_kp = row[1]
sec.end_kp = row[2]
sec.begin_strickler = bs
sec.end_strickler = es
new[ind] = sec
yield ind, sec
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
ind = data["ind"]
execute(
"INSERT INTO " +
"friction(ind, begin_kp, end_kp, reach, begin_strickler, end_strickler) " +
"friction(ind, begin_kp, end_kp, " +
"reach, begin_strickler, end_strickler) " +
"VALUES (" +
f"{ind}, {self._begin_kp}, {self._end_kp}, " +
f"{self._edge.id}, " +
@@ -121,7 +121,7 @@ class Friction(SQLSubModel):
self._edge = edge
if (edge is not None and
self._begin_kp == 0.0 and
self._end_kp == 0.0):
self._end_kp == 0.0):
self._begin_kp = self._edge.reach.get_kp_min()
self._end_kp = self._edge.reach.get_kp_max()
self._status.modified()
+34 -72
View File
@@ -21,26 +21,19 @@ import logging
from copy import copy
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Tools.PamhyrList import PamhyrModelList
from Model.Friction.Friction import Friction
logger = logging.getLogger()
class FrictionList(SQLSubModel):
class FrictionList(PamhyrModelList):
_sub_classes = [
Friction
]
def __init__(self, status = None):
super(FrictionList, self).__init__()
self._status = status
self._frictions = []
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update_0_0_1(cls, execute, version):
execute("ALTER TABLE `section` RENAME TO `friction`")
@@ -54,85 +47,54 @@ class FrictionList(SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data['status'])
def _sql_load(cls, execute, data=None):
new = cls(status=data['status'])
new._frictions = Friction._sql_load(
new._lst = Friction._sql_load(
execute, data
)
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
reach = data["reach"]
execute(f"DELETE FROM friction WHERE reach = {reach.id}")
ok = True
ind = 0
for friction in self._frictions:
for friction in self._lst:
data["ind"] = ind
ok &= friction._sql_save(execute, data = data)
ok &= friction._sql_save(execute, data=data)
ind += 1
return ok
def __len__(self):
return len(self._frictions)
@property
def frictions(self):
return self._frictions.copy()
def get(self, row):
return self._frictions[row]
def set(self, row, new):
self._frictions[row] = new
self._status.modified()
def new(self, index):
n = Friction(status = self._status)
self._frictions.insert(index, n)
self._status.modified()
return n
def insert(self, index, new):
self._frictions.insert(index, new)
self._status.modified()
def delete(self, frictions):
for friction in frictions:
self._frictions.remove(friction)
self._status.modified()
def delete_i(self, indexes):
frictions = list(
def _get_frictions_list(self):
# Frictions list generator is type (int, Point) with the first
# element the index of the Point in list
return list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._frictions)
lambda p: p[1],
sorted(
self._lst,
key=lambda p: p[0]
)
)
)
self.delete(frictions)
def sort(self, reverse=False, key=None):
self._frictions.sort(reverse=reverse, key=key)
@property
def lst(self):
if not isinstance(self._lst, list):
self._lst = self._get_frictions_list()
return self._lst
@property
def frictions(self):
return self.lst
def new(self, index):
n = Friction(status=self._status)
self._lst.insert(index, n)
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()
return n
+4 -4
View File
@@ -18,8 +18,9 @@
from Model.Except import NotImplementedMethodeError
class Point(object):
def __init__(self, name:str = "", profile=None, status = None):
def __init__(self, name: str = "", profile=None, status=None):
super(Point, self).__init__()
self._status = status
@@ -28,7 +29,6 @@ class Point(object):
self._profile = profile
self._sl = None
@property
def sl(self):
if self._sl is None:
@@ -40,8 +40,8 @@ class Point(object):
def sl(self, sl):
if sl == self._profile.sl:
self._sl = None
self._sl = sl
else:
self._sl = sl
@property
def name(self):
+4 -3
View File
@@ -20,10 +20,11 @@ from math import dist
from Model.Geometry.Point import Point
class PointAC(Point):
def __init__(self, a:float = 0.0, c:float = 0.0,
name: str = "", status = None):
super(PointXY, self).__init__(name = name, status = status)
def __init__(self, a: float = 0.0, c: float = 0.0,
name: str = "", status=None):
super(PointXY, self).__init__(name=name, status=status)
self._a = float(a)
self._c = float(c)
+15 -23
View File
@@ -19,15 +19,17 @@
from math import dist
import numpy as np
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Geometry.Point import Point
class PointXYZ(Point, SQLSubModel):
_sub_classes = []
def __init__(self, x:float = 0.0, y:float = 0.0, z:float = 0.0,
name:str = "", profile = None, status = None):
super(PointXYZ, self).__init__(name=name, profile=profile, status=status)
def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0,
name: str = "", profile=None, status=None):
super(PointXYZ, self).__init__(
name=name, profile=profile, status=status)
self._x = float(x)
self._y = float(y)
@@ -69,9 +71,7 @@ class PointXYZ(Point, SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
points = []
def _sql_load(cls, execute, data=None):
status = data["status"]
profile = data["profile"]
@@ -81,10 +81,6 @@ class PointXYZ(Point, SQLSubModel):
f"WHERE profile = {profile.id}"
)
# Create points list
for _ in table:
points.append(None)
# Fill points list with new point
for row in table:
ind = row[0]
@@ -95,13 +91,13 @@ class PointXYZ(Point, SQLSubModel):
sl = row[5]
new = cls(
name = name,
x = x, y = y, z = z,
profile = profile,
status = status
name=name,
x=x, y=y, z=z,
profile=profile,
status=status
)
if sl == -1 or sl == None:
if sl == -1 or sl is None:
new._sl = None
else:
new._sl = next(
@@ -111,11 +107,9 @@ class PointXYZ(Point, SQLSubModel):
)
)
points[ind] = new
yield ind, new
return points
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
profile = data["profile"]
ind = data["ind"]
@@ -123,7 +117,7 @@ class PointXYZ(Point, SQLSubModel):
sql = (
"INSERT OR REPLACE INTO " +
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) "+
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) " +
"VALUES (" +
f"{ind}, '{self._sql_format(self._name)}', " +
f"{self.x}, {self.y}, {self.z}, " +
@@ -134,7 +128,6 @@ class PointXYZ(Point, SQLSubModel):
return True
@classmethod
def from_data(cls, header, data):
point = None
@@ -157,7 +150,6 @@ class PointXYZ(Point, SQLSubModel):
return point
def __repr__(self):
return f"({self._x}, {self._y}, {self._z}, {self._name})"
+47 -29
View File
@@ -25,14 +25,15 @@ from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
class Profile(object):
_id_cnt = 0
def __init__(self, id:int = -1, num:int = 0,
kp:float = 0.0, name:str = "",
code1:int = 0, code2:int = 0,
_type:str = "", reach = None,
status = None):
def __init__(self, id: int = -1, num: int = 0,
kp: float = 0.0, name: str = "",
code1: int = 0, code2: int = 0,
_type: str = "", reach=None,
status=None):
super(Profile, self).__init__()
self._status = status
@@ -56,16 +57,35 @@ class Profile(object):
self._profile_type = _type
def __len__(self):
return len(self.points)
@property
def number_points(self):
return len(self._points)
return len(self.points)
def _get_points_list(self):
# Points list generator is type (int, Point) with the first
# element the index of the Point in list
return list(
map(
lambda p: p[1],
sorted(
self._points,
key=lambda p: p[0]
)
)
)
@property
def points(self):
return self._points.copy()
if not isinstance(self._points, list):
self._points = self._get_points_list()
return self._points
def point(self, index):
return self._points[index]
return self.points[index]
@property
def reach(self):
@@ -112,7 +132,7 @@ class Profile(object):
@property
def nb_points(self):
return len(self._points)
return len(self.points)
@property
def kp(self):
@@ -166,9 +186,9 @@ class Profile(object):
self._profile_type = value
self._status.modified()
def point(self, i:int):
if i < len(self._points):
return self._points[i]
def point(self, i: int):
if i < len(self.points):
return self.points[i]
return None
@@ -178,11 +198,10 @@ class Profile(object):
Returns:
The list of named point
"""
return [point for point in self._points
return [point for point in self.points
if point.point_is_named()]
def insert_point(self, index: int, point:Point):
def insert_point(self, index: int, point: Point):
"""Insert point at index.
Args:
@@ -192,10 +211,9 @@ class Profile(object):
Returns:
Nothing.
"""
self._points.insert(index, point)
self.points.insert(index, point)
self._status.modified()
def delete(self, indexes: int):
"""Delete points at index
@@ -215,7 +233,7 @@ class Profile(object):
)
)
self._points = list(
self.points = list(
filter(
lambda p: p not in points,
self.points
@@ -232,7 +250,7 @@ class Profile(object):
Returns:
Nothing.
"""
self._points = list(
self.points = list(
filter(
lambda p: p not in points,
self.points
@@ -243,10 +261,10 @@ class Profile(object):
# Move
def move_up_point(self, index: int):
if index < len(self._points):
if index < len(self.points):
next = index - 1
p = self._points
p = self.points
p[index], p[next] = p[next], p[index]
self._status.modified()
@@ -254,7 +272,7 @@ class Profile(object):
if index >= 0:
prev = index + 1
p = self._points
p = self.points
p[index], p[prev] = p[prev], p[index]
self._status.modified()
@@ -262,14 +280,14 @@ class Profile(object):
@timer
def sort(self, column, is_reversed: bool = False):
predicate = lambda p: p.x
def predicate(p): return p.x
if column == 'y':
predicate = lambda p: p.y
def predicate(p): return p.y
elif column == 'z':
predicate = lambda p: p.z
def predicate(p): return p.z
self._points = sorted(
self._points,
self.points = sorted(
self.points,
key=predicate,
reverse=is_reversed
)
@@ -277,10 +295,10 @@ class Profile(object):
@timer
def sort_with_indexes(self, indexes: list):
if len(self._points) != len(indexes):
if len(self.points) != len(indexes):
logger.critical("Indexes list do not correspond to point list")
self._points = list(
self.points = list(
map(
lambda x: x[1],
sorted(
+58 -52
View File
@@ -16,17 +16,21 @@
# -*- coding: utf-8 -*-
import logging
import numpy as np
from typing import List
from tools import timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import ClipboardFormatError
from Model.Geometry.Profile import Profile
from Model.Geometry.PointXYZ import PointXYZ
from Model.Geometry.Vector_1d import Vector1d
logger = logging.getLogger()
class ProfileXYZ(Profile, SQLSubModel):
_sub_classes = [
PointXYZ,
@@ -36,11 +40,11 @@ class ProfileXYZ(Profile, SQLSubModel):
id: int = -1,
name: str = "",
kp: float = 0.,
reach = None,
num = 0,
reach=None,
num=0,
nb_point: int = 0,
code1: int = 0, code2: int = 0,
status = None):
status=None):
"""ProfileXYZ constructor
Args:
@@ -54,14 +58,14 @@ class ProfileXYZ(Profile, SQLSubModel):
Nothing.
"""
super(ProfileXYZ, self).__init__(
id = id,
num = num,
name = name,
kp = kp,
code1 = code1, code2 = code2,
_type = "XYZ",
reach = reach,
status = status,
id=id,
num=num,
name=name,
kp=kp,
code1=code1, code2=code2,
_type="XYZ",
reach=reach,
status=status,
)
@classmethod
@@ -100,7 +104,7 @@ class ProfileXYZ(Profile, SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
profiles = []
status = data["status"]
reach = data["reach"]
@@ -108,7 +112,7 @@ class ProfileXYZ(Profile, SQLSubModel):
table = execute(
"SELECT id, ind, name, kp, num, code1, code2, sl " +
"FROM geometry_profileXYZ " +
f"WHERE reach = {reach}"
f"WHERE reach = {reach.id}"
)
for _ in table:
@@ -125,14 +129,14 @@ class ProfileXYZ(Profile, SQLSubModel):
sl = row[7]
new = cls(
id=id, num = num,
name = name, kp = kp,
code1 = code1, code2 = code2,
reach = data["parent"],
status = status
id=id, num=num,
name=name, kp=kp,
code1=code1, code2=code2,
reach=data["parent"],
status=status
)
if sl == -1 or sl == None:
if sl == -1 or sl is None:
new._sl = None
else:
new._sl = next(
@@ -143,13 +147,15 @@ class ProfileXYZ(Profile, SQLSubModel):
)
data["profile"] = new
new._points = PointXYZ._sql_load(execute, data)
new._points = PointXYZ._sql_load(execute, data.copy())
profiles[ind] = new
yield ind, new
return profiles
# profiles[ind] = new
def _sql_save(self, execute, data = None):
# return profiles
def _sql_save(self, execute, data=None):
ok = True
ind = data["ind"]
@@ -157,7 +163,8 @@ class ProfileXYZ(Profile, SQLSubModel):
sql = (
"INSERT OR REPLACE INTO " +
"geometry_profileXYZ(id, ind, name, reach, kp, num, code1, code2, sl) "+
"geometry_profileXYZ(id, ind, name, reach, " +
"kp, num, code1, code2, sl) " +
"VALUES (" +
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
f"{self.reach.id}, {self.kp}, {self.num}, " +
@@ -170,7 +177,7 @@ class ProfileXYZ(Profile, SQLSubModel):
execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}")
ind = 0
for point in self._points:
for point in self.points:
data["ind"] = ind
ok &= point._sql_save(execute, data)
ind += 1
@@ -220,18 +227,17 @@ class ProfileXYZ(Profile, SQLSubModel):
return point
def x(self):
return [point.x for point in self._points]
return [point.x for point in self.points]
def y(self):
return [point.y for point in self._points]
return [point.y for point in self.points]
def z(self):
return [point.z for point in self._points]
return [point.z for point in self.points]
def names(self):
return [point.name for point in self._points]
return [point.name for point in self.points]
def x_max(self):
return max(self.filter_isnan(self.x()))
@@ -262,7 +268,7 @@ class ProfileXYZ(Profile, SQLSubModel):
"""
for point in list_points:
pt = PointXYZ(*point, profile=self, status=self._status)
self._points.append(pt)
self.points.append(pt)
self._status.modified()
def get_point_i(self, index: int) -> PointXYZ:
@@ -275,7 +281,7 @@ class ProfileXYZ(Profile, SQLSubModel):
The point.
"""
try:
return self._points[index]
return self.points[index]
except IndexError:
raise IndexError(f"Invalid point index: {index}")
@@ -286,7 +292,7 @@ class ProfileXYZ(Profile, SQLSubModel):
Nothing.
"""
point_xyz = PointXYZ(0., 0., 0., profile=self, status=self._status)
self._points.append(point_xyz)
self.points.append(point_xyz)
self._status.modified()
def insert(self, index: int):
@@ -299,7 +305,7 @@ class ProfileXYZ(Profile, SQLSubModel):
The new point.
"""
point = PointXYZ(0., 0., 0., profile=self, status=self._status)
self._points.insert(index, point)
self.points.insert(index, point)
self._status.modified()
return point
@@ -315,9 +321,9 @@ class ProfileXYZ(Profile, SQLSubModel):
return [x for x in lst if not np.isnan(x)]
def _first_point_not_nan(self):
first_point = self._points[0]
first_point = self.points[0]
for point in self._points:
for point in self.points:
if not point.is_nan():
first_point = point
break
@@ -325,9 +331,9 @@ class ProfileXYZ(Profile, SQLSubModel):
return first_point
def _last_point_not_nan(self):
last_point = self._points[-1]
last_point = self.points[-1]
for point in self._points[::-1]:
for point in self.points[::-1]:
if not point.is_nan():
last_point = point
break
@@ -354,13 +360,13 @@ class ProfileXYZ(Profile, SQLSubModel):
first_point_not_nan = self._first_point_not_nan()
last_point_not_nan = self._last_point_not_nan()
for index, point in enumerate(self._points):
for index, point in enumerate(self.points):
if point.point_is_named():
index_first_named_point = index
first_named_point = point
break
for point in reversed(self._points):
for point in reversed(self.points):
if point.point_is_named():
last_named_point = point
break
@@ -369,32 +375,32 @@ class ProfileXYZ(Profile, SQLSubModel):
constant = 0.0
if (first_named_point is not None and
last_named_point is not None):
last_named_point is not None):
if (first_named_point != last_named_point and
first_named_point.x != last_named_point.x):
first_named_point.x != last_named_point.x):
vector = Vector1d(first_named_point, last_named_point)
normalized_direction_vec = vector.normalized_direction_vector()
norm_dir_vec = vector.normalized_direction_vector()
else:
vector = Vector1d(first_point_not_nan, last_point_not_nan)
normalized_direction_vec = vector.normalized_direction_vector()
norm_dir_vec = vector.normalized_direction_vector()
for point in self._points:
for point in self.points:
xi = point.x - first_named_point.x
yi = point.y - first_named_point.y
station_i = (normalized_direction_vec[0] * xi +
normalized_direction_vec[1] * yi)
station_i = (norm_dir_vec[0] * xi +
norm_dir_vec[1] * yi)
station.append(station_i)
constant = station[index_first_named_point]
elif first_named_point is None:
vector = Vector1d(first_point_not_nan, last_point_not_nan)
normalized_direction_vec = vector.normalized_direction_vector()
norm_dir_vec = vector.normalized_direction_vector()
for point in self._points:
for point in self.points:
xi = point.x - first_point_not_nan.x
yi = point.y - first_point_not_nan.y
station_i = (normalized_direction_vec[0] * xi +
normalized_direction_vec[1] * yi)
station_i = (norm_dir_vec[0] * xi +
norm_dir_vec[1] * yi)
station.append(station_i)
z_min = self.z_min()
+61 -37
View File
@@ -27,7 +27,7 @@ from functools import reduce
from tools import flatten, timer, trace
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Geometry.Profile import Profile
from Model.Geometry.ProfileXYZ import ProfileXYZ
@@ -36,13 +36,14 @@ from Model.Except import FileFormatError, exception_message_box
logger = logging.getLogger()
class Reach(SQLSubModel):
_sub_classes = [
ProfileXYZ,
]
def __init__(self, status=None, parent=None):
self.id = parent.id
self.id = parent.id if parent is not None else 0
self._status = status
self._parent = parent
self._profiles: List[Profile] = []
@@ -59,17 +60,17 @@ class Reach(SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data["status"], parent = data["parent"])
def _sql_load(cls, execute, data=None):
new = cls(status=data["status"], parent=data["parent"])
new._profiles = ProfileXYZ._sql_load(
execute,
data = data
data=data
)
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
# Delete old data
execute(f"DELETE FROM geometry_profileXYZ WHERE reach = {self.id}")
# execute(f"DELETE FROM geometry_pointXYZ")
@@ -78,7 +79,7 @@ class Reach(SQLSubModel):
data = {}
ind = 0
for profile in self._profiles:
for profile in self.profiles:
data["ind"] = ind
profile._sql_save(execute, data)
ind += 1
@@ -94,26 +95,49 @@ class Reach(SQLSubModel):
Returns:
The profile at index i
"""
if i < len(self._profiles):
return self._profiles[i]
if i < len(self.profiles):
return self.profiles[i]
return None
@property
def name(self):
if self._parent == None:
if self._parent is None:
return ""
return self._parent.name
def _get_profiles_list(self):
# Profiles list generator is type (int, Point) with the first
# element the index of the Point in list
logger.info(f"Load profiles from reach {self.name}")
return list(
map(
lambda p: p[1],
sorted(
self._profiles,
key=lambda p: p[0]
)
)
)
def __len__(self):
if not isinstance(self._profiles, list):
self._profiles = self._get_profiles_list()
return len(self._profiles)
@property
def profiles(self):
return self._profiles.copy()
if not isinstance(self._profiles, list):
self._profiles = self._get_profiles_list()
return self._profiles
def get_profiles_from_kp(self, kp):
return list(
filter(
lambda p: p.kp == kp, self._profiles
lambda p: p.kp == kp, self.profiles
)
)
@@ -123,14 +147,14 @@ class Reach(SQLSubModel):
Returns:
Number of profiles
"""
return len(self._profiles)
return len(self.profiles)
def get_geometry(self) -> List[Profile]:
"""
Returns:
The profiles list.
"""
return self._profiles
return self.profiles
def _update_profile_numbers(self):
"""Update profiles index
@@ -152,7 +176,7 @@ class Reach(SQLSubModel):
"""
profile = ProfileXYZ(reach=self, status=self._status)
self._profiles.insert(index, profile)
self.profiles.insert(index, profile)
self._update_profile_numbers()
self._status.modified()
@@ -168,11 +192,10 @@ class Reach(SQLSubModel):
Returns:
Nothing.
"""
self._profiles.insert(index, profile)
self.profiles.insert(index, profile)
self._update_profile_numbers()
self._status.modified()
def delete(self, indexes):
"""Delete some elements in profile list
@@ -192,7 +215,7 @@ class Reach(SQLSubModel):
)
)
self._profiles = list(
self.profiles = list(
filter(
lambda p: p not in profiles,
self.profiles
@@ -210,7 +233,7 @@ class Reach(SQLSubModel):
Returns:
Nothing.
"""
self._profiles = list(
self.profiles = list(
filter(
lambda p: p not in profiles,
self.profiles
@@ -219,12 +242,11 @@ class Reach(SQLSubModel):
self._update_profile_numbers()
self._status.modified()
def move_up_profile(self, index: int):
if index < len(self.profiles):
next = index - 1
p = self._profiles
p = self.profiles
p[index], p[next] = p[next], p[index]
self._status.modified()
@@ -232,7 +254,7 @@ class Reach(SQLSubModel):
if index >= 0:
prev = index + 1
p = self._profiles
p = self.profiles
p[index], p[prev] = p[prev], p[index]
self._status.modified()
@@ -275,7 +297,6 @@ class Reach(SQLSubModel):
def get_kp_max(self):
return max(self.get_kp())
# Sediment Layers
def get_sl(self):
@@ -324,12 +345,12 @@ class Reach(SQLSubModel):
for guide in guide_set:
self._guidelines[guide] = flatten(
map(
lambda l: list(
lambda lst: list(
# Filter point with name (we assume we have
# only one point by profile)
filter(
lambda p: p.name == guide,
l
lst
)
),
named_points
@@ -344,7 +365,7 @@ class Reach(SQLSubModel):
Tuple of complete and incomplete guidelines name.
"""
# Get all point contains into a guideline
named_points = [profile.named_points() for profile in self._profiles]
named_points = [profile.named_points() for profile in self.profiles]
points_name = list(
map(
lambda lst: list(map(lambda p: p.name, lst)),
@@ -364,7 +385,9 @@ class Reach(SQLSubModel):
reduce(
lambda acc, h: acc + h,
map(
lambda l: list(set(l).symmetric_difference(guide_set)),
lambda lst: list(
set(lst).symmetric_difference(guide_set)
),
points_name
),
[]
@@ -414,13 +437,12 @@ class Reach(SQLSubModel):
def get_guidelines_z(self):
return self._map_guidelines_points(lambda p: p.z)
# Sort
@timer
def sort(self, is_reversed: bool = False):
self._profiles = sorted(
self._profiles,
self.profiles = sorted(
self.profiles,
key=lambda profile: profile.kp,
reverse=is_reversed
)
@@ -428,10 +450,10 @@ class Reach(SQLSubModel):
@timer
def sort_with_indexes(self, indexes: list):
if len(self._profiles) != len(indexes):
if len(self.profiles) != len(indexes):
logger.critical("Indexes list do not correspond to profile list")
self._profiles = list(
self.profiles = list(
map(
lambda x: x[1],
sorted(
@@ -449,7 +471,8 @@ class Reach(SQLSubModel):
"""Import a geometry from file (.ST or .st)
Args:
file_path_name: The absolute path of geometry file (.ST or .st) to import.
file_path_name: The absolute path of geometry file (.ST or .st)
to import.
Returns:
Nothing.
@@ -459,7 +482,8 @@ class Reach(SQLSubModel):
try:
list_profile, list_header = self.read_file_st(str(file_path_name))
profile_header = ["num", "code1", "code2", "nb_point", "kp", "name"]
profile_header = ["num", "code1",
"code2", "nb_point", "kp", "name"]
if list_profile and list_header:
for ind, profile in enumerate(list_profile):
@@ -471,7 +495,7 @@ class Reach(SQLSubModel):
**d, reach=self, status=self._status
)
prof.import_points(profile)
self._profiles.append(prof)
self.profiles.append(prof)
self._update_profile_numbers()
self._status.modified()
@@ -535,8 +559,8 @@ class Reach(SQLSubModel):
# TODO: Move this function to model reach
def export_reach(self, filename):
with open(f"{filename}", "w") as file_st:
for index in range(len(self._profiles)):
profile = self._profiles[index]
for index in range(len(self.profiles)):
profile = self.profiles[index]
for v in profile.header:
file_st.write(f"{v:>6}")
+1
View File
@@ -19,6 +19,7 @@
import numpy as np
from Model.Geometry.PointXYZ import PointXYZ
class Vector1d:
def __init__(self, a: PointXYZ, b: PointXYZ):
self.A = a
@@ -20,14 +20,15 @@ from copy import copy, deepcopy
from tools import trace, timer
from functools import reduce
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
class Data(SQLSubModel):
def __init__(self, name:str = "",
comment:str = "", reach = None,
kp:float = 0.0, discharge:float = 0.0,
height:float = 0.0,
status = None):
def __init__(self, name: str = "",
comment: str = "", reach=None,
kp: float = 0.0, discharge: float = 0.0,
height: float = 0.0,
status=None):
super(Data, self).__init__()
self._status = status
@@ -73,7 +74,7 @@ class Data(SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
id = data["reach"].id
table = execute(
"SELECT ind, name, comment, kp, discharge, height " +
@@ -95,25 +96,26 @@ class Data(SQLSubModel):
height = row[5]
d = cls(
reach = data["reach"],
status = data["status"],
name = name,
comment = comment,
kp = kp,
discharge = discharge,
height = height,
reach=data["reach"],
status=data["status"],
name=name,
comment=comment,
kp=kp,
discharge=discharge,
height=height,
)
new[ind] = d
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
ind = data["ind"]
execute(
"INSERT INTO " +
"initial_conditions(ind, name, comment, kp, discharge, height, reach) " +
"initial_conditions(ind, name, comment, kp, " +
"discharge, height, reach) " +
"VALUES (" +
f"{ind}, '{self._sql_format(self.name)}', " +
f"'{self._sql_format(self._comment)}', " +
@@ -124,16 +126,15 @@ class Data(SQLSubModel):
return True
def copy(self):
new = Data(
name = self.name,
comment = self._comment,
kp = self._kp,
discharge = self._discharge,
height = self._height,
reach = self._reach,
status = self._status,
name=self.name,
comment=self._comment,
kp=self._kp,
discharge=self._discharge,
height=self._height,
reach=self._reach,
status=self._status,
)
return new
@@ -173,7 +174,7 @@ class Data(SQLSubModel):
def _update_from_kp(self):
min = self._update_get_min()
self._elevation = min - self._height
self._elevation = min + self._height
def _update_from_elevation(self):
min = self._update_get_min()
@@ -216,7 +217,7 @@ class InitialConditions(SQLSubModel):
Data
]
def __init__(self, reach = None, status = None):
def __init__(self, reach=None, status=None):
super(InitialConditions, self).__init__()
self._status = status
@@ -233,23 +234,21 @@ class InitialConditions(SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = cls(
reach = data["reach"],
status = data["status"]
reach=data["reach"],
status=data["status"]
)
new._data = Data._sql_load(
execute,
data = data
data=data
)
if new._data is None:
return None
if new._data is not None:
yield new
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
ok = True
ind = 0
@@ -288,7 +287,7 @@ class InitialConditions(SQLSubModel):
self._status.modified()
def new(self, index):
n = Data(reach = self._reach, status = self._status)
n = Data(reach=self._reach, status=self._status)
self._data.insert(index, n)
self._status.modified()
@@ -321,7 +320,6 @@ class InitialConditions(SQLSubModel):
self._data.sort(reverse=reverse, key=key)
self._status.modified()
def _data_get(self, key):
return list(
map(
@@ -341,8 +339,8 @@ class InitialConditions(SQLSubModel):
def _sort_by_z_and_kp(self, profiles):
profiles.sort(
reverse = False,
key = lambda p: p.kp
reverse=False,
key=lambda p: p.kp
)
first_z = profiles[0].z()
@@ -350,11 +348,11 @@ class InitialConditions(SQLSubModel):
if first_z > last_z:
profiles.sort(
reverse = True,
key = lambda p: p.kp
reverse=True,
key=lambda p: p.kp
)
def generate_growing_constante_height(self, height:float):
def generate_growing_constante_height(self, height: float):
self._data = []
profiles = self._reach.reach.profiles
@@ -362,7 +360,7 @@ class InitialConditions(SQLSubModel):
prev = None
for profile in profiles:
new = Data(reach = self._reach, status = self._status)
new = Data(reach=self._reach, status=self._status)
new["kp"] = profile.kp
if prev is None:
@@ -381,11 +379,11 @@ class InitialConditions(SQLSubModel):
is_reverse = True
self._data.sort(
reverse = not is_reverse,
key = lambda d: d['kp']
reverse=not is_reverse,
key=lambda d: d['kp']
)
def generate_discharge(self, discharge:float):
def generate_discharge(self, discharge: float):
self._new = []
for d in self._data:
@@ -16,81 +16,62 @@
# -*- coding: utf-8 -*-
import types
from copy import copy
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDict import PamhyrModelDict
from Model.InitialConditions.InitialConditions import InitialConditions
class InitialConditionsDict(SQLSubModel):
class InitialConditionsDict(PamhyrModelDict):
_sub_classes = [
InitialConditions,
]
def __init__(self, status = None):
super(InitialConditionsDict, self).__init__()
self._status = status
self._reach = {}
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data["status"])
def _sql_load(cls, execute, data=None):
new = cls(status=data["status"])
for reach in data["edges"]:
data["reach"] = reach
ic = InitialConditions._sql_load(
execute,
data = data
data=data
)
if ic is not None:
new._reach[reach] = ic
new._dict[reach] = ic
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
ok = True
if data is None:
data = {}
execute("DELETE FROM initial_conditions")
for reach in self._reach:
for reach in self._dict:
data["reach"] = reach
ok &= self._reach[reach]._sql_save(execute, data)
ok &= self._dict[reach]._sql_save(execute, data)
return ok
def __len__(self):
return len(self._reach)
def get(self, key):
if key in self._dict:
v = self._dict[key]
if isinstance(v, types.GeneratorType):
self._dict[key] = list(v)[0]
def is_defined(self, reach):
return reach in self._reach
return self._dict[key]
def get(self, reach):
if reach in self._reach:
return self._reach[reach]
new = self.new(reach)
self.set(reach, new)
new = self.new(key)
self.set(key, new)
return new
def set(self, reach, new):
self._reach[reach] = new
self._status.modified()
def new(self, reach):
new = InitialConditions(reach = reach, status = self._status)
new = InitialConditions(reach=reach, status=self._status)
self.set(reach, new)
return new
@@ -20,16 +20,17 @@ import logging
from tools import trace, timer, old_pamhyr_date_to_timestamp
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
class LateralContribution(SQLSubModel):
_sub_classes = []
_id_cnt = 0
def __init__(self, id:int = -1, name:str = "", status = None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(LateralContribution, self).__init__()
self._status = status
@@ -39,7 +40,6 @@ class LateralContribution(SQLSubModel):
else:
self.id = id
self._name = name
self._type = ""
self._edge = None
@@ -49,7 +49,8 @@ class LateralContribution(SQLSubModel):
self._header = []
self._types = [float, float]
LateralContribution._id_cnt = max(LateralContribution._id_cnt + 1, self.id)
LateralContribution._id_cnt = max(
LateralContribution._id_cnt + 1, self.id)
@classmethod
def _sql_create(cls, execute):
@@ -99,7 +100,7 @@ class LateralContribution(SQLSubModel):
return res
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = []
tab = data["tab"]
@@ -112,9 +113,9 @@ class LateralContribution(SQLSubModel):
t = row[2]
ctor = cls._get_ctor_from_type(t)
lc = ctor(
id = row[0],
name = row[1],
status = data['status']
id=row[0],
name=row[1],
status=data['status']
)
lc.edge = None
if row[3] != -1:
@@ -141,7 +142,7 @@ class LateralContribution(SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
tab = data["tab"]
execute(f"DELETE FROM lateral_contribution WHERE id = {self.id}")
@@ -153,7 +154,8 @@ class LateralContribution(SQLSubModel):
sql = (
"INSERT INTO " +
"lateral_contribution(id, name, type, tab, edge, begin_kp, end_kp) "+
"lateral_contribution(id, name, type, tab, " +
"edge, begin_kp, end_kp) " +
"VALUES (" +
f"{self.id}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', '{tab}', {edge}, " +
@@ -169,7 +171,7 @@ class LateralContribution(SQLSubModel):
sql = (
"INSERT INTO " +
"lateral_contribution_data(ind, data0, data1, lc) "+
"lateral_contribution_data(ind, data0, data1, lc) " +
f"VALUES ({ind}, '{data0}', {data1}, {self.id})"
)
execute(sql)
@@ -186,7 +188,7 @@ class LateralContribution(SQLSubModel):
@classmethod
def time_convert(cls, data):
if type(data) == str and data.count(":") == 3:
if data is str and data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
return int(data)
@@ -282,7 +284,7 @@ class LateralContribution(SQLSubModel):
new_1 = self._default_1
if len(header) != 0:
for i in [0,1]:
for i in [0, 1]:
for j in range(len(header)):
if self._header[i] == header[j]:
if i == 0:
@@ -295,13 +297,13 @@ class LateralContribution(SQLSubModel):
return (new_0, new_1)
def add(self, index:int):
def add(self, index: int):
value = (self._default_0, self._default_1)
self._data.insert(index, value)
self._status.modified()
return value
def insert(self, index:int, value):
def insert(self, index: int, value):
self._data.insert(index, value)
self._status.modified()
@@ -337,11 +339,10 @@ class LateralContribution(SQLSubModel):
return self.data[index]
def get_range(self, _range):
l = []
lst = []
for r in _range:
l.append(r)
return l
lst.append(r)
return lst
def _set_i_c_v(self, index, column, value):
v = list(self._data[index])
@@ -349,15 +350,15 @@ class LateralContribution(SQLSubModel):
self._data[index] = tuple(v)
self._status.modified()
def set_i_0(self, index:int, value):
def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value)
def set_i_1(self, index:int, value):
def set_i_1(self, index: int, value):
self._set_i_c_v(index, 1, value)
@timer
def convert(self, cls):
new = cls(name = self.name, status = self._status)
new = cls(name=self.name, status=self._status)
new.edge = self.edge
new.begin_kp = self.begin_kp
new.end_kp = self.end_kp
@@ -365,8 +366,8 @@ class LateralContribution(SQLSubModel):
for i, _ in enumerate(self.data):
new.add(i)
for i in [0,1]:
for j in [0,1]:
for i in [0, 1]:
for j in [0, 1]:
if self._header[i] == new.header[j]:
for ind, v in self.data:
try:
@@ -19,7 +19,7 @@
from copy import copy
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrList import PamhyrModelListWithTab
from Model.Except import NotImplementedMethodeError
from Model.LateralContribution.LateralContribution import LateralContribution
@@ -27,33 +27,16 @@ from Model.LateralContribution.LateralContributionTypes import (
NotDefined, LateralContrib, Rain, Evaporation,
)
class LateralContributionList(SQLSubModel):
class LateralContributionList(PamhyrModelListWithTab):
_tabs_list = ["liquid", "solid", "suspenssion"]
_sub_classes = [
LateralContribution,
]
def __init__(self, status = None):
super(LateralContributionList, self).__init__()
self._status = status
self._tabs = {
"liquid" : [],
"solid" : [],
"suspenssion" : []
}
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
return True
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data['status'])
def _sql_load(cls, execute, data=None):
new = cls(status=data['status'])
if data is None:
data = {}
@@ -66,7 +49,7 @@ class LateralContributionList(SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
execute("DELETE FROM lateral_contribution")
if data is None:
@@ -75,83 +58,29 @@ class LateralContributionList(SQLSubModel):
for tab in self._tabs:
data["tab"] = tab
for lc in self._tabs[tab]:
lc._sql_save(execute, data = data)
lc._sql_save(execute, data=data)
return True
def len(self, lst):
return len(self._tabs[lst])
def get_tab(self, lst):
return self._tabs[lst].copy()
def get(self, lst, row):
return self._tabs[lst][row]
def set(self, lst, row, new):
self._tabs[lst][row] = new
self._status.modified()
def new(self, lst, index):
n = NotDefined(status=self._status)
self._tabs[lst].insert(index, n)
self._status.modified()
return n
def insert(self, lst, index, new):
self._tabs[lst].insert(index, new)
self._status.modified()
def delete(self, lst, bcs):
for bc in bcs:
self._tabs[lst].remove(bc)
self._status.modified()
def delete_i(self, lst, indexes):
bcs = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._tabs[lst])
)
)
)
self.delete(lst, bcs)
def sort(self, lst, reverse=False, key=None):
self._tabs[lst].sort(reverse=reverse, key=key)
self._status.modified()
def move_up(self, lst, index):
if index < len(self._tabs[lst]):
next = index - 1
l = self._tabs[lst]
l[index], l[next] = l[next], l[index]
self._status.modified()
def move_down(self, lst, index):
if index >= 0:
prev = index + 1
l = self._tabs[lst]
l[index], l[prev] = l[prev], l[index]
self._status.modified()
def __copy__(self):
new = LateralContributionList()
for l in self._tabs:
new.tabs[l] = self._tabs[l].copy()
for lst in self._tabs:
new.tabs[lst] = self._tabs[lst].copy()
return new
def __deepcopy__(self):
new = LateralContributionList()
for l in self._tabs:
new.tabs[l] = self._tabs[l].deepcopy()
for lst in self._tabs:
new.tabs[lst] = self._tabs[lst].deepcopy()
return new
@@ -22,7 +22,7 @@ from Model.LateralContribution.LateralContribution import LateralContribution
class NotDefined(LateralContribution):
def __init__(self, id:int = -1, name:str = "", status=None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(NotDefined, self).__init__(id=id, name=name, status=status)
self._type = "ND"
@@ -32,8 +32,9 @@ class NotDefined(LateralContribution):
def _default_0(self):
return 0.0
class LateralContrib(LateralContribution):
def __init__(self, id:int = -1, name:str = "", status=None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(LateralContrib, self).__init__(id=id, name=name, status=status)
self._type = "LC"
@@ -44,8 +45,9 @@ class LateralContrib(LateralContribution):
def compatibility(cls):
return ["liquid"]
class Rain(LateralContribution):
def __init__(self, id:int = -1, name:str = "", status=None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(Rain, self).__init__(id=id, name=name, status=status)
self._type = "RA"
@@ -56,8 +58,9 @@ class Rain(LateralContribution):
def compatibility(cls):
return ["liquid"]
class Evaporation(LateralContribution):
def __init__(self, id:int = -1, name:str = "", status=None):
def __init__(self, id: int = -1, name: str = "", status=None):
super(Evaporation, self).__init__(id=id, name=name, status=status)
self._type = "EV"
+9 -5
View File
@@ -18,13 +18,14 @@
from Model.Network.Node import Node
class Edge(object):
_id_cnt = 0
def __init__(self, id:int, name:str,
node1:Node = None,
node2:Node = None,
status = None):
def __init__(self, id: int, name: str,
node1: Node = None,
node2: Node = None,
status=None):
super(Edge, self).__init__()
self._status = status
@@ -74,7 +75,10 @@ class Edge(object):
@property
def name(self):
return self._name if self._name != "" else f"{self.node1.name} -> {self.node2.name}"
name = self._name
if self._name == "":
name = f"{self.node1.name} -> {self.node2.name}"
return name
def is_enable(self):
return self._enable
+15 -15
View File
@@ -21,8 +21,9 @@ from functools import reduce
from Model.Network.Node import Node
from Model.Network.Edge import Edge
class Graph(object):
def __init__(self, status = None):
def __init__(self, status=None):
super(Graph, self).__init__()
self._status = status
@@ -83,7 +84,7 @@ class Graph(object):
False
)
def node(self, node_name:str):
def node(self, node_name: str):
node = list(
filter(
lambda n: n.name == node_name,
@@ -96,7 +97,7 @@ class Graph(object):
return node[0]
def edge(self, edge_name:str):
def edge(self, edge_name: str):
edge = list(
filter(
lambda e: e.name == edge_name,
@@ -109,12 +110,12 @@ class Graph(object):
return edge[0]
def _create_node(self, x:float, y:float):
def _create_node(self, x: float, y: float):
node = self._node_ctor(
-1,
"",
x = x, y = y,
status = self._status
x=x, y=y,
status=self._status
)
return node
@@ -123,14 +124,14 @@ class Graph(object):
self._status.modified()
return node
def add_node(self, x:float = 0.0, y:float = 0.0):
def add_node(self, x: float = 0.0, y: float = 0.0):
node = self._create_node(x, y)
return self._add_node(node)
def insert_node(self, node):
return self._add_node(node)
def remove_node(self, node_name:str):
def remove_node(self, node_name: str):
self._nodes = list(
filter(
lambda n: n.name != node_name,
@@ -139,19 +140,18 @@ class Graph(object):
)
self._status.modified()
def create_node(self, x:float = 0.0, y:float = 0.0):
def create_node(self, x: float = 0.0, y: float = 0.0):
node = self._create_node(x, y)
return node
def _create_edge(self, n1:Node, n2:Node):
def _create_edge(self, n1: Node, n2: Node):
edge = self._edge_ctor(
-1,
"", n1, n2,
status = self._status
status=self._status
)
return edge
def _add_edge(self, edge):
# This edge already exists ?
if any(filter(lambda e: (e.node1 == edge.node1 and
@@ -164,17 +164,17 @@ class Graph(object):
self._status.modified()
return edge
def add_edge(self, n1:Node, n2:Node):
def add_edge(self, n1: Node, n2: Node):
edge = self._create_edge(n1, n2)
return self._add_edge(edge)
def insert_edge(self, edge):
return self._add_edge(edge)
def create_edge(self, n1:Node, n2:Node):
def create_edge(self, n1: Node, n2: Node):
return self._create_edge(n1, n2)
def remove_edge(self, edge_name:str):
def remove_edge(self, edge_name: str):
self._edges = list(
filter(
lambda e: e.name != edge_name,
+4 -4
View File
@@ -18,12 +18,13 @@
from Model.Network.Point import Point
class Node(object):
_id_cnt = 0
def __init__(self, id:int, name:str,
x:float = 0.0, y:float = 0.0,
status = None):
def __init__(self, id: int, name: str,
x: float = 0.0, y: float = 0.0,
status=None):
super(Node, self).__init__()
self._status = status
@@ -73,7 +74,6 @@ class Node(object):
def y(self):
return self.pos.y
def setPos(self, x, y):
self.pos.x = x
self.pos.y = y
+1 -1
View File
@@ -17,7 +17,7 @@
# -*- coding: utf-8 -*-
class Point(object):
def __init__(self, x:float, y:float):
def __init__(self, x: float, y: float):
super(Point, self).__init__()
self.x = x
+18 -1
View File
@@ -24,9 +24,15 @@ from Model.Results.River.River import River
logger = logging.getLogger()
class Results(object):
def __init__(self, study = None):
def __init__(self, study=None, solver=None,
repertory="", name="0"):
self._study = study
self._solver = solver
self._repertory = repertory
self._name = name
self._river = River(self._study)
self._meta_data = {
@@ -43,8 +49,19 @@ class Results(object):
def river(self):
return self._river
@property
def study(self):
return self._study
def set(self, key, value):
self._meta_data[key] = value
def get(self, key):
return self._meta_data[key]
def reload(self):
return self._solver.results(
self._study,
self._repertory,
qlog=None,
)
+15 -3
View File
@@ -20,11 +20,15 @@ from datetime import datetime
logger = logging.getLogger()
class Profile(object):
def __init__(self, profile, study):
self._study = study
self._profile = profile # Source profile in the study
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
self._profile = profile # Source profile in the study
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
def __len__(self):
return len(self._data)
@property
def name(self):
@@ -55,10 +59,11 @@ class Profile(object):
def get_ts_key(self, timestamp, key):
return self._data[timestamp][key]
class Reach(object):
def __init__(self, reach, study):
self._study = study
self._reach = reach # Source reach in the study
self._reach = reach # Source reach in the study
self._profiles = list(
map(
lambda p: Profile(p, self._study),
@@ -66,6 +71,9 @@ class Reach(object):
)
)
def __len__(self):
return len(self._profiles)
@property
def name(self):
return self._reach.name
@@ -84,6 +92,7 @@ class Reach(object):
def set(self, profile_id, timestamp, key, data):
self._profiles[profile_id].set(timestamp, key, data)
class River(object):
def __init__(self, study):
self._study = study
@@ -91,6 +100,9 @@ class River(object):
# Dict with timestamps as key
self._reachs = []
def __len__(self):
return len(self._reachs)
@property
def reachs(self):
return self._reachs.copy()
+34 -34
View File
@@ -18,7 +18,7 @@
from tools import flatten
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Network.Node import Node
from Model.Network.Edge import Edge
@@ -28,7 +28,9 @@ from Model.Geometry.Profile import Profile
from Model.Geometry.Reach import Reach
from Model.BoundaryCondition.BoundaryConditionList import BoundaryConditionList
from Model.LateralContribution.LateralContributionList import LateralContributionList
from Model.LateralContribution.LateralContributionList import (
LateralContributionList
)
from Model.InitialConditions.InitialConditionsDict import InitialConditionsDict
from Model.Stricklers.StricklersList import StricklersList
from Model.Friction.FrictionList import FrictionList
@@ -37,16 +39,17 @@ from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
from Solver.Solvers import solver_type_list
class RiverNode(Node, SQLSubModel):
_sub_classes = []
def __init__(self, id:str, name:str,
x:float, y:float,
status = None):
def __init__(self, id: str, name: str,
x: float, y: float,
status=None):
super(RiverNode, self).__init__(
id, name,
x, y,
status = status
status=status
)
self._locker = None
@@ -70,7 +73,7 @@ class RiverNode(Node, SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
nodes = []
table = execute("SELECT id, name, x, y FROM river_node")
@@ -82,11 +85,11 @@ class RiverNode(Node, SQLSubModel):
return nodes
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
sql = (
"INSERT OR REPLACE INTO river_node(id, name, x, y) VALUES (" +
f"{self.id}, '{self._sql_format(self.name)}', " +
f"{self.x}, {self.y}"+
f"{self.x}, {self.y}" +
")"
)
execute(sql)
@@ -108,18 +111,18 @@ class RiverReach(Edge, SQLSubModel):
FrictionList,
]
def __init__(self, id:str, name:str,
node1:RiverNode = None,
node2:RiverNode = None,
status = None):
def __init__(self, id: str, name: str,
node1: RiverNode = None,
node2: RiverNode = None,
status=None):
super(RiverReach, self).__init__(
id, name,
node1, node2,
status = status
status=status
)
self._reach = Reach(status=self._status, parent=self)
self._frictions = FrictionList(status = self._status)
self._frictions = FrictionList(status=self._status)
@classmethod
def _sql_create(cls, execute):
@@ -143,13 +146,14 @@ class RiverReach(Edge, SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
reachs = []
if data is None:
data = {}
table = execute("SELECT id, name, enable, node1, node2 FROM river_reach")
table = execute(
"SELECT id, name, enable, node1, node2 FROM river_reach")
for row in table:
# Update id counter
cls._id_cnt = max(cls._id_cnt, row[0])
@@ -161,8 +165,8 @@ class RiverReach(Edge, SQLSubModel):
node1 = next(filter(lambda n: n.id == row[3], data["nodes"]))
node2 = next(filter(lambda n: n.id == row[4], data["nodes"]))
new = cls(id, name, node1, node2, status = data["status"])
new.enable(enable = enable)
new = cls(id, name, node1, node2, status=data["status"])
new.enable(enable=enable)
data["reach"] = id
data["parent"] = new
@@ -174,14 +178,14 @@ class RiverReach(Edge, SQLSubModel):
return reachs
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
sql = (
"INSERT OR REPLACE INTO " +
"river_reach(id, name, enable, node1, node2) "+
"river_reach(id, name, enable, node1, node2) " +
"VALUES (" +
f"{self.id}, '{self._sql_format(self._name)}', " +
f"{self._sql_format(self.is_enable())},"
f"{self.node1.id}, {self.node2.id}"+
f"{self.node1.id}, {self.node2.id}" +
")"
)
execute(sql)
@@ -202,6 +206,7 @@ class RiverReach(Edge, SQLSubModel):
def frictions(self):
return self._frictions
class River(Graph, SQLSubModel):
_sub_classes = [
RiverNode,
@@ -223,7 +228,8 @@ class River(Graph, SQLSubModel):
self._current_reach = None
self._boundary_condition = BoundaryConditionList(status=self._status)
self._lateral_contribution = LateralContributionList(status=self._status)
self._lateral_contribution = LateralContributionList(
status=self._status)
self._initial_conditions = InitialConditionsDict(status=self._status)
self._stricklers = StricklersList(status=self._status)
self._parameters = {}
@@ -240,7 +246,7 @@ class River(Graph, SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = cls(data["status"])
# Stricklers (Stricklers is load in first because it's needed
@@ -251,7 +257,6 @@ class River(Graph, SQLSubModel):
)
data["stricklers"] = new._stricklers
# Initial conditions
new._sediment_layers = SedimentLayerList._sql_load(
execute,
@@ -297,7 +302,7 @@ class River(Graph, SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
objs = (self._nodes + self._edges)
objs.append(self._boundary_condition)
objs.append(self._initial_conditions)
@@ -311,10 +316,6 @@ class River(Graph, SQLSubModel):
self._save_submodel(execute, objs, data)
return True
@property
def frictions(self):
return self._frictions
@property
def boundary_condition(self):
return self._boundary_condition
@@ -348,7 +349,6 @@ class River(Graph, SQLSubModel):
return ret[0]
@property
def parameters(self):
return self._parameters
@@ -358,8 +358,8 @@ class River(Graph, SQLSubModel):
return self._parameters[solver]
new = SolverParametersList(
solver_type = solver_type_list[solver],
status = self._status
solver_type=solver_type_list[solver],
status=self._status
)
self._parameters[solver] = new
self._status.modified()
@@ -385,7 +385,7 @@ class River(Graph, SQLSubModel):
has = len(self._sediment_layers) != 0
has &= any(
filter(
lambda p: p.sl != None,
lambda p: p.sl is not None,
flatten(
map(lambda e: e.reach.profiles, self.edges())
)
+1
View File
@@ -20,6 +20,7 @@ import logging
logger = logging.getLogger()
class SavedStatus(object):
def __init__(self):
super(SavedStatus, self).__init__()
+36 -33
View File
@@ -2,19 +2,20 @@
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
class Layer(SQLSubModel):
_sub_classes = []
_id_cnt = 0
def __init__(self,
id:int = -1, name:str = "",
type = "",
height = 0.0, d50 = 0.0, sigma = 0.0,
critical_constraint = 0.0,
sl = None, status = None):
id: int = -1, name: str = "",
type="",
height=0.0, d50=0.0, sigma=0.0,
critical_constraint=0.0,
sl=None, status=None):
super(Layer, self).__init__()
self._status = status
@@ -111,12 +112,13 @@ class Layer(SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = []
sl = data["sl"]
table = execute(
"SELECT id, ind, name, type, height, d50, sigma, critical_constraint " +
"SELECT id, ind, name, type, height, " +
"d50, sigma, critical_constraint " +
"FROM sedimentary_layer_layer " +
f"WHERE sl = {sl}"
)
@@ -128,24 +130,25 @@ class Layer(SQLSubModel):
ind = row[1]
layer = cls(
id = row[0], name = row[2],
type = row[3], height = row[4],
d50 = row[5], sigma = row[6],
critical_constraint = row[7],
sl = sl, status = data['status']
id=row[0], name=row[2],
type=row[3], height=row[4],
d50=row[5], sigma=row[6],
critical_constraint=row[7],
sl=sl, status=data['status']
)
new[ind] = layer
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
ind = data["ind"]
sl = data["sl"]
sql = (
"INSERT INTO " +
"sedimentary_layer_layer(id, ind, name, type, height, d50, sigma, critical_constraint, sl) "+
"sedimentary_layer_layer(id, ind, name, type, height, " +
"d50, sigma, critical_constraint, sl) " +
"VALUES (" +
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', {self._height}, " +
@@ -162,9 +165,9 @@ class SedimentLayer(SQLSubModel):
_sub_classes = [Layer]
_id_cnt = 0
def __init__(self, id:int = -1,
name:str = "", comment:str = "",
status = None):
def __init__(self, id: int = -1,
name: str = "", comment: str = "",
status=None):
super(SedimentLayer, self).__init__()
self._status = status
@@ -196,7 +199,7 @@ class SedimentLayer(SQLSubModel):
def height(self):
return list(
map(lambda l: l.height, self._layers)
map(lambda layer: layer.height, self._layers)
)
@property
@@ -209,7 +212,7 @@ class SedimentLayer(SQLSubModel):
def names(self):
return list(
map(lambda l: l.name, self._layers)
map(lambda layer: layer.name, self._layers)
)
@property
@@ -242,7 +245,7 @@ class SedimentLayer(SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
new = []
table = execute(
@@ -252,10 +255,10 @@ class SedimentLayer(SQLSubModel):
for row in table:
sl = cls(
id = row[0],
name = row[1],
comment = row[2],
status = data['status']
id=row[0],
name=row[1],
comment=row[2],
status=data['status']
)
data["sl"] = sl.id
@@ -265,7 +268,7 @@ class SedimentLayer(SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
if data is None:
data = {}
@@ -279,9 +282,9 @@ class SedimentLayer(SQLSubModel):
data["sl"] = self
ind = 0
for l in self._layers:
for layer in self._layers:
data["ind"] = ind
l._sql_save(execute, data)
layer._sql_save(execute, data)
ind += 1
return True
@@ -298,7 +301,7 @@ class SedimentLayer(SQLSubModel):
self._status.modified()
def new(self, index):
n = Layer(sl=self, status = self._status)
n = Layer(sl=self, status=self._status)
self.insert(index, n)
self._status.modified()
return n
@@ -324,8 +327,8 @@ class SedimentLayer(SQLSubModel):
if index >= 0:
next = index - 1
l = self._layers
l[index], l[next] = l[next], l[index]
lst = self._layers
lst[index], lst[next] = lst[next], lst[index]
self._status.modified()
@@ -333,7 +336,7 @@ class SedimentLayer(SQLSubModel):
if index + 1 < len(self._layers):
prev = index + 1
l = self._layers
l[index], l[prev] = l[prev], l[index]
lst = self._layers
lst[index], lst[prev] = lst[prev], lst[index]
self._status.modified()
+10 -70
View File
@@ -2,100 +2,40 @@
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrList import PamhyrModelList
from Model.SedimentLayer.SedimentLayer import SedimentLayer
class SedimentLayerList(SQLSubModel):
class SedimentLayerList(PamhyrModelList):
_sub_classes = [SedimentLayer]
def __init__(self, status = None):
super(SedimentLayerList, self).__init__()
self._status = status
self._sl = []
def __len__(self):
return len(self._sl)
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
def _sql_load(cls, execute, data=None):
new = cls(status=data["status"])
@classmethod
def _sql_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data["status"])
new._sl = SedimentLayer._sql_load(execute, data)
new._lst = SedimentLayer._sql_load(execute, data)
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
ok = True
# Delete previous data
execute("DELETE FROM sedimentary_layer")
execute("DELETE FROM sedimentary_layer_layer")
for sl in self._sl:
for sl in self._lst:
ok &= sl._sql_save(execute, data)
return ok
@property
def sediment_layers(self):
return self._sl.copy()
def get(self, index):
return self._sl[index]
def set(self, index, new):
self._sl[index] = new
self._status.modified()
def insert(self, index, new):
self._sl.insert(index, new)
self._status.modified()
return self.lst
def new(self, index):
n = SedimentLayer(status = self._status)
n = SedimentLayer(status=self._status)
self.insert(index, n)
self._status.modified()
return n
def delete(self, els):
for el in els:
self._sl.remove(el)
self._status.modified()
def delete_i(self, indexes):
els = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._sl)
)
)
)
self.delete(els)
def move_up(self, index):
if index < len(self._sl):
next = index - 1
l = self._sl
l[index], l[next] = l[next], l[index]
self._status.modified()
def move_down(self, index):
if index >= 0:
prev = index + 1
l = self._sl
l[index], l[prev] = l[prev], l[index]
self._status.modified()
+1
View File
@@ -18,6 +18,7 @@
import pickle
class Serializable():
def __init__(self):
return
@@ -20,12 +20,13 @@ from copy import copy
from tools import trace, timer
from Solver.Solvers import solver_type_list
from Model.DB import SQLSubModel
from Model.Tools.PamhyrList import PamhyrModelList
class Parameter():
def __init__(self,
name = "", value = "",
status = None):
name="", value="",
status=None):
self._status = status
self._name = name
@@ -57,26 +58,26 @@ class Parameter():
@classmethod
def from_tuple(cls, data, status):
new = cls(status = status)
new = cls(status=status)
new["name"] = data[0]
new["value"] = data[1]
return new
class SolverParametersList(SQLSubModel):
def __init__(self, solver_type = None, status = None):
class SolverParametersList(PamhyrModelList):
def __init__(self, solver_type=None, status=None):
super(SolverParametersList, self).__init__()
self._status = status
self._solver = solver_type
self._parameters = list(
self._lst = list(
map(
lambda t: Parameter.from_tuple(t, self._status),
solver_type.default_parameters()
)
)
@classmethod
def _sql_create(cls, execute):
execute("""
@@ -97,35 +98,42 @@ class SolverParametersList(SQLSubModel):
if major == minor == "0":
if int(release) < 3:
execute(f"UPDATE solver_parameter SET name='mage_implicitation' WHERE name='mage_implication'")
execute(f"UPDATE solver_parameter SET name='mage_iteration_type' WHERE name='mage_iter_type'")
execute(
"UPDATE solver_parameter SET name='mage_implicitation' " +
"WHERE name='mage_implication'"
)
execute(
"UPDATE solver_parameter SET name='mage_iteration_type' " +
"WHERE name='mage_iter_type'"
)
if int(release) < 4:
solvers = execute("SELECT DISTINCT solver FROM solver_parameter")
solvers = execute(
"SELECT DISTINCT solver FROM solver_parameter")
for solver in solvers:
if solver != "mage8":
continue
data = execute("SELECT ind, name, value " +
"FROM solver_parameter "+
f"WHERE solver = '{solver}' " +
"ORDER BY ind ACS")
"FROM solver_parameter " +
f"WHERE solver = '{solver}' " +
"ORDER BY ind ACS")
execute(
"DELETE FROM solver_parameter " +
f"WHERE solver = '{solver}'"
)
insert = lambda name, value, ind: execute(
"INSERT INTO " +
"solver_parameter(ind, name, value, solver) "+
"VALUES (" +
f"{ind}, " +
f"'{cls._sql_format(name)}', " +
f"'{cls._sql_format(value)}', " +
f"'{cls._sql_format(solver)}'" +
")"
def insert(name, value, ind): return execute(
"INSERT INTO " +
"solver_parameter(ind, name, value, solver) " +
"VALUES (" +
f"{ind}, " +
f"'{cls._sql_format(name)}', " +
f"'{cls._sql_format(value)}', " +
f"'{cls._sql_format(solver)}'" +
")"
)
ind = 0
@@ -156,7 +164,7 @@ class SolverParametersList(SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
status = data["status"]
solvers = execute("SELECT DISTINCT solver FROM solver_parameter")
new = {}
@@ -167,7 +175,7 @@ class SolverParametersList(SQLSubModel):
continue
st = solver_type_list[solver]
n = cls(solver_type = st, status = status)
n = cls(solver_type=st, status=status)
table = execute(
"SELECT ind, name, value " +
@@ -186,7 +194,7 @@ class SolverParametersList(SQLSubModel):
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
t = self._solver._type
execute(
"DELETE FROM solver_parameter " +
@@ -194,10 +202,10 @@ class SolverParametersList(SQLSubModel):
)
ind = 0
for param in self._parameters:
for param in self._lst:
sql = (
"INSERT INTO " +
"solver_parameter(ind, name, value, solver) "+
"solver_parameter(ind, name, value, solver) " +
"VALUES (" +
f"{ind}, " +
f"'{self._sql_format(param.name)}', " +
@@ -210,90 +218,46 @@ class SolverParametersList(SQLSubModel):
return True
def __len__(self):
return len(self._parameters)
@property
def parameters(self):
return self._parameters.copy()
return self.lst
def get(self, index):
return self._parameters[index]
return self._lst[index]
def get_by_key(self, key):
try:
return next(
filter(
lambda p: p["name"] == key,
self._parameters
self._lst
)
)["value"]
except:
except Exception:
return None
def set(self, index, new):
self._parameters[index] = new
self._lst[index] = new
self._status.modified()
def set_value(self, key, value):
for p in self._parameters:
for p in self._lst:
if p["name"] == key:
p["value"] = value
self._status.modified()
return
self._parameters.append(
self._lst.append(
Parameter(
name = key,
value = value,
status = self._status
name=key,
value=value,
status=self._status
)
)
self._status.modified()
def new(self, index):
n = Parameter(status = self._status)
self._parameters.insert(index, n)
n = Parameter(status=self._status)
self._lst.insert(index, n)
self._status.modified()
return n
def insert(self, index, new):
self._parameters.insert(index, new)
self._status.modified()
def delete(self, parameters):
for parameter in parameters:
self._parameters.remove(parameter)
self._status.modified()
def delete_i(self, indexes):
parameters = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._parameters)
)
)
)
self.delete(parameters)
def sort(self, reverse=False, key=None):
self._parameters.sort(reverse=reverse, key=key)
self._status.modified()
def move_up(self, index):
if index < len(self._parameters):
next = index - 1
l = self._parameters
l[index], l[next] = l[next], l[index]
self._status.modified()
def move_down(self, index):
if index >= 0:
prev = index + 1
l = self._parameters
l[index], l[prev] = l[prev], l[index]
self._status.modified()
+16 -16
View File
@@ -18,18 +18,19 @@
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Tools.PamhyrDB import SQLSubModel
class Stricklers(SQLSubModel):
_id_cnt = 0
_sub_classes = []
def __init__(self, id:int = -1,
name:str = "",
comment:str = "",
minor:float = 35.0,
medium:float = 15.0,
status = None):
def __init__(self, id: int = -1,
name: str = "",
comment: str = "",
minor: float = 35.0,
medium: float = 15.0,
status=None):
super(Stricklers, self).__init__()
self._status = status
@@ -46,7 +47,6 @@ class Stricklers(SQLSubModel):
self._minor = minor
self._medium = medium
@classmethod
def _sql_create(cls, execute):
execute("""
@@ -66,7 +66,7 @@ class Stricklers(SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
stricklers = []
status = data["status"]
@@ -86,21 +86,21 @@ class Stricklers(SQLSubModel):
medium = row[4]
new = cls(
id = id,
name = name,
comment = comment,
minor = minor, medium = medium,
status = status
id=id,
name=name,
comment=comment,
minor=minor, medium=medium,
status=status
)
stricklers.append(new)
return stricklers
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
sql = (
"INSERT INTO " +
"stricklers(id, name, comment, minor, medium) "+
"stricklers(id, name, comment, minor, medium) " +
"VALUES (" +
f"{self.id}, " +
f"'{self._sql_format(self.name)}', " +
+21 -59
View File
@@ -18,22 +18,16 @@
from tools import trace, timer
from Model.DB import SQLSubModel
from Model.Saved import SavedStatus
from Model.Tools.PamhyrList import PamhyrModelList
from Model.Stricklers.Stricklers import Stricklers
class StricklersList(SQLSubModel):
class StricklersList(PamhyrModelList):
_sub_classes = [
Stricklers,
]
def __init__(self, status = None):
if status is None:
status = SavedStatus()
self._status = status
self._stricks = []
@classmethod
def _sql_create(cls, execute):
return cls._create_submodel(execute)
@@ -43,74 +37,42 @@ class StricklersList(SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data = None):
new = cls(status = data["status"])
def _sql_load(cls, execute, data=None):
new = cls(status=data["status"])
new._stricks = Stricklers._sql_load(
new._lst = Stricklers._sql_load(
execute,
data = data
data=data
)
return new
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
execute("DELETE FROM stricklers")
objs = self._stricks
objs = self._lst
return self._save_submodel(execute, objs, data)
def __len__(self):
return len(self._stricks)
@property
def stricklers(self):
return self._stricks.copy()
return self.lst
def get(self, index):
if 0 <= index < len(self._stricks):
return self._stricks[index]
return None
def insert(self, index, strick):
self._stricks.insert(index, strick)
self._status.modified()
def add(self, index):
s = Stricklers(status = self._status)
def new(self, index):
s = Stricklers(status=self._status)
self.insert(index, s)
return s
def delete(self, stricks):
self._stricks = list(
filter(
lambda s: s not in stricks,
self._stricks
)
)
self._status.modified()
def delete_i(self, indexes):
stricks = set(
map(
lambda e: e[1],
filter(
lambda e: e[0] in indexes,
enumerate(self._stricks)
)
)
)
self.delete(stricks)
@timer
def sort(self, reverse:bool = False, key=None):
f = lambda st: st.name
def sort(self, reverse: bool = False, key=None):
def f(st): return st.name
if key is not None:
f = key
self._stricks = sorted(
self._stricks,
key = f,
reverse = reverse,
self._lst = sorted(
self._lst,
key=f,
reverse=reverse,
)
self._status.modified()
if self._status is not None:
self._status.modified()
+65 -27
View File
@@ -23,7 +23,7 @@ from datetime import datetime
from tools import timer, timestamp
from Model.DB import SQLModel
from Model.Tools.PamhyrDB import SQLModel
from Model.Saved import SavedStatus
from Model.Serializable import Serializable
from Model.Except import NotImplementedMethodeError
@@ -33,12 +33,13 @@ from Checker.Study import *
logger = logging.getLogger()
class Study(SQLModel):
_sub_classes = [
River,
]
def __init__(self, filename = None, init_new = True):
def __init__(self, filename=None, init_new=True):
# Metadata
self._version = "0.0.4"
self.creation_date = datetime.now()
@@ -46,7 +47,7 @@ class Study(SQLModel):
self.last_save_date = datetime.now()
self._filename = filename
super(Study, self).__init__(filename = filename)
super(Study, self).__init__(filename=filename)
self.status = SavedStatus()
@@ -59,9 +60,9 @@ class Study(SQLModel):
if init_new:
# Study data
self._river = River(status = self.status)
self._river = River(status=self.status)
else:
self._init_db_file(filename, is_new = False)
self._init_db_file(filename, is_new=False)
self._old_save_id = 0
@@ -128,7 +129,7 @@ class Study(SQLModel):
@filename.setter
def filename(self, filename):
self._filename = str(filename)
self._init_db_file(filename, is_new = True)
self._init_db_file(filename, is_new=True)
self.status.modified()
@property
@@ -139,7 +140,7 @@ class Study(SQLModel):
self._time_system = "time"
self.status.modified()
def use_date(self, date:datetime):
def use_date(self, date: datetime):
self._time_system = "date"
self._date = date
self.status.modified()
@@ -158,7 +159,7 @@ class Study(SQLModel):
# return cls()
@classmethod
def new(cls, name, description, date = None):
def new(cls, name, description, date=None):
me = cls()
me.name = name
me.description = description
@@ -178,14 +179,17 @@ class Study(SQLModel):
def _create(self):
# Info (metadata)
self.execute("CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)")
self.execute(
f"INSERT INTO info VALUES ('version', '{self._sql_format(self._version)}')",
commit = True
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)")
self.execute(
"INSERT INTO info VALUES ('version', " +
f"'{self._sql_format(self._version)}')",
commit=True
)
self.execute("INSERT INTO info VALUES ('name', '')")
self.execute("INSERT INTO info VALUES ('description', '')")
self.execute(f"INSERT INTO info VALUES ('time_system', '{self._time_system}')")
self.execute(
f"INSERT INTO info VALUES ('time_system', '{self._time_system}')")
self.execute(
f"INSERT INTO info VALUES ('date', " +
"'{self._date.timestamp()}')"
@@ -213,7 +217,8 @@ class Study(SQLModel):
logger.info("Update database")
if self._update_submodel(version[0]):
self.execute(f"UPDATE info SET value='{self._version}' WHERE key='version'")
self.execute(
f"UPDATE info SET value='{self._version}' WHERE key='version'")
return True
logger.info("TODO: update failed")
@@ -221,42 +226,75 @@ class Study(SQLModel):
@classmethod
def _load(cls, filename):
new = cls(init_new = False, filename = filename)
new = cls(init_new=False, filename=filename)
# TODO: Load metadata
new.name = new.execute("SELECT value FROM info WHERE key='name'")[0]
new.description = new.execute("SELECT value FROM info WHERE key='description'")[0]
new._time_system = new.execute("SELECT value FROM info WHERE key='time_system'")[0]
new.description = new.execute(
"SELECT value FROM info WHERE key='description'")[0]
new._time_system = new.execute(
"SELECT value FROM info WHERE key='time_system'")[0]
new._date = datetime.fromtimestamp(
float(new.execute("SELECT value FROM info WHERE key='date'")[0])
)
new.creation_date = datetime.fromtimestamp(
float(new.execute("SELECT value FROM info WHERE key='creation_date'")[0])
float(new.execute(
"SELECT value FROM info WHERE key='creation_date'")[0])
)
new.last_save_date = datetime.fromtimestamp(
float(new.execute("SELECT value FROM info WHERE key='last_save_date'")[0])
float(new.execute(
"SELECT value FROM info WHERE key='last_save_date'")[0])
)
# Load river data
new._river = River._sql_load(
lambda sql: new.execute(
sql,
fetch_one = False,
commit = True
fetch_one=False,
commit=True
),
data = {"status": new.status}
data={"status": new.status}
)
return new
def _save(self):
self.execute(f"UPDATE info SET value='{self._sql_format(self.name)}' WHERE key='name'")
self.execute(f"UPDATE info SET value='{self._sql_format(self.description)}' WHERE key='description'")
self.execute(f"UPDATE info SET value='{self._time_system}' WHERE key='time_system'")
self.execute(f"UPDATE info SET value='{timestamp(self._date)}' WHERE key='date'")
self.execute(f"UPDATE info SET value='{timestamp(self.creation_date)}' WHERE key='creation_date'")
self.execute(f"UPDATE info SET value='{timestamp(self.last_save_date)}' WHERE key='last_save_date'")
self.execute(
f"UPDATE info SET " +
f"value='{self._sql_format(self.name)}' WHERE key='name'"
)
self.execute(
f"UPDATE info SET " +
f"value='{self._sql_format(self.description)}' " +
"WHERE key='description'"
)
self.execute(
f"UPDATE info SET " +
f"value='{self._time_system}' WHERE key='time_system'"
)
self.execute(
f"UPDATE info SET " +
f"value='{timestamp(self._date)}' WHERE key='date'"
)
self.execute(
f"UPDATE info SET " +
f"value='{timestamp(self.creation_date)}' " +
"WHERE key='creation_date'"
)
self.execute(
f"UPDATE info SET " +
f"value='{timestamp(self.last_save_date)}' " +
"WHERE key='last_save_date'"
)
self._save_submodel([self._river])
self.commit()
def close(self):
"""Close db connection
Returns:
Nothing.
"""
self._close()
+24 -21
View File
@@ -1,4 +1,4 @@
# DB.py -- Pamhyr abstract model database classes
# PamhyrDB.py -- Pamhyr abstract model database classes
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
@@ -28,10 +28,12 @@ from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
# Top level model class
class SQLModel(SQL):
_sub_classes = []
def _init_db_file(self, db, is_new = True):
def _init_db_file(self, db, is_new=True):
exists = Path(db).exists()
if exists and is_new:
@@ -48,15 +50,14 @@ class SQLModel(SQL):
self._update() # Update db scheme if necessary
# self._load() # Load data
def __init__(self, filename = None):
def __init__(self, filename=None):
self._db = None
def _create_submodel(self):
fn = lambda sql: self.execute(
def fn(sql): return self.execute(
sql,
fetch_one = False,
commit = False
fetch_one=False,
commit=False
)
for cls in self._sub_classes:
@@ -69,10 +70,10 @@ class SQLModel(SQL):
raise NotImplementedMethodeError(self, self._create)
def _update_submodel(self, version):
fn = lambda sql: self.execute(
def fn(sql): return self.execute(
sql,
fetch_one = False,
commit = False
fetch_one=False,
commit=False
)
ok = True
@@ -85,11 +86,11 @@ class SQLModel(SQL):
def _update(self):
raise NotImplementedMethodeError(self, self._update)
def _save_submodel(self, objs, data = None):
fn = lambda sql: self.execute(
def _save_submodel(self, objs, data=None):
def fn(sql): return self.execute(
sql,
fetch_one = False,
commit = False
fetch_one=False,
commit=False
)
ok = True
@@ -103,18 +104,20 @@ class SQLModel(SQL):
raise NotImplementedMethodeError(self, self._save)
@classmethod
def _load(cls, filename = None):
def _load(cls, filename=None):
raise NotImplementedMethodeError(cls, cls._load)
# Sub model class
class SQLSubModel(object):
_sub_classes = []
def _sql_format(self, value):
# Replace ''' by '&#39;' to preserve SQL injection
if type(value) == str:
if value is str:
value = value.replace("'", "&#39;")
elif type(value) == bool:
elif value is bool:
value = 'TRUE' if value else 'FALSE'
return value
@@ -154,7 +157,7 @@ class SQLSubModel(object):
raise NotImplementedMethodeError(cls, cls._sql_update)
@classmethod
def _sql_load(cls, execute, data = None):
def _sql_load(cls, execute, data=None):
"""Load instance of this class from SQL data base
Args:
@@ -166,11 +169,11 @@ class SQLSubModel(object):
"""
raise NotImplementedMethodeError(cls, cls._sql_load)
def _save_submodel(self, execute, objs, data = None):
def _save_submodel(self, execute, objs, data=None):
for o in objs:
o._sql_save(execute, data = data)
o._sql_save(execute, data=data)
def _sql_save(self, execute, data = None):
def _sql_save(self, execute, data=None):
"""Save class data to data base
Args:
+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)
+241
View File
@@ -0,0 +1,241 @@
# 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 #
################
@property
def lst(self):
return self._lst
def __len__(self):
return len(self.lst)
def get(self, row):
return self._lst[row]
def set(self, row, new):
self._lst[row] = new
if self._status is not None:
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)
if self._status is not None:
self._status.modified()
def delete(self, lst):
for el in lst:
self._lst.remove(el)
if self._status is not None:
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)
if self._status is not None:
self._status.modified()
def move_up(self, index):
if index < len(self._lst):
next = index - 1
lst = self._lst
lst[index], lst[next] = lst[next], lst[index]
if self._status is not None:
self._status.modified()
def move_down(self, index):
if index >= 0:
prev = index + 1
lst = self._lst
lst[index], lst[prev] = lst[prev], lst[index]
if self._status is not None:
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
lst = self._tabs[lst]
lst[index], lst[next] = lst[next], lst[index]
self._status.modified()
def move_down(self, lst, index):
if index >= 0:
prev = index + 1
lst = self._tabs[lst]
lst[index], lst[prev] = lst[prev], lst[index]
self._status.modified()
+17
View File
@@ -0,0 +1,17 @@
# __init__.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
+110
View File
@@ -0,0 +1,110 @@
# test_Model.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
import unittest
import tempfile
from Model.Saved import SavedStatus
from Model.Study import Study
from Model.River import River
class StudyTestCase(unittest.TestCase):
def test_create_study(self):
study = Study.new("foo", "bar")
self.assertEqual(study.name, "foo")
self.assertEqual(study.description, "bar")
def test_open_study(self):
study = Study.open("../tests_cases/Enlargement/Enlargement.pamhyr")
self.assertNotEqual(study, None)
self.assertEqual(study.name, "Enlargement")
def test_save_open_study(self):
study = Study.new("foo", "bar")
dir = tempfile.mkdtemp()
f = os.path.join(dir, "foo.pamhyr")
# Save study
study.filename = f
study.save()
study.close()
# Reopen study
study = Study.open(f)
# Check
self.assertNotEqual(study, None)
self.assertEqual(study.name, "foo")
self.assertEqual(study.description, "bar")
def test_create_study_river(self):
study = Study.new("foo", "bar")
self.assertNotEqual(study.river, None)
class RiverTestCase(unittest.TestCase):
def test_create_river(self):
status = SavedStatus()
river = River(status=status)
self.assertNotEqual(river, None)
def test_create_river_nodes(self):
status = SavedStatus()
river = River(status=status)
self.assertNotEqual(river, None)
# Add nodes
n0 = river.add_node()
n1 = river.add_node(x=1.0, y=0.0)
n2 = river.add_node(x=0.0, y=1.0)
# Checks
self.assertEqual(river.nodes_counts(), 3)
nodes = river.nodes()
self.assertEqual(nodes[0], n0)
self.assertEqual(nodes[1], n1)
self.assertEqual(nodes[2], n2)
def test_create_river_edges(self):
status = SavedStatus()
river = River(status=status)
self.assertNotEqual(river, None)
# Add nodes
n0 = river.add_node()
n1 = river.add_node(x=1.0, y=0.0)
n2 = river.add_node(x=0.0, y=1.0)
self.assertEqual(river.nodes_counts(), 3)
# Add edges
e0 = river.add_edge(n0, n1)
e1 = river.add_edge(n1, n2)
# Checks
self.assertEqual(river.edges_counts(), 2)
edges = river.edges()
self.assertEqual(edges[0], e0)
self.assertEqual(edges[1], e1)
+37
View File
@@ -0,0 +1,37 @@
# 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
+38
View File
@@ -0,0 +1,38 @@
# ListSolver.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
import logging
from Scripts.AScript import AScript
logger = logging.getLogger()
class ScriptListSolver(AScript):
name = "ListSolver"
description = "List configured solver(s) for Pamhyr2"
def usage(self):
logger.info(f"Usage : {self._args[0]} {self._args[1]}")
def run(self):
for solver in self._conf.solvers:
print(f"{solver.name:<16} ({solver.type}): {solver.description}")
return 0
+116
View File
@@ -0,0 +1,116 @@
# 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
+113
View File
@@ -0,0 +1,113 @@
# Run.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
import logging
from queue import Queue
from PyQt5.QtCore import QProcess
from Scripts.AScript import AScript
from Model.Study import Study
logger = logging.getLogger()
class ScriptRun(AScript):
name = "Run"
description = "Run solver on Pamhyr2 a study"
def usage(self):
logger.info(
f"Usage : {self._args[0]} {self._args[1]} " +
"<SOLVER> <STUDY>"
)
def run(self):
if len(self._args) < 4:
return 1
command = self._args[1]
solver_name = self._args[2]
study_file = os.path.abspath(
self._args[3]
)
try:
solver = next(
filter(
lambda solver: solver.name == solver_name,
self._conf.solvers
)
)
except Exception as e:
logger.error(f"No solver found: {e}")
return 2
study = Study.open(study_file)
self._solver = solver
self._study = study
logger.info(
f"Run {solver.name} ({solver.type}) " +
"on study '{study.name}' ({study_file})"
)
# Workdir
workdir = os.path.join(
os.path.dirname(study.filename),
"_PAMHYR_",
study.name.replace(" ", "_"),
solver.name.replace(" ", "_")
)
os.makedirs(workdir, exist_ok=True)
logger.info(f"Set working dir to {workdir}")
# Preparate process
p = QProcess(None)
p.setWorkingDirectory(workdir)
self._q = Queue()
# Export and Run
logger.info(f"~Export~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
solver.export(study, workdir, qlog=self._q)
while self._q.qsize() != 0:
s = self._q.get()
logger.info(s)
if command == "run":
logger.info(f"~Run~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
solver.run(
study,
process=p,
output_queue=self._q
)
p.waitForFinished()
logger.info(f"~End~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
return 0
class ScriptExport(ScriptRun):
name = "Export"
description = "Export Pamhyr2 study for solver"
-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()
+31 -14
View File
@@ -22,9 +22,10 @@ import logging
from tools import timer
try:
# Installation allow Unix-like signal
from signal import SIGTERM, SIGSTOP, SIGCONT
_signal = True
except:
except Exception:
_signal = False
from enum import Enum
@@ -36,12 +37,14 @@ from Model.Results.River.River import River, Reach, Profile
logger = logging.getLogger()
class STATUS(Enum):
NOT_LAUNCHED = -1
STOPED = 0
RUNNING = 1
PAUSED = 5
class AbstractSolver(object):
_type = ""
@@ -154,7 +157,7 @@ class AbstractSolver(object):
# Export #
##########
def export(self, study, repertory, qlog = None):
def export(self, study, repertory, qlog=None):
raise NotImplementedMethodeError(self, self.export)
def cmd_args(self, study):
@@ -176,6 +179,14 @@ class AbstractSolver(object):
"""
raise NotImplementedMethodeError(self, self.input_param)
def output_param(self):
"""Return output command line parameter(s)
Returns:
Returns output parameter(s) string
"""
raise NotImplementedMethodeError(self, self.output_param)
def log_file(self):
"""Return log file name
@@ -189,8 +200,8 @@ class AbstractSolver(object):
###########
@timer
def results(self, study, repertory, qlog = None):
results = Results(study = study)
def results(self, study, repertory, qlog=None):
results = Results(study=study)
return results
#######
@@ -201,11 +212,11 @@ class AbstractSolver(object):
return os.path.abspath(
os.path.join(
os.path.dirname(__file__),
".."
"..", ".."
)
)
def _format_command(self, study, cmd, path = ""):
def _format_command(self, study, cmd, path=""):
"""Format command line
Args:
@@ -216,10 +227,10 @@ class AbstractSolver(object):
The executable and list of arguments
"""
# HACK: Works in most case... Trust me i'm an engineer
cmd = cmd.replace("@install_dir", self._install_dir())
cmd = cmd.replace("@path", path.replace(" ", "\ "))
cmd = cmd.replace("@path", path.replace(" ", "%20"))
cmd = cmd.replace("@input", self.input_param())
cmd = cmd.replace("@output", self.output_param())
cmd = cmd.replace("@dir", self._process.workingDirectory())
cmd = cmd.replace("@args", " ".join(self.cmd_args(study)))
@@ -228,7 +239,7 @@ class AbstractSolver(object):
if cmd[0] == "\"":
# Command line executable path is between " char
cmd = cmd.split("\"")
exe = cmd[1].replace("\ ", " ")
exe = cmd[1].replace("%20", " ")
args = list(
filter(
lambda s: s != "",
@@ -237,12 +248,12 @@ class AbstractSolver(object):
)
else:
# We suppose the command line executable path as no space char
cmd = cmd.replace("\ ", "&_&").split(" ")
cmd = cmd.replace("\\ ", "&_&").split(" ")
exe = cmd[0].replace("&_&", " ")
args = list(
filter(
lambda s: s != "",
map(lambda s: s.replace("&_&", "\ "), cmd[1:])
map(lambda s: s.replace("&_&", " "), cmd[1:])
)
)
@@ -284,6 +295,7 @@ class AbstractSolver(object):
self._process.start(
exe, args,
)
self._process.waitForStarted()
self._status = STATUS.RUNNING
return True
@@ -328,14 +340,17 @@ class AbstractSolver(object):
self._run_next(study)
def run(self, study, process = None, output_queue = None):
def run(self, study, process=None, output_queue=None):
self._study = study
if process is not None:
self._process = process
if output_queue is not None:
self._output = output_queue
self._process.readyRead.connect(self._data_ready)
self._process.finished.connect(lambda c, s: self._finished(study, c, s))
self._process.finished.connect(
lambda c, s: self._finished(study, c, s))
self._runs = [
self.run_input_data_fomater,
@@ -356,8 +371,9 @@ class AbstractSolver(object):
self._status = STATUS.STOPED
return True
def start(self, study, process = None):
def start(self, study, process=None):
if _signal:
# Solver is PAUSED, so continue execution
if self._status == STATUS.PAUSED:
os.kill(self._process.pid(), SIGCONT)
self._status = STATUS.RUNNING
@@ -371,6 +387,7 @@ class AbstractSolver(object):
if self._process is None:
return False
# Send SIGSTOP to PAUSED solver
os.kill(self._process.pid(), SIGSTOP)
self._status = STATUS.PAUSED
return True
+1
View File
@@ -20,6 +20,7 @@ from Solver.ASolver import (
AbstractSolver, STATUS
)
class GenericSolver(AbstractSolver):
_type = "generic"
+107 -58
View File
@@ -30,6 +30,7 @@ from Model.Results.River.River import River, Reach, Profile
logger = logging.getLogger()
def mage_file_open(filepath, mode):
f = open(filepath, mode)
@@ -39,6 +40,7 @@ def mage_file_open(filepath, mode):
return f
class Mage(AbstractSolver):
_type = "mage"
@@ -51,7 +53,6 @@ class Mage(AbstractSolver):
self._cmd_solver = "@path @input -o @output"
self._cmd_output = ""
@classmethod
def default_parameters(cls):
lst = super(Mage, cls).default_parameters()
@@ -87,8 +88,8 @@ class Mage(AbstractSolver):
@classmethod
def checkers(cls):
lst = [
MageNetworkGraphChecker(connectivity = True),
MageNetworkGraphChecker(connectivity = False)
MageNetworkGraphChecker(connectivity=True),
MageNetworkGraphChecker(connectivity=False)
]
return lst
@@ -98,16 +99,20 @@ class Mage(AbstractSolver):
##########
def cmd_args(self, study):
l = super(Mage, self).cmd_args(study)
lst = super(Mage, self).cmd_args(study)
l.append("-r")
lst.append("-r")
return l
return lst
def input_param(self):
name = self._study.name
return f"{name}.REP"
def output_param(self):
name = self._study.name
return f"{name}.BIN"
def log_file(self):
name = self._study.name
return f"{name}.TRA"
@@ -136,7 +141,10 @@ class Mage(AbstractSolver):
if edge._name == "":
name = f"Reach_{edge.id}"
with mage_file_open(os.path.join(repertory, "net", f"{name}.ST"), "w+") as f:
with mage_file_open(
os.path.join(repertory, "net", f"{name}.ST"),
"w+"
) as f:
files.append(str(os.path.join("net", f"{name}.ST")))
cnt_num = 1
@@ -145,42 +153,64 @@ class Mage(AbstractSolver):
c1 = f"{profile.code1:>6}"
c2 = f"{profile.code2:>6}"
t = f"{len(profile.points):>6}"
kp = f"{profile.kp:>13.4f}"
kp = f"{profile.kp:>12f}"[0:12]
pname = profile.name
if profile.name == "":
pname = f"p{profile.id:>3}".replace(" ", "p")
name = f"{pname}"
# Generate name from profile id prefixed with
# 'p' (and replace space char with '0' char)
pname = f"p{profile.id:>3}".replace(" ", "0")
name = f"{pname:<19}"
# Generate sediment additional data if available
sediment = ""
if profile.sl is not None:
if not any(filter(lambda f: ".GRA" in f, files)):
files.append(gra_file)
# Number of layers
nl = len(profile.sl)
sediment = f" {nl:>3}"
for l in profile.sl.layers:
sediment += f" {l.height:>10} {l.d50:>10} {l.sigma:>10} {l.critical_constraint:>10}"
f.write(f"{num}{c1}{c2}{t}{kp} {name} {sediment}\n")
# Layers data
for layer in profile.sl.layers:
sediment += (
f" {layer.height:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Profile header line
f.write(f"{num}{c1}{c2}{t} {kp} {name} {sediment}\n")
cnt_num += 1
# Points
for point in profile.points:
x = f"{point.x:>13.4f}"
y = f"{point.y:>13.4f}"
z = f"{point.z:>13.4f}"
x = f"{point.x:<12f}"[0:12]
y = f"{point.y:<12f}"[0:12]
z = f"{point.z:<12f}"[0:12]
n = f"{point.name:<3}"
# Generate sediment additional data if available
sediment = ""
prev = point.z
if point.sl is not None:
# Number of layers
nl = len(point.sl)
sediment = f"{nl:>3}"
for l in point.sl.layers:
prev = round(prev - l.height, 5)
sediment += f" {prev:>10} {l.d50:>10} {l.sigma:>10} {l.critical_constraint:>10}"
f.write(f"{x}{y}{z} {n} {sediment}\n")
# Layers data
for layer in point.sl.layers:
prev = round(prev - layer.height, 5)
sediment += (
f" {prev:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Point line
f.write(f"{x} {y} {z} {n} {sediment}\n")
# Profile last line
f.write(f" 999.9990 999.9990 999.9990\n")
return files
@@ -210,7 +240,7 @@ class Mage(AbstractSolver):
v1 = d[1]
if t in ["HYD", "QSO", "LIM"]:
v0 /= 60 # Convert first column to minute
v0 /= 60 # Convert first column to minute
f.write(f"{v0:10}{v1:10}\n")
@@ -251,7 +281,10 @@ class Mage(AbstractSolver):
# if qlog is not None:
# qlog.put(f"Export LAT file")
# with mage_file_open(os.path.join(repertory, f"{name}.LAT"), "w+") as f:
# with mage_file_open(
# os.path.join(repertory, f"{name}.LAT"),
# "w+"
# ) as f:
# files.append(f"{name}.LAT")
# name = f"{lateral.node.id:3}".replace(" ", "x")
@@ -391,13 +424,12 @@ class Mage(AbstractSolver):
if EXT in ["GRA"]:
f.write(f"{EXT} {file}\n")
@timer
def export(self, study, repertory, qlog = None):
def export(self, study, repertory, qlog=None):
self._study = study
name = study.name.replace(" ", "_")
self._export_ST(study, repertory, qlog, name = name)
self._export_ST(study, repertory, qlog, name=name)
return True
@@ -405,13 +437,17 @@ class Mage(AbstractSolver):
# RESULTS #
###########
def read_bin(self, study, repertory, results, qlog = None, name="0"):
def read_bin(self, study, repertory, results, qlog=None, name="0"):
return
@timer
def results(self, study, repertory, qlog = None, name="0"):
results = Results(study = study)
def results(self, study, repertory, qlog=None, name="0"):
results = Results(
study=study,
solver=self,
repertory=repertory,
name=name,
)
self.read_bin(study, repertory, results, qlog, name=name)
return results
@@ -420,6 +456,7 @@ class Mage(AbstractSolver):
# MAGE 7 #
##########
class Mage7(Mage):
_type = "mage7"
@@ -438,6 +475,7 @@ class Mage7(Mage):
# MAGE 8 #
##########
class Mage8(Mage):
_type = "mage8"
@@ -476,15 +514,15 @@ class Mage8(Mage):
##########
def cmd_args(self, study):
l = super(Mage8, self).cmd_args(study)
lst = super(Mage8, self).cmd_args(study)
if study.river.has_sediment():
l.append("-c=3")
lst.append("-c=3")
return l
return lst
@timer
def _export_PAR(self, study, repertory, qlog = None, name = "0"):
def _export_PAR(self, study, repertory, qlog=None, name="0"):
files = []
if qlog is not None:
@@ -503,7 +541,7 @@ class Mage8(Mage):
if name in ["command_line_arguments"]:
continue
if name is "mage_compute_reach_volume_balance":
if name == "mage_compute_reach_volume_balance":
value = "O" if value == "y" else "N"
f.write(f"{name} {value}\n")
@@ -511,7 +549,7 @@ class Mage8(Mage):
return files
@timer
def _export_NET(self, study, repertory, qlog = None, name="0"):
def _export_NET(self, study, repertory, qlog=None, name="0"):
files = []
if qlog is not None:
@@ -579,9 +617,8 @@ class Mage8(Mage):
return files
@timer
def export(self, study, repertory, qlog = None, name="0"):
def export(self, study, repertory, qlog=None, name="0"):
self._study = study
name = study.name.replace(" ", "_")
@@ -590,7 +627,8 @@ class Mage8(Mage):
files = self._export_ST(study, repertory, qlog, name=name)
files = files + self._export_PAR(study, repertory, qlog, name=name)
files = files + self._export_NET(study, repertory, qlog, name=name)
files = files + self._export_bound_cond(study, repertory, qlog, name=name)
files = files + \
self._export_bound_cond(study, repertory, qlog, name=name)
files = files + self._export_RUG(study, repertory, qlog, name=name)
files = files + self._export_INI(study, repertory, qlog, name=name)
self._export_REP(study, repertory, files, qlog, name=name)
@@ -602,17 +640,22 @@ class Mage8(Mage):
###########
@timer
def read_bin(self, study, repertory, results, qlog = None, name="0"):
def read_bin(self, study, repertory, results, qlog=None, name="0"):
fname = os.path.join(repertory, f"{name}.BIN")
logger.info(f"read_bin: Start reading '{fname}' ...")
with mage_file_open(fname, "r") as f:
newline = lambda: np.fromfile(f, dtype=np.int32, count=1)
endline = lambda: np.fromfile(f, dtype=np.int32, count=1)
def newline(): return np.fromfile(f, dtype=np.int32, count=1)
def endline(): return np.fromfile(f, dtype=np.int32, count=1)
read_int = lambda size: np.fromfile(f, dtype=np.int32, count=size)
read_float = lambda size: np.fromfile(f, dtype=np.float32, count=size)
read_float64 = lambda size: np.fromfile(f, dtype=np.float64, count=size)
def read_int(size): return np.fromfile(
f, dtype=np.int32, count=size)
def read_float(size): return np.fromfile(
f, dtype=np.float32, count=size)
def read_float64(size): return np.fromfile(
f, dtype=np.float64, count=size)
# Meta data (1st line)
newline()
@@ -686,7 +729,7 @@ class Mage8(Mage):
# Data
newline()
ip_to_r = lambda i: iprofiles[
def ip_to_r(i): return iprofiles[
next(
filter(
lambda k: k[0] <= i <= k[1],
@@ -694,14 +737,15 @@ class Mage8(Mage):
)
)
]
ip_to_ri = lambda r, i: i - reach_offset[r]
def ip_to_ri(r, i): return i - reach_offset[r]
ts = set()
end = False
while not end:
n = read_int(1)[0]
timestamp = read_float64(1)[0]
key = bytearray(np.fromfile(f, dtype=np.byte, count=1)).decode()
key = bytearray(np.fromfile(
f, dtype=np.byte, count=1)).decode()
data = read_float(n)
logger.debug(f"read_bin: timestamp = {timestamp} sec")
@@ -725,7 +769,7 @@ class Mage8(Mage):
logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
@timer
def read_gra(self, study, repertory, results, qlog = None, name="0"):
def read_gra(self, study, repertory, results, qlog=None, name="0"):
if not study.river.has_sediment():
return
@@ -733,12 +777,17 @@ class Mage8(Mage):
logger.info(f"read_gra: Start reading '{fname}' ...")
with mage_file_open(fname, "r") as f:
newline = lambda: np.fromfile(f, dtype=np.int32, count=1)
endline = lambda: np.fromfile(f, dtype=np.int32, count=1)
def newline(): return np.fromfile(f, dtype=np.int32, count=1)
def endline(): return np.fromfile(f, dtype=np.int32, count=1)
read_int = lambda size: np.fromfile(f, dtype=np.int32, count=size)
read_float = lambda size: np.fromfile(f, dtype=np.float32, count=size)
read_float64 = lambda size: np.fromfile(f, dtype=np.float64, count=size)
def read_int(size): return np.fromfile(
f, dtype=np.int32, count=size)
def read_float(size): return np.fromfile(
f, dtype=np.float32, count=size)
def read_float64(size): return np.fromfile(
f, dtype=np.float64, count=size)
# Meta data (1st line)
newline()
@@ -810,7 +859,7 @@ class Mage8(Mage):
endline()
# Data
ip_to_r = lambda i: iprofiles[
def ip_to_r(i): return iprofiles[
next(
filter(
lambda k: k[0] <= i <= k[1],
@@ -818,7 +867,7 @@ class Mage8(Mage):
)
)
]
ip_to_ri = lambda r, i: i - reach_offset[r]
def ip_to_ri(r, i): return i - reach_offset[r]
ts = set()
ind = 0
@@ -878,11 +927,11 @@ class Mage8(Mage):
logger.info(f"read_gra: ... end with {len(ts)} timestamp read")
@timer
def results(self, study, repertory, qlog = None):
def results(self, study, repertory, qlog=None):
self._study = study
name = study.name.replace(" ", "_")
results = super(Mage8, self).results(study, repertory, qlog, name = name)
self.read_gra(study, repertory, results, qlog, name = name)
results = super(Mage8, self).results(study, repertory, qlog, name=name)
self.read_gra(study, repertory, results, qlog, name=name)
return results
+3 -3
View File
@@ -24,9 +24,9 @@ from Solver.Mage import Mage7, Mage8
_translate = QCoreApplication.translate
solver_long_name = {
"generic": _translate("Solvers", "Generic"),
# "mage7": _translate("Solvers", "Mage version 7"),
"mage8": _translate("Solvers", "Mage version 8"),
"generic": "Generic",
# "mage7": "Mage v7",
"mage8": "Mage v8",
}
solver_type_list = {
+14 -6
View File
@@ -19,14 +19,18 @@
import os
import logging
from View.ASubWindow import ASubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtCore import QCoreApplication
_translate = QCoreApplication.translate
logger = logging.getLogger()
class AboutWindow(ASubWindow):
class AboutWindow(PamhyrDialog):
_pamhyr_ui = "about"
_pamhyr_name = "About"
def _path_file(self, filename):
return os.path.abspath(
os.path.join(
@@ -35,10 +39,14 @@ class AboutWindow(ASubWindow):
)
)
def __init__(self, title="About", parent=None):
super(AboutWindow, self).__init__(name=title, ui="about", parent=parent)
self.ui.setWindowTitle(title)
def __init__(self, study=None, config=None, parent=None):
super(AboutWindow, self).__init__(
title=_translate("About", "About"),
study=study,
config=config,
options=[],
parent=parent
)
# Version
with open(self._path_file("VERSION"), "r") as f:
+17 -13
View File
@@ -21,27 +21,32 @@ import logging
from datetime import datetime
from tools import timer, trace
from View.Plot.APlot import APlot
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
)
from View.BoundaryCondition.Edit.translate import *
from View.BoundaryCondition.Edit.translate import BCETranslate
_translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(APlot):
def __init__(self, canvas=None, data=None,
mode = "time", toolbar=None):
class Plot(PamhyrPlot):
def __init__(self, mode="time", data=None,
trad=None, canvas=None, toolbar=None,
parent=None):
super(Plot, self).__init__(
canvas=canvas,
trad=trad,
data=data,
toolbar=toolbar
toolbar=toolbar,
parent=parent
)
self._table_headers = self._trad.get_dict("table_headers")
self._mode = mode
def custom_ticks(self):
@@ -68,8 +73,8 @@ class Plot(APlot):
map(
lambda v: str(
datetime.fromtimestamp(v[0]) - t0
).split(",")[0]\
.replace("days", _translate("BoundaryCondition", "days"))\
).split(",")[0]
.replace("days", _translate("BoundaryCondition", "days"))
.replace("day", _translate("BoundaryCondition", "day")),
fx
)
@@ -84,7 +89,6 @@ class Plot(APlot):
self.canvas.axes.set_xticks(ticks=xx, labels=xt, rotation=45)
@timer
def draw(self):
self.canvas.axes.cla()
@@ -109,22 +113,22 @@ class Plot(APlot):
# Plot label
header = self.data.header
self.canvas.axes.set_xlabel(
table_headers[header[0]], color='black', fontsize=10
self._table_headers[header[0]], color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
table_headers[header[1]], color='black', fontsize=10
self._table_headers[header[1]], color='black', fontsize=10
)
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
#self.toolbar.update()
# self.toolbar.update()
self._init = True
@timer
def update(self, ind=None):
if self._init == False:
if not self._init:
self.draw()
return
+3 -137
View File
@@ -23,8 +23,7 @@ from datetime import date, time, datetime, timedelta
from tools import trace, timer
from View.ASubWindow import ASubMainWindow, AWidget
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrTable import PamhyrTableModel
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
@@ -47,131 +46,13 @@ from View.BoundaryCondition.Edit.UndoCommand import (
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand,
)
from View.BoundaryCondition.Edit.translate import *
_translate = QCoreApplication.translate
logger = logging.getLogger()
class ExtendedTimeEdit(AWidget):
def __init__(self, parent=None):
super(ExtendedTimeEdit, self).__init__(
ui="extendedTimeEdit",
parent=parent
)
self.parent = parent
self.spinBox_days = self.find(QSpinBox, "spinBox_days")
self.timeEdit = self.find(QTimeEdit, "timeEdit")
def set_time(self, time):
days = 0
stime = time
# if ',' in time, time format is 'DD days, HH:MM:SS',
# otherelse is 'HH:MM:SS'
if "," in time:
s = time.strip().split(" ")
days = int(s[0])
stime = s[-1]
qtime = QTime.fromString(
stime,
"h:mm:ss"
)
self.spinBox_days.setValue(days)
self.timeEdit.setTime(qtime)
def get_time(self):
days = self.spinBox_days.value()
time = self.timeEdit.time().toPyTime()
secs = (
(time.hour * 3600) +
(time.minute * 60) +
time.second
)
return timedelta(days=days, seconds=secs)
class ExtendedDateTimeEdit(AWidget):
def __init__(self, parent=None):
super(ExtendedDateTimeEdit, self).__init__(
ui="extendedDateTimeEdit",
parent=parent
)
self.parent = parent
self.dateTimeEdit = self.find(QDateTimeEdit, "dateTimeEdit")
def set_time(self, time):
qdatetime = QDateTime.fromString(
time,
"yyyy-MM-dd hh:mm:ss"
)
self.dateTimeEdit.setDateTime(qdatetime)
def get_time(self):
time = self.dateTimeEdit.dateTime().toPyDateTime()
return time
class ExTimeDelegate(QItemDelegate):
def __init__(self, data=None, mode="time", parent=None):
super(ExTimeDelegate, self).__init__(parent)
self._data = data
self._mode = mode
def createEditor(self, parent, option, index):
if self._mode == "time":
self.editor = ExtendedTimeEdit(parent=parent)
else:
self.editor = ExtendedDateTimeEdit(parent=parent)
value = index.data(Qt.DisplayRole)
self.editor.set_time(value)
logger.debug(str(value))
return self.editor
def setModelData(self, editor, model, index):
time = editor.get_time()
if self._mode == "time":
model.setData(index, int(time.total_seconds()))
else:
logger.debug(str(time.timestamp()))
model.setData(index, int(time.timestamp()))
editor.close()
editor.deleteLater()
def updateEditorGeometry(self, editor, option, index):
r = QRect(option.rect)
if self.editor.windowFlags() & Qt.Popup and editor.parent() is not None:
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
editor.setGeometry(r)
@pyqtSlot()
def currentItemChanged(self):
self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel):
def __init__(self, data=None, mode="time", undo=None):
super(QAbstractTableModel, self).__init__()
self._headers = data.header
self._data = data
self._mode = mode
self._undo = undo
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
return options
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return len(self._headers)
class TableModel(PamhyrTableModel):
def data(self, index, role):
if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter
@@ -189,7 +70,7 @@ class TableModel(QAbstractTableModel):
if self._data.get_type_column(column) == float:
value = f"{v:.4f}"
elif self._data.header[column] == "time":
if self._mode == "time":
if self._opt_data == "time":
t0 = datetime.fromtimestamp(0)
t = datetime.fromtimestamp(v)
value = str(t - t0)
@@ -200,12 +81,6 @@ class TableModel(QAbstractTableModel):
return value
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[section]]
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
@@ -315,12 +190,3 @@ class TableModel(QAbstractTableModel):
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
@@ -29,6 +29,7 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
logger = logging.getLogger()
class SetDataCommand(QUndoCommand):
def __init__(self, data, index, column, new_value):
QUndoCommand.__init__(self)
@@ -46,6 +47,7 @@ class SetDataCommand(QUndoCommand):
def redo(self):
self._data._set_i_c_v(self._index, self._column, self._new)
class SetMetaDataCommand(QUndoCommand):
def __init__(self, data, column, new_value):
QUndoCommand.__init__(self)
@@ -89,6 +91,7 @@ class AddCommand(QUndoCommand):
else:
self._data.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, data, rows):
QUndoCommand.__init__(self)
@@ -108,6 +111,7 @@ class DelCommand(QUndoCommand):
def redo(self):
self._data.delete_i(self._rows)
class SortCommand(QUndoCommand):
def __init__(self, data, _reverse):
QUndoCommand.__init__(self)
+72 -71
View File
@@ -16,10 +16,13 @@
# -*- coding: utf-8 -*-
import logging
from tools import timer, trace
from View.ASubWindow import ASubMainWindow, AWidget
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.PamhyrWidget import PamhyrWidget
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
from PyQt5.QtGui import (
QKeySequence,
@@ -38,26 +41,29 @@ from PyQt5.QtWidgets import (
QHeaderView, QDoubleSpinBox, QVBoxLayout,
)
from View.Plot.MplCanvas import MplCanvas
from View.Plot.navigation_toolbar_2qt import PamHyrNavigationToolbar2QT
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.BoundaryCondition.translate import long_types
from View.BoundaryCondition.Edit.translate import BCETranslate
from View.BoundaryCondition.Edit.UndoCommand import SetMetaDataCommand
from View.BoundaryCondition.Edit.Table import TableModel, ExTimeDelegate
from View.BoundaryCondition.Edit.Table import TableModel
from View.BoundaryCondition.Edit.Plot import Plot
_translate = QCoreApplication.translate
class WD50Sigma(AWidget):
logger = logging.getLogger()
class WD50Sigma(PamhyrWidget):
_pamhyr_ui = "d50sigma"
d50Changed = pyqtSignal(float)
sigmaChanged = pyqtSignal(float)
def __init__(self, parent=None):
super(WD50Sigma, self).__init__(
ui="d50sigma",
parent=parent
)
self.parent = parent
self.spinBox_d50 = self.find(QDoubleSpinBox, "doubleSpinBox_d50")
self.spinBox_sigma = self.find(QDoubleSpinBox, "doubleSpinBox_sigma")
@@ -89,75 +95,77 @@ class WD50Sigma(AWidget):
def valueChangedSigma(self, value):
self.sigmaChanged.emit(value)
class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Edit boundary condition",
data=None, study=None, parent=None):
self._data = data
self._study = study
self._title = title
self.compute_title()
class EditBoundaryConditionWindow(PamhyrWindow):
_pamhyr_ui = "EditBoundaryConditions"
_pamhyr_name = "Edit Boundary Conditions"
def __init__(self, data=None, study=None, config=None, parent=None):
self._data = data
trad = BCETranslate()
self._long_types = trad.get_dict("long_types")
name = self._pamhyr_name
if self._data is not None:
node_name = (self._data.node.name if self._data.node is not None
else _translate("BoundaryCondition", "Not associate"))
name = (
_translate("Edit boundary condition", self._pamhyr_name) +
f" - {study.name} " +
f" - {self._data.name} ({self._data.id}) " +
f"({self._long_types[self._data.bctype]} - {node_name})"
)
super(EditBoundaryConditionWindow, self).__init__(
name=self._title, ui="EditBoundaryConditions", parent=parent
title=name,
study=study,
config=config,
trad=trad,
parent=parent
)
self.ui.setWindowTitle(self._title)
self._hash_data.append(data)
self.setup_sc()
self.setup_table()
self.setup_plot()
self.setup_data()
self.setup_connections()
def compute_title(self):
if self._data is not None:
node_name = (self._data.node.name if self._data.node is not None
else _translate("BoundaryCondition", "Not associate"))
self._title = (
_translate("Edit boundary condition", self._title) +
f" - {self._study.name} " +
f" - {self._data.name} ({self._data.id}) " +
f"({long_types[self._data.bctype]} - {node_name})"
)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_data(self):
self._is_solid = self._data.bctype == "SL"
if self._is_solid:
layout = self.find(QVBoxLayout, "verticalLayout_table")
self._d50sigma = WD50Sigma(parent = self)
self._d50sigma = WD50Sigma(parent=self)
layout.addWidget(self._d50sigma)
self._d50sigma.set_d50(self._data.d50)
self._d50sigma.set_sigma(self._data.sigma)
def setup_table(self):
table = self.find(QTableView, "tableView")
self._table = TableModel(
data = self._data,
undo = self._undo_stack,
mode = self._study.time_system
headers = {}
table_headers = self._trad.get_dict("table_headers")
for h in self._data.header:
headers[h] = table_headers[h]
self._delegate_time = PamhyrExTimeDelegate(
data=self._data,
mode=self._study.time_system,
parent=self
)
if self._data.header[0] == "time":
self._delegate_time = ExTimeDelegate(
data = self._data,
mode = self._study.time_system,
parent = self
)
table.setItemDelegateForColumn(
0, self._delegate_time
)
table = self.find(QTableView, "tableView")
self._table = TableModel(
table_view=table,
table_headers=headers,
editable_headers=self._data.header,
delegates={
"time": self._delegate_time,
},
data=self._data,
undo=self._undo_stack,
opt_data=self._study.time_system
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
@@ -167,31 +175,26 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def setup_plot(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.toolbar = PamHyrNavigationToolbar2QT(
self.toolbar = PamhyrPlotToolbar(
self.canvas, self
)
self.verticalLayout.addWidget(self.toolbar)
self.verticalLayout.addWidget(self.canvas)
self.plot = Plot(
canvas = self.canvas,
data = self._data,
mode = self._study.time_system,
toolbar = self.toolbar,
canvas=self.canvas,
data=self._data,
mode=self._study.time_system,
trad=self._trad,
toolbar=self.toolbar,
)
self.plot.draw()
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
self._table.dataChanged.connect(self.update)
if self._is_solid:
@@ -240,7 +243,6 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._data) == 0 or len(rows) == 0:
@@ -272,8 +274,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self._table.move_down(row)
self.plot.update()
def copy(self):
def _copy(self):
rows = self.index_selected_rows()
table = []
@@ -285,7 +286,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.copyTableIntoClipboard(table)
def paste(self):
def _paste(self):
header, data = self.parseClipboardTable()
if len(data) == 0:
@@ -299,12 +300,12 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self._table.paste(row, header, data)
self.plot.update()
def undo(self):
def _undo(self):
self._table.undo()
self.plot.update()
self.widget_update()
def redo(self):
def _redo(self):
self._table.redo()
self.plot.update()
self.widget_update()
+18 -9
View File
@@ -18,14 +18,23 @@
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from View.BoundaryCondition.translate import BCTranslate
_translate = QCoreApplication.translate
table_headers = {
"x": _translate("BoundaryCondition", "X"),
"y": _translate("BoundaryCondition", "Y"),
"time": _translate("BoundaryCondition", "Time"),
"date": _translate("BoundaryCondition", "Date"),
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"),
"z": _translate("BoundaryCondition", "Z (m)"),
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
}
class BCETranslate(BCTranslate):
def __init__(self):
super(BCETranslate, self).__init__()
self._sub_dict["table_headers"] = {
"x": _translate("BoundaryCondition", "X"),
"y": _translate("BoundaryCondition", "Y"),
"time": _translate("BoundaryCondition", "Time"),
"date": _translate("BoundaryCondition", "Date"),
"discharge": _translate("BoundaryCondition", "Discharge (m³/s)"),
"z": _translate("BoundaryCondition", "Z (m)"),
"solid": _translate("BoundaryCondition", "Solid (kg/s)"),
}
+40 -41
View File
@@ -39,24 +39,33 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
TimeOverZ, TimeOverDischarge, ZOverDischarge
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.BoundaryCondition.UndoCommand import (
SetNameCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand,
)
from View.BoundaryCondition.translate import *
from View.BoundaryCondition.translate import BC_types
logger = logging.getLogger()
_translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, mode="type", tab="", parent=None):
def __init__(self, data=None, mode="type", tab="",
trad=None, parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._mode = mode
self._tab = tab
self._trad = trad
self._long_types = {}
if self._trad is not None:
self._long_types = self._trad.get_dict("long_types")
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
@@ -64,7 +73,7 @@ class ComboBoxDelegate(QItemDelegate):
if self._mode == "type":
lst = list(
map(
lambda k: long_types[k],
lambda k: self._long_types[k],
filter(
lambda k: self._tab in BC_types[k].compatibility(),
BC_types.keys()
@@ -95,8 +104,9 @@ class ComboBoxDelegate(QItemDelegate):
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()))
if self.editor.windowFlags() & Qt.Popup:
if editor.parent() is not None:
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
editor.setGeometry(r)
@pyqtSlot()
@@ -104,26 +114,20 @@ class ComboBoxDelegate(QItemDelegate):
self.commitData.emit(self.sender())
class TableModel(QAbstractTableModel):
def __init__(self, data=None, undo=None, tab=""):
super(QAbstractTableModel, self).__init__()
self._headers = list(table_headers.keys())
self._data = data
self._undo = undo
self._tab = tab
self._bcs = self._data.boundary_condition
class TableModel(PamhyrTableModel):
def __init__(self, **kwargs):
super(TableModel, self).__init__(**kwargs)
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
options |= Qt.ItemIsEditable
self._long_types = {}
if self._trad is not None:
self._long_types = self._trad.get_dict("long_types")
return options
def _setup_lst(self):
self._lst = self._data.boundary_condition
self._tab = self._opt_data
def rowCount(self, parent):
return self._bcs.len(self._tab)
def columnCount(self, parent):
return len(self._headers)
return self._lst.len(self._tab)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
@@ -133,24 +137,18 @@ class TableModel(QAbstractTableModel):
column = index.column()
if self._headers[column] == "name":
return self._bcs.get(self._tab, row).name
return self._lst.get(self._tab, row).name
elif self._headers[column] == "type":
t = self._bcs.get(self._tab, row).bctype
return long_types[t]
t = self._lst.get(self._tab, row).bctype
return self._long_types[t]
elif self._headers[column] == "node":
n = self._bcs.get(self._tab, row).node
n = self._lst.get(self._tab, row).node
if n is None:
return _translate("BoundaryCondition", "Not associate")
return n.name
return QVariant()
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return table_headers[self._headers[section]]
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
@@ -162,20 +160,21 @@ class TableModel(QAbstractTableModel):
if self._headers[column] == "name":
self._undo.push(
SetNameCommand(
self._bcs, self._tab,row, value
self._lst, self._tab, row, value
)
)
elif self._headers[column] == "type":
key = next(k for k, v in long_types.items() if v == value)
key = next(k for k, v in self._long_types.items()
if v == value)
self._undo.push(
SetTypeCommand(
self._bcs, self._tab,row, BC_types[key]
self._lst, self._tab, row, BC_types[key]
)
)
elif self._headers[column] == "node":
self._undo.push(
SetNodeCommand(
self._bcs, self._tab,row, self._data.node(value)
self._lst, self._tab, row, self._data.node(value)
)
)
except Exception as e:
@@ -190,7 +189,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
AddCommand(
self._bcs, self._tab,row
self._lst, self._tab, row
)
)
@@ -202,7 +201,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
DelCommand(
self._bcs, self._tab,rows
self._lst, self._tab, rows
)
)
@@ -214,7 +213,7 @@ class TableModel(QAbstractTableModel):
self._undo.push(
SortCommand(
self._bcs, self._tab,False
self._lst, self._tab, False
)
)
@@ -231,7 +230,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push(
MoveCommand(
self._bcs, self._tab,"up", row
self._lst, self._tab, "up", row
)
)
@@ -239,7 +238,7 @@ class TableModel(QAbstractTableModel):
self.layoutChanged.emit()
def move_down(self, index, parent=QModelIndex()):
if row > len(self._bcs):
if row > len(self._lst):
return
target = row
@@ -248,7 +247,7 @@ class TableModel(QAbstractTableModel):
self._undo_stack.push(
MoveCommand(
self._bcs, self._tab,"down", row
self._lst, self._tab, "down", row
)
)
@@ -26,6 +26,7 @@ from PyQt5.QtWidgets import (
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
from Model.BoundaryCondition.BoundaryConditionList import BoundaryConditionList
class SetNameCommand(QUndoCommand):
def __init__(self, bcs, tab, index, new_value):
QUndoCommand.__init__(self)
@@ -42,6 +43,7 @@ class SetNameCommand(QUndoCommand):
def redo(self):
self._bcs.get(self._tab, self._index).name = self._new
class SetNodeCommand(QUndoCommand):
def __init__(self, bcs, tab, index, node):
QUndoCommand.__init__(self)
@@ -58,6 +60,7 @@ class SetNodeCommand(QUndoCommand):
def redo(self):
self._bcs.get(self._tab, self._index).node = self._new
class SetTypeCommand(QUndoCommand):
def __init__(self, bcs, tab, index, _type):
QUndoCommand.__init__(self)
@@ -76,6 +79,7 @@ class SetTypeCommand(QUndoCommand):
def redo(self):
self._bcs.set(self._tab, self._index, self._new)
class AddCommand(QUndoCommand):
def __init__(self, bcs, tab, index):
QUndoCommand.__init__(self)
@@ -94,6 +98,7 @@ class AddCommand(QUndoCommand):
else:
self._bcs.insert(self._tab, self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, bcs, tab, rows):
QUndoCommand.__init__(self)
@@ -114,6 +119,7 @@ class DelCommand(QUndoCommand):
def redo(self):
self._bcs.delete_i(self._tab, self._rows)
class SortCommand(QUndoCommand):
def __init__(self, bcs, tab, _reverse):
QUndoCommand.__init__(self)
+54 -60
View File
@@ -20,8 +20,7 @@ import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -56,71 +55,71 @@ from View.BoundaryCondition.Table import (
)
from View.Network.GraphWidget import GraphWidget
from View.BoundaryCondition.translate import *
from View.BoundaryCondition.translate import BCTranslate
from View.BoundaryCondition.Edit.Window import EditBoundaryConditionWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Boundary conditions", study=None, parent=None):
self._title = title + " - " + study.name
class BoundaryConditionWindow(PamhyrWindow):
_pamhyr_ui = "BoundaryConditions"
_pamhyr_name = "Boundary conditions"
def __init__(self, study=None, config=None, parent=None):
name = self._pamhyr_name + " - " + study.name
super(BoundaryConditionWindow, self).__init__(
name=title, ui="BoundaryConditions", parent=parent
title=name,
study=study,
config=config,
trad=BCTranslate(),
parent=parent
)
self._study = study
self._bcs = self._study.river.boundary_condition
self.setup_sc()
self.setup_table()
self.setup_graph()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_sc(self):
self._undo_stack = QUndoStack()
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def setup_table(self):
self._table = {}
for t in ["liquid", "solid", "suspenssion"]:
table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel(
data = self._study.river,
undo = self._undo_stack,
tab = t,
)
table.setModel(self._table[t])
self._delegate_type = ComboBoxDelegate(
data = self._study.river,
mode = "type",
tab = t,
trad=self._trad,
data=self._study.river,
mode="type",
tab=t,
parent=self
)
self._delegate_node = ComboBoxDelegate(
data = self._study.river,
mode = "node",
tab = t,
trad=self._trad,
data=self._study.river,
mode="node",
tab=t,
parent=self
)
table.setItemDelegateForColumn(
1, self._delegate_type
table = self.find(QTableView, f"tableView_{t}")
self._table[t] = TableModel(
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=["name", "type", "node"],
delegates={
"type": self._delegate_type,
"node": self._delegate_node,
},
trad=self._trad,
data=self._study.river,
undo=self._undo_stack,
opt_data=t,
)
table.setItemDelegateForColumn(
2, self._delegate_node
)
table.setModel(self._table[t])
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
@@ -128,7 +127,7 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
def setup_graph(self):
self.graph_widget = GraphWidget(
self._study.river,
min_size=None, size=(200,200),
min_size=None, size=(200, 200),
only_display=True,
parent=self
)
@@ -141,11 +140,6 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
self.find(QAction, "action_edit").triggered.connect(self.edit)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self.undo_sc.activated.connect(self.undo)
self.redo_sc.activated.connect(self.redo)
self.copy_sc.activated.connect(self.copy)
self.paste_sc.activated.connect(self.paste)
def current_tab(self):
return self.find(QTabWidget, "tabWidget")\
.currentWidget()\
@@ -202,17 +196,17 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
row = self.index_selected_row()
self._table[tab].move_down(row)
def copy(self):
def _copy(self):
logger.info("TODO: copy")
def paste(self):
def _paste(self):
logger.info("TODO: paste")
def undo(self):
def _undo(self):
tab = self.current_tab()
self._table[tab].undo()
def redo(self):
def _redo(self):
tab = self.current_tab()
self._table[tab].redo()
@@ -220,17 +214,17 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
tab = self.current_tab()
rows = self.index_selected_rows()
for row in rows:
win = self.sub_win_filter_first(
"Edit boundary condition",
contain = [f"({self._bcs.get(tab, row).id})"]
)
data = self._bcs.get(tab, row)
if win is None:
win = EditBoundaryConditionWindow(
data=self._bcs.get(tab, row),
study=self._study,
parent=self
)
win.show()
else:
win.activateWindow()
if self.sub_window_exists(
EditBoundaryConditionWindow,
data=[self._study, None, data]
):
continue
win = EditBoundaryConditionWindow(
data=data,
study=self._study,
parent=self
)
win.show()
+22 -15
View File
@@ -18,6 +18,8 @@
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from Model.BoundaryCondition.BoundaryConditionTypes import (
NotDefined, PonctualContribution,
TimeOverZ, TimeOverDischarge, ZOverDischarge,
@@ -26,21 +28,6 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
_translate = QCoreApplication.translate
long_types = {
"ND": _translate("BoundaryCondition", "Not defined"),
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
"TZ": _translate("BoundaryCondition", "TimeZ"),
"TD": _translate("BoundaryCondition", "TimeDischarge"),
"ZD": _translate("BoundaryCondition", "ZDischarge"),
"SL": _translate("BoundaryCondition", "Solid"),
}
table_headers = {
"name": _translate("BoundaryCondition", "Name"),
"type": _translate("BoundaryCondition", "Type"),
"node": _translate("BoundaryCondition", "Node")
}
BC_types = {
"ND": NotDefined,
"PC": PonctualContribution,
@@ -49,3 +36,23 @@ BC_types = {
"ZD": ZOverDischarge,
"SL": Solid,
}
class BCTranslate(PamhyrTranslate):
def __init__(self):
super(BCTranslate, self).__init__()
self._sub_dict["long_types"] = {
"ND": _translate("BoundaryCondition", "Not defined"),
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
"TZ": _translate("BoundaryCondition", "TimeZ"),
"TD": _translate("BoundaryCondition", "TimeDischarge"),
"ZD": _translate("BoundaryCondition", "ZDischarge"),
"SL": _translate("BoundaryCondition", "Solid"),
}
self._sub_dict["table_headers"] = {
"name": _translate("BoundaryCondition", "Name"),
"type": _translate("BoundaryCondition", "Type"),
"node": _translate("BoundaryCondition", "Node")
}
+3 -25
View File
@@ -35,25 +35,12 @@ from PyQt5.QtWidgets import (
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
_translate = QCoreApplication.translate
class TableModel(QAbstractTableModel):
def __init__(self, data=None):
super(QAbstractTableModel, self).__init__()
self._headers = ["name", "status"]
self._data = data
def flags(self, index):
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
return options
def rowCount(self, parent):
return len(self._data)
def columnCount(self, parent):
return len(self._headers)
class TableModel(PamhyrTableModel):
def data(self, index, role):
row = index.row()
column = index.column()
@@ -81,12 +68,3 @@ class TableModel(QAbstractTableModel):
return self._data[row].summary
return QVariant()
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return self._headers[section]
return QVariant()
def update(self):
self.layoutChanged.emit()
+33
View File
@@ -0,0 +1,33 @@
# 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"),
}
+29 -25
View File
@@ -18,8 +18,7 @@
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
@@ -41,33 +40,40 @@ from PyQt5.QtWidgets import (
from View.CheckList.Table import TableModel
from View.CheckList.Worker import Worker
from View.CheckList.Translate import CheckListTranslate
_translate = QCoreApplication.translate
class CheckListWindow(ASubMainWindow, ListedSubWindow):
class CheckListWindow(PamhyrWindow):
_pamhyr_ui = "CheckList"
_pamhyr_name = "Check list"
signalStatus = pyqtSignal(str)
def __init__(self, title="Check list",
autorun:bool=True,
def __init__(self, autorun: bool = True,
study=None, config=None,
solver=None, parent=None):
self._title = title + " - " + study.name
self._autorun = autorun
self._study = study
self._config = config
self._solver = solver
self._parent = parent
name = self._pamhyr_name + " - " + study.name
super(CheckListWindow, self).__init__(
name=self._title, ui="CheckList", parent=parent
title=name,
study=study,
config=config,
trad=CheckListTranslate(),
options=[],
parent=parent
)
self.ui.setWindowTitle(self._title)
# Add solver to hash computation data
self._hash_data.append(self._solver)
self._checker_list = (
self._study.checkers() + self._solver.checkers()
self._study.checkers() +
self._solver.checkers()
)
self.setup_table()
@@ -78,16 +84,12 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._table = TableModel(
data = self._checker_list,
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
data=self._checker_list,
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_progress_bar(self):
self._progress = self.find(QProgressBar, f"progressBar")
self._p = 0 # Progress current step
@@ -98,7 +100,8 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
def setup_connections(self):
self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
self.find(QPushButton, "pushButton_retry").clicked.connect(self.retry)
self.find(QPushButton, "pushButton_cancel").clicked.connect(self.reject)
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.reject)
def setup_thread(self):
self._worker = Worker(self._study, self._checker_list)
@@ -122,7 +125,8 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
def _compute_status(self):
ok = len(list(filter(lambda c: c.is_ok(), self._checker_list)))
warning = len(list(filter(lambda c: c.is_warning(), self._checker_list)))
warning = len(
list(filter(lambda c: c.is_warning(), self._checker_list)))
error = len(list(filter(lambda c: c.is_error(), self._checker_list)))
return ok, warning, error
@@ -166,7 +170,7 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
self.update_statusbar()
def update(self, key:str):
def update(self, key: str):
if key == "start":
self.start_compute()
self.info_compute("Starting ...")
@@ -189,4 +193,4 @@ class CheckListWindow(ASubMainWindow, ListedSubWindow):
def accept(self):
self._parent.solver_log(self._solver)
#self.end()
# self.end()
+4 -2
View File
@@ -22,6 +22,7 @@ from PyQt5.QtCore import (
Qt, QObject, pyqtSlot, pyqtSignal
)
class Worker(QObject):
signalStatus = pyqtSignal(str)
@@ -37,10 +38,11 @@ class Worker(QObject):
self.signalStatus.emit('end')
def _compute(self):
# The computation loop
for checker in self._checker_list:
self.signalStatus.emit(checker.name)
# time.sleep(1)
checker.run(self._study)
# Run checker
checker._run(self._study)
self.signalStatus.emit("progress")
+32 -16
View File
@@ -16,7 +16,7 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from Solver.Solvers import solver_type_list
from Solver.GenericSolver import GenericSolver
@@ -24,12 +24,23 @@ from PyQt5.QtWidgets import (
QPushButton,
)
class ConfigureSolverWindow(ASubWindow):
def __init__(self, data=None, title="Configuration : Add Solver", parent=None):
class ConfigureSolverWindow(PamhyrDialog):
_pamhyr_ui = "ConfigureAddSolverDialog"
_pamhyr_name = "Add/Edit Solver"
def __init__(self, data=None, config=None, parent=None):
if data is not None:
name = f"Edit Solver - {data.name}"
else:
name = "Add a new Solver"
super(ConfigureSolverWindow, self).__init__(
name=title, ui="ConfigureAddSolverDialog", parent=parent
title=name,
config=config,
options=[],
parent=parent
)
self.ui.setWindowTitle(title)
# Combo box item
for solver in solver_type_list:
@@ -37,10 +48,10 @@ class ConfigureSolverWindow(ASubWindow):
# Data to return
self.data = data
if not self.data is None:
if self.data is not None:
self.copy_data()
self.connect()
self.setup_connection()
def copy_data(self):
self.set_combobox_text("comboBox_solver", self.data.type)
@@ -53,20 +64,23 @@ class ConfigureSolverWindow(ASubWindow):
self.set_line_edit_text("lineEdit_output", self.data._path_output)
self.set_line_edit_text("lineEdit_output_cmd", self.data._cmd_output)
def connect(self):
def setup_connection(self):
# File button
buttons = {
"pushButton_input": (lambda: self.file_dialog(
select_file = True,
callback = lambda f: self.set_line_edit_text("lineEdit_input", f[0])
select_file=True,
callback=lambda f: self.set_line_edit_text(
"lineEdit_input", f[0])
)),
"pushButton_solver": (lambda: self.file_dialog(
select_file = True,
callback = lambda f: self.set_line_edit_text("lineEdit_solver", f[0])
select_file=True,
callback=lambda f: self.set_line_edit_text(
"lineEdit_solver", f[0])
)),
"pushButton_output": (lambda: self.file_dialog(
select_file = True,
callback = lambda f: self.set_line_edit_text("lineEdit_output", f[0])
select_file=True,
callback=lambda f: self.set_line_edit_text(
"lineEdit_output", f[0])
)),
}
@@ -82,9 +96,11 @@ class ConfigureSolverWindow(ASubWindow):
else:
# Build new solver from selected type
stype = self.get_combobox_text("comboBox_solver")
self.data = solver_type_list[stype](self.get_line_edit_text("lineEdit_name"))
self.data = solver_type_list[stype](
self.get_line_edit_text("lineEdit_name"))
self.data.description = self.get_line_edit_text("lineEdit_description")
self.data.description = self.get_line_edit_text(
"lineEdit_description")
self.data.set_input(
self.get_line_edit_text("lineEdit_input"),
self.get_line_edit_text("lineEdit_input_cmd")
+34
View File
@@ -0,0 +1,34 @@
# 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 ConfigureTranslate(PamhyrTranslate):
def __init__(self):
super(ConfigureTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"name": _translate("Configure", "Name"),
"type": _translate("Configure", "Type"),
"description": _translate("Configure", "Description"),
}
+96 -104
View File
@@ -21,13 +21,14 @@ import logging
from copy import deepcopy
from config import Config
from View.ASubWindow import ASubWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrDialog
from View.Tools.PamhyrTable import PamhyrTableModel
from View.Stricklers.Table import TableModel
from View.Stricklers.translate import *
from View.Stricklers.translate import StricklersTranslate
from View.Stricklers.UndoCommand import *
from View.Configure.Translate import ConfigureTranslate
from View.Configure.Solver.Window import ConfigureSolverWindow
from PyQt5.QtGui import (
@@ -48,126 +49,109 @@ from Solver.Solvers import solver_long_name
logger = logging.getLogger()
class SolverTableModel(QAbstractTableModel):
def __init__(self, headers=[], rows=[]):
super(QAbstractTableModel, self).__init__()
self.rows = rows
self.headers = headers
def rowCount(self, parent):
return len(self.rows)
def columnCount(self, parent):
return len(self.headers)
class SolverTableModel(PamhyrTableModel):
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
ret = self.rows[index.row()][self.headers[index.column()]]
ret = self._data[index.row()][self._headers[index.column()]]
if self.headers[index.column()] == "type":
if self._headers[index.column()] == "type":
ret = solver_long_name[ret]
return ret
def headerData(self, section, orientation, role):
if (role == Qt.ItemDataRole.DisplayRole and
orientation == Qt.Orientation.Horizontal):
return self.headers[section].capitalize()
if (role == Qt.ItemDataRole.DisplayRole and
orientation == Qt.Orientation.Vertical):
return section + 1 # Start at 1
return QVariant()
def removeRow(self, index):
del self.rows[index.row()]
del self._data[index.row()]
self.layoutChanged.emit()
def add_solver(self, solver):
self.rows.append(solver)
self._data.append(solver)
self.layoutChanged.emit()
def change_solver(self, solver, index):
self.rows[index.row()] = solver
self._data[index.row()] = solver
self.layoutChanged.emit()
class ConfigureWindow(ASubWindow, ListedSubWindow):
def __init__(self, conf=None, title="Configure", parent=None):
class ConfigureWindow(PamhyrDialog):
_pamhyr_ui = "ConfigureDialog"
_pamhyr_name = "Configure"
def __init__(self, config=None, parent=None):
if config is None:
config = Config()
super(ConfigureWindow, self).__init__(
name=title, ui="ConfigureDialog", parent=parent
title=self._pamhyr_name,
config=config,
trad=ConfigureTranslate(),
options=[],
parent=parent
)
self.ui.setWindowTitle(title)
if conf is None:
self.conf = Config()
else:
self.conf = conf
self._trad_stricklers = StricklersTranslate()
self.setup_sc()
self.setup_custom_sc()
self.setup_solver()
self.setup_stricklers()
self.setup_data()
self.setup_connection()
# Solver
def setup_solver(self):
table = self.find(QTableView, "tableView_solver")
self.solver_table_model = SolverTableModel(
headers = ["name", "type", "description"],
rows = conf.solvers
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
data=self._config.solvers
)
table.setModel(self.solver_table_model)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_stricklers(self):
table = self.find(QTableView, f"tableView_stricklers")
self._stricklers = deepcopy(self._config.stricklers)
self._stricklers_table = TableModel(
table_view=table,
table_headers=self._trad_stricklers.get_dict("table_headers"),
editable_headers=["name", "comment", "minor", "medium"],
data=self._stricklers,
undo=self._undo_stack,
)
def setup_data(self):
# Meshing_Tool
self.set_line_edit_text("lineEdit_meshing_tool", self.conf.meshing_tool)
self.set_line_edit_text("lineEdit_meshing_tool",
self._config.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))
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.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")
self._stricklers = deepcopy(self.conf.stricklers)
self._stricklers_table = TableModel(
data = self._stricklers,
undo = self._undo_stack,
)
table.setModel(self._stricklers_table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
self.set_check_box("checkBox_backup", self._config.backup_enable)
self.set_line_edit_text("lineEdit_backup_path",
self._config.backup_path)
self.set_time_edit("timeEdit_backup_frequence",
self._config.backup_frequence)
self.set_spin_box("spinBox_backup_max", self._config.backup_max)
# Editor
self.set_line_edit_text("lineEdit_editor_cmd", str(self.conf.editor))
self.set_line_edit_text("lineEdit_editor_cmd",
str(self._config.editor))
# Language
languages = Config.languages()
for lang in languages:
self.combobox_add_item("comboBox_language", lang)
if self.conf.lang == languages[lang]:
if self._config.lang == languages[lang]:
self.set_combobox_text("comboBox_language", lang)
def setup_sc(self):
self._undo_stack = QUndoStack()
def setup_custom_sc(self):
self._debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
self._debug_sc.activated.connect(self.set_debug)
self.debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
self.undo_sc = QShortcut(QKeySequence.Undo, self)
self.redo_sc = QShortcut(QKeySequence.Redo, self)
self.copy_sc = QShortcut(QKeySequence.Copy, self)
self.paste_sc = QShortcut(QKeySequence.Paste, self)
def connect(self):
def setup_connection(self):
buttons = {
# Solvers
"pushButton_solver_add": self.add_solver,
@@ -179,14 +163,14 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
"pushButton_stricklers_sort": self.sort_stricklers,
# Others
"pushButton_backup_path": lambda: self.file_dialog(
select_file = False,
callback = lambda f: self.set_line_edit_text(
select_file=False,
callback=lambda f: self.set_line_edit_text(
"lineEdit_backup_path", f[0]
)
),
"pushButton_meshing_tool" : lambda: self.file_dialog(
select_file = True,
callback = lambda f: self.set_line_edit_text(
"pushButton_meshing_tool": lambda: self.file_dialog(
select_file=True,
callback=lambda f: self.set_line_edit_text(
"lineEdit_meshing_tool", f[0]
)
),
@@ -195,33 +179,36 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
for button in buttons:
self.find(QPushButton, button).clicked.connect(buttons[button])
self.debug_sc.activated.connect(self.set_debug)
def accept(self):
# Solvers
self.conf.solvers = self.solver_table_model.rows.copy()
self._config.solvers = self.solver_table_model._data.copy()
# Meshing_Tool
self.conf.meshing_tool = self.get_line_edit_text("lineEdit_meshing_tool")
self._config.meshing_tool = self.get_line_edit_text(
"lineEdit_meshing_tool")
# Const
self.conf.segment = self.get_line_edit_text("lineEdit_segment")
self.conf.max_listing = self.get_line_edit_text("lineEdit_max_listing")
self._config.segment = self.get_line_edit_text("lineEdit_segment")
self._config.max_listing = self.get_line_edit_text(
"lineEdit_max_listing")
# Backup
self.conf.backup_enable = self.get_check_box("checkBox_backup")
self.conf.backup_path = self.get_line_edit_text("lineEdit_backup_path")
self.conf.backup_frequence = self.get_time_edit("timeEdit_backup_frequence")
self.conf.backup_max = self.get_spin_box("spinBox_backup_max")
self._config.backup_enable = self.get_check_box("checkBox_backup")
self._config.backup_path = self.get_line_edit_text(
"lineEdit_backup_path")
self._config.backup_frequence = self.get_time_edit(
"timeEdit_backup_frequence")
self._config.backup_max = self.get_spin_box("spinBox_backup_max")
# Stricklers
self.conf.stricklers = deepcopy(self._stricklers)
self._config.stricklers = deepcopy(self._stricklers)
# Editor
self.conf.editor = self.get_line_edit_text("lineEdit_editor_cmd")
self._config.editor = self.get_line_edit_text("lineEdit_editor_cmd")
# Language
self.conf.lang = Config.languages()[self.get_combobox_text("comboBox_language")]
self._config.lang = Config.languages(
)[self.get_combobox_text("comboBox_language")]
self.end()
@@ -230,26 +217,30 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
self.end()
def end(self):
self.conf.save()
self._config.save()
self.close()
# Debug
def set_debug(self):
self.conf.debug = not self.conf.debug
logger.info(f"Debug mode set : {self.conf.debug}")
self._config.debug = not self._config.debug
logger.info(f"Debug mode set : {self._config.debug}")
self.parent.setup_debug_mode()
# Solvers
def edit_solver(self):
indexes = self.find(QTableView, "tableView_solver").selectionModel().selectedRows()
indexes = self.find(
QTableView, "tableView_solver").selectionModel().selectedRows()
for index in indexes:
self.edit_solver = ConfigureSolverWindow(
data=self.solver_table_model.rows[index.row()],
data=self.solver_table_model._data[index.row()],
config=self._config,
parent=self
)
if self.edit_solver.exec_():
self.solver_table_model.change_solver(self.edit_solver.data, index)
self.solver_table_model.change_solver(
self.edit_solver.data, index)
def add_solver(self):
dialog_solver = ConfigureSolverWindow(parent=self)
@@ -257,7 +248,8 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
self.solver_table_model.add_solver(dialog_solver.data)
def remove_solver(self):
indices = self.find(QTableView, "tableView_solver").selectionModel().selectedRows()
indices = self.find(
QTableView, "tableView_solver").selectionModel().selectedRows()
for index in sorted(indices):
self.solver_table_model.removeRow(index)
+32 -20
View File
@@ -20,8 +20,8 @@ import logging
from tools import trace, timer
from View.ASubWindow import ASubMainWindow
from View.ListedSubWindow import ListedSubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.PamhyrPythonEditor import PamhyrPythonEditor
from PyQt5.QtGui import (
QKeySequence,
@@ -38,34 +38,42 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QProgressBar, QLabel, QTextEdit,
QProgressBar, QLabel, QTextEdit, QHBoxLayout,
)
from PyQt5.Qsci import QsciScintilla, QsciLexerPython
logger = logging.getLogger()
_translate = QCoreApplication.translate
class ReplWindow(ASubMainWindow, ListedSubWindow):
def __init__(self, title="Debug REPL",
study=None, config=None,
class ReplWindow(PamhyrWindow):
_pamhyr_ui = "DebugRepl"
_pamhyr_name = "Debug REPL"
def __init__(self, study=None, config=None,
solver=None, parent=None):
self._title = title
self._study = study
self._config = config
self._parent = parent
super(ReplWindow, self).__init__(
name=self._title, ui="DebugRepl", parent=parent
title=self._pamhyr_name,
study=study,
config=config,
options=[],
parent=parent
)
self.ui.setWindowTitle(self._title)
self.__debug_exec_result__ = None
self._history = []
self._history_ind = 0
self.setup_editor()
self.setup_connections()
def setup_editor(self):
layout = self.find(QHBoxLayout, "horizontalLayout")
self._editor = PamhyrPythonEditor()
layout.insertWidget(0, self._editor)
def setup_connections(self):
self._hup_sc = QShortcut(QKeySequence("ctrl+Up"), self)
self._hdown_sc = QShortcut(QKeySequence("ctrl+Down"), self)
@@ -77,31 +85,35 @@ class ReplWindow(ASubMainWindow, ListedSubWindow):
def history_up(self):
if self._history_ind < len(self._history):
self._history_ind += 1
self.set_plaintext_edit_text("plainTextEdit", self._history[-self._history_ind])
self._editor.setText(self._history[-self._history_ind])
def history_down(self):
if self._history_ind > 0:
self._history_ind -= 1
self.set_plaintext_edit_text("plainTextEdit", self._history[-self._history_ind])
self._editor.setText(self._history[-self._history_ind])
else:
self.set_plaintext_edit_text("plainTextEdit", "")
self._editor.setText("")
def eval_python(self):
code = self.get_plaintext_edit_text("plainTextEdit")
code = self._editor.text()
self._history.append(code)
self.set_plaintext_edit_text("plainTextEdit", "")
self._editor.setText("")
self._history_ind = 0
# Code to rich_code
rich_code = code.strip().split("\n")
# Add return variable for results display at last row
rich_code[-1] = "self.__debug_exec_result__ = " + rich_code[-1]
rich_code = "\n".join(rich_code)
logger.debug(f"User debug command : {rich_code}")
logger.debug(f"User debug command : \n{rich_code}")
try:
value = exec(rich_code)
value = self.__debug_exec_result__
except Exception as e:
value = f"<font color=\"red\">" + str(e) + "</font>"
# Display code
msg = f"<font color=\"grey\"> # " + code + " #</font>"
self.find(QTextEdit, "textEdit").append(msg)
# Display results
self.find(QTextEdit, "textEdit").append(str(value))
+102
View File
@@ -0,0 +1,102 @@
# 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
import sys
import logging
import subprocess
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QAction
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
from View.Tools.PamhyrWindow import PamhyrWindow
logger = logging.getLogger()
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_open").triggered.connect(self.open)
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(self):
url = QUrl(f"file://{self._path}")
QDesktopServices.openUrl(url)
+11 -5
View File
@@ -16,9 +16,15 @@
# -*- coding: utf-8 -*-
from View.ASubWindow import ASubWindow
from View.Tools.PamhyrWindow import PamhyrWindow
class DummyWindow(ASubWindow):
def __init__(self, title="Dummy", parent=None):
super(DummyWindow, self).__init__(name=title, ui="dummy", parent=parent)
self.ui.setWindowTitle(title)
class DummyWindow(PamhyrWindow):
__ui = "Dummy"
__name = "Pamhyr Dummy Window"
def __init__(self, parent=None):
super(DummyWindow, self).__init__(
title=self.__name,
parent=parent,
)
Loaded 100 of 215 files, more files were not shown because too many files have changed in this diff. Show more