PyVista and Sphinx#

Leverage PyVista to make some awesome interactive web documentation.

Tip

This section of the tutorial was adopted from Plotting Themes and Sphinx PyVista Plot Directive chapter of the PyVista documentation.

Dynamically Generating 3D Plots in your Documentation#

PyVista allows you to generate static or dynamic images directly within Sphinx much like the Matplotlib plot_directive. Rather than manually creating and adding plots after code sections, you can instead dynamically generate images and embed them directly within your documentation. This also works seamlessly with sphinx-gallery, so you can create notebook examples just like the Matplotlib Example Gallery.

As an added side benefit, you can be sure that the documentation you generate matches your project API. If you include this within a GitHub Workflow

This section covers the following topics.

Static Plotting - Sphinx PyVista Plot Directive#

You can generate static images of PyVista plots within sphinx using the pyvista-plot directive by adding the following to your conf.py when building your documentation using Sphinx.

extensions = [
    "sphinx.ext.napoleon",
    "pyvista.ext.plot_directive",
]

You can then issue the plotting directive within your sphinx documentation files:

.. pyvista-plot::
   :caption: A sphere
   :include-source: True

   >>> import pyvista
   >>> sphere = pyvista.Sphere()
   >>> out = sphere.plot()

Which will be rendered as:

>>> import pyvista
>>> sphere = pyvista.Sphere()
>>> out = sphere.plot()
../../_images/index-1_00_003.png

This is a default sphere#

Dynamic Plotting Using the Jupyter Sphinx Extension#

PyVista also supports the jupyter-sphinx extension. With this extension you can execute code blocks within a sphinx *.rst file using the jupyter-execute directive:

.. jupyter-execute::

   name = 'world'
   print('hello ' + name + '!')

Will be rendered as:

name = 'world'
print('hello ' + name + '!')
hello world!

Likewise, output from PyVista that would normally be rendered within a notebook will be rendered in the output cell from the jupyter-execute directive. For example, here’s a plot using the pythreejs backend:

.. jupyter-execute::

   from pyvista import examples
   dataset = examples.download_urn()
   dataset.plot(color='tan', jupyter_backend='pythreejs', window_size=(700, 400))

Which is rendered as:

from pyvista import examples
dataset = examples.download_urn()
dataset.plot(color='tan', jupyter_backend='pythreejs', window_size=(700, 400))

Using the Panel backend with PyVista#

PyVista supports the usage of the panel library as a vtk.js jupyterlab plotting backend that can be utilized as either a standalone VTK viewer, or as a tightly integrated pyvista plotting backend. For example, within a Jupyter notebook environment, you can pass jupyter_backend='panel' to plot, or Plotter.show to automatically enable plotting with Juptyer and panel.

For example, here’s the PyVista logo:

.. jupyter-execute::

   from pyvista import demos
   demos.plot_logo(background='white', jupyter_backend='panel')

Which is shown within the documentation as:

from pyvista import demos
demos.plot_logo(background='white', jupyter_backend='panel')

Examples and Usage#

There are two ways to use panel within Jupyter notebooks. You can use it on a plot by plot basis by setting the jupyter_backend in mesh.plot():

.. jupyter-execute::

    import pyvista as pv
    from pyvista import examples

    # create a point cloud from lidar data and add height scalars
    dataset = examples.download_lidar()
    point_cloud = pv.PolyData(dataset.points[::100])
    point_cloud['height'] = point_cloud.points[:, 2]
    point_cloud.plot(window_size=[500, 500],
                     jupyter_backend='panel',
                     cmap='jet',
                     point_size=2,
                     background='w')

And here’s the resulting output in Sphinx:

import pyvista as pv
from pyvista import examples

# create a point cloud from lidar data and add height scalars
dataset = examples.download_lidar()
point_cloud = pv.PolyData(dataset.points[::100])
point_cloud['height'] = point_cloud.points[:, 2]
point_cloud.plot(window_size=[500, 500],
                 jupyter_backend='panel',
                 cmap='jet',
                 point_size=2,
                 background='w')

Or you can first hide code that sets up the plotting backend and global theme:

.. jupyter-execute::
    :hide-code:

    import pyvista as pv

    # Set the global jupyterlab backend.  All plots from this point
    # onward will use the ``panel`` backend and do not have to be
    # specified in ``show``
    pv.set_jupyter_backend('panel')

And now just directly execute plot on any dataset:

.. jupyter-execute::

   from pyvista import examples
   dataset = examples.download_dragon()
   dataset.plot(cpos="xy")

Which looks like:

from pyvista import examples
dataset = examples.download_dragon()
dataset.plot(cpos="xy")