render_pyvista

render_pyvista class

Reactively render a pyvista 3D plot.

Usage Source

render_pyvista(
    _fn: Optional[ValueFn[ValueT]] = None,
    *,
    width: Optional[str] = None,
    height: Optional[str] = None,
    fill: Optional[bool] = None,
    fillable: Optional[bool] = None,
    rate_policy: Optional[RatePolicy] = None,
    rate_delay: Optional[float] = None,
)

@render_pyvista is a decorator that creates a render function – a function that returns the plot to display and re-runs when the reactive values it reads change:

import pyvista as pv

from shinywidgets import render_pyvista

pv.OFF_SCREEN = True


@render_pyvista
def plot():
    pl = pv.Plotter()
    pl.add_mesh(pv.Sphere())
    return pl

The render function may return either a pyvista.Plotter (which is coerced to an ipywidget via pyvista’s jupyter_backend="html" export; requires pip install 'shinywidgets[pyvista]') or the exported trame_pyvista.jupyter.EmbeddableWidget itself. In a filling layout, the view fills its container unless an explicit height is given. On a headless server, set pyvista.OFF_SCREEN = True before plotting.

Return the Plotter itself – do not call Plotter.show(). Outside Jupyter, show() ignores jupyter_backend, returns None, and the output renders nothing. If you pre-build the widget (e.g., via trame_pyvista.jupyter.show_trame(pl, mode="html")), pass width="100%", height="100%" or it keeps the plotter’s pixel window_size instead of filling the container.

The export is a self-contained, client-side vtk.js view: camera interaction (rotate/zoom/pan) happens entirely in the browser. The vtk.js runtime ships once per session (shared by every pyvista output in the app); each render then embeds only the exported scene in the widget state, so input-driven re-renders are cheap. Still, very large scenes (millions of cells) produce multi-MB exports, so prefer fewer, larger re-renders for those.

Note

Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered EmbeddableWidget instance. Note that, unlike most other render_* decorators, the widget has no live connection to the plotter.

Responding to user interaction: none: no widget state is synced back to Python, so there is nothing to read with reactive_read(). (For server-driven interactivity, use pyvista’s jupyter_backend="trame" with a trame server embedded via shiny.ui.tags.iframe instead – shinywidgets can’t host trame’s websocket server.)

Mutating the widget: the scene can’t be updated in place via .widget; re-render instead.

Parameters

width : Optional[str] = None

A CSS length unit (e.g., "400px" or "100%") for the output’s width.

height : Optional[str] = None

A CSS length unit for the output’s height. When None (the default), the output is designed to fill the height of its parent container (see fill and fillable).

fill : Optional[bool] = None

Whether the output should grow or shrink to fit its parent container. When None (the default), this is True if height is not specified.

fillable : Optional[bool] = None

Whether the output container should fill available space (scrolling its contents on overflow). When None (the default), this is True if height is not specified.

Examples

import pyvista as pv
from shiny import App, ui
from shinywidgets import output_widget, render_pyvista

pv.OFF_SCREEN = True

app_ui = ui.page_fillable(output_widget("plot"))


def server(input, output, session):
    @render_pyvista
    def plot():
        pl = pv.Plotter()
        pl.add_mesh(pv.Sphere(), show_edges=True)
        return pl


app = App(app_ui, server)

See also

output_widget(): in Shiny Core, the UI needs this to hold the output (Shiny Express displays it where the render function is defined).