diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5e670bc1ceb92579ebf2e2aa39bbef6eede619be..19d0962c0d241fe98da88f4f8d60edfbede18130 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 <https://www.gnu.org/licenses/>. + stages: - build - test + - package - release ######### # BUILD # ######### -build: +build-mage-linux: stage: build + tags: + - linux + rules: + - if: $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG script: - - echo "TODO build pamhyr" + - 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-lang: +# 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 ./src/lang/ -# - ./create_ts.sh +# - 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: + - cd packages + - ./version.sh "$CI_COMMIT_BRANCH" "$CI_COMMIT_TAG" "$CI_COMMIT_SHORT_SHA" + artifacts: + paths: + - VERSION + +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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..0ce9cdac7226e76c18076df0cbba8c49420dbec3 --- /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 0000000000000000000000000000000000000000..001d08835979794ea7f4c1b6a8ac9656dd06fec8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + 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 <https://www.gnu.org/licenses/>. + +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 +<https://www.gnu.org/licenses/>. + + 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 +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..5664e303b5dc2e9ef8e14a0845d9486ec1920afd --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +git diff --git a/packages/linux.sh b/packages/linux.sh index 1cd72d28d5e304c806bc4c7ede828ea1def1e3b3..44770b6eef28dd8315d7ca9dd4f89bd3fc1b62a6 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 <https://www.gnu.org/licenses/>. + 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 35be24ad6ac50cf19d0eaa5861cd4404ddcc4431..ccd25a761247c4d00703e78d7ceaf498ef197d33 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 0000000000000000000000000000000000000000..8f5e8bcfbc0e2736a958cef9822c8a275f08444b --- /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 <https://www.gnu.org/licenses/>. + +# ./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 0000000000000000000000000000000000000000..16aba85c2721e92929d43dc5dcd2c76d32ec0e33 --- /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 <https://www.gnu.org/licenses/>. + +@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 1a769f515d699932bcfaf5d90dc88574510027af..4f794c51e90bc22e29e83d3dfcd7a927223f2161 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 <https://www.gnu.org/licenses/>. + 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 0000000000000000000000000000000000000000..9eadf7123842d406c7106d95bc0822b664562408 --- /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 6e87a601a34a497a1b7974602ffa6cdc198e072a..d10a8c4d514bcc2e2d238b40aa49aac4dbb14d06 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from enum import Enum diff --git a/src/Checker/Mage.py b/src/Checker/Mage.py index 4e121d51c67c9e35f90230332a9c5dcefa3d09e5..edb0c349a685408d8243309a8a06ad3549206550 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import time diff --git a/src/Checker/Study.py b/src/Checker/Study.py index c6c41feda47b968923d653953ae96291797aab25..868ac1041bb8ebc2bf2d07b7f012d585b1b9effd 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import time diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index 152a626927021414c833e931389734ab2d0f3466..86e0f1d2048bd4e28c3c9fd981d512c52c2fed5d 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 <https://www.gnu.org/licenses/>. + # -*- 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 d14a870b48cfe2373deb45b56febb3be2e4a2fca..1a342dc781bd846d47c81bdac4c0321a5c64ee6b 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/BoundaryCondition/BoundaryConditionTypes.py b/src/Model/BoundaryCondition/BoundaryConditionTypes.py index c87e5b83d3ad1922a53056e3471a39130a5f806b..b2c73f11326b652184cc92d9fd3d01ae361490a8 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.Except import NotImplementedMethodeError diff --git a/src/Model/DB.py b/src/Model/DB.py index 90869740a42bf6d456fd165851adde995241c4f3..ef7d729db628a9a74971b0aa2445db22e927e215 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import os diff --git a/src/Model/Except.py b/src/Model/Except.py index ad311666c4c1d4eec651ee316bce82edb4695df5..7d5e3959c933d2856c00340b0be2c3b9e1d9f9ad 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import ( diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index f7328dd4277ae996261ccb5d63836b160b467116..18167a86a3e30d11d8b8d929c3869ad11dabacec 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/Model/Friction/FrictionList.py b/src/Model/Friction/FrictionList.py index a82c9f22227ae44bc07d0a6e784596d132eefa62..21e10695bbf23f6b2b80c4c47a133ba80e021d65 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/Model/Geometry/Point.py b/src/Model/Geometry/Point.py index 98d1e348da5bea3493c0d57e5098d9f969e71f85..cc1971c096db57224da5c4ef046a68c41ec864b8 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.Except import NotImplementedMethodeError diff --git a/src/Model/Geometry/PointXY.py b/src/Model/Geometry/PointXY.py index 9b84ef7ce0697d2233390ff38bf68fd7ce152794..7448d0459ef8912bbd29deb26b8d9c316693f9ea 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from math import dist diff --git a/src/Model/Geometry/PointXYZ.py b/src/Model/Geometry/PointXYZ.py index ff0a62fb4d854bc1ab1d74470b11fd05efa17903..33a3bebf8c170bb0711c225b3e5b661eb8f102bb 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from math import dist diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py index 9fa8e9edf00b186e67148db3c0d967db3d6d1dfb..d81379322e1c990f4a0d33686a6519de7bab59a8 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index cb681383902b33ec0c753cb6b2a6308b32ed7c13..12c4ea77e93a53be83a91f9e98fe338896004d97 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import numpy as np diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 728cf2db7d42fc00d27207b97f7d4e2bbc10aaf2..0ddf06d8a21510e9f0d89df58889e0bd86430e85 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/Model/Geometry/Vector_1d.py b/src/Model/Geometry/Vector_1d.py index 51fef7f2c45cb2c339950402b24c867fc1297513..a3da9358a4a4946b5b6510d0a6fabf5476c9b086 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import numpy as np diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py index bb106662d485005854d93327de781e3c133f7c04..3bf747b089a6e0d0c9e3dd7d675c57dd80697361 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from copy import copy, deepcopy diff --git a/src/Model/InitialConditions/InitialConditionsDict.py b/src/Model/InitialConditions/InitialConditionsDict.py index 5e8d5fbac9764527a8843312683c932ecbca3ef2..48da56a0598e40d3426de80d72c7e6663988cbe2 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/LateralContribution/LateralContribution.py b/src/Model/LateralContribution/LateralContribution.py index 5098e9ba8d574e20b73f683153f1a693618a757a..38cbada442d04f2e522996d93727ce5d6da82403 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 <https://www.gnu.org/licenses/>. + # -*- 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 4d6a856744db1b76560f1dd5dc99712212ced865..ca7c111e1dd92abe56dbec2bb7eda237a6dabf34 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/LateralContribution/LateralContributionTypes.py b/src/Model/LateralContribution/LateralContributionTypes.py index 69d9946ca3745f4bf3b86629dfd22f00f10c7576..d669266a4d3e6ea36952fe8d149f998458e5ceaf 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.Except import NotImplementedMethodeError diff --git a/src/Model/Network/Edge.py b/src/Model/Network/Edge.py index f1ced151f21c55e32a3a870fff69fdf634980fb7..122499434559a5165e2479686433fa8216d604ec 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.Network.Node import Node diff --git a/src/Model/Network/Graph.py b/src/Model/Network/Graph.py index e2d549fbf6a8a922ff5b8a360ecbe3f5330c21ba..62948a2b8a6b50b79ee845d44b443f00be800bcb 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from functools import reduce diff --git a/src/Model/Network/Node.py b/src/Model/Network/Node.py index 5f37101f83b08900e3f20a2602652b53a17be339..7b8f52bab01b5c23623d5f630b091c39f49a06a5 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.Network.Point import Point diff --git a/src/Model/Network/Point.py b/src/Model/Network/Point.py index 8ea0ecd929f8fca0646cb598b9ca5788e35b2057..0bb1260994f1ab8dfb58ab1975961e543ab72833 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 <https://www.gnu.org/licenses/>. + # -*- 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 0000000000000000000000000000000000000000..67698f4ee607b86d9c5dca556ee7dc6b9e705ba0 --- /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 <https://www.gnu.org/licenses/>. + +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 0000000000000000000000000000000000000000..5ae1e90f50f755868c3e24434b289abed3d5ad8c --- /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 <https://www.gnu.org/licenses/>. + +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 {<ts>: {<key>: <value>, ...}, ...} + + @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 b21a95a91e4ae9751a0f6a4248a75ec54235e4d2..3041c6c7c1f02356071321074293d855bb862087 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.DB import SQLSubModel diff --git a/src/Model/Saved.py b/src/Model/Saved.py index 3f672731b7d329fbf1a11b123e7e1dd08b29a3f1..60013cded8abc606f0f62b8d810bbb17c77cef62 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 <https://www.gnu.org/licenses/>. + # -*- 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 57f2a4c16fa01424e625685756efabcb59363d11..0c0834483cb32fd6566dd870b6bfcfa5a4756e8c 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import pickle diff --git a/src/Model/SolverParameters/SolverParametersList.py b/src/Model/SolverParameters/SolverParametersList.py index 88d1d567a00a7a739f340a503288b22ddf1a4972..a647135680c9e71e41d3c2b316302ee67f71f620 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from copy import copy diff --git a/src/Model/Stricklers/Stricklers.py b/src/Model/Stricklers/Stricklers.py index cbc10009418899cd1be8d5a79efbb8e984f2a9aa..baa484fa62664847680e2bc69765519eae2330c1 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 <https://www.gnu.org/licenses/>. + # -*- 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 049d9b833687b36d6246128763f295e8cfbef5f8..82076bb1b4d1a3aca3cc3215689d6a000b1c8c01 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/Model/Study.py b/src/Model/Study.py index fc553f9412317477620405331360a1c3c41e3615..c907da3e3b59aabc62d04e31d1e9f4b17f13dfd2 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import os diff --git a/src/Scripts/plot_3DST.py b/src/Scripts/plot_3DST.py index 2647a32ac1d4f2e4a38b44b31e16a2cc56b87bbf..bcadf1f188ff0b922fa66fff282dc6f26035d073 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- # a lancer depuis src diff --git a/src/Solver/ASolver.py b/src/Solver/ASolver.py index e94bac4b706ecbe7ac723a27e1a6654e506b1317..64cc01cbffb4cb71dddf7c17e4f84b7160bcd43f 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 <https://www.gnu.org/licenses/>. + # -*- 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()) - - logger.debug(f"! {cmd}") + exe, args = self._format_command(cmd, self._path_input) - 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()) - - logger.debug(f"! {cmd}") + exe, args = self._format_command(cmd, self._path_solver) - 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()) - - logger.debug(f"! {cmd}") + exe, args = self._format_command(cmd, self._path_output) - 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 a80adf234868ed704ecd046167814b2bc502bde5..9e588baed0c84620af4d8dde35f56a4716e6eace 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Solver.ASolver import ( diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py index 125fcfffe766ecafbf3e60f30ea7439e34932b3a..6c129f887d717a19d0b88b06fca112451143b7ad 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 <https://www.gnu.org/licenses/>. + # -*- 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 58db6f4913daf4c364067e499cda4bf2fcd40d33..221f1ff4dd028f25f458da81e8028d3fa56303f1 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/VERSION b/src/VERSION new file mode 120000 index 0000000000000000000000000000000000000000..6ff19de4b804f2eca2b2d72657dd908c216b6537 --- /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 478993cf81b6e37fea77e523bc01c56850c7943c..4e1c13279313adb77683afe7a3e9a4a9c36e6bba 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 <https://www.gnu.org/licenses/>. + # -*- 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 9f84ad8674d044dd2b14883669b6a7b936194527..7c269aaf17155517136c4bf6f83a39c557b36301 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 <https://www.gnu.org/licenses/>. + # -*- 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 9f6df875ba341c4011b2682a31e9756ad49fb5f9..b27380a59742ec12bf4fde1c0aaf3ecbc815584d 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 <https://www.gnu.org/licenses/>. + # -*- 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 dfe805e1c60ae19e067463b2ed060303effc95e5..6536bd0d14a7e948e1f157a603ca6299da89c9b9 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 <https://www.gnu.org/licenses/>. + # -*- 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 b2eb09acfd3648a34e752cc243dc3a5466090820..4bb2605fc94213a2b70559807b79ca4e1425613c 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 <https://www.gnu.org/licenses/>. + # -*- 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 4e8513faa636bf6720e867ace5dfaeffb9b1b745..efcf0993770dee1bc6476f965a240dddc2a5d9d6 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 <https://www.gnu.org/licenses/>. + # -*- 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 44ef5a46ee29e845e24166fd5f458fdf85e4c8ed..4c09f5fce4e24180390dfca3cd9d7d6bb46ac5e4 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/BoundaryCondition/Table.py b/src/View/BoundaryCondition/Table.py index 98890e0ce0d36c744c365f6a80970e20b2fde137..fd39a13aff69623acbfe805d358d8824cbc23b99 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 <https://www.gnu.org/licenses/>. + # -*- 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 7b1c7f5e995c97dc91997b6c50f0723c0698084e..bbf8cb2c64ca351622b272af5ad94f8a10aa693f 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 <https://www.gnu.org/licenses/>. + # -*- 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 2e7dd43025a1937a378b77268d6b2121ad20f415..a2853590260278704db9f768cd50a8e6f17cc1cd 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 <https://www.gnu.org/licenses/>. + # -*- 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 e191d0beab228365163ec5719a57b7488f475f88..661c1ad071a4a475ab3783a53b4474c3b0e0e2f1 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/CheckList/Table.py b/src/View/CheckList/Table.py index 071b49076d6c8687680415a8c224029344303774..5810c4be6fd2e014513bfc0b898ecbb1c0b771ca 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/View/CheckList/Window.py b/src/View/CheckList/Window.py index d04c89138eff314ba58181c2e316b3d1168a1137..4c40ad1faba8ada49537efc0e233f9a1e51cc69b 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from tools import trace, timer diff --git a/src/View/CheckList/Worker.py b/src/View/CheckList/Worker.py index 9137d26bf449b690478ee3a117b3b1b0cfcfe24c..efd95dedf0d34f3ba3e88ba11d70997a66afa6a0 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import time diff --git a/src/View/Configure/Solver/Window.py b/src/View/Configure/Solver/Window.py index 71d4f798c97922c460308e78696b10aa190697c4..c295481f1e46ecd870137db6c3d8a29a4466d38c 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/Configure/Window.py b/src/View/Configure/Window.py index 818e5d9c2e9c4170dc68d1e3b006c9a3899ca1f4..23a371ac98d702e1a435098b47e05a6d046bcf8a 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Debug/Window.py b/src/View/Debug/Window.py index 974eace334a6cbfdb91fc8ca99ac3bdb3aeb2f1d..346c2ce09b7aa20e12f92b196318e6416b7015e4 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/View/DummyWindow.py b/src/View/DummyWindow.py index 576b51881df3ddc922121c10f44f7cdae3ad75f4..05117bca15659184948745eac437321b3a282d8b 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/Frictions/PlotStricklers.py b/src/View/Frictions/PlotStricklers.py index 8f706782eedafb72a895910dcfd87e6446d3ae8e..71ff6e6f5ab8134374fe5ae22befef9a9ae64e92 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from tools import timer, flatten diff --git a/src/View/Frictions/Table.py b/src/View/Frictions/Table.py index 2c96ef97848efe46eed0e5a2bded85efab6bfeba..ef9dc32f5cce327fc5e10efbc17520c7dbaa0cd3 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 <https://www.gnu.org/licenses/>. + # -*- 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 0d685ace8ada99c6a63b86a43dca930dc2007dce..ef902f07d55453daf9ae9e6e0830fb6b1915c23c 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 <https://www.gnu.org/licenses/>. + # -*- 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 18b39e3cbe4a95d80d5fa3fc49ac3f58c55e0d90..4af3b66c11643f034912b66fbce44bfba56b181b 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 <https://www.gnu.org/licenses/>. + # -*- 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 c404f59c7bf68aee46347dd1d357c19fddc18dcc..21981ab143ea072be1df098c3a4a02fcdd156ec1 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/Geometry/PlotAC.py b/src/View/Geometry/PlotAC.py index 9709d3bc740597841d2e6107708c88b562fea43f..fe6d933f4cf0420ff2a172a633b3ad0c5f877a93 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Geometry/PlotKPZ.py b/src/View/Geometry/PlotKPZ.py index b37f988fc71b28d85e47c85bf48a0b21b60b0a31..eb7d5e28b8660bf62ef11874522ad3c646290784 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Geometry/PlotXY.py b/src/View/Geometry/PlotXY.py index 36de09cc61126f6ae97402576e9bd55bfde952eb..54e056a910050ab661e472685ce1fd7eb107de4b 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 <https://www.gnu.org/licenses/>. + # -*- 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 2dc7f52276ca37918b48bb486bdc04a852b97115..076c27031cad042079823ec6ac7e5407d4a6e6e8 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/View/Geometry/Profile/Table.py b/src/View/Geometry/Profile/Table.py index d55ebfcd663a9ca5f4d3fde948a58610fd5d62dc..2de665aad935b2825dddde4388a9047f1ad790f0 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 <https://www.gnu.org/licenses/>. + # -*- 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 d0d45b990af3c56b1041c59bfbf57cbdcbaeff74..7f0cd1388973ca9461cb1d1b355fdc21193d69f2 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 <https://www.gnu.org/licenses/>. + # -*- 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 815cc2ecb26802e345cd9329ed581fa412a86509..54d1e0111bb39fa10a4f1045065c3976b9c248c2 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 <https://www.gnu.org/licenses/>. + # -*- 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 8de74df0e2ef9f5470b31cc8372741df1f7add71..0b9443f8e9816968ea5a87855f440e206a2ae5dd 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import os.path diff --git a/src/View/Geometry/Table.py b/src/View/Geometry/Table.py index 8cc29dc89b9e8cb032971bf85aa38779ff4dd943..1b055209d9fb4feeadd26d6ab40ea381f7acf24c 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 <https://www.gnu.org/licenses/>. + # -*- 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 ed911c664fd05d06593b8f44882c852aeb03bd50..1df9c58c87cd68a22ae8c90b8691196945b702f6 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 <https://www.gnu.org/licenses/>. + # -*- 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 835db523370437df8c1c2b6c9e692d2994e928a2..7776d737389c564c89b4bb752cb0dfc600ddc7b8 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 <https://www.gnu.org/licenses/>. + # -*- 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 f04ee9e8915555748c078fc1a75ddcdf79e605e3..747b6ca4f7cf128fe8e371083b9fa995956dfe50 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import sys diff --git a/src/View/InitialConditions/DialogDischarge.py b/src/View/InitialConditions/DialogDischarge.py index 8d01ef0eaf29382995ac86003a974cb43519a12a..f8e7e32a41aa630dcaf4a3cbaee35dd8220b4ca7 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/InitialConditions/DialogHeight.py b/src/View/InitialConditions/DialogHeight.py index 91ceadb1b891674d96d0f8f72bdfac0ac0d3264d..18ad67c85f13c880f34e3b7424d86ef440652d5c 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from View.ASubWindow import ASubWindow diff --git a/src/View/InitialConditions/PlotDKP.py b/src/View/InitialConditions/PlotDKP.py index 247605b3a143e280845d2984dd2f47911e64352f..58a83219e3afea0ccc3672433ded6550a080f17e 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 <https://www.gnu.org/licenses/>. + # -*- 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 07664683b05742dc09b53449ad0482e954fe94ec..1fe05defc0abfe05d64b17bae5c2f79e515a3ac6 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from tools import timer diff --git a/src/View/InitialConditions/Table.py b/src/View/InitialConditions/Table.py index 13eb52181869a72dbc5cfd0532c0958f57be59c7..5e704ba921635b633a2c947cc0b922d8bfaa422d 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 <https://www.gnu.org/licenses/>. + # -*- 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 215c299bc36b89495131c071070c1d567be0f3fa..c96f90fa2f759800cf40fb713dc593c4db503610 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 <https://www.gnu.org/licenses/>. + # -*- 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 358228119b599fe0c0317a5a51f9ce5bb1939729..95f6004ad7f6dbbc236d18c67ec958f2a50935ae 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import logging diff --git a/src/View/InitialConditions/translate.py b/src/View/InitialConditions/translate.py index 43890acbaa6f07c20f0de9befb5c8440e76d3da7..370d4a22381ee73fe4c7ad5fbeb2c275158e94c9 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 <https://www.gnu.org/licenses/>. + # -*- 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 d7c95ec0c63571d82056d29104a7db9d00c85f48..5843ee56ab6a150ddac588a012a98493f56f6ad0 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 <https://www.gnu.org/licenses/>. + # -*- 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 4101995ccf279b8c288f35a459fecead49d082fd..e9e878fddc8fd623d18ee683674e1030ec5cb94e 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 <https://www.gnu.org/licenses/>. + # -*- 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 b5963b76fa24f95f28da98438de43f2693bd5283..715683c944864f05411cc6c23825c15f8cdc6fa0 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 <https://www.gnu.org/licenses/>. + # -*- 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 e992771023a6ef68c61666c968203f0097051842..3a801591a54f82f0299c998cfccb1af668f38fe1 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 <https://www.gnu.org/licenses/>. + # -*- 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 e44147e2a34fe89ada02ead48ce0d726ea408a28..243d9ad8c089fdea0b864b71714202ac06aecd94 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/LateralContribution/Table.py b/src/View/LateralContribution/Table.py index 307fc56e50450a3dfcd12421dfad2c8b691c3c54..682bf00dc8a0ab23fbdcc7c53b5cb82a34855976 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 <https://www.gnu.org/licenses/>. + # -*- 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 f7e680b36b4e8c5aa55c821afece4006d3d875bf..3d8584368704fe6a23cb4fbaddc58cda6ea0a500 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 <https://www.gnu.org/licenses/>. + # -*- 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 b87e9968050cb9ca75e67033d3739bd0b2535fed..dc3dd94f7449092252c7b16d5ab0bd7a50fe9087 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 <https://www.gnu.org/licenses/>. + # -*- 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 c2b9e7893c5da5e3a551e617855b5e3f3cb53cf5..e9c6deeb7d2a8c9e1d2dc3fd86bea9e0741f47fe 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/ListedSubWindow.py b/src/View/ListedSubWindow.py index c88fd7b41bf6441fa69b9f2d993eb72ed05281d0..b4dfe416d1dc62899f54dc88eddc36c5e78058aa 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 <https://www.gnu.org/licenses/>. + # -*- 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 71301e1ba3dfc4cdf26073026d400137d3b56935..2fc14efab43d6c6622282b8a28f585e1619272aa 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 <https://www.gnu.org/licenses/>. + # -*- 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 2569497ad08fa52321732841454d86361e254b82..870792b84de7cef30efbf386e413d98d8b1876cc 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 <https://www.gnu.org/licenses/>. + # -*- 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 a8254745962940028f8ec27e0efcc57f06942d65..b24865e8b15a6daf9671e4b05d9e017f19b1cb7d 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 <https://www.gnu.org/licenses/>. + # -*- 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 - ) - ) - elif self.headers[index.column()] == "enable": - self._undo.push( - EnableEdgeCommand( - self.rows[index.row()], value + 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 + ) ) - ) - else: - self._undo.push( - SetCommand( - self.rows[index.row()], - self.headers[index.column()], - 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 + ) ) - ) + 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 d6d381251eca3fb1fd3a9178433fa335ddea756f..c9cce895fb1adba400b32645cc4f0a47667a318a 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from copy import deepcopy diff --git a/src/View/Network/Window.py b/src/View/Network/Window.py index 8fdf39889584cba8d6b797fdd34505c9493eb55b..0505186287d9f67354e5536e022923d52c9f9e5d 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 <https://www.gnu.org/licenses/>. + # -*- 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 cca27087aeb411db64e4e27de7f6e357ec613194..cd9988cfbe4cc19a23953d9f7145d8d085a34e8b 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- class APlot(object): diff --git a/src/View/Plot/MplCanvas.py b/src/View/Plot/MplCanvas.py index 5b3fc4a8ff5a8eafb273fe81a765fdc78505136c..ee1f5373544bb9096839da3e6b7ef964679035b4 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 <https://www.gnu.org/licenses/>. + 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 010959bc699d205365ff51b05e112cfa2a2b955b..fdfa1de6c7950d98d9ff53004ed4ccfcabbd3f80 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 <https://www.gnu.org/licenses/>. + 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 371864502ffcfea4b977e91bedb351d34b5584de..1cca7cf04d18a98ca78edb869017ce8dd96a54ff 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import os diff --git a/src/View/Results/PlotAC.py b/src/View/Results/PlotAC.py new file mode 100644 index 0000000000000000000000000000000000000000..f0e4b0e287ae351699c5a531aba8aae22f5db797 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 0000000000000000000000000000000000000000..dcc9f7f6c3fdd6e2c11943e004e17e6ec38c1dd8 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 0000000000000000000000000000000000000000..68f6fb4e043e8873d232857bd2ff3863c42ba719 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 0000000000000000000000000000000000000000..3e7a88f3d0daf53b860eeb0deba0e9525f77c3c8 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 0000000000000000000000000000000000000000..8e167459593af03d444b0cfa113d87202544e2a0 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 0000000000000000000000000000000000000000..ca18efaa83e126490b58714fbe6cf34775dfb5e9 --- /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 <https://www.gnu.org/licenses/>. + +# -*- coding: utf-8 -*- diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py new file mode 100644 index 0000000000000000000000000000000000000000..22d0a2c44e4158ebcf92664774483a6f2b88b461 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 0000000000000000000000000000000000000000..4b5ab3c523d4814e91d664701d591e7d32404835 --- /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 <https://www.gnu.org/licenses/>. + +# -*- 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 fb7ba631ff98c5be4f5ac6c305403f28e58ab73e..93d42165492ade7584584cb5e465b38d5aca3d87 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- import tempfile diff --git a/src/View/RunSolver/Window.py b/src/View/RunSolver/Window.py index 822f2bb7db78c0bda5575be7117d70ffac10e3f8..872d72ef008dd30db05c20c7ae481c1ddf575f19 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 <https://www.gnu.org/licenses/>. + # -*- 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 7b44159d512a91c2428cc34f0b4e518965b83dca..5aa488abacd2dde2293349e75f77f304f10b1d9e 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 <https://www.gnu.org/licenses/>. + # -*- 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 6a8dc1eb211c5183479ec51b03def2e4b5bf6e24..d3343d007ca17ad5c54a1398b92c1862c320e93b 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 <https://www.gnu.org/licenses/>. + # -*- 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 4e154a54b2caf130bea146db9179f73f25e55226..c04eebc7ed777cbdcc384d17442cce8f0eb5fb7d 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 <https://www.gnu.org/licenses/>. + # -*- 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 6085dbfb57b7ae89964d883f1805641da1dc90ca..76cb70ac627ce7fd4a9b15a618946a3c1cf30e16 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/Stricklers/Table.py b/src/View/Stricklers/Table.py index 29f8fd53902bbb433a8c62b68363a116be71523b..5d32cda51b19f49434c11360d6aa2973de57e44c 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 <https://www.gnu.org/licenses/>. + # -*- 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 07e782deb6a071ff2b0567ca267c4ef601a37815..ee75cf99c9b2205a87853d86a0bc7ebfa8208ef6 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 <https://www.gnu.org/licenses/>. + # -*- 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 1d2bfe43bbd710974ef8cbd539463e1897bb32ae..8726f971403ddc5cb6043c7b297918a354a16a5e 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 <https://www.gnu.org/licenses/>. + # -*- 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 4664e163e2af0ef17649431669da65a72077d0a9..ccafd5d8a7136d3efbcc36c69f2aaae744da62e1 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/View/Study/Window.py b/src/View/Study/Window.py index a90ed549551715004b17de342a02ee3a30d4ac72..7218f9d36571cc315142ea9a3bb6ad7aa207a867 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from Model.Study import Study diff --git a/src/View/ui/MainWindow.ui b/src/View/ui/MainWindow.ui index e91bd14d43a600c063e2c450c481119c7a13856d..ea77d698538b0c64532eeb76c8942fe436f55e94 100644 --- a/src/View/ui/MainWindow.ui +++ b/src/View/ui/MainWindow.ui @@ -85,17 +85,12 @@ <addaction name="separator"/> <addaction name="action_menu_open"/> <addaction name="separator"/> - <addaction name="action_menu_import_mage"/> - <addaction name="action_menu_import_rubarbe"/> - <addaction name="separator"/> - <addaction name="separator"/> <addaction name="action_menu_close"/> <addaction name="separator"/> <addaction name="action_menu_edit"/> <addaction name="action_menu_save"/> <addaction name="action_menu_save_as"/> <addaction name="separator"/> - <addaction name="separator"/> <addaction name="action_menu_config"/> <addaction name="separator"/> <addaction name="action_menu_quit"/> @@ -117,32 +112,8 @@ <property name="title"> <string>&Geometry</string> </property> - <widget class="QMenu" name="menuCompare"> - <property name="title"> - <string>Comparer</string> - </property> - <widget class="QMenu" name="menu_cross_profile"> - <property name="title"> - <string>Profil en travers</string> - </property> - <addaction name="action_menu_abscisse_cote"/> - <addaction name="action_menu_XYZ"/> - </widget> - <addaction name="menu_cross_profile"/> - </widget> <addaction name="action_menu_edit_geometry"/> <addaction name="separator"/> - <addaction name="action_menu_import_geometry"/> - <addaction name="action_menu_export_geometry"/> - <addaction name="separator"/> - <addaction name="action_menu_run_meshing_tool"/> - <addaction name="action_menu_view_mesh"/> - <addaction name="action_menu_export_mesh"/> - <addaction name="action_menu_delete_mesh_for_current_reach"/> - <addaction name="action_menu_delete_mesh"/> - <addaction name="separator"/> - <addaction name="separator"/> - <addaction name="menuCompare"/> </widget> <widget class="QMenu" name="menu_run"> <property name="locale"> @@ -152,8 +123,8 @@ <string>&Execute</string> </property> <addaction name="action_menu_numerical_parameter"/> + <addaction name="separator"/> <addaction name="action_menu_run_solver"/> - <addaction name="action_simulation_directory_management"/> <addaction name="separator"/> </widget> <widget class="QMenu" name="menu_Hydraulics"> @@ -166,14 +137,9 @@ <addaction name="action_menu_boundary_conditions"/> <addaction name="separator"/> <addaction name="action_menu_initial_conditions"/> - <addaction name="action_initia_conditions_export"/> - <addaction name="action_import_final_conditions_as_initial"/> <addaction name="separator"/> <addaction name="action_menu_edit_friction"/> <addaction name="action_menu_edit_lateral_contribution"/> - <addaction name="action_edit_spills"/> - <addaction name="separator"/> - <addaction name="action_edi_cross_building"/> </widget> <widget class="QMenu" name="menu_plot"> <property name="title"> @@ -272,8 +238,6 @@ <addaction name="separator"/> <addaction name="action_toolBar_network"/> <addaction name="action_toolBar_geometry"/> - <addaction name="action_toolBar_mesh"/> - <addaction name="action_toolBar_run_meshing_tool"/> </widget> <widget class="QToolBar" name="toolBar_2"> <property name="font"> @@ -298,12 +262,8 @@ <addaction name="separator"/> <addaction name="action_toolBar_lateral_contrib"/> <addaction name="separator"/> - <addaction name="action_toolBar_spills"/> - <addaction name="separator"/> <addaction name="action_toolBar_frictions"/> <addaction name="separator"/> - <addaction name="action_toolBar_building"/> - <addaction name="separator"/> <addaction name="action_toolBar_initial_cond"/> </widget> <action name="action_menu_new"> @@ -675,11 +635,17 @@ </property> </action> <action name="action_menu_help_pamhyr"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> <string>Help PAMHYR</string> </property> </action> <action name="action_menu_help_mage"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> <string>Help MAGE</string> </property> @@ -737,10 +703,10 @@ <normaloff>ressources/exit_bis.png</normaloff>ressources/exit_bis.png</iconset> </property> <property name="text"> - <string>Quit application</string> + <string>Quit</string> </property> <property name="toolTip"> - <string>Quitter l'application (Ctrl+Q)</string> + <string>Quit the application (Ctrl+Q)</string> </property> <property name="shortcut"> <string>Ctrl+Q</string> @@ -755,7 +721,7 @@ <string>Run solver</string> </property> <property name="toolTip"> - <string>Run solver on current study</string> + <string>Run a solver</string> </property> </action> <action name="action_toolBar_kill_solver"> @@ -836,7 +802,7 @@ <string>Mesh</string> </property> <property name="toolTip"> - <string>Afficher le maillage</string> + <string>Display meshed reach</string> </property> </action> <action name="action_toolBar_boundary_cond"> @@ -871,7 +837,7 @@ <string>Friction</string> </property> <property name="toolTip"> - <string>Edit friction frictions and lateral contributions</string> + <string>Edit friction frictions</string> </property> </action> <action name="action_toolBar_stricklers"> @@ -916,6 +882,9 @@ <property name="text"> <string>Initial conditions</string> </property> + <property name="toolTip"> + <string>Define initial conditions</string> + </property> </action> <action name="action_menu_sediment_layers"> <property name="text"> diff --git a/src/View/ui/Results.ui b/src/View/ui/Results.ui new file mode 100644 index 0000000000000000000000000000000000000000..d975641b9d1fd8ba90c54f4a33f7feeb5fd7150f --- /dev/null +++ b/src/View/ui/Results.ui @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>942</width> + <height>655</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <property name="locale"> + <locale language="English" country="Europe"/> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QSplitter" name="splitter_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSplitter" name="splitter_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QTableView" name="tableView_reach"/> + <widget class="QTableView" name="tableView_profile"/> + </widget> + <widget class="QWidget" name=""> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Geometry</string> + </attribute> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QSplitter" name="splitter_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QWidget" name="verticalLayoutWidget"> + <layout class="QVBoxLayout" name="verticalLayout"/> + </widget> + <widget class="QWidget" name="verticalLayoutWidget_2"> + <layout class="QVBoxLayout" name="verticalLayout_2"/> + </widget> + </widget> + <widget class="QWidget" name="verticalLayoutWidget_3"> + <layout class="QVBoxLayout" name="verticalLayout_3"/> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Hydrograph</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_hydrograph"/> + </item> + </layout> + </widget> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSlider" name="verticalSlider_profile"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="invertedAppearance"> + <bool>true</bool> + </property> + <property name="invertedControls"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QSlider" name="horizontalSlider_time"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>942</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/View/ui/SolverLog.ui b/src/View/ui/SolverLog.ui index bbbc3edbb35d9ea3c52881c4bd618942de029b8b..c94c57dbc14a5d0e81ea240ba405c1ee08a203ab 100644 --- a/src/View/ui/SolverLog.ui +++ b/src/View/ui/SolverLog.ui @@ -63,6 +63,7 @@ <addaction name="action_pause"/> <addaction name="action_stop"/> <addaction name="action_log_file"/> + <addaction name="action_results"/> </widget> <action name="action_stop"> <property name="icon"> @@ -100,6 +101,11 @@ <string>LogFile</string> </property> </action> + <action name="action_results"> + <property name="text"> + <string>results</string> + </property> + </action> </widget> <resources/> <connections/> diff --git a/src/View/ui/about.ui b/src/View/ui/about.ui index 6b4f695782947c63f3a0e29ecc762a93cf61f19e..579e0095f9e36116e06a7cd98bedaf6817318c86 100644 --- a/src/View/ui/about.ui +++ b/src/View/ui/about.ui @@ -6,53 +6,86 @@ <rect> <x>0</x> <y>0</y> - <width>362</width> - <height>98</height> + <width>553</width> + <height>262</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> + <property name="locale"> + <locale language="English" country="Europe"/> + </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="label_logo"> <property name="text"> <string/> </property> <property name="pixmap"> - <pixmap>ressources/logoCemagref.gif</pixmap> + <pixmap>ressources/Logo-INRAE.png</pixmap> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_title"> + <property name="font"> + <font> + <pointsize>16</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>About PAMHYR</string> </property> - <property name="scaledContents"> - <bool>true</bool> + </widget> + </item> + <item> + <widget class="QLabel" name="label_copyright"> + <property name="text"> + <string>...</string> </property> </widget> </item> <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QLabel" name="label_2"> - <property name="font"> - <font> - <pointsize>16</pointsize> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>PamHyr</string> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Version en developpement:</string> - </property> - </widget> - </item> - </layout> + <widget class="QLabel" name="label_version"> + <property name="text"> + <string>Version: @version</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_license"> + <property name="text"> + <string>License: GPLv3+</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string><a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a></string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> </item> </layout> </item> diff --git a/src/View/ui/ressources/Logo-INRAE.png b/src/View/ui/ressources/Logo-INRAE.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd93142e9ad2127cf167eac4acf97afed6be74b 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 92b12ff285bbe50a95681147a2ea5a2e629a46d4..f4d11a5d8891e8b1a7dbe2091ffbefee07a02b68 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 <https://www.gnu.org/licenses/>. + 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 46e4ad2ea8fbbc27b640268ff20dc904b9c6a3b7..d1ebb71ec8cdfb4df4bc9ae8ded5ca045c71a8f9 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 <https://www.gnu.org/licenses/>. + # -*- 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 b599389a274977e08988bc12b31ced977b4422e2..8466a3b447cc8070b828434a25fc1566f5d9afb7 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 <https://www.gnu.org/licenses/>. + # -*- coding: utf-8 -*- from PyQt5.QtCore import QCoreApplication diff --git a/src/lang/create_ts.sh b/src/lang/create_ts.sh index c301991643b6c6a0b999a55646ae33ccb9da6591..1b30d6847ff96b4c3f00e0ca9741284fa76cc1a1 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 <https://www.gnu.org/licenses/>. + SOURCES=$(find ../ -name "*.py") FROM=$(find ../ -name "*.ui") diff --git a/src/lang/fr.ts b/src/lang/fr.ts index d44729f7467b6d723fd75a5d214bff52f875ee1a..9bc3f3453531e7b48536e04506e64e826de239a2 100644 --- a/src/lang/fr.ts +++ b/src/lang/fr.ts @@ -1,1101 +1,2132 @@ <?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE TS><TS version="2.0" language="fr_FR" sourcelanguage="en"> +<!DOCTYPE TS><TS version="2.0" language="fr_FR" sourcelanguage="en_150"> +<context> + <name>About</name> + <message> + <location filename="../View/About/Window.py" line="61"/> + <source>Contributors: </source> + <translation>Contributeurs : </translation> + </message> +</context> +<context> + <name>BoundaryCondition</name> + <message> + <location filename="../View/BoundaryCondition/Table.py" line="143"/> + <source>Not associate</source> + <translation>Non associer</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="24"/> + <source>X</source> + <translation></translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="25"/> + <source>Y</source> + <translation></translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="26"/> + <source>Time</source> + <translation>Temps</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="27"/> + <source>Date</source> + <translation>Date</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="29"/> + <source>Z (m)</source> + <translation>Z (m)</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/Plot.py" line="60"/> + <source>days</source> + <translation>jours</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/Plot.py" line="60"/> + <source>day</source> + <translation>jour</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="29"/> + <source>Not defined</source> + <translation>Non définie</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="30"/> + <source>Ponctual contribution</source> + <translation>Contributions ponctuelles</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="31"/> + <source>Time over Z</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="32"/> + <source>Time over Discharge</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="33"/> + <source>Z over Discharge</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="37"/> + <source>Name</source> + <translation>Nom</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="38"/> + <source>Type</source> + <translation>Type</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/translate.py" line="39"/> + <source>Node</source> + <translation>NÅ“ud</translation> + </message> + <message> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="12"/> + <source>Discharge (m³/s)</source> + <translation type="obsolete">Débit (m³/s)</translation> + </message> + <message encoding="UTF-8"> + <location filename="../View/BoundaryCondition/Edit/translate.py" line="28"/> + <source>Discharge (m³/s)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Checker</name> + <message> + <location filename="../Checker/Mage.py" line="41"/> + <source>Mage network graph {mode} checker</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Checker/Mage.py" line="42"/> + <source>Check if the network graph is valid</source> + <translation>Vérifie si le graph réseau est valide</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="32"/> + <source>Study network reach checker</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Checker/Study.py" line="33"/> + <source>Check if exists at least one reach for study</source> + <translation>Vérifie si il exists au moins un Bief dans l'étude</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="61"/> + <source>Study geometry checker</source> + <translation>Vérificateur de géometrie de l'étude</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="62"/> + <source>Check if exists geometry for each reach of study</source> + <translation>Vérifie si la géométrie exists pour chaque bief de l'étude</translation> + </message> + <message> + <location filename="../Checker/Study.py" line="105"/> + <source>Dummy ok</source> + <translation></translation> + </message> + <message> + <location filename="../Checker/Study.py" line="121"/> + <source>Dummy warning</source> + <translation></translation> + </message> + <message> + <location filename="../Checker/Study.py" line="136"/> + <source>Dummy error</source> + <translation></translation> + </message> +</context> <context> <name>Dialog</name> <message> - <location filename="../view/ui/NewStudy.ui" line="14"/> + <location filename="../View/ui/InitialConditions_Dialog_Generator_Discharge.ui" line="14"/> <source>Dialog</source> <translation></translation> </message> <message> - <location filename="../view/ui/NewStudy.ui" line="26"/> + <location filename="../View/ui/NewStudy.ui" line="22"/> <source>Name</source> <translation>Nom</translation> </message> <message> - <location filename="../view/ui/ConfigureAddSolverDialog.ui" line="31"/> + <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="31"/> <source>Type</source> <translation>Type</translation> </message> <message> - <location filename="../view/ui/NewStudy.ui" line="33"/> + <location filename="../View/ui/NewStudy.ui" line="95"/> <source>Description</source> <translation>Description</translation> </message> <message> - <location filename="../view/ui/ConfigureAddSolverDialog.ui" line="62"/> + <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="62"/> <source>Solver</source> - <translation>Solveur</translation> + <translation>Solver</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="227"/> + <location filename="../View/ui/ConfigureDialog.ui" line="256"/> <source>Path</source> <translation>Chemin</translation> </message> <message> - <location filename="../view/ui/ConfigureAddSolverDialog.ui" line="97"/> + <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="97"/> <source>Output formater</source> - <translation>Formateur de resultat</translation> + <translation>Formateur de sortie</translation> </message> <message> - <location filename="../view/ui/ConfigureAddSolverDialog.ui" line="107"/> + <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="107"/> <source>Command line</source> <translation>Ligne de commande</translation> </message> <message> - <location filename="../view/ui/ConfigureAddSolverDialog.ui" line="117"/> + <location filename="../View/ui/ConfigureAddSolverDialog.ui" line="120"/> <source>Input formater</source> <translation>Formateur d'entrée</translation> </message> <message> - <location filename="../view/ui/Network.ui" line="136"/> - <source>Reverse</source> - <translation>Retourner</translation> + <location filename="../View/ui/SelectSolver.ui" line="41"/> + <source>Cancel</source> + <translation>Annuler</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="41"/> + <location filename="../View/ui/SelectSolver.ui" line="48"/> + <source>Run</source> + <translation>Lancer</translation> + </message> + <message> + <location filename="../View/ui/InitialConditions_Dialog_Generator_Height.ui" line="25"/> + <source>Draft</source> + <translation>Débit (m³/s)</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="47"/> <source>Solvers</source> - <translation>Solveurs</translation> + <translation>Solvers</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="112"/> + <location filename="../View/ui/ConfigureDialog.ui" line="121"/> <source>Meshing tool</source> <translation>Mailleur</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="127"/> + <location filename="../View/ui/ConfigureDialog.ui" line="131"/> <source>Meshing tool path</source> <translation>Chemin du mailleur</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="150"/> + <location filename="../View/ui/ConfigureDialog.ui" line="170"/> <source>Constants</source> <translation>Constantes</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="167"/> + <location filename="../View/ui/ConfigureDialog.ui" line="182"/> <source>Segment number</source> - <translation>Nombre de segment</translation> + <translation>Nombre se segments</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="174"/> + <location filename="../View/ui/ConfigureDialog.ui" line="189"/> <source>Listing maximum size</source> - <translation>Taille maximum des listings</translation> + <translation>Taille maximal du listing</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="185"/> + <location filename="../View/ui/ConfigureDialog.ui" line="200"/> <source>1000</source> <translation>1000</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="192"/> + <location filename="../View/ui/ConfigureDialog.ui" line="207"/> <source>500000</source> - <translation>500000</translation> + <translation></translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="203"/> + <location filename="../View/ui/ConfigureDialog.ui" line="237"/> <source>Backup</source> - <translation>Backup</translation> + <translation>Archive</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="220"/> + <location filename="../View/ui/ConfigureDialog.ui" line="249"/> <source>Auto save</source> - <translation>Sauvegarde automatique</translation> + <translation>Sauvegarde auto</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="234"/> + <location filename="../View/ui/ConfigureDialog.ui" line="263"/> <source>Frequence</source> <translation>Fréquence</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="241"/> + <location filename="../View/ui/ConfigureDialog.ui" line="270"/> <source>Max. archives</source> - <translation>Nombre maximum de sauvegarde</translation> + <translation>Nombre max d'archive</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="252"/> + <location filename="../View/ui/ConfigureDialog.ui" line="281"/> <source>Enable</source> <translation>Activé</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="287"/> + <location filename="../View/ui/ConfigureDialog.ui" line="316"/> <source>HH:mm:ss</source> <translation></translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="351"/> + <location filename="../View/ui/ConfigureDialog.ui" line="363"/> + <source>Stricklers</source> + <translation>Stricklers</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="423"/> + <source>Editor</source> + <translation>Éditeur</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="447"/> + <source>This value must be used for reading or editing files in speficic case.</source> + <translation>Cette valeur peut être utiliser dans des cas spécifique pour lire ou écrire dans un fichier.</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="456"/> + <source>Editor command</source> + <translation>Commande de l'éditeur</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="472"/> + <source> - The "@file" keyworkd is replace by the path of file to open.</source> + <translation> - Le mot clef "@file" sera remplacer par le chemin du fichier à ouvrir.</translation> + </message> + <message> + <location filename="../View/ui/ConfigureDialog.ui" line="502"/> <source>Language</source> <translation>Langage</translation> </message> <message> - <location filename="../view/ui/ConfigureDialog.ui" line="335"/> + <location filename="../View/ui/ConfigureDialog.ui" line="493"/> <source>Please restart application after language modification</source> - <translation type="unfinished">Please restart application after language modification</translation> + <translation>Please restart application after language modification</translation> </message> <message> - <location filename="../view/ui/NewStudy.ui" line="40"/> + <location filename="../View/ui/NewStudy.ui" line="29"/> <source>MyNewStudy</source> - <translation>MaNouvelleÉtude</translation> + <translation>Ma nouvelle étude</translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="36"/> + <source>Time system</source> + <translation>Système de temps</translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="45"/> + <source>Time</source> + <translation>Temps</translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="57"/> + <source>Date</source> + <translation>Date</translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="74"/> + <source>Staring date</source> + <translation>Date de départ</translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="84"/> + <source>dd/MM/yyyy HH:mm:ss</source> + <translation></translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="116"/> + <source>Creation date :</source> + <translation>Date de création :</translation> + </message> + <message> + <location filename="../View/ui/NewStudy.ui" line="130"/> + <source>Last modification :</source> + <translation>Dernière modification :</translation> + </message> + <message> + <location filename="../View/ui/InitialConditions_Dialog_Generator_Discharge.ui" line="25"/> + <source>Discharge</source> + <translation>Débit (m³/s)</translation> + </message> +</context> +<context> + <name>Exception</name> + <message> + <location filename="../Model/Except.py" line="53"/> + <source>Generic error message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="59"/> + <source>Undefined error message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="74"/> + <source>Method not implemented</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="99"/> + <source>Method</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="99"/> + <source>not implemented</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="99"/> + <source>for class</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="96"/> + <source>Not implemented method</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="109"/> + <source>FileFormatError</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="117"/> + <source>Invalid file format:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="123"/> + <source>File format error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="126"/> + <source>Invalid file format</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="129"/> + <source>Invalid file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="129"/> + <source>format because of</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="169"/> + <source>Clipboard format error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="151"/> + <source>without header</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="153"/> + <source>with header</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="158"/> + <source>Invalid clipboard data format:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Model/Except.py" line="172"/> + <source>Clipboard format unknown</source> + <translation type="unfinished"></translation> </message> </context> <context> <name>Form</name> <message> - <location filename="../view/ui/dummy.ui" line="14"/> + <location filename="../View/ui/Widgets/extendedTimeEdit.ui" line="20"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/about.ui" line="44"/> - <source>PamHyr</source> + <location filename="../View/ui/Widgets/extendedDateTimeEdit.ui" line="35"/> + <source>dd/MM/yyyy HH:mm:ss</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/Widgets/extendedTimeEdit.ui" line="36"/> + <source>days</source> + <translation>jours</translation> + </message> + <message> + <location filename="../View/ui/Widgets/extendedTimeEdit.ui" line="52"/> + <source>HH:mm:ss</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/about.ui" line="51"/> - <source>Version en developpement:</source> + <location filename="../View/ui/about.ui" line="42"/> + <source>About PAMHYR</source> + <translation>À propos de PAMHYR</translation> + </message> + <message> + <location filename="../View/ui/about.ui" line="56"/> + <source>Version: @version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/about.ui" line="63"/> + <source>License: GPLv3+</source> + <translation>Licence: GPLv3+</translation> + </message> + <message> + <location filename="../View/ui/about.ui" line="70"/> + <source><a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Source code</a></source> + <translation><a href="https://gitlab.irstea.fr/theophile.terraz/pamhyr">Code source</a></translation> + </message> + <message> + <location filename="../View/ui/about.ui" line="49"/> + <source>...</source> <translation type="unfinished"></translation> </message> </context> -<context encoding="UTF-8"> +<context> + <name>Frictions</name> + <message> + <location filename="../View/Frictions/Table.py" line="138"/> + <source>Not defined</source> + <translation>Non définie</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="24"/> + <source>Name</source> + <translation>Nom</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="26"/> + <source>Begin kp (m)</source> + <translation>Pk de départ (m)</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="27"/> + <source>End kp (m)</source> + <translation>Pk de fin (m)</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="28"/> + <source>Begin strickler</source> + <translation>strickler de départ</translation> + </message> + <message> + <location filename="../View/Frictions/translate.py" line="29"/> + <source>End strickler</source> + <translation>Strickler de fin</translation> + </message> +</context> +<context> + <name>Geometry</name> + <message> + <location filename="../View/Geometry/Table.py" line="56"/> + <source>Name</source> + <translation>Nom</translation> + </message> + <message> + <location filename="../View/Geometry/Table.py" line="57"/> + <source>Kp (m)</source> + <translation>Pk (m)</translation> + </message> + <message> + <location filename="../View/Geometry/Table.py" line="58"/> + <source>Type</source> + <translation>Type</translation> + </message> + <message> + <location filename="../View/Geometry/Table.py" line="96"/> + <source>upstream</source> + <translation>amont</translation> + </message> + <message> + <location filename="../View/Geometry/Table.py" line="99"/> + <source>downstream</source> + <translation>aval</translation> + </message> +</context> +<context> + <name>LateralContribution</name> + <message> + <location filename="../View/InitialConditions/translate.py" line="24"/> + <source>Name</source> + <translation>Nom</translation> + </message> + <message> + <location filename="../View/Stricklers/translate.py" line="29"/> + <source>Minor bed</source> + <translation>Lit mineur</translation> + </message> + <message> + <location filename="../View/Stricklers/translate.py" line="30"/> + <source>Medium bed</source> + <translation>Lit moyen</translation> + </message> + <message> + <location filename="../View/InitialConditions/translate.py" line="30"/> + <source>Comment</source> + <translation>Commentaire</translation> + </message> + <message> + <location filename="../View/LateralContribution/Table.py" line="141"/> + <source>Not associate</source> + <translation>Non associer</translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/translate.py" line="24"/> + <source>X</source> + <translation></translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/translate.py" line="25"/> + <source>Y</source> + <translation></translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/translate.py" line="26"/> + <source>Time</source> + <translation>Temps</translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/translate.py" line="27"/> + <source>Date</source> + <translation>Date</translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/translate.py" line="29"/> + <source>Z (m)</source> + <translation></translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/Plot.py" line="60"/> + <source>days</source> + <translation>jours</translation> + </message> + <message> + <location filename="../View/LateralContribution/Edit/Plot.py" line="60"/> + <source>day</source> + <translation>jour</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="28"/> + <source>Not defined</source> + <translation>Non définie</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="29"/> + <source>Lateral contribution</source> + <translation>Contribution laterale</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="30"/> + <source>Rain</source> + <translation>Pluie</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="31"/> + <source>Evaporation</source> + <translation>Évaporation</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="36"/> + <source>Type</source> + <translation>Type</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="37"/> + <source>Reach</source> + <translation>Bief</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="38"/> + <source>Begin kp (m)</source> + <translation>Pk de départ (m)</translation> + </message> + <message> + <location filename="../View/LateralContribution/translate.py" line="39"/> + <source>End kp (m)</source> + <translation>Pk de fin (m)</translation> + </message> + <message> + <location filename="../View/InitialConditions/translate.py" line="25"/> + <source>KP (m)</source> + <translation>PK (m)</translation> + </message> + <message> + <location filename="../View/InitialConditions/translate.py" line="28"/> + <source>Elevation (m)</source> + <translation>Altitude (m)</translation> + </message> + <message> + <location filename="../View/InitialConditions/translate.py" line="29"/> + <source>Height (m)</source> + <translation>Hauteur (m)</translation> + </message> + <message> + <location filename="../View/InitialConditions/translate.py" line="11"/> + <source>Discharge (m³/s)</source> + <translation type="obsolete">Débit (m³/s)</translation> + </message> + <message encoding="UTF-8"> + <location filename="../View/InitialConditions/translate.py" line="27"/> + <source>Discharge (m³/s)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>MainWindow</name> <message> - <location filename="../view/ui/MainWindow_old.ui" line="37"/> + <location filename="../View/ui/EditLateralContribution.ui" line="20"/> + <source>MainWindow</source> + <translation>Fenêtre principale</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="58"/> + <source>toolBar</source> + <translation>Bar d'outils</translation> + </message> + <message> + <location filename="../View/ui/Network.ui" line="218"/> + <source>Add node or edge</source> + <translation>Ajouter un nÅ“ud ou une arête</translation> + </message> + <message> + <location filename="../View/ui/Network.ui" line="230"/> + <source>Remove node or edge</source> + <translation>Supprimer un nÅ“ud ou une arête</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="37"/> <source>PAMHYR</source> - <translation type="unfinished"></translation> + <translation>PAMHYR</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="82"/> + <location filename="../View/ui/MainWindow.ui" line="82"/> <source>&File</source> <translation>&Fichier</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="109"/> + <location filename="../View/ui/MainWindow.ui" line="104"/> <source>&River Network</source> <translation>&Réseau</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="118"/> + <location filename="../View/ui/MainWindow.ui" line="113"/> <source>&Geometry</source> <translation>&Géométrie</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="112"/> + <location filename="../View/ui/MainWindow.ui" line="122"/> <source>Comparer</source> - <translation>Comparer</translation> - </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="116"/> - <source>Profil en travers</source> - <translation type="unfinished"></translation> + <translation type="obsolete">Comparer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="152"/> + <location filename="../View/ui/MainWindow.ui" line="123"/> <source>&Execute</source> - <translation>&Exécuter</translation> + <translation>&Executer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="166"/> + <location filename="../View/ui/MainWindow.ui" line="135"/> <source>&Hydraulics</source> - <translation>&Hydrolique</translation> + <translation>&Hydraulique</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="182"/> + <location filename="../View/ui/MainWindow.ui" line="146"/> <source>&Plots</source> - <translation>&Graphiques</translation> + <translation>Gra&phique</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="192"/> + <location filename="../View/ui/MainWindow.ui" line="156"/> <source>&Cartography</source> <translation>&Cartographie</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="198"/> + <location filename="../View/ui/MainWindow.ui" line="162"/> <source>&Help</source> <translation>&Aide</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="240"/> - <source>toolBar</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="284"/> + <location filename="../View/ui/MainWindow.ui" line="245"/> <source>toolBar_2</source> - <translation type="unfinished"></translation> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="315"/> + <location filename="../View/ui/MainWindow.ui" line="270"/> <source>New study</source> <translation>Nouvelle étude</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="323"/> + <location filename="../View/ui/EditLateralContribution.ui" line="85"/> <source>Ctrl+N</source> - <translation type="unfinished"></translation> + <translation>Ctrl+N</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="346"/> + <location filename="../View/ui/MainWindow.ui" line="301"/> <source>Open a study</source> <translation>Ouvrir une étude</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="353"/> + <location filename="../View/ui/MainWindow.ui" line="304"/> <source>Ctrl+O</source> - <translation type="unfinished"></translation> + <translation>Ctrl+O</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="354"/> + <location filename="../View/ui/MainWindow.ui" line="312"/> <source>Import data from MAGE</source> - <translation>Importer un jeu de données MAGE</translation> + <translation>Importer des données d'une étude MAGE</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="362"/> + <location filename="../View/ui/MainWindow.ui" line="323"/> <source>Import data from RubarBE</source> - <translation>Importer un jeu de données RubarBE</translation> + <translation>Importer des données d'une étude RubarBE</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="592"/> + <location filename="../View/ui/MainWindow.ui" line="589"/> <source>Close</source> <translation>Fermer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="671"/> + <location filename="../View/ui/MainWindow.ui" line="683"/> <source>Close current study</source> - <translation>Fermer l'étude courante</translation> + <translation>Fermer l'étude en cours</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="385"/> + <location filename="../View/ui/MainWindow.ui" line="346"/> <source>Save mesh</source> <translation>Sauvegarder le maillage</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="394"/> + <location filename="../View/ui/MainWindow.ui" line="355"/> <source>Save</source> <translation>Sauvegarder</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="398"/> + <location filename="../View/ui/MainWindow.ui" line="358"/> <source>Ctrl+S</source> - <translation type="unfinished"></translation> + <translation>Ctrl+S</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="406"/> + <location filename="../View/ui/MainWindow.ui" line="367"/> <source>Save as ...</source> - <translation>Sauvegarder sous ...</translation> + <translation>Sauvegarder sous...</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="410"/> + <location filename="../View/ui/MainWindow.ui" line="370"/> <source>Ctrl+Shift+S</source> - <translation type="unfinished"></translation> + <translation>Ctrl+Shift+S</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="414"/> + <location filename="../View/ui/MainWindow.ui" line="375"/> <source>Archive</source> - <translation type="unfinished"></translation> + <translation>Archive</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="419"/> + <location filename="../View/ui/MainWindow.ui" line="380"/> <source>Pamhyr configuration</source> - <translation>Configuration de PamHyr</translation> + <translation>Configuration de PAMHYR</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="428"/> + <location filename="../View/ui/MainWindow.ui" line="698"/> <source>Quit</source> <translation>Quitter</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="432"/> + <location filename="../View/ui/MainWindow.ui" line="392"/> <source>Ctrl+F4</source> - <translation type="unfinished"></translation> + <translation>Ctrl+F4</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="764"/> + <location filename="../View/ui/MainWindow.ui" line="773"/> <source>Edit river network</source> <translation>Éditer le réseau</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="441"/> + <location filename="../View/ui/MainWindow.ui" line="402"/> <source>Edit geometry</source> <translation>Éditer la géométrie</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="446"/> + <location filename="../View/ui/MainWindow.ui" line="410"/> <source>Import geometry</source> - <translation>Importer une géometrie</translation> + <translation>Importer une géométrie</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="451"/> + <location filename="../View/ui/MainWindow.ui" line="418"/> <source>Export geometry</source> - <translation type="unfinished"></translation> + <translation>Exporter la géométrie</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="456"/> + <location filename="../View/ui/MainWindow.ui" line="426"/> <source>Run extrenal meshing tool</source> - <translation type="unfinished"></translation> + <translation>Lancer le mailler externe</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="461"/> + <location filename="../View/ui/MainWindow.ui" line="431"/> <source>choose meshing tool by reach</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="469"/> + <location filename="../View/ui/MainWindow.ui" line="439"/> <source>View meshed geometry</source> - <translation type="unfinished"></translation> + <translation>Voir la géométrie mailler</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="477"/> + <location filename="../View/ui/MainWindow.ui" line="447"/> <source>Export mesh</source> - <translation type="unfinished"></translation> + <translation>Exporter le maillage</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="485"/> + <location filename="../View/ui/MainWindow.ui" line="455"/> <source>Delete mesh of current reach</source> - <translation type="unfinished"></translation> + <translation>Supprimer le maillage</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="493"/> + <location filename="../View/ui/MainWindow.ui" line="463"/> <source>Delete all mesh</source> - <translation type="unfinished"></translation> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="487"/> + <location filename="../View/ui/MainWindow.ui" line="471"/> <source>Abscisse - Cote</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="492"/> + <location filename="../View/ui/MainWindow.ui" line="479"/> <source>XYZ</source> - <translation type="unfinished"></translation> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="508"/> - <source>Numerical parameter for MAGE solver</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/MainWindow.ui" line="487"/> + <source>Numerical parameter for solvers</source> + <translation>Paramètre numerique des solvers</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="513"/> + <location filename="../View/ui/MainWindow.ui" line="492"/> <source>Boundary conditions and one-time contributions</source> - <translation type="unfinished"></translation> + <translation>Condition aux limites et apports ponctuels</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="524"/> + <location filename="../View/ui/MainWindow.ui" line="875"/> <source>Initial conditions</source> - <translation type="unfinished"></translation> + <translation>Conditions initiales</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="532"/> + <location filename="../View/ui/MainWindow.ui" line="514"/> <source>Export initial conditions</source> - <translation type="unfinished"></translation> + <translation>Exporter les conditions initiales</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="537"/> + <location filename="../View/ui/MainWindow.ui" line="522"/> <source>Import final state as initial condition</source> - <translation type="unfinished"></translation> + <translation>Importer un état final comme conditions initiales</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="542"/> + <location filename="../View/ui/MainWindow.ui" line="527"/> <source>Edit friction</source> - <translation type="unfinished"></translation> + <translation>Éditer les frottements</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="547"/> + <location filename="../View/ui/MainWindow.ui" line="532"/> <source>Edit lateral contributions</source> - <translation type="unfinished"></translation> + <translation>Éditer les apports latéraux</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="552"/> + <location filename="../View/ui/MainWindow.ui" line="540"/> <source>Edit spills</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="562"/> + <location filename="../View/ui/MainWindow.ui" line="553"/> <source>Edit cross building</source> + <translation>Éditer les ouvrages</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="713"/> + <source>Run solver</source> + <translation>Lancer un solver</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="561"/> + <source>F5</source> + <translation>F5</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="566"/> + <source>Stop solver</source> + <translation>Stopper le solver</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="571"/> + <source>Display listings</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="701"/> - <source>Run solver</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/MainWindow.ui" line="579"/> + <source>Simulation directory management</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="584"/> + <source>Open</source> + <translation>Ouvrir</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="597"/> + <source>Hydrograph</source> + <translation>Hydrogramme</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="612"/> + <source>Limnigram</source> + <translation>Limnigramme</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="626"/> + <source>Map current reach</source> + <translation>Cartographier le bief sélectionné</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="634"/> + <source>Help PAMHYR</source> + <translation>Aide de PAMHYR</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="642"/> + <source>Help MAGE</source> + <translation>Aide de MAGE</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="647"/> + <source>About</source> + <translation>A propos</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="656"/> + <source>ouvrir</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="668"/> + <source>Save current study</source> + <translation>Sauvegarder l'étude</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="689"/> + <source>Ctrl+F</source> + <translation>Ctrl+F</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="732"/> + <source>Quit application</source> + <translation type="obsolete">Quitter l'application</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="704"/> + <source>Ctrl+Q</source> + <translation>Ctrl+Q</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="728"/> + <source>stop solver</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="731"/> + <source>Interrompt la simulation en cours</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="734"/> + <source>Ctrl+C</source> + <translation>Ctrl+C</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="743"/> + <source>Run external meshing tool</source> + <translation>Lancer le mailler externe</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="746"/> + <source>Run meshing tool on current reach geometry</source> + <translation>Lancer le mailler externe sur le bief selectionné</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="758"/> + <source>Display simulation listing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="761"/> + <source>Display current simulation listing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="770"/> + <source>River network</source> + <translation>Réseau</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="782"/> + <source>Geometry</source> + <translation>Géométrie</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="785"/> + <source>Edit reach geometry</source> + <translation>Éditer la géométrie</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="794"/> + <source>Mesh</source> + <translation>Maillage</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="802"/> + <source>Boundary conditions</source> + <translation>Conditions aux limites</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="805"/> + <source>Edit boundary conditions and one-time contributions</source> + <translation>Éditer les conditions aux limites et les apports ponctuels</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="813"/> + <source>Lateral contribution</source> + <translation>Contributions latérales</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="816"/> + <source>Edit lateral contribution</source> + <translation>Éditer les contributions latérales</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="821"/> + <source>Spills</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="824"/> + <source>Edit lateral spills</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="829"/> + <source>Friction</source> + <translation>Frottements</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="837"/> + <source>Stricklers</source> + <translation></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="840"/> + <source>Edit the study stricklers</source> + <translation>Éditer les Stricklers de l'étude</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="845"/> + <source>Building</source> + <translation>Ouvrages</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="848"/> + <source>Edit building (valve, ...), singularity and pump</source> + <translation>Éditer les ouvrages</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="860"/> + <source>Edit study</source> + <translation>Éditer l'étude</translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="865"/> + <source>English</source> + <translation></translation> + </message> + <message> + <location filename="../View/ui/MainWindow.ui" line="870"/> + <source>French</source> + <translation></translation> + </message> + <message> + <location filename="../View/ui/SolverLogFile.ui" line="65"/> + <source>Revert</source> + <translation>Retourner</translation> + </message> + <message> + <location filename="../View/ui/SolverLogFile.ui" line="74"/> + <source>Open in editor</source> + <translation>Ouvrir dans l'éditeur</translation> + </message> + <message> + <location filename="../View/ui/DebugRepl.ui" line="29"/> + <source>Eval</source> + <translation>Évaluer</translation> + </message> + <message> + <location filename="../View/ui/DebugRepl.ui" line="36"/> + <source>Ctrl+Return</source> + <translation>Ctrl+Return</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="44"/> + <source>Liquid</source> + <translation>Liquide</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="54"/> + <source>Solid</source> + <translation>Solide</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="64"/> + <source>Suspenssion</source> + <translation>Suspenssion</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="79"/> + <source>Add</source> + <translation>Ajouter</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="117"/> + <source>Add a new boundary condition or lateral contribution</source> + <translation>Ajouter une condition aux limites ou un apport ponctuel</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="94"/> + <source>Delete</source> + <translation>Supprimer</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="97"/> + <source>Delete current selected rows</source> + <translation>Supprimer les lignes selectionnées</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="100"/> + <source>Ctrl+D</source> + <translation>Ctrl+D</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="144"/> + <source>Edit</source> + <translation>Éditer</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="147"/> + <source>Edit boundary condition or lateral contribution</source> + <translation>Éditer une condition aux limites ou un apport ponctuel</translation> + </message> + <message> + <location filename="../View/ui/Frictions.ui" line="104"/> + <source>Ctrl+E</source> + <translation>Ctrl+E</translation> + </message> + <message> + <location filename="../View/ui/EditLateralContribution.ui" line="109"/> + <source>Sort</source> + <translation>Trier</translation> + </message> + <message> + <location filename="../View/ui/BoundaryConditions.ui" line="162"/> + <source>Sort boundary condition by name</source> + <translation>Trié par nom</translation> + </message> + <message> + <location filename="../View/ui/Stricklers.ui" line="28"/> + <source>Study stricklers</source> + <translation>Stricklers de l'étude</translation> + </message> + <message> + <location filename="../View/ui/Stricklers.ui" line="38"/> + <source>Application stricklers</source> + <translation>Stricklers de l'application</translation> + </message> + <message> + <location filename="../View/ui/Stricklers.ui" line="73"/> + <source>Add new stricklers</source> + <translation>Ajouter un stricklers</translation> + </message> + <message> + <location filename="../View/ui/Stricklers.ui" line="85"/> + <source>Delete selected stricklers</source> + <translation>Supprimer les stricklers selectionnés</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="572"/> - <source>Stop solver</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/Stricklers.ui" line="97"/> + <source>Sort stricklers</source> + <translation>Trier les stricklers</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="577"/> - <source>Display listings</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="106"/> + <source>delete</source> + <translation>Supprimer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="582"/> - <source>Simulation directory management</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/Frictions.ui" line="101"/> + <source>Edit stricklers</source> + <translation>Éditer les stricklers</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="587"/> - <source>Open</source> - <translation type="unfinished">Ouvrir</translation> + <location filename="../View/ui/CheckList.ui" line="97"/> + <source>Cancel</source> + <translation>Annuler</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="597"/> - <source>Hydrograph</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/CheckList.ui" line="85"/> + <source>Retry</source> + <translation>Ressayer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="609"/> - <source>Limnigram</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/CheckList.ui" line="66"/> + <source>Run</source> + <translation>Lancer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="620"/> - <source>Map current reach</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/CheckList.ui" line="88"/> + <source>Retry check</source> + <translation>Ressayer la vérification</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="625"/> - <source>Help PAMHYR</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/SolverLog.ui" line="74"/> + <source>Stop</source> + <translation>Stopper</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="630"/> - <source>help MAGE</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/SolverLog.ui" line="83"/> + <source>Start</source> + <translation>Commencer</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="635"/> - <source>About</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/SolverLog.ui" line="92"/> + <source>Pause</source> + <translation>Pause</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="690"/> - <source>ouvrir</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/SolverLog.ui" line="101"/> + <source>LogFile</source> + <translation>Fichier de log</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="656"/> - <source>Save current study</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="30"/> + <source>Generate minimal height</source> + <translation>Généré une hauteur minimale</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="723"/> - <source>Ctrl+F</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="37"/> + <source>Generate constant discharge</source> + <translation>Généré un debit constant</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="686"/> - <source>Quit application</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="97"/> + <source>Add new initial condition</source> + <translation>Ajouter une nouvelle condition initiale</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="735"/> - <source>Quitter l'application (Ctrl+Q)</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="109"/> + <source>Delete inital condition</source> + <translation>Supprimer une condition initiale</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="738"/> - <source>Ctrl+Q</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="118"/> + <source>sort</source> + <translation>Trier</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="704"/> - <source>Run solver on current study</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/InitialConditions.ui" line="121"/> + <source>Sort inital condition</source> + <translation>Trier les conditions initiales</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="753"/> - <source>Ctrl+X</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/EditLateralContribution.ui" line="82"/> + <source>Add a new point in boundary condition or lateral contribution</source> + <translation>Ajouter un nouveau point</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="719"/> - <source>stop solver</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/EditLateralContribution.ui" line="112"/> + <source>Sort boundary condition point</source> + <translation>Trier les points des conditions aux limites</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="768"/> - <source>Interrompt la simulation en cours</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/MainWindow.ui" line="701"/> + <source>Quit the application (Ctrl+Q)</source> + <translation>Quitter l'application (Ctrl+Q)</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="771"/> - <source>Ctrl+C</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/MainWindow.ui" line="716"/> + <source>Run a solver</source> + <translation>Lancer un solver</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="734"/> - <source>Run external meshing tool</source> + <location filename="../View/ui/MainWindow.ui" line="797"/> + <source>Display meshed reach</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="737"/> - <source>Run meshing tool on current reach geometry</source> - <translation type="unfinished"></translation> + <location filename="../View/ui/MainWindow.ui" line="832"/> + <source>Edit friction frictions</source> + <translation>Éditer les frottements</translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="749"/> - <source>Display simulation listing</source> + <location filename="../View/ui/MainWindow.ui" line="878"/> + <source>Define initial conditions</source> + <translation>Définire les conditions initiales</translation> + </message> + <message encoding="UTF-8"> + <location filename="../View/ui/MainWindow.ui" line="545"/> + <source>Édition des Tronçons</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow.ui" line="752"/> - <source>Display current simulation listing</source> + <message encoding="UTF-8"> + <location filename="../View/ui/MainWindow.ui" line="659"/> + <source>Ouvrir une étude</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow.ui" line="761"/> - <source>River network</source> + <message encoding="UTF-8"> + <location filename="../View/ui/MainWindow.ui" line="671"/> + <source>Enrégistrer étude en cours (Ctrl+S)</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow.ui" line="773"/> - <source>Geometry</source> + <message encoding="UTF-8"> + <location filename="../View/ui/MainWindow.ui" line="686"/> + <source>Fermer étude en cours (Ctrl+F)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="776"/> - <source>Edit reach geometry</source> + <location filename="../View/ui/SolverLog.ui" line="106"/> + <source>results</source> <translation type="unfinished"></translation> </message> +</context> +<context encoding="UTF-8"> + <name>MainWindowProfile</name> <message> - <location filename="../view/ui/MainWindow.ui" line="785"/> - <source>Mesh</source> + <location filename="../View/Geometry/Profile/Window.py" line="83"/> + <source>Profile</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="834"/> - <source>Afficher le maillage</source> + <location filename="../View/Geometry/Profile/Window.py" line="87"/> + <source>(no name)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="793"/> - <source>Boundary conditions</source> + <location filename="../View/Geometry/Profile/Window.py" line="382"/> + <source>Quittez ?</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="796"/> - <source>Edit boundary conditions and one-time contributions</source> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="223"/> + <source>MainWindowProfile</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="804"/> - <source>Lateral contribution</source> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="227"/> + <source>Trier les points par ordre croissant de X</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="807"/> - <source>Edit lateral contribution</source> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="231"/> + <source>Trier les points par ordre croissant de Y</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="812"/> - <source>Spills</source> + <location filename="../View/Geometry/Profile/Table.py" line="56"/> + <source>Nom</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="815"/> - <source>Edit lateral spills</source> + <location filename="../View/Geometry/Profile/Table.py" line="57"/> + <source>Abs en travers (m)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="820"/> - <source>Sections</source> + <location filename="../View/Geometry/Profile/Table.py" line="107"/> + <source>La cote du fond</source> + <comment>Z minimale</comment> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="823"/> - <source>Edit section frictions and lateral contributions</source> + <location filename="../View/Geometry/Profile/Table.py" line="111"/> + <source>La cote maximale</source> + <comment>Z maximale</comment> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="828"/> - <source>Frictions</source> + <location filename="../View/Geometry/Profile/Table.py" line="129"/> + <source>Rive gauche</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="831"/> - <source>Edit friction at the bottom</source> + <location filename="../View/Geometry/Profile/Table.py" line="131"/> + <source>Rive droite</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="836"/> - <source>Building</source> + <location filename="../View/Geometry/Profile/Plot.py" line="74"/> + <source>Abscisse en travers (m)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow.ui" line="839"/> - <source>Edit building (valve, ...), singularity and pump</source> + <location filename="../View/Geometry/Profile/Plot.py" line="79"/> + <source>Cote (m)</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow.ui" line="851"/> - <source>Edit study</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/Window.py" line="328"/> + <source>Suppression les lignes incomplètes</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow.ui" line="856"/> - <source>English</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/Window.py" line="328"/> + <source>Supprimer les lignes des cellules non renseignées ?</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow.ui" line="861"/> - <source>French</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/Window.py" line="371"/> + <source>Suppression des noms répétés</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="76"/> - <source>&Fichier</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/Window.py" line="382"/> + <source>Etes-vous sûr de vouloir quitter ?</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="159"/> - <source>&Hydraulique</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="224"/> + <source>Insérer un point</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="175"/> - <source>&Graphiques</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="225"/> + <source>Supprimer le/les point(s) sélectionnés</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="191"/> - <source>&Cartographie</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="229"/> + <source>Trier les points par ordre décroissant de X</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="197"/> - <source>&Aide</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="234"/> + <source>Trier les points par ordre décroissant de Y</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="340"/> - <source>Ctrl+R</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="236"/> + <source>Décaler le point sélectionné vers le haut</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="581"/> - <source>Fermer</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="238"/> + <source>Décaler le point sélectionné vers le bas</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="386"/> - <source>Enregistrer le maillage</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="240"/> + <source>Exporter (dans un fichier) les points du profil au format tabulé</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="395"/> - <source>Enregistrer</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="242"/> + <source>Copier la sélection au format tabulé</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="407"/> - <source>Enregistrer sous ...</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="244"/> + <source>Coller la sélection depuis le presse-papier au format tabulé</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="415"/> - <source>Archiver</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="246"/> + <source>Vérifier la validité de la saisie et garder ou pas les modifications apportées</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="420"/> - <source>Configuration de Pamhyr</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="249"/> + <source>Annuler toutes les modifications depuis la dernière validation</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="429"/> - <source>Quitter</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="251"/> + <source>Annuler toutes les modifications et revenir à l'état initial</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="457"/> - <source>Lancer le mailleur externe</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="253"/> + <source>Ligne d'eau : + <byte value="x9"/>Z : Cote (m) + <byte value="x9"/>A : Aire mouillée (mu00B2) + <byte value="x9"/>p : Périmètre mouillé (m) + <byte value="x9"/>L : Largeur au miroir (m)</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="462"/> - <source>Choix du mailleur par bief</source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/mainwindow_ui_profile.py" line="257"/> + <source>'Maj + Clic' : Ligne d'eau & 'Ctrl + Clic' : Sélectionner des points</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../view/ui/MainWindow_old.ui" line="472"/> - <source>Exporter le maillage </source> + <message encoding="UTF-8"> + <location filename="../View/Geometry/Profile/Table.py" line="142"/> + <source>Abscisse en travers calculée en projétant les points +sur le plan défini par les deux points nommés extrêmes </source> <translation type="unfinished"></translation> </message> +</context> +<context> + <name>MainWindow_reach</name> <message> - <location filename="../view/ui/MainWindow_old.ui" line="477"/> - <source>Supprimer le maillage du bief courant</source> - <translation type="unfinished"></translation> + <location filename="../View/Frictions/PlotStricklers.py" line="76"/> + <source>Stricklers</source> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="482"/> - <source>Supprimer l'ensemble des maillages</source> - <translation type="unfinished"></translation> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="339"/> + <source>Kp (m)</source> + <translation>Pk (m)</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="502"/> - <source>Conditions aux Limites & Apports Ponctuels</source> + <location filename="../View/Geometry/Window.py" line="139"/> + <source>Ouvrir un fichier</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="513"/> - <source>Conditions initiales</source> + <location filename="../View/Geometry/Window.py" line="139"/> + <source>Fichiers .ST (*.ST)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="556"/> - <source>Solveur MAGE</source> + <location filename="../View/Geometry/Window.py" line="139"/> + <source>Fichiers .M (*.M)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="561"/> - <source>Stop Solveur</source> + <location filename="../View/Geometry/Window.py" line="139"/> + <source>Tous les fichiers (*)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="566"/> - <source>Afficher les listings</source> + <location filename="../View/Geometry/Window.py" line="322"/> + <source>Kp : </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="576"/> - <source>Ouvrir</source> + <location filename="../View/Geometry/Window.py" line="464"/> + <source>Files .ST(*.ST or *.st)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="586"/> - <source>Hydrogramme</source> + <location filename="../View/Geometry/Window.py" line="464"/> + <source>All files (*)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="598"/> - <source>Limnigramme</source> + <location filename="../View/Geometry/PlotAC.py" line="169"/> + <source>Abscisse en travers (m)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="609"/> - <source>Ligne d'eau</source> + <location filename="../View/Geometry/PlotKPC.py" line="66"/> + <source>Cote (m)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="614"/> - <source>Ligne d'eau finale</source> + <location filename="../View/Geometry/PlotAC.py" line="181"/> + <source>Profil suivant</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="619"/> - <source>Ligne d'eau enveloppe</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="314"/> + <source>Jeu de sections du Bief</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="650"/> - <source>Voir l'animation (MAGE)</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="316"/> + <source> +Ordre des sections : Amont --> Aval</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="666"/> - <source>Cartographier le bief courant</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="320"/> + <source>Pk = </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="671"/> - <source>Aide de PAMHYR</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="322"/> + <source> Nouveau profil</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="676"/> - <source>Aide de MAGE</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="331"/> + <source> Trier les profils par ordre croissant des Pk</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="702"/> - <source>enregistrer_etude_en_cours</source> - <translation type="unfinished"></translation> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="338"/> + <source>Name</source> + <translation>Nom</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="717"/> - <source>fermer_etude_en_cours</source> - <translation type="unfinished"></translation> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="340"/> + <source>Type</source> + <translation>Type</translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="732"/> - <source>quitter_application</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="342"/> + <source>Alt+Z</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="747"/> - <source>lancer_solveur</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="345"/> + <source>Alt+E</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="765"/> - <source>interrompt_simulation_en_cours</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="348"/> + <source>Alt+R</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="780"/> - <source>lancer_mailleur_externe</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="352"/> + <source>Vue globale automatique (Alt+S)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="795"/> - <source>afficher_listings_simulation</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="356"/> + <source>Vue globale automatique (Alt+D)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="798"/> - <source>Aficher les listings de la simulation courante</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="360"/> + <source>Vue globale automatique (Alt+F)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="831"/> - <source>Maillage</source> - <translation type="unfinished"></translation> + <location filename="../View/Geometry/PlotXY.py" line="62"/> + <source>X (m)</source> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="839"/> - <source>Cond. Limites</source> - <translation type="unfinished"></translation> + <location filename="../View/Geometry/PlotXY.py" line="66"/> + <source>Y (m)</source> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="874"/> - <source>Frottements</source> - <translation type="unfinished"></translation> + <location filename="../View/InitialConditions/PlotDischarge.py" line="48"/> + <source>KP (m)</source> + <translation></translation> </message> <message> - <location filename="../view/ui/MainWindow_old.ui" line="882"/> - <source>Ouvrages</source> - <translation type="unfinished"></translation> + <location filename="../View/InitialConditions/PlotDischarge.py" line="44"/> + <source>Discharge (m^3/s)</source> + <translation>Débit (m³/s)</translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="546"/> - <source>Édition des Tronçons</source> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="125"/> + <source>Choisissez un nom de fichier à sauvegarder</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="693"/> - <source>Ouvrir une étude</source> + <location filename="../View/Geometry/Window.py" line="164"/> + <source>Édition des profils sélectionnés</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="705"/> - <source>Enrégistrer étude en cours (Ctrl+S)</source> + <location filename="../View/Geometry/Window.py" line="166"/> + <source>Vous avez sélectionné plus de 5 profils. +Seuls les 5 premiers seront édités.</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="720"/> - <source>Fermer étude en cours (Ctrl+F)</source> + <location filename="../View/Geometry/Window.py" line="322"/> + <source>Profil N° : </source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="102"/> - <source>&Réseau</source> + <location filename="../View/Geometry/PlotAC.py" line="179"/> + <source>Profil précédent</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="108"/> - <source>&Géométrie</source> + <location filename="../View/Geometry/PlotAC.py" line="180"/> + <source>Profil sélectionné</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="140"/> - <source>&Exécuter</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="321"/> + <source> Importer une géométrie</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="144"/> - <source>Sous-étude Rubar3</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="323"/> + <source> Supprimer le profil sélectionné</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="314"/> - <source>Nouvelle étude MAGE</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="324"/> + <source> Éditer le profil sélectionné</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="337"/> - <source>Nouvelle étude RubarBE</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="325"/> + <source> Copier le profil sélectionné</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="358"/> - <source>Importer un jeu de données MAGE</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="326"/> + <source>Coller le profil en fin de liste (penser à modifier le Pk avant de trier)</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="366"/> - <source>Importer un jeu de données RubarBE</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="329"/> + <source> Dupliquer la section sélectionnée</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="437"/> - <source> Éditer le réseau</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="332"/> + <source> Trier les profils par ordre décroissant des Pk</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="442"/> - <source>Éditer la géométrie </source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="333"/> + <source> Changer l'ordre des profils (en décalant le profil sélectionné vers le haut)</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="447"/> - <source>Importer une géométrie</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="335"/> + <source> Changer l'ordre des profils (en décalant le profil sélectionné vers le bas)</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="452"/> - <source>Exporter la géométrie</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="337"/> + <source> Terminer l'édition</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="467"/> - <source>Visualiser la géométrie maillée</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="343"/> + <source>Vue isométrique (Alt+Z)</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="497"/> - <source>Paramètres numériques du solveur MAGE</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="346"/> + <source>Vue isométrique (Alt+E)</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="521"/> - <source>Activer/Désactiver l'export des conditions initiales</source> + <location filename="../View/Geometry/mainwindow_ui_reach.py" line="349"/> + <source>Vue isométrique (Alt+R)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="526"/> - <source>Importer l'état final comme état initial</source> + <message> + <location filename="../View/InitialConditions/PlotDKP.py" line="44"/> + <source>Elevation (m)</source> + <translation type="unfinished">Altitude (m)</translation> + </message> +</context> +<context> + <name>Network</name> + <message> + <location filename="../View/Network/GraphWidget.py" line="809"/> + <source>Add node</source> + <translation>Ajouter un nÅ“ud</translation> + </message> + <message> + <location filename="../View/Network/GraphWidget.py" line="817"/> + <source>Delete the node</source> + <translation>Supprimer un nÅ“ud</translation> + </message> + <message> + <location filename="../View/Network/GraphWidget.py" line="818"/> + <source>Disable the node</source> + <translation>Déactiver un nÅ“ud</translation> + </message> + <message> + <location filename="../View/Network/GraphWidget.py" line="826"/> + <source>Delete the reach</source> + <translation>Supprimer un bief</translation> + </message> + <message> + <location filename="../View/Network/GraphWidget.py" line="829"/> + <source>Disable the reach</source> + <translation>Déactiver un bief</translation> + </message> + <message> + <location filename="../View/Network/GraphWidget.py" line="832"/> + <source>Enable the reach</source> + <translation>Activer un bief</translation> + </message> + <message> + <location filename="../View/Network/GraphWidget.py" line="835"/> + <source>Reverse the reach orientation</source> + <translation>Inverser l'orientation du bief</translation> + </message> +</context> +<context> + <name>Results</name> + <message> + <location filename="../View/Results/PlotXY.py" line="72"/> + <source>X (m)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="531"/> - <source>Édition des Frottements</source> + <message> + <location filename="../View/Results/PlotXY.py" line="76"/> + <source>Y (m)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="536"/> - <source>Édition des Apports Latéraux</source> + <message> + <location filename="../View/Results/translate.py" line="24"/> + <source>Reach name</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="541"/> - <source>Édition des déversements</source> + <message> + <location filename="../View/Results/translate.py" line="28"/> + <source>Name</source> + <translation type="unfinished">Nom</translation> + </message> + <message> + <location filename="../View/Results/translate.py" line="29"/> + <source>KP (m)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="551"/> - <source>Édition des ouvrages en travers</source> +</context> +<context> + <name>SolverParameters</name> + <message> + <location filename="../View/SolverParameters/translate.py" line="31"/> + <source>Name</source> + <translation>Nom</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="32"/> + <source>Value</source> + <translation>Valeur</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="47"/> + <source>Yes</source> + <translation>Oui</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="48"/> + <source>No</source> + <translation>Non</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="49"/> + <source>Y</source> + <translation>O</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="50"/> + <source>N</source> + <translation>N</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="55"/> + <source>Initial time (jj:hh:mm:ss)</source> + <translation>Temps initial (jj:hh:mm:ss)</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="56"/> + <source>Final time (jj:hh:mm:ss)</source> + <translation>Temps final (jj:hh:mm:ss)</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="57"/> + <source>Timestep (second)</source> + <translation>Pas de temps (en second)</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="59"/> + <source>Minimum timestep (second)</source> + <translation>Pas de temps minimal (en second)</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="60"/> + <source>Time step of writing on .TRA</source> + <translation>Pas de temps d'écriture dans le fichier .TRA</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="61"/> + <source>Time step of writing on .BIN</source> + <translation>Pas de temps d'écriture dans le fichier .BIN</translation> + </message> + <message> + <location filename="../View/SolverParameters/translate.py" line="62"/> + <source>Implicitation parameter</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="571"/> - <source>Gestion des répertoires de simulation</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="63"/> + <source>Continuity discretization type (S/L)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="624"/> - <source>Vitesse(Pk) à t fixé</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="64"/> + <source>QSJ discretization (A/B)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="629"/> - <source>Vitesse(t) à Pk fixé</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="65"/> + <source>Stop criterion iterations (G/A/R)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="634"/> - <source>Charge hydraulique(Pk) à t fixé</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="66"/> + <source>Iteration type</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="639"/> - <source>Charge hydraulique(t) à Pk fixé</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="67"/> + <source>Smoothing coefficient</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="655"/> - <source>Autres résulats MAGE</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="68"/> + <source>Maximun accepted number of CFL</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="681"/> - <source>À propos</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="69"/> + <source>Minimum water height (meter)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="750"/> - <source>Lancer le solveur pour réaliser une simulation</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="70"/> + <source>Maximun number of iterations (< 100)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="783"/> - <source>Lancer le mailleur externe sur la géométrie du bief courant</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="71"/> + <source>Timestep reduction factor</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="807"/> - <source>Réseau</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="72"/> + <source>Reduction precision factor of Z</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="810"/> - <source>Ouvrir l'éditeur de la topologie du réseau</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="73"/> + <source>Reduction precision factor of Q</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="819"/> - <source>Géométrie</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="74"/> + <source>Reduction precision factor of residue</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="822"/> - <source>Ouvrir l'éditeur de géométrie</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="75"/> + <source>Number of iteration at maximum precision</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="842"/> - <source>Ouvir l'éditeur des Conditions aux Limites & Apports Ponctuels</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="76"/> + <source>Number of iteration before switch</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="850"/> - <source>App. Latéraux</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="77"/> + <source>Maximum accepted Froude number</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="853"/> - <source>Ouvrir l'éditeur des Apports Latéraux Distribués</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="78"/> + <source>Diffluence node height balance</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="858"/> - <source>Déversements</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="79"/> + <source>Compute reach volume balance (Y/N)</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="861"/> - <source>Ouvrir l'éditeur des Déversements Latéraux</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="80"/> + <source>Maximum reach volume balance</source> <translation type="unfinished"></translation> </message> - <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="866"/> - <source>Tronçons</source> + <message> + <location filename="../View/SolverParameters/translate.py" line="81"/> + <source>Minimum reach volume to check</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Solvers</name> + <message> + <location filename="../Solver/Solvers.py" line="27"/> + <source>Generic</source> + <translation>Générique</translation> + </message> + <message> + <location filename="../Solver/Solvers.py" line="29"/> + <source>Mage version 8</source> + <translation>Mage en version 8</translation> + </message> +</context> +<context> + <name>Toolbar</name> + <message> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="157"/> + <source>Vue originale</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="160"/> + <source>Panoramique des axes avec la souris gauche, zoom avec la droite</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="162"/> + <source>Zoom</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="165"/> + <source>Vue globale automatique (Shift+X)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="163"/> + <source>Enregistrer la figure</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="869"/> - <source>Ouvrir l'éditeur des tronçons pour les frottements et Apports Latéraux</source> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="158"/> + <source>Retour à la vue précédente</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="877"/> - <source>Ouvrir l'éditeur des frottements au fond</source> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="159"/> + <source>Passer à la vue suivante</source> <translation type="unfinished"></translation> </message> <message encoding="UTF-8"> - <location filename="../view/ui/MainWindow_old.ui" line="885"/> - <source>Ouvrir l'éditeur des ouvrages (seuils, vannes, etc.), singularités et pompes</source> + <location filename="../View/Plot/navigation_toolbar_2qt.py" line="164"/> + <source>Vue isométrique (Shift+W)</source> <translation type="unfinished"></translation> </message> </context> diff --git a/src/pamhyr.py b/src/pamhyr.py index 8a832e1885e8ee15e78538720d82f8a3b7993724..55172c780e3adaa401316c8a8a8eef11ddd92b2f 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 <https://www.gnu.org/licenses/>. + # -*- 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 c71c91d199a1bd435596a053610bbcc1b971ddb5..c345fb7f691fb939df3b2effc7b3ea4d46c5c959 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 <https://www.gnu.org/licenses/>. + # -*- 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 0000000000000000000000000000000000000000..a35e4308fa47a7ed8af75bdd708b7fc1c8195e0e --- /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 <https://www.gnu.org/licenses/>. + +;; -*- 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 <https://www.gnu.org/licenses/>. +" filename)))) diff --git a/tools/ssh-run.sh b/tools/ssh-run.sh index 9c59bb9fb828aafde2dc5a345cf05674e009c24d..a7ca1af7e1422304ba46f3ed91d46f08367ab392 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"