diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5e670bc1..19d0962c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,22 +1,130 @@ +# .gitlab-ci.yml -- Pamhyr gitlab-ci +# 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 . + stages: - build - test + - package - release ######### # BUILD # ######### +build-mage-linux: + stage: build + tags: + - linux + rules: + - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG + script: + - curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test5/downloads/packages/mage_linux.tgz + - mkdir -p mage + - cd mage + - tar xvf ../mage.tgz + artifacts: + paths: + - mage/mage + - mage/mage_extraire + - mage/mailleurPF + +# build-mage-linux: +# stage: build +# tags: +# - linux +# rules: +# - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG +# variables: +# GIT_SUBMODULE_STRATEGY: recursive +# GIT_SUBMODULE_DEPTH: 20 +# script: +# - cd mage/src/ +# - mkdir -p build +# - cd build +# - cmake .. +# - make +# artifacts: +# paths: +# - mage/src/build/mage +# - mage/src/build/mage_extraire +# - mage/src/build/mailleurPF + +build-mage-windows: + stage: build + tags: + - linux + rules: + - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG + script: + - curl -L -o mage.tgz https://gitlab.irstea.fr/jean-baptiste.faure/mage/-/releases/Test5/downloads/packages/mage_windows.tgz + - mkdir -p mage + - cd mage + - tar xvf ../mage.tgz + artifacts: + paths: + - mage/mage.exe + - mage/mage_extraire.exe + - mage/mailleurPF.exe + +# build-mage-windows: +# stage: build +# tags: +# - wine +# rules: +# - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG +# variables: +# GIT_SUBMODULE_STRATEGY: recursive +# GIT_SUBMODULE_DEPTH: 20 +# script: +# - cd mage/src/ +# - sed s/add_compile_definitions(win)/add_compile_definitions(windows)/ CMakeLists.txt > __tmp__ +# - echo set(CMAKE_Fortran_COMPILER "C:/Program\ Files/gcc/bin/gfortran.exe") > CMakeLists.txt +# - echo set(CMAKE_MAKE_PROGRAM "C:/Program Files\ \(x86\)/GnuWin32/bin/make.exe") >> CMakeLists.txt +# - type __tmp__ >> CMakeLists.txt +# - mkdir build +# - cd build +# - cmake -G "MinGW Makefiles" .. +# - make +# artifacts: +# paths: +# - mage/src/build/mage.exe +# - mage/src/build/mage_extraire.exe +# - mage/src/build/mailleurPF.exe + build: stage: build + tags: + - linux script: - - echo "TODO build pamhyr" + - cd packages + - ./version.sh "$CI_COMMIT_BRANCH" "$CI_COMMIT_TAG" "$CI_COMMIT_SHORT_SHA" + artifacts: + paths: + - VERSION -# build-lang: -# stage: build -# script: -# - cd ./src/lang/ -# - ./create_ts.sh +build-lang: + stage: build + tags: + - linux + script: + - cd ./src/lang/ + - ./create_ts.sh + artifacts: + paths: + - src/lang/*.qm ######### # TESTS # @@ -32,56 +140,69 @@ test: ############ linux-package: - stage: release + stage: package tags: - release - linux + needs: + - job: build-lang + artifacts: true + - job: build + artifacts: true + - job: build-mage-linux + artifacts: true rules: - - if: $CI_COMMIT_BRANCH == 'master' - - if: $CI_COMMIT_TAG - when: never + - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG artifacts: paths: - # - packages/pamhyr-src.tar.gz - packages/pamhyr-gnulinux-amd64.tar.xz script: - cd packages - ./linux.sh -# windows-package: -# stage: release -# tags: -# - release -# - wine -# rules: -# - if: $CI_COMMIT_BRANCH == 'master' -# - if: $CI_COMMIT_TAG -# when: never -# artifacts: -# paths: -# - packages/pamhyr-win-amd64.zip -# - packages/pamhyr-win-amd64.exe -# script: -# - cd packages -# - ./wine.sh ci +windows-package: + stage: package + tags: + - release + - wine + needs: + - job: build-lang + artifacts: true + - job: build + artifacts: true + - job: build-mage-windows + artifacts: true + rules: + - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG + artifacts: + paths: + - packages/pamhyr-win-amd64.exe + script: + - cd packages + - ./windows.bat + +########### +# RELEASE # +########### tag-release: stage: release tags: - release - linux - - wine + needs: + - job: linux-package + artifacts: true + - job: windows-package + artifacts: true rules: - if: $CI_COMMIT_TAG artifacts: paths: - packages/pamhyr-gnulinux-amd64.tar.xz - # - packages/pamhyr-win-amd64.zip - # - packages/pamhyr-win-amd64.exe + - packages/pamhyr-win-amd64.exe script: - cd packages - - ./linux.sh - # - ./wine.sh ci release: name: '$CI_COMMIT_TAG' description: 'Automatic release from tag $CI_COMMIT_TAG' @@ -92,12 +213,8 @@ tag-release: - 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' - link_type: 'other' - # - name: 'Windows amd64 (zip)' - # url: '${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/packages/pamhyr-win-amd64.zip' - # filepath: '/packages/pamhyr-win-amd64.zip' - # link_type: 'Packages' - # - 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' - # link_type: 'Packages' + 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' + link_type: 'package' diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..0ce9cdac --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Sylvain COULIBALY, INRAE, 2020 - 2022 +Théophile TERRAZ, INRAE, 2022 - 2023 +Pierre-Antoine ROUBY, INRAE, 2023 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..001d0883 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + pamhyr Copyright (C) 2023 INRAE + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..5664e303 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +git diff --git a/packages/linux.sh b/packages/linux.sh index 1cd72d28..44770b6e 100755 --- a/packages/linux.sh +++ b/packages/linux.sh @@ -1,5 +1,21 @@ #! /bin/sh +# linux.sh -- 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 . + echo " *** RM OLD ENV" rm dist/ -rf @@ -35,6 +51,18 @@ cp -r ../src/View/ui/*.ui dist/pamhyr/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 -p dist/pamhyr/mage/ +cp ../mage/mage dist/pamhyr/mage/ +cp ../mage/mage_extraire dist/pamhyr/mage/ +cp ../mage/mailleurPF dist/pamhyr/mage/ + +mkdir -p dist/pamhyr/tests_cases/ +mkdir -p dist/pamhyr/tests_cases/Saar +cp ../tests_cases/Saar/Saar.pamhyr dist/pamhyr/tests_cases/Saar/ echo " *** MAKE SRC PACKAGE" diff --git a/packages/pamhyr.nsi b/packages/pamhyr.nsi index 35be24ad..ccd25a76 100644 --- a/packages/pamhyr.nsi +++ b/packages/pamhyr.nsi @@ -1,8 +1,12 @@ !include "x64.nsh" -Name "PAMHYR" +!define LIC_NAME "LICENSE" +!define APP_NAME "PAMHYR" +Name "PAMHYR" OutFile "pamhyr-win-amd64.exe" +LicenseData "..\LICENSE" +LicenseText "I Agree" RequestExecutionLevel admin @@ -11,6 +15,7 @@ InstallDir $PROGRAMFILES\PAMHYR InstallDirRegKey HKLM "Software\PAMHYR" "Install_Dir" +Page license Page components Page directory Page instfiles diff --git a/packages/version.sh b/packages/version.sh new file mode 100755 index 00000000..8f5e8bcf --- /dev/null +++ b/packages/version.sh @@ -0,0 +1,26 @@ +#! /bin/sh + +# version.sh -- 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 . + +# ./version.sh BRANCH TAG COMMIT + +if [ -z $2 ]; +then + echo "$1-$3" > ../VERSION +else + echo "$2" > ../VERSION +fi diff --git a/packages/windows.bat b/packages/windows.bat new file mode 100644 index 00000000..16aba85c --- /dev/null +++ b/packages/windows.bat @@ -0,0 +1,54 @@ +rem windows.bat -- Pamhyr Windows batch for windows version building +rem Copyright (C) 2023 INRAE +rem +rem This program is free software: you can redistribute it and/or modify +rem it under the terms of the GNU General Public License as published by +rem the Free Software Foundation, either version 3 of the License, or +rem (at your option) any later version. +rem +rem This program is distributed in the hope that it will be useful, +rem but WITHOUT ANY WARRANTY; without even the implied warranty of +rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +rem GNU General Public License for more details. +rem +rem You should have received a copy of the GNU General Public License +rem along with this program. If not, see . + +@ECHO ON + +rem Python environment +python -m pip install -r ..\requirements.txt + +rem Build windows version +pyinstaller ..\src\pamhyr.py -y + +rem Copy data +mkdir dist\pamhyr\View\ui\ressources +mkdir dist\pamhyr\View\ui\Widgets + +rem UI +copy /y ..\src\View\ui\ressources\ dist\pamhyr\View\ui\ressources +copy /y ..\src\View\ui\Widgets\*.ui dist\pamhyr\View\ui\Widgets +copy /y ..\src\View\ui\*.ui dist\pamhyr\View\ui\ + +rem Lang +copy /y ..\src\lang\*.qm dist\pamhyr\lang\ + +rem Information +copy /y ..\VERSION dist\pamhyr\ +copy /y ..\AUTHORS dist\pamhyr\ +copy /y ..\LICENSE dist\pamhyr\ + +rem MAGE +mkdir dist\pamhyr\mage +copy /y ..\mage\mage.exe dist\pamhyr\mage\ +copy /y ..\mage\mage_extraire.exe dist\pamhyr\mage\ +copy /y ..\mage\mailleurPF.exe dist\pamhyr\mage\ + +rem Copy tests_cases +mkdir dist\pamhyr\tests_cases +mkdir dist\pamhyr\tests_cases\Saar +copy /y ..\tests_cases\Saar\Saar.pamhyr dist\pamhyr\tests_cases\Saar\ + +rem Make installer +"C:\Program Files (x86)\NSIS\makensis.exe" pamhyr.nsi diff --git a/packages/wine.sh b/packages/wine.sh index 1a769f51..4f794c51 100755 --- a/packages/wine.sh +++ b/packages/wine.sh @@ -1,5 +1,21 @@ #! /bin/sh +# wine.sh -- 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 . + echo " *** SETUP ENV" export WINARCH=win64 @@ -70,6 +86,9 @@ 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/ +cp ../VERSION dist/pamhyr/ +cp ../AUTHORS dist/pamhyr/ + # Update TS and build QM files OLD_PWD=$PWD cd ../src/lang/ diff --git a/src/AUTHORS b/src/AUTHORS new file mode 120000 index 00000000..9eadf712 --- /dev/null +++ b/src/AUTHORS @@ -0,0 +1 @@ +../AUTHORS \ No newline at end of file diff --git a/src/Checker/Checker.py b/src/Checker/Checker.py index 6e87a601..d10a8c4d 100644 --- a/src/Checker/Checker.py +++ b/src/Checker/Checker.py @@ -1,3 +1,19 @@ +# Checker.py -- Pamhyr abstract checker 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 . + # -*- coding: utf-8 -*- from enum import Enum diff --git a/src/Checker/Mage.py b/src/Checker/Mage.py index 4e121d51..edb0c349 100644 --- a/src/Checker/Mage.py +++ b/src/Checker/Mage.py @@ -1,3 +1,19 @@ +# Mage.py -- Pamhyr MAGE checkers +# 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 . + # -*- coding: utf-8 -*- import time diff --git a/src/Checker/Study.py b/src/Checker/Study.py index c6c41fed..868ac104 100644 --- a/src/Checker/Study.py +++ b/src/Checker/Study.py @@ -1,3 +1,19 @@ +# Study.py -- Pamhyr study checkers +# 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 . + # -*- coding: utf-8 -*- import time diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index 152a6269..86e0f1d2 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -1,10 +1,30 @@ +# BoundaryCondition.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 . + # -*- coding: utf-8 -*- +import logging + from tools import trace, timer, old_pamhyr_date_to_timestamp from Model.DB import SQLSubModel from Model.Except import NotImplementedMethodeError +logger = logging.getLogger() + class BoundaryCondition(SQLSubModel): _sub_classes = [] _id_cnt = 0 @@ -301,14 +321,17 @@ class BoundaryCondition(SQLSubModel): new = cls(name = self.name, status = self._status) new.node = self.node - for i, _ in self.data: + for i, _ in enumerate(self.data): new.add(i) for i in [0,1]: for j in [0,1]: if self._header[i] == new.header[j]: for ind, v in self.data: - new._set_i_c_v(ind, j, v[i]) + try: + new._set_i_c_v(ind, j, v[i]) + except Exception as e: + logger.info(e) return new diff --git a/src/Model/BoundaryCondition/BoundaryConditionList.py b/src/Model/BoundaryCondition/BoundaryConditionList.py index d14a870b..1a342dc7 100644 --- a/src/Model/BoundaryCondition/BoundaryConditionList.py +++ b/src/Model/BoundaryCondition/BoundaryConditionList.py @@ -1,3 +1,19 @@ +# BoundaryConditionList.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 . + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/BoundaryCondition/BoundaryConditionTypes.py b/src/Model/BoundaryCondition/BoundaryConditionTypes.py index c87e5b83..b2c73f11 100644 --- a/src/Model/BoundaryCondition/BoundaryConditionTypes.py +++ b/src/Model/BoundaryCondition/BoundaryConditionTypes.py @@ -1,3 +1,19 @@ +# BoundaryConditionTypes.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 . + # -*- coding: utf-8 -*- from Model.Except import NotImplementedMethodeError diff --git a/src/Model/DB.py b/src/Model/DB.py index 90869740..ef7d729d 100644 --- a/src/Model/DB.py +++ b/src/Model/DB.py @@ -1,3 +1,19 @@ +# DB.py -- Pamhyr abstract model database 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 . + # -*- coding: utf-8 -*- import os diff --git a/src/Model/Except.py b/src/Model/Except.py index ad311666..7d5e3959 100644 --- a/src/Model/Except.py +++ b/src/Model/Except.py @@ -1,3 +1,19 @@ +# Except.py -- Pamhyr model exceptions +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import ( diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index f7328dd4..18167a86 100644 --- a/src/Model/Friction/Friction.py +++ b/src/Model/Friction/Friction.py @@ -1,3 +1,19 @@ +# Friction.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 . + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/Model/Friction/FrictionList.py b/src/Model/Friction/FrictionList.py index a82c9f22..21e10695 100644 --- a/src/Model/Friction/FrictionList.py +++ b/src/Model/Friction/FrictionList.py @@ -1,3 +1,19 @@ +# FrictionList.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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/Model/Geometry/Point.py b/src/Model/Geometry/Point.py index 98d1e348..cc1971c0 100644 --- a/src/Model/Geometry/Point.py +++ b/src/Model/Geometry/Point.py @@ -1,3 +1,19 @@ +# Point.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 . + # -*- coding: utf-8 -*- from Model.Except import NotImplementedMethodeError diff --git a/src/Model/Geometry/PointXY.py b/src/Model/Geometry/PointXY.py index 9b84ef7c..7448d045 100644 --- a/src/Model/Geometry/PointXY.py +++ b/src/Model/Geometry/PointXY.py @@ -1,3 +1,19 @@ +# PointXY.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 . + # -*- coding: utf-8 -*- from math import dist diff --git a/src/Model/Geometry/PointXYZ.py b/src/Model/Geometry/PointXYZ.py index ff0a62fb..33a3bebf 100644 --- a/src/Model/Geometry/PointXYZ.py +++ b/src/Model/Geometry/PointXYZ.py @@ -1,3 +1,19 @@ +# PointXYZ.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 . + # -*- coding: utf-8 -*- from math import dist diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py index 9fa8e9ed..d8137932 100644 --- a/src/Model/Geometry/Profile.py +++ b/src/Model/Geometry/Profile.py @@ -1,3 +1,19 @@ +# Profile.py -- Pamhyr +# Copyright (C) 2023 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + # -*- coding: utf-8 -*- import logging diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index cb681383..12c4ea77 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -1,3 +1,19 @@ +# ProfileXYZ.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 . + # -*- coding: utf-8 -*- import numpy as np diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 728cf2db..0ddf06d8 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -1,3 +1,19 @@ +# Reach.py -- Pamhyr +# Copyright (C) 2023 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + # -*- coding: utf-8 -*- import logging diff --git a/src/Model/Geometry/Vector_1d.py b/src/Model/Geometry/Vector_1d.py index 51fef7f2..a3da9358 100644 --- a/src/Model/Geometry/Vector_1d.py +++ b/src/Model/Geometry/Vector_1d.py @@ -1,3 +1,19 @@ +# Vector_1d.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 . + # -*- coding: utf-8 -*- import numpy as np diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index bb106662..3bf747b0 100644 --- a/src/Model/InitialConditions/InitialConditions.py +++ b/src/Model/InitialConditions/InitialConditions.py @@ -1,3 +1,19 @@ +# InitialConditions.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 . + # -*- coding: utf-8 -*- from copy import copy, deepcopy diff --git a/src/Model/InitialConditions/InitialConditionsDict.py b/src/Model/InitialConditions/InitialConditionsDict.py index 5e8d5fba..48da56a0 100644 --- a/src/Model/InitialConditions/InitialConditionsDict.py +++ b/src/Model/InitialConditions/InitialConditionsDict.py @@ -1,3 +1,19 @@ +# InitialConditionsDict.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 . + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/LateralContribution/LateralContribution.py b/src/Model/LateralContribution/LateralContribution.py index 5098e9ba..38cbada4 100644 --- a/src/Model/LateralContribution/LateralContribution.py +++ b/src/Model/LateralContribution/LateralContribution.py @@ -1,10 +1,30 @@ +# LateralContribution.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 . + # -*- coding: utf-8 -*- +import logging + from tools import trace, timer, old_pamhyr_date_to_timestamp from Model.DB import SQLSubModel from Model.Except import NotImplementedMethodeError +logger = logging.getLogger() + class LateralContribution(SQLSubModel): _sub_classes = [] _id_cnt = 0 @@ -342,14 +362,17 @@ class LateralContribution(SQLSubModel): new.begin_kp = self.begin_kp new.end_kp = self.end_kp - for i, _ in self.data: + for i, _ in enumerate(self.data): new.add(i) for i in [0,1]: for j in [0,1]: if self._header[i] == new.header[j]: for ind, v in self.data: - new._set_i_c_v(ind, j, v[i]) + try: + new._set_i_c_v(ind, j, v[i]) + except Exception as e: + logger.info(e) self._status.modified() return new diff --git a/src/Model/LateralContribution/LateralContributionList.py b/src/Model/LateralContribution/LateralContributionList.py index 4d6a8567..ca7c111e 100644 --- a/src/Model/LateralContribution/LateralContributionList.py +++ b/src/Model/LateralContribution/LateralContributionList.py @@ -1,3 +1,19 @@ +# LateralContributionList.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 . + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/LateralContribution/LateralContributionTypes.py b/src/Model/LateralContribution/LateralContributionTypes.py index 69d9946c..d669266a 100644 --- a/src/Model/LateralContribution/LateralContributionTypes.py +++ b/src/Model/LateralContribution/LateralContributionTypes.py @@ -1,3 +1,19 @@ +# LateralContributionTypes.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 . + # -*- coding: utf-8 -*- from Model.Except import NotImplementedMethodeError diff --git a/src/Model/Network/Edge.py b/src/Model/Network/Edge.py index f1ced151..12249943 100644 --- a/src/Model/Network/Edge.py +++ b/src/Model/Network/Edge.py @@ -1,3 +1,19 @@ +# Edge.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 . + # -*- coding: utf-8 -*- from Model.Network.Node import Node diff --git a/src/Model/Network/Graph.py b/src/Model/Network/Graph.py index e2d549fb..62948a2b 100644 --- a/src/Model/Network/Graph.py +++ b/src/Model/Network/Graph.py @@ -1,3 +1,19 @@ +# Graph.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 . + # -*- coding: utf-8 -*- from functools import reduce diff --git a/src/Model/Network/Node.py b/src/Model/Network/Node.py index 5f37101f..7b8f52ba 100644 --- a/src/Model/Network/Node.py +++ b/src/Model/Network/Node.py @@ -1,3 +1,19 @@ +# Node.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 . + # -*- coding: utf-8 -*- from Model.Network.Point import Point diff --git a/src/Model/Network/Point.py b/src/Model/Network/Point.py index 8ea0ecd9..0bb12609 100644 --- a/src/Model/Network/Point.py +++ b/src/Model/Network/Point.py @@ -1,3 +1,19 @@ +# Point.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 . + # -*- coding: utf-8 -*- class Point(object): diff --git a/src/Model/Results/Results.py b/src/Model/Results/Results.py new file mode 100644 index 00000000..67698f4e --- /dev/null +++ b/src/Model/Results/Results.py @@ -0,0 +1,50 @@ +# Results.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 . + +import logging +import numpy as np + +from copy import deepcopy +from datetime import datetime + +from Model.Results.River.River import River + +logger = logging.getLogger() + +class Results(object): + def __init__(self, study = None): + self._study = study + self._river = River(self._study) + + self._meta_data = { + # Keep results creation date + "creation_date": datetime.now(), + } + + @property + def date(self): + date = self._meta_data["creation_date"] + return f"{date.isoformat(sep=' ')}" + + @property + def river(self): + return self._river + + def set(self, key, value): + self._meta_data[key] = value + + def get(self, key): + return self._meta_data[key] diff --git a/src/Model/Results/River/River.py b/src/Model/Results/River/River.py new file mode 100644 index 00000000..5ae1e90f --- /dev/null +++ b/src/Model/Results/River/River.py @@ -0,0 +1,107 @@ +# River.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 . + +import logging + +from datetime import datetime + +logger = logging.getLogger() + +class Profile(object): + def __init__(self, profile, study): + self._study = study + self._profile = profile # Source profile in the study + self._data = {} # Dict of dict {: {: , ...}, ...} + + @property + def name(self): + return self._profile.name + + @property + def kp(self): + return self._profile.kp + + @property + def geometry(self): + return self._profile + + def set(self, timestamp, key, data): + if timestamp not in self._data: + self._data[timestamp] = {} + + self._data[timestamp][key] = data + + def get_ts(self, timestamp): + return self._data[timestamp] + + def get_key(self, key): + return list( + map(lambda ts: self._data[ts][key], self._data) + ) + + def get_ts_key(self, timestamp, key): + return self._data[timestamp][key] + +class Reach(object): + def __init__(self, reach, study): + self._study = study + self._reach = reach # Source reach in the study + self._profiles = list( + map( + lambda p: Profile(p, self._study), + reach.profiles + ) + ) + + @property + def name(self): + return self._reach.name + + @property + def geometry(self): + return self._reach + + @property + def profiles(self): + return self._profiles.copy() + + def profile(self, id): + return self._profiles[id] + + def set(self, profile_id, timestamp, key, data): + self._profiles[profile_id].set(timestamp, key, data) + +class River(object): + def __init__(self, study): + self._study = study + + # Dict with timestamps as key + self._reachs = [] + + @property + def reachs(self): + return self._reachs.copy() + + def reach(self, id): + return self._reachs[id] + + def add(self, reach_id): + reachs = self._study.river.enable_edges() + + new = Reach(reachs[reach_id].reach, self._study) + + self._reachs.append(new) + return new diff --git a/src/Model/River.py b/src/Model/River.py index b21a95a9..3041c6c7 100644 --- a/src/Model/River.py +++ b/src/Model/River.py @@ -1,3 +1,19 @@ +# River.py -- Pamhyr river 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 . + # -*- coding: utf-8 -*- from Model.DB import SQLSubModel diff --git a/src/Model/Saved.py b/src/Model/Saved.py index 3f672731..60013cde 100644 --- a/src/Model/Saved.py +++ b/src/Model/Saved.py @@ -1,3 +1,19 @@ +# Saved.py -- Pamhyr model status 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 . + # -*- coding: utf-8 -*- import logging @@ -17,5 +33,5 @@ class SavedStatus(object): self._saved = True def modified(self): - logger.debug("model status set as modified") + # logger.debug("model status set as modified") self._saved = False diff --git a/src/Model/Serializable.py b/src/Model/Serializable.py index 57f2a4c1..0c083448 100644 --- a/src/Model/Serializable.py +++ b/src/Model/Serializable.py @@ -1,3 +1,19 @@ +# Serializable.py -- Pamhyr pickle abstract 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 . + # -*- coding: utf-8 -*- import pickle diff --git a/src/Model/SolverParameters/SolverParametersList.py b/src/Model/SolverParameters/SolverParametersList.py index 88d1d567..a6471356 100644 --- a/src/Model/SolverParameters/SolverParametersList.py +++ b/src/Model/SolverParameters/SolverParametersList.py @@ -1,3 +1,19 @@ +# SolverParametersList.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 . + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/Stricklers/Stricklers.py b/src/Model/Stricklers/Stricklers.py index cbc10009..baa484fa 100644 --- a/src/Model/Stricklers/Stricklers.py +++ b/src/Model/Stricklers/Stricklers.py @@ -1,3 +1,19 @@ +# Stricklers.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 . + # -*- coding: utf-8 -*- from tools import trace, timer @@ -124,7 +140,7 @@ class Stricklers(SQLSubModel): @minor.setter def minor(self, minor): - self._minor = int(minor) + self._minor = float(minor) @property def medium(self): @@ -132,4 +148,4 @@ class Stricklers(SQLSubModel): @medium.setter def medium(self, medium): - self._medium = int(medium) + self._medium = float(medium) diff --git a/src/Model/Stricklers/StricklersList.py b/src/Model/Stricklers/StricklersList.py index 049d9b83..82076bb1 100644 --- a/src/Model/Stricklers/StricklersList.py +++ b/src/Model/Stricklers/StricklersList.py @@ -1,3 +1,19 @@ +# StricklersList.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 . + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/Model/Study.py b/src/Model/Study.py index fc553f94..c907da3e 100644 --- a/src/Model/Study.py +++ b/src/Model/Study.py @@ -1,3 +1,19 @@ +# Study.py -- Pamhyr Study 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 . + # -*- coding: utf-8 -*- import os diff --git a/src/Scripts/plot_3DST.py b/src/Scripts/plot_3DST.py index 2647a32a..bcadf1f1 100644 --- a/src/Scripts/plot_3DST.py +++ b/src/Scripts/plot_3DST.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- # a lancer depuis src diff --git a/src/Solver/ASolver.py b/src/Solver/ASolver.py index e94bac4b..64cc01cb 100644 --- a/src/Solver/ASolver.py +++ b/src/Solver/ASolver.py @@ -1,8 +1,26 @@ +# ASolver.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 . + # -*- coding: utf-8 -*- import os import logging +from tools import timer + try: from signal import SIGTERM, SIGSTOP, SIGCONT _signal = True @@ -13,6 +31,9 @@ from enum import Enum from Model.Except import NotImplementedMethodeError +from Model.Results.Results import Results +from Model.Results.River.River import River, Reach, Profile + logger = logging.getLogger() class STATUS(Enum): @@ -148,25 +169,72 @@ class AbstractSolver(object): """ raise NotImplementedMethodeError(self, self.log_file) + ########### + # RESULTS # + ########### + + @timer + def results(self, study, repertory, qlog = None): + results = Results(study = study) + return results + ####### # Run # ####### + def _install_dir(self): + return os.path.abspath( + os.path.join( + os.path.dirname(__file__), + ".." + ) + ) + + def _format_command(self, cmd, path = ""): + """Format command line + + Args: + cmd: The command line + path: Optional path string (replace @path in cmd) + + Returns: + The executable and list of arguments + """ + # HACK: Works in most case... Trust me i'm an engineer + + cmd = cmd.replace("@install_dir", self._install_dir()) + cmd = cmd.replace("@path", path.replace(" ", "\ ")) + cmd = cmd.replace("@input", self.input_param()) + cmd = cmd.replace("@dir", self._process.workingDirectory()) + + logger.debug(f"! {cmd}") + + if cmd[0] == "\"": + # Command line executable path is between " char + cmd = cmd.split("\"") + exe = cmd[1].replace("\ ", " ") + args = "\"".join(cmd[2:]).split(" ")[1:] + else: + # We suppose the command line executable path as no space char + cmd = cmd.replace("\ ", "&_&").split(" ") + exe = cmd[0].replace("&_&", " ") + args = list(map(lambda s: s.replace("&_&", "\ "), cmd[1:])) + + logger.info(f"! {exe} {args}") + return exe, args + def run_input_data_fomater(self): if self._cmd_input == "": self._run_next() return True cmd = self._cmd_input - cmd = cmd.replace("@path", self._path_input) - cmd = cmd.replace("@input", self.input_param()) - cmd = cmd.replace("@dir", self._process.workingDirectory()) + exe, args = self._format_command(cmd, self._path_input) - logger.debug(f"! {cmd}") - - cmd = cmd.split() - exe = cmd[0] - args = cmd[1:] + if not os.path.exists(exe): + error = f"[ERROR] Path {exe} do not exists" + logger.info(error) + return error self._process.start( exe, args, @@ -180,15 +248,12 @@ class AbstractSolver(object): return True cmd = self._cmd_solver - cmd = cmd.replace("@path", self._path_solver) - cmd = cmd.replace("@input", self.input_param()) - cmd = cmd.replace("@dir", self._process.workingDirectory()) + exe, args = self._format_command(cmd, self._path_solver) - logger.debug(f"! {cmd}") - - cmd = cmd.split() - exe = cmd[0] - args = cmd[1:] + if not os.path.exists(exe): + error = f"[ERROR] Path {exe} do not exists" + logger.info(error) + return error self._process.start( exe, args, @@ -203,15 +268,12 @@ class AbstractSolver(object): return True cmd = self._cmd_output - cmd = cmd.replace("@path", self._path_output) - cmd = cmd.replace("@input", self.input_param()) - cmd = cmd.replace("@dir", self._process.workingDirectory()) + exe, args = self._format_command(cmd, self._path_output) - logger.debug(f"! {cmd}") - - cmd = cmd.split() - exe = cmd[0] - args = cmd[1:] + if not os.path.exists(exe): + error = f"[ERROR] Path {exe} do not exists" + logger.info(error) + return error self._process.start( exe, args, @@ -228,7 +290,9 @@ class AbstractSolver(object): def _run_next(self): self._step += 1 if self._step < len(self._runs): - self._runs[self._step]() + res = self._runs[self._step]() + if res is not True: + self._output.put(res) else: self._status = STATUS.STOPED @@ -254,7 +318,9 @@ class AbstractSolver(object): ] self._step = 0 # Run first step - self._runs[0]() + res = self._runs[0]() + if res is not True: + self._output.put(res) def kill(self): if self._process is None: diff --git a/src/Solver/GenericSolver.py b/src/Solver/GenericSolver.py index a80adf23..9e588bae 100644 --- a/src/Solver/GenericSolver.py +++ b/src/Solver/GenericSolver.py @@ -1,3 +1,19 @@ +# GenericSolver.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 . + # -*- coding: utf-8 -*- from Solver.ASolver import ( diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py index 125fcfff..6c129f88 100644 --- a/src/Solver/Mage.py +++ b/src/Solver/Mage.py @@ -1,12 +1,35 @@ +# Mage.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 . + # -*- coding: utf-8 -*- import os +import logging +import numpy as np from tools import timer from Solver.ASolver import AbstractSolver from Checker.Mage import MageNetworkGraphChecker +from Model.Results.Results import Results +from Model.Results.River.River import River, Reach, Profile + +logger = logging.getLogger() + def mage_file_open(filepath, mode): f = open(filepath, mode) @@ -321,6 +344,21 @@ class Mage(AbstractSolver): return True + ########### + # RESULTS # + ########### + + def read_bin(self, study, repertory, results, qlog = None): + return + + @timer + def results(self, study, repertory, qlog = None): + results = Results(study = study) + + self.read_bin(study, repertory, results, qlog) + + return results + ########## # MAGE 7 # ########## @@ -434,3 +472,128 @@ class Mage8(Mage): self._export_REP(study, repertory, files, qlog) return True + + ########### + # RESULTS # + ########### + + def read_bin(self, study, repertory, results, qlog = None): + logger.info(f"read_bin: Start ...") + + with mage_file_open(os.path.join(repertory, f"0.BIN"), "r") as f: + newline = lambda: np.fromfile(f, dtype=np.int32, count=1) + endline = lambda: 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) + + # Meta data (1st line) + newline() + + data = read_int(3) + + nb_reach = data[0] + nb_profile = data[1] + mage_version = data[2] + + logger.debug(f"read_bin: nb_reach = {nb_reach}") + logger.debug(f"read_bin: nb_profile = {nb_profile}") + logger.debug(f"read_bin: mage_version = {mage_version}") + + if mage_version <= 80: + msg = ( + "Read BIN files: " + + f"Possible incompatible mage version '{mage_version}', " + + "please check your solver configuration..." + ) + logger.warning(msg) + + if qlog is not None: + qlog.put("[WARNING] " + msg) + + results.set("solver_version", f"Mage8 ({mage_version})") + results.set("nb_reach", f"{nb_reach}") + results.set("nb_profile", f"{nb_profile}") + + endline() + + # Reach information (2nd line) + newline() + + reachs = [] + iprofiles = {} + reach_offset = {} + + data = read_int(2*nb_reach) + + for i in range(nb_reach): + # Add results reach to reach list + r = results.river.add(i) + reachs.append(r) + + # ID of first and last reach profiles + i1 = data[2*i] - 1 + i2 = data[2*i+1] - 1 + + # Add profile id correspondance to reach + key = (i1, i2) + iprofiles[key] = r + + # Profile ID offset + reach_offset[r] = i1 + + logger.debug(f"read_bin: iprofiles = {iprofiles}") + + endline() + + # X (3rd line) + newline() + _ = read_float(nb_profile) + endline() + + # Z and Y (4th line) + newline() + _ = read_float(3*nb_profile) + endline() + + # Data + newline() + + ip_to_r = lambda i: iprofiles[ + next( + filter( + lambda k: k[0] <= i <= k[1], + iprofiles + ) + ) + ] + ip_to_ri = lambda r, i: 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() + data = read_float(n) + + logger.debug(f"read_bin: timestamp = {timestamp} sec") + ts.add(timestamp) + + if key in ["Z", "Q"]: + for i, d in enumerate(data): + # Get reach corresponding to profile ID + reach = ip_to_r(i) + # Get profile id in reach + ri = ip_to_ri(reach, i) + + # Set data for profile RI + reach.set(ri, timestamp, key, d) + + endline() + end = newline().size <= 0 + + logger.debug(reachs[0].profiles[0]._data) + results.set("timestamps", ts) + logger.info(f"read_bin: ... end with {len(ts)} timestamp read") diff --git a/src/Solver/Solvers.py b/src/Solver/Solvers.py index 58db6f49..221f1ff4 100644 --- a/src/Solver/Solvers.py +++ b/src/Solver/Solvers.py @@ -1,3 +1,19 @@ +# Solvers.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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/VERSION b/src/VERSION new file mode 120000 index 00000000..6ff19de4 --- /dev/null +++ b/src/VERSION @@ -0,0 +1 @@ +../VERSION \ No newline at end of file diff --git a/src/View/ASubWindow.py b/src/View/ASubWindow.py index 478993cf..4e1c1327 100644 --- a/src/View/ASubWindow.py +++ b/src/View/ASubWindow.py @@ -1,3 +1,19 @@ +# ASubWindow.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 . + # -*- coding: utf-8 -*- import os @@ -19,6 +35,7 @@ from PyQt5.QtWidgets import ( QRadioButton, QComboBox, QFileDialog, QMessageBox, QTableView, QAction, QDateTimeEdit, QWidget, QPlainTextEdit, + QLabel, ) from PyQt5.QtCore import ( QTime, QDateTime, @@ -61,13 +78,14 @@ class WindowToolKit(object): return header, values - def file_dialog(self, select_file=True, callback=lambda x: None): + def file_dialog(self, select_file=True, callback=lambda x: None, directory=None): """Open a new file dialog and send result to callback function Args: select_file: Select a file if True, else select a dir callback: The callback function with one arguments, files selection list + directory: Defaut directory Returns: The returns of callback @@ -80,6 +98,8 @@ class WindowToolKit(object): mode = QFileDialog.FileMode.Directory dialog.setFileMode(mode) + if directory is not None: + dialog.setDirectory(directory) if dialog.exec_(): file_names = dialog.selectedFiles() @@ -130,6 +150,30 @@ class ASubWindowFeatures(object): return qtype + def get_label_text(self, name:str): + """Get text of label component + + Args: + label: The label component name + + Returns: + Text + """ + return self.find(QLabel, name).text() + + def set_label_text(self, name:str, text:str): + """Set text of label component + + Args: + text_edit: The label component name + text: The text + + Returns: + Nothing + """ + self.find(QLabel, name).setText(text) + + def set_line_edit_text(self, name:str, text:str): """Set text of line edit component @@ -421,10 +465,12 @@ class ASubWindowFeatures(object): class ASubMainWindow(QMainWindow, ASubWindowFeatures, WindowToolKit): def __init__(self, name="", ui="dummy", parent=None): super(ASubMainWindow, self).__init__(parent=parent) - self.ui = loadUi( - os.path.join(os.path.dirname(__file__), "ui", f"{ui}.ui"), - self - ) + if ui is not None: + self.ui = loadUi( + os.path.join(os.path.dirname(__file__), "ui", f"{ui}.ui"), + self + ) + self.name = name self.parent = parent if self.parent is not None: diff --git a/src/View/About/Window.py b/src/View/About/Window.py index 9f84ad86..7c269aaf 100644 --- a/src/View/About/Window.py +++ b/src/View/About/Window.py @@ -1,8 +1,63 @@ +# 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 . + # -*- coding: utf-8 -*- +import os +import logging + from View.ASubWindow import ASubWindow +from PyQt5.QtCore import QCoreApplication + +_translate = QCoreApplication.translate +logger = logging.getLogger() + class AboutWindow(ASubWindow): + def _path_file(self, filename): + return os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "..", "..", filename + ) + ) + + def __init__(self, title="About", parent=None): super(AboutWindow, self).__init__(name=title, ui="about", parent=parent) self.ui.setWindowTitle(title) + + # Version + with open(self._path_file("VERSION"), "r") as f: + version = f.readline().strip() + logger.info(f"version: {version}") + + label = self.get_label_text("label_version") + label = label.replace("@version", version) + self.set_label_text("label_version", label) + + # Authors + with open(self._path_file("AUTHORS"), "r") as f: + label = "" + try: + while True: + author = next(f).strip() + logger.info(f"author: {author}") + label = f"\n - {author}" + label + except StopIteration: + label = _translate("About", "Contributors: ") + label + label = "Copyright © 2023 INRAE\n" + label + self.set_label_text("label_copyright", label) diff --git a/src/View/BoundaryCondition/Edit/Plot.py b/src/View/BoundaryCondition/Edit/Plot.py index 9f6df875..b27380a5 100644 --- a/src/View/BoundaryCondition/Edit/Plot.py +++ b/src/View/BoundaryCondition/Edit/Plot.py @@ -1,5 +1,23 @@ +# Plot.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 . + # -*- coding: utf-8 -*- +import logging + from datetime import datetime from tools import timer, trace @@ -13,6 +31,8 @@ from View.BoundaryCondition.Edit.translate import * _translate = QCoreApplication.translate +logger = logging.getLogger() + class Plot(APlot): def __init__(self, canvas=None, data=None, mode = "time", toolbar=None): @@ -25,6 +45,9 @@ class Plot(APlot): self._mode = mode def custom_ticks(self): + if self.data.header[0] != "time": + return + t0 = datetime.fromtimestamp(0) nb = len(self.data.data) mod = int(nb / 5) diff --git a/src/View/BoundaryCondition/Edit/Table.py b/src/View/BoundaryCondition/Edit/Table.py index dfe805e1..6536bd0d 100644 --- a/src/View/BoundaryCondition/Edit/Table.py +++ b/src/View/BoundaryCondition/Edit/Table.py @@ -1,6 +1,23 @@ +# Table.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 . + # -*- coding: utf-8 -*- import logging +import traceback from datetime import date, time, datetime, timedelta @@ -196,11 +213,15 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - self._undo.push( - SetDataCommand( - self._data, row, column, value + try: + self._undo.push( + SetDataCommand( + self._data, row, column, value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/BoundaryCondition/Edit/UndoCommand.py b/src/View/BoundaryCondition/Edit/UndoCommand.py index b2eb09ac..4bb2605f 100644 --- a/src/View/BoundaryCondition/Edit/UndoCommand.py +++ b/src/View/BoundaryCondition/Edit/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -17,7 +33,8 @@ class SetDataCommand(QUndoCommand): self._index = index self._column = column self._old = self._data.get_i(self._index)[self._column] - self._new = new_value + _type = self._data.get_type_column(self._column) + self._new = _type(new_value) def undo(self): self._data._set_i_c_v(self._index, self._column, self._old) diff --git a/src/View/BoundaryCondition/Edit/Window.py b/src/View/BoundaryCondition/Edit/Window.py index 4e8513fa..efcf0993 100644 --- a/src/View/BoundaryCondition/Edit/Window.py +++ b/src/View/BoundaryCondition/Edit/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from tools import timer, trace @@ -55,7 +71,7 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow): self._title = ( _translate("Edit boundary condition", self._title) + f" - {self._study.name} " + - f" - {self._data.name} " + + f" - {self._data.name} ({self._data.id}) " + f"({long_types[self._data.bctype]} - {node_name})" ) diff --git a/src/View/BoundaryCondition/Edit/translate.py b/src/View/BoundaryCondition/Edit/translate.py index 44ef5a46..4c09f5fc 100644 --- a/src/View/BoundaryCondition/Edit/translate.py +++ b/src/View/BoundaryCondition/Edit/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/BoundaryCondition/Table.py b/src/View/BoundaryCondition/Table.py index 98890e0c..fd39a13a 100644 --- a/src/View/BoundaryCondition/Table.py +++ b/src/View/BoundaryCondition/Table.py @@ -1,5 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback + from tools import trace, timer from PyQt5.QtCore import ( @@ -27,6 +46,8 @@ from View.BoundaryCondition.UndoCommand import ( ) from View.BoundaryCondition.translate import * +logger = logging.getLogger() + _translate = QCoreApplication.translate class ComboBoxDelegate(QItemDelegate): @@ -137,25 +158,29 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - if self._headers[column] == "name": - self._undo.push( - SetNameCommand( - self._bcs, self._tab,row, value + try: + if self._headers[column] == "name": + self._undo.push( + SetNameCommand( + self._bcs, self._tab,row, value + ) ) - ) - elif self._headers[column] == "type": - key = next(k for k, v in long_types.items() if v == value) - self._undo.push( - SetTypeCommand( - self._bcs, self._tab,row, BC_types[key] + elif self._headers[column] == "type": + key = next(k for k, v in long_types.items() if v == value) + self._undo.push( + SetTypeCommand( + self._bcs, self._tab,row, BC_types[key] + ) ) - ) - elif self._headers[column] == "node": - self._undo.push( - SetNodeCommand( - self._bcs, self._tab,row, self._data.node(value) + elif self._headers[column] == "node": + self._undo.push( + SetNodeCommand( + self._bcs, self._tab,row, self._data.node(value) + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/BoundaryCondition/UndoCommand.py b/src/View/BoundaryCondition/UndoCommand.py index 7b1c7f5e..bbf8cb2c 100644 --- a/src/View/BoundaryCondition/UndoCommand.py +++ b/src/View/BoundaryCondition/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -18,7 +34,7 @@ class SetNameCommand(QUndoCommand): self._tab = tab self._index = index self._old = self._bcs.get(self._tab, self._index).name - self._new = new_value + self._new = str(new_value) def undo(self): self._bcs.get(self._tab, self._index).name = self._old diff --git a/src/View/BoundaryCondition/Window.py b/src/View/BoundaryCondition/Window.py index 2e7dd430..a2853590 100644 --- a/src/View/BoundaryCondition/Window.py +++ b/src/View/BoundaryCondition/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging @@ -49,7 +65,7 @@ logger = logging.getLogger() class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow): def __init__(self, title="Boundary conditions", study=None, parent=None): - title = title + " - " + study.name + self._title = title + " - " + study.name super(BoundaryConditionWindow, self).__init__( name=title, ui="BoundaryConditions", parent=parent @@ -63,7 +79,7 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow): self.setup_graph() self.setup_connections() - self.ui.setWindowTitle(title) + self.ui.setWindowTitle(self._title) def setup_sc(self): self._undo_stack = QUndoStack() @@ -204,9 +220,17 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow): tab = self.current_tab() rows = self.index_selected_rows() for row in rows: - win = EditBoundaryConditionWindow( - data=self._bcs.get(tab, row), - study=self._study, - parent=self + win = self.sub_win_filter_first( + "Edit boundary condition", + contain = [f"({self._bcs.get(tab, row).id})"] ) - win.show() + + if win is None: + win = EditBoundaryConditionWindow( + data=self._bcs.get(tab, row), + study=self._study, + parent=self + ) + win.show() + else: + win.activateWindow() diff --git a/src/View/BoundaryCondition/translate.py b/src/View/BoundaryCondition/translate.py index e191d0be..661c1ad0 100644 --- a/src/View/BoundaryCondition/translate.py +++ b/src/View/BoundaryCondition/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/CheckList/Table.py b/src/View/CheckList/Table.py index 071b4907..5810c4be 100644 --- a/src/View/CheckList/Table.py +++ b/src/View/CheckList/Table.py @@ -1,3 +1,19 @@ +# Table.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 . + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/View/CheckList/Window.py b/src/View/CheckList/Window.py index d04c8913..4c40ad1f 100644 --- a/src/View/CheckList/Window.py +++ b/src/View/CheckList/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/View/CheckList/Worker.py b/src/View/CheckList/Worker.py index 9137d26b..efd95ded 100644 --- a/src/View/CheckList/Worker.py +++ b/src/View/CheckList/Worker.py @@ -1,3 +1,19 @@ +# Worker.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 . + # -*- coding: utf-8 -*- import time diff --git a/src/View/Configure/Solver/Window.py b/src/View/Configure/Solver/Window.py index 71d4f798..c295481f 100644 --- a/src/View/Configure/Solver/Window.py +++ b/src/View/Configure/Solver/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/Configure/Window.py b/src/View/Configure/Window.py index 818e5d9c..23a371ac 100644 --- a/src/View/Configure/Window.py +++ b/src/View/Configure/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Debug/Window.py b/src/View/Debug/Window.py index 974eace3..346c2ce0 100644 --- a/src/View/Debug/Window.py +++ b/src/View/Debug/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/View/DummyWindow.py b/src/View/DummyWindow.py index 576b5188..05117bca 100644 --- a/src/View/DummyWindow.py +++ b/src/View/DummyWindow.py @@ -1,3 +1,19 @@ +# DummyWindow.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 . + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/Frictions/PlotStricklers.py b/src/View/Frictions/PlotStricklers.py index 8f706782..71ff6e6f 100644 --- a/src/View/Frictions/PlotStricklers.py +++ b/src/View/Frictions/PlotStricklers.py @@ -1,3 +1,19 @@ +# PlotStricklers.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 . + # -*- coding: utf-8 -*- from tools import timer, flatten diff --git a/src/View/Frictions/Table.py b/src/View/Frictions/Table.py index 2c96ef97..ef9dc32f 100644 --- a/src/View/Frictions/Table.py +++ b/src/View/Frictions/Table.py @@ -1,5 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback + from tools import trace, timer from PyQt5.QtCore import ( @@ -24,6 +43,8 @@ from View.Frictions.UndoCommand import ( from View.Frictions.translate import * +logger = logging.getLogger() + _translate = QCoreApplication.translate class ComboBoxDelegate(QItemDelegate): @@ -132,36 +153,40 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - if self._headers[column] == "name": - self._undo.push( - SetNameCommand( - self._frictions, row, value + try: + if self._headers[column] == "name": + self._undo.push( + SetNameCommand( + self._frictions, row, value + ) ) - ) - elif self._headers[column] == "begin_kp": - self._undo.push( - SetBeginCommand( - self._frictions, row, value + elif self._headers[column] == "begin_kp": + self._undo.push( + SetBeginCommand( + self._frictions, row, value + ) ) - ) - elif self._headers[column] == "end_kp": - self._undo.push( - SetEndCommand( - self._frictions, row, value + elif self._headers[column] == "end_kp": + self._undo.push( + SetEndCommand( + self._frictions, row, value + ) ) - ) - elif self._headers[column] == "begin_strickler": - self._undo.push( - SetBeginStricklerCommand( - self._frictions, row, self._study.river.strickler(value) + elif self._headers[column] == "begin_strickler": + self._undo.push( + SetBeginStricklerCommand( + self._frictions, row, self._study.river.strickler(value) + ) ) - ) - elif self._headers[column] == "end_strickler": - self._undo.push( - SetEndStricklerCommand( - self._frictions, row, self._study.river.strickler(value) + elif self._headers[column] == "end_strickler": + self._undo.push( + SetEndStricklerCommand( + self._frictions, row, self._study.river.strickler(value) + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/Frictions/UndoCommand.py b/src/View/Frictions/UndoCommand.py index 0d685ace..ef902f07 100644 --- a/src/View/Frictions/UndoCommand.py +++ b/src/View/Frictions/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -17,7 +33,7 @@ class SetNameCommand(QUndoCommand): self._frictions = frictions self._index = index self._old = self._frictions.get(self._index).name - self._new = new_value + self._new = str(new_value) def undo(self): self._frictions.get(self._index).name = self._old @@ -32,7 +48,7 @@ class SetBeginCommand(QUndoCommand): self._frictions = frictions self._index = index self._old = self._frictions.get(self._index).begin_kp - self._new = new_value + self._new = float(new_value) def undo(self): self._frictions.get(self._index).begin_kp = float(self._old) @@ -47,7 +63,7 @@ class SetEndCommand(QUndoCommand): self._frictions = frictions self._index = index self._old = self._frictions.get(self._index).end_kp - self._new = new_value + self._new = float(new_value) def undo(self): self._frictions.get(self._index).end_kp = float(self._old) diff --git a/src/View/Frictions/Window.py b/src/View/Frictions/Window.py index 18b39e3c..4af3b66c 100644 --- a/src/View/Frictions/Window.py +++ b/src/View/Frictions/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging @@ -52,7 +68,7 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow): self.setup_title(title) super(FrictionsWindow, self).__init__( - name=self._title, ui="Frictions", parent=parent + name=title, ui="Frictions", parent=parent ) self.setup_sc() @@ -230,9 +246,17 @@ class FrictionsWindow(ASubMainWindow, ListedSubWindow): self._table.redo() def edit_stricklers(self): - self.strick = StricklersWindow( - study = self._study, - config = self.parent.conf, - parent = self + strick = self.sub_win_filter_first( + "Stricklers", + contain = [] ) - self.strick.show() + + if strick is None: + strick = StricklersWindow( + study = self._study, + config = self.parent.conf, + parent = self + ) + strick.show() + else: + strick.activateWindow() diff --git a/src/View/Frictions/translate.py b/src/View/Frictions/translate.py index c404f59c..21981ab1 100644 --- a/src/View/Frictions/translate.py +++ b/src/View/Frictions/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/Geometry/PlotAC.py b/src/View/Geometry/PlotAC.py index 9709d3bc..fe6d933f 100644 --- a/src/View/Geometry/PlotAC.py +++ b/src/View/Geometry/PlotAC.py @@ -1,3 +1,19 @@ +# PlotAC.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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Geometry/PlotKPZ.py b/src/View/Geometry/PlotKPZ.py index b37f988f..eb7d5e28 100644 --- a/src/View/Geometry/PlotKPZ.py +++ b/src/View/Geometry/PlotKPZ.py @@ -1,3 +1,19 @@ +# PlotKPC.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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Geometry/PlotXY.py b/src/View/Geometry/PlotXY.py index 36de09cc..54e056a9 100644 --- a/src/View/Geometry/PlotXY.py +++ b/src/View/Geometry/PlotXY.py @@ -1,3 +1,19 @@ +# PlotXY.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 . + # -*- coding: utf-8 -*- from tools import timer, trace diff --git a/src/View/Geometry/Profile/Plot.py b/src/View/Geometry/Profile/Plot.py index 2dc7f522..076c2703 100644 --- a/src/View/Geometry/Profile/Plot.py +++ b/src/View/Geometry/Profile/Plot.py @@ -1,3 +1,19 @@ +# Plot.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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Geometry/Profile/Table.py b/src/View/Geometry/Profile/Table.py index d55ebfcd..2de665aa 100644 --- a/src/View/Geometry/Profile/Table.py +++ b/src/View/Geometry/Profile/Table.py @@ -1,6 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- import numpy as np +import logging +import traceback from tools import timer, trace @@ -20,6 +38,8 @@ from Model.Geometry.ProfileXYZ import ProfileXYZ from View.Geometry.Profile.UndoCommand import * +logger = logging.getLogger() + _translate = QCoreApplication.translate @@ -132,38 +152,42 @@ class TableEditableModel(QAbstractTableModel): column = index.column() if role == Qt.EditRole: - if column == 0: - self._undo_stack.push( - SetXCommand( - self._profile, row, - self._profile.point(row).x, - value + try: + if column == 0: + self._undo_stack.push( + SetXCommand( + self._profile, row, + self._profile.point(row).x, + value + ) ) - ) - elif column == 1: - self._undo_stack.push( - SetYCommand( - self._profile, row, - self._profile.point(row).y, - value + elif column == 1: + self._undo_stack.push( + SetYCommand( + self._profile, row, + self._profile.point(row).y, + value + ) ) - ) - elif column == 2: - self._undo_stack.push( - SetZCommand( - self._profile, row, - self._profile.point(row).z, - value + elif column == 2: + self._undo_stack.push( + SetZCommand( + self._profile, row, + self._profile.point(row).z, + value + ) ) - ) - elif column == 3: - self._undo_stack.push( - SetNameCommand( - self._profile, row, - self._profile.point(row).name, - value + elif column == 3: + self._undo_stack.push( + SetNameCommand( + self._profile, row, + self._profile.point(row).name, + value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/Geometry/Profile/UndoCommand.py b/src/View/Geometry/Profile/UndoCommand.py index d0d45b99..7f0cd138 100644 --- a/src/View/Geometry/Profile/UndoCommand.py +++ b/src/View/Geometry/Profile/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from tools import trace, timer @@ -16,9 +32,13 @@ class SetDataCommand(QUndoCommand): self._profile = profile self._index = index self._old = old_value - self._new = new_value + self._new = self.type(new_value) class SetXCommand(SetDataCommand): + def __init__(self, reach, index, old_value, new_value): + self.type = float + super(SetXCommand, self).__init__(reach, index, old_value, new_value) + def undo(self): self._profile.point(self._index).x = self._old @@ -26,6 +46,10 @@ class SetXCommand(SetDataCommand): self._profile.point(self._index).x = self._new class SetYCommand(SetDataCommand): + def __init__(self, reach, index, old_value, new_value): + self.type = float + super(SetYCommand, self).__init__(reach, index, old_value, new_value) + def undo(self): self._profile.point(self._index).y = self._old @@ -33,6 +57,10 @@ class SetYCommand(SetDataCommand): self._profile.point(self._index).y = self._new class SetZCommand(SetDataCommand): + def __init__(self, reach, index, old_value, new_value): + self.type = float + super(SetZCommand, self).__init__(reach, index, old_value, new_value) + def undo(self): self._profile.point(self._index).z = self._old @@ -40,6 +68,10 @@ class SetZCommand(SetDataCommand): self._profile.point(self._index).z = self._new class SetNameCommand(SetDataCommand): + def __init__(self, reach, index, old_value, new_value): + self.type = str + super(SetNameCommand, self).__init__(reach, index, old_value, new_value) + def undo(self): self._profile.point(self._index).name = self._old diff --git a/src/View/Geometry/Profile/Window.py b/src/View/Geometry/Profile/Window.py index 815cc2ec..54d1e011 100644 --- a/src/View/Geometry/Profile/Window.py +++ b/src/View/Geometry/Profile/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import copy @@ -21,7 +37,7 @@ from PyQt5.QtWidgets import ( from Model.Geometry.Reach import Reach from Model.Geometry.ProfileXYZ import ProfileXYZ -from View.ASubWindow import WindowToolKit +from View.ASubWindow import ASubMainWindow from View.Geometry.Profile.mainwindow_ui_profile import Ui_MainWindow from View.Geometry.Profile.Plot import Plot from View.Geometry.Profile.Table import * @@ -29,10 +45,14 @@ from View.Geometry.Profile.Table import * _translate = QCoreApplication.translate -class ProfileWindow(QMainWindow, WindowToolKit): - def __init__(self, profile=None, parent=None): +class ProfileWindow(ASubMainWindow): + def __init__(self, profile=None, title="Profile", parent=None): + self._title = title self.parent = parent - super(ProfileWindow, self).__init__(self.parent) + super(ProfileWindow, self).__init__( + name=self._title, + parent=self.parent + ) self.ui = Ui_MainWindow() self.ui.setupUi(self) @@ -66,12 +86,14 @@ class ProfileWindow(QMainWindow, WindowToolKit): if (name is None) or (name == ""): name = _translate("MainWindowProfile", "(no name)") - self.setWindowTitle( + self._title = ( header + " - " + f"{self._profile.reach.name}" + " - " + f"{name} ({self._profile.kp})" ) + self.setWindowTitle(self._title) + def setup_sc(self): self._undo_stack = QUndoStack() diff --git a/src/View/Geometry/Profile/mainwindow_ui_profile.py b/src/View/Geometry/Profile/mainwindow_ui_profile.py index 8de74df0..0b9443f8 100644 --- a/src/View/Geometry/Profile/mainwindow_ui_profile.py +++ b/src/View/Geometry/Profile/mainwindow_ui_profile.py @@ -1,3 +1,19 @@ +# mainwindow_ui_profile.py -- Pamhyr +# Copyright (C) 2023 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + # -*- coding: utf-8 -*- import os.path diff --git a/src/View/Geometry/Table.py b/src/View/Geometry/Table.py index 8cc29dc8..1b055209 100644 --- a/src/View/Geometry/Table.py +++ b/src/View/Geometry/Table.py @@ -1,6 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- import time +import logging +import traceback from tools import timer, trace @@ -21,6 +39,7 @@ from Model.Geometry import Reach from Model.Geometry.ProfileXYZ import ProfileXYZ from View.Geometry.UndoCommand import * +logger = logging.getLogger() _translate = QCoreApplication.translate @@ -98,23 +117,27 @@ class TableEditableModel(QAbstractTableModel): column = index.column() if role == Qt.EditRole and index.column() != 2: - if index.column() == 0: - self._undo_stack.push( - SetNameCommand( - self._reach, index.row(), - self._reach.profile(index.row()).name, - value + try: + if index.column() == 0: + self._undo_stack.push( + SetNameCommand( + self._reach, index.row(), + self._reach.profile(index.row()).name, + value + ) ) - ) - if index.column() == 1: - self._undo_stack.push( - SetKPCommand( - self._reach, index.row(), - self._reach.profile(index.row()).kp, + if index.column() == 1: + self._undo_stack.push( + SetKPCommand( + self._reach, index.row(), + self._reach.profile(index.row()).kp, value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) self.layoutChanged.emit() diff --git a/src/View/Geometry/UndoCommand.py b/src/View/Geometry/UndoCommand.py index ed911c66..1df9c58c 100644 --- a/src/View/Geometry/UndoCommand.py +++ b/src/View/Geometry/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -17,9 +33,13 @@ class SetDataCommand(QUndoCommand): self._reach = reach self._index = index self._old = old_value - self._new = new_value + self._new = self.type(new_value) class SetNameCommand(SetDataCommand): + def __init__(self, reach, index, old_value, new_value): + self.type = str + super(SetNameCommand, self).__init__(reach, index, old_value, new_value) + def undo(self): self._reach.profile(self._index).name = self._old @@ -27,6 +47,10 @@ class SetNameCommand(SetDataCommand): self._reach.profile(self._index).name = self._new class SetKPCommand(SetDataCommand): + def __init__(self, reach, index, old_value, new_value): + self.type = float + super(SetKPCommand, self).__init__(reach, index, old_value, new_value) + def undo(self): self._reach.profile(self._index).kp = self._old diff --git a/src/View/Geometry/Window.py b/src/View/Geometry/Window.py index 835db523..7776d737 100644 --- a/src/View/Geometry/Window.py +++ b/src/View/Geometry/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import os @@ -25,7 +41,8 @@ from View.Geometry.PlotXY import PlotXY from View.Geometry.PlotKPZ import PlotKPZ from View.Geometry.PlotAC import PlotAC -from View.ASubWindow import WindowToolKit +from View.ASubWindow import ASubMainWindow, WindowToolKit +from View.ListedSubWindow import ListedSubWindow from View.Geometry.mainwindow_ui_reach import Ui_MainWindow from View.Geometry.Table import * from View.Geometry.Profile.Window import ProfileWindow @@ -33,10 +50,14 @@ from View.Geometry.Profile.Window import ProfileWindow _translate = QCoreApplication.translate -class GeometryWindow(QMainWindow, WindowToolKit): - def __init__(self, model=None, parent=None): +class GeometryWindow(ASubMainWindow, ListedSubWindow): + def __init__(self, model=None, title="Geometry", parent=None): + self._title = title self.parent = parent - super(GeometryWindow, self).__init__(parent=parent) + super(GeometryWindow, self).__init__( + name=self._title, + parent=parent + ) self._model = model self._reach = model.river.current_reach().reach @@ -59,7 +80,8 @@ class GeometryWindow(QMainWindow, WindowToolKit): self.changed_slider_value() def setup_window(self): - self.setWindowTitle(f"{self.ui.mainwindow_title} - {self._reach.name}") + self._title = f"{self.ui.mainwindow_title} - {self._reach.name}" + self.setWindowTitle(self._title) def setup_sc(self): self._undo_stack = QUndoStack() @@ -162,13 +184,20 @@ class GeometryWindow(QMainWindow, WindowToolKit): for row in rows: profile = self._reach.profile(row) - win = ProfileWindow( - profile = profile, - parent = self, + win = self.sub_win_filter_first( + "Profile", + contain = [self._reach.name, str(profile.kp)] ) - self._profile_window.append(win) - win.show() + if win is None: + win = ProfileWindow( + profile = profile, + parent = self, + ) + self._profile_window.append(win) + win.show() + else: + win.activateWindow() self.tableView.model().blockSignals(False) diff --git a/src/View/Geometry/mainwindow_ui_reach.py b/src/View/Geometry/mainwindow_ui_reach.py index f04ee9e8..747b6ca4 100644 --- a/src/View/Geometry/mainwindow_ui_reach.py +++ b/src/View/Geometry/mainwindow_ui_reach.py @@ -1,3 +1,19 @@ +# mainwindow_ui_reach.py -- Pamhyr +# Copyright (C) 2023 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + # -*- coding: utf-8 -*- import sys diff --git a/src/View/InitialConditions/DialogDischarge.py b/src/View/InitialConditions/DialogDischarge.py index 8d01ef0e..f8e7e32a 100644 --- a/src/View/InitialConditions/DialogDischarge.py +++ b/src/View/InitialConditions/DialogDischarge.py @@ -1,3 +1,19 @@ +# DialogDischarge.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 . + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/InitialConditions/DialogHeight.py b/src/View/InitialConditions/DialogHeight.py index 91ceadb1..18ad67c8 100644 --- a/src/View/InitialConditions/DialogHeight.py +++ b/src/View/InitialConditions/DialogHeight.py @@ -1,3 +1,19 @@ +# DialogHeight.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 . + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/InitialConditions/PlotDKP.py b/src/View/InitialConditions/PlotDKP.py index 247605b3..58a83219 100644 --- a/src/View/InitialConditions/PlotDKP.py +++ b/src/View/InitialConditions/PlotDKP.py @@ -1,3 +1,19 @@ +# PlotDKP.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 . + # -*- coding: utf-8 -*- from tools import timer @@ -26,7 +42,7 @@ class PlotDKP(APlot): return self.canvas.axes.set_ylabel( - _translate("MainWindow_reach", "Draft (m)"), + _translate("MainWindow_reach", "Elevation (m)"), color='green', fontsize=11 ) self.canvas.axes.set_xlabel( diff --git a/src/View/InitialConditions/PlotDischarge.py b/src/View/InitialConditions/PlotDischarge.py index 07664683..1fe05def 100644 --- a/src/View/InitialConditions/PlotDischarge.py +++ b/src/View/InitialConditions/PlotDischarge.py @@ -1,3 +1,19 @@ +# PlotDischarge.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 . + # -*- coding: utf-8 -*- from tools import timer diff --git a/src/View/InitialConditions/Table.py b/src/View/InitialConditions/Table.py index 13eb5218..5e704ba9 100644 --- a/src/View/InitialConditions/Table.py +++ b/src/View/InitialConditions/Table.py @@ -1,5 +1,23 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback from tools import trace, timer from PyQt5.QtCore import ( @@ -23,6 +41,8 @@ from View.InitialConditions.UndoCommand import ( from View.InitialConditions.translate import * +logger = logging.getLogger() + _translate = QCoreApplication.translate class ComboBoxDelegate(QItemDelegate): @@ -119,12 +139,16 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - if self._headers[column] is not None: - self._undo.push( - SetCommand( - self._ics, row, self._headers[column], value + try: + if self._headers[column] is not None: + self._undo.push( + SetCommand( + self._ics, row, self._headers[column], value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/InitialConditions/UndoCommand.py b/src/View/InitialConditions/UndoCommand.py index 215c299b..c96f90fa 100644 --- a/src/View/InitialConditions/UndoCommand.py +++ b/src/View/InitialConditions/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -18,7 +34,12 @@ class SetCommand(QUndoCommand): self._row = row self._column = column self._old = self._ics.get(self._row)[column] - self._new = new_value + + _type = float + if column == "name" or column == "comment": + _type = str + + self._new = _type(new_value) def undo(self): self._ics.get(self._row)[self._column] = self._old diff --git a/src/View/InitialConditions/Window.py b/src/View/InitialConditions/Window.py index 35822811..95f6004a 100644 --- a/src/View/InitialConditions/Window.py +++ b/src/View/InitialConditions/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging diff --git a/src/View/InitialConditions/translate.py b/src/View/InitialConditions/translate.py index 43890acb..370d4a22 100644 --- a/src/View/InitialConditions/translate.py +++ b/src/View/InitialConditions/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/LateralContribution/Edit/Plot.py b/src/View/LateralContribution/Edit/Plot.py index d7c95ec0..5843ee56 100644 --- a/src/View/LateralContribution/Edit/Plot.py +++ b/src/View/LateralContribution/Edit/Plot.py @@ -1,3 +1,19 @@ +# Plot.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 . + # -*- coding: utf-8 -*- from datetime import datetime @@ -25,6 +41,9 @@ class Plot(APlot): self._mode = mode def custom_ticks(self): + if self.data.header[0] != "time": + return + t0 = datetime.fromtimestamp(0) nb = len(self.data.data) mod = int(nb / 5) diff --git a/src/View/LateralContribution/Edit/Table.py b/src/View/LateralContribution/Edit/Table.py index 4101995c..e9e878fd 100644 --- a/src/View/LateralContribution/Edit/Table.py +++ b/src/View/LateralContribution/Edit/Table.py @@ -1,6 +1,23 @@ +# Table.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 . + # -*- coding: utf-8 -*- import logging +import traceback from datetime import date, time, datetime, timedelta from tools import trace, timer @@ -194,11 +211,15 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - self._undo.push( - SetDataCommand( - self._data, row, column, value + try: + self._undo.push( + SetDataCommand( + self._data, row, column, value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/LateralContribution/Edit/UndoCommand.py b/src/View/LateralContribution/Edit/UndoCommand.py index b5963b76..715683c9 100644 --- a/src/View/LateralContribution/Edit/UndoCommand.py +++ b/src/View/LateralContribution/Edit/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -17,7 +33,8 @@ class SetDataCommand(QUndoCommand): self._index = index self._column = column self._old = self._data.get_i(self._index)[self._column] - self._new = new_value + _type = self._data.get_type_column(self._column) + self._new = _type(new_value) def undo(self): self._data._set_i_c_v(self._index, self._column, self._old) diff --git a/src/View/LateralContribution/Edit/Window.py b/src/View/LateralContribution/Edit/Window.py index e9927710..3a801591 100644 --- a/src/View/LateralContribution/Edit/Window.py +++ b/src/View/LateralContribution/Edit/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from tools import timer, trace @@ -29,7 +45,7 @@ from View.LateralContribution.Edit.Plot import Plot _translate = QCoreApplication.translate class EditLateralContributionWindow(ASubMainWindow, ListedSubWindow): - def __init__(self, title="Edit lateral conditribution", + def __init__(self, title="Edit lateral contribution", data=None, study=None, parent=None): self._data = data self._study = study @@ -38,7 +54,7 @@ class EditLateralContributionWindow(ASubMainWindow, ListedSubWindow): self.compute_title() super(EditLateralContributionWindow, self).__init__( - name=self._title, ui="EditLateralContribution", parent=parent + name=title, ui="EditLateralContribution", parent=parent ) self.ui.setWindowTitle(self._title) @@ -53,9 +69,9 @@ class EditLateralContributionWindow(ASubMainWindow, ListedSubWindow): edge_name = (self._data.edge.name if self._data.edge is not None else _translate("LateralContribution", "Not associate")) self._title = ( - _translate("Edit Lateral contribution", self._title) + + _translate("Edit lateral contribution", self._title) + f" - {self._study.name} " + - f" - {self._data.name} " + + f" - {self._data.name} ({self._data.id}) " + f"({long_types[self._data.lctype]} - {edge_name})" ) diff --git a/src/View/LateralContribution/Edit/translate.py b/src/View/LateralContribution/Edit/translate.py index e44147e2..243d9ad8 100644 --- a/src/View/LateralContribution/Edit/translate.py +++ b/src/View/LateralContribution/Edit/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/LateralContribution/Table.py b/src/View/LateralContribution/Table.py index 307fc56e..682bf00d 100644 --- a/src/View/LateralContribution/Table.py +++ b/src/View/LateralContribution/Table.py @@ -1,5 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback + from tools import trace, timer from PyQt5.QtCore import ( @@ -27,6 +46,8 @@ from Model.LateralContribution.LateralContributionTypes import ( ) from View.LateralContribution.translate import * +logger = logging.getLogger() + _translate = QCoreApplication.translate class ComboBoxDelegate(QItemDelegate): @@ -140,37 +161,41 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - if self._headers[column] == "name": - self._undo.push( - SetNameCommand( - self._lcs, self._tab, row, value + try: + if self._headers[column] == "name": + self._undo.push( + SetNameCommand( + self._lcs, self._tab, row, value + ) ) - ) - elif self._headers[column] == "type": - key = next(k for k, v in long_types.items() if v == value) - self._undo.push( - SetTypeCommand( - self._lcs, self._tab, row, LC_types[key] + elif self._headers[column] == "type": + key = next(k for k, v in long_types.items() if v == value) + self._undo.push( + SetTypeCommand( + self._lcs, self._tab, row, LC_types[key] + ) ) - ) - elif self._headers[column] == "edge": - self._undo.push( - SetEdgeCommand( - self._lcs, self._tab, row, self._data.edge(value) + elif self._headers[column] == "edge": + self._undo.push( + SetEdgeCommand( + self._lcs, self._tab, row, self._data.edge(value) + ) ) - ) - elif self._headers[column] == "begin_kp": - self._undo.push( - SetBeginCommand( - self._lcs, self._tab, row, value + elif self._headers[column] == "begin_kp": + self._undo.push( + SetBeginCommand( + self._lcs, self._tab, row, value + ) ) - ) - elif self._headers[column] == "end_kp": - self._undo.push( - SetEndCommand( - self._lcs, self._tab, row, value + elif self._headers[column] == "end_kp": + self._undo.push( + SetEndCommand( + self._lcs, self._tab, row, value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/LateralContribution/UndoCommand.py b/src/View/LateralContribution/UndoCommand.py index f7e680b3..3d858436 100644 --- a/src/View/LateralContribution/UndoCommand.py +++ b/src/View/LateralContribution/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -18,7 +34,7 @@ class SetNameCommand(QUndoCommand): self._tab = tab self._index = index self._old = self._lcs.get(self._tab, self._index).name - self._new = new_value + self._new = str(new_value) def undo(self): self._lcs.get(self._tab, self._index).name = self._old @@ -34,7 +50,7 @@ class SetBeginCommand(QUndoCommand): self._tab = tab self._index = index self._old = self._lcs.get(self._tab, self._index).begin_kp - self._new = new_value + self._new = float(new_value) def undo(self): self._lcs.get(self._tab, self._index).begin_kp = float(self._old) @@ -50,7 +66,7 @@ class SetEndCommand(QUndoCommand): self._tab = tab self._index = index self._old = self._lcs.get(self._tab, self._index).end_kp - self._new = new_value + self._new = float(new_value) def undo(self): self._lcs.get(self._tab, self._index).end_kp = float(self._old) diff --git a/src/View/LateralContribution/Window.py b/src/View/LateralContribution/Window.py index b87e9968..dc3dd94f 100644 --- a/src/View/LateralContribution/Window.py +++ b/src/View/LateralContribution/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging @@ -49,7 +65,7 @@ logger = logging.getLogger() class LateralContributionWindow(ASubMainWindow, ListedSubWindow): def __init__(self, title="Lateral contribution", study=None, parent=None): - title = title + " - " + study.name + self._title = title + " - " + study.name super(LateralContributionWindow, self).__init__( name=title, ui="LateralContributions", parent=parent @@ -63,7 +79,7 @@ class LateralContributionWindow(ASubMainWindow, ListedSubWindow): self.setup_graph() self.setup_connections() - self.ui.setWindowTitle(title) + self.ui.setWindowTitle(self._title) def setup_sc(self): self._undo_stack = QUndoStack() @@ -250,9 +266,17 @@ class LateralContributionWindow(ASubMainWindow, ListedSubWindow): tab = self.current_tab() rows = self.index_selected_rows() for row in rows: - win = EditLateralContributionWindow( - data=self._lcs.get(tab, row), - study=self._study, - parent=self + win = self.sub_win_filter_first( + "Edit lateral contribution", + contain = [f"({self._lcs.get(tab, row).id})"] ) - win.show() + + if win is None: + win = EditLateralContributionWindow( + data=self._lcs.get(tab, row), + study=self._study, + parent=self + ) + win.show() + else: + win.activateWindow() diff --git a/src/View/LateralContribution/translate.py b/src/View/LateralContribution/translate.py index c2b9e789..e9c6deeb 100644 --- a/src/View/LateralContribution/translate.py +++ b/src/View/LateralContribution/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/ListedSubWindow.py b/src/View/ListedSubWindow.py index c88fd7b4..b4dfe416 100644 --- a/src/View/ListedSubWindow.py +++ b/src/View/ListedSubWindow.py @@ -1,3 +1,19 @@ +# ListedSubWindow.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 . + # -*- coding: utf-8 -*- import logging @@ -32,9 +48,50 @@ class ListedSubWindow(object): self.sub_win_cnt = len(self.sub_win_list) logger.info(f"Close window: {name} ({self.sub_win_cnt})") - def sub_win_exists(self, name): + def _sub_win_exists(self, name): return reduce( lambda acc, n: (acc or (n[0] == name)), self.sub_win_list, False ) + + def _sub_win_exists_with_contain(self, name, contain): + return reduce( + lambda acc, n: ( + acc or + ( + (n[0] == name) and + reduce( + lambda acc, c: acc and (c in n[1]._title), + contain, + True + ) + ) + ), + self.sub_win_list, + False + ) + + def sub_win_exists(self, name, contain = []): + if contain == []: + return self._sub_win_exists(name) + else: + return self._sub_win_exists_with_contain(name, contain) + + def sub_win_filter_first(self, name, contain): + try: + return next( + filter( + lambda n: ( + (name in n[0]) and + reduce( + lambda acc, c: acc and (c in n[1]._title), + contain, + True + ) + ), + self.sub_win_list, + ) + )[1] + except: + return None diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py index 71301e1b..2fc14efa 100644 --- a/src/View/MainWindow.py +++ b/src/View/MainWindow.py @@ -1,3 +1,19 @@ +# MainWindow.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 . + # -*- coding: utf-8 -*- import os @@ -39,6 +55,7 @@ from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow from View.SolverParameters.Window import SolverParametersWindow from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow from View.CheckList.Window import CheckListWindow +from View.Results.Window import ResultsWindow from View.Debug.Window import ReplWindow from Model.Study import Study @@ -93,6 +110,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): # Model self.model = None + # Results + self._last_results = None + # UI self.ui = loadUi( os.path.join(os.path.dirname(__file__), "ui", "MainWindow.ui"), @@ -107,6 +127,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): self.trans = QTranslator(self) #self.ui.retranslateUi() + if self.conf.last_study != "" and not self.conf.close_correctly: + self.dialog_reopen_study() + def set_title(self): if self.model is not None: self.setWindowTitle(f"PAMHYR - {self.model.name}") @@ -192,16 +215,24 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): if self.model is not None and not self.model.is_saved: self._close_question = True if self.dialog_close(): + # PAMHYR is close correctly (no crash) + self.conf.set_close_correctly() + super(ApplicationWindow, self).close() else: self._close_question = False else: + # PAMHYR is close correctly (no crash) + self.conf.set_close_correctly() + super(ApplicationWindow, self).close() def closeEvent(self, event): if not self._close_question: if self.model is not None and not self.model.is_saved: if self.dialog_close(cancel = False): + # PAMHYR is close correctly (no crash) + self.conf.set_close_correctly() super(ApplicationWindow, self).closeEvent(event) else: super(ApplicationWindow, self).closeEvent(event) @@ -261,11 +292,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): def set_model(self, model): self.model = model self.update_enable_action() + self.conf.set_last_study(self.model.filename) self.set_title() def close_model(self): self.model = None self.update_enable_action() + self.conf.set_close_correctly() self.set_title() def update_enable_action(self): @@ -288,10 +321,26 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): for action in model_action: self.enable_actions(action, not no_model) + def set_results(self, results): + self._last_results = results + ############ # FEATURES # ############ + def open_study(self, filename): + """Open a study + + Args: + filename: The study path + + Returns: + Nothing + """ + self.set_model(Study.open(filename)) + logger.info(f"Open Study - {self.model.name}") + self.set_title() + def save_study(self): """Save current study @@ -348,6 +397,27 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): "Geometry edition need a reach selected " "into river network window to work on it") + def dialog_reopen_study(self): + dlg = QMessageBox(self) + + dlg.setWindowTitle("Last open study") + dlg.setText("Do you want to open again the last open study?") + opt = QMessageBox.Cancel | QMessageBox.Ok #| QMessageBox.Open + + dlg.setStandardButtons(opt) + dlg.setIcon(QMessageBox.Question) + + res = dlg.exec() + + if res == QMessageBox.Ok: + self.open_study(self.conf.last_study) + return True + elif res == QMessageBox.Open: + self.open_model() + return True + elif res == QMessageBox.Cancel: + return False + def dialog_close(self, cancel = True): dlg = QMessageBox(self) @@ -408,12 +478,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): dialog.setDefaultSuffix(".pamhyr") #dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden) dialog.setNameFilters(['PamHyr (*.pamhyr)']) + dialog.setDirectory(os.path.dirname(self.conf.last_study)) if dialog.exec_(): file_name = dialog.selectedFiles() - self.set_model(Study.open(file_name[0])) - logger.info(f"Open Study - {self.model.name}") - self.set_title() + self.open_study(file_name[0]) def open_new_study(self): """Open dialog to set new study @@ -441,10 +510,12 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): Returns: Nothing """ - if (self.model is not None and - not self.sub_win_exists("River network")): - self.network = NetworkWindow(model=self.model, parent=self) - self.network.show() + if self.model is not None: + if not self.sub_win_exists("River network"): + self.network = NetworkWindow(model=self.model, parent=self) + self.network.show() + else: + self.network.activateWindow() def open_geometry(self): """Open geometry window @@ -452,56 +523,112 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): Returns: Nothing """ - if (self.model is not None and - self.model.river.has_current_reach()): - geometry = GeometryWindow(model=self.model, parent=self) - geometry.show() + if (self.model is not None and self.model.river.has_current_reach()): + geometry = self.sub_win_filter_first( + "Geometry", + contain = [self.model.river.current_reach().name] + ) + + if geometry is None: + geometry = GeometryWindow(model=self.model, parent=self) + geometry.show() + else: + geometry.activateWindow() else: self.msg_select_reach() def open_boundary_cond(self): - bound = BoundaryConditionWindow(study = self.model, parent = self) - bound.show() + bound = self.sub_win_filter_first( + "Boundary conditions", + contain = [] + ) + + if bound is None: + bound = BoundaryConditionWindow(study = self.model, parent = self) + bound.show() + else: + bound.activateWindow() def open_lateral_contrib(self): - lateral = LateralContributionWindow(study = self.model, parent = self) - lateral.show() + lateral = self.sub_win_filter_first( + "Lateral contribution", + contain = [] + ) + + if lateral is None: + lateral = LateralContributionWindow(study = self.model, parent = self) + lateral.show() + else: + lateral.activateWindow() def open_stricklers(self): - strick = StricklersWindow( - study = self.model, - config = self.conf, - parent = self + strick = self.sub_win_filter_first( + "Stricklers", + contain = [] ) - strick.show() + + if strick is None: + strick = StricklersWindow( + study = self.model, + config = self.conf, + parent = self + ) + strick.show() + else: + strick.activateWindow() def open_frictions(self): if (self.model is not None and self.model.river.has_current_reach()): - frictions = FrictionsWindow( - study = self.model, - parent = self + + frictions = self.sub_win_filter_first( + "Frictions", + contain = [self.model.river.current_reach().name] ) - frictions.show() + + if frictions is None: + frictions = FrictionsWindow( + study = self.model, + parent = self + ) + frictions.show() + else: + frictions.activateWindow() else: self.msg_select_reach() def open_initial_conditions(self): if self.model.river.has_current_reach(): - initial = InitialConditionsWindow( - study = self.model, - parent = self + initial = self.sub_win_filter_first( + "Initial condition", + contain = [self.model.river.current_reach().name] ) - initial.show() + + if initial is None: + initial = InitialConditionsWindow( + study = self.model, + parent = self + ) + initial.show() + else: + initial.activateWindow() else: self.msg_select_reach() def open_solver_parameters(self): - params = SolverParametersWindow( - study = self.model, - parent = self + params = self.sub_win_filter_first( + "Solver parameters", + contain = [] ) - params.show() + + if params is None: + params = SolverParametersWindow( + study = self.model, + parent = self + ) + params.show() + else: + params.activateWindow() def open_sediment_layers(self): sl = SedimentLayersWindow( @@ -545,6 +672,32 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit): ) sol.show() + def open_solver_results(self, solver, results = None): + # If no specific results, get last results + if results is None: + results = self._last_results + + # No results available + if results is None: + return + + # Windows already opened + res = self.sub_win_filter_first( + "Results", + contain = [solver.name, results.date] + ) + + if res is None: + res = ResultsWindow( + study = self.model, + solver = solver, + results = results, + parent = self + ) + res.show() + else: + res.activateWindow() + ######### # DEBUG # ######### diff --git a/src/View/Network/GraphWidget.py b/src/View/Network/GraphWidget.py index 2569497a..870792b8 100644 --- a/src/View/Network/GraphWidget.py +++ b/src/View/Network/GraphWidget.py @@ -1,6 +1,23 @@ +# GraphWidget.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 . + # -*- coding: utf-8 -*- import math +import logging from tools import timer @@ -23,6 +40,8 @@ from Model.Network.Graph import Graph from View.Network.UndoCommand import * +logger = logging.getLogger() + _translate = QCoreApplication.translate @@ -158,7 +177,7 @@ class EdgeItem(QGraphicsItem): if self.graph.selected_item() == self: color = Qt.red elif self.graph.current_edge() == self: - color = Qt.blue + color = Qt.black elif not self.graph.graph.is_enable_edge(self.edge): color = Qt.darkGray @@ -555,14 +574,17 @@ class GraphWidget(QGraphicsView): Returns: Nothing """ - previous_item = self._selected_item - self._selected_item = item + try: + previous_item = self._selected_item + self._selected_item = item - if previous_item: - previous_item.update() + if previous_item: + previous_item.update() - if item: - item.update() + if item: + item.update() + except Exception as e: + logger.debug(str(e)) def selected_new_edge_src_node(self): """The current node item selected to add new edge @@ -714,6 +736,9 @@ class GraphWidget(QGraphicsView): def mouseReleaseEvent(self, event): self.clicked = False + if self._only_display: + return + if self._state == "move": if self._current_moved_node is not None: pos = self.mapToScene(event.pos()) @@ -819,6 +844,9 @@ class GraphWidget(QGraphicsView): self.reverse_edge(items[0]) def contextMenuEvent(self, event): + if self._only_display: + return + pos = self.mapToScene(event.pos()) items = self.items(event.pos()) diff --git a/src/View/Network/Table.py b/src/View/Network/Table.py index a8254745..b24865e8 100644 --- a/src/View/Network/Table.py +++ b/src/View/Network/Table.py @@ -1,5 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback + from Model.Network.Node import Node from Model.Network.Edge import Edge from Model.Network.Graph import Graph @@ -137,31 +156,35 @@ class GraphTableModel(QAbstractTableModel): def setData(self, index, value, role=Qt.EditRole): if index.isValid(): if role == Qt.EditRole: - if (self.headers[index.column()] == "node1" or - self.headers[index.column()] == "node2"): - node = self.graph.node(value) - self._undo.push( - SetNodeCommand( - self.graph, - self.rows[index.row()], - self.headers[index.column()], - node + try: + if (self.headers[index.column()] == "node1" or + self.headers[index.column()] == "node2"): + node = self.graph.node(value) + self._undo.push( + SetNodeCommand( + self.graph, + self.rows[index.row()], + self.headers[index.column()], + node + ) ) - ) - elif self.headers[index.column()] == "enable": - self._undo.push( - EnableEdgeCommand( - self.rows[index.row()], value + # elif self.headers[index.column()] == "enable": + # self._undo.push( + # EnableEdgeCommand( + # self.rows[index.row()], value + # ) + # ) + else: + self._undo.push( + SetCommand( + self.rows[index.row()], + self.headers[index.column()], + value + ) ) - ) - else: - self._undo.push( - SetCommand( - self.rows[index.row()], - self.headers[index.column()], - value - ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index, [Qt.DisplayRole]) self.layoutChanged.emit() diff --git a/src/View/Network/UndoCommand.py b/src/View/Network/UndoCommand.py index d6d38125..c9cce895 100644 --- a/src/View/Network/UndoCommand.py +++ b/src/View/Network/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy diff --git a/src/View/Network/Window.py b/src/View/Network/Window.py index 8fdf3988..05051862 100644 --- a/src/View/Network/Window.py +++ b/src/View/Network/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtGui import ( @@ -34,7 +50,7 @@ class NetworkWindow(ASubMainWindow): self.setup_title() super(NetworkWindow, self).__init__( - name=self._title, ui="Network", parent=parent + name=title, ui="Network", parent=parent ) self.ui.setWindowTitle(self._title) @@ -71,7 +87,8 @@ class NetworkWindow(ASubMainWindow): # Edges table self._reachs_model = GraphTableModel( - headers = ["name", "enable", "node1", "node2"], + headers = ["name", # "enable", + "node1", "node2"], graph = self._graph, rows_type = "edges", undo = self._undo_stack, @@ -86,9 +103,9 @@ class NetworkWindow(ASubMainWindow): table = self.find(QTableView, "tableView_reachs") table.setModel(self._reachs_model) - table.setItemDelegateForColumn(1, self.delegate_true_false_combobox) + # table.setItemDelegateForColumn(1, self.delegate_true_false_combobox) + table.setItemDelegateForColumn(1, self.delegate_combobox) table.setItemDelegateForColumn(2, self.delegate_combobox) - table.setItemDelegateForColumn(3, self.delegate_combobox) table.setSelectionBehavior(QAbstractItemView.SelectRows) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) #table.resizeColumnsToContents() diff --git a/src/View/Plot/APlot.py b/src/View/Plot/APlot.py index cca27087..cd9988cf 100644 --- a/src/View/Plot/APlot.py +++ b/src/View/Plot/APlot.py @@ -1,3 +1,19 @@ +# APlot.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 . + # -*- coding: utf-8 -*- class APlot(object): diff --git a/src/View/Plot/MplCanvas.py b/src/View/Plot/MplCanvas.py index 5b3fc4a8..ee1f5373 100644 --- a/src/View/Plot/MplCanvas.py +++ b/src/View/Plot/MplCanvas.py @@ -1,3 +1,19 @@ +# MplCanvas.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 . + from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg from matplotlib.figure import Figure diff --git a/src/View/Plot/mpl_canvas_onpick_event.py b/src/View/Plot/mpl_canvas_onpick_event.py index 010959bc..fdfa1de6 100644 --- a/src/View/Plot/mpl_canvas_onpick_event.py +++ b/src/View/Plot/mpl_canvas_onpick_event.py @@ -1,3 +1,19 @@ +# mpl_canvas_onpick_event.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 . + import logging from time import time diff --git a/src/View/Plot/navigation_toolbar_2qt.py b/src/View/Plot/navigation_toolbar_2qt.py index 37186450..1cca7cf0 100644 --- a/src/View/Plot/navigation_toolbar_2qt.py +++ b/src/View/Plot/navigation_toolbar_2qt.py @@ -1,3 +1,19 @@ +# navigation_toolbar_2qt.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 . + # -*- coding: utf-8 -*- import os diff --git a/src/View/Results/PlotAC.py b/src/View/Results/PlotAC.py new file mode 100644 index 00000000..f0e4b0e2 --- /dev/null +++ b/src/View/Results/PlotAC.py @@ -0,0 +1,115 @@ +# PlotAC.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 . + +# -*- coding: utf-8 -*- + +from tools import timer +from View.Plot.APlot import APlot + +from PyQt5.QtCore import ( + QCoreApplication +) + +_translate = QCoreApplication.translate + +class PlotAC(APlot): + def __init__(self, canvas=None, results=None, + reach_id=0, profile_id=0, + toolbar=None): + super(PlotAC, self).__init__( + canvas=canvas, + data=results, + toolbar=toolbar + ) + + self._current_timestamp = max(results.get("timestamps")) + self._current_reach_id = reach_id + self._current_profile_id = profile_id + + @property + def results(self): + return self.data + + @timer + def draw(self, highlight=None): + self.canvas.axes.cla() + self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5) + + if self.results is None: + return + + self.canvas.axes.set_xlabel( + _translate("MainWindow_reach", "X (m)"), + color='green', fontsize=11 + ) + self.canvas.axes.set_ylabel( + _translate("MainWindow_reach", "Elevation (m)"), + color='green', fontsize=11 + ) + + reach = self.results.river.reach(self._current_reach_id) + profile = reach.profile(self._current_profile_id) + x = profile.geometry.get_station() + z = profile.geometry.z() + + self.canvas.axes.set_xlim( + left = min(x), right = max(x) + ) + + self.line_kp, = self.canvas.axes.plot( + x, z, + linestyle="solid", + lw=1.8, + color='grey', + ) + + kp = reach.geometry.get_kp() + + # Water elevation + water_z = profile.get_ts_key(self._current_timestamp, "Z") + + self.canvas.axes.plot( + [min(x), max(x)], [water_z, water_z], + lw=1., color='b', + ) + + x_z = list(map(lambda _: water_z, x)) + self.canvas.axes.fill_between( + x, z, water_z, + where=z <= water_z, + color='blue', alpha=0.5, interpolate=True + ) + + self.canvas.figure.tight_layout() + self.canvas.figure.canvas.draw_idle() + if self.toolbar is not None: + self.toolbar.update() + + def set_reach(self, reach_id): + self._current_reach_id = reach_id + self._current_profile_id = 0 + self.draw() + + def set_profile(self, profile_id): + self._current_profile_id = profile_id + self.draw() + + def set_timestamp(self, timestamp): + self._current_timestamp = timestamp + self.draw() + + def update(self): + self.draw() diff --git a/src/View/Results/PlotH.py b/src/View/Results/PlotH.py new file mode 100644 index 00000000..dcc9f7f6 --- /dev/null +++ b/src/View/Results/PlotH.py @@ -0,0 +1,131 @@ +# PlotH.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 . + +# -*- coding: utf-8 -*- + +import logging + +from functools import reduce + +from tools import timer, trace +from View.Plot.APlot import APlot + +from PyQt5.QtCore import ( + QCoreApplication +) + +_translate = QCoreApplication.translate + +logger = logging.getLogger() + +class PlotH(APlot): + def __init__(self, canvas=None, results=None, + reach_id=0, profile_id=0, + toolbar=None, display_current=True): + super(PlotH, self).__init__( + canvas=canvas, + data=results, + toolbar=toolbar + ) + + self.display_current = display_current + + self._current_timestamp = max(results.get("timestamps")) + self._current_reach_id = reach_id + self._current_profile_id = profile_id + + @property + def results(self): + return self.data + + @timer + def draw(self, highlight=None): + self.canvas.axes.cla() + self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5) + + if self.results is None: + return + + reach = self.results.river.reach(self._current_reach_id) + profile = reach.profile(self._current_profile_id) + + if reach.geometry.number_profiles == 0: + self._init = False + return + + kp_min, kp_max = (-1, -1) + if highlight is not None: + kp_min, kp_max = highlight + + # Axes + self.canvas.axes.set_xlabel( + _translate("Results", "Time (s)"), + color='green', fontsize=12 + ) + self.canvas.axes.set_ylabel( + _translate("Results", "Discharge (m³/s)"), + color='green', fontsize=12 + ) + + ts = list(self.results.get("timestamps")) + ts.sort() + + self.canvas.axes.set_xlim( + left = min(ts), right = max(ts) + ) + + # Draw discharge for each timestamp + x = ts + y = profile.get_key("Q") + + if len(ts) != len(x): + logger.warning( + "Results as less Q data ({len(x)}) " + + "than timestamps ({len(ts)}) " + + "for profile {self._current_profile_id}" + ) + return + + self._line = [ + self.canvas.axes.plot( + x, y, lw=1., + color='r', + markersize=3, marker='+' + ) + ] + + self.canvas.axes.autoscale_view(True, True, True) + self.canvas.axes.autoscale() + self.canvas.figure.tight_layout() + self.canvas.figure.canvas.draw_idle() + if self.toolbar is not None: + self.toolbar.update() + + def set_reach(self, reach_id): + self._current_reach_id = reach_id + self._current_profile_id = 0 + self.draw() + + def set_profile(self, profile_id): + self._current_profile_id = profile_id + self.draw() + + def set_timestamp(self, timestamp): + self._current_timestamp = timestamp + self.draw() + + def update(self): + self.draw() diff --git a/src/View/Results/PlotKPC.py b/src/View/Results/PlotKPC.py new file mode 100644 index 00000000..68f6fb4e --- /dev/null +++ b/src/View/Results/PlotKPC.py @@ -0,0 +1,117 @@ +# PlotKPC.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 . + +# -*- coding: utf-8 -*- + +from tools import timer +from View.Plot.APlot import APlot + +from PyQt5.QtCore import ( + QCoreApplication +) + +_translate = QCoreApplication.translate + +class PlotKPC(APlot): + def __init__(self, canvas=None, results=None, + reach_id=0, profile_id=0, + toolbar=None): + super(PlotKPC, self).__init__( + canvas=canvas, + data=results, + toolbar=toolbar + ) + + self._current_timestamp = max(results.get("timestamps")) + self._current_reach_id = reach_id + self._current_profile_id = profile_id + + @property + def results(self): + return self.data + + @timer + def draw(self, highlight=None): + self.canvas.axes.cla() + self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5) + + if self.results is None: + return + + reach = self.results.river.reach(self._current_reach_id) + + self.canvas.axes.set_ylabel( + _translate("MainWindow_reach", "Elevation (m)"), + color='green', fontsize=11 + ) + self.canvas.axes.set_xlabel( + _translate("MainWindow_reach", "KP (m)"), + color='green', fontsize=11 + ) + + kp = reach.geometry.get_kp() + z_min = reach.geometry.get_z_min() + + self.canvas.axes.set_xlim( + left = min(kp), right = max(kp) + ) + + self.line_kp_zmin = self.canvas.axes.plot( + kp, z_min, + color='grey', lw=1. + ) + + if len(reach.geometry.profiles) != 0: + kp = reach.geometry.get_kp() + + # Water elevation + water_z = list( + map( + lambda p: p.get_ts_key(self._current_timestamp, "Z"), + reach.profiles + ) + ) + + self.canvas.axes.plot( + kp, water_z, lw=1., + color='b', + ) + + self.canvas.axes.fill_between( + kp, z_min, water_z, + color='blue', alpha=0.5, interpolate=True + ) + + self.canvas.figure.tight_layout() + self.canvas.figure.canvas.draw_idle() + if self.toolbar is not None: + self.toolbar.update() + + def set_reach(self, reach_id): + self._current_reach_id = reach_id + self._current_profile_id = 0 + self.draw() + + def set_profile(self, profile_id): + self._current_profile_id = profile_id + self.draw() + + def set_timestamp(self, timestamp): + self._current_timestamp = timestamp + self.draw() + + def update(self): + self.draw() diff --git a/src/View/Results/PlotXY.py b/src/View/Results/PlotXY.py new file mode 100644 index 00000000..3e7a88f3 --- /dev/null +++ b/src/View/Results/PlotXY.py @@ -0,0 +1,186 @@ +# PlotXY.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 . + +# -*- coding: utf-8 -*- + +import logging + +from functools import reduce + +from tools import timer, trace +from View.Plot.APlot import APlot + +from PyQt5.QtCore import ( + QCoreApplication +) + +_translate = QCoreApplication.translate + +logger = logging.getLogger() + +class PlotXY(APlot): + def __init__(self, canvas=None, results=None, + reach_id=0, profile_id=0, + toolbar=None, display_current=True): + super(PlotXY, self).__init__( + canvas=canvas, + data=results, + toolbar=toolbar + ) + + self.display_current = display_current + + self.line_xy = [] + self.line_gl = [] + + self._current_timestamp = max(results.get("timestamps")) + self._current_reach_id = reach_id + self._current_profile_id = profile_id + + @property + def results(self): + return self.data + + @timer + def draw(self, highlight=None): + self.canvas.axes.cla() + self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5) + + if self.results is None: + return + + reach = self.results.river.reach(self._current_reach_id) + + if reach.geometry.number_profiles == 0: + self._init = False + return + + kp_min, kp_max = (-1, -1) + if highlight is not None: + kp_min, kp_max = highlight + + # Axes + self.canvas.axes.set_xlabel( + _translate("Results", "X (m)"), + color='green', fontsize=12 + ) + self.canvas.axes.set_ylabel( + _translate("Results", "Y (m)"), + color='green', fontsize=12 + ) + self.canvas.axes.axis("equal") + + kp = reach.geometry.get_kp() + self.canvas.axes.set_xlim( + left = min(kp), right = max(kp) + ) + + # Draw line for each profile + self.line_xy = [ + self.canvas.axes.plot( + x, y, lw=1., + color='b' if kp_min <= kp <= kp_max else 'grey', + markersize=3, marker='+' + ) + for x, y, kp in zip( + reach.geometry.get_x(), + reach.geometry.get_y(), + kp + ) + ] + + # Guide lines + x_complete = reach.geometry.get_guidelines_x() + y_complete = reach.geometry.get_guidelines_y() + + self.line_gl = [ + self.canvas.axes.plot( + x, y, + ) + for x, y in zip(x_complete, y_complete) + ] + + if self.display_current: + # Current profile + profile = reach.profile(self._current_profile_id).geometry + + self.plot_selected, = self.canvas.axes.plot( + profile.x(), + profile.y(), + lw=1., markersize=3, + marker='+', color="b" + ) + self.plot_selected.set_visible(False) + + # Display point under water + poly_l_x = [] + poly_l_y = [] + poly_r_x = [] + poly_r_y = [] + for profile in reach.profiles: + water_z = profile.get_ts_key( + self._current_timestamp, "Z" + ) + pgeo = profile.geometry + + x, y = reduce( + lambda acc, pts: (acc[0] + [pts.x], acc[1] + [pts.y]), + filter( + lambda pts: pts.z < water_z, + pgeo.points + ), + ([], []) + ) + + poly_l_x.append(x[0]) + poly_l_y.append(y[0]) + poly_r_x.append(x[-1]) + poly_r_y.append(y[-1]) + + self.canvas.axes.plot( + x, y, lw=1., + color='b', + markersize=1, + marker='o' + ) + + poly_x = poly_l_x + list(reversed(poly_r_x)) + [poly_l_x[0]] + poly_y = poly_l_y + list(reversed(poly_r_y)) + [poly_l_y[0]] + + self.canvas.axes.fill(poly_x, poly_y, color='blue', alpha=1) + + self.canvas.axes.autoscale_view(True, True, True) + self.canvas.axes.autoscale() + self.canvas.figure.tight_layout() + self.canvas.figure.canvas.draw_idle() + if self.toolbar is not None: + self.toolbar.update() + + def set_reach(self, reach_id): + self._current_reach_id = reach_id + self._current_profile_id = 0 + self.draw() + + def set_profile(self, profile_id): + self._current_profile_id = profile_id + self.draw() + + def set_timestamp(self, timestamp): + self._current_timestamp = timestamp + self.draw() + + def update(self): + self.draw() diff --git a/src/View/Results/Table.py b/src/View/Results/Table.py new file mode 100644 index 00000000..8e167459 --- /dev/null +++ b/src/View/Results/Table.py @@ -0,0 +1,107 @@ +# Table.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 . + +# -*- coding: utf-8 -*- + +import logging +import traceback + +from tools import timer, trace + +from PyQt5.QtGui import ( + QKeySequence, QColor +) +from PyQt5.QtCore import ( + Qt, QAbstractTableModel, QModelIndex, + QVariant, pyqtSlot, QCoreApplication, +) +from PyQt5.QtWidgets import ( + QMessageBox, QUndoCommand, QUndoStack, + QStyledItemDelegate, QLineEdit, QAbstractItemView, + QComboBox, +) + +from View.Results.translate import * + +logger = logging.getLogger() + +_translate = QCoreApplication.translate + + +class TableModel(QAbstractTableModel): + def __init__(self, results = None, study = None, mode = None, undo=None): + super(QAbstractTableModel, self).__init__() + + self._results = results + self._study = study + self._mode = mode + self._undo_stack = undo + + self._table_headers = table_headers_reach + if mode != "reach": + self._table_headers = table_headers_profile + + self._headers = list(self._table_headers.keys()) + + self._selected = 0 + self._timestamp = 0 + + def rowCount(self, parent=QModelIndex()): + if self._mode == "reach": + return len(self._results.river.reachs) + + current_reach = self._results.river.reach(self._selected) + return len(current_reach.profiles) + + def columnCount(self, parent=QModelIndex()): + return len(self._headers) + + def data(self, index, role=Qt.DisplayRole): + if role != Qt.ItemDataRole.DisplayRole: + return QVariant() + + row = index.row() + column = index.column() + + if self._mode == "reach": + if self._headers[column] == "name": + v = self._results.river.reach(row).name + return str(v) + else: + current_reach = self._results.river.reach(self._selected) + if self._headers[column] == "name": + v = current_reach.profile(row).name + return str(v) + elif self._headers[column] == "kp": + v = current_reach.profile(row).kp + return f"{v:.4f}" + + return QVariant() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal: + return self._table_headers[self._headers[section]] + + return QVariant() + + def index(self, row, column, parent=QModelIndex()): + if not self.hasIndex(row, column, parent): + return QModelIndex() + + return self.createIndex(row, column, QModelIndex()) + + def flags(self, index): + return Qt.ItemIsEnabled | Qt.ItemIsSelectable diff --git a/src/View/Results/UndoCommand.py b/src/View/Results/UndoCommand.py new file mode 100644 index 00000000..ca18efaa --- /dev/null +++ b/src/View/Results/UndoCommand.py @@ -0,0 +1,17 @@ +# UndoCommand.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 . + +# -*- coding: utf-8 -*- diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py new file mode 100644 index 00000000..22d0a2c4 --- /dev/null +++ b/src/View/Results/Window.py @@ -0,0 +1,268 @@ +# 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 . + +# -*- coding: utf-8 -*- + +import logging + +from tools import trace, timer + +from View.ASubWindow import ASubMainWindow +from View.ListedSubWindow import ListedSubWindow + +from PyQt5.QtGui import ( + QKeySequence, +) + +from PyQt5.QtCore import ( + Qt, QVariant, QAbstractTableModel, + QCoreApplication, QModelIndex, pyqtSlot, +) + +from PyQt5.QtWidgets import ( + QDialogButtonBox, QPushButton, QLineEdit, + QFileDialog, QTableView, QAbstractItemView, + QUndoStack, QShortcut, QAction, QItemDelegate, + QComboBox, QVBoxLayout, QHeaderView, QTabWidget, + QSlider, +) + +from View.Plot.MplCanvas import MplCanvas + +from View.Results.PlotXY import PlotXY +from View.Results.PlotAC import PlotAC +from View.Results.PlotKPC import PlotKPC +from View.Results.PlotH import PlotH + +from View.Results.Table import TableModel +from View.Results.translate import * +from View.Stricklers.Window import StricklersWindow + +_translate = QCoreApplication.translate + +logger = logging.getLogger() + +class ResultsWindow(ASubMainWindow, ListedSubWindow): + def __init__(self, title="Results", + study=None, solver=None, results=None, + parent=None): + self._study = study + self._solver = solver + self._results = results + + self._timestamps = sorted(list(self._results.get("timestamps"))) + + self.setup_title(title) + + super(ResultsWindow, self).__init__( + name=title, ui="Results", parent=parent + ) + + self.setup_sc() + self.setup_table() + self.setup_graph() + self.setup_slider() + self.setup_connections() + + self.ui.setWindowTitle(self._title) + + def setup_title(self, title): + self._title = ( + title + " - " + + self._study.name + " - " + + self._solver.name + " - " + + self._results.date + ) + + 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 ["reach", "profile"]: + table = self.find(QTableView, f"tableView_{t}") + self._table[t] = TableModel( + results = self._results, + study = self._study, + mode = t, + undo = self._undo_stack, + ) + table.setModel(self._table[t]) + + table.setSelectionBehavior(QAbstractItemView.SelectRows) + table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + table.setAlternatingRowColors(True) + + def setup_slider(self): + self._slider_profile = self.find(QSlider, f"verticalSlider_profile") + default_reach = self._results.river.reach(0) + self._slider_profile.setMaximum(len(default_reach.profiles) - 1) + self._slider_profile.setValue(0) + + self._slider_time = self.find(QSlider, f"horizontalSlider_time") + self._slider_time.setMaximum(len(self._timestamps) - 1) + self._slider_time.setValue(len(self._timestamps) - 1) + + def setup_graph(self): + self.canvas = MplCanvas(width=5, height=4, dpi=100) + self.canvas.setObjectName("canvas") + self.plot_layout = self.find(QVBoxLayout, "verticalLayout") + self.plot_layout.addWidget(self.canvas) + + self.plot_xy = PlotXY( + canvas = self.canvas, + results = self._results, + reach_id = 0, + profile_id = 0, + toolbar = None, + display_current = False + ) + self.plot_xy.draw() + + self.canvas_2 = MplCanvas(width=5, height=4, dpi=100) + self.canvas_2.setObjectName("canvas_2") + self.plot_layout_2 = self.find(QVBoxLayout, "verticalLayout_2") + self.plot_layout_2.addWidget(self.canvas_2) + + self.plot_kpc = PlotKPC( + canvas = self.canvas_2, + results = self._results, + reach_id = 0, + profile_id = 0, + toolbar = None + ) + self.plot_kpc.draw() + + self.canvas_3 = MplCanvas(width=5, height=4, dpi=100) + self.canvas_3.setObjectName("canvas_3") + self.plot_layout_3 = self.find(QVBoxLayout, "verticalLayout_3") + self.plot_layout_3.addWidget(self.canvas_3) + + self.plot_ac = PlotAC( + canvas = self.canvas_3, + results = self._results, + reach_id = 0, + profile_id = 0, + toolbar = None + ) + self.plot_ac.draw() + + self.canvas_4 = MplCanvas(width=5, height=4, dpi=100) + self.canvas_4.setObjectName("canvas_4") + self.plot_layout_4 = self.find(QVBoxLayout, "verticalLayout_hydrograph") + self.plot_layout_4.addWidget(self.canvas_4) + + self.plot_h = PlotH( + canvas = self.canvas_4, + results = self._results, + reach_id = 0, + profile_id = 0, + toolbar = None + ) + self.plot_h.draw() + + def setup_connections(self): + 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) + + fun = { + "reach": self._set_current_reach, + "profile": self._set_current_profile, + } + + for t in ["reach", "profile"]: + table = self.find(QTableView, f"tableView_{t}") + + table.selectionModel()\ + .selectionChanged\ + .connect(fun[t]) + + self._table[t].dataChanged.connect(fun[t]) + + self._slider_profile.valueChanged.connect(self._set_current_profile_slider) + self._slider_time.valueChanged.connect(self._set_current_timestamp) + + + def update(self, reach_id = None, profile_id = None, timestamp = None): + if reach_id is not None: + self.plot_xy.set_reach(reach_id) + self.plot_ac.set_reach(reach_id) + self.plot_kpc.set_reach(reach_id) + self.plot_h.set_reach(reach_id) + if profile_id is not None: + self.plot_xy.set_profile(profile_id) + self.plot_ac.set_profile(profile_id) + self.plot_kpc.set_profile(profile_id) + self.plot_h.set_profile(profile_id) + if timestamp is not None: + self.plot_xy.set_timestamp(timestamp) + self.plot_ac.set_timestamp(timestamp) + self.plot_kpc.set_timestamp(timestamp) + self.plot_h.set_timestamp(timestamp) + + self.plot_xy.draw() + self.plot_ac.draw() + self.plot_kpc.draw() + self.plot_h.draw() + + + def _set_current_reach(self): + table = self.find(QTableView, f"tableView_reach") + indexes = table.selectedIndexes() + if len(indexes) == 0: + return + + self.update(reach_id = indexes[0].row()) + + def _set_current_profile(self): + table = self.find(QTableView, f"tableView_profile") + indexes = table.selectedIndexes() + if len(indexes) == 0: + return + + ind = indexes[0].row() + self.update(profile_id = ind) + self._slider_profile.setValue(ind) + + def _set_current_profile_slider(self): + pid = self._slider_profile.value() + self.update(profile_id = pid) + + return + + def _set_current_timestamp(self): + timestamp = self._timestamps[self._slider_time.value()] + self.update(timestamp = timestamp) + + def copy(self): + logger.info("TODO: copy") + + def paste(self): + logger.info("TODO: paste") + + def undo(self): + self._table.undo() + + def redo(self): + self._table.redo() diff --git a/src/View/Results/translate.py b/src/View/Results/translate.py new file mode 100644 index 00000000..4b5ab3c5 --- /dev/null +++ b/src/View/Results/translate.py @@ -0,0 +1,30 @@ +# 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 . + +# -*- coding: utf-8 -*- + +from PyQt5.QtCore import QCoreApplication + +_translate = QCoreApplication.translate + +table_headers_reach = { + "name": _translate("Results", "Reach name"), +} + +table_headers_profile = { + "name": _translate("Results", "Name"), + "kp": _translate("Results", "KP (m)"), +} diff --git a/src/View/RunSolver/Log/Window.py b/src/View/RunSolver/Log/Window.py index fb7ba631..93d42165 100644 --- a/src/View/RunSolver/Log/Window.py +++ b/src/View/RunSolver/Log/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import tempfile diff --git a/src/View/RunSolver/Window.py b/src/View/RunSolver/Window.py index 822f2bb7..872d72ef 100644 --- a/src/View/RunSolver/Window.py +++ b/src/View/RunSolver/Window.py @@ -1,7 +1,24 @@ +# 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 . + # -*- coding: utf-8 -*- -import tempfile import os +import logging +import tempfile from queue import Queue from tools import trace, timer @@ -37,6 +54,8 @@ except: _translate = QCoreApplication.translate +logger = logging.getLogger() + class SelectSolverWindow(ASubWindow, ListedSubWindow): def __init__(self, title="Select solver", study=None, config=None, @@ -94,6 +113,8 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self._config = config self._solver = solver + self._results = None + super(SolverLogWindow, self).__init__( name=self._title, ui="SolverLog", parent=parent ) @@ -153,6 +174,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self.find(QAction, "action_pause").triggered.connect(self.pause) self.find(QAction, "action_stop").triggered.connect(self.stop) self.find(QAction, "action_log_file").triggered.connect(self.log_file) + self.find(QAction, "action_results").triggered.connect(self.results) self._alarm.timeout.connect(self.update) @@ -177,12 +199,16 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self.find(QAction, "action_start").setEnabled(True) self.find(QAction, "action_pause").setEnabled(False) self.find(QAction, "action_stop").setEnabled(False) + self.find(QAction, "action_results").setEnabled(True) if self._solver.log_file() != "": self.find(QAction, "action_log_file").setEnabled(True) while self._output.qsize() != 0: s = self._output.get() - self._log(s) + if type(s) is str and "[ERROR]" in s: + self._log(s, color="red") + else: + self._log(s) def start(self): if self._solver.is_stoped(): @@ -193,6 +219,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self._process = self.new_process(self._parent) self._log(" *** Start", color="blue") + self._results = None self._solver.start(self._process) self.find(QAction, "action_start").setEnabled(False) @@ -202,6 +229,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self.find(QAction, "action_pause").setEnabled(False) self.find(QAction, "action_stop").setEnabled(True) self.find(QAction, "action_log_file").setEnabled(False) + self.find(QAction, "action_results").setEnabled(False) def pause(self): self._log(" *** Pause", color="blue") @@ -210,6 +238,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self.find(QAction, "action_start").setEnabled(True) self.find(QAction, "action_pause").setEnabled(False) self.find(QAction, "action_stop").setEnabled(True) + self.find(QAction, "action_results").setEnabled(False) def stop(self): self._log(" *** Stop", color="blue") @@ -218,9 +247,15 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow): self.find(QAction, "action_start").setEnabled(True) self.find(QAction, "action_pause").setEnabled(False) self.find(QAction, "action_stop").setEnabled(False) + self.find(QAction, "action_results").setEnabled(True) if self._solver.log_file() != "": self.find(QAction, "action_log_file").setEnabled(True) + def results(self): + if self._results is None: + self._results = self._solver.results(self._study, self._workdir, qlog = self._output) + self._parent.open_solver_results(self._solver, self._results) + def log_file(self): file_name = os.path.join(self._workdir, self._solver.log_file()) log = SolverLogFileWindow( diff --git a/src/View/SolverParameters/Table.py b/src/View/SolverParameters/Table.py index 7b44159d..5aa488ab 100644 --- a/src/View/SolverParameters/Table.py +++ b/src/View/SolverParameters/Table.py @@ -1,5 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback + from tools import trace, timer from PyQt5.QtCore import ( @@ -81,16 +100,20 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - if self._headers[column] == "value": - if value in tr.r_yes_no: - value = tr.r_yes_no[value].lower() + try: + if self._headers[column] == "value": + if value in tr.r_yes_no: + value = tr.r_yes_no[value].lower() - self._undo.push( - SetCommand( - self._params, row, - "value", value + self._undo.push( + SetCommand( + self._params, row, + "value", value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/SolverParameters/UndoCommand.py b/src/View/SolverParameters/UndoCommand.py index 6a8dc1eb..d3343d00 100644 --- a/src/View/SolverParameters/UndoCommand.py +++ b/src/View/SolverParameters/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -17,7 +33,7 @@ class SetCommand(QUndoCommand): self._index = index self._column = column self._old = self._data.get(self._index)[column] - self._new = new_value + self._new = str(new_value) def undo(self): self._data.get(self._index)[self._column] = self._old diff --git a/src/View/SolverParameters/Window.py b/src/View/SolverParameters/Window.py index 4e154a54..c04eebc7 100644 --- a/src/View/SolverParameters/Window.py +++ b/src/View/SolverParameters/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging @@ -40,7 +56,7 @@ class SolverParametersWindow(ASubMainWindow, ListedSubWindow): # Init tanslate dictionary tr.init() - title = title + " - " + study.name + self._title = title + " - " + study.name super(SolverParametersWindow, self).__init__( name=title, ui="SolverParameters", parent=parent @@ -53,7 +69,7 @@ class SolverParametersWindow(ASubMainWindow, ListedSubWindow): self.setup_table() self.setup_connections() - self.ui.setWindowTitle(title) + self.ui.setWindowTitle(self._title) def setup_sc(self): self._undo_stack = {} diff --git a/src/View/SolverParameters/translate.py b/src/View/SolverParameters/translate.py index 6085dbfb..76cb70ac 100644 --- a/src/View/SolverParameters/translate.py +++ b/src/View/SolverParameters/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/Stricklers/Table.py b/src/View/Stricklers/Table.py index 29f8fd53..5d32cda5 100644 --- a/src/View/Stricklers/Table.py +++ b/src/View/Stricklers/Table.py @@ -1,5 +1,24 @@ +# Table.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 . + # -*- coding: utf-8 -*- +import logging +import traceback + from tools import trace, timer from PyQt5.QtCore import ( @@ -23,8 +42,9 @@ from View.Stricklers.UndoCommand import ( from View.Stricklers.translate import * -_translate = QCoreApplication.translate +logger = logging.getLogger() +_translate = QCoreApplication.translate class TableModel(QAbstractTableModel): def __init__(self, data=None, undo=None, tab=""): @@ -76,30 +96,34 @@ class TableModel(QAbstractTableModel): row = index.row() column = index.column() - if self._headers[column] == "name": - self._undo.push( - SetNameCommand( - self._data, row, value + try: + if self._headers[column] == "name": + self._undo.push( + SetNameCommand( + self._data, row, value + ) ) - ) - elif self._headers[column] == "comment": - self._undo.push( - SetCommentCommand( - self._data, row, value + elif self._headers[column] == "comment": + self._undo.push( + SetCommentCommand( + self._data, row, value + ) ) - ) - elif self._headers[column] == "minor": - self._undo.push( - SetMinorCommand( - self._data, row, value + elif self._headers[column] == "minor": + self._undo.push( + SetMinorCommand( + self._data, row, value + ) ) - ) - elif self._headers[column] == "medium": - self._undo.push( - SetMediumCommand( - self._data, row, value + elif self._headers[column] == "medium": + self._undo.push( + SetMediumCommand( + self._data, row, value + ) ) - ) + except Exception as e: + logger.info(e) + logger.debug(traceback.format_exc()) self.dataChanged.emit(index, index) return True diff --git a/src/View/Stricklers/UndoCommand.py b/src/View/Stricklers/UndoCommand.py index 07e782de..ee75cf99 100644 --- a/src/View/Stricklers/UndoCommand.py +++ b/src/View/Stricklers/UndoCommand.py @@ -1,3 +1,19 @@ +# UndoCommand.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 . + # -*- coding: utf-8 -*- from copy import deepcopy @@ -17,7 +33,7 @@ class SetNameCommand(QUndoCommand): self._data = data self._index = index self._old = self._data.get(self._index).name - self._new = new_value + self._new = str(new_value) def undo(self): self._data.get(self._index).name = self._old @@ -32,7 +48,7 @@ class SetCommentCommand(QUndoCommand): self._data = data self._index = index self._old = self._data.get(self._index).comment - self._new = new_value + self._new = str(new_value) def undo(self): self._data.get(self._index).comment = self._old @@ -47,7 +63,7 @@ class SetMinorCommand(QUndoCommand): self._data = data self._index = index self._old = self._data.get(self._index).minor - self._new = new_value + self._new = float(new_value) def undo(self): self._data.get(self._index).minor = self._old @@ -62,7 +78,7 @@ class SetMediumCommand(QUndoCommand): self._data = data self._index = index self._old = self._data.get(self._index).medium - self._new = new_value + self._new = float(new_value) def undo(self): self._data.get(self._index).medium = self._old diff --git a/src/View/Stricklers/Window.py b/src/View/Stricklers/Window.py index 1d2bfe43..8726f971 100644 --- a/src/View/Stricklers/Window.py +++ b/src/View/Stricklers/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- import logging @@ -35,7 +51,7 @@ logger = logging.getLogger() class StricklersWindow(ASubMainWindow, ListedSubWindow): def __init__(self, title="Stricklers", study=None, config=None, parent=None): - title = title + " - " + study.name + self._title = title + " - " + study.name super(StricklersWindow, self).__init__( name=title, ui="Stricklers", parent=parent @@ -48,7 +64,7 @@ class StricklersWindow(ASubMainWindow, ListedSubWindow): self.setup_table() self.setup_connections() - self.ui.setWindowTitle(title) + self.ui.setWindowTitle(self._title) def setup_sc(self): self._undo_stack = QUndoStack() diff --git a/src/View/Stricklers/translate.py b/src/View/Stricklers/translate.py index 4664e163..ccafd5d8 100644 --- a/src/View/Stricklers/translate.py +++ b/src/View/Stricklers/translate.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/Study/Window.py b/src/View/Study/Window.py index a90ed549..7218f9d3 100644 --- a/src/View/Study/Window.py +++ b/src/View/Study/Window.py @@ -1,3 +1,19 @@ +# 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 . + # -*- coding: utf-8 -*- from Model.Study import Study diff --git a/src/View/ui/MainWindow.ui b/src/View/ui/MainWindow.ui index e91bd14d..ea77d698 100644 --- a/src/View/ui/MainWindow.ui +++ b/src/View/ui/MainWindow.ui @@ -85,17 +85,12 @@ - - - - - @@ -117,32 +112,8 @@ &Geometry - - - Comparer - - - - Profil en travers - - - - - - - - - - - - - - - - - @@ -152,8 +123,8 @@ &Execute + - @@ -166,14 +137,9 @@ - - - - - @@ -272,8 +238,6 @@ - - @@ -298,12 +262,8 @@ - - - - @@ -675,11 +635,17 @@ + + false + Help PAMHYR + + false + Help MAGE @@ -737,10 +703,10 @@ ressources/exit_bis.pngressources/exit_bis.png - Quit application + Quit - Quitter l'application (Ctrl+Q) + Quit the application (Ctrl+Q) Ctrl+Q @@ -755,7 +721,7 @@ Run solver - Run solver on current study + Run a solver @@ -836,7 +802,7 @@ Mesh - Afficher le maillage + Display meshed reach @@ -871,7 +837,7 @@ Friction - Edit friction frictions and lateral contributions + Edit friction frictions @@ -916,6 +882,9 @@ Initial conditions + + Define initial conditions + diff --git a/src/View/ui/Results.ui b/src/View/ui/Results.ui new file mode 100644 index 00000000..d975641b --- /dev/null +++ b/src/View/ui/Results.ui @@ -0,0 +1,120 @@ + + + MainWindow + + + + 0 + 0 + 942 + 655 + + + + MainWindow + + + + + + + + + + Qt::Horizontal + + + + Qt::Vertical + + + + + + + + + + 0 + + + + Geometry + + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + + + + + + + + + + + + + + + Hydrograph + + + + + + + + + + + + + Qt::Vertical + + + true + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + + + + 0 + 0 + 942 + 22 + + + + + + + + diff --git a/src/View/ui/SolverLog.ui b/src/View/ui/SolverLog.ui index bbbc3edb..c94c57db 100644 --- a/src/View/ui/SolverLog.ui +++ b/src/View/ui/SolverLog.ui @@ -63,6 +63,7 @@ + @@ -100,6 +101,11 @@ LogFile + + + results + + diff --git a/src/View/ui/about.ui b/src/View/ui/about.ui index 6b4f6957..579e0095 100644 --- a/src/View/ui/about.ui +++ b/src/View/ui/about.ui @@ -6,53 +6,86 @@ 0 0 - 362 - 98 + 553 + 262 Form + + + - + - + - ressources/logoCemagref.gif - - - true + ressources/Logo-INRAE.png - - - - - - 16 - 75 - true - - - - PamHyr - - - - - - - Version en developpement: - - - - + + + + 16 + 75 + true + + + + About PAMHYR + + + + + + + ... + + + + + + + Version: @version + + + + + + + License: GPLv3+ + + + + + + + <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a> + + + Qt::RichText + + + + + + + Qt::Vertical + + + + 20 + 40 + + + diff --git a/src/View/ui/ressources/Logo-INRAE.png b/src/View/ui/ressources/Logo-INRAE.png new file mode 100644 index 00000000..0cd93142 Binary files /dev/null and b/src/View/ui/ressources/Logo-INRAE.png differ diff --git a/src/View/ui/ui_to_py.sh b/src/View/ui/ui_to_py.sh index 92b12ff2..f4d11a5d 100755 --- a/src/View/ui/ui_to_py.sh +++ b/src/View/ui/ui_to_py.sh @@ -1,5 +1,21 @@ #! /bin/sh +# ui_to_py.sh -- 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 . + for ui in $(ls *.ui) do python3 -m PyQt5.uic.pyuic -x $ui -o ${ui%.*}.py diff --git a/src/config.py b/src/config.py index 46e4ad2e..d1ebb71e 100644 --- a/src/config.py +++ b/src/config.py @@ -1,3 +1,19 @@ +# config.py -- Pamhyr configuration manager +# 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 . + # -*- coding: utf-8 -*- import os @@ -18,7 +34,7 @@ logger = logging.getLogger() class Config(SQL): def __init__(self): - self._version = '0.0.1' + self._version = '0.0.3' self.filename = Config.filename() self.set_default_value() @@ -63,12 +79,38 @@ class Config(SQL): self.commit() + def _update(self): version = self.execute(f"SELECT value FROM info WHERE key='version'")[0] if version != self._version: logger.info(f"Configuration file update from {version} to {self._version}...") + major, minor, release = version.strip().split(".") + + if major == minor == "0": + if int(release) < 2: + # Add default solver + posix = os.name == 'posix' + self.execute(f""" + INSERT INTO solver VALUES ( + 'mage8', + 'default-mage', + 'Default PAMHYR mage 8 version', + + '', '', '', + + '', + '@install_dir/mage/mage{"" if posix else ".exe"} -fp=1 @input', + '' + ) + """) + if int(release) < 3: + self.execute(f"INSERT INTO data VALUES ('last_study', '')") + self.execute(f"INSERT INTO data VALUES ('close_correctly', 'True')") + + self.commit() + def _load_solver(self): self._solvers = [] @@ -138,6 +180,12 @@ class Config(SQL): v = self.execute("SELECT value FROM data WHERE key='lang'") self.lang = v[0] + # Last study + v = self.execute("SELECT value FROM data WHERE key='last_study'") + self.last_study = v[0] + v = self.execute("SELECT value FROM data WHERE key='close_correctly'") + self.close_correctly = v[0] == "True" + # Debug v = self.execute("SELECT value FROM data WHERE key='debug'") self.debug = v[0] == "True" @@ -190,6 +238,8 @@ class Config(SQL): "backup_max": self.backup_max, "editor": self.editor, "lang": self.lang, + "last_study": self.last_study, + "close_correctly": self.close_correctly, "debug": self.debug, } @@ -212,6 +262,13 @@ class Config(SQL): # Solvers self._solvers = [] + posix = os.name == 'posix' + ctor = solver_type_list["mage8"] + new = ctor("default-mage") + new._description = "Default PAMHYR mage 8 version" + new._cmd_solver = f""""@install_dir/mage/mage{"" if posix else ".exe"}" -fp=1 @input""" + self._solvers.append(new) + # Meshing tool self.meshing_tool = "" @@ -234,9 +291,25 @@ class Config(SQL): # Stricklers self.stricklers = StricklersList() + # Last study + self.last_study = "" + self.close_correctly = False + # Debug self.debug = False + def set_close_correctly(self): + self.close_correctly = True + self.execute(f"UPDATE data SET value='True' WHERE key='close_correctly'") + self.commit() + + def set_last_study(self, filename): + self.last_study = filename + self.close_correctly = False + self.execute(f"UPDATE data SET value='{self._sql_format(self.last_study)}' WHERE key='last_study'") + self.execute(f"UPDATE data SET value='{self.close_correctly}' WHERE key='close_correctly'") + self.commit() + @classmethod def filename(cls): file = "" diff --git a/src/lang/constant_string.py b/src/lang/constant_string.py index b599389a..8466a3b4 100644 --- a/src/lang/constant_string.py +++ b/src/lang/constant_string.py @@ -1,3 +1,19 @@ +# constant_string.py -- Pamhyr constant string definition for translate +# 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 . + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/lang/create_ts.sh b/src/lang/create_ts.sh index c3019916..1b30d684 100755 --- a/src/lang/create_ts.sh +++ b/src/lang/create_ts.sh @@ -1,5 +1,21 @@ #! /bin/sh +# create_ts.sh -- Pamhyr script to generate translate files +# 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 . + SOURCES=$(find ../ -name "*.py") FROM=$(find ../ -name "*.ui") diff --git a/src/lang/fr.ts b/src/lang/fr.ts index d44729f7..9bc3f345 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -1,1101 +1,2132 @@ - + - Dialog + About - - Dialog + + Contributors: + Contributeurs : + + + + BoundaryCondition + + + Not associate + Non associer + + + + X - + + Y + + + + + Time + Temps + + + + Date + Date + + + + Z (m) + Z (m) + + + + days + jours + + + + day + jour + + + + Not defined + Non définie + + + + Ponctual contribution + Contributions ponctuelles + + + + Time over Z + + + + + Time over Discharge + + + + + Z over Discharge + + + + Name Nom - + Type Type - + + Node + Nœud + + + + Discharge (m³/s) + Débit (m³/s) + + + + Discharge (m³/s) + + + + + Checker + + + Mage network graph {mode} checker + + + + + Check if the network graph is valid + Vérifie si le graph réseau est valide + + + + Study network reach checker + + + + + Check if exists at least one reach for study + Vérifie si il exists au moins un Bief dans l'étude + + + + Study geometry checker + Vérificateur de géometrie de l'étude + + + + Check if exists geometry for each reach of study + Vérifie si la géométrie exists pour chaque bief de l'étude + + + + Dummy ok + + + + + Dummy warning + + + + + Dummy error + + + + + Dialog + + + Dialog + + + + + Name + Nom + + + + Type + Type + + + Description Description - + Solver - Solveur + Solver - + Path Chemin - + Output formater - Formateur de resultat + Formateur de sortie - + Command line Ligne de commande - + Input formater Formateur d'entrée - - Reverse - Retourner + + Cancel + Annuler - + + Run + Lancer + + + + Draft + Débit (m³/s) + + + Solvers - Solveurs + Solvers - + Meshing tool Mailleur - + Meshing tool path Chemin du mailleur - + Constants Constantes - + Segment number - Nombre de segment + Nombre se segments - + Listing maximum size - Taille maximum des listings + Taille maximal du listing - + 1000 1000 - + 500000 - 500000 + - + Backup - Backup + Archive - + Auto save - Sauvegarde automatique + Sauvegarde auto - + Frequence Fréquence - + Max. archives - Nombre maximum de sauvegarde + Nombre max d'archive - + Enable Activé - + HH:mm:ss - + + Stricklers + Stricklers + + + + Editor + Éditeur + + + + This value must be used for reading or editing files in speficic case. + Cette valeur peut être utiliser dans des cas spécifique pour lire ou écrire dans un fichier. + + + + Editor command + Commande de l'éditeur + + + + - The "@file" keyworkd is replace by the path of file to open. + - Le mot clef "@file" sera remplacer par le chemin du fichier à ouvrir. + + + Language Langage - + Please restart application after language modification - Please restart application after language modification + Please restart application after language modification - + MyNewStudy - MaNouvelleÉtude + Ma nouvelle étude + + + + Time system + Système de temps + + + + Time + Temps + + + + Date + Date + + + + Staring date + Date de départ + + + + dd/MM/yyyy HH:mm:ss + + + + + Creation date : + Date de création : + + + + Last modification : + Dernière modification : + + + + Discharge + Débit (m³/s) + + + + Exception + + + Generic error message + + + + + Undefined error message + + + + + Method not implemented + + + + + Method + + + + + not implemented + + + + + for class + + + + + Not implemented method + + + + + FileFormatError + + + + + Invalid file format: + + + + + File format error + + + + + Invalid file format + + + + + Invalid file + + + + + format because of + + + + + Clipboard format error + + + + + without header + + + + + with header + + + + + Invalid clipboard data format: + + + + + Clipboard format unknown + Form - + Form - - PamHyr + + dd/MM/yyyy HH:mm:ss - - Version en developpement: + + days + jours + + + + HH:mm:ss + + + + + About PAMHYR + À propos de PAMHYR + + + + Version: @version + + + + + License: GPLv3+ + Licence: GPLv3+ + + + + <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a> + <a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Code source</a> + + + + ... - - MainWindow + + Frictions - - PAMHYR - + + Not defined + Non définie - + + Name + Nom + + + + Begin kp (m) + Pk de départ (m) + + + + End kp (m) + Pk de fin (m) + + + + Begin strickler + strickler de départ + + + + End strickler + Strickler de fin + + + + Geometry + + + Name + Nom + + + + Kp (m) + Pk (m) + + + + Type + Type + + + + upstream + amont + + + + downstream + aval + + + + LateralContribution + + + Name + Nom + + + + Minor bed + Lit mineur + + + + Medium bed + Lit moyen + + + + Comment + Commentaire + + + + Not associate + Non associer + + + + X + + + + + Y + + + + + Time + Temps + + + + Date + Date + + + + Z (m) + + + + + days + jours + + + + day + jour + + + + Not defined + Non définie + + + + Lateral contribution + Contribution laterale + + + + Rain + Pluie + + + + Evaporation + Évaporation + + + + Type + Type + + + + Reach + Bief + + + + Begin kp (m) + Pk de départ (m) + + + + End kp (m) + Pk de fin (m) + + + + KP (m) + PK (m) + + + + Elevation (m) + Altitude (m) + + + + Height (m) + Hauteur (m) + + + + Discharge (m³/s) + Débit (m³/s) + + + + Discharge (m³/s) + + + + + MainWindow + + + MainWindow + Fenêtre principale + + + + toolBar + Bar d'outils + + + + Add node or edge + Ajouter un nœud ou une arête + + + + Remove node or edge + Supprimer un nœud ou une arête + + + + PAMHYR + PAMHYR + + + &File &Fichier - + &River Network &Réseau - + &Geometry &Géométrie - + Comparer - Comparer + Comparer - - Profil en travers - - - - + &Execute - &Exécuter + &Executer - + &Hydraulics - &Hydrolique + &Hydraulique - + &Plots - &Graphiques + Gra&phique - + &Cartography &Cartographie - + &Help &Aide - - toolBar - - - - + toolBar_2 - + - + New study Nouvelle étude - + Ctrl+N - + Ctrl+N - + Open a study Ouvrir une étude - + Ctrl+O - + Ctrl+O - + Import data from MAGE - Importer un jeu de données MAGE + Importer des données d'une étude MAGE - + Import data from RubarBE - Importer un jeu de données RubarBE + Importer des données d'une étude RubarBE - + Close Fermer - + Close current study - Fermer l'étude courante + Fermer l'étude en cours - + Save mesh Sauvegarder le maillage - + Save Sauvegarder - + Ctrl+S - + Ctrl+S - + Save as ... - Sauvegarder sous ... + Sauvegarder sous... - + Ctrl+Shift+S - + Ctrl+Shift+S - + Archive - + Archive - + Pamhyr configuration - Configuration de PamHyr + Configuration de PAMHYR - + Quit Quitter - + Ctrl+F4 - + Ctrl+F4 - + Edit river network Éditer le réseau - + Edit geometry Éditer la géométrie - + Import geometry - Importer une géometrie + Importer une géométrie - + Export geometry - + Exporter la géométrie - + Run extrenal meshing tool - + Lancer le mailler externe - + choose meshing tool by reach - + View meshed geometry - + Voir la géométrie mailler - + Export mesh - + Exporter le maillage - + Delete mesh of current reach - + Supprimer le maillage - + Delete all mesh - + - + Abscisse - Cote - + XYZ - + - - Numerical parameter for MAGE solver - + + Numerical parameter for solvers + Paramètre numerique des solvers - + Boundary conditions and one-time contributions - + Condition aux limites et apports ponctuels - + Initial conditions - + Conditions initiales - + Export initial conditions - + Exporter les conditions initiales - + Import final state as initial condition - + Importer un état final comme conditions initiales - + Edit friction - + Éditer les frottements - + Edit lateral contributions - + Éditer les apports latéraux - + Edit spills - + Edit cross building - + Éditer les ouvrages - + Run solver - + Lancer un solver - + + F5 + F5 + + + Stop solver - + Stopper le solver - + Display listings - + Simulation directory management - + Open - Ouvrir + Ouvrir - + Hydrograph - + Hydrogramme - + Limnigram - + Limnigramme - + Map current reach - + Cartographier le bief sélectionné - + Help PAMHYR - + Aide de PAMHYR - - help MAGE - + + Help MAGE + Aide de MAGE - + About - + A propos - + ouvrir - + Save current study - + Sauvegarder l'étude - + Ctrl+F - + Ctrl+F - + Quit application - + Quitter l'application - - Quitter l'application (Ctrl+Q) - - - - + Ctrl+Q - + Ctrl+Q - - Run solver on current study - - - - - Ctrl+X - - - - + stop solver - + Interrompt la simulation en cours - + Ctrl+C - + Ctrl+C - + Run external meshing tool - + Lancer le mailler externe - + Run meshing tool on current reach geometry - + Lancer le mailler externe sur le bief selectionné - + Display simulation listing - + Display current simulation listing - + River network - + Réseau - + Geometry - + Géométrie - + Edit reach geometry - + Éditer la géométrie - + Mesh - + Maillage - - Afficher le maillage - - - - + Boundary conditions - + Conditions aux limites - + Edit boundary conditions and one-time contributions - + Éditer les conditions aux limites et les apports ponctuels - + Lateral contribution - + Contributions latérales - + Edit lateral contribution - + Éditer les contributions latérales - + Spills - + Edit lateral spills - - Sections - + + Friction + Frottements - - Edit section frictions and lateral contributions - + + Stricklers + - - Frictions - + + Edit the study stricklers + Éditer les Stricklers de l'étude - - Edit friction at the bottom - - - - + Building - + Ouvrages - + Edit building (valve, ...), singularity and pump - + Éditer les ouvrages - + Edit study - + Éditer l'étude - + English - + - + French + + + + + Revert + Retourner + + + + Open in editor + Ouvrir dans l'éditeur + + + + Eval + Évaluer + + + + Ctrl+Return + Ctrl+Return + + + + Liquid + Liquide + + + + Solid + Solide + + + + Suspenssion + Suspenssion + + + + Add + Ajouter + + + + Add a new boundary condition or lateral contribution + Ajouter une condition aux limites ou un apport ponctuel + + + + Delete + Supprimer + + + + Delete current selected rows + Supprimer les lignes selectionnées + + + + Ctrl+D + Ctrl+D + + + + Edit + Éditer + + + + Edit boundary condition or lateral contribution + Éditer une condition aux limites ou un apport ponctuel + + + + Ctrl+E + Ctrl+E + + + + Sort + Trier + + + + Sort boundary condition by name + Trié par nom + + + + Study stricklers + Stricklers de l'étude + + + + Application stricklers + Stricklers de l'application + + + + Add new stricklers + Ajouter un stricklers + + + + Delete selected stricklers + Supprimer les stricklers selectionnés + + + + Sort stricklers + Trier les stricklers + + + + delete + Supprimer + + + + Edit stricklers + Éditer les stricklers + + + + Cancel + Annuler + + + + Retry + Ressayer + + + + Run + Lancer + + + + Retry check + Ressayer la vérification + + + + Stop + Stopper + + + + Start + Commencer + + + + Pause + Pause + + + + LogFile + Fichier de log + + + + Generate minimal height + Généré une hauteur minimale + + + + Generate constant discharge + Généré un debit constant + + + + Add new initial condition + Ajouter une nouvelle condition initiale + + + + Delete inital condition + Supprimer une condition initiale + + + + sort + Trier + + + + Sort inital condition + Trier les conditions initiales + + + + Add a new point in boundary condition or lateral contribution + Ajouter un nouveau point + + + + Sort boundary condition point + Trier les points des conditions aux limites + + + + Quit the application (Ctrl+Q) + Quitter l'application (Ctrl+Q) + + + + Run a solver + Lancer un solver + + + + Display meshed reach - - &Fichier - + + Edit friction frictions + Éditer les frottements - - &Hydraulique - - - - - &Graphiques - - - - - &Cartographie - - - - - &Aide - - - - - Ctrl+R - - - - - Fermer - - - - - Enregistrer le maillage - - - - - Enregistrer - - - - - Enregistrer sous ... - - - - - Archiver - - - - - Configuration de Pamhyr - - - - - Quitter - - - - - Lancer le mailleur externe - - - - - Choix du mailleur par bief - - - - - Exporter le maillage - - - - - Supprimer le maillage du bief courant - - - - - Supprimer l'ensemble des maillages - - - - - Conditions aux Limites & Apports Ponctuels - - - - - Conditions initiales - - - - - Solveur MAGE - - - - - Stop Solveur - - - - - Afficher les listings - - - - - Ouvrir - - - - - Hydrogramme - - - - - Limnigramme - - - - - Ligne d'eau - - - - - Ligne d'eau finale - - - - - Ligne d'eau enveloppe - - - - - Voir l'animation (MAGE) - - - - - Cartographier le bief courant - - - - - Aide de PAMHYR - - - - - Aide de MAGE - - - - - enregistrer_etude_en_cours - - - - - fermer_etude_en_cours - - - - - quitter_application - - - - - lancer_solveur - - - - - interrompt_simulation_en_cours - - - - - lancer_mailleur_externe - - - - - afficher_listings_simulation - - - - - Aficher les listings de la simulation courante - - - - - Maillage - - - - - Cond. Limites - - - - - Frottements - - - - - Ouvrages - + + Define initial conditions + Définire les conditions initiales - + Édition des Tronçons - + Ouvrir une étude - + Enrégistrer étude en cours (Ctrl+S) - + Fermer étude en cours (Ctrl+F) - - - &Réseau + + + results + + + + + MainWindowProfile + + + Profile + + + + + (no name) + + + + + Quittez ? + + + + + MainWindowProfile + + + + + Trier les points par ordre croissant de X + + + + + Trier les points par ordre croissant de Y + + + + + Nom + + + + + Abs en travers (m) + + + + + La cote du fond + Z minimale + + + + + La cote maximale + Z maximale + + + + + Rive gauche + + + + + Rive droite + + + + + Abscisse en travers (m) + + + + + Cote (m) - - &Géométrie + + Suppression les lignes incomplètes - - &Exécuter + + Supprimer les lignes des cellules non renseignées ? - - Sous-étude Rubar3 + + Suppression des noms répétés - - Nouvelle étude MAGE + + Etes-vous sûr de vouloir quitter ? - - Nouvelle étude RubarBE + + Insérer un point - - Importer un jeu de données MAGE + + Supprimer le/les point(s) sélectionnés - - Importer un jeu de données RubarBE + + Trier les points par ordre décroissant de X - - Éditer le réseau + + Trier les points par ordre décroissant de Y - - Éditer la géométrie + + Décaler le point sélectionné vers le haut - - Importer une géométrie + + Décaler le point sélectionné vers le bas - - Exporter la géométrie + + Exporter (dans un fichier) les points du profil au format tabulé - - Visualiser la géométrie maillée + + Copier la sélection au format tabulé - - Paramètres numériques du solveur MAGE + + Coller la sélection depuis le presse-papier au format tabulé - - Activer/Désactiver l'export des conditions initiales + + Vérifier la validité de la saisie et garder ou pas les modifications apportées - - Importer l'état final comme état initial + + Annuler toutes les modifications depuis la dernière validation - - Édition des Frottements + + Annuler toutes les modifications et revenir à l'état initial - - Édition des Apports Latéraux + + Ligne d'eau : + Z : Cote (m) + A : Aire mouillée (mu00B2) + p : Périmètre mouillé (m) + L : Largeur au miroir (m) - - Édition des déversements + + 'Maj + Clic' : Ligne d'eau & 'Ctrl + Clic' : Sélectionner des points - - Édition des ouvrages en travers + + Abscisse en travers calculée en projétant les points +sur le plan défini par les deux points nommés extrêmes + + + + + MainWindow_reach + + + Stricklers + + + + + Kp (m) + Pk (m) + + + + Ouvrir un fichier + + + + + Fichiers .ST (*.ST) + + + + + Fichiers .M (*.M) + + + + + Tous les fichiers (*) + + + + + Kp : + + + + + Files .ST(*.ST or *.st) + + + + + All files (*) + + + + + Abscisse en travers (m) + + + + + Cote (m) + + + + + Profil suivant + + + + + Jeu de sections du Bief + + + + + +Ordre des sections : Amont --> Aval + + + + + Pk = + + + + + Nouveau profil + + + + + Trier les profils par ordre croissant des Pk + + + + + Name + Nom + + + + Type + Type + + + + Alt+Z + + + + + Alt+E + + + + + Alt+R + + + + + Vue globale automatique (Alt+S) + + + + + Vue globale automatique (Alt+D) + + + + + Vue globale automatique (Alt+F) + + + + + X (m) + + + + + Y (m) + + + + + KP (m) + + + + + Discharge (m^3/s) + Débit (m³/s) + + + + Choisissez un nom de fichier à sauvegarder - - Gestion des répertoires de simulation + + Édition des profils sélectionnés - - Vitesse(Pk) à t fixé + + Vous avez sélectionné plus de 5 profils. +Seuls les 5 premiers seront édités. - - Vitesse(t) à Pk fixé + + Profil N° : - - Charge hydraulique(Pk) à t fixé + + Profil précédent - - Charge hydraulique(t) à Pk fixé + + Profil sélectionné - - Autres résulats MAGE + + Importer une géométrie - - À propos + + Supprimer le profil sélectionné - - Lancer le solveur pour réaliser une simulation + + Éditer le profil sélectionné - - Lancer le mailleur externe sur la géométrie du bief courant + + Copier le profil sélectionné - - Réseau + + Coller le profil en fin de liste (penser à modifier le Pk avant de trier) - - Ouvrir l'éditeur de la topologie du réseau + + Dupliquer la section sélectionnée - - Géométrie + + Trier les profils par ordre décroissant des Pk - - Ouvrir l'éditeur de géométrie + + Changer l'ordre des profils (en décalant le profil sélectionné vers le haut) - - Ouvir l'éditeur des Conditions aux Limites & Apports Ponctuels + + Changer l'ordre des profils (en décalant le profil sélectionné vers le bas) - - App. Latéraux + + Terminer l'édition - - Ouvrir l'éditeur des Apports Latéraux Distribués + + Vue isométrique (Alt+Z) - - Déversements + + Vue isométrique (Alt+E) - - Ouvrir l'éditeur des Déversements Latéraux + + Vue isométrique (Alt+R) + + + + + Elevation (m) + Altitude (m) + + + + Network + + + Add node + Ajouter un nœud + + + + Delete the node + Supprimer un nœud + + + + Disable the node + Déactiver un nœud + + + + Delete the reach + Supprimer un bief + + + + Disable the reach + Déactiver un bief + + + + Enable the reach + Activer un bief + + + + Reverse the reach orientation + Inverser l'orientation du bief + + + + Results + + + X (m) + + + + + Y (m) + + + + + Reach name + + + + + Name + Nom + + + + KP (m) + + + + + SolverParameters + + + Name + Nom + + + + Value + Valeur + + + + Yes + Oui + + + + No + Non + + + + Y + O + + + + N + N + + + + Initial time (jj:hh:mm:ss) + Temps initial (jj:hh:mm:ss) + + + + Final time (jj:hh:mm:ss) + Temps final (jj:hh:mm:ss) + + + + Timestep (second) + Pas de temps (en second) + + + + Minimum timestep (second) + Pas de temps minimal (en second) + + + + Time step of writing on .TRA + Pas de temps d'écriture dans le fichier .TRA + + + + Time step of writing on .BIN + Pas de temps d'écriture dans le fichier .BIN + + + + Implicitation parameter + + + + + Continuity discretization type (S/L) + + + + + QSJ discretization (A/B) + + + + + Stop criterion iterations (G/A/R) + + + + + Iteration type + + + + + Smoothing coefficient + + + + + Maximun accepted number of CFL + + + + + Minimum water height (meter) + + + + + Maximun number of iterations (< 100) + + + + + Timestep reduction factor + + + + + Reduction precision factor of Z + + + + + Reduction precision factor of Q + + + + + Reduction precision factor of residue + + + + + Number of iteration at maximum precision + + + + + Number of iteration before switch + + + + + Maximum accepted Froude number + + + + + Diffluence node height balance + + + + + Compute reach volume balance (Y/N) + + + + + Maximum reach volume balance + + + + + Minimum reach volume to check + + + + + Solvers + + + Generic + Générique + + + + Mage version 8 + Mage en version 8 + + + + Toolbar + + + Vue originale + + + + + Panoramique des axes avec la souris gauche, zoom avec la droite + + + + + Zoom + + + + + Vue globale automatique (Shift+X) + + + + + Enregistrer la figure - - Tronçons + + Retour à la vue précédente - - Ouvrir l'éditeur des tronçons pour les frottements et Apports Latéraux + + Passer à la vue suivante - - Ouvrir l'éditeur des frottements au fond - - - - - Ouvrir l'éditeur des ouvrages (seuils, vannes, etc.), singularités et pompes + + Vue isométrique (Shift+W) diff --git a/src/pamhyr.py b/src/pamhyr.py index 8a832e18..55172c78 100755 --- a/src/pamhyr.py +++ b/src/pamhyr.py @@ -1,4 +1,21 @@ #!/usr/bin/env python3 + +# pamhyr.py -- Pamhyr entrypoint +# 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 . + # -*- coding: utf-8 -*- import sys, os @@ -10,7 +27,8 @@ from PyQt5.QtWidgets import QApplication from config import Config from tools import ( - reset_timers, display_timers, timer + reset_timers, display_timers, timer, + logger_color_blue, logger_color_red, logger_color_green, logger_color_reset ) from View.MainWindow import ApplicationWindow @@ -18,17 +36,59 @@ from Model.Study import Study logging.basicConfig( level=logging.DEBUG, - format='[PAMHYR][%(levelname)s] %(message)s' + format=(f'[{logger_color_blue()}PAMHYR{logger_color_reset()}]' + + f'[{logger_color_green()}%(levelname)s{logger_color_reset()}]' + + ' %(message)s') ) logger = logging.getLogger() logger.setLevel(logging.INFO) +try: + log = os.path.join( + os.path.dirname(Config.filename()), "log.txt" + ) + logfile = open(log, "w+") + handler = logging.StreamHandler(logfile) + formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') + handler.setFormatter(formatter) + handler.setLevel(logging.DEBUG) + logger.addHandler(handler) +except: + logger.error("Failed to create logfile...") + +def license(): + blue = lambda s: logger.info(f"{logger_color_blue()}{s}{logger_color_reset()}") + + blue("""`7MM\"""Mq. db `7MMM. ,MMF'`7MMF' `7MMF'`YMM' `MM'`7MM\"""Mq.""") + blue(""" MM `MM. ;MM: MMMb dPMM MM MM VMA ,V MM `MM.""") + blue(""" MM ,M9 ,V^MM. M YM ,M MM MM MM VMA ,V MM ,M9 pd*"*b.""") + blue(""" MMmmdM9 ,M `MM M Mb M' MM MMmmmmmmMM VMMP MMmmdM9 (O) j8""") + blue(""" MM AbmmmqMA M YM.P' MM MM MM MM MM YM. ,;j9""") + blue(""" MM A' VML M `YM' MM MM MM MM MM `Mb. ,-='""") + blue(""".JMML. .AMA. .AMMA..JML. `' .JMML..JMML. .JMML. .JMML. .JMML. .JMM. Ammmmmmm""") + + with open(os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "VERSION" + ) + ), "r") as f: + version = f.readline().strip() + logger.info(f"version: {logger_color_green()}{version}{logger_color_reset()}") + + logger.info("license: pamhyr Copyright (C) 2023 INRAE") + logger.info("license: This program comes with ABSOLUTELY NO WARRANTY.") + logger.info("license: This is free software, and you are welcome to redistribute it") + logger.info("license: under certain conditions.") + def main(): conf = Config.load() app = QApplication(sys.argv) translator = QTranslator() + license() + lang_file = "" if conf.lang == "": # System language diff --git a/src/tools.py b/src/tools.py index c71c91d1..c345fb7f 100644 --- a/src/tools.py +++ b/src/tools.py @@ -1,3 +1,19 @@ +# tools.py -- Pamhyr tools function collection +# 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 . + # -*- coding: utf-8 -*- import os @@ -23,6 +39,30 @@ from functools import ( logger = logging.getLogger() +def logger_color_blue(): + posix = os.name == "posix" + if posix: + return f"{Style.BRIGHT}{Fore.BLUE}" + return "" + +def logger_color_red(): + posix = os.name == "posix" + if posix: + return f"{Style.BRIGHT}{Fore.RED}" + return "" + +def logger_color_green(): + posix = os.name == "posix" + if posix: + return f"{Style.BRIGHT}{Fore.GREEN}" + return "" + +def logger_color_reset(): + posix = os.name == "posix" + if posix: + return f"{Style.RESET_ALL}" + return "" + ########## # TIMERS # ########## diff --git a/tools/license.el b/tools/license.el new file mode 100644 index 00000000..a35e4308 --- /dev/null +++ b/tools/license.el @@ -0,0 +1,41 @@ +;; license.el -- 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 . + +;; -*- coding: utf-8 -*- + +(defun pamhyr-current-filename () + (car (last (file-name-split (buffer-file-name))))) + +(defun pamhyr-insert-license () + (interactive) + (let ((filename (pamhyr-current-filename))) + (insert (format + "# %s -- 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 . +" filename)))) diff --git a/tools/ssh-run.sh b/tools/ssh-run.sh index 9c59bb9f..a7ca1af7 100755 --- a/tools/ssh-run.sh +++ b/tools/ssh-run.sh @@ -1,10 +1,14 @@ #! /bin/sh -# > ssh-run.sh SERVER DESTDIR SOLVER INPUT +# > ssh-run.sh SERVER DESTDIR SOLVER ARGS INPUT # First argument is the server name/addr # The second argument is the destination directory to copy input data # The third argument is the solver path -# The fourth argument is the input name +# The fourth argument is an solver args separate by ',' or input name +# The sixth argument is the input name or nothing -ssh $1 "cd $2; $3 $4" +args=$(echo $4 | tr ',' ' ') + +echo "ssh $1 \"cd $2; $3 $args $5\"" +ssh $1 "cd $2; $3 $args $5"