This commit is contained in:
cjw
2026-02-12 23:22:11 +08:00
parent 7b09eb3d89
commit 89660bba4e
5988 changed files with 2517516 additions and 0 deletions
@@ -0,0 +1,116 @@
"""Jupyter notebook plotting module."""
from __future__ import annotations
import importlib.util
from typing import Literal
from typing import get_args
from typing_extensions import TypeIs
import pyvista
from pyvista.core.errors import PyVistaDeprecationWarning as PyVistaDeprecationWarning
JupyterBackendOptions = Literal['static', 'client', 'server', 'trame', 'html', 'none']
ALLOWED_BACKENDS = get_args(JupyterBackendOptions)
def _is_jupyter_backend(backend: str) -> TypeIs[JupyterBackendOptions]:
"""Return True if backend is allowed jupyter backend."""
return backend in ALLOWED_BACKENDS
def _validate_jupyter_backend(
backend: str | None,
) -> JupyterBackendOptions:
"""Validate that a jupyter backend is valid.
Returns the normalized name of the backend. Raises if the backend is invalid.
"""
# Must be a string
if backend is None:
backend = 'none'
backend = backend.lower()
if not importlib.util.find_spec('IPython'):
msg = 'Install IPython to display with pyvista in a notebook.'
raise ImportError(msg)
if not _is_jupyter_backend(backend):
backend_list_str = ', '.join([f'"{item}"' for item in ALLOWED_BACKENDS])
msg = (
f'Invalid Jupyter notebook plotting backend "{backend}".\n'
f'Use one of the following:\n{backend_list_str}'
)
raise ValueError(msg)
if backend in ['server', 'client', 'trame', 'html']:
try:
from pyvista.trame.jupyter import show_trame as show_trame # noqa: PLC0415
except ImportError: # pragma: no cover
msg = 'Please install trame dependencies: pip install "pyvista[jupyter]"'
raise ImportError(msg)
return backend
def set_jupyter_backend(backend, name=None, **kwargs): # noqa: ARG001
"""Set the plotting backend for a jupyter notebook.
Parameters
----------
backend : str
Jupyter backend to use when plotting. Must be one of the following:
* ``'static'`` : Display a single static image within the
Jupyterlab environment. Still requires that a virtual
framebuffer be set up when displaying on a headless server,
but does not require any additional modules to be installed.
* ``'client'`` : Export/serialize the scene graph to be rendered
with VTK.js client-side through ``trame``. Requires ``trame``
and ``jupyter-server-proxy`` to be installed.
* ``'server'``: Render remotely and stream the resulting VTK
images back to the client using ``trame``. This replaces the
``'ipyvtklink'`` backend with better performance.
Supports the most VTK features, but suffers from minor lag due
to remote rendering. Requires that a virtual framebuffer be set
up when displaying on a headless server. Must have at least ``trame``
and ``jupyter-server-proxy`` installed for cloud/remote Jupyter
instances. This mode is also aliased by ``'trame'``.
* ``'trame'``: The full Trame-based backend that combines both
``'server'`` and ``'client'`` into one backend. This requires a
virtual frame buffer.
* ``'html'`` : Export/serialize the scene graph to be rendered
with the Trame client backend but in a static HTML file.
* ``'none'`` : Do not display any plots within jupyterlab,
instead display using dedicated VTK render windows. This
will generate nothing on headless servers even with a
virtual framebuffer.
name : str, optional
The unique name identifier for the server.
**kwargs : dict, optional
Any additional keyword arguments to pass to the server launch.
Examples
--------
Enable the trame Trame backend.
>>> pv.set_jupyter_backend('trame') # doctest:+SKIP
Just show static images.
>>> pv.set_jupyter_backend('static') # doctest:+SKIP
Disable all plotting within JupyterLab and display using a
standard desktop VTK render window.
>>> pv.set_jupyter_backend(None) # doctest:+SKIP
"""
pyvista.global_theme._jupyter_backend = _validate_jupyter_backend(backend)
@@ -0,0 +1,75 @@
"""Support dynamic or static jupyter notebook plotting.
Includes:
* ``trame``
* ``client``
* ``server``
* ``html``
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import warnings
import numpy as np
if TYPE_CHECKING:
import io
from pathlib import Path
from IPython.lib.display import IFrame
from PIL.Image import Image
from pyvista.jupyter import JupyterBackendOptions
from pyvista.plotting.plotter import Plotter
from pyvista.trame.jupyter import EmbeddableWidget
from pyvista.trame.jupyter import Widget
def handle_plotter(
plotter: Plotter,
backend: JupyterBackendOptions | None = None,
screenshot: str | Path | io.BytesIO | bool | None = None, # noqa: FBT001
**kwargs,
) -> EmbeddableWidget | IFrame | Widget | Image:
"""Show the ``pyvista`` plot in a jupyter environment.
Returns
-------
IPython Widget
IPython widget or image.
"""
if screenshot is False:
screenshot = None
try:
if backend in ['server', 'client', 'trame', 'html']:
from pyvista.trame.jupyter import show_trame # noqa: PLC0415
return show_trame(plotter, mode=backend, **kwargs)
except ImportError as e:
warnings.warn(
f'Failed to use notebook backend: \n\n{e}\n\nFalling back to a static output.',
)
return show_static_image(plotter, screenshot)
def show_static_image(
plotter: Plotter,
screenshot: str | Path | io.BytesIO | bool | None, # noqa: FBT001
) -> Image: # numpydoc ignore=RT01
"""Display a static image to be displayed within a jupyter notebook."""
import PIL.Image # noqa: PLC0415
if plotter.last_image is None:
# Must render here, otherwise plotter will segfault.
plotter.render()
plotter.last_image = plotter.screenshot(screenshot, return_img=True)
assert isinstance(plotter.last_image, np.ndarray)
return PIL.Image.fromarray(plotter.last_image)