From 6ced9749d68cd8bdb5d2bd005a301e8a552b1575 Mon Sep 17 00:00:00 2001 From: jmkerloch <53606373+jmkerloch@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:16:40 +0200 Subject: [PATCH] Features/antares xpansion ui (#254) * First version of antares-xpansion-ui * update font for console display remove QThread * refactor * add steps and simulation name support * save value of last installed and antares study directory * check if path define to init simulation name combobox * sonarcloud smell code for python variable rename * remove definition of install directory in ui + add BUILD_UI option * add nb core definition * update ui * add keepMps option * rename method and attributes * rename requirements-gui.txt to requirements-ui.txt * add ui generation in Gitlab CI * correction of command of resources.py generation * removed unused github workflow * update doc * Correction of run if keepMps not checked and small refactor * change to UTF8 encoding * add line at end of file * dos2unix for requirements-tests.txt and requirements-ui.txt * Convert requirements.txt to ASCII Using iconv and dos2unix Co-authored-by: Florian OMNES --- .../workflows/centos7-system-deps-build.yml | 3 +- .github/workflows/centos7-system.yml | 3 +- .../workflows/centos7-system_boost_1_75.yml | 140 -------- .github/workflows/releases-centos7.yml | 3 +- .github/workflows/releases-ubuntu.yml | 3 +- .github/workflows/releases-windows.yml | 3 +- .../workflows/ubuntu-system-deps-build.yml | 3 +- .github/workflows/ubuntu-system.yml | 3 +- .../workflows/windows-vcpkg-deps-build.yml | 3 +- .github/workflows/windows-vcpkg.yml | 3 +- CMakeLists.txt | 1 + docs/build/3-Build.md | 1 + docs/build/6-ui-creation.md | 19 ++ mkdocs.yml | 1 + requirements-tests.txt | Bin 108 -> 51 bytes requirements-ui.txt | 2 + requirements.txt | Bin 54 -> 24 bytes src/CMakeLists.txt | 16 +- src/python/antares-xpansion-ui.py | 300 ++++++++++++++++++ src/python/config-ui.yaml.in | 1 + src/python/images.qrc | 7 + src/python/images/loading.gif | Bin 0 -> 2608 bytes src/python/images/play-48.png | Bin 0 -> 616 bytes src/python/images/stop-48.png | Bin 0 -> 111 bytes src/python/requirements.txt | Bin 54 -> 68 bytes 25 files changed, 364 insertions(+), 151 deletions(-) delete mode 100644 .github/workflows/centos7-system_boost_1_75.yml create mode 100644 docs/build/6-ui-creation.md create mode 100644 requirements-ui.txt create mode 100644 src/python/antares-xpansion-ui.py create mode 100644 src/python/config-ui.yaml.in create mode 100644 src/python/images.qrc create mode 100644 src/python/images/loading.gif create mode 100644 src/python/images/play-48.png create mode 100644 src/python/images/stop-48.png diff --git a/.github/workflows/centos7-system-deps-build.yml b/.github/workflows/centos7-system-deps-build.yml index ea8da7819..da0a1bbfb 100644 --- a/.github/workflows/centos7-system-deps-build.yml +++ b/.github/workflows/centos7-system-deps-build.yml @@ -49,6 +49,7 @@ jobs: - name: Install dependencies run: | pip3 install -r requirements-tests.txt + pip3 install -r requirements-ui.txt - name: Configure run: | @@ -57,7 +58,7 @@ jobs: source /opt/rh/rh-git227/enable export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install + cmake3 -B _build -S . -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/centos7-system.yml b/.github/workflows/centos7-system.yml index d3d43fc3f..c7eeb353e 100644 --- a/.github/workflows/centos7-system.yml +++ b/.github/workflows/centos7-system.yml @@ -91,13 +91,14 @@ jobs: - name: Install dependencies run: | pip3 install -r requirements-tests.txt + pip3 install -r requirements-ui.txt - name: Configure run: | source /opt/rh/devtoolset-7/enable export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . -DDEPS_INSTALL_DIR=/rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF + cmake3 -B _build -S . -DDEPS_INSTALL_DIR=/rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/centos7-system_boost_1_75.yml b/.github/workflows/centos7-system_boost_1_75.yml deleted file mode 100644 index 1617fa145..000000000 --- a/.github/workflows/centos7-system_boost_1_75.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Centos7 CI (boost 1_75) - -on: - push: - branches: - - main - - develop - - features/* - - feature/* - - fix/* - - release/* -jobs: - - build: - - runs-on: ubuntu-latest - container: 'centos:7' - - steps: - - - name: Set up Python - run: | - yum update -y - yum install -y python3 python3-pip - pip3 install --upgrade pip - - - name: Install libraries - run: | - yum install -y wget git epel-release redhat-lsb-core gcc gcc-c++ make centos-release-scl scl-utils - yum install -y jsoncpp-devel gtest-devel openmpi-devel doxygen graphviz - yum install -y cmake3 devtoolset-7 environment-modules rpm-build zlib-devel - - - - uses: nelonoel/branch-name@v1.0.1 - - - name: Checkout - run: | - git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git -b ${BRANCH_NAME} . - - - name: Read antares-solver version - id: antares-version - uses: notiz-dev/github-action-json-property@release - with: - path: 'antares-version.json' - prop_path: 'antares_version' - - - name: Read antares-xpansion version - id: antares-xpansion-version - uses: notiz-dev/github-action-json-property@release - with: - path: 'antares-version.json' - prop_path: 'antares_xpansion_version' - - - name: Read antares-deps version - id: antares-deps-version - uses: notiz-dev/github-action-json-property@release - with: - path: 'antares-version.json' - prop_path: 'antares_deps_version' - - - name: Download pre-compiled librairies - uses: ./.github/workflows/download-extract-precompiled-libraries-tgz - with: - antares-deps-version: ${{steps.antares-deps-version.outputs.prop}} - antares-version: ${{steps.antares-version.outputs.prop}} - os: centos7 - os-full-name: CentOS-7.9.2009 - - - name: Init submodule - run: | - git submodule update --init --recursive . - - - name: Install dependencies - run: | - pip3 install -r requirements-tests.txt - - - name: Install Boost 1_75_0 - run: | - source /opt/rh/devtoolset-7/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - wget https://boostorg.jfrog.io/artifactory/main/release/1.75.0/source/boost_1_75_0.tar.gz - tar xvfz boost_1_75_0.tar.gz - cd boost_1_75_0/ - ./bootstrap.sh --prefix=../opt/boost_1_75_0 --without-libraries=python - echo "using mpi ;" >>project-config.jam - ./b2 install - - - name: Configure - run: | - source /opt/rh/devtoolset-7/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_PREFIX_PATH=./opt/boost_1_75_0 -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF - - - name: Build - run: | - source /opt/rh/devtoolset-7/enable - export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH - export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 --build _build --config Release -j2 --target install - - - name: Running unit tests - run: | - cd _build - ctest3 -C Release --output-on-failure -L "unit|benders" - - - id: create-single-file - name: Single file .tar.gz creation - uses: ./.github/workflows/single-file-creation-tgz - with: - antares-xpansion-version: ${{steps.antares-xpansion-version.outputs.prop}} - - - name: Installer .rpm creation - run: | - cd _build - cpack3 -G RPM - - - name: Installer .tar.gz creation - run: | - cd _build - cpack3 -G TGZ - - - name: Installer archive upload - uses: actions/upload-artifact@v2 - with: - name: antares-xpansion-centos7-archive - path: _build/*.tar.gz - - - name: Installer rpm upload - uses: actions/upload-artifact@v2 - with: - name: antares-xpansion-centos7-rpm - path: _build/*.rpm - - - name: Single file upload - uses: actions/upload-artifact@v2 - with: - name: antares-xpansion-centos-single-file - path: ${{ steps.create-single-file.outputs.archive-path }} diff --git a/.github/workflows/releases-centos7.yml b/.github/workflows/releases-centos7.yml index 16a75cd1e..df7a3d8fa 100644 --- a/.github/workflows/releases-centos7.yml +++ b/.github/workflows/releases-centos7.yml @@ -87,13 +87,14 @@ jobs: - name: Install dependencies run: | pip3 install -r requirements-tests.txt + pip3 install -r requirements-ui.txt - name: Configure run: | source /opt/rh/devtoolset-7/enable export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH export PATH=/usr/lib64/openmpi/bin:$PATH - cmake3 -B _build -S . -DDEPS_INSTALL_DIR=/rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF + cmake3 -B _build -S . -DDEPS_INSTALL_DIR=/rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/releases-ubuntu.yml b/.github/workflows/releases-ubuntu.yml index d544b27ef..fc6e98e07 100644 --- a/.github/workflows/releases-ubuntu.yml +++ b/.github/workflows/releases-ubuntu.yml @@ -28,6 +28,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements-tests.txt + pip install -r requirements-ui.txt - name: Get release id: get_release @@ -76,7 +77,7 @@ jobs: - name: Configure run: | - cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF + cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/releases-windows.yml b/.github/workflows/releases-windows.yml index e349f0823..d2cf3c317 100644 --- a/.github/workflows/releases-windows.yml +++ b/.github/workflows/releases-windows.yml @@ -52,6 +52,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements-tests.txt + pip install -r requirements-ui.txt - name: Get release id: get_release @@ -105,7 +106,7 @@ jobs: - name: Configure with VCPKG run: | $pwd=Get-Location - cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DCMAKE_PREFIX_PATH="$pwd\rte-antares-${{steps.antares-version.outputs.prop}}-installer-64bits" -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_TOOLCHAIN_FILE="${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF + cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DCMAKE_PREFIX_PATH="$pwd\rte-antares-${{steps.antares-version.outputs.prop}}-installer-64bits" -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_TOOLCHAIN_FILE="${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/ubuntu-system-deps-build.yml b/.github/workflows/ubuntu-system-deps-build.yml index 1341a3763..ca2158070 100644 --- a/.github/workflows/ubuntu-system-deps-build.yml +++ b/.github/workflows/ubuntu-system-deps-build.yml @@ -34,6 +34,7 @@ jobs: run: | python -m pip install --upgrade pip pip3 install -r requirements-tests.txt + pip3 install -r requirements-ui.txt - name: Install libraries run: | @@ -52,7 +53,7 @@ jobs: - name: Configure run: | - cmake -B _build -S . -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install + cmake -B _build -S . -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/ubuntu-system.yml b/.github/workflows/ubuntu-system.yml index a6726ffa4..d80bba0c8 100644 --- a/.github/workflows/ubuntu-system.yml +++ b/.github/workflows/ubuntu-system.yml @@ -36,6 +36,7 @@ jobs: run: | python -m pip install --upgrade pip pip3 install -r requirements-tests.txt + pip3 install -r requirements-ui.txt - name: Install libraries run: | @@ -76,7 +77,7 @@ jobs: - name: Configure run: | - cmake -B _build -S . -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF + cmake -B _build -S . -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/windows-vcpkg-deps-build.yml b/.github/workflows/windows-vcpkg-deps-build.yml index 4d5a85ac2..957bfa16a 100644 --- a/.github/workflows/windows-vcpkg-deps-build.yml +++ b/.github/workflows/windows-vcpkg-deps-build.yml @@ -54,6 +54,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements-tests.txt + pip install -r requirements-ui.txt - name: Pre-requisites shell: cmd @@ -82,7 +83,7 @@ jobs: - name: Configure run: | $pwd=Get-Location - cmake -B _build -S . -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=true -DCMAKE_TOOLCHAIN_FILE="vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install + cmake -B _build -S . -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=true -DCMAKE_TOOLCHAIN_FILE="vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_UI=ON - name: Build run: | diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index ed94b7514..aed399d61 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -57,6 +57,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements-tests.txt + pip install -r requirements-ui.txt - name: Pre-requisites shell: cmd @@ -106,7 +107,7 @@ jobs: - name: Configure run: | $pwd=Get-Location - cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DCMAKE_PREFIX_PATH="$pwd\rte-antares-${{steps.antares-version.outputs.prop}}-installer-64bits" -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_TOOLCHAIN_FILE="${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF + cmake -B _build -S . -DDEPS_INSTALL_DIR=rte-antares-deps-Release -DCMAKE_PREFIX_PATH="$pwd\rte-antares-${{steps.antares-version.outputs.prop}}-installer-64bits" -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON -DCMAKE_TOOLCHAIN_FILE="${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} -DCMAKE_INSTALL_PREFIX=_install -DBUILD_not_system=OFF -DBUILD_antares_solver=OFF -DBUILD_UI=ON - name: Build run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 78d10d86f..2afff63f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,6 +293,7 @@ endif(COIN_OR) # =========================================================================== # Targets # =========================================================================== +option(BUILD_UI "Build antares-xpansion ui" OFF) option(BUILD_TESTING "Activates unit tests building" OFF) if(WIN32) diff --git a/docs/build/3-Build.md b/docs/build/3-Build.md index 28f4ca812..f8c8ef21d 100644 --- a/docs/build/3-Build.md +++ b/docs/build/3-Build.md @@ -41,6 +41,7 @@ Here is a list of available CMake configure option : |`BUILD_ALL`|`OFF`|Enable build of ALL external librairies| |`DEPS_INSTALL_DIR`|`../rte-antares-deps-`|Define dependencies libraries install directory| |`BUILD_TESTING`|`OFF`|Enable test build| +|`BUILD_UI`|`OFF`|Enable UI build| Additionnal options for windows diff --git a/docs/build/6-ui-creation.md b/docs/build/6-ui-creation.md new file mode 100644 index 000000000..06559656b --- /dev/null +++ b/docs/build/6-ui-creation.md @@ -0,0 +1,19 @@ +# User Interface creation +A PyQt5 User Interface (UI) is available : `src\python\antares-xpansion-ui.py` + +To generate needed resources `BUILD_UI` option must be enabled. + +## Install requirements +``` +pip3 install -r requirements-ui.txt +``` + +## Force new generation for resource file +A `resource.py` python module is created when configuring CMake. + +To force the regeneration of this file if images are changed, use `pyrcc5` +``` +cd src\python +pyrcc5 images.qrc > resources.py +``` + diff --git a/mkdocs.yml b/mkdocs.yml index 041a7211a..c2b3021db 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,7 @@ nav: - 'Dependencies install': 'build/2-Dependencies-install.md' - 'Build': 'build/3-Build.md' - 'Tests': 'build/4-Tests.md' + - 'UI creation': 'build/6-ui-creation.md' - 'Installer creation': 'build/5-Installer-creation.md' - 'Changelog': 'CHANGELOG.md' diff --git a/requirements-tests.txt b/requirements-tests.txt index bcff359e8f12ffe4ea403f271c7cf5af718ece94..b18828cf6e7855f2801e92c87a0ea5c3b2668264 100644 GIT binary patch literal 51 zcmdNbQYcC-EX^!R%}vcKDb_2gC{f@ls4Ph>F5${6%`K?pD#>(U844Ln88X4FTp%xxp@gBBK@Uh)05LBE7efI Yq9EB4hI}ADnIQv1tu9bBpP`Ha0NaEV?f?J) diff --git a/requirements-ui.txt b/requirements-ui.txt new file mode 100644 index 000000000..7984510f3 --- /dev/null +++ b/requirements-ui.txt @@ -0,0 +1,2 @@ +-r requirements.txt +PyQt5 diff --git a/requirements.txt b/requirements.txt index e2e4d55df646f5afa1d8047ffec9ef810f281761..f9c2c097426f5aa40381380a970f54509861967f 100644 GIT binary patch literal 24 fcmXS@%*-n;NzBPfEwZyU(KF^MsH{xP&EWz7auo=8 literal 54 zcmezWuYjSFA(J7Gp_rkBA(0^m2vZq~80;8q8B7@T7>pTs8MqigYAS)Uxj : need to be done with generate instead of configure because values are not available at configure time file(GENERATE OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/python/config.yaml INPUT ${CMAKE_CURRENT_SOURCE_DIR}/python/config.yaml) +if (BUILD_UI) + execute_process(COMMAND pyrcc5 images.qrc + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/python/ + OUTPUT_FILE resources.py) + + #configure file to define install directory + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/python/config-ui.yaml.in ${CMAKE_CURRENT_SOURCE_DIR}/python/config-ui.yaml) +endif() + find_program(PYINSTALLER "pyinstaller") #if pyinstaller available add compiled version of python scripts if (PYINSTALLER) - if (MSVC) install(CODE "execute_process(COMMAND ${PYINSTALLER} -F ${CMAKE_CURRENT_SOURCE_DIR}/python/launch.py -n antares-xpansion-launcher --add-data \"${CMAKE_CURRENT_SOURCE_DIR}/python/config.yaml;.\")" ) else() install(CODE "execute_process(COMMAND ${PYINSTALLER} -F ${CMAKE_CURRENT_SOURCE_DIR}/python/launch.py -n antares-xpansion-launcher --add-data \"${CMAKE_CURRENT_SOURCE_DIR}/python/config.yaml:.\")" ) endif() + if (BUILD_UI) + install(CODE "execute_process(COMMAND ${PYINSTALLER} -F ${CMAKE_CURRENT_SOURCE_DIR}/python/antares-xpansion-ui.py -n antares-xpansion-ui)" ) + endif() + #code is executed in CMAKE_BINARY_DIR install(DIRECTORY ${CMAKE_BINARY_DIR}/dist/ DESTINATION . @@ -45,5 +57,5 @@ else () install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/python/" DESTINATION xpansion-launcher USE_SOURCE_PERMISSIONS) - + endif() diff --git a/src/python/antares-xpansion-ui.py b/src/python/antares-xpansion-ui.py new file mode 100644 index 000000000..f6cf65e35 --- /dev/null +++ b/src/python/antares-xpansion-ui.py @@ -0,0 +1,300 @@ +import os + +import yaml +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QFont, QMovie, QIcon +from PyQt5.QtWidgets import QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, \ + QHBoxLayout, QWidget, QFileDialog, QRadioButton, QSpacerItem, QSizePolicy, QPlainTextEdit, QMessageBox, QGridLayout, \ + QComboBox, QGroupBox, QSpinBox, QCheckBox +from PyQt5.QtCore import QProcess, QByteArray, QSettings +from pathlib import Path + +import resources + +STEP_WITH_SIMULATION_NAME = ["getnames", "lp", "optim", "update"] +NEW_SIMULATION_NAME = "New" +LAST_ANTARES_STUDY_DIR = "last_antares_study_dir" + + +class MainWidget(QWidget): + + def __init__(self, parent=None): + QWidget.__init__(self, parent=parent) + + self.setWindowTitle('antares-xpansion-ui') + + self._define_install_dir() + + self._settings = QSettings("pyqt_settings.ini", QSettings.IniFormat) + + self._main_layout = QVBoxLayout() + + self._init_antares_study_selection_widget() + self._init_antares_xpansion_run_widget() + self._init_log_widget() + + self.setLayout(self._main_layout) + + self._check_run_availability() + + def _define_install_dir(self): + self._install_dir = Path("bin") + if Path('config-ui.yaml').is_file(): + with open('config-ui.yaml') as file: + content = yaml.full_load(file) + if content is not None: + self._install_dir = content.get('INSTALL_DIR', "bin") + + def _init_log_widget(self): + log_layout = QHBoxLayout() + self._log_text_edit = QPlainTextEdit() + self._log_text_edit.setReadOnly(True) + font = QFont() + font.setFamily("DejaVu Sans Mono") + self._log_text_edit.setFont(font) + log_layout.addWidget(self._log_text_edit) + self._main_layout.addLayout(log_layout) + + def _init_antares_xpansion_run_widget(self): + + self._xpansion_config_layout = QHBoxLayout() + + self._init_step_selection_widget() + self._init_xpansion_config_widget() + + self._xpansion_config_layout.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Fixed)) + self._init_xpansion_run_widget() + + self._main_layout.addLayout(self._xpansion_config_layout) + + def _init_xpansion_config_widget(self): + + method_layout = QHBoxLayout() + self._sequential_radio_button = QRadioButton('Sequential') + self._sequential_radio_button.setChecked(True) + self._sequential_radio_button.toggled.connect(self._method_changed) + method_layout.addWidget(self._sequential_radio_button) + self._mpibenders_radio_button = QRadioButton('Parallel') + self._mpibenders_radio_button.toggled.connect(self._method_changed) + method_layout.addWidget(self._mpibenders_radio_button) + method_layout.addWidget(QLabel("core number")) + self._nb_core_edit = QSpinBox() + self._nb_core_edit.setMinimum(2) + self._nb_core_edit.setMaximum(128) + self._nb_core_edit.setValue(os.cpu_count()) + self._nb_core_edit.setEnabled(False) + method_layout.addWidget(self._nb_core_edit) + nb_cpu_label = QLabel( + "available core {nb_cpu}".format( + nb_cpu=os.cpu_count())) + nb_cpu_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse) + nb_cpu_label.linkActivated.connect(self._use_available_core) + method_layout.addWidget(nb_cpu_label) + method_gb = QGroupBox("Method") + method_gb.setLayout(method_layout) + self._xpansion_config_layout.addWidget(method_gb) + + option_gb = QGroupBox("Options") + option_layout = QVBoxLayout() + self._keep_mps_checkbox = QCheckBox("Keep intermediate files") + self._keep_mps_checkbox.setChecked(False) + option_layout.addWidget(self._keep_mps_checkbox) + option_gb.setLayout(option_layout) + self._xpansion_config_layout.addWidget(option_gb) + + def _init_xpansion_run_widget(self): + self._running_label = QLabel() + self.movie = QMovie(":/images/loading.gif", QByteArray()) + self._running_label.setMovie(self.movie) + self.movie.start() + self._running_label.setVisible(False) + self._xpansion_config_layout.addWidget(self._running_label) + self._run_process = QProcess() + self._run_process.readyReadStandardOutput.connect(self._handle_stdout) + self._run_process.readyReadStandardError.connect(self._handle_stderr) + self._run_process.stateChanged.connect(self._handle_state) + self._run_process.finished.connect(self._cleanup_run_process) + self._run_button = QPushButton('Run') + self._set_run_label() + self._run_button.clicked.connect(self._run_or_stop) + self._xpansion_config_layout.addWidget(self._run_button) + + def _init_antares_study_selection_widget(self): + layout_study_path = QGridLayout() + + layout_study_path.addWidget(QLabel('Antares study path'), 0, 0) + self._study_path_text_edit = QLineEdit(self._settings.value(LAST_ANTARES_STUDY_DIR)) + layout_study_path.addWidget(self._study_path_text_edit, 0, 1) + select_button = QPushButton('...') + select_button.clicked.connect(self._select_study_path) + layout_study_path.addWidget(select_button, 0, 2) + + self._combo_simulation_name = QComboBox() + self._combo_simulation_name.currentTextChanged.connect(self._simulation_name_changed) + layout_study_path.addWidget(QLabel('Simulation name'), 1, 0) + layout_study_path.addWidget(self._combo_simulation_name, 1, 1) + self._init_simulation_name_combo(self._settings.value(LAST_ANTARES_STUDY_DIR)) + + self._main_layout.addLayout(layout_study_path) + + def _init_step_selection_widget(self): + step_layout = QHBoxLayout() + + steps = ["full", "antares", "getnames", "lp", "optim", "update"] + self._step_buttons = {} + for step in steps: + self._step_buttons[step] = QRadioButton(step) + self._step_buttons[step].setEnabled(step not in STEP_WITH_SIMULATION_NAME) + step_layout.addWidget(self._step_buttons[step]) + + self._step_buttons["full"].setChecked(True) + + self._step_gb = QGroupBox("Steps") + self._step_gb.setLayout(step_layout) + self._xpansion_config_layout.addWidget(self._step_gb) + + def set_study_path(self, study_path: str): + self._settings.setValue(LAST_ANTARES_STUDY_DIR, study_path) + self._study_path_text_edit.setText(study_path) + self._init_simulation_name_combo(study_path) + self._check_run_availability() + + def _init_simulation_name_combo(self, study_path: str): + if study_path: + self._combo_simulation_name.blockSignals(True) + self._combo_simulation_name.clear() + self._combo_simulation_name.addItem(NEW_SIMULATION_NAME) + self._insert_study_simulation_in_combobox(study_path) + self._combo_simulation_name.blockSignals(False) + + def _insert_study_simulation_in_combobox(self, study_path): + output_path = Path(study_path) / 'output' + if output_path.is_dir(): + for directory in sorted(output_path.iterdir(), key=os.path.getmtime, reverse=True): + if (output_path / directory).is_dir(): + self._combo_simulation_name.addItem(directory.name) + + def _select_study_path(self): + self.set_study_path( + QFileDialog.getExistingDirectory(self, 'Select study folder', self._study_path_text_edit.text())) + + def _check_run_availability(self): + study_path = self._study_path_text_edit.text() + run_available = Path(study_path).is_dir() + self._run_button.setEnabled(run_available) + self._step_gb.setEnabled(run_available) + + def _get_method(self): + if self._mpibenders_radio_button.isChecked(): + return "mpibenders" + if self._sequential_radio_button.isChecked(): + return "sequential" + + def _get_step(self): + for step in self._step_buttons: + if self._step_buttons[step].isChecked(): + return step + + def _get_nb_core(self): + return self._nb_core_edit.value() + + def _get_keep_mps(self): + return self._keep_mps_checkbox.isChecked() + + def _handle_stdout(self): + data = self._run_process.readAllStandardOutput() + stdout = bytes(data).decode("utf8") + self._add_text_to_log(stdout) + + def _handle_stderr(self): + data = self._run_process.readAllStandardError() + stderr = bytes(data).decode("utf8") + self._add_text_to_log(stderr) + + def _handle_state(self, state): + if state == QProcess.NotRunning: + self._set_run_label() + if self._get_step() not in STEP_WITH_SIMULATION_NAME: + self._init_simulation_name_combo(self._study_path_text_edit.text()) + else: + self._set_stop_label() + + def _method_changed(self): + self._nb_core_edit.setEnabled(self._mpibenders_radio_button.isChecked()) + + def _use_available_core(self): + self._nb_core_edit.setValue(os.cpu_count()) + + def _set_stop_label(self): + self._run_button.setText("Stop") + self._run_button.setIcon(QIcon(":/images/stop-48.png")) + self._running_label.setVisible(True) + + def _set_run_label(self): + self._run_button.setText("Run") + self._run_button.setIcon(QIcon(":/images/play-48.png")) + self._running_label.setVisible(False) + + def _simulation_name_changed(self, text): + for step in self._step_buttons: + if text == NEW_SIMULATION_NAME: + self._step_buttons[step].setEnabled(step not in STEP_WITH_SIMULATION_NAME) + else: + self._step_buttons[step].setEnabled(step in STEP_WITH_SIMULATION_NAME) + + def _add_text_to_log(self, s): + self._log_text_edit.appendPlainText(s.rstrip('\r\n')) + + def _cleanup_run_process(self): + self._run_process.close(); + + def _run_or_stop(self): + if self._run_process and self._run_process.isOpen(): + qm = QMessageBox() + ret = qm.question(self, "Stop simulation", "Do you want to stop current simulation ?") + if ret == qm.Yes: + self._run_process.close() + else: + self._run() + + def _run(self): + self._log_text_edit.clear() + study_path = self._study_path_text_edit.text() + if study_path: + if Path(study_path).is_dir(): + self._run_study(study_path) + else: + self._add_text_to_log("Study path is not a directory") + else: + self._add_text_to_log("No study path defined") + + def _run_study(self, study_path): + install_dir = self._install_dir + install_dir_path = Path(install_dir) + if install_dir_path.is_dir(): + install_dir_full = str(install_dir_path.resolve()) + program = "antares-xpansion-launcher.exe" + commands = ["--installDir", install_dir_full, + "--dataDir", str(study_path), + "--method", self._get_method(), + "--step", self._get_step(), + "-n", str(self._get_nb_core())] + if self._get_keep_mps(): + commands.append("--keepMps") + if not self._step_buttons["full"].isChecked(): + commands.append("--simulationName") + commands.append(self._combo_simulation_name.currentText()) + if Path("launch.py").is_file(): + commands.insert(0, "launch.py") + program = "python" + self._run_process.start(program, commands) + self._set_stop_label() + else: + self._add_text_to_log("Error in install directory definition.") + + +app = QApplication([]) + +window = MainWidget() +window.show() +app.exec() diff --git a/src/python/config-ui.yaml.in b/src/python/config-ui.yaml.in new file mode 100644 index 000000000..b1b7a6cc6 --- /dev/null +++ b/src/python/config-ui.yaml.in @@ -0,0 +1 @@ +INSTALL_DIR : ${CURRENT_RUNTIME_OUTPUT_DIRECTORY} diff --git a/src/python/images.qrc b/src/python/images.qrc new file mode 100644 index 000000000..dfefd8bf5 --- /dev/null +++ b/src/python/images.qrc @@ -0,0 +1,7 @@ + + + images/loading.gif + images/play-48.png + images/stop-48.png + + diff --git a/src/python/images/loading.gif b/src/python/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..7c12c21fcb05ccbc90eb90202c96fb4578f8066d GIT binary patch literal 2608 zcmdVcdr(tX9tZGCZgP`cV{Tp-NWvpvKt|wv@-B&^pMYg=krz)&_+_N{pyjc|;8*19IJuz|8@Xac3z#I4;0G5`P005Yqn;RP&>+S9B=;&x_YO>jE27{r#zTR%P zTdh{TUf`T5Ma4#l}?>0I+(4~ic~qOyf^o%GJ~@BX!Zg=&&UAm z+}USoRYG)1G8QS14h;$cU7SlBmJhUy06}sZEy`A?|oBLVej0 z{z`KDk;1@TJv%DaZru@bU9(YON(Ti`4DSo!U;&V?$SOb&u1bRU(u)*K1ZDvwH@-l7 zk^x6$7Sq`rl@RhQQIk{hfrbOS@~M(kHJf_?`)brZ5q+td(cY#P4vfnwNl>m$%KJ){OneHJ4@0=g9!DrUy~?1lPl zRal?ki+4uXzL6}p=jbT6S+UU~ z7G>+Qo12-ER3)3M!@N8{Mm+$M=FF!U)Po`*{-*&9(M|^9|hccTp|4A zL~jpoW@1WtZ-RwyyX%1Q_*)JHPq^x+QYL;r!O_SwRHtj-l(YrX5?b}O<_b5T`YGKa z4`#XemQ1B5nTFWnw*~xk?ih8%y}DX3d^&^5lx--mbnc_aFb7MkZH;BA4*MxakR#7C zuDj(L_vJ|EeR_VcNdFt4{clD6yN{ZYjs2)|TtQALs?EvPybk;z!#$&|W^2rD-HY;kGHTWf>dB6Btm4D(K*=2tW zO-GMV81(Y?pWYeyS8r70)$t+jG^%NpP(k76x<#FzrBXs!A8VZXGVT9qWrRr zcUDZb#GhC$`Efo%VP!A%;sLa^{& z5xwRf3cQfX$s_swIkA2BZmBBGGM6Mi>>C)J*7=Yb9lDwp*9AUw)cz*NYB{>*YN|W; zrQp%nbG~h7qjyUy1623LwmQ*@6ZL0KzZZwN!Yj>|vapX>r`RAeI((Vq?bPbPy_a_$ z@!gtP=xqP`u5?iH#LzoQ3tr4m%POD)tM^C2j2(blz;}}`x0^PLu?r*Dp`MUX#bF=Q zk}+xl;B^4lRgezQa@pLECEOORGrzOO~DVbTvNx%H!PE(OYE zjY?R#(DbNHJ2gF{-FC-5p^eWSnI6wJYlo(9>`d$*9n5VUmL#uN1a0mGTB;uCZ?E6n z1%%@@B2q|F3*;BRDT;k1i0KvZB&>bRFX(jB-$sWjSt<74`dEnu+BpZYVu9%E6TX<))p&PW|b8ac{(y`#k2I zW^;qRMQ<{4DV@O5Zn@f(~Oo1Y855N0d_cC79u^?;m6m#v(EB^oIUnHYB zn_;EOijbI>9jAH>W;43lh!;HHUT<-rI^tj0?PAv5zuWU1kQf11bq<4=?K&0B%ZepmiH*k@vOoyn?xr~Hmr^XF(W{+ab(?6~_gAMT90^`#FN z)CSIBEw(bjZ6IpJ&(^|Ofs6~Z0w;ZR637om3IY zGc*ub>^SY?dJX2o(i3m<@VI17_@R9Ez=xiRA32!=rCK8Qtu!= zZJ%YIaJGM14E@oiRY4A_Jy1j8GC_+44 L{an^LB{Ts5vLC~Z literal 0 HcmV?d00001 diff --git a/src/python/images/stop-48.png b/src/python/images/stop-48.png new file mode 100644 index 0000000000000000000000000000000000000000..35e53f248d5bbd793446fdb20f373e2c6497cb72 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZA`BpB)|k7xlYrjj7PU+`vYkJ delta 4 LcmZ=Un_vb20}KH{