render_niivue
render_niivue class
Reactively render a NiiVue neuroimaging viewer (volumes, overlays, and meshes), via ipyniivue.
Usage Source
render_niivue(
_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_niivue is a decorator that creates a render function – a function that returns the viewer to display and re-runs when the reactive values it reads change:
from ipyniivue import NiiVue, SliceType, Volume
from shinywidgets import render_niivue
@render_niivue
def nv():
nv = NiiVue(slice_type=SliceType.MULTIPLANAR)
nv.add_volume(Volume(path="mni152.nii.gz", name="mni152"))
return nvThe render function should return an ipyniivue.NiiVue widget with volumes and/or meshes loaded (requires pip install 'shinywidgets[niivue]'). In a filling layout, the viewer grows or shrinks to fit its container (tracking resizes, including full_screen); an explicit output_widget(height=...) is respected too. Don’t pass height= to NiiVue() itself – it sets the canvas’s pixel height, which disagrees with the output’s height and causes clipping or dead space.
Note
Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered NiiVue instance.
Responding to user interaction: nv.location() reads the crosshair position, much like input.my_slider() reads an input. Call it in a reactive function (a @render.* function, @reactive.calc, or @reactive.effect), which then re-runs whenever the crosshair moves. It attaches at render, seeded with the position the viewer has already synced to Python, so a crosshair move made before its first read (e.g., behind a button or chat handler) is reflected in that read instead of lost. A re-render re-seeds from the new viewer at attach (a freshly rendered viewer starts at the volume center, [0.5, 0.5, 0.5]). Debounced (250 ms) by default; see rate_policy and rate_delay. For every event un-debounced, register a callback with @nv.on_location_change inside the render function and have it set a session-scoped reactive.Value.
Reading the crosshair: location() (above) and the on_location_change callback are the ways to read the crosshair reactively. The position does sync back to nv.widget.scene.crosshair_pos (location()’s seed reads it), but ipyniivue applies that sync silently, so reactive_read(nv.widget.scene, "crosshair_pos") never re-runs. (Beware: exceptions raised inside on_location_change callbacks are swallowed by ipywidgets, so a failing callback just looks like “no updates”.) Similarly, nv.frac2mm()/nv.mm2frac() don’t work on Shiny (the coordinate matrices exist only in the browser); use the millimeter coordinates from the location() payload instead.
Mutating the widget: use .widget to update the viewer in place without a re-render: .widget.set_colormap(volume_id, name), .widget.set_opacity(index, value), .widget.set_gamma(value), .widget.set_slice_type(SliceType.RENDER), and .widget.move_crosshair_in_vox(dx, dy, dz) all apply live (trait changes on the widget are synced to the browser).
Data size: each render sends the viewer’s JavaScript bundle plus every volume to the browser, so re-renders are expensive for large volumes. Prefer Volume(url=...) for remote data (the browser fetches it directly), and prefer in-place .widget.set_* updates over re-rendering.
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 (seefillandfillable). fill : Optional[bool] = None-
Whether the output should grow or shrink to fit its parent container. When
None(the default), this isTrueifheightis not specified. fillable : Optional[bool] = None-
Whether the output container should fill available space (scrolling its contents on overflow). When
None(the default), this isTrueifheightis not specified. rate_policy : Optional[RatePolicy] = None-
How to rate-limit
location()(debounced by default) andreactive_read()on the widget:"debounce"or"throttle".None(the default) keeps the debounce, and debouncesreactive_read()only ifrate_delayis given. rate_delay : Optional[float] = None-
The rate-limit window in seconds (default 0.25).
0disables rate limiting.
Examples
from ipyniivue import NiiVue, Volume
from shiny import App, reactive, render, ui
from shinywidgets import output_widget, render_niivue
app_ui = ui.page_fluid(
output_widget("brain"),
ui.output_text_verbatim("crosshair"),
)
def server(input, output, session):
location = reactive.Value("Click the brain to inspect a voxel")
@render_niivue
def brain():
nv = NiiVue()
nv.load_volumes(
[Volume(url="https://niivue.com/demos/images/mni152.nii.gz")]
)
@nv.on_location_change
def _(loc):
location.set(loc["string"])
return nv
@render.text
def crosshair():
return location()
app = App(app_ui, server)Each accessor can also be read by name – out.read("location") – which also accepts the widget’s bare synced-trait names.
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).
Methods
location() |
The most recent crosshair position, as a dict with keys ax_cor_sag (the view index), frac (fractional volume coordinates), mm (millimeter coordinates), vox (voxel coordinates), values (voxel intensities under the crosshair, one per volume), string (a formatted summary), and xy (canvas coordinates). |
location()
render_niivue.location()The most recent crosshair position, as a dict with keys ax_cor_sag (the view index), frac (fractional volume coordinates), mm (millimeter coordinates), vox (voxel coordinates), values (voxel intensities under the crosshair, one per volume), string (a formatted summary), and xy (canvas coordinates).
The accessor attaches at render, seeded with the position the viewer has already synced to Python (nv.widget.scene.crosshair_pos), so a crosshair move made before its first read is reflected in that read instead of lost, and a re-render re-seeds from the new viewer’s position at attach. Only frac syncs back, so a seeded value carries just frac plus a "seeded": True marker; the other keys (mm, vox, values, …) appear with the next crosshair move, which reports the full dict. Debounced (250 ms) by default; see the rate_policy and rate_delay parameters.