PyVista + Trame = Jupyter 3D Visualization

We’ve recently been improving the support between PyVista and Trame to provide a cutting-edge viewer for Jupyter. This is included in PyVista’s latest 0.38.1 release!

pip install 'pyvista[jupyter]>=0.38.1'
  1. Trame provides a high-level framework for building reactive, stateful web applications
  2. PyVista provides a high-level framework for 3D visualization, exposing VTK in a “Pythonic” manner

High-level framework 1 + high-level framework 2 = a streamlined approach to making powerful web applications with 3D visualization front and center.

An example of this I am most thrilled to share is with Jupyter Notebooks. For PyVista’s latest release, we included a Jupyter widget built on Trame that will connect to a PyVista Plotter to stream both server-rendered visualizations and/or the scene data for client-side rendering with VTK.js.

I’d include a video, but I’m limited to upload sizes of 4Mb. Perhaps check out my tweet:

https://twitter.com/banesullivan/status/1621535825103126530?s=20&t=h4BRJe_dn-jcU7Boowh16A



More information

Feel free to post questions here, and I’d be happy share more!

12 Likes

I also want to highlight a long-requested feature for PyVista from VTK power-users: vtkAgorithm support with PyVista’s Plotter API. This was also added in PyVista’s 0.38.1 release, and we have examples here:

2 Likes

FYI @Thomas_Galland @Timothee_Chabat @Charles_Gueunet

May I ask how to deploy this with jupyter-notebook in container?

You only need an offscreen version of vtk and trame.

pip uninstall vtk
pip install vtk-osmesa --extra-index-url https://wheels.vtk.org # or vtk-egl if you have a GPU
pip install trame trame-vtk trame-vuetify

Then in a cell you can do

from trame.app import get_server
from trame.decorators import TrameApp, change
from trame.widgets import vuetify, vtk as vtk_widgets
from trame.ui.vuetify import SinglePageLayout

from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import (
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkPolyDataMapper,
    vtkActor,
)

# VTK factory initialization
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch  # noqa
import vtkmodules.vtkRenderingOpenGL2  # noqa

@TrameApp()
class Cone:
    def __init__(self, server_or_name=None):
        self.server = get_server(server_or_name, client_type="vue2")
        self._vtk_rw, self._vtk_cone = self._vtk_setup()
        self.ui = self._generate_ui()

    @property
    def ctrl(self):
        return self.server.controller

    @property
    def state(self):
        return self.server.state

    @change("resolution")
    def on_resolution_change(self, resolution, **kwargs):
        self._vtk_cone.SetResolution(resolution)
        self.ctrl.view_update()

    @property
    def resolution(self):
        return self.state.resolution

    @resolution.setter
    def resolution(self, v):
        with self.state:
            self.state.resolution = v

    def reset_resolution(self):
        self.resolution = 6

    def _vtk_setup(self):
        renderer = vtkRenderer()
        renderWindow = vtkRenderWindow()
        renderWindow.AddRenderer(renderer)
        renderWindow.OffScreenRenderingOn()

        renderWindowInteractor = vtkRenderWindowInteractor()
        renderWindowInteractor.SetRenderWindow(renderWindow)
        renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

        cone_source = vtkConeSource()
        mapper = vtkPolyDataMapper()
        actor = vtkActor()
        mapper.SetInputConnection(cone_source.GetOutputPort())
        actor.SetMapper(mapper)
        renderer.AddActor(actor)
        renderer.ResetCamera()
        renderWindow.Render()

        return renderWindow, cone_source

    def _generate_ui(self):
        with SinglePageLayout(self.server) as layout:
            layout.title.set_text("Trame demo")
            with layout.toolbar as toolbar:
                toolbar.dense = True
                vuetify.VSpacer()
                vuetify.VSlider(
                    v_model=("resolution", 6),
                    min=3,
                    max=60,
                    step=1,
                    hide_details=True,
                    style="max-width: 300px;",
                )
                with vuetify.VBtn(icon=True, click=self.reset_resolution):
                    vuetify.VIcon("mdi-lock-reset")
                with vuetify.VBtn(icon=True, click=self.ctrl.view_reset_camera):
                    vuetify.VIcon("mdi-crop-free")

            with layout.content:
                with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
                    view = vtk_widgets.VtkRemoteView(self._vtk_rw)
                    self.ctrl.view_update = view.update
                    self.ctrl.view_reset_camera = view.reset_camera

            return layout

Then in another cell you can do

app = Cone()
await app.ui.ready
app.ui
1 Like

I start the environment with jupyter-notebook in docker container. With the codes you provided, I got the error:
ERROR:root:Exception raised by task = <Task finished name=‘Task-5’ coro=<WebAppServer.start() done, defined at /usr/local/envs/ssenv/lib/python3.8/site-packages/wslink/backends/aiohttp/init.py:110> exception=OSError(99, “error while attempting to bind on address (‘::1’, 0, 0, 0): cannot assign requested address”)>
Traceback (most recent call last):
File “/usr/local/envs/ssenv/lib/python3.8/site-packages/trame_server/core.py”, line 686, in on_done
task.result()
File “/usr/local/envs/ssenv/lib/python3.8/site-packages/wslink/backends/aiohttp/init.py”, line 123, in start
await self._site.start()
File “/usr/local/envs/ssenv/lib/python3.8/site-packages/aiohttp/web_runner.py”, line 119, in start
self._server = await loop.create_server(
File “/usr/local/envs/ssenv/lib/python3.8/asyncio/base_events.py”, line 1463, in create_server
raise OSError(err.errno, ‘error while attempting ’
OSError: [Errno 99] error while attempting to bind on address (’::1’, 0, 0, 0): cannot assign requested address

Try to set ENV TRAME_DEFAULT_HOST=127.0.0.1

Interactive visualisation using PyVista
I was trying to generate an interactive visualisation but it was throwing errors,
so installed these modules along with ipywidgets to generate viz in jupyter notebook and it worked fine.
Hope this might help someone