Render PyVista plotter scenes through Blender (bpy) for photoreal output.
Project description
pyvista-blender
[!WARNING] Very early development. APIs, defaults, and module layout will change without notice until the first stable release. Bug reports, missing-feature requests, and PRs are all very welcome — please open an issue if something doesn't work or behaves surprisingly.
Render PyVista plotter scenes through
Blender (bpy) — photoreal output, full
animation export, no file round-trip.
Build the scene once in PyVista. Pick VTK's preview or Blender's
Cycles / Eevee Next for the final render. Export entire deforming,
colour-evolving, lit animations to a single .blend that plays
natively in Blender with no Python script inside.
Showcase
These structural mechanics examples start as PyVista scenes and use Blender for the final render. The transparent animated WebP previews link to the transparent VP9 WebM files.
|
|
|
| I-beam bending Fedoo stress animation with support and load geometry. |
Gyroid compression Metallic gyroid material revealed through a stress-alpha overlay. |
Status
Initial public release (0.1.0). The offline render path, the desktop
and browser interactive viewports, the Jupyter inline backend, and the
full animation export to .blend are implemented and tested. The full
ruff (select = ["ALL"]) + ty + format gate is clean.
See the features list in the docs for the
full surface and the architecture page for
the translation pipeline.
Install
pip install pyvista-blender
# or
uv add pyvista-blender
For the Jupyter inline backend and the Trame-served browser viewport
(pl.blender.show(backend="web")), install pyvista[jupyter]
alongside; the bridge reuses pyvista's Trame stack rather than
shipping its own extras.
pip install pyvista-blender 'pyvista[jupyter]'
Requires Python 3.11 (Blender 4.5 LTS via bpy>=4.5,<5) or
3.13 (Blender 5.x via bpy>=5.0,<6). Python 3.10, 3.12, and 3.14
have no matching bpy wheel on PyPI; requires-python blocks them.
Quick start
import pyvista as pv
pl = pv.Plotter(off_screen=True, window_size=(1920, 1080))
pl.add_mesh(pv.read("data.vtu"), scalars="von_mises", cmap="viridis", pbr=True)
pl.add_light(pv.Light(position=(5, -5, 5), light_type="scene light"))
pl.camera_position = "iso"
# Offline render via Cycles + OptiX denoise. No `import pyvista_blender`
# needed — the namespace registers itself on install through PyVista
# 0.48's plotter-component entry point.
pl.blender.render("frame.png", samples=128)
# Interactive hybrid viewport: VTK 60 fps during drag, Cycles render
# on mouse release. One window, mouse-driven, no auto-exec prompts.
pl.blender.show()
API surface
| Entry point | What it does |
|---|---|
pl.blender.render(path, ...) |
Render a single frame to PNG via Cycles or Eevee Next. |
pl.blender.animate(path, updater, frames, fps=...) |
Render an animation as gif / mp4 / webm / mov / mkv via imageio + imageio-ffmpeg. |
pl.blender.show() |
Hybrid VTK / Cycles interactive viewport — VTK during drag, settled Cycles on release. |
pl.blender.add_glyph(source, geom, *, orient, scale, factor) |
Geometry-Nodes-instanced glyphs; memory cost scales N + V instead of N · V. |
pl.blender.export_blend(path) |
Save the live PyVista scene as a .blend for finishing in Blender. |
pl.blender.export_animation_blend(path, updater, frames, fps, **bake) |
Bake a full animation into a .blend that plays natively on file open. |
pl.blender.orbit_camera(n_frames=...) |
Build an updater for animate / export_animation_blend. |
Three-tier config resolution everywhere: per-call kwarg → component
attribute (pl.blender.engine = ...) → module default
(pyvista_blender.config.*).
Animation export to .blend
export_animation_blend bakes each animation channel through its own
opt-in kwarg. Every backend plays back natively when the file is
opened in Blender — no Python in the .blend, no auto-execution
prompt, no need to "Run Scripts" on load.
| Kwarg | Mechanism | File layout |
|---|---|---|
bake_camera=True (default) |
Keyframes on scene.camera.location + rotation_quaternion per frame |
inside .blend |
bake_deformation="shape_keys" |
One Shape Key per frame, value-keyframed with linear interpolation | inside .blend |
bake_deformation="mdd" (= True) |
Lightwave MDD sidecar + MESH_CACHE modifier |
.blend + __<mesh>.mdd |
bake_scalars=True |
Packed PNG (rows = frames, columns = vertices / cells) + Geometry Nodes graph; works for point-data and cell-data scalars | inside .blend |
bake_lights=True |
Per-light keyframes on location, rotation, energy, color |
inside .blend |
Example: full multi-channel animation in one self-contained file.
import numpy as np
import pyvista as pv
pl = pv.Plotter(off_screen=True, window_size=(1280, 720))
plane = pv.Plane(i_resolution=60, j_resolution=60)
plane.cell_data["heat"] = np.zeros(plane.n_cells, dtype=np.float32)
pl.add_mesh(plane, scalars="heat", cmap="inferno", clim=[-1.0, 1.0])
light = pv.Light(position=(5, 0, 4), light_type="scene light", intensity=1.0)
pl.add_light(light)
n_frames = 60
orbit = pl.blender.orbit_camera(n_frames=n_frames)
centers_xy = plane.cell_centers().points[:, :2]
def update(frame: int) -> None:
orbit(frame)
angle = -frame * (2.0 * np.pi / n_frames)
light.position = (5.0 * np.cos(angle), 5.0 * np.sin(angle), 5.0)
light.intensity = 0.4 + 0.6 * (0.5 + 0.5 * np.sin(frame * 0.3))
r = np.linalg.norm(centers_xy, axis=1)
plane.cell_data["heat"] = np.sin(2.0 * r - frame * 0.2).astype(np.float32)
pl.blender.export_animation_blend(
"scene.blend",
update,
frames=range(n_frames),
fps=30,
bake_camera=True,
bake_lights=True,
bake_scalars=True,
)
pl.close()
Open scene.blend in Blender 4.5+ / 5.x and press Space:
camera orbits, the explicit light orbits in the opposite direction
while pulsing, the heat field travels as a radial wave with flat per-
cell shading. One file, ~500 KB, nothing to ship alongside.
Development
uv sync --group dev --no-install-package bpy # fast local sync (no 200 MB bpy wheel)
uvx prek install # install pre-commit hooks
uv run pytest # ~10 s with bpy installed
uv run ruff check src/ tests/
uv run ruff format --check src/ tests/
uv run ty check
Real bpy is only needed for the @pytest.mark.bpy tests. CI runs
the full suite against the live wheel.
License
GPL-3.0-or-later. Forced by bpy's license; every source file
carries an SPDX header. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyvista_blender-0.1.0.tar.gz.
File metadata
- Download URL: pyvista_blender-0.1.0.tar.gz
- Upload date:
- Size: 136.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00cb7e52aee3485ff9670bd1ebe4a3724489644d6675852ccba39cafcaad7ca0
|
|
| MD5 |
4f13785ef821fc1ff8eb9cb324e8207d
|
|
| BLAKE2b-256 |
cbc2acf1f432f2936ce142ab4784b9d3f555c8221130e3e56649804e3b1fe0f9
|
Provenance
The following attestation bundles were made for pyvista_blender-0.1.0.tar.gz:
Publisher:
release.yml on kmarchais/pyvista-blender
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyvista_blender-0.1.0.tar.gz -
Subject digest:
00cb7e52aee3485ff9670bd1ebe4a3724489644d6675852ccba39cafcaad7ca0 - Sigstore transparency entry: 2088590212
- Sigstore integration time:
-
Permalink:
kmarchais/pyvista-blender@c1550ec92aafc211b379d3723a48aa5a1d5feec5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kmarchais
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c1550ec92aafc211b379d3723a48aa5a1d5feec5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyvista_blender-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyvista_blender-0.1.0-py3-none-any.whl
- Upload date:
- Size: 156.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d0bf07dfdda51af183c6d3166bc86dfbc0e8a2a5bd5b66ee72e1d247924b727
|
|
| MD5 |
5c404f9561f84bc43ea32afab3bb0664
|
|
| BLAKE2b-256 |
94f5d08defd5bfdcc4933da36edc0c8beb143e8cd583450bc503fb899d4caec4
|
Provenance
The following attestation bundles were made for pyvista_blender-0.1.0-py3-none-any.whl:
Publisher:
release.yml on kmarchais/pyvista-blender
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyvista_blender-0.1.0-py3-none-any.whl -
Subject digest:
6d0bf07dfdda51af183c6d3166bc86dfbc0e8a2a5bd5b66ee72e1d247924b727 - Sigstore transparency entry: 2088590484
- Sigstore integration time:
-
Permalink:
kmarchais/pyvista-blender@c1550ec92aafc211b379d3723a48aa5a1d5feec5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kmarchais
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c1550ec92aafc211b379d3723a48aa5a1d5feec5 -
Trigger Event:
push
-
Statement type: