Viewer classes (Jupyter objects you control from Python)#

Viewer classes are the stateful notebook objects returned by show() and show_anndata().

They are designed for two-way interaction:

  • Viewer → Python: hooks/events like @viewer.on_selection

  • Python → Viewer: commands like viewer.highlight_cells(...)

If you prefer a minimal interface, start with Jupyter (notebook embedding + hooks).


Fast path#

B) AnnData mode (convenient for exploration)#

from cellucid import AnnDataViewer

viewer = AnnDataViewer(adata)  # or "data.h5ad" / "data.zarr"
viewer.display()

Practical path (what you can do with a viewer)#

1) Register hooks (viewer → Python)#

@viewer.on_selection
def on_selection(event):
    cells = event.get("cells", [])
    print("Selected", len(cells))

@viewer.on_ready
def on_ready(event):
    print("Viewer ready:", event)

2) Drive the UI from Python (Python → viewer)#

viewer.set_color_by("cell_type")
viewer.highlight_cells([0, 1, 2], color="#00ff00")
viewer.reset_view()

3) Robust notebooks: wait for readiness#

viewer.wait_for_ready(timeout=30)

4) Capture a session bundle (advanced)#

bundle = viewer.get_session_bundle(timeout=60)

See Sessions (.cellucid-session bundles) for applying the bundle back to AnnData.


Lifecycle and cleanup (important)#

Under the hood, a viewer starts a local server process/thread.

Best practices:

  • If you create many viewers in a long-running notebook, call viewer.stop() when done.

  • If you repeatedly re-run a cell, you may end up with multiple servers unless the old viewer is stopped.


API reference#

CellucidViewer#

class cellucid.CellucidViewer(data_dir, port=None, height=600, auto_open=True)[source]#

Bases: BaseViewer

Interactive cellucid viewer for Jupyter notebooks (pre-exported data).

Embeds the cellucid web viewer in a notebook cell, connected to a local data server. Supports bidirectional communication via hooks.

Example

>>> viewer = CellucidViewer("/path/to/dataset")
>>> viewer.display()
>>>
>>> @viewer.on_selection
... def handle_selection(event):
...     print(f"Selected {len(event['cells'])} cells")
>>>
>>> # Low-level message API still available:
>>> viewer.send_message({'type': 'highlight', 'cells': [1,2,3]})
Parameters:
  • data_dir (str | Path)

  • port (int | None)

  • height (int)

  • auto_open (bool)

__init__(data_dir, port=None, height=600, auto_open=True)[source]#

Initialize the viewer.

Parameters:
  • data_dir (str | Path) – Path to the cellucid dataset directory

  • port (Optional[int]) – Port for the data server (auto-selected if None)

  • height (int) – Height of the embedded viewer in pixels

  • auto_open (bool) – Automatically display when created

AnnDataViewer#

class cellucid.AnnDataViewer(data, port=None, height=600, auto_open=True, **adapter_kwargs)[source]#

Bases: BaseViewer

Interactive viewer for AnnData objects in Jupyter notebooks.

This viewer serves AnnData directly without requiring prepare. It’s more convenient for interactive exploration but slower than using pre-exported data. Supports bidirectional communication via hooks.

Supports: - In-memory AnnData objects - h5ad files (HDF5-based, with lazy loading via backed mode) - zarr stores (directory-based, inherently lazy-loaded)

Example

>>> viewer = AnnDataViewer(adata)
>>> viewer.display()
>>>
>>> @viewer.on_selection
... def analyze_selection(event):
...     subset = adata[event['cells']]
...     sc.pl.violin(subset, ['gene1', 'gene2'])
>>>
>>> # From h5ad file with lazy loading
>>> viewer = AnnDataViewer("/path/to/data.h5ad")
Parameters:
__init__(data, port=None, height=600, auto_open=True, **adapter_kwargs)[source]#

Initialize the AnnData viewer.

Parameters:
  • data (str | Path | anndata.AnnData) – AnnData object or path to h5ad file or zarr directory.

  • port (int | None) – Port for the data server (auto-selected if None).

  • height (int) – Height of the embedded viewer in pixels.

  • auto_open (bool) – Automatically display when created.

  • **adapter_kwargs – Additional arguments passed to AnnDataAdapter.

property viewer_url: str#

Get the full viewer URL with anndata flag.


Edge cases (do not skip)#

Not running in a notebook#

  • If you instantiate a viewer in a plain Python script, display() will print a URL instead of embedding an iframe.

Re-running cells creates multiple servers#

  • Each viewer starts a local server on a port.

  • If you re-run the cell repeatedly without stopping old viewers, you may end up with multiple servers and confusing behavior.

Callbacks and exceptions#

  • Hook callbacks are best-effort; exceptions are logged rather than raised to the notebook output.


Troubleshooting (symptom → diagnosis → fix)#

Symptom: “viewer.display() prints a URL instead of showing an embedded viewer”#

Fix:

  • You are likely not in a notebook context; open the printed URL in a browser tab, or run inside Jupyter/VSCode/Colab.

Symptom: “Hooks never fire”#

Fix:

  • Wait for the viewer to be ready: viewer.wait_for_ready().

  • Temporarily register @viewer.on_message to see raw events and confirm communication.

Symptom: “I have too many running servers”#

Fix:

  • Call viewer.stop() on old viewers.

  • Restart the kernel if you’ve lost track of old viewer instances.


See also#