Skip to content

CellierController

The top-level entry point for building and driving a cellier scene.

cellier.CellierController

The main class for constructing and controlling a cellier visualization.

Wraps a ViewerModel (model layer) and a RenderManager (render layer) and performs the synchronization between both.

incoming_events property

incoming_events: EventBus

Incoming event bus for GUI-driven model mutations.

Emit AppearanceUpdateEvent, DimsUpdateEvent, or AABBUpdateEvent onto this bus to request model changes. The controller dispatches each event to the corresponding update_* method, preserving source_id end-to-end.

camera_reslice_enabled property writable

camera_reslice_enabled: bool

Whether camera movement triggers automatic reslicing.

camera_settle_threshold_s property writable

camera_settle_threshold_s: float

Debounce delay before reslice after camera movement.

set_widget_parent

set_widget_parent(parent: object) -> None

Set the Qt parent for subsequently created canvas widgets.

Only meaningful when self._gui == "qt"; the anywidget gui ignores the parent (notebook canvases are not laid out by a Qt parent).

from_model classmethod

from_model(model: ViewerModel, widget_parent: QWidget | None = None, render_config: RenderManagerConfig | None = None) -> CellierController

Construct a controller from a serialized ViewerModel.

Iteratively adds all data stores, scenes, visuals, and canvases through the public API. The order is:

  1. Data stores — registered before visuals reference them.
  2. Scenes — registered with render_modes and lighting from model.
  3. Visuals — added per scene; data stores must already be present.
  4. Canvases — restored with camera state from model.

Parameters:

Name Type Description Default
model ViewerModel

A ViewerModel loaded from disk or constructed programmatically.

required
widget_parent QWidget or None

Qt parent for canvas widgets. Defaults to None.

None
render_config RenderManagerConfig or None

Render pipeline configuration. Defaults to None (uses defaults).

None

Returns:

Type Description
CellierController

from_file classmethod

from_file(path: str | Path, widget_parent: QWidget | None = None, render_config: RenderManagerConfig | None = None) -> CellierController

Deserialize a ViewerModel from disk and construct a controller.

Parameters:

Name Type Description Default
path str or Path

Path to a JSON file previously written by to_file.

required
widget_parent QWidget or None

Qt parent for canvas widgets.

None
render_config RenderManagerConfig or None

Render pipeline configuration.

None

Returns:

Type Description
CellierController

to_file

to_file(path: str | Path) -> None

Serialize the current model state to a JSON file.

to_model

to_model() -> ViewerModel

Return a copy of the current model state.

add_scene_model

add_scene_model(scene: Scene) -> Scene

Register a pre-built Scene model with the controller.

Used by from_model to restore scenes from a serialized ViewerModel, and called internally by add_scene.

Parameters:

Name Type Description Default
scene Scene

Pre-built scene model.

required

Returns:

Type Description
Scene

The same object passed in.

add_scene

add_scene(*, name: str = 'scene', dim: Literal['2d', '3d'] = '3d', coordinate_system: CoordinateSystem | None = None, render_modes: set[Literal['2d', '3d']] | None = None, lighting: Literal['none', 'default'] = 'none') -> Scene

Create a Scene from keyword arguments and register it.

Parameters:

Name Type Description Default
name str

Human-readable scene name.

'scene'
dim '2d' or '3d'

Initial display dimensionality. "3d" sets displayed_axes to the last three axes of the coordinate system; "2d" sets it to the last two.

'3d'
coordinate_system CoordinateSystem or None

World coordinate system. Defaults to a 3-axis ("z", "y", "x") system when None.

None
render_modes set or None

Which rendering modes visuals should support. Defaults to {"2d", "3d"}.

None
lighting 'none' or 'default'

Pass "default" to add ambient/directional lights (required for MeshPhongAppearance).

'none'

Returns:

Type Description
Scene

The newly created and registered Scene.

add_data_store

add_data_store(data_store: BaseDataStore) -> BaseDataStore

Register a data store and return it.

Parameters:

Name Type Description Default
data_store BaseDataStore

The store to register.

required

Returns:

Type Description
BaseDataStore

The same object passed in.

add_visual

add_visual(scene_id: UUID, visual_model: VisualType, data_store: BaseDataStore | None = None) -> VisualType

Register a pre-built visual model with a scene.

This is the canonical construction path used by from_model. All typed convenience methods (add_image, add_mesh, etc.) delegate to this method internally.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required
visual_model VisualType

Pre-built visual model. Its data_store_id must already be registered via add_data_store, or data_store must be passed explicitly.

required
data_store BaseDataStore or None

If provided, register the store first (no-op if already present), then use it. If None, the store is looked up by visual_model.data_store_id; a KeyError is raised if not found.

None

Returns:

Type Description
VisualType

The same visual_model passed in.

Raises:

Type Description
KeyError

If data_store is None and visual_model.data_store_id is not registered.

TypeError

If the visual type is not recognized.

add_image

add_image(data: ImageMemoryStore, scene_id: UUID, appearance: BaseImageAppearance, name: str = 'image') -> ImageVisual

Add an in-memory image visual to a scene.

Parameters:

Name Type Description Default
data ImageMemoryStore

The backing data store.

required
scene_id UUID

ID of an existing scene.

required
appearance BaseImageAppearance

Appearance parameters.

required
name str

Human-readable label. Default "image".

'image'

Returns:

Type Description
ImageVisual

add_labels

add_labels(data: LabelMemoryStore, scene_id: UUID, appearance: BaseLabelsAppearance | None = None, name: str = 'labels', transform: AffineTransform | None = None) -> LabelMemoryVisual

Add an in-memory label visual to a scene.

Parameters:

Name Type Description Default
data LabelMemoryStore

Backing int32 label store.

required
scene_id UUID

ID of an existing scene.

required
appearance BaseLabelsAppearance or None

Appearance parameters. Defaults to InMemoryLabelsAppearance().

None
name str

Human-readable label. Default "labels".

'labels'
transform AffineTransform or None

Data-to-world transform. Defaults to identity when None.

None

Returns:

Type Description
LabelMemoryVisual

add_mesh

add_mesh(data: MeshMemoryStore, scene_id: UUID, appearance: MeshAppearance, name: str = 'mesh', transform: AffineTransform | None = None) -> MeshVisual

Add a mesh visual to a scene.

Parameters:

Name Type Description Default
data MeshMemoryStore

In-memory mesh. Normals are auto-computed if not supplied; indices are coerced to int32.

required
scene_id UUID

ID of an existing scene.

required
appearance MeshFlatAppearance | MeshPhongAppearance

Appearance. Use MeshPhongAppearance with lighting="default" on the scene for shaded rendering.

required
name str

Human-readable label. Default "mesh".

'mesh'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None

Returns:

Type Description
MeshVisual

add_points

add_points(data: PointsMemoryStore, scene_id: UUID, appearance: PointsMarkerAppearance | None = None, name: str = 'points', transform: AffineTransform | None = None) -> PointsVisual

Add a points visual backed by a PointsMemoryStore.

Parameters:

Name Type Description Default
data PointsMemoryStore

The backing data store.

required
scene_id UUID

ID of the target scene.

required
appearance PointsMarkerAppearance or None

Appearance model. Defaults to PointsMarkerAppearance() if None.

None
name str

Human-readable label for the visual.

'points'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None

Returns:

Type Description
PointsVisual

add_lines

add_lines(data: LinesMemoryStore, scene_id: UUID, appearance: LinesMemoryAppearance | None = None, name: str = 'lines', transform: AffineTransform | None = None) -> LinesVisual

Add a lines visual backed by a LinesMemoryStore.

Parameters:

Name Type Description Default
data LinesMemoryStore

The backing data store.

required
scene_id UUID

ID of the target scene.

required
appearance LinesMemoryAppearance or None

Appearance model. Defaults to LinesMemoryAppearance() if None.

None
name str

Human-readable label for the visual.

'lines'
transform AffineTransform or None

Data-to-world transform for this visual. Defaults to identity when None.

None

Returns:

Type Description
LinesVisual

add_image_multiscale

add_image_multiscale(data: BaseDataStore, scene_id: UUID, appearance: MultiscaleImageAppearance, name: str = 'image', render_config: MultiscaleImageRenderConfig | None = None, transform: AffineTransform | None = None) -> MultiscaleImageVisual

Add a multiscale image visual to a scene.

Parameters:

Name Type Description Default
data BaseDataStore

The backing data store.

required
scene_id UUID

ID of an existing scene.

required
appearance MultiscaleImageAppearance

Visual appearance parameters.

required
name str

Human-readable label. Default "image".

'image'
render_config MultiscaleImageRenderConfig or None

Render-layer configuration. Defaults to MultiscaleImageRenderConfig() with all default values if None.

None
transform AffineTransform or None

Data-to-world transform. Defaults to identity when None.

None

Returns:

Type Description
MultiscaleImageVisual

add_labels_multiscale

add_labels_multiscale(data: BaseDataStore, scene_id: UUID, appearance: MultiscaleLabelsAppearance, name: str = 'labels', render_config: MultiscaleLabelRenderConfig | None = None, transform: AffineTransform | None = None) -> MultiscaleLabelVisual

Add a multiscale label visual to a scene.

Parameters:

Name Type Description Default
data BaseDataStore

The backing label data store (e.g. OMEZarrLabelDataStore).

required
scene_id UUID

ID of an existing scene.

required
appearance MultiscaleLabelsAppearance

Visual appearance parameters.

required
name str

Human-readable label. Default "labels".

'labels'
render_config MultiscaleLabelRenderConfig or None

Render-layer configuration. Defaults to MultiscaleLabelRenderConfig() with all default values if None.

None
transform AffineTransform or None

Data-to-world transform. Defaults to identity when None.

None

Returns:

Type Description
MultiscaleLabelVisual

add_multichannel_image

add_multichannel_image(data: ImageMemoryStore, scene_id: UUID, channel_axis: int, channels: dict[int, ChannelAppearance], name: str = 'multichannel_image', max_channels_2d: int = 8, max_channels_3d: int = 4) -> MultichannelImageVisual

Add an in-memory multichannel image visual to a scene.

Parameters:

Name Type Description Default
data ImageMemoryStore

Backing data store.

required
scene_id UUID

Target scene.

required
channel_axis int

Data axis index for the channel dimension.

required
channels dict[int, ChannelAppearance]

Per-channel appearance keyed by channel index.

required
name str

Display name for the visual.

'multichannel_image'
max_channels_2d int

Maximum simultaneous 2D channel nodes.

8
max_channels_3d int

Maximum simultaneous 3D channel nodes.

4

Returns:

Type Description
MultichannelImageVisual

add_multichannel_image_multiscale

add_multichannel_image_multiscale(data: BaseDataStore, scene_id: UUID, channel_axis: int, channels: dict[int, ChannelAppearance], name: str = 'multichannel_image', render_config: MultiscaleImageRenderConfig | None = None, transform: AffineTransform | None = None, max_channels_2d: int = 8, max_channels_3d: int = 4) -> MultichannelMultiscaleImageVisual

Add a multiscale multichannel image visual to a scene.

Parameters:

Name Type Description Default
data BaseDataStore

Backing multiscale data store.

required
scene_id UUID

Target scene.

required
channel_axis int

Data axis index for the channel dimension.

required
channels dict[int, ChannelAppearance]

Per-channel appearance keyed by channel index.

required
name str

Display name for the visual.

'multichannel_image'
render_config MultiscaleImageRenderConfig or None

LOD and rendering configuration; uses defaults when None.

None
transform AffineTransform or None

Data-to-world transform; uses identity when None.

None
max_channels_2d int

Maximum simultaneous 2D channel nodes.

8
max_channels_3d int

Maximum simultaneous 3D channel nodes.

4

Returns:

Type Description
MultichannelMultiscaleImageVisual

add_channel

add_channel(visual_id: UUID, channel_index: int, appearance: ChannelAppearance) -> None

Add a channel to a multichannel image visual.

Parameters:

Name Type Description Default
visual_id UUID

ID of a MultichannelImageVisual or MultichannelMultiscaleImageVisual.

required
channel_index int

Index along the visual's channel_axis. Must not already be present.

required
appearance ChannelAppearance

Colormap, clim, and opacity settings for the new channel.

required

Raises:

Type Description
ValueError

If channel_index is already present.

RuntimeError

If the pool is full.

remove_channel

remove_channel(visual_id: UUID, channel_index: int) -> None

Remove a channel from a multichannel image visual.

Parameters:

Name Type Description Default
visual_id UUID

ID of a MultichannelImageVisual or MultichannelMultiscaleImageVisual.

required
channel_index int

Index of the channel to remove.

required

Raises:

Type Description
KeyError

If channel_index is not in visual.channels.

add_canvas

add_canvas(scene_id: UUID, render_modes: set[str] | None = None, initial_dim: str | None = None, fov: float = 70.0, depth_range_3d: tuple[float, float] = (1.0, 8000.0), depth_range_2d: tuple[float, float] = (-500.0, 500.0), canvas_size: tuple[int, int] | None = None) -> QWidget

Create a canvas attached to a scene and return its embeddable widget.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required
render_modes set[str] or None

Which camera modes to prepare on the canvas. Each entry must be "2d" or "3d". When None, defaults to the scene's own render_modes. Pass {"2d", "3d"} for a canvas that can switch between views.

None
initial_dim str or None

Which mode is active when the canvas first appears. Must be a member of render_modes. When None, inferred from the scene's current displayed_axes length (3 axes -> "3d", otherwise "2d").

None
fov float

Vertical field of view in degrees for the 3D perspective camera. Ignored when "3d" is not in render_modes. Default 70.0.

70.0
depth_range_3d tuple[float, float]

(near, far) clip distances for the 3D perspective camera. Default (1.0, 8000.0).

(1.0, 8000.0)
depth_range_2d tuple[float, float]

(near, far) clip distances for the 2D orthographic camera. Default (-500.0, 500.0).

(-500.0, 500.0)
canvas_size tuple[int, int] or None

Initial CSS pixel size for the anywidget canvas. Ignored for the Qt gui (which is sized by its parent layout). Defaults to (600, 600) when None for the anywidget gui.

None

Returns:

Type Description
QWidget

The render widget. Embed with layout.addWidget(widget).

Raises:

Type Description
ValueError

If initial_dim is supplied but is not a member of render_modes.

add_canvas_model

add_canvas_model(scene_id: UUID, canvas_model: Canvas, initial_dim: str | None = None, canvas_size: tuple[int, int] | None = None) -> QWidget

Register a pre-built Canvas model with a scene.

Used by from_model to restore canvases from a serialized ViewerModel, and called internally by add_canvas. Camera state (position, rotation, fov, depth range) is read from the camera models stored in canvas_model.cameras.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required
canvas_model Canvas

Pre-built canvas model. Must have at least one entry in canvas_model.cameras.

required
initial_dim str or None

Which dim to activate first. When None, the first key in canvas_model.cameras is used (insertion order is preserved by Python dicts, so this is deterministic for serialized models).

None
canvas_size tuple[int, int] or None

Initial CSS pixel size for the anywidget canvas. Ignored for the Qt gui. Defaults to (600, 600) for the anywidget gui.

None

Returns:

Type Description
QWidget

The render widget.

Raises:

Type Description
ValueError

If canvas_model.cameras is empty, or if initial_dim is not a key in canvas_model.cameras.

get_scene

get_scene(scene_id: UUID) -> Scene

Return the live Scene model for scene_id.

get_data_store

get_data_store(store_id: UUID) -> BaseDataStore

Return the registered data store for store_id.

Parameters:

Name Type Description Default
store_id UUID

ID of a previously registered data store.

required

Returns:

Type Description
BaseDataStore

Raises:

Type Description
KeyError

If no store with store_id has been registered.

fit_camera

fit_camera(scene_id: UUID, canvas_id: UUID | None = None) -> None

Fit the camera to the current scene bounding box.

Safe to call immediately after add_image / add_image_multiscale and transform assignment — the node matrix is set at construction time so no chunk data needs to be loaded first.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene whose camera should be fitted.

required
canvas_id UUID or None

If provided, fit only that canvas. When None (default), all canvases attached to scene_id are fitted.

None

add_canvas_overlay_model

add_canvas_overlay_model(canvas_id: UUID, overlay: CanvasOverlay) -> CanvasOverlay

Attach a screen-space overlay to a specific canvas.

The overlay is rendered as a post-pass on top of the main scene each frame. It does not participate in reslicing, has no world-space transform, and is not added to scene.visuals.

The overlay model is stored in the Canvas.overlays list of canvas_id, making it part of the serializable model.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas that should display the overlay. Use :meth:get_canvas_ids to look up canvas IDs for a scene.

required
overlay CanvasOverlay

Model-layer overlay description. Typically a :class:~cellier.visuals._canvas_overlay.CenteredAxes2D.

required

Returns:

Type Description
CanvasOverlay

The same overlay object passed in (for ID access or chaining).

Raises:

Type Description
KeyError

If canvas_id is not registered.

set_overlay_visible

set_overlay_visible(overlay_id: UUID, visible: bool) -> None

Toggle the visibility of a canvas overlay.

Searches all canvases across all scenes for an overlay with overlay_id. Updates both the model field and the render layer.

Parameters:

Name Type Description Default
overlay_id UUID

ID of the :class:~cellier.visuals._canvas_overlay.CanvasOverlay to toggle.

required
visible bool

True to show the overlay, False to hide it.

required

Raises:

Type Description
KeyError

If no overlay with overlay_id is found.

get_scene_by_name

get_scene_by_name(name: str) -> Scene

Return the live Scene model for the given name.

Raises KeyError if no scene with that name exists.

get_canvas_ids

get_canvas_ids(scene_id: UUID) -> list[UUID]

Return the IDs of all canvases registered for scene_id.

Parameters:

Name Type Description Default
scene_id UUID

ID of an existing scene.

required

Returns:

Type Description
list[UUID]

Canvas IDs in registration order. Empty if no canvases have been added yet.

get_canvas_view

get_canvas_view(canvas_id: UUID) -> CanvasView

Return the render-layer CanvasView for canvas_id.

Provides access to the rendering backend (camera, widget, overlays) for a canvas registered via :meth:add_canvas or :meth:add_canvas_model.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas.

required

Returns:

Type Description
CanvasView

Raises:

Type Description
KeyError

If canvas_id is not registered.

get_camera_state

get_camera_state(canvas_id: UUID) -> CameraState

Return a snapshot of the current camera state for canvas_id.

Useful for seeding downstream widgets (e.g. orientation overlays) with the post-fit camera state without constructing a synthetic CameraChangedEvent.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas.

required

Returns:

Type Description
CameraState

Raises:

Type Description
KeyError

If canvas_id is not registered.

screenshot

screenshot(canvas_id: UUID, size: tuple[int, int] | None = None, scale: float = 1.0) -> ndarray

Capture a screenshot of the canvas as an RGBA uint8 array.

Temporarily resizes the canvas to size (scaled by scale), renders one frame, grabs the framebuffer, then restores the original size and camera state. Follows the same pattern as napari's resize_canvas context manager.

Parameters:

Name Type Description Default
canvas_id UUID

ID of a registered canvas.

required
size tuple[int, int] or None

Target (width, height) in logical pixels before applying scale. When None the current canvas size is used.

None
scale float

Multiplier applied to size (or the current size when size is None). scale=2 doubles the resolution for high-DPI output.

1.0

Returns:

Type Description
ndarray

RGBA uint8 array of shape (height, width, 4).

get_visual_model

get_visual_model(visual_id: UUID) -> MultiscaleImageVisual

Return the live visual model for visual_id.

Searches all scenes. Raises KeyError if not found.

reslice_all

reslice_all() -> None

Trigger a data load for all visuals across all scenes.

reslice_scene

reslice_scene(scene_id: UUID, *, on_ready: Callable[[], None] | None = None, owner_id: UUID | None = None) -> None

Trigger a data load for all visuals in one scene.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to reslice.

required
on_ready Callable[[], None] or None

If provided, a zero-argument callback fired exactly once after all visuals loaded by this reslice have committed to the GPU (across every canvas attached to the scene). Visuals with no data in the current view (culled, empty, or hidden) do not delay it. Works uniformly for in-memory, multiscale, multichannel, and geometry visuals. See :meth:on_scene_ready.

The callback tracks this reslice generation. If a superseding reslice (e.g. a camera-settle reload) cancels these in-flight reads before they commit, the cancelled visual never reports completion and the callback may not fire. Callers that need a guaranteed startup signal should suppress camera-driven reslicing during the load (the convenience launchers do this automatically).

None
owner_id UUID or None

Owner under which the temporary on_ready subscriptions are registered (for teardown). Defaults to the controller's own id.

None

reslice_visual

reslice_visual(visual_id: UUID) -> None

Trigger a data load for one visual.

suppress_reslice

suppress_reslice() -> Generator[None, None, None]

Context manager that blocks reslice_scene inside transform handlers.

Use this when updating a visual's transform without needing to reload its underlying data — for example, repositioning a static-geometry mesh by translation only.

.. warning:: _suppress_reslice is a flat boolean. Nested calls or concurrent async tasks that mutate transforms inside overlapping suppress_reslice blocks will interfere. Replace with a depth counter if that becomes necessary.

set_visual_transform

set_visual_transform(visual_id: UUID, transform: AffineTransform, *, reslice: bool = True) -> None

Update the data-to-world transform of a visual.

Assigns transform to the live visual model, which fires its psygnal field event and propagates the change to the render layer via the event bus.

Parameters:

Name Type Description Default
visual_id UUID

ID of the visual to update.

required
transform AffineTransform

New data-to-world transform.

required
reslice bool

If True (default), a full reslice is triggered after the transform is applied — required for image visuals where the transform changes which data falls in the current slab. Pass False for static-geometry visuals (mesh, points, lines) where the transform only repositions the node and the underlying data is unchanged.

True

update_slice_indices

update_slice_indices(scene_id: UUID, slice_indices: dict[int, int], *, source_id: UUID | None = None) -> None

Set slice_indices on a scene's dims.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own DimsChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
slice_indices dict[int, int]

Mapping of axis index → slice position.

required
source_id UUID | None

UUID to stamp on the emitted DimsChangedEvent. Defaults to the controller's own ID.

None

update_appearance_field

update_appearance_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on a visual's appearance model.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own AppearanceChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
field str

Attribute name on the appearance model, e.g. "clim".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted AppearanceChangedEvent. Defaults to the controller's own ID.

None

update_channel_appearance_field

update_channel_appearance_field(visual_id: UUID, channel_index: int, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on one channel of a multichannel visual.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own ChannelAppearanceChangedEvent subscription can ignore the echo.

This mutates a single visual only. When a channel is shared in lock-step across several panels (e.g. an OrthoViewer), calling this on one panel's visual leaves the sibling panels unequal until they are written too; use update_channel_group_field to keep the group in lock-step.

A pydantic.ValidationError from a malformed value is allowed to propagate (matching update_appearance_field).

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
channel_index int

Index into visual.channels selecting the ChannelAppearance.

required
field str

Attribute name on the channel appearance model, e.g. "clim".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted ChannelAppearanceChangedEvent. Defaults to the controller's own ID.

None

update_channel_group_field

update_channel_group_field(visual_ids: list[UUID], channel_index: int, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one channel field across a group of visuals in lock-step.

Fan-out over update_channel_appearance_field so every visual in the group (e.g. the per-panel visuals of an OrthoViewer) receives the same channel change. This is the programmatic write-side companion to the widget subscribe-to-all read side.

Parameters:

Name Type Description Default
visual_ids list[UUID]

Target visuals sharing the channel set.

required
channel_index int

Index into each visual's channels mapping.

required
field str

Attribute name on the channel appearance model.

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on each emitted ChannelAppearanceChangedEvent. Defaults to the controller's own ID.

None

update_aabb_field

update_aabb_field(visual_id: UUID, field: str, value: Any, *, source_id: UUID | None = None) -> None

Set one field on a visual's AABB params model.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own AABBChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
field str

Attribute name on the AABB model, e.g. "enabled".

required
value Any

New value for the field.

required
source_id UUID | None

UUID to stamp on the emitted AABBChangedEvent. Defaults to the controller's own ID.

None

update_displayed_axes

update_displayed_axes(scene_id: UUID, displayed_axes: tuple[int, ...], *, source_id: UUID | None = None) -> None

Set displayed_axes on a scene's dims.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own DimsChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
displayed_axes tuple[int, ...]

Tuple of axis indices to display; length 2 for 2D, 3 for 3D.

required
source_id UUID | None

UUID to stamp on the emitted DimsChangedEvent. Defaults to the controller's own ID.

None

update_stacked_axes

update_stacked_axes(scene_id: UUID, stacked_axes: tuple[int, ...], *, source_id: UUID | None = None) -> None

Set stacked_axes on a scene's dims.

Tags the emitted bus event with source_id. GUI widgets should pass source_id=self._id so their own DimsChangedEvent subscription can ignore the echo.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
stacked_axes tuple[int, ...]

Tuple of axis indices whose full extent is composited by the render layer (e.g. channel axis). Pass () for no stacked axes.

required
source_id UUID | None

UUID to stamp on the emitted DimsChangedEvent. Defaults to the controller's own ID.

None

set_displayed_axes

set_displayed_axes(scene_id: UUID, displayed_axes: tuple[int, ...], *, source_id: UUID | None = None) -> None

Set displayed axes on a scene's dims (preferred public API).

Equivalent to :meth:update_displayed_axes. The controller's psygnal bridge fires _rebuild_visuals_geometry and _switch_canvas_cameras automatically when the model field changes.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
displayed_axes tuple[int, ...]

Tuple of axis indices to display; length 2 for 2D, 3 for 3D.

required
source_id UUID | None

UUID stamped on the emitted DimsChangedEvent.

None

set_stacked_axes

set_stacked_axes(scene_id: UUID, stacked_axes: tuple[int, ...], *, source_id: UUID | None = None) -> None

Set stacked axes on a scene's dims (preferred public API).

Equivalent to :meth:update_stacked_axes. After updating the model, notifies each visual via on_stacked_axes_changed so they can adjust internal state (e.g. LUT channel routing) without a node swap.

Parameters:

Name Type Description Default
scene_id UUID

Target scene.

required
stacked_axes tuple[int, ...]

Tuple of axis indices whose full extent is composited by the render layer (e.g. channel axis). Pass () for none.

required
source_id UUID | None

UUID stamped on the emitted DimsChangedEvent.

None

look_at_visual

look_at_visual(visual_id: UUID, canvas_id: UUID, view_direction: tuple[float, float, float] = (-1, -1, -1), up: tuple[float, float, float] = (0, 0, 1)) -> None

Fit the camera to a visual's bounding box.

Parameters:

Name Type Description Default
visual_id UUID

ID of the target visual.

required
canvas_id UUID

ID of the canvas whose camera should be fitted.

required
view_direction tuple[float, float, float]

Camera look direction vector (need not be normalized).

(-1, -1, -1)
up tuple[float, float, float]

Camera up vector.

(0, 0, 1)

set_camera_depth_range

set_camera_depth_range(canvas_id: UUID, depth_range: tuple[float, float]) -> None

Set the near/far clip distances for a canvas camera.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the target canvas.

required
depth_range tuple[float, float]

(near, far) clip distances in world units.

required

add_paint_controller

add_paint_controller(visual_id: UUID, canvas_id: UUID, brush_value: int = 1, brush_radius_voxels: float = 2.0, history_depth: int = 100, autosave_interval_s: float | None = None)

Create and wire a paint controller for the visual's data store.

Parameters:

Name Type Description Default
visual_id UUID

Visual to paint on.

required
canvas_id UUID

Canvas to bind to. Its camera controller is disabled for the session. Pass controller.get_canvas_ids(scene_id)[0] for the common single-canvas case.

required
brush_value int

Integer label ID written to every painted voxel.

1
brush_radius_voxels float

Brush radius in level-0 voxel units.

2.0
history_depth int

Maximum undoable strokes.

100
autosave_interval_s float | None

Seconds between automatic flushes for MultiscalePaintController. Each autosave rebuilds the pyramid and resets GPU paint textures. None disables autosave. Ignored for SyncPaintController.

None

Returns:

Type Description
AbstractPaintController

Fully wired; caller owns the object.

Raises:

Type Description
TypeError

If the data store type has no registered paint controller.

remove_scene

remove_scene(scene_id: UUID) -> None

Remove a scene and all its visuals and canvases.

Teardown order mirrors remove_visual for each child visual, then cleans up the scene-level maps and render layer.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to remove.

required

Raises:

Type Description
KeyError

If scene_id is not registered.

remove_canvas

remove_canvas(canvas_id: UUID) -> None

Remove a canvas from its scene, disconnecting all wiring.

Teardown order mirrors remove_scene for the canvas-level steps: 1. Cancel any pending camera-settle task. 2. Remove bus subscriptions owned by this canvas. 3. Update controller lookup maps. 4. Remove from the model layer. 5. Render-layer teardown (drops widget and GPU references).

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas to remove.

required

Raises:

Type Description
KeyError

If canvas_id is not registered.

remove_visual

remove_visual(visual_id: UUID) -> None

Remove a visual from its scene, disconnecting all wiring.

Teardown order: 1. Psygnal bridge handlers disconnected first — prevents the bridge closures from firing during any subsequent model access. 2. Bus subscriptions removed — prevents dangling GFX-layer handlers from receiving events after the node is gone from the scene graph. 3. Render-layer removal — drops scene-graph node and GPU references. 4. VisualRemovedEvent emitted for external observers.

Parameters:

Name Type Description Default
visual_id UUID

ID of the visual to remove.

required

Raises:

Type Description
KeyError

If visual_id is not registered.

remove_data_store

remove_data_store(data_store_id: UUID) -> None

Remove a data store from the model.

Raises ValueError if any live visual still references the store. Call remove_visual for each referencing visual first.

Parameters:

Name Type Description Default
data_store_id UUID

ID of the data store to remove.

required

Raises:

Type Description
ValueError

If one or more visuals still reference the store. The error message names each visual so the caller can identify them.

KeyError

If data_store_id is not registered.

on_dims_changed

on_dims_changed(scene_id: UUID, callback: Callable[[DimsChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the dims for scene_id change.

The callback receives the full DimsChangedEvent, which includes source_id for echo-filtering and dims_state for the new state.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[DimsChangedEvent], None]

Called with the DimsChangedEvent on each dims change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback. Use for transient widgets that may be destroyed outside the controller's teardown path. Cannot be used with lambdas.

False

Returns:

Type Description
SubscriptionHandle

Pass to EventBus.unsubscribe() for individual removal.

on_camera_changed

on_camera_changed(scene_id: UUID, callback: Callable[[CameraChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the camera for scene_id changes.

The callback receives a CameraChangedEvent carrying the latest CameraState, including extent (width, height) for OrthographicCamera scenes.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[CameraChangedEvent], None]

Called with the CameraChangedEvent on each camera change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

unsubscribe_mouse

unsubscribe_mouse(handle: SubscriptionHandle) -> None

Remove a mouse subscription created by an on_mouse_* method.

Use this in place of EventBus.unsubscribe for handles returned by the six on_mouse_* methods so the per-canvas picking-subscriber count stays accurate; the last unsubscribe disables element-detail extraction for that canvas.

Parameters:

Name Type Description Default
handle SubscriptionHandle

A handle returned by one of the on_mouse_* methods.

required

on_mouse_press_2d

on_mouse_press_2d(canvas_id: UUID, callback: Callable[[CanvasMousePress2DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-down event on a 2D canvas.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to watch.

required
callback Callable[[CanvasMousePress2DEvent], None]

Called with the CanvasMousePress2DEvent on each press.

required
owner_id UUID

UUID under which this subscription is registered for bulk removal via unsubscribe_all(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

on_mouse_move_2d

on_mouse_move_2d(canvas_id: UUID, callback: Callable[[CanvasMouseMove2DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-move event on a 2D canvas.

on_mouse_release_2d

on_mouse_release_2d(canvas_id: UUID, callback: Callable[[CanvasMouseRelease2DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-up event on a 2D canvas.

on_mouse_press_3d

on_mouse_press_3d(canvas_id: UUID, callback: Callable[[CanvasMousePress3DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-down event on a 3D canvas.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas to watch.

required
callback Callable[[CanvasMousePress3DEvent], None]

Called with the CanvasMousePress3DEvent on each press.

required
owner_id UUID

UUID under which this subscription is registered for bulk removal via unsubscribe_all(owner_id).

required
weak bool

If True, hold only a weak reference to callback.

False

on_mouse_move_3d

on_mouse_move_3d(canvas_id: UUID, callback: Callable[[CanvasMouseMove3DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-move event on a 3D canvas.

on_mouse_release_3d

on_mouse_release_3d(canvas_id: UUID, callback: Callable[[CanvasMouseRelease3DEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired on every pointer-up event on a 3D canvas.

set_camera_controller_enabled

set_camera_controller_enabled(canvas_id: UUID, enabled: bool) -> None

Enable or disable the camera controller for one canvas.

Parameters:

Name Type Description Default
canvas_id UUID

The canvas whose controller state should change.

required
enabled bool

False disables the controller (paint session active). True restores normal camera interaction (session ended).

required

on_appearance_changed

on_appearance_changed(visual_id: UUID, callback: Callable[[AppearanceChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the appearance of visual_id changes.

The callback receives the full AppearanceChangedEvent, which includes source_id for echo-filtering, field_name, and new_value.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[AppearanceChangedEvent], None]

Called with the AppearanceChangedEvent on each appearance change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_visual_changed

on_visual_changed(visual_id: UUID, callback: Callable[[AppearanceChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Deprecated. Use on_appearance_changed instead.

on_visibility_changed

on_visibility_changed(visual_id: UUID, callback: Callable[[VisualVisibilityChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the visibility of visual_id changes.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[VisualVisibilityChangedEvent], None]

Called with the VisualVisibilityChangedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

set_visual_visible

set_visual_visible(visual_id: UUID, visible: bool) -> None

Show or hide a visual.

Parameters:

Name Type Description Default
visual_id UUID

Target visual.

required
visible bool

True to show, False to hide.

required

on_scene_added

on_scene_added(scene_id: UUID, callback: Callable[[SceneAddedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when scene_id is added.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[SceneAddedEvent], None]

Called with the SceneAddedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_scene_removed

on_scene_removed(scene_id: UUID, callback: Callable[[SceneRemovedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when scene_id is removed.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[SceneRemovedEvent], None]

Called with the SceneRemovedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_visual_added

on_visual_added(visual_id: UUID, callback: Callable[[VisualAddedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when visual_id is added.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[VisualAddedEvent], None]

Called with the VisualAddedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_visual_removed

on_visual_removed(visual_id: UUID, callback: Callable[[VisualRemovedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when visual_id is removed.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[VisualRemovedEvent], None]

Called with the VisualRemovedEvent.

required
owner_id UUID

UUID under which this subscription is registered.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

cancel_pending_slices

cancel_pending_slices(scene_id: UUID) -> None

Cancel all in-flight slice requests for scene_id.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene whose pending slices to cancel.

required

close

close() -> None

Cancel in-flight slices and close every canvas this viewer owns.

Releases the render surfaces and GPU resources held by the controller. Closing is explicit because the canvases are owned by the GUI backend, not by Python refcounting, so dropping the controller alone leaks them (see :meth:CanvasView.close).

Safe to call more than once; the controller must not be used afterwards.

on_aabb_changed

on_aabb_changed(visual_id: UUID, callback: Callable[[AABBChangedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired whenever the AABB params of visual_id change.

The callback receives the full AABBChangedEvent, which includes source_id for echo-filtering, field_name, and new_value.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[AABBChangedEvent], None]

Called with the AABBChangedEvent on each AABB change.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_reslice_started

on_reslice_started(scene_id: UUID, callback: Callable[[ResliceStartedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a reslice cycle begins for scene_id.

Useful for showing a loading indicator. Fired once per reslice submission, before any async data fetching starts.

Parameters:

Name Type Description Default
scene_id UUID

The scene to watch.

required
callback Callable[[ResliceStartedEvent], None]

Called with the ResliceStartedEvent.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_reslice_completed

on_reslice_completed(visual_id: UUID, callback: Callable[[ResliceCompletedEvent], None], *, owner_id: UUID, weak: bool = False) -> SubscriptionHandle

Register a callback fired when a reslice cycle completes for visual_id.

Useful for hiding a loading indicator. Fired once per visual per reslice cycle, after all bricks/tiles in the batch are committed.

Parameters:

Name Type Description Default
visual_id UUID

The visual to watch.

required
callback Callable[[ResliceCompletedEvent], None]

Called with the ResliceCompletedEvent.

required
owner_id UUID

UUID under which this subscription is registered. Pass the caller's own UUID so unsubscribe_owner(owner_id) removes it during teardown.

required
weak bool

If True, hold only a weak reference to callback.

False

Returns:

Type Description
SubscriptionHandle

on_scene_ready

on_scene_ready(scene_id: UUID, callback: Callable[[], None], *, owner_id: UUID | None = None) -> None

Reslice scene_id and fire callback once all its data is on the GPU.

This triggers a reslice of every visual in the scene and invokes callback exactly once, after all visuals loaded by that reslice have committed to the GPU across every attached canvas. Visuals with no data in the current view (frustum-culled, empty slab, or hidden) do not delay the callback.

Unlike :meth:on_reslice_completed (which fires per-visual on every cycle), this is a scene-level, one-shot readiness signal — the right hook for fitting the camera or hiding a startup spinner once a mixed scene of multiscale images, in-memory images, and geometry has fully loaded.

Parameters:

Name Type Description Default
scene_id UUID

ID of the scene to reslice and watch.

required
callback Callable[[], None]

Zero-argument callback fired once the scene's data is resident.

required
owner_id UUID or None

Owner under which the temporary subscriptions are registered. Defaults to the controller's own id.

None

on_canvas_first_frame

on_canvas_first_frame(canvas_id: UUID, callback: Callable[[], None], *, owner_id: UUID | None = None) -> None

Fire callback once canvas_id has rendered its first frame.

The first rendered frame guarantees the canvas has reached its final logical size and its camera matrix has been applied — the precondition for fitting the camera and computing view-dependent (multiscale) slice requests at the correct level of detail. This is a timer-free replacement for deferring startup work with QTimer.singleShot(0).

A draw is requested immediately so the frame is guaranteed to arrive whether or not the event loop is already running.

Parameters:

Name Type Description Default
canvas_id UUID

ID of the canvas to watch.

required
callback Callable[[], None]

Zero-argument callback fired once, on the first frame.

required
owner_id UUID or None

Owner under which the temporary subscription is registered. Defaults to the controller's own id.

None

unsubscribe_owner

unsubscribe_owner(owner_id: UUID) -> None

Remove all event subscriptions registered under owner_id.

GUI widgets should call this from their Qt closeEvent or destroyed signal handler to deterministically clean up their bus subscriptions.

Parameters:

Name Type Description Default
owner_id UUID

The UUID used as owner_id when the subscriptions were registered (typically the widget's own self._id).

required

connect_widget

connect_widget(widget: WidgetView, *, subscription_specs: list[SubscriptionSpec] | None = None) -> None

Wire a widget's psygnal signals to the bus and register subscriptions.

Widgets declare their intent through two psygnal signals and an optional list of SubscriptionSpec objects, so they never import or hold a reference to CellierController.

The caller is responsible for constructing the widget and passing the specs — typically obtained from widget.subscription_specs().

Parameters:

Name Type Description Default
widget WidgetView

Any object exposing:

  • widget._id — a UUID identifying the widget.
  • widget.changed — a psygnal Signal that emits a CellierUpdateEventTypes instance when the user changes a value. Connected to incoming_events.emit.
  • widget.closed — a psygnal Signal (no arguments) emitted when the widget is closed. Triggers unsubscribe_owner(widget._id).
required
subscription_specs list[SubscriptionSpec] | None

Optional list of SubscriptionSpec entries describing which outgoing bus events the widget wants to receive. Pass None (or omit the argument) for pure-output widgets that do not need model-driven updates.

None