From c0fbf2c53de79fabad74a14f1aa833450ebf4591 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 12:15:11 +0100 Subject: [PATCH 01/49] feat: Add PyVista Qt support --- .../backends/pyvista/pyvista.py | 19 ++++++++++++++----- .../backends/pyvista/pyvista_interface.py | 11 ++++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py index a7fc01fd..96552550 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py @@ -84,6 +84,12 @@ class PyVistaBackendInterface(BaseBackend): allow_hovering : Optional[bool], default: False Whether to allow hovering capabilities in the window. Incompatible with picking. Picking will take precedence over hovering. + plot_picked_names : Optional[bool], default: False + Whether to plot the names of the picked objects. + show_plane : Optional[bool], default: False + Whether to show the plane in the plotter. + use_qt : Optional[bool], default: False + Whether to use the Qt backend for the plotter. """ def __init__( @@ -93,13 +99,14 @@ def __init__( allow_hovering: Optional[bool] = False, plot_picked_names: Optional[bool] = False, show_plane: Optional[bool] = False, + use_qt: Optional[bool] = False, **plotter_kwargs, ) -> None: """Initialize the ``use_trame`` parameter and save the current ``pv.OFF_SCREEN`` value.""" # Check if the use of trame was requested if use_trame is None: use_trame = ansys.tools.visualization_interface.USE_TRAME - + self._use_qt = use_qt self._use_trame = use_trame self._allow_picking = allow_picking self._allow_hovering = allow_hovering @@ -146,7 +153,7 @@ def __init__( logger.warning(warn_msg) self._pl = PyVistaInterface(show_plane=show_plane) else: - self._pl = PyVistaInterface(show_plane=show_plane) + self._pl = PyVistaInterface(show_plane=show_plane, use_qt=use_qt, **plotter_kwargs) self._enable_widgets = self._pl._enable_widgets @@ -175,7 +182,8 @@ def enable_widgets(self): ] self._widgets.append(MeasureWidget(self)) self._widgets.append(ScreenshotButton(self)) - self._widgets.append(MeshSliderWidget(self)) + if not self._use_qt: + self._widgets.append(MeshSliderWidget(self)) self._widgets.append(HideButton(self)) def add_widget(self, widget: Union[PlotterWidget, List[PlotterWidget]]): @@ -541,10 +549,11 @@ def __init__( use_trame: Optional[bool] = None, allow_picking: Optional[bool] = False, allow_hovering: Optional[bool] = False, - plot_picked_names: Optional[bool] = True + plot_picked_names: Optional[bool] = True, + use_qt: Optional[bool] = False ) -> None: """Initialize the generic plotter.""" - super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) + super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names, use_qt=use_qt) def plot_iter( self, diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index c8ecfe1c..c93583e6 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -58,6 +58,8 @@ class PyVistaInterface: for visualization. show_plane : bool, default: False Whether to show the XY plane in the plotter window. + use_qt : bool, default: False + Whether to use the Qt backend for the plotter window. """ @@ -68,6 +70,7 @@ def __init__( num_points: int = 100, enable_widgets: bool = True, show_plane: bool = False, + use_qt: bool = False, **plotter_kwargs, ) -> None: """Initialize the plotter.""" @@ -75,6 +78,9 @@ def __init__( if scene is None: if viz_interface.TESTING_MODE: scene = pv.Plotter(off_screen=True, **plotter_kwargs) + elif use_qt: + import pyvistaqt + scene = pyvistaqt.BackgroundPlotter() else: scene = pv.Plotter(**plotter_kwargs) # If required, use a white background with no gradient @@ -335,7 +341,10 @@ def show( if jupyter_backend: self.scene.show(jupyter_backend=jupyter_backend, **kwargs) else: - self.scene.show(**kwargs) + if self._use_qt: + self.scene.show() + else: + self.scene.show(**kwargs) def set_add_mesh_defaults(self, plotting_options: Optional[Dict]) -> None: """Set the default values for the plotting options. From 3bb9f2b170c5d2df2b62c5f80f179e4ac2555571 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 14:44:18 +0100 Subject: [PATCH 02/49] feat: Add example --- .../00-basic-pyvista-examples/qt_backend.py | 64 +++++++++++++++++++ .../backends/pyvista/pyvista_interface.py | 2 + 2 files changed, 66 insertions(+) create mode 100644 examples/00-basic-pyvista-examples/qt_backend.py diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py new file mode 100644 index 00000000..91e2596e --- /dev/null +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -0,0 +1,64 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _ref_backgroundplotter: + +======================== +Use a PyVista Qt backend +======================== + +PyVista Qt is a package that extends the PyVista functionality through the +usage of Qt. Qt applications operate in a separate thread than VTK, you can +simultaneously have an active VTK plot and a non-blocking Python session. + +This example shows how to use the PyVista Qt backend to create a plotter +""" + +import pyvista as pv + +from ansys.tools.visualization_interface import Plotter +from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend + +# Create a PyVista mesh +cube = pv.Cube() + +pv_backend = PyVistaBackend(use_qt=True) + +# Create a plotter +pl = Plotter(backend=pv_backend) + +# Add the mesh to the plotter +pl.plot(cube) + +# Show the plotter +pl.show() + +##################### +# Parallel VTK window +# =================== + +sphere = pv.Sphere() + +pl_parallel = Plotter() +pl_parallel.plot(sphere) +pl_parallel.show() diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index c93583e6..5635d28d 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -83,6 +83,8 @@ def __init__( scene = pyvistaqt.BackgroundPlotter() else: scene = pv.Plotter(**plotter_kwargs) + + self._use_qt = use_qt # If required, use a white background with no gradient if not color_opts: color_opts = dict(color="white") From 492e9fcf228f1a9fadb147871aec490d84a93383 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 14:56:05 +0100 Subject: [PATCH 03/49] test: Add test for Qt backend --- tests/test_generic_plotter.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_generic_plotter.py b/tests/test_generic_plotter.py index 77b6df0f..dac90a92 100644 --- a/tests/test_generic_plotter.py +++ b/tests/test_generic_plotter.py @@ -26,6 +26,7 @@ import pyvista as pv from ansys.tools.visualization_interface import ClipPlane, MeshObjectPlot, Plotter +from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend class CustomTestClass: @@ -44,6 +45,15 @@ def test_plotter_add_pd(): pl.show() +def test_plotter_pyvistaqt(): + """Adds polydata to the plotter.""" + qt_backend = PyVistaBackend(use_qt=True) + pl = Plotter(backend=qt_backend) + sphere = pv.Sphere() + pl.plot(sphere) + pl.show() + + def test_plotter_add_mb(): """Adds multiblock to the plotter.""" pl = Plotter() From c6208cb2408265f9511e3a6eb0619adcf945a907 Mon Sep 17 00:00:00 2001 From: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:58:12 +0000 Subject: [PATCH 04/49] chore: adding changelog file 192.added.md [dependabot-skip] --- doc/changelog.d/192.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changelog.d/192.added.md diff --git a/doc/changelog.d/192.added.md b/doc/changelog.d/192.added.md new file mode 100644 index 00000000..d1c8d379 --- /dev/null +++ b/doc/changelog.d/192.added.md @@ -0,0 +1 @@ +feat: Add PyVista Qt support \ No newline at end of file From c864bc7851ff70b783fd871c9d950a69c7f27fde Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 15:52:06 +0100 Subject: [PATCH 05/49] feat: Add PyVistaQt as optional dependency --- pyproject.toml | 15 ++++++++++++++- .../backends/pyvista/pyvista_interface.py | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 160ed22d..02f9333e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,13 +30,26 @@ dependencies = [ ] [project.optional-dependencies] -tests = ["pytest==8.3.3", "pytest-pyvista==0.1.9", "pytest-cov==6.0.0"] +pyvistaqt = [ + "pyside6 >= 6.8.0,<7", + "pyvistaqt >= 0.11.1,<1", +] +tests = [ + "pytest==8.3.3", + "pytest-pyvista==0.1.9", + "pytest-cov==6.0.0", + "pyside6 == 6.8.0,<7", + "pyvistaqt == 0.11.1,<1", +] + doc = [ "ansys-sphinx-theme==1.2.0", "jupyter_sphinx==0.5.3", "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", + "pyside6 == 6.8.0,<7", + "pyvistaqt == 0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", "sphinx-copybutton==0.5.2", diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index 5635d28d..0c8d0628 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -20,11 +20,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Provides plotting for various PyAnsys objects.""" +import importlib import re from typing import Any, Dict, List, Optional, Union import pyvista as pv from pyvista.plotting.plotter import Plotter as PyVistaPlotter +import pyvistaqt import ansys.tools.visualization_interface as viz_interface from ansys.tools.visualization_interface.types.edge_plot import EdgePlot @@ -33,6 +35,8 @@ from ansys.tools.visualization_interface.utils.color import Color from ansys.tools.visualization_interface.utils.logger import logger +_HAS_PYVISTAQT = importlib.util.find_spec("pyvistaqt") + class PyVistaInterface: """Provides the middle class between PyVista plotting operations and PyAnsys objects. @@ -77,9 +81,15 @@ def __init__( # Generate custom scene if ``None`` is provided if scene is None: if viz_interface.TESTING_MODE: - scene = pv.Plotter(off_screen=True, **plotter_kwargs) + if use_qt and _HAS_PYVISTAQT: + scene = pyvistaqt.BackgroundPlotter(off_screen=True) + else: + if use_qt and not _HAS_PYVISTAQT: + message = "PyVistaQt dependency is not installed. Install it with " + \ + "`pip install ansys-tools-visualization-interface[pyvistaqt]`." + logger.warning(message) + scene = pv.Plotter(off_screen=True, **plotter_kwargs) elif use_qt: - import pyvistaqt scene = pyvistaqt.BackgroundPlotter() else: scene = pv.Plotter(**plotter_kwargs) From 87d0227728dc0d6ab8984c9dc760d8892aa1063d Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:24:00 +0100 Subject: [PATCH 06/49] fix(temp): Try windows machine --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 1f4009f8..542bf29c 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -102,7 +102,7 @@ jobs: testing: name: Run Unit Tests needs: [ smoke-tests ] - runs-on: ubuntu-latest + runs-on: windows-latest steps: - name: Restore images cache uses: actions/cache@v4 From 1357f71185718420c90bbf0a46d6637b6b9e17dd Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:29:14 +0100 Subject: [PATCH 07/49] fix(temp): Windows test --- .github/workflows/ci_cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 542bf29c..92f43e56 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -101,7 +101,7 @@ jobs: testing: name: Run Unit Tests - needs: [ smoke-tests ] + # needs: [ smoke-tests ] runs-on: windows-latest steps: - name: Restore images cache @@ -115,7 +115,7 @@ jobs: uses: ansys/actions/tests-pytest@v8 with: python-version: ${{ env.MAIN_PYTHON_VERSION }} - requires-xvfb: true + # requires-xvfb: true - name: Upload PyVista generated images (cache and results) if: always() From d7ba564935d1eb8f9854e9f30ceebf1c7f905e76 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:36:35 +0100 Subject: [PATCH 08/49] fix: Update python version in CICD --- .github/workflows/ci_cd.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 92f43e56..6c0398f6 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -10,7 +10,7 @@ on: - main env: - MAIN_PYTHON_VERSION: '3.11' + MAIN_PYTHON_VERSION: '3.12' RESET_IMAGE_CACHE: 0 PACKAGE_NAME: ansys-tools-visualization-interface DOCUMENTATION_CNAME: visualization-interface.tools.docs.pyansys.com @@ -101,8 +101,8 @@ jobs: testing: name: Run Unit Tests - # needs: [ smoke-tests ] - runs-on: windows-latest + needs: [ smoke-tests ] + runs-on: ubuntu-latest steps: - name: Restore images cache uses: actions/cache@v4 @@ -115,7 +115,7 @@ jobs: uses: ansys/actions/tests-pytest@v8 with: python-version: ${{ env.MAIN_PYTHON_VERSION }} - # requires-xvfb: true + requires-xvfb: true - name: Upload PyVista generated images (cache and results) if: always() From d931271dbf33f44dbff86c5e7bb4c7e77a1a35bd Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:38:36 +0100 Subject: [PATCH 09/49] fix: Possible missing installations --- .github/workflows/ci_cd.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 6c0398f6..4bc3e5d5 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -101,9 +101,11 @@ jobs: testing: name: Run Unit Tests - needs: [ smoke-tests ] + # needs: [ smoke-tests ] runs-on: ubuntu-latest steps: + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libxcb-xinerama0 libxkbcommon-x11-0 - name: Restore images cache uses: actions/cache@v4 with: From 2211c18ad9ab0649e5f90985117c4a6676753e04 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:43:23 +0100 Subject: [PATCH 10/49] fix: Add qt plugin --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 02f9333e..579747d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,8 +38,9 @@ tests = [ "pytest==8.3.3", "pytest-pyvista==0.1.9", "pytest-cov==6.0.0", - "pyside6 == 6.8.0,<7", - "pyvistaqt == 0.11.1,<1", + "pyside6==6.8.0,<7", + "pyvistaqt==0.11.1,<1", + "pytest-qt==4.4.0" ] doc = [ From f8d065d7c222f1f2704cb963b10b5a907040a417 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:45:48 +0100 Subject: [PATCH 11/49] fix: Installable --- .github/workflows/ci_cd.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 4bc3e5d5..17ebc9c4 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,7 +105,8 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libxcb-xinerama0 libxkbcommon-x11-0 + run: apt install libegl1 + - name: Restore images cache uses: actions/cache@v4 with: From 26934fa96052f6ca53c95b6669e320fc22d0df25 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:47:29 +0100 Subject: [PATCH 12/49] fix: Missing sudo --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 17ebc9c4..998d9b5f 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: apt install libegl1 + run: sudo apt install libegl1 - name: Restore images cache uses: actions/cache@v4 From ed4a6fbb8ae22377d5f89c5330914cdd8f372685 Mon Sep 17 00:00:00 2001 From: afernand Date: Wed, 20 Nov 2024 16:49:39 +0100 Subject: [PATCH 13/49] fix: More installations --- .github/workflows/ci_cd.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 998d9b5f..faa72516 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,8 +105,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt install libegl1 - + run: sudo apt-get update && sudo apt-get install -y libxcb-xinerama0 libxkbcommon-x11-0 libegl1 - name: Restore images cache uses: actions/cache@v4 with: From e8958de56c5fb509862585253c6c9870be62f3fa Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 09:11:11 +0100 Subject: [PATCH 14/49] temp: Downgrade PySide6 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 579747d7..9dd262d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6 == 6.8.0,<7", + "pyside6 == 6.1.0,<7", "pyvistaqt == 0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", From 6cc09ec8853074c916e54ed7f2a9fad580996474 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 09:57:09 +0100 Subject: [PATCH 15/49] fix: Doc dependency --- .github/workflows/ci_cd.yml | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index faa72516..19b2c8b7 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -68,7 +68,7 @@ jobs: docs-build: name: Documentation Build runs-on: ubuntu-latest - needs: [docs-style] + # needs: [docs-style] steps: - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 diff --git a/pyproject.toml b/pyproject.toml index 9dd262d3..2ce305b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,8 +49,8 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6 == 6.1.0,<7", - "pyvistaqt == 0.11.1,<1", + "pyside6==6.1.0", + "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", "sphinx-copybutton==0.5.2", From 430570df83f76eddaf90687b458321600b3a9b83 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 09:59:49 +0100 Subject: [PATCH 16/49] fix: Dependencies --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2ce305b3..47576402 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ tests = [ "pytest==8.3.3", "pytest-pyvista==0.1.9", "pytest-cov==6.0.0", - "pyside6==6.8.0,<7", + "pyside6==6.2.0,<7", "pyvistaqt==0.11.1,<1", "pytest-qt==4.4.0" ] @@ -49,7 +49,7 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6==6.1.0", + "pyside6==6.8.0", "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", From 5595c573c4426b9497c745dec0357dcef6f03b1b Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 10:09:30 +0100 Subject: [PATCH 17/49] fix: Dependency --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 47576402..f024dc8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ tests = [ "pytest==8.3.3", "pytest-pyvista==0.1.9", "pytest-cov==6.0.0", - "pyside6==6.2.0,<7", + "pyside6==6.2.0", "pyvistaqt==0.11.1,<1", "pytest-qt==4.4.0" ] From e93241bd217b33c618f4585438340796e803af93 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 10:52:35 +0100 Subject: [PATCH 18/49] test --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f024dc8e..77eeb074 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ tests = [ "pytest==8.3.3", "pytest-pyvista==0.1.9", "pytest-cov==6.0.0", - "pyside6==6.2.0", + "pyside==6.7.3", "pyvistaqt==0.11.1,<1", "pytest-qt==4.4.0" ] From 54cf87ac421334ad1d483d72ea0abbf25ed9f17a Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 10:56:59 +0100 Subject: [PATCH 19/49] fix: deps --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 77eeb074..4de53a02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ tests = [ "pytest==8.3.3", "pytest-pyvista==0.1.9", "pytest-cov==6.0.0", - "pyside==6.7.3", + "pyside6==6.7.3", "pyvistaqt==0.11.1,<1", "pytest-qt==4.4.0" ] From f7eb4ccbaf4b2dd31f95bf3d0f409a14a71372cb Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 11:02:38 +0100 Subject: [PATCH 20/49] fix: Deps --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4de53a02..fd28c33f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6==6.8.0", + "pyqt5", "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", From 2ca6e59ad3125b82674abbe6055040dbc185cc0a Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 11:09:23 +0100 Subject: [PATCH 21/49] fix deps --- .github/workflows/ci_cd.yml | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 19b2c8b7..941a4e88 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libxcb-xinerama0 libxkbcommon-x11-0 libegl1 + run: sudo apt install libegl1 - name: Restore images cache uses: actions/cache@v4 with: diff --git a/pyproject.toml b/pyproject.toml index fd28c33f..696663b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ tests = [ "pytest-cov==6.0.0", "pyside6==6.7.3", "pyvistaqt==0.11.1,<1", - "pytest-qt==4.4.0" + "pytest-qt" ] doc = [ @@ -49,7 +49,7 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyqt5", + "pyside6==6.8.0", "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", From 1128b44864cc313e27c986a7cbaa52b7f03629ae Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 11:51:25 +0100 Subject: [PATCH 22/49] fix: Deps --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 941a4e88..3f9ae43d 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt install libegl1 + run: sudo apt install libegl1 libxcb-cursor0 - name: Restore images cache uses: actions/cache@v4 with: From 3bdb57d9c0246f66ecf0ed10134568f34e09e946 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 12:08:24 +0100 Subject: [PATCH 23/49] examples test --- examples/00-basic-pyvista-examples/qt_backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py index 91e2596e..bb3c75ba 100644 --- a/examples/00-basic-pyvista-examples/qt_backend.py +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -62,3 +62,5 @@ pl_parallel = Plotter() pl_parallel.plot(sphere) pl_parallel.show() + +pl._backend._pl.scene.close() \ No newline at end of file From cd4df1bce1f72bc5b0af1246f0a26db430d9bee1 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 12:26:26 +0100 Subject: [PATCH 24/49] fixes --- pyproject.toml | 2 +- .../backends/pyvista/pyvista_interface.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 696663b1..13ffefb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6==6.8.0", + "pyside6==6.7.3", "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index 0c8d0628..c71e4b53 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -29,6 +29,7 @@ import pyvistaqt import ansys.tools.visualization_interface as viz_interface +from ansys.tools.visualization_interface import DOCUMENTATION_BUILD from ansys.tools.visualization_interface.types.edge_plot import EdgePlot from ansys.tools.visualization_interface.types.mesh_object_plot import MeshObjectPlot from ansys.tools.visualization_interface.utils.clip_plane import ClipPlane @@ -109,8 +110,9 @@ def __init__( # Show the XY plane self._show_plane = show_plane + if (not DOCUMENTATION_BUILD) or (DOCUMENTATION_BUILD and not use_qt): + self.scene.add_axes(interactive=False) - self.scene.add_axes(interactive=False) # objects to actors mapping self._object_to_actors_map = {} self._enable_widgets = enable_widgets From fcdf1d979b1218cc368d98ac950e2e5c63901a75 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 13:08:03 +0100 Subject: [PATCH 25/49] feat: Implement close for PyvistaQT --- .../tools/visualization_interface/backends/pyvista/pyvista.py | 4 ++++ .../backends/pyvista/pyvista_interface.py | 4 ++-- src/ansys/tools/visualization_interface/plotter.py | 2 +- tests/test_generic_plotter.py | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py index 96552550..e8424fd2 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py @@ -600,3 +600,7 @@ def plot(self, plottable_object: Any, name_filter: str = None, **plotting_option else: self.pv_interface.plot(plottable_object, name_filter, **plotting_options) + def close(self): + """Close the plotter for PyVistaQT.""" + if self._use_qt: + self.pv_interface.scene.close() \ No newline at end of file diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index c71e4b53..b4a98291 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -29,7 +29,6 @@ import pyvistaqt import ansys.tools.visualization_interface as viz_interface -from ansys.tools.visualization_interface import DOCUMENTATION_BUILD from ansys.tools.visualization_interface.types.edge_plot import EdgePlot from ansys.tools.visualization_interface.types.mesh_object_plot import MeshObjectPlot from ansys.tools.visualization_interface.utils.clip_plane import ClipPlane @@ -110,7 +109,8 @@ def __init__( # Show the XY plane self._show_plane = show_plane - if (not DOCUMENTATION_BUILD) or (DOCUMENTATION_BUILD and not use_qt): + # if (not DOCUMENTATION_BUILD) or (DOCUMENTATION_BUILD and not use_qt) or (TESTING_MODE and not use_qt): + if not use_qt: self.scene.add_axes(interactive=False) # objects to actors mapping diff --git a/src/ansys/tools/visualization_interface/plotter.py b/src/ansys/tools/visualization_interface/plotter.py index 64734a69..33bfbfd3 100644 --- a/src/ansys/tools/visualization_interface/plotter.py +++ b/src/ansys/tools/visualization_interface/plotter.py @@ -81,4 +81,4 @@ def show( screenshot=screenshot, name_filter=name_filter, **plotting_options - ) + ) diff --git a/tests/test_generic_plotter.py b/tests/test_generic_plotter.py index dac90a92..5035feaa 100644 --- a/tests/test_generic_plotter.py +++ b/tests/test_generic_plotter.py @@ -51,7 +51,8 @@ def test_plotter_pyvistaqt(): pl = Plotter(backend=qt_backend) sphere = pv.Sphere() pl.plot(sphere) - pl.show() + # PyVista QT show() breaks PyTest, so we avoid it. + qt_backend.close() def test_plotter_add_mb(): From 8151f484136b04f61c83a315a0615b119b604501 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 13:13:21 +0100 Subject: [PATCH 26/49] fix: linux deps --- .github/workflows/ci_cd.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 3f9ae43d..93d4bbfe 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -70,6 +70,8 @@ jobs: runs-on: ubuntu-latest # needs: [docs-style] steps: + - name: Install system dependencies + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 @@ -105,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 -y - name: Restore images cache uses: actions/cache@v4 with: From 37b173c73630653e59fc8b9841ae76b2ac08fb08 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 13:18:29 +0100 Subject: [PATCH 27/49] fix: linux deps --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 93d4bbfe..983d745b 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 -y + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 From 3fc8d5b68af5448cf9ec34f169b746b8c06e8cc5 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 13:18:48 +0100 Subject: [PATCH 28/49] fix: Linux deps --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 983d745b..635a2bf6 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 -y + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: From a638e998520ae7d4ac5178627471444c3b2878aa Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 13:23:19 +0100 Subject: [PATCH 29/49] fix: linux deps --- .github/workflows/ci_cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 635a2bf6..c033d87d 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install libegl1 libxcb-cursor0 xcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install libegl1 libxcb-cursor0 xcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: From 3000aab37e4c8e08cb28cbaf174a179c3deca8c5 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 14:58:20 +0100 Subject: [PATCH 30/49] fix: deps --- .github/workflows/ci_cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index c033d87d..635a2bf6 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 xcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 xcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: From 0639461d59799eb0119c773486db5e82342ac1e0 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 15:22:24 +0100 Subject: [PATCH 31/49] test --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 13ffefb4..82a3451c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ doc = [ "nbsphinx==0.9.5", "numpydoc==1.8.0", "pyside6==6.7.3", + "qt5-default", "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", From 2ffbceffd7e6ed56c01869387a889b0378e54072 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 15:23:44 +0100 Subject: [PATCH 32/49] test --- .github/workflows/ci_cd.yml | 2 +- pyproject.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 635a2bf6..b1653fd4 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 qt5-default -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 diff --git a/pyproject.toml b/pyproject.toml index 82a3451c..13ffefb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,6 @@ doc = [ "nbsphinx==0.9.5", "numpydoc==1.8.0", "pyside6==6.7.3", - "qt5-default", "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", From c08845b856ea604bc4ca3c4db85f4c8293f8b448 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 15:44:01 +0100 Subject: [PATCH 33/49] fix: linux dep --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index b1653fd4..a7993466 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 qt5-default -y + run: sudo apt install libegl1 libxcb-cursor-dev libsm6 libxext6 libxcb-xinerama0 -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 From c18d57033a1ebe57051a98c4b861133308023430 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 15:50:07 +0100 Subject: [PATCH 34/49] test --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index a7993466..75fbfa50 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor-dev libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install libegl1 qt6-base-dev libsm6 libxext6 libxcb-xinerama0 -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 From 48f6f5c6e83bf2c9f0a416e9644746bfbe719b21 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 16:02:58 +0100 Subject: [PATCH 35/49] test --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 75fbfa50..ac8b3734 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install libegl1 qt6-base-dev libsm6 libxext6 libxcb-xinerama0 -y + run: sudo apt install qt6-base-dev -y - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 From 02cb7bc21b9154da677c5783b0179da5ef1de0c0 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 16:05:11 +0100 Subject: [PATCH 36/49] test --- .github/workflows/ci_cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index ac8b3734..1cbda8b0 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,7 +71,7 @@ jobs: # needs: [docs-style] steps: - name: Install system dependencies - run: sudo apt install qt6-base-dev -y + run: sudo apt install -qq libglu1-mesa-dev libx11-xcb-dev '^libxcb*' - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 From d6e14809a0135b1e1aef95cc7840caa609b6e5f4 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 16:15:51 +0100 Subject: [PATCH 37/49] fix: Skip Qt test --- .github/workflows/ci_cd.yml | 1 + tests/test_generic_plotter.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 1cbda8b0..904f3c95 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -14,6 +14,7 @@ env: RESET_IMAGE_CACHE: 0 PACKAGE_NAME: ansys-tools-visualization-interface DOCUMENTATION_CNAME: visualization-interface.tools.docs.pyansys.com + IN_GITHUB_ACTIONS: true concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/tests/test_generic_plotter.py b/tests/test_generic_plotter.py index 5035feaa..0a03876e 100644 --- a/tests/test_generic_plotter.py +++ b/tests/test_generic_plotter.py @@ -20,14 +20,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Test module for the generic plotter.""" +import os from pathlib import Path import numpy as np +import pytest import pyvista as pv from ansys.tools.visualization_interface import ClipPlane, MeshObjectPlot, Plotter from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend +IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" + class CustomTestClass: """Mock custom class for testing MeshObjectPlot.""" @@ -44,7 +48,7 @@ def test_plotter_add_pd(): pl.plot(sphere) pl.show() - +@pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Qt breaks CICD.") def test_plotter_pyvistaqt(): """Adds polydata to the plotter.""" qt_backend = PyVistaBackend(use_qt=True) From 0f3ac43745cf8311ea6088d0e85543dd6a138c82 Mon Sep 17 00:00:00 2001 From: afernand Date: Thu, 21 Nov 2024 16:30:13 +0100 Subject: [PATCH 38/49] fix: Remove code execution from example --- .../00-basic-pyvista-examples/qt_backend.py | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py index bb3c75ba..baf8ff05 100644 --- a/examples/00-basic-pyvista-examples/qt_backend.py +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -39,19 +39,18 @@ from ansys.tools.visualization_interface import Plotter from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend -# Create a PyVista mesh -cube = pv.Cube() - -pv_backend = PyVistaBackend(use_qt=True) - -# Create a plotter -pl = Plotter(backend=pv_backend) - -# Add the mesh to the plotter -pl.plot(cube) +######################### +# Open a pyvistaqt window +# ======================= +# .. code-block:: python +# +# cube = pv.Cube() +# pv_backend = PyVistaBackend(use_qt=True) +# pl = Plotter(backend=pv_backend) +# pl.plot(cube) +# pl.show() +# -# Show the plotter -pl.show() ##################### # Parallel VTK window @@ -63,4 +62,10 @@ pl_parallel.plot(sphere) pl_parallel.show() -pl._backend._pl.scene.close() \ No newline at end of file +############################ +# Close the pyvistaqt window +# ========================== +# .. code-block:: python +# +# pv_backend.close() +# From 508b62575d2c06aa736506471ad2183819b7d038 Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 11:06:29 +0100 Subject: [PATCH 39/49] fix: Refactor to have independent backend --- .github/workflows/ci_cd.yml | 4 +- .../00-basic-pyvista-examples/qt_backend.py | 6 +- .../backends/pyvista/pyvista.py | 17 +- .../backends/pyvista/pyvista_interface.py | 29 +- .../backends/pyvista/pyvistaqt_interface.py | 420 ++++++++++++++++++ tests/test_generic_plotter.py | 8 +- 6 files changed, 437 insertions(+), 47 deletions(-) create mode 100644 src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 904f3c95..a9c94927 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -69,7 +69,7 @@ jobs: docs-build: name: Documentation Build runs-on: ubuntu-latest - # needs: [docs-style] + needs: [docs-style] steps: - name: Install system dependencies run: sudo apt install -qq libglu1-mesa-dev libx11-xcb-dev '^libxcb*' @@ -104,7 +104,7 @@ jobs: testing: name: Run Unit Tests - # needs: [ smoke-tests ] + needs: [ smoke-tests ] runs-on: ubuntu-latest steps: - name: Install system dependencies diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py index baf8ff05..1b5affcd 100644 --- a/examples/00-basic-pyvista-examples/qt_backend.py +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -37,7 +37,9 @@ import pyvista as pv from ansys.tools.visualization_interface import Plotter -from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend +from ansys.tools.visualization_interface.backends.pyvista.pyvistaqt_interface import ( + PyVistaQtBackend, +) ######################### # Open a pyvistaqt window @@ -45,7 +47,7 @@ # .. code-block:: python # # cube = pv.Cube() -# pv_backend = PyVistaBackend(use_qt=True) +# pv_backend = PyVistaQtBackend() # pl = Plotter(backend=pv_backend) # pl.plot(cube) # pl.show() diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py index e8424fd2..c0f8c9ef 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py @@ -88,8 +88,6 @@ class PyVistaBackendInterface(BaseBackend): Whether to plot the names of the picked objects. show_plane : Optional[bool], default: False Whether to show the plane in the plotter. - use_qt : Optional[bool], default: False - Whether to use the Qt backend for the plotter. """ def __init__( @@ -99,14 +97,12 @@ def __init__( allow_hovering: Optional[bool] = False, plot_picked_names: Optional[bool] = False, show_plane: Optional[bool] = False, - use_qt: Optional[bool] = False, **plotter_kwargs, ) -> None: """Initialize the ``use_trame`` parameter and save the current ``pv.OFF_SCREEN`` value.""" # Check if the use of trame was requested if use_trame is None: use_trame = ansys.tools.visualization_interface.USE_TRAME - self._use_qt = use_qt self._use_trame = use_trame self._allow_picking = allow_picking self._allow_hovering = allow_hovering @@ -153,7 +149,7 @@ def __init__( logger.warning(warn_msg) self._pl = PyVistaInterface(show_plane=show_plane) else: - self._pl = PyVistaInterface(show_plane=show_plane, use_qt=use_qt, **plotter_kwargs) + self._pl = PyVistaInterface(show_plane=show_plane, **plotter_kwargs) self._enable_widgets = self._pl._enable_widgets @@ -182,8 +178,7 @@ def enable_widgets(self): ] self._widgets.append(MeasureWidget(self)) self._widgets.append(ScreenshotButton(self)) - if not self._use_qt: - self._widgets.append(MeshSliderWidget(self)) + self._widgets.append(MeshSliderWidget(self)) self._widgets.append(HideButton(self)) def add_widget(self, widget: Union[PlotterWidget, List[PlotterWidget]]): @@ -550,10 +545,9 @@ def __init__( allow_picking: Optional[bool] = False, allow_hovering: Optional[bool] = False, plot_picked_names: Optional[bool] = True, - use_qt: Optional[bool] = False ) -> None: """Initialize the generic plotter.""" - super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names, use_qt=use_qt) + super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) def plot_iter( self, @@ -599,8 +593,3 @@ def plot(self, plottable_object: Any, name_filter: str = None, **plotting_option self.pv_interface.plot_iter(plottable_object, name_filter, **plotting_options) else: self.pv_interface.plot(plottable_object, name_filter, **plotting_options) - - def close(self): - """Close the plotter for PyVistaQT.""" - if self._use_qt: - self.pv_interface.scene.close() \ No newline at end of file diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index b4a98291..c21ad7b4 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -20,13 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Provides plotting for various PyAnsys objects.""" -import importlib import re from typing import Any, Dict, List, Optional, Union import pyvista as pv from pyvista.plotting.plotter import Plotter as PyVistaPlotter -import pyvistaqt import ansys.tools.visualization_interface as viz_interface from ansys.tools.visualization_interface.types.edge_plot import EdgePlot @@ -35,8 +33,6 @@ from ansys.tools.visualization_interface.utils.color import Color from ansys.tools.visualization_interface.utils.logger import logger -_HAS_PYVISTAQT = importlib.util.find_spec("pyvistaqt") - class PyVistaInterface: """Provides the middle class between PyVista plotting operations and PyAnsys objects. @@ -62,9 +58,6 @@ class PyVistaInterface: for visualization. show_plane : bool, default: False Whether to show the XY plane in the plotter window. - use_qt : bool, default: False - Whether to use the Qt backend for the plotter window. - """ def __init__( @@ -74,27 +67,16 @@ def __init__( num_points: int = 100, enable_widgets: bool = True, show_plane: bool = False, - use_qt: bool = False, **plotter_kwargs, ) -> None: """Initialize the plotter.""" # Generate custom scene if ``None`` is provided if scene is None: if viz_interface.TESTING_MODE: - if use_qt and _HAS_PYVISTAQT: - scene = pyvistaqt.BackgroundPlotter(off_screen=True) - else: - if use_qt and not _HAS_PYVISTAQT: - message = "PyVistaQt dependency is not installed. Install it with " + \ - "`pip install ansys-tools-visualization-interface[pyvistaqt]`." - logger.warning(message) - scene = pv.Plotter(off_screen=True, **plotter_kwargs) - elif use_qt: - scene = pyvistaqt.BackgroundPlotter() + scene = pv.Plotter(off_screen=True, **plotter_kwargs) else: scene = pv.Plotter(**plotter_kwargs) - self._use_qt = use_qt # If required, use a white background with no gradient if not color_opts: color_opts = dict(color="white") @@ -109,9 +91,7 @@ def __init__( # Show the XY plane self._show_plane = show_plane - # if (not DOCUMENTATION_BUILD) or (DOCUMENTATION_BUILD and not use_qt) or (TESTING_MODE and not use_qt): - if not use_qt: - self.scene.add_axes(interactive=False) + self.scene.add_axes(interactive=False) # objects to actors mapping self._object_to_actors_map = {} @@ -355,10 +335,7 @@ def show( if jupyter_backend: self.scene.show(jupyter_backend=jupyter_backend, **kwargs) else: - if self._use_qt: - self.scene.show() - else: - self.scene.show(**kwargs) + self.scene.show(**kwargs) def set_add_mesh_defaults(self, plotting_options: Optional[Dict]) -> None: """Set the default values for the plotting options. diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py new file mode 100644 index 00000000..58207a66 --- /dev/null +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py @@ -0,0 +1,420 @@ +# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Module for the PyVistaQt functionalities.""" +import importlib +from typing import Any, Dict, List, Optional + +import pyvista as pv +from vtkmodules.vtkInteractionWidgets import vtkHoverWidget +from vtkmodules.vtkRenderingCore import vtkPointPicker + +import ansys.tools.visualization_interface as viz_interface +from ansys.tools.visualization_interface.backends.pyvista.pyvista import PyVistaBackendInterface +from ansys.tools.visualization_interface.backends.pyvista.pyvista_interface import PyVistaInterface +from ansys.tools.visualization_interface.backends.pyvista.widgets.displace_arrows import ( + CameraPanDirection, + DisplacementArrow, +) +from ansys.tools.visualization_interface.backends.pyvista.widgets.hide_buttons import HideButton +from ansys.tools.visualization_interface.backends.pyvista.widgets.measure import MeasureWidget +from ansys.tools.visualization_interface.backends.pyvista.widgets.ruler import Ruler +from ansys.tools.visualization_interface.backends.pyvista.widgets.screenshot import ScreenshotButton +from ansys.tools.visualization_interface.backends.pyvista.widgets.view_button import ( + ViewButton, + ViewDirection, +) +from ansys.tools.visualization_interface.backends.pyvista.widgets.widget import PlotterWidget +from ansys.tools.visualization_interface.utils.logger import logger + +_HAS_PYVISTAQT = importlib.util.find_spec("pyvistaqt") +if _HAS_PYVISTAQT: + import pyvistaqt + + +class PyVistaQtInterface(PyVistaInterface): + """Provides the middle class between PyVistaQt plotting operations and PyAnsys objects. + + The main purpose of this class is to simplify interaction between PyVista and the PyVistaQt + backend provided. This class is responsible for creating the PyVista scene and adding + the PyAnsys objects to it. + + + Parameters + ---------- + scene : ~pyvista.Plotter, default: None + Scene for rendering the objects. If passed, ``off_screen`` needs to + be set manually beforehand for documentation and testing. + color_opts : dict, default: None + Dictionary containing the background and top colors. + num_points : int, default: 100 + Number of points to use to render the shapes. + enable_widgets : bool, default: True + Whether to enable widget buttons in the plotter window. + Widget buttons must be disabled when using + `trame `_ + for visualization. + show_plane : bool, default: False + Whether to show the XY plane in the plotter window. + """ + def __init__( + self, + scene: Optional[pv.Plotter] = None, + color_opts: Optional[Dict] = None, + num_points: int = 100, + enable_widgets: bool = True, + show_plane: bool = False, + **plotter_kwargs, + ) -> None: + """Initialize the plotter.""" + # Generate custom scene if ``None`` is provided + + if scene is None: + if not _HAS_PYVISTAQT: + message = "PyVistaQt dependency is not installed. Install it with " + \ + "`pip install ansys-tools-visualization-interface[pyvistaqt]`." + logger.warning(message) + elif viz_interface.TESTING_MODE: + scene = pyvistaqt.BackgroundPlotter(off_screen=True) + else: + scene = pyvistaqt.BackgroundPlotter() + + # If required, use a white background with no gradient + if not color_opts: + color_opts = dict(color="white") + + # Create the scene + self._scene = scene + # Scene: assign the background + self._scene.set_background(**color_opts) + + # Save the desired number of points + self._num_points = num_points + + # Show the XY plane + self._show_plane = show_plane + + # objects to actors mapping + self._object_to_actors_map = {} + self._enable_widgets = enable_widgets + self._show_edges = None + + def show( + self, + show_plane: bool = False, + **kwargs: Optional[Dict], + ) -> None: + """Show the rendered scene on the screen. + + Parameters + ---------- + show_plane : bool, default: True + Whether to show the XY plane. + + Notes + ----- + For more information on supported Jupyter backends, see + `Jupyter Notebook Plotting `_ + in the PyVista documentation. + + """ + # Compute the scaling + bounds = self.scene.renderer.bounds + x_length, y_length = bounds[1] - bounds[0], bounds[3] - bounds[2] + sfac = max(x_length, y_length) + + # Create the fundamental XY plane + if show_plane or self._show_plane: + # self.scene.bounds + plane = pv.Plane(i_size=sfac * 1.3, j_size=sfac * 1.3) + self.scene.add_mesh(plane, color="white", show_edges=True, opacity=0.1) + + # Enabling anti-aliasing by default on scene + self.scene.enable_anti_aliasing("ssaa") + + # If screenshot is requested, set off_screen to True for the plotter + if kwargs.get("screenshot") is not None: + self.scene.off_screen = True + else: + self.scene.show() + + +class PyVistaQtBackendInterface(PyVistaBackendInterface): + """Provides the interface for the Visualization Interface Tool plotter for PyVistaQt. + + This class is intended to be used as a base class for the custom plotters + in the different PyAnsys libraries. It provides the basic plotter functionalities, + such as adding objects and enabling widgets and picking capabilities. It also + provides the ability to show the plotter using the `trame `_ + service. + + You can override the ``plot_iter()``, ``plot()``, and ``picked_operation()`` methods. + The ``plot_iter()`` method is intended to plot a list of objects to the plotter, while the + ``plot()`` method is intended to plot a single object to the plotter. The ``show()`` method is + intended to show the plotter. The ``picked_operation()`` method is + intended to perform an operation on the picked objects. + + Parameters + ---------- + allow_picking : Optional[bool], default: False + Whether to allow picking capabilities in the window. Incompatible with hovering. + Picking will take precedence over hovering. + allow_hovering : Optional[bool], default: False + Whether to allow hovering capabilities in the window. Incompatible with picking. + Picking will take precedence over hovering. + plot_picked_names : Optional[bool], default: False + Whether to plot the names of the picked objects. + show_plane : Optional[bool], default: False + Whether to show the plane in the plotter. + """ + def __init__( + self, + allow_picking: Optional[bool] = False, + allow_hovering: Optional[bool] = False, + plot_picked_names: Optional[bool] = False, + show_plane: Optional[bool] = False, + **plotter_kwargs, + ) -> None: + """Initialize the plotter.""" + self._allow_picking = allow_picking + self._allow_hovering = allow_hovering + if self._allow_picking and self._allow_hovering: + logger.warning( + "Picking and hovering are incompatible. Picking will take precedence." + ) + self._allow_hovering = False + self._pv_off_screen_original = bool(pv.OFF_SCREEN) + self._plot_picked_names = plot_picked_names + # Map that relates PyVista actors with PyAnsys objects + self._object_to_actors_map = {} + + # PyVista plotter + self._pl = None + + # Dictionary of picked objects in MeshObject format. + self._picked_dict = {} + + # Map that relates PyVista actors with the added actors by the picker + self._picker_added_actors_map = {} + + # Map that relates PyVista actors with EdgePlot objects + self._edge_actors_map = {} + + # List of widgets added to the plotter. + self._widgets = [] + + # Map that saves original colors of the plotted objects. + self._origin_colors = {} + + self._pl = PyVistaQtInterface(show_plane=show_plane, **plotter_kwargs) + + self._enable_widgets = self._pl._enable_widgets + + self._hover_picker = vtkPointPicker() if self. _allow_hovering else None + self._hover_widget = vtkHoverWidget() if self. _allow_hovering else None + self._added_hover_labels = [] + + def enable_widgets(self): + """Enable the widgets for the plotter.""" + # Create Plotter widgets + if self._enable_widgets: + self._widgets: List[PlotterWidget] = [] + self._widgets.append(Ruler(self._pl._scene)) + [ + self._widgets.append(DisplacementArrow(self._pl._scene, direction=dir)) + for dir in CameraPanDirection + ] + [ + self._widgets.append(ViewButton(self._pl._scene, direction=dir)) + for dir in ViewDirection + ] + self._widgets.append(MeasureWidget(self)) + self._widgets.append(ScreenshotButton(self)) + self._widgets.append(HideButton(self)) + + def close(self): + """Close the plotter for PyVistaQT.""" + self.pv_interface.scene.close() + + def show_plotter(self, screenshot: Optional[str] = None) -> None: + """Show the plotter or start the `trame `_ service. + + Parameters + ---------- + plotter : Plotter + Visualization Interface Tool plotter with the meshes added. + screenshot : str, default: None + Path for saving a screenshot of the image that is being represented. + + """ + self.pv_interface.show(screenshot=screenshot) + pv.OFF_SCREEN = self._pv_off_screen_original + + def show( + self, + plottable_object: Any = None, + screenshot: Optional[str] = None, + view_2d: Dict = None, + name_filter: str = None, + **plotting_options, + ) -> List[Any]: + """Plot and show any PyAnsys object. + + The types of objects supported are ``MeshObjectPlot``, + ``pv.MultiBlock``, and ``pv.PolyData``. + + Parameters + ---------- + plottable_object : Any, default: None + Object or list of objects to plot. + screenshot : str, default: None + Path for saving a screenshot of the image that is being represented. + view_2d : Dict, default: None + Dictionary with the plane and the viewup vectors of the 2D plane. + name_filter : str, default: None + Regular expression with the desired name or names to include in the plotter. + **plotting_options : dict, default: None + Keyword arguments. For allowable keyword arguments, see the + :meth:`Plotter.add_mesh ` method. + + Returns + ------- + List[Any] + List with the picked bodies in the picked order. + + """ + self.plot(plottable_object, name_filter, **plotting_options) + if self._pl.object_to_actors_map: + self._object_to_actors_map = self._pl.object_to_actors_map + else: + logger.warning("No actors were added to the plotter.") + + # Compute mapping between the objects and its edges. + _ = self.compute_edge_object_map() + + if view_2d is not None: + self._pl.scene.view_vector( + vector=view_2d["vector"], + viewup=view_2d["viewup"], + ) + + if self._allow_picking: + self.enable_picking() + elif self._allow_hovering: + self.enable_hover() + + # Update all buttons/widgets + [widget.update() for widget in self._widgets] + + self.show_plotter(screenshot) + + picked_objects_list = [] + if isinstance(plottable_object, list): + # Keep them ordered based on picking + for meshobject in self._picked_dict.values(): + for elem in plottable_object: + if hasattr(elem, "name") and elem.name == meshobject.name: + picked_objects_list.append(elem) + elif hasattr(plottable_object, "name") and plottable_object.name in self._picked_dict: + picked_objects_list = [plottable_object] + else: + # Return the picked objects in the order they were picked + picked_objects_list = list(self._picked_dict.values()) + + return picked_objects_list + + +class PyVistaQtBackend(PyVistaQtBackendInterface): + """Provides the generic plotter implementation for PyAnsys libraries. + + This class accepts ``MeshObjectPlot``, ``pv.MultiBlock`` and ``pv.PolyData`` objects. + + Parameters + ---------- + use_trame : bool, default: None + Whether to enable the use of `trame `_. + The default is ``None``, in which case the ``USE_TRAME`` global setting + is used. + allow_picking : Optional[bool], default: False + Whether to allow picking capabilities in the window. Incompatible with hovering. + Picking will take precedence over hovering. + allow_hovering : Optional[bool], default: False + Whether to allow hovering capabilities in the window. Incompatible with picking. + Picking will take precedence over hovering. + plot_picked_names : bool, default: True + Whether to plot the names of the picked objects. + + """ + + def __init__( + self, + use_trame: Optional[bool] = None, + allow_picking: Optional[bool] = False, + allow_hovering: Optional[bool] = False, + plot_picked_names: Optional[bool] = True, + ) -> None: + """Initialize the generic plotter.""" + super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) + + def plot_iter( + self, + plotting_list: List[Any], + name_filter: str = None, + **plotting_options, + ) -> None: + """Plot the elements of an iterable of any type of object to the scene. + + The types of objects supported are ``Body``, ``Component``, ``List[pv.PolyData]``, + ``pv.MultiBlock``, and ``Sketch``. + + Parameters + ---------- + plotting_list : List[Any] + List of objects to plot. + name_filter : str, default: None + Regular expression with the desired name or names to include in the plotter. + **plotting_options : dict, default: None + Keyword arguments. For allowable keyword arguments, see the + :meth:`Plotter.add_mesh ` method. + + """ + for plottable_object in plotting_list: + self.plot(plottable_object, name_filter, **plotting_options) + + def plot(self, plottable_object: Any, name_filter: str = None, **plotting_options): + """Plot a ``pyansys`` or ``PyVista`` object to the plotter. + + Parameters + ---------- + plottable_object : Any + Object to add. + name_filter : str + Regular expression with the desired name or names to include in the plotter. + **plotting_options : dict, default: None + Keyword arguments. For allowable keyword arguments, see the + :meth:`Plotter.add_mesh ` method. + + """ + if hasattr(plottable_object, "__iter__"): + logger.debug("Plotting objects in list...") + self.pv_interface.plot_iter(plottable_object, name_filter, **plotting_options) + else: + self.pv_interface.plot(plottable_object, name_filter, **plotting_options) diff --git a/tests/test_generic_plotter.py b/tests/test_generic_plotter.py index 0a03876e..91236a15 100644 --- a/tests/test_generic_plotter.py +++ b/tests/test_generic_plotter.py @@ -28,7 +28,9 @@ import pyvista as pv from ansys.tools.visualization_interface import ClipPlane, MeshObjectPlot, Plotter -from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend +from ansys.tools.visualization_interface.backends.pyvista.pyvistaqt_interface import ( + PyVistaQtBackend, +) IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" @@ -50,8 +52,8 @@ def test_plotter_add_pd(): @pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Qt breaks CICD.") def test_plotter_pyvistaqt(): - """Adds polydata to the plotter.""" - qt_backend = PyVistaBackend(use_qt=True) + """Run PyVistaQt backend.""" + qt_backend = PyVistaQtBackend() pl = Plotter(backend=qt_backend) sphere = pv.Sphere() pl.plot(sphere) From e6883200a1ca291a30bc0df34ba10d20aaa87e4d Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 11:14:16 +0100 Subject: [PATCH 40/49] fix: Depenency pinning, remove sys deps --- .github/workflows/ci_cd.yml | 4 ---- pyproject.toml | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index a9c94927..b361e563 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -71,8 +71,6 @@ jobs: runs-on: ubuntu-latest needs: [docs-style] steps: - - name: Install system dependencies - run: sudo apt install -qq libglu1-mesa-dev libx11-xcb-dev '^libxcb*' - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 @@ -107,8 +105,6 @@ jobs: needs: [ smoke-tests ] runs-on: ubuntu-latest steps: - - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: diff --git a/pyproject.toml b/pyproject.toml index 13ffefb4..29d3967d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ tests = [ "pytest-cov==6.0.0", "pyside6==6.7.3", "pyvistaqt==0.11.1,<1", - "pytest-qt" + "pytest-qt==4.4.0" ] doc = [ @@ -49,8 +49,6 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6==6.7.3", - "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", "sphinx-copybutton==0.5.2", From 42a345974cfeaea60fded175cc0fe1a3d697d36a Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 11:19:13 +0100 Subject: [PATCH 41/49] fix: Minor details --- .../tools/visualization_interface/backends/pyvista/pyvista.py | 2 +- .../backends/pyvista/pyvistaqt_interface.py | 2 +- src/ansys/tools/visualization_interface/plotter.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py index c0f8c9ef..731bc5f0 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py @@ -544,7 +544,7 @@ def __init__( use_trame: Optional[bool] = None, allow_picking: Optional[bool] = False, allow_hovering: Optional[bool] = False, - plot_picked_names: Optional[bool] = True, + plot_picked_names: Optional[bool] = True ) -> None: """Initialize the generic plotter.""" super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py index 58207a66..69fa4f95 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py @@ -369,7 +369,7 @@ def __init__( use_trame: Optional[bool] = None, allow_picking: Optional[bool] = False, allow_hovering: Optional[bool] = False, - plot_picked_names: Optional[bool] = True, + plot_picked_names: Optional[bool] = True ) -> None: """Initialize the generic plotter.""" super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) diff --git a/src/ansys/tools/visualization_interface/plotter.py b/src/ansys/tools/visualization_interface/plotter.py index 33bfbfd3..ad7f3c20 100644 --- a/src/ansys/tools/visualization_interface/plotter.py +++ b/src/ansys/tools/visualization_interface/plotter.py @@ -62,7 +62,7 @@ def show( screenshot: str = None, name_filter: bool = None, **plotting_options - ) -> None: + ) -> None: """Show the plotted objects. Parameters From aba32e420b7dba72fba7246e436ef1d749c7cd70 Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 11:34:57 +0100 Subject: [PATCH 42/49] fix: Sys deps --- .github/workflows/ci_cd.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index b361e563..67aee464 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,6 +105,8 @@ jobs: needs: [ smoke-tests ] runs-on: ubuntu-latest steps: + - name: Install system dependencies + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: From cb593a45ac0576744ada6b48e16faa4c4bc7f221 Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 12:59:06 +0100 Subject: [PATCH 43/49] Revert "fix: Sys deps" This reverts commit aba32e420b7dba72fba7246e436ef1d749c7cd70. --- .github/workflows/ci_cd.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 67aee464..b361e563 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -105,8 +105,6 @@ jobs: needs: [ smoke-tests ] runs-on: ubuntu-latest steps: - - name: Install system dependencies - run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: From 5678add20ccb85d05ed88a20dc786f9624856e43 Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 11:34:57 +0100 Subject: [PATCH 44/49] fix: Revert refactor commits Revert "fix: Sys deps" This reverts commit aba32e420b7dba72fba7246e436ef1d749c7cd70. Revert "fix: Minor details" This reverts commit 42a345974cfeaea60fded175cc0fe1a3d697d36a. Revert "fix: Depenency pinning, remove sys deps" This reverts commit e6883200a1ca291a30bc0df34ba10d20aaa87e4d. Revert "fix: Refactor to have independent backend" This reverts commit 508b62575d2c06aa736506471ad2183819b7d038. --- .github/workflows/ci_cd.yml | 8 +- .../00-basic-pyvista-examples/qt_backend.py | 6 +- pyproject.toml | 4 +- .../backends/pyvista/pyvista.py | 19 +- .../backends/pyvista/pyvista_interface.py | 29 +- .../backends/pyvista/pyvistaqt_interface.py | 420 ------------------ .../tools/visualization_interface/plotter.py | 2 +- tests/test_generic_plotter.py | 8 +- 8 files changed, 56 insertions(+), 440 deletions(-) delete mode 100644 src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index b361e563..904f3c95 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -69,8 +69,10 @@ jobs: docs-build: name: Documentation Build runs-on: ubuntu-latest - needs: [docs-style] + # needs: [docs-style] steps: + - name: Install system dependencies + run: sudo apt install -qq libglu1-mesa-dev libx11-xcb-dev '^libxcb*' - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 @@ -102,9 +104,11 @@ jobs: testing: name: Run Unit Tests - needs: [ smoke-tests ] + # needs: [ smoke-tests ] runs-on: ubuntu-latest steps: + - name: Install system dependencies + run: sudo apt install libegl1 libxcb-cursor0 libsm6 libxext6 libxcb-xinerama0 -y - name: Restore images cache uses: actions/cache@v4 with: diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py index 1b5affcd..baf8ff05 100644 --- a/examples/00-basic-pyvista-examples/qt_backend.py +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -37,9 +37,7 @@ import pyvista as pv from ansys.tools.visualization_interface import Plotter -from ansys.tools.visualization_interface.backends.pyvista.pyvistaqt_interface import ( - PyVistaQtBackend, -) +from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend ######################### # Open a pyvistaqt window @@ -47,7 +45,7 @@ # .. code-block:: python # # cube = pv.Cube() -# pv_backend = PyVistaQtBackend() +# pv_backend = PyVistaBackend(use_qt=True) # pl = Plotter(backend=pv_backend) # pl.plot(cube) # pl.show() diff --git a/pyproject.toml b/pyproject.toml index 29d3967d..13ffefb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ tests = [ "pytest-cov==6.0.0", "pyside6==6.7.3", "pyvistaqt==0.11.1,<1", - "pytest-qt==4.4.0" + "pytest-qt" ] doc = [ @@ -49,6 +49,8 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", + "pyside6==6.7.3", + "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", "sphinx-copybutton==0.5.2", diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py index 731bc5f0..e8424fd2 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py @@ -88,6 +88,8 @@ class PyVistaBackendInterface(BaseBackend): Whether to plot the names of the picked objects. show_plane : Optional[bool], default: False Whether to show the plane in the plotter. + use_qt : Optional[bool], default: False + Whether to use the Qt backend for the plotter. """ def __init__( @@ -97,12 +99,14 @@ def __init__( allow_hovering: Optional[bool] = False, plot_picked_names: Optional[bool] = False, show_plane: Optional[bool] = False, + use_qt: Optional[bool] = False, **plotter_kwargs, ) -> None: """Initialize the ``use_trame`` parameter and save the current ``pv.OFF_SCREEN`` value.""" # Check if the use of trame was requested if use_trame is None: use_trame = ansys.tools.visualization_interface.USE_TRAME + self._use_qt = use_qt self._use_trame = use_trame self._allow_picking = allow_picking self._allow_hovering = allow_hovering @@ -149,7 +153,7 @@ def __init__( logger.warning(warn_msg) self._pl = PyVistaInterface(show_plane=show_plane) else: - self._pl = PyVistaInterface(show_plane=show_plane, **plotter_kwargs) + self._pl = PyVistaInterface(show_plane=show_plane, use_qt=use_qt, **plotter_kwargs) self._enable_widgets = self._pl._enable_widgets @@ -178,7 +182,8 @@ def enable_widgets(self): ] self._widgets.append(MeasureWidget(self)) self._widgets.append(ScreenshotButton(self)) - self._widgets.append(MeshSliderWidget(self)) + if not self._use_qt: + self._widgets.append(MeshSliderWidget(self)) self._widgets.append(HideButton(self)) def add_widget(self, widget: Union[PlotterWidget, List[PlotterWidget]]): @@ -544,10 +549,11 @@ def __init__( use_trame: Optional[bool] = None, allow_picking: Optional[bool] = False, allow_hovering: Optional[bool] = False, - plot_picked_names: Optional[bool] = True + plot_picked_names: Optional[bool] = True, + use_qt: Optional[bool] = False ) -> None: """Initialize the generic plotter.""" - super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) + super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names, use_qt=use_qt) def plot_iter( self, @@ -593,3 +599,8 @@ def plot(self, plottable_object: Any, name_filter: str = None, **plotting_option self.pv_interface.plot_iter(plottable_object, name_filter, **plotting_options) else: self.pv_interface.plot(plottable_object, name_filter, **plotting_options) + + def close(self): + """Close the plotter for PyVistaQT.""" + if self._use_qt: + self.pv_interface.scene.close() \ No newline at end of file diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index c21ad7b4..b4a98291 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -20,11 +20,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Provides plotting for various PyAnsys objects.""" +import importlib import re from typing import Any, Dict, List, Optional, Union import pyvista as pv from pyvista.plotting.plotter import Plotter as PyVistaPlotter +import pyvistaqt import ansys.tools.visualization_interface as viz_interface from ansys.tools.visualization_interface.types.edge_plot import EdgePlot @@ -33,6 +35,8 @@ from ansys.tools.visualization_interface.utils.color import Color from ansys.tools.visualization_interface.utils.logger import logger +_HAS_PYVISTAQT = importlib.util.find_spec("pyvistaqt") + class PyVistaInterface: """Provides the middle class between PyVista plotting operations and PyAnsys objects. @@ -58,6 +62,9 @@ class PyVistaInterface: for visualization. show_plane : bool, default: False Whether to show the XY plane in the plotter window. + use_qt : bool, default: False + Whether to use the Qt backend for the plotter window. + """ def __init__( @@ -67,16 +74,27 @@ def __init__( num_points: int = 100, enable_widgets: bool = True, show_plane: bool = False, + use_qt: bool = False, **plotter_kwargs, ) -> None: """Initialize the plotter.""" # Generate custom scene if ``None`` is provided if scene is None: if viz_interface.TESTING_MODE: - scene = pv.Plotter(off_screen=True, **plotter_kwargs) + if use_qt and _HAS_PYVISTAQT: + scene = pyvistaqt.BackgroundPlotter(off_screen=True) + else: + if use_qt and not _HAS_PYVISTAQT: + message = "PyVistaQt dependency is not installed. Install it with " + \ + "`pip install ansys-tools-visualization-interface[pyvistaqt]`." + logger.warning(message) + scene = pv.Plotter(off_screen=True, **plotter_kwargs) + elif use_qt: + scene = pyvistaqt.BackgroundPlotter() else: scene = pv.Plotter(**plotter_kwargs) + self._use_qt = use_qt # If required, use a white background with no gradient if not color_opts: color_opts = dict(color="white") @@ -91,7 +109,9 @@ def __init__( # Show the XY plane self._show_plane = show_plane - self.scene.add_axes(interactive=False) + # if (not DOCUMENTATION_BUILD) or (DOCUMENTATION_BUILD and not use_qt) or (TESTING_MODE and not use_qt): + if not use_qt: + self.scene.add_axes(interactive=False) # objects to actors mapping self._object_to_actors_map = {} @@ -335,7 +355,10 @@ def show( if jupyter_backend: self.scene.show(jupyter_backend=jupyter_backend, **kwargs) else: - self.scene.show(**kwargs) + if self._use_qt: + self.scene.show() + else: + self.scene.show(**kwargs) def set_add_mesh_defaults(self, plotting_options: Optional[Dict]) -> None: """Set the default values for the plotting options. diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py deleted file mode 100644 index 69fa4f95..00000000 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvistaqt_interface.py +++ /dev/null @@ -1,420 +0,0 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -"""Module for the PyVistaQt functionalities.""" -import importlib -from typing import Any, Dict, List, Optional - -import pyvista as pv -from vtkmodules.vtkInteractionWidgets import vtkHoverWidget -from vtkmodules.vtkRenderingCore import vtkPointPicker - -import ansys.tools.visualization_interface as viz_interface -from ansys.tools.visualization_interface.backends.pyvista.pyvista import PyVistaBackendInterface -from ansys.tools.visualization_interface.backends.pyvista.pyvista_interface import PyVistaInterface -from ansys.tools.visualization_interface.backends.pyvista.widgets.displace_arrows import ( - CameraPanDirection, - DisplacementArrow, -) -from ansys.tools.visualization_interface.backends.pyvista.widgets.hide_buttons import HideButton -from ansys.tools.visualization_interface.backends.pyvista.widgets.measure import MeasureWidget -from ansys.tools.visualization_interface.backends.pyvista.widgets.ruler import Ruler -from ansys.tools.visualization_interface.backends.pyvista.widgets.screenshot import ScreenshotButton -from ansys.tools.visualization_interface.backends.pyvista.widgets.view_button import ( - ViewButton, - ViewDirection, -) -from ansys.tools.visualization_interface.backends.pyvista.widgets.widget import PlotterWidget -from ansys.tools.visualization_interface.utils.logger import logger - -_HAS_PYVISTAQT = importlib.util.find_spec("pyvistaqt") -if _HAS_PYVISTAQT: - import pyvistaqt - - -class PyVistaQtInterface(PyVistaInterface): - """Provides the middle class between PyVistaQt plotting operations and PyAnsys objects. - - The main purpose of this class is to simplify interaction between PyVista and the PyVistaQt - backend provided. This class is responsible for creating the PyVista scene and adding - the PyAnsys objects to it. - - - Parameters - ---------- - scene : ~pyvista.Plotter, default: None - Scene for rendering the objects. If passed, ``off_screen`` needs to - be set manually beforehand for documentation and testing. - color_opts : dict, default: None - Dictionary containing the background and top colors. - num_points : int, default: 100 - Number of points to use to render the shapes. - enable_widgets : bool, default: True - Whether to enable widget buttons in the plotter window. - Widget buttons must be disabled when using - `trame `_ - for visualization. - show_plane : bool, default: False - Whether to show the XY plane in the plotter window. - """ - def __init__( - self, - scene: Optional[pv.Plotter] = None, - color_opts: Optional[Dict] = None, - num_points: int = 100, - enable_widgets: bool = True, - show_plane: bool = False, - **plotter_kwargs, - ) -> None: - """Initialize the plotter.""" - # Generate custom scene if ``None`` is provided - - if scene is None: - if not _HAS_PYVISTAQT: - message = "PyVistaQt dependency is not installed. Install it with " + \ - "`pip install ansys-tools-visualization-interface[pyvistaqt]`." - logger.warning(message) - elif viz_interface.TESTING_MODE: - scene = pyvistaqt.BackgroundPlotter(off_screen=True) - else: - scene = pyvistaqt.BackgroundPlotter() - - # If required, use a white background with no gradient - if not color_opts: - color_opts = dict(color="white") - - # Create the scene - self._scene = scene - # Scene: assign the background - self._scene.set_background(**color_opts) - - # Save the desired number of points - self._num_points = num_points - - # Show the XY plane - self._show_plane = show_plane - - # objects to actors mapping - self._object_to_actors_map = {} - self._enable_widgets = enable_widgets - self._show_edges = None - - def show( - self, - show_plane: bool = False, - **kwargs: Optional[Dict], - ) -> None: - """Show the rendered scene on the screen. - - Parameters - ---------- - show_plane : bool, default: True - Whether to show the XY plane. - - Notes - ----- - For more information on supported Jupyter backends, see - `Jupyter Notebook Plotting `_ - in the PyVista documentation. - - """ - # Compute the scaling - bounds = self.scene.renderer.bounds - x_length, y_length = bounds[1] - bounds[0], bounds[3] - bounds[2] - sfac = max(x_length, y_length) - - # Create the fundamental XY plane - if show_plane or self._show_plane: - # self.scene.bounds - plane = pv.Plane(i_size=sfac * 1.3, j_size=sfac * 1.3) - self.scene.add_mesh(plane, color="white", show_edges=True, opacity=0.1) - - # Enabling anti-aliasing by default on scene - self.scene.enable_anti_aliasing("ssaa") - - # If screenshot is requested, set off_screen to True for the plotter - if kwargs.get("screenshot") is not None: - self.scene.off_screen = True - else: - self.scene.show() - - -class PyVistaQtBackendInterface(PyVistaBackendInterface): - """Provides the interface for the Visualization Interface Tool plotter for PyVistaQt. - - This class is intended to be used as a base class for the custom plotters - in the different PyAnsys libraries. It provides the basic plotter functionalities, - such as adding objects and enabling widgets and picking capabilities. It also - provides the ability to show the plotter using the `trame `_ - service. - - You can override the ``plot_iter()``, ``plot()``, and ``picked_operation()`` methods. - The ``plot_iter()`` method is intended to plot a list of objects to the plotter, while the - ``plot()`` method is intended to plot a single object to the plotter. The ``show()`` method is - intended to show the plotter. The ``picked_operation()`` method is - intended to perform an operation on the picked objects. - - Parameters - ---------- - allow_picking : Optional[bool], default: False - Whether to allow picking capabilities in the window. Incompatible with hovering. - Picking will take precedence over hovering. - allow_hovering : Optional[bool], default: False - Whether to allow hovering capabilities in the window. Incompatible with picking. - Picking will take precedence over hovering. - plot_picked_names : Optional[bool], default: False - Whether to plot the names of the picked objects. - show_plane : Optional[bool], default: False - Whether to show the plane in the plotter. - """ - def __init__( - self, - allow_picking: Optional[bool] = False, - allow_hovering: Optional[bool] = False, - plot_picked_names: Optional[bool] = False, - show_plane: Optional[bool] = False, - **plotter_kwargs, - ) -> None: - """Initialize the plotter.""" - self._allow_picking = allow_picking - self._allow_hovering = allow_hovering - if self._allow_picking and self._allow_hovering: - logger.warning( - "Picking and hovering are incompatible. Picking will take precedence." - ) - self._allow_hovering = False - self._pv_off_screen_original = bool(pv.OFF_SCREEN) - self._plot_picked_names = plot_picked_names - # Map that relates PyVista actors with PyAnsys objects - self._object_to_actors_map = {} - - # PyVista plotter - self._pl = None - - # Dictionary of picked objects in MeshObject format. - self._picked_dict = {} - - # Map that relates PyVista actors with the added actors by the picker - self._picker_added_actors_map = {} - - # Map that relates PyVista actors with EdgePlot objects - self._edge_actors_map = {} - - # List of widgets added to the plotter. - self._widgets = [] - - # Map that saves original colors of the plotted objects. - self._origin_colors = {} - - self._pl = PyVistaQtInterface(show_plane=show_plane, **plotter_kwargs) - - self._enable_widgets = self._pl._enable_widgets - - self._hover_picker = vtkPointPicker() if self. _allow_hovering else None - self._hover_widget = vtkHoverWidget() if self. _allow_hovering else None - self._added_hover_labels = [] - - def enable_widgets(self): - """Enable the widgets for the plotter.""" - # Create Plotter widgets - if self._enable_widgets: - self._widgets: List[PlotterWidget] = [] - self._widgets.append(Ruler(self._pl._scene)) - [ - self._widgets.append(DisplacementArrow(self._pl._scene, direction=dir)) - for dir in CameraPanDirection - ] - [ - self._widgets.append(ViewButton(self._pl._scene, direction=dir)) - for dir in ViewDirection - ] - self._widgets.append(MeasureWidget(self)) - self._widgets.append(ScreenshotButton(self)) - self._widgets.append(HideButton(self)) - - def close(self): - """Close the plotter for PyVistaQT.""" - self.pv_interface.scene.close() - - def show_plotter(self, screenshot: Optional[str] = None) -> None: - """Show the plotter or start the `trame `_ service. - - Parameters - ---------- - plotter : Plotter - Visualization Interface Tool plotter with the meshes added. - screenshot : str, default: None - Path for saving a screenshot of the image that is being represented. - - """ - self.pv_interface.show(screenshot=screenshot) - pv.OFF_SCREEN = self._pv_off_screen_original - - def show( - self, - plottable_object: Any = None, - screenshot: Optional[str] = None, - view_2d: Dict = None, - name_filter: str = None, - **plotting_options, - ) -> List[Any]: - """Plot and show any PyAnsys object. - - The types of objects supported are ``MeshObjectPlot``, - ``pv.MultiBlock``, and ``pv.PolyData``. - - Parameters - ---------- - plottable_object : Any, default: None - Object or list of objects to plot. - screenshot : str, default: None - Path for saving a screenshot of the image that is being represented. - view_2d : Dict, default: None - Dictionary with the plane and the viewup vectors of the 2D plane. - name_filter : str, default: None - Regular expression with the desired name or names to include in the plotter. - **plotting_options : dict, default: None - Keyword arguments. For allowable keyword arguments, see the - :meth:`Plotter.add_mesh ` method. - - Returns - ------- - List[Any] - List with the picked bodies in the picked order. - - """ - self.plot(plottable_object, name_filter, **plotting_options) - if self._pl.object_to_actors_map: - self._object_to_actors_map = self._pl.object_to_actors_map - else: - logger.warning("No actors were added to the plotter.") - - # Compute mapping between the objects and its edges. - _ = self.compute_edge_object_map() - - if view_2d is not None: - self._pl.scene.view_vector( - vector=view_2d["vector"], - viewup=view_2d["viewup"], - ) - - if self._allow_picking: - self.enable_picking() - elif self._allow_hovering: - self.enable_hover() - - # Update all buttons/widgets - [widget.update() for widget in self._widgets] - - self.show_plotter(screenshot) - - picked_objects_list = [] - if isinstance(plottable_object, list): - # Keep them ordered based on picking - for meshobject in self._picked_dict.values(): - for elem in plottable_object: - if hasattr(elem, "name") and elem.name == meshobject.name: - picked_objects_list.append(elem) - elif hasattr(plottable_object, "name") and plottable_object.name in self._picked_dict: - picked_objects_list = [plottable_object] - else: - # Return the picked objects in the order they were picked - picked_objects_list = list(self._picked_dict.values()) - - return picked_objects_list - - -class PyVistaQtBackend(PyVistaQtBackendInterface): - """Provides the generic plotter implementation for PyAnsys libraries. - - This class accepts ``MeshObjectPlot``, ``pv.MultiBlock`` and ``pv.PolyData`` objects. - - Parameters - ---------- - use_trame : bool, default: None - Whether to enable the use of `trame `_. - The default is ``None``, in which case the ``USE_TRAME`` global setting - is used. - allow_picking : Optional[bool], default: False - Whether to allow picking capabilities in the window. Incompatible with hovering. - Picking will take precedence over hovering. - allow_hovering : Optional[bool], default: False - Whether to allow hovering capabilities in the window. Incompatible with picking. - Picking will take precedence over hovering. - plot_picked_names : bool, default: True - Whether to plot the names of the picked objects. - - """ - - def __init__( - self, - use_trame: Optional[bool] = None, - allow_picking: Optional[bool] = False, - allow_hovering: Optional[bool] = False, - plot_picked_names: Optional[bool] = True - ) -> None: - """Initialize the generic plotter.""" - super().__init__(use_trame, allow_picking, allow_hovering, plot_picked_names) - - def plot_iter( - self, - plotting_list: List[Any], - name_filter: str = None, - **plotting_options, - ) -> None: - """Plot the elements of an iterable of any type of object to the scene. - - The types of objects supported are ``Body``, ``Component``, ``List[pv.PolyData]``, - ``pv.MultiBlock``, and ``Sketch``. - - Parameters - ---------- - plotting_list : List[Any] - List of objects to plot. - name_filter : str, default: None - Regular expression with the desired name or names to include in the plotter. - **plotting_options : dict, default: None - Keyword arguments. For allowable keyword arguments, see the - :meth:`Plotter.add_mesh ` method. - - """ - for plottable_object in plotting_list: - self.plot(plottable_object, name_filter, **plotting_options) - - def plot(self, plottable_object: Any, name_filter: str = None, **plotting_options): - """Plot a ``pyansys`` or ``PyVista`` object to the plotter. - - Parameters - ---------- - plottable_object : Any - Object to add. - name_filter : str - Regular expression with the desired name or names to include in the plotter. - **plotting_options : dict, default: None - Keyword arguments. For allowable keyword arguments, see the - :meth:`Plotter.add_mesh ` method. - - """ - if hasattr(plottable_object, "__iter__"): - logger.debug("Plotting objects in list...") - self.pv_interface.plot_iter(plottable_object, name_filter, **plotting_options) - else: - self.pv_interface.plot(plottable_object, name_filter, **plotting_options) diff --git a/src/ansys/tools/visualization_interface/plotter.py b/src/ansys/tools/visualization_interface/plotter.py index ad7f3c20..33bfbfd3 100644 --- a/src/ansys/tools/visualization_interface/plotter.py +++ b/src/ansys/tools/visualization_interface/plotter.py @@ -62,7 +62,7 @@ def show( screenshot: str = None, name_filter: bool = None, **plotting_options - ) -> None: + ) -> None: """Show the plotted objects. Parameters diff --git a/tests/test_generic_plotter.py b/tests/test_generic_plotter.py index 91236a15..0a03876e 100644 --- a/tests/test_generic_plotter.py +++ b/tests/test_generic_plotter.py @@ -28,9 +28,7 @@ import pyvista as pv from ansys.tools.visualization_interface import ClipPlane, MeshObjectPlot, Plotter -from ansys.tools.visualization_interface.backends.pyvista.pyvistaqt_interface import ( - PyVistaQtBackend, -) +from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" @@ -52,8 +50,8 @@ def test_plotter_add_pd(): @pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Qt breaks CICD.") def test_plotter_pyvistaqt(): - """Run PyVistaQt backend.""" - qt_backend = PyVistaQtBackend() + """Adds polydata to the plotter.""" + qt_backend = PyVistaBackend(use_qt=True) pl = Plotter(backend=qt_backend) sphere = pv.Sphere() pl.plot(sphere) From 7915d3b57744ca954d98f9825d67e7e5740d1201 Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 13:20:05 +0100 Subject: [PATCH 45/49] fix: Review comments --- .github/workflows/ci_cd.yml | 6 ++---- examples/00-basic-pyvista-examples/qt_backend.py | 12 ++++++------ pyproject.toml | 2 -- .../backends/pyvista/pyvista.py | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 904f3c95..67aee464 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -69,10 +69,8 @@ jobs: docs-build: name: Documentation Build runs-on: ubuntu-latest - # needs: [docs-style] + needs: [docs-style] steps: - - name: Install system dependencies - run: sudo apt install -qq libglu1-mesa-dev libx11-xcb-dev '^libxcb*' - name: Setup headless display uses: pyvista/setup-headless-display-action@v2 @@ -104,7 +102,7 @@ jobs: testing: name: Run Unit Tests - # needs: [ smoke-tests ] + needs: [ smoke-tests ] runs-on: ubuntu-latest steps: - name: Install system dependencies diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py index baf8ff05..a74e2226 100644 --- a/examples/00-basic-pyvista-examples/qt_backend.py +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -44,12 +44,12 @@ # ======================= # .. code-block:: python # -# cube = pv.Cube() -# pv_backend = PyVistaBackend(use_qt=True) -# pl = Plotter(backend=pv_backend) -# pl.plot(cube) -# pl.show() -# +cube = pv.Cube() +pv_backend = PyVistaBackend(use_qt=True) +pl = Plotter(backend=pv_backend) +pl.plot(cube) +pl.show() + ##################### diff --git a/pyproject.toml b/pyproject.toml index 13ffefb4..b3568b33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,8 +49,6 @@ doc = [ "jupytext==1.16.4", "nbsphinx==0.9.5", "numpydoc==1.8.0", - "pyside6==6.7.3", - "pyvistaqt==0.11.1,<1", "sphinx==8.1.3", "sphinx-autoapi==3.3.3", "sphinx-copybutton==0.5.2", diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py index e8424fd2..c24a8379 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista.py @@ -603,4 +603,4 @@ def plot(self, plottable_object: Any, name_filter: str = None, **plotting_option def close(self): """Close the plotter for PyVistaQT.""" if self._use_qt: - self.pv_interface.scene.close() \ No newline at end of file + self.pv_interface.scene.close() From a4ed1183b9727e51ce0999e5cd5b05e3c10beabf Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 13:29:27 +0100 Subject: [PATCH 46/49] fix: Commented code --- .../backends/pyvista/pyvista_interface.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index b4a98291..571af2b4 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -109,7 +109,6 @@ def __init__( # Show the XY plane self._show_plane = show_plane - # if (not DOCUMENTATION_BUILD) or (DOCUMENTATION_BUILD and not use_qt) or (TESTING_MODE and not use_qt): if not use_qt: self.scene.add_axes(interactive=False) From b5995f1f2f596b159ac4c53cf9e2af48621dcaff Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 13:34:38 +0100 Subject: [PATCH 47/49] fix: Revert code block comment in examples --- examples/00-basic-pyvista-examples/qt_backend.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/00-basic-pyvista-examples/qt_backend.py b/examples/00-basic-pyvista-examples/qt_backend.py index a74e2226..baf8ff05 100644 --- a/examples/00-basic-pyvista-examples/qt_backend.py +++ b/examples/00-basic-pyvista-examples/qt_backend.py @@ -44,12 +44,12 @@ # ======================= # .. code-block:: python # -cube = pv.Cube() -pv_backend = PyVistaBackend(use_qt=True) -pl = Plotter(backend=pv_backend) -pl.plot(cube) -pl.show() - +# cube = pv.Cube() +# pv_backend = PyVistaBackend(use_qt=True) +# pl = Plotter(backend=pv_backend) +# pl.plot(cube) +# pl.show() +# ##################### From 74f6b8773ca8ff89b9410328d5c60c89a5b64abd Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 14:13:37 +0100 Subject: [PATCH 48/49] fix: Add missing import check --- .../backends/pyvista/pyvista_interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py index 571af2b4..8d2110ee 100644 --- a/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py +++ b/src/ansys/tools/visualization_interface/backends/pyvista/pyvista_interface.py @@ -26,7 +26,6 @@ import pyvista as pv from pyvista.plotting.plotter import Plotter as PyVistaPlotter -import pyvistaqt import ansys.tools.visualization_interface as viz_interface from ansys.tools.visualization_interface.types.edge_plot import EdgePlot @@ -36,7 +35,8 @@ from ansys.tools.visualization_interface.utils.logger import logger _HAS_PYVISTAQT = importlib.util.find_spec("pyvistaqt") - +if _HAS_PYVISTAQT: + import pyvistaqt class PyVistaInterface: """Provides the middle class between PyVista plotting operations and PyAnsys objects. From 6c6fcc58791541d1266bbbcee1a1bba3783a0c8d Mon Sep 17 00:00:00 2001 From: afernand Date: Fri, 22 Nov 2024 15:22:40 +0100 Subject: [PATCH 49/49] fix: Align env variables --- tests/test_generic_plotter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_generic_plotter.py b/tests/test_generic_plotter.py index 0a03876e..b931b77e 100644 --- a/tests/test_generic_plotter.py +++ b/tests/test_generic_plotter.py @@ -30,7 +30,7 @@ from ansys.tools.visualization_interface import ClipPlane, MeshObjectPlot, Plotter from ansys.tools.visualization_interface.backends.pyvista import PyVistaBackend -IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" +IN_GITHUB_ACTIONS = os.getenv("IN_GITHUB_ACTIONS") == "true" class CustomTestClass: