Bidirectional Sync¶
Multiple NiiVue instances can communicate with each other, enabling synchronized updates such as shared crosshair positions. This bidirectional link helps verify image alignment and ensures consistent navigation across views. This demo mirrors https://niivue.com/demos/features/sync.bidirectional.html.
In [1]:
import ipywidgets as widgets
from IPython.display import display
from ipyniivue import MultiplanarType, NiiVue
## Create three instances
nv1 = NiiVue(
height=400,
multiplanar_force_render=True,
back_color=(0.0, 0.0, 0.0, 1.0),
)
nv2 = NiiVue(
height=400,
multiplanar_force_render=True,
back_color=(0.0, 0.0, 0.0, 1.0),
)
nv3 = NiiVue(
height=400,
multiplanar_force_render=True,
back_color=(0.0, 0.0, 0.0, 1.0),
)
nv1.load_volumes([{"path": "../images/pcasl.nii.gz"}])
nv2.load_volumes([{"path": "../images/aal.nii.gz"}]),
nv3.load_volumes([{"path": "../images/mni152.nii.gz"}])
## Create interactive controls
layout_dropdown = widgets.Dropdown(
options=[
("Auto", MultiplanarType.AUTO),
("Column", MultiplanarType.COLUMN),
("Grid", MultiplanarType.GRID),
("Row", MultiplanarType.ROW),
],
value=MultiplanarType.AUTO,
description="Layout:",
)
sync_dropdown = widgets.Dropdown(
options=[
("Sync Disabled", 0),
("Sync 2D", 1),
("Sync 3D", 2),
("Sync 2D and 3D", 3),
],
value=3,
description="Broadcast:",
)
status1 = widgets.HTML(value=" ")
status2 = widgets.HTML(value=" ")
status3 = widgets.HTML(value=" ")
## Setup Event Handlers
def on_layout_change(change):
"""Handle layout dropdown changes."""
new_layout = change["new"]
nv1.opts.multiplanar_layout = new_layout
nv2.opts.multiplanar_layout = new_layout
nv3.opts.multiplanar_layout = new_layout
def on_sync_change(change):
"""Handle sync mode dropdown changes."""
v = change["new"]
is_2d = False
is_3d = False
if v % 2: # If odd (1 or 3)
is_2d = True
if v > 1: # If 2 or 3
is_3d = True
nv1.broadcast_to([nv2, nv3], {"2d": is_2d, "3d": is_3d})
nv2.broadcast_to([nv1, nv3], {"2d": is_2d, "3d": is_3d})
nv3.broadcast_to([nv1, nv2], {"2d": is_2d, "3d": is_3d})
@nv1.on_location_change
def handle_location1(data):
"""Handle location string."""
status1.value = f" {data['string']}"
@nv2.on_location_change
def handle_location2(data):
"""Handle location string."""
status2.value = f" {data['string']}"
@nv3.on_location_change
def handle_location3(data):
"""Handle location string."""
status3.value = f" {data['string']}"
layout_dropdown.observe(on_layout_change, names="value")
sync_dropdown.observe(on_sync_change, names="value")
on_sync_change({"new": sync_dropdown.value})
## Display all
controls = widgets.HBox([layout_dropdown, sync_dropdown])
viewers = widgets.GridspecLayout(1, 3, height="400px")
viewers[0, 0] = nv1
viewers[0, 1] = nv2
viewers[0, 2] = nv3
status_row = widgets.HBox([status1, status2, status3])
display(widgets.VBox([controls, viewers, status_row]))
In [ ]:
In [ ]: