Show3D#
A 3D volume scrubbed slice by slice, with playback and an interactive-HTML export. See the Show3D tutorial.
Reference#
- class quantem.widget.show3d.Show3D(*args: Any, **kwargs: Any)#
Bases:
WatchedImageFolderMixin,StaticFallbackMixin,AnyWidgetInteractive 3D stack viewer for sequential 2D images.
Renders an (N, H, W) stack through a WebGPU canvas (CPU fallback when WebGPU is unavailable) with a sliding prefetch buffer for smooth playback. The full stack is held once on the Python side; scrubbing and playback ship individual frames or chunks over the Jupyter Comm channel. Common use cases: defocus sweep, time series, depth stack, in-situ movie, side-by-side trial comparison.
Features#
Interactive scrubber +
play/pause/stop/gotowith bookmark and loop rangePer-frame statistics, log scale, percentile auto-contrast, manual vmin/vmax
Diff mode (vs first frame or vs previous frame) for delta visualization
ROI tools (circle / square / rectangle / annular) with per-frame timeseries
Line profiles sampled across the full stack
Page-aware depth/time kymographs for matched single-panel comparisons
FFT panel with compact quality labels and optional Hann window
Side-by-side multi-panel mode with linked or independent zoom / pan / contrast
Panel visibility controls by index or title without removing data
Frame hiding (
hide,show,set_hidden,show_all) without rebuildingPython-side PNG / PDF / TIFF single-frame save via
save_imageJSON state save/load via
state_dict/load_state_dict/saveExplicit
freeto release VRAM/RAM held by traitlets observers
- param data:
3D array of shape (N, height, width) where N is the stack dimension. Also accepts a 2D array (treated as a single-frame stack), a torch tensor (CPU or GPU, any dtype), or a quantem
Dataset3d. Complex input is rejected; cast to magnitude or phase first.- type data:
array_like
- param labels:
Labels for each slice (e.g.,
["C10=-500nm", "C10=-400nm", ...]). IfNone, uses string slice indices.- type labels:
list[str] | None, optional
- param frame_metadata:
Generic metadata for each frame. If
labelsis not provided, entries are formatted into labels usingframe_label_formator a compactkey=valuedefault. This is repository-neutral: upstream packages can pass fields such asiteration,defocus_nm,loss,dose, or any other JSON-like values.- type frame_metadata:
sequence of mapping, optional
- param panel_frame_metadata:
Per-panel frame metadata, shaped
[panel][frame]. Ifpanel_frame_labelsis not provided, entries are formatted into the per-panel overlay labels usingframe_label_format.- type panel_frame_metadata:
sequence of sequence of mapping, optional
- param frame_label_format:
Formatter for metadata-derived labels. A string uses Python format fields, for example
"iter {iteration} · df={defocus_nm:.1f} nm". A callable may accept(metadata),(metadata, frame_idx), or(metadata, frame_idx, panel_idx)and is resolved to strings before HTML export.- type frame_label_format:
str or callable, optional
- param title:
Title to display above the image.
- type title:
str, optional
- param cmap:
Colormap name. Use Colormap enum (Colormap.MAGMA, Colormap.VIRIDIS, etc.) or string (“magma”, “viridis”, “gray”, “inferno”, “plasma”).
- type cmap:
str or Colormap, default Colormap.MAGMA
- param vmin:
Minimum value for colormap. If None, uses data min.
- type vmin:
float, optional
- param vmax:
Maximum value for colormap. If None, uses data max.
- type vmax:
float, optional
- param sampling:
Real-space sampling for the scale bar (lateral). Pairs with
units.- type sampling:
float or tuple of float, optional
- param units:
Unit for
sampling(e.g."A"for Angstrom,"nm"). Matches the quantemDataset/Show2D/Show4DSTEMconvention.- type units:
str or list of str, optional
- param log_scale:
Use log scale for intensity mapping.
- type log_scale:
bool, default False
- param auto_contrast:
Use percentile-based contrast (ignores vmin/vmax).
- type auto_contrast:
bool, default True
- param link_contrast:
Share one contrast range across panels. Paged data defaults to
Falseso each reconstruction uses its own automatic percentile range; ordinary multi-panel data defaults toTrue. PassTruefor a matched physical scale across every page and panel.- type link_contrast:
bool, optional
- param percentile_low:
Lower percentile for auto-contrast.
- type percentile_low:
float, default 0.5
- param percentile_high:
Upper percentile for auto-contrast.
- type percentile_high:
float, default 99.5
- param fps:
Frames per second for playback, capped at 60.
- type fps:
float, default 30.0
- param timestamps:
Timestamps for each frame (e.g., seconds or dose values).
- type timestamps:
list of float, optional
- param timestamp_unit:
Unit for timestamps (e.g., “s”, “ms”, “e/A2”).
- type timestamp_unit:
str, default “s”
- param show_fft:
Show the FFT for the current frame and visible panels.
- type show_fft:
bool, default False
- param fft_layout:
Place the FFT below the real-space grid, beside it, or as one inset in every visible panel.
- type fft_layout:
{“bottom”, “right”, “overlay”}, default “bottom”
- param fft_overlay_position:
Initial corner for inset FFTs. Inset overlays can be dragged and snap to the nearest corner.
- type fft_overlay_position:
{“top-left”, “top-right”, “bottom-left”, “bottom-right”}, default “top-left”
- param fft_overlay_size:
Inset FFT size as a fraction of its real-space panel, from 0.2 to 0.7.
- type fft_overlay_size:
float, default 0.35
- param fft_overlay_zoom:
Initial shared FFT magnification for bottom, right, and overlay layouts. Every FFT tile or inset displays this live value as an
N.N×badge whileshow_zoom_indicatoris enabled.- type fft_overlay_zoom:
float, default 1.0
- param fft_window:
Apply a Hann window before computing the FFT.
- type fft_window:
bool, default True
- param fft_metrics:
Show cached FFT sharpness, peak-count, and peak-SNR labels.
- type fft_metrics:
bool, default True
- param size:
Canvas rendering size in CSS pixels (the on-screen width of the main viewport).
0uses the frontend default (500 px). Pass e.g.size=800to enlarge for a presentation, orsize=300to compress alongside a control panel. This controls display only - the underlying stack resolution is never resampled; scrubbing and zoom still see every pixel of the full-resolution frame.- type size:
int, default 0
- param panel_width_px:
Display width for each panel in CSS pixels. When
>0, this wins oversizeand the frontend derives the total grid canvas from the active column count. This is display-only; the syncedpanel_width_pxtrait still records source panel geometry for multi-panel frame slicing.- type panel_width_px:
int, default 0
- param padding:
Zero-valued spatial border added to every frame before display. Use this when aligned movies need extra field of view so shifted frames do not crop at the canvas edge. An int pads rows and columns equally;
(rows, cols)pads each axis independently.- type padding:
int or tuple[int, int], default 0
- param crop:
Spatial crop applied before padding. Use an int to crop all sides,
(rows, cols)for symmetric row/column cropping, or(top, bottom, left, right)for side-specific cropping.- type crop:
int or tuple[int, int] or tuple[int, int, int, int], default 0
- param max_cols:
Multi-panel grid wrap.
0= single row (no wrap),N>0= wrap into rows of at mostNpanels. Default4is a good fit for a 13”–16” laptop screen; bump to6on wide monitors or drop to3for a portrait split layout. Empty trailing cells in a partial last row are not rendered (transparent, non-interactive).- type max_cols:
int, default 4
- param panel_gap:
Gap in CSS pixels between adjacent panels.
0= flush (panels share an edge - useful for tiled montages),20= roomy (clear separation for slides). Single-panel widgets ignore this.- type panel_gap:
int, default 0
- param inter_panel_gap_px:
Explicit replacement for
panel_gapwhen the layer between panels needs a chosen color. The gap is real layout space between panel slots, not a stroke drawn inside a panel.- type inter_panel_gap_px:
int, str, optional
- param inter_panel_gap_color:
Explicit replacement for
panel_gapwhen the layer between panels needs a chosen color. The gap is real layout space between panel slots, not a stroke drawn inside a panel.- type inter_panel_gap_color:
int, str, optional
- param gallery_outer_border_px:
Frame around the entire multi-panel gallery. This is separate from the inter-panel gap and from per-panel strokes.
- type gallery_outer_border_px:
int, str, optional
- param gallery_outer_border_color:
Frame around the entire multi-panel gallery. This is separate from the inter-panel gap and from per-panel strokes.
- type gallery_outer_border_color:
int, str, optional
- param panel_inner_border_px:
Stroke drawn inside each panel slot over the image edge. Defaults to
0in Show3D to preserve the existing no-stroke canvas look.- type panel_inner_border_px:
float, str, optional
- param panel_inner_border_color:
Stroke drawn inside each panel slot over the image edge. Defaults to
0in Show3D to preserve the existing no-stroke canvas look.- type panel_inner_border_color:
float, str, optional
- param panel_title_font_size:
Font size in CSS pixels for the per-panel title drawn at the top of each multi-panel slot. Bump to
14–16for slide-projection clarity; drop to9to fit titles inside narrow panels on a small screen.- type panel_title_font_size:
int, default 11
- param hidden_panels:
Multi-panel entries to collapse from the initial view. Integers are zero-based panel indices; strings match
panel_titlesexactly. Hidden panels stay in the widget state and exported HTML, and can be restored later withshow_panelorshow_all_panels.- type hidden_panels:
sequence of int or str, optional
- param panel_order:
Multi-panel display order. Integers are zero-based panel indices; strings match
panel_titlesexactly. Reordering is display-only: source data, titles, stars, and hidden-panel state stay keyed by their original panel indices.- type panel_order:
sequence of int or str, optional
- param panel_groups:
Rectangular grouping overlays for related panels. Each dict accepts
{"panels": [0, 1, 2], "color": "#22c55e", "label": "raw"}or contiguous{"start": 0, "end": 2, ...}panel indices.- type panel_groups:
sequence of dict, optional
- param panel_overlays:
Reproducible per-panel circle, rectangle, and square overlays. A mapping keyed by panel index or
panel_titlesvalue targets specific panels; a per-panel list aligns with panel order. Coordinates are data pixels by default using(row, col)conventions, andcoords="relative"switches to normalized 0-1 panel coordinates. Style keys includestroke,stroke_width,line_style,dash,stroke_opacity,fill,fill_opacity,opacity, andz_order.- type panel_overlays:
mapping or sequence, optional
- param overlays:
Convenience alias for shared geometric overlays. A single overlay or a flat list without
panel=is broadcast to every panel. Use eitheroverlaysorpanel_overlays, not both.- type overlays:
mapping or sequence, optional
- param show_panel_titles:
Draw the top-center per-panel title and frame counter on multi-panel canvases. Set
Falsefor clean GIF/video exports.- type show_panel_titles:
bool, default True
- param show_resize_handles:
Render the bottom-right corner triangle on every real panel. Dragging any handle resizes the entire multi-panel canvas (linked). Set
Falseto declutter a screenshot or printed figure where the operator already has the layout they want.- type show_resize_handles:
bool, default True
- param show_zoom_indicator:
Draw the live
1.0×-style zoom readout at the bottom-left of every real-space panel and every FFT tile or inset. SetFalsefor clean static layouts or when the scale bar alone is enough to communicate scale.- type show_zoom_indicator:
bool, default True
- param show_scale_bar:
Draw the bottom-right scale bar. When
pixel_sizeis provided the label uses physical units; otherwise it shows pixel units. SetFalsefor GIF/video exports or uncluttered figure captures.- type show_scale_bar:
bool, default True
- param show_controls:
Show the live control UI. Set
Falsefor a permanently clean display.- type show_controls:
bool, default True
- param controls_collapsed:
Start with the live control UI collapsed behind a small GUI toggle. Unlike
show_controls=False, users can expand the controls in the frontend and Python can callexpand_controls()later.- type controls_collapsed:
bool, default False
- param debug:
Show a compact frontend FPS/debug badge in the widget title row.
- type debug:
bool, default False
- param save_state:
When False, saved notebooks omit heavy stack buffers and keep a compact static preview for cold reopen. Set True only for small widgets that must reopen interactively without rerunning the kernel.
- type save_state:
bool, default False
- param notebook_preview_format:
Static preview format used when
save_state=False."jpeg"is the most portable notebook default,"webp"is smaller for local/report workflows,"png"is lossless but larger, andNonedisables the preview.- type notebook_preview_format:
{“jpeg”, “webp”, “png”} or None, default “jpeg”
- param notebook_preview_quality:
Lossy preview quality for JPEG/WebP, from 1 to 100. Ignored for PNG.
- type notebook_preview_quality:
int, default 88
- param notebook_preview_max_px:
Longest panel side for the saved-notebook preview. Lower values make notebooks smaller; higher values make the static fallback sharper.
- type notebook_preview_max_px:
int, default 512
- param notebook_preview_frames:
Zero-based frame indices to render into the saved-notebook static preview for single-panel Show3D. When omitted, the preview shows the current
slice_idxonly. Use this for compact cold-reopen contact sheets such as frames[0, 12, 25, 80].- type notebook_preview_frames:
sequence of int, optional
- param notebook_preview_ncols:
Number of columns for
notebook_preview_frames.Noneor0uses the widget column setting, capped by the number of preview frames.- type notebook_preview_ncols:
int, optional
- param denoise:
Display-only denoise method for sparse map stacks (EDS, low dose), applied to every playback frame and to the FFT view. Three orthogonal choices:
"none","gaussian", or"anscombe"(Poisson count-respecting smoothing); binning is the separatedenoise_binknob. Recommendation ladder: sparse EDS ->"anscombe"withdenoise_bin=2and sigma 6-10; very sparse ->"anscombe"withdenoise_bin=4and sigma 8-12; decent-dose HAADF ->"gaussian"sigma 1-2 or"none"; anything quantitative ->"none". The compound spellings"bin2","bin2_anscombe"and"bin4_anscombe"stay accepted as aliases that fold into (mode, bin);"tv"remains available from Python (not in the UI menu). Pure view transform: the stored stack, the stats row, and every export of raw data keep the original counts, and the lossless default is"none". When active, a one-line banner announces the reduction and how to get raw counts back. An active filter also reshapes the FFT view, so setdenoise="none"for quantitative FFT work.- type denoise:
str, default “none”
- param denoise_sigma:
Smoothing scale in pixels for the Gaussian/Anscombe display filters.
- type denoise_sigma:
float, default 4.0
- param denoise_bin:
Display-side 2x bin passes for SNR, combined with
denoise.1(the default) is lossless. Independent ofdisplay_bin(the GPU display budget knob).- type denoise_bin:
{1, 2, 4}, default 1
- param show_denoise:
Show the denoise controls row. Hidden by default to keep the widget clean; auto-enabled when the stack starts with an active denoise. An active reduction always shows its banner, even with the row hidden. The deprecated aliases
display_filter,display_sigmaandspatial_binare still accepted for one release and map ontodenoise,denoise_sigmaanddenoise_bin.- type show_denoise:
bool, default False
- render_total_ms#
End-to-end wall clock from constructor start to first browser paint, populated by a JS→Python round-trip after the first canvas render.
Noneuntil the browser has actually painted; also printed to stdout when it fires. Use to triage “is it Python, wire, or the browser?” during live acquisitions.- Type:
int or None
- render_python_build_ms#
Subset of
render_total_mscovering Python__init__only.- Type:
int or None
- render_wire_js_ms#
Subset covering everything after Python returns: Comm transfer, JS decode, colormap, and canvas paint.
- Type:
int or None
Example
Instantiate Show3D with a stack of 2D images, drive playback, and persist the display state to disk:
>>> import numpy as np >>> from quantem.widget import Show3D >>> stack = np.random.rand(12, 256, 256).astype(np.float32) >>> labels = [f"C10={c:.0f}nm" for c in np.linspace(-500, -200, 12)] >>> w = Show3D(stack, labels=labels, title="Defocus Sweep", sampling=0.5, units="A") >>> w.play() >>> w.goto(3) >>> w.save("show3d_state.json")
Notes
The stack is loaded once into
self._data(float32);set_imagereplaces the data without rebuilding the widget.For live acquisitions or reconstruction loops where the stack grows over time, construct the widget with
offline=Falsebefore callingset_image. Small initial stacks can otherwise use the saved/offline notebook representation, which is meant for static notebook state and standalone exports rather than streaming new frames.display_bin="auto"keeps Show3D source pixels unchanged. For heavy multi-panel movies or standalone exports, pass an explicitdisplay_bin=Nto build a smaller display stack. This is a display tradeoff: it improves first load and playback, while native-pixel viewing requiresdisplay_bin=1or a separate high-resolution workflow.Multi-panel mode is auto-detected from input shape and configured via
n_panels,panel_titles, andlink_panels.Call
free()before discarding the widget;delalone will not release VRAM because traitlets observers pin the refcount.
- classmethod from_rgb(stack, **kwargs) Self#
Play a true-color stack, bypassing grayscale/color auto-detection.
The plain constructor treats a trailing axis of 3 or 4 as color only when it is unambiguous (
N > 4frames).from_rgbis the explicit path for the cases it cannot call: a short color stack of 4 or fewer frames, or an(N, H, W, 3)array the heuristic might read as a scalar field. Color ships to the browser in full; a Rec. 709 luminance plane drives stats, FFT, and ROI.- Parameters:
stack (np.ndarray) – Color frames as
(N, H, W, 3),(N, H, W, 4), or a single(H, W, 3)frame.uint8or float in[0, 1].**kwargs – Any other
Show3Doption (title,fps,sampling, …).
- Returns:
A viewer painting the stack in true color.
- Return type:
Examples
>>> import numpy as np >>> Show3D.from_rgb(np.stack([frame_a, frame_b])) # 2-frame color
- classmethod from_figure_gallery(figures: Mapping[str, ndarray] | Sequence[ndarray], *, labels: Sequence[str] | None = None, **kwargs) Self#
Browse a set of named result figures as one labeled, scrubbable stack.
Built for figure selection - hand it the candidate figures (a lambda sweep, competing reconstructions, before/after variants) and scrub or keyboard through them to pick the best, with each figure’s name in the frame label. Grayscale and true-color figures can be mixed: if any figure is color, all are promoted to RGB so one viewer shows them together. Every figure must share the same height and width.
- Parameters:
figures (mapping or sequence of np.ndarray) – A
{label: image}mapping (labels come from the keys), or a sequence of images. Each image is(H, W)grayscale or(H, W, 3)/(H, W, 4)color.labels (sequence of str, optional) – Frame labels when
figuresis a sequence. Defaults tofigure 1,figure 2, … .**kwargs – Any other
Show3Doption (title,fps,cmap, …).
- Returns:
A viewer scrubbing through the labeled figures.
- Return type:
- Raises:
ValueError – If
figuresis empty, or the figures do not all share one(H, W).
Examples
>>> Show3D.from_figure_gallery({"lambda 0.3": fig_a, "lambda 3": fig_b}) >>> Show3D.from_figure_gallery([recon_1, recon_2], labels=["adam", "lbfgs"])
- classmethod from_gif(path: str | Path, *, fps: float | None = None, title: str | None = None, frame_labels: list[str] | bool | None = None, **kwargs) Self#
Open a static or animated GIF as a playable stack.
The GIF is decoded through
quantem.widget.io.read_gifinto a grayscaleDataset3d. Pass normalShow3Dkeyword arguments such ascmap,show_controls,max_cols, orpanel_width_px.
- classmethod from_folder(path: str | Path, *, pattern: str = '*', recursive: bool = False, watch: bool = True, watch_interval: float = 1.0, **kwargs) Self#
Play naturally ordered, full-resolution folder images as one stack.
Stable additions update this widget in place. Every file is decoded through
quantem.widget.io.read_image(); failed or partially written files remain pending for a later poll. Folder size never creates pages: every matching file extends the frame axis of this one stack.
- classmethod from_panel_folders(panel_paths: Sequence[str | Path], *, pattern: str | None = None, file_type: str | None = None, frame_count: int | None = None, labels: Sequence[str] | None = None, panel_frame_labels: Sequence[Sequence[str]] | None = None, panel_titles: Sequence[str] | None = None, **kwargs) Self#
Open same-shape image folders as lazy full-resolution panels.
Only the first frame from each panel is decoded during construction so remote notebooks can mount promptly. The frame server decodes requested native frames on demand; source pixels remain full resolution.
- to_show2d(frame: int | None = None, panel: Sequence[int | str] | int | str | None = None, *, title: str | None = None, copy: bool = True, include_hidden: bool = False)#
Create a
Show2Dview from the current Show3D frame.By default this converts the current frame for all visible panels. Pass
panel=...to choose a panel by index or title. Hidden panels are skipped unlessinclude_hidden=True.
- set_image(data, labels: list[str] | None = None, *, rgb: bool | None = None, display_bin: int | None = None) None#
Replace the stack data in place without rebuilding the widget.
Swaps in a new stack while preserving display settings (cmap, contrast, log scale, pixel size, FFT toggle, playback config) so the operator can cycle through datasets in one widget cell. Resets state that is tied to the previous frame dimensions: ROIs and the line profile are cleared if
(height, width)changes; bookmarks and the loop range are clamped to the newn_slices; the playback prefetch buffer is invalidated; cached display data and any in-flight ROI debounce timer are dropped.- Parameters:
data (array_like) – 3D stack
(N, H, W), a 2D image (promoted to a single-frame stack), a torch tensor, or a quantemDataset3d. Complex data is rejected (cast to magnitude or phase first); non-finite data is rejected with a hint tonp.nan_to_num.labels (list[str] | None, optional) – New per-frame labels. If
None, string indices"0"..."N-1"are used.display_bin (int | None, optional) – Explicit display-only binning for the replacement stack.
Nonekeeps the historicalset_imagebehavior of native-pixel display. Folder-backed updates pass their existing factor so a watched append cannot silently switch resolution.
- Returns:
Mutates the widget in place. The browser canvas re-renders automatically via traitlet sync.
- Return type:
None
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack_a, title="A") >>> w.set_image(stack_b, labels=[f"frame {i}" for i in range(len(stack_b))])
Notes
Prefer
set_imageover constructing a newShow3Dwhen iterating through datasets in the same cell: it avoids re-creating the canvas and preserves the operator’s contrast / zoom / cmap state.For live stack growth, instantiate the widget with
offline=False:w = Show3D(first_frame[None], offline=False). Then append frames withw.set_image(np.stack(frames))and setw.slice_idx = len(frames) - 1to jump to the newest frame.The new stack is cast to
float32. If float64 input contains values outsidefloat32range, an error is raised (silent overflow toinfwould corrupt stats).
- state_dict() dict#
Return a JSON-serializable snapshot of every user-tunable trait.
Captures display config (cmap, contrast, log scale, FFT, diff mode), playback config (fps, loop, range, bookmarks, playback path), per-frame labels and timestamps, scale-bar settings, and any active ROIs and line profile. The raw stack data is NOT included; pair the snapshot with the original
dataargument (or re-attach viaset_image) on restore.Key order in the returned dict is deliberate: cross-validating pairs (
percentile_highbeforepercentile_low,loop_endbeforeloop_start) are emitted in the order the validators expect so a round-trip throughload_state_dictcannot wedge in an intermediate state.- Returns:
Mapping of trait name -> serializable value. Suitable for
json.dumpor use withsave/load_state_dict.- Return type:
dict
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> state = w.state_dict() >>> w2 = Show3D(stack) >>> w2.load_state_dict(state)
Notes
Schema versioning is handled inside
save; the dict returned here is the unversioned inner payload.To save directly to disk in one step, use
save(path)instead.
- save(path: str)#
Write the current widget state to a versioned JSON file.
Wraps
state_dictin a small envelope that records the widget type ("Show3D") and a schema version soload_state_dictcan refuse states that belong to a different widget. The raw stack data is NOT written; only the display / playback / ROI / profile configuration.- Parameters:
path (str) – Destination JSON file path. Parent directories must already exist.
- Return type:
None
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack, cmap="viridis", sampling=0.5, units="A") >>> w.save("show3d_state.json")
Notes
To restore, instantiate
Show3Dwith the (possibly different) data and callw.load_state_dict(json.loads(open(path).read())).Use
state_dictdirectly if you want to embed widget state in a larger document instead of a standalone file.
- export_html(path: str | Path | None = None, *, title: str | None = None, mode: str = 'single', encoding: str = 'full', downsample: int | None = None, quantized: bool | None = None, max_mb: float | None = 80.0) Path#
Write a standalone HTML viewer for sharing.
The exact export embeds the current float32 stack bytes and preserves numerical precision. The quantized export writes the existing offline uint8 representation plus global min/max metadata, making a smaller single-file report for visual sharing. Preferred export options are
mode="single",encoding="full"orencoding="uint8". Usedownsample=2/4/8withencoding="uint8"for compact report HTML from heavy multi-panel movies.quantizedis kept as a compatibility alias forencoding="uint8". If the widget is playing when exported, the standalone HTML starts playback on load.- Parameters:
path (str or pathlib.Path, optional) – Destination HTML path. Defaults to the current working directory with the widget title, stack shape, and export mode in the name.
quantized (bool, default False) – If True, write the uint8 offline pack. If False, write exact float32 bytes.
downsample (int, optional) – Spatial integer binning factor for the exported HTML only. The source widget data and live notebook view are unchanged. Binned exports multiply the scale-bar pixel size by this factor.
title (str, optional) – Browser page title. Defaults to the widget title or class name.
- Returns:
The written HTML file.
- Return type:
pathlib.Path
- export_sidecar(out_dir: str | Path, *, title: str | None = None, stack_filename: str = 'offline_stack.u8', html_filename: str = 'index.html', manifest_filename: str = 'manifest.json') Path#
Write a thin HTML viewer + on-disk offline stack (no single-file embed).
Layout:
out_dir/ index.html # small: widget shell only offline_stack.u8 # raw uint8 stack bytes (sidecar) manifest.json # shape / titles / paths
Serve
out_dirover HTTP (notfile://) so the browser canfetchthe sidecar. Full spatial resolution is preserved (display_binof the live widget is used; no export downsample).
- load_state_dict(state: dict) None#
Apply a saved
state_dictsnapshot to this widget.Restores display, playback, ROI, and profile configuration from a dict produced by
state_dict. Unknown keys (typically from a newer widget version or a different widget type) are dropped with aUserWarninginstead of raising, so partial / forward-compatible loads succeed. Cross-validated trait pairs are applied atomically:percentile_low/percentile_highare validated together and applied in whichever order keeps the invariantlow < high.vmin/vmaxare cleared first so either bound can be set regardless of the current contrast limits.loop_start/loop_endare clamped to[0, n_slices)and applied in safe order via the-1sentinel.
- Parameters:
state (dict) – Mapping previously returned by
state_dict(or its on-disk equivalent). Oldcanvas_sizealiases are migrated tosize; the constructor-deriveddisplay_binkey is ignored.- Returns:
Mutates the widget in place.
- Return type:
None
Example
>>> import json >>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.load_state_dict(json.load(open("show3d_state.json")))
Notes
Unknown keys raise
warnings.warnrather than an exception so forward-compatible files from a future widget still load.The new contrast (
vmin/vmax) is also stored on the private user-override slots so subsequentset_imagecalls keep the loaded contrast pinned.
- summary() None#
Print a one-screen status report for the current widget.
Sections include: title and stack shape (with pixel-size readout when set), current frame index and label, raw data min/max/mean, display config (cmap, contrast, log/linear, FFT, diff mode), playback config (fps, loop, reverse, boomerang) and loop range, active ROI count, line profile endpoints if set, and render timing once the first browser paint has fired. Useful for notebook reproducibility and bug reports.
- Returns:
Prints to stdout.
- Return type:
None
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack, title="defocus") >>> w.summary()
Notes
Rendered:reads(pending first browser paint)until the JS side has round-tripped its first paint timestamp.
- play() Self#
Start playback from the current frame.
Sets the
playingtrait toTrue, which triggers the JS playback loop and the Python-side sliding-prefetch buffer that ships chunks of frames ahead of the scrubber position.- Returns:
The widget, for chaining (
w.play().goto(0)).- Return type:
Self
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack, fps=10) >>> w.play()
- pause() Self#
Pause playback at the current frame.
Sets
playingtoFalsewithout resettingslice_idx. Per-frame statistics are refreshed for the current frame on pause.- Returns:
The widget, for chaining.
- Return type:
Self
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.play().pause()
- stop() Self#
Stop playback and jump back to frame 0.
- Returns:
The widget, for chaining.
- Return type:
Self
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.play().stop()
Notes
Unlike
pause,stopresetsslice_idxto 0. Usepauseto keep the current frame visible.
- goto(index: int) Self#
Jump to a specific frame index.
The index is taken modulo
n_slices, so negative or out-of-range values wrap rather than raise.- Parameters:
index (int) – Target frame index. Wrapped into
[0, n_slices).- Returns:
The widget, for chaining.
- Return type:
Self
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.goto(5) >>> w.goto(-1) # last frame
- star_panel(panel: int = 0, frame: int | None = None) Self#
Mark a “best frame” star on a panel.
- Parameters:
panel (int, default 0) – Panel index (0-based). For single-panel widgets, panel=0 is the only valid value.
frame (int | None, default None) – Frame index to star.
Nonestars the currently displayed frame (slice_idx).
- Returns:
The widget, for chaining.
- Return type:
Self
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack_a, stack_b) >>> w.goto(50) >>> w.star_panel(0) # mark frame 50 as best on panel 0 >>> w.star_panel(1, frame=80) # mark frame 80 as best on panel 1 >>> w.starred_frames # → {0: 50, 1: 80}
- unstar_panel(panel: int) Self#
Clear the star on a panel (sets starred[panel] = -1).
- property starred_frames: dict[int, int]#
Mapping of panel index → starred frame index, only for panels that have a star set. Returns
{}if no panel is starred. Useful for downstream code likebest_iters = {trial: w.starred_frames.get(i)}.
- property starred_pages: list[int]#
Zero-based page indices marked with a star.
- property ordered_panels: list[int]#
Zero-based movie panel indices in the current display order.
- property visible_panels: list[int]#
Zero-based panel indices currently visible in the canvas grid.
- star_page(page: int) Self#
Mark a page with a star.
- unstar_page(page: int) Self#
Clear the star on a page.
Replace the hidden panel set by index or exact panel title.
Hidden panels are collapsed from the canvas grid and skipped by panel-scoped frontend computations, but remain available in state and standalone HTML export. At least one panel must stay visible.
- Parameters:
panels (sequence of int or str, int, or str) – Panels to hide. Integers are zero-based panel indices; strings must match a panel title exactly.
- Returns:
The widget, for chaining.
- Return type:
Examples
>>> w = Show3D(a, b, panel_titles=["SSB", "Mean DP"]) >>> w.set_hidden_panels(["Mean DP"]) >>> w.set_hidden_panels([1])
- hide_panel(*panels: int | str) Self#
Hide one or more panels by zero-based index or exact title.
Hidden panels are not removed from the widget; they can be restored with
show_panelorshow_all_panelsand are preserved in HTML export.
- show_panel(*panels: int | str) Self#
Restore one or more hidden panels by zero-based index or exact title.
- show_all_panels() Self#
Restore every panel in the canvas grid.
- set_panel_order(panels: Sequence[int | str]) Self#
Set the multi-panel display order by panel index or exact title.
The order is display-only: source data, titles, hidden state, stars, and per-panel contrast stay keyed by their original panel indices.
- reset_panel_order() Self#
Restore the natural source-panel order.
- move_panel(panel: int | str, position: int) Self#
Move one panel to a zero-based display position.
- collapse_controls() Self#
Collapse the live control UI while leaving the GUI toggle available.
- expand_controls() Self#
Expand the live control UI.
- toggle_controls() Self#
Toggle the collapsed state of the live control UI.
- property visible_indices: list[int]#
Live list of frame indices NOT in hidden_indices. Read-only; mutate via set_hidden() / show_all() / hide().
- hide(*indices: int) Self#
Mark one or more frames as hidden from the scrubber.
Hidden frames are excluded from the scrubber UI and from playback but kept in memory; restore them with
showorshow_all. Idempotent: hiding an already-hidden index is a no-op. At least one frame always stays visible, so a call that would hide every frame is silently rejected.- Parameters:
*indices (int) – One or more frame indices to hide. Values outside
[0, n_slices)are accepted but never become visible-or-hidden in the UI.- Returns:
The widget, for chaining (
w.hide(0, 1).hide(5)).- Return type:
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.hide(0, 1, 2)
Notes
The
hideabletrait must beTruefor the JS scrubber to show the hide overlay; the underlyinghidden_indicestrait is always honored regardless.
- show(*indices: int) Self#
Restore one or more previously hidden frames.
Idempotent: indices that are not currently hidden are silently ignored.
- Parameters:
*indices (int) – Frame indices to make visible again.
- Returns:
The widget, for chaining.
- Return type:
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.hide(2).show(2)
Replace the hidden set wholesale.
Discards any current
hidden_indicesand installsindicesclamped to[0, n_slices). At least one frame is always visible; if the input would hide every frame, the largest index is dropped.- Parameters:
indices (list[int]) – Full replacement set of hidden frame indices. Order and duplicates are normalized internally.
- Returns:
The widget, for chaining.
- Return type:
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.set_hidden([0, 1, 2, 7])
- show_all() Self#
Clear the hidden set so every frame is visible.
- Returns:
The widget, for chaining.
- Return type:
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.hide(0, 1, 2).show_all()
- property roi: dict#
The selected ROI dict (or the first ROI if none is selected).
- delete_selected_roi() Self#
Delete the currently selected ROI.
- get_roi_geometries(*, visible_only: bool = True) list[dict[str, Any]]#
Return normalized ROI geometry in image
(row, col)coordinates.The raw
roi_listtrait remains synced for widget state, while this helper gives notebooks, reports, and agents a stable public shape for downstream measurements across the scrubbed stack. Circle ROIs includecenterandradius. Rectangles and squares include clockwisecorners. Annular ROIs includeradius_innerandradius_outer.
- roi_geometries(*, visible_only: bool = True) list[dict[str, Any]]#
Return normalized ROI geometry in image
(row, col)coordinates.The raw
roi_listtrait remains synced for widget state, while this helper gives notebooks, reports, and agents a stable public shape for downstream measurements across the scrubbed stack. Circle ROIs includecenterandradius. Rectangles and squares include clockwisecorners. Annular ROIs includeradius_innerandradius_outer.
- set_notebook_preview_frames(frames: Sequence[int] | int | None, *, ncols: int | None = None) Self#
Choose Show3D frames for the saved-notebook static preview.
- Parameters:
frames (sequence of int, int, or None) – Zero-based frame indices to show when a lightweight saved notebook is reopened without a running kernel.
Noneor an empty sequence restores the default current-frame preview.ncols (int, optional) – Contact-sheet column count.
Noneleaves the current setting unchanged;0uses the widget’s column setting.
- Returns:
The widget, for chaining.
- Return type:
- clear_notebook_preview_frames() Self#
Restore the default saved preview: the current frame only.
- set_roi(row: int, col: int, radius: int = 10) Self#
Set selected ROI position and size (creates one if needed).
- roi_circle(radius: int = 10) Self#
Set selected ROI shape to circle.
- roi_square(half_size: int = 10) Self#
Set selected ROI shape to square.
- roi_rectangle(width: int = 20, height: int = 10) Self#
Set selected ROI shape to rectangle.
- roi_annular(inner: int = 5, outer: int = 10) Self#
Set selected ROI shape to annular (donut).
- property profile#
Get profile line endpoints as [(row0, col0), (row1, col1)] or [].
- property profile_values#
Get profile values for the current frame and active single-panel page.
- property profile_distance#
Get total distance of the profile line in calibrated units (Å or px).
- set_profile(start: tuple[float, float], end: tuple[float, float]) Self#
Set a line profile between two points (image pixel coordinates).
- Parameters:
start (tuple of (row, col)) – Start point in pixel coordinates.
end (tuple of (row, col)) – End point in pixel coordinates.
- clear_profile() Self#
Clear the current line profile.
- profile_all_frames(start: tuple[float, float] | None = None, end: tuple[float, float] | None = None, *, panel: int | None = None, page: int | None = None) ndarray#
Extract the line profile from every frame, returning (n_slices, n_points).
Uses the current profile_line unless start/end are provided. Always samples raw data (ignores diff_mode).
- Parameters:
start (tuple of (row, col), optional) – Start point. Overrides current profile_line.
end (tuple of (row, col), optional) – End point. Overrides current profile_line.
panel (int, optional) – Absolute source-panel index. Defaults to the first panel on the active page, or panel 0 for a non-paged widget.
page (int, optional) – Page index for a paged widget. The first panel on that page is sampled. Pass
panelinstead when a page contains several panels.
- Returns:
Shape (n_slices, n_points) float32 array.
- Return type:
np.ndarray
- profile_all_pages(start: tuple[float, float] | None = None, end: tuple[float, float] | None = None, *, panel_slot: int = 0) ndarray#
Extract one spatially matched profile stack from every page.
Returns an array shaped
(n_pages, n_slices, n_points). The same profile endpoints and panel slot are used on every page, which makes raw/corrected or method-to-method depth comparisons directly aligned.
- save_image(path: str | Path, *, frame_idx: int | None = None, format: str | None = None, dpi: int = 150) Path#
Save a single frame as a PNG, PDF, or TIFF file.
The saved image is colorized with the current
cmapand contrast (vmin/vmaxor percentile auto-contrast), so the saved file matches what the browser shows for that frame.diff_modeis respected:"previous"savesframe - frame[idx-1]and"first"savesframe - frame[0].- Parameters:
path (str or pathlib.Path) – Output file path. Parent directories are created if needed.
frame_idx (int | None, optional) – Frame index to save. Defaults to the current
slice_idx.format (str | None, optional) – One of
"png","pdf","tiff". If omitted, inferred from the file extension; defaults to"png"if no extension.dpi (int, default 150) – DPI metadata written into the file.
- Returns:
The written file path.
- Return type:
pathlib.Path
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack, cmap="viridis") >>> w.save_image("frame_5.png", frame_idx=5) >>> w.save_image("frame.pdf", dpi=300)
Notes
PDF output is converted to RGB internally (no alpha channel).
Frame indices outside
[0, n_slices)raiseIndexError; unsupported extensions raiseValueError.For multi-frame output, call
save_imagein a loop.
- save_gif(path: str | Path, *, quality: str = 'high', fps: float | None = None, playback: str = 'forward', show_frame_labels: bool = False, background: str | tuple[int, int, int] = 'dark', frame_start: int | None = None, frame_stop: int | None = None, every_n: int = 1, max_frames: int | None = None, downsample: int = 1, max_edge_px: int | None = None, show_panel_titles: bool | None = None, show_scale_bar: bool | None = None, show_zoom: bool | None = None, slides_preset: bool = False, ppt_preset: bool = False) Path#
Save the z-stack panels as an animated GIF matching the live image view.
Each frame is colorized with the current
cmapand contrast (vmin/vmaxor percentile auto-contrast,log_scalehonored), carries per-panel scale bars when enabled, respects hidden panels and panel titles, and excludes FFT/profiles/controls from the export.- Parameters:
path (str or pathlib.Path) – Output
.gifpath. Parent directories are created.quality ({"high", "medium", "low"}, default "high") – Spatial resolution tier (1.0 / 0.6 / 0.35). GIF is always a 256-color palette, so quality trades resolution for file size.
fps (float, optional) – Playback rate. Defaults to the widget’s
fps.playback ({"forward", "bounce"}, default "forward") – Frame order.
"forward"is best for time series;"bounce"plays forward then backward without duplicating endpoint frames.show_frame_labels (bool, default False) – Draw per-panel dynamic frame labels from
panel_frame_labels. When enabled and no labels were provided, draw"i/n".background ({"dark", "black", "white"} or RGB tuple, default "dark") – Grid gutter/background color for multi-panel exports.
frame_start (int, optional) – Zero-based Python slice bounds for the exported frame range. The default exports every visible frame.
frame_stop (int, optional) – Zero-based Python slice bounds for the exported frame range. The default exports every visible frame.
every_n (int, default 1) – Export every Nth frame from the selected range.
max_frames (int, optional) – Evenly sample at most this many frames from the selected range.
downsample ({1, 2, 4, 8}, default 1) – Display-only spatial downsample for the animation file.
max_edge_px (int, optional) – Cap each source panel’s exported edge length in pixels.
show_panel_titles (bool, optional) – Override the corresponding live-view overlay settings for the exported animation.
show_scale_bar (bool, optional) – Override the corresponding live-view overlay settings for the exported animation.
show_zoom (bool, optional) – Override the corresponding live-view overlay settings for the exported animation.
slides_preset (bool, default False) – Convenience preset for slide decks. Unless explicitly overridden, exports at most 40 frames with
max_edge_px=512.ppt_preset (bool, default False) – Deprecated alias for
slides_preset.
- Returns:
The written GIF path.
- Return type:
pathlib.Path
Notes
Browser zoom/pan is a view-only transform and is not reflected (the full frame is exported). FFT overlays and analysis panels are intentionally omitted so the GIF contains only the scientific image panels.
- save_mp4(path: str | Path, *, quality: str = 'high', fps: float | None = None, playback: str = 'forward', crf: int = 18, show_frame_labels: bool = False, background: str | tuple[int, int, int] = 'dark', frame_start: int | None = None, frame_stop: int | None = None, every_n: int = 1, max_frames: int | None = None, downsample: int = 1, max_edge_px: int | None = None, show_panel_titles: bool | None = None, show_scale_bar: bool | None = None, show_zoom: bool | None = None, slides_preset: bool = False, ppt_preset: bool = False) Path#
Save the z-stack panels as an H.264 MP4.
The rendered content matches
save_gif(): image panels only, with current colormap/contrast, panel titles, frame labels, hidden panels, and scale bars. FFT/profiles/controls are omitted.- Parameters:
path (str or pathlib.Path) – Output
.mp4path. Parent directories are created.quality ({"high", "medium", "low"}, default "high") – Spatial resolution tier (1.0 / 0.6 / 0.35).
fps (float, optional) – Playback rate. Defaults to the widget’s
fps.playback ({"forward", "bounce"}, default "forward") – Frame order.
crf (int, default 18) – x264 constant-rate-factor. Lower values are larger and higher quality; 18 is visually high quality.
show_frame_labels (bool, default False) – Draw per-panel dynamic frame labels from
panel_frame_labels. When enabled and no labels were provided, draw"i/n".background ({"dark", "black", "white"} or RGB tuple, default "dark") – Grid gutter/background color for multi-panel exports.
frame_start (int, optional) – Zero-based Python slice bounds for the exported frame range.
frame_stop (int, optional) – Zero-based Python slice bounds for the exported frame range.
every_n (int, default 1) – Export every Nth frame from the selected range.
max_frames (int, optional) – Evenly sample at most this many frames from the selected range.
downsample ({1, 2, 4, 8}, default 1) – Display-only spatial downsample for the animation file.
max_edge_px (int, optional) – Cap each source panel’s exported edge length in pixels.
show_panel_titles (bool, optional) – Override the corresponding live-view overlay settings.
show_scale_bar (bool, optional) – Override the corresponding live-view overlay settings.
show_zoom (bool, optional) – Override the corresponding live-view overlay settings.
slides_preset (bool, default False) – Convenience preset for slide decks. Unless explicitly overridden, exports at most 40 frames with
max_edge_px=512.ppt_preset (bool, default False) – Deprecated alias for
slides_preset.
- Returns:
The written MP4 path.
- Return type:
pathlib.Path
- save_animation_preview(directory: str | Path, *, stem: str | None = None, formats: Sequence[str] = ('gif', 'mp4'), quality: str = 'medium', fps: float | None = None, playback: str = 'forward', show_frame_labels: bool = False, background: str | tuple[int, int, int] = 'dark', frame_start: int | None = None, frame_stop: int | None = None, every_n: int = 1, max_frames: int | None = None, downsample: int = 1, max_edge_px: int | None = None, show_panel_titles: bool | None = None, show_scale_bar: bool | None = None, show_zoom: bool | None = None, slides_preset: bool = False, ppt_preset: bool = False) AnimationExportPreview#
Save GIF/MP4 animation exports and return a notebook preview.
This is a convenience wrapper around
save_gif()andsave_mp4()for notebooks. It writes one or more animation files and returns an object with a rich HTML representation showing the media previews and file sizes.- Parameters:
directory (str or pathlib.Path) – Output directory. Created if missing.
stem (str, optional) – Filename stem. Defaults to a slug derived from
title.formats (sequence of {"gif", "mp4"}, default ("gif", "mp4")) – Animation formats to write.
quality ({"low", "medium", "high"}, default "medium") – Export quality. MP4 also maps this to the same CRF values used by the GUI export menu.
fps (float, optional) – Playback rate. Defaults to the widget’s
fps.playback ({"forward", "bounce"}, default "forward") – Frame order.
show_frame_labels (bool, default False) – Draw per-panel dynamic frame labels from
panel_frame_labels.background ({"dark", "black", "white"} or RGB tuple, default "dark") – Grid gutter/background color for multi-panel exports.
frame_start (int, optional) – Zero-based Python slice bounds for the exported frame range.
frame_stop (int, optional) – Zero-based Python slice bounds for the exported frame range.
every_n (int, default 1) – Export every Nth frame from the selected range.
max_frames (int, optional) – Evenly sample at most this many frames from the selected range.
downsample ({1, 2, 4, 8}, default 1) – Display-only spatial downsample for the animation files.
max_edge_px (int, optional) – Cap each source panel’s exported edge length in pixels.
show_panel_titles (bool, optional) – Override the corresponding live-view overlay settings.
show_scale_bar (bool, optional) – Override the corresponding live-view overlay settings.
show_zoom (bool, optional) – Override the corresponding live-view overlay settings.
slides_preset (bool, default False) – Convenience preset for slide decks.
ppt_preset (bool, default False) – Deprecated alias for
slides_preset.
- Returns:
Notebook-displayable preview object keyed by format label.
- Return type:
AnimationExportPreview
- free() None#
Release VRAM and RAM held by this widget.
Drops the numpy stack, the torch view (if any), the binned display copy, synced frame / ROI / prefetch bytes, and cancels any in-flight ROI debounce timer. When applicable, also flushes the CuPy memory pool and the FFT plan cache (in case a torch tensor was a view into CuPy memory), and releases the active torch device cache (
mpsorcuda).del widgetalone does NOT free memory: traitlets installs strong observer references that pin the widget’s refcount untilfreeis called.- Returns:
Mutates the widget in place. After this call, frame data is gone and rendering will be blank; rebuild a new widget for further use.
- Return type:
None
Example
>>> from quantem.widget import Show3D >>> w = Show3D(stack) >>> w.free()
Notes
Idempotent: calling
freetwice is a no-op.After
free,set_imagecannot restore the widget (the observers are still pinned but_dataisNone); construct a newShow3Dif you need to re-display something.
- get_state(key=None, drop_defaults=False)#
Trait state for comm sync and notebook embedding.
ipywidgets calls this with
key=Noneto snapshot the FULL state that gets written into the saved notebook’smetadata.widgets. Whensave_stateis False we drop the heavy frame buffers from that snapshot, so a plain Show3D does not bake a hundreds-of-MB z-stack into the .ipynb. Targeted syncs (keyis a name or set, used by hold_sync / send_state during live streaming) are untouched, so the frontend still receivesframe_bytes/_buffer_bytesnormally.save_state=Trueembeds everything so a reopened notebook restores the interactive widget without a kernel.
Interactive controls#
Control |
Trait |
Expected effect |
|---|---|---|
Slice slider |
|
Canvas shows that depth slice |
Arrow keys |
|
Step one slice per press |
Play / pause |
|
Auto-advances slices at |
Reverse |
|
Playback direction flips |
Boomerang |
|
Ping-pongs at the ends instead of looping |
FPS field |
|
Playback rate changes, capped at 60 fps |
Loop range |
|
Playback confined to the sub-range |
Playback dynamics |
|
More-menu presets can play a time series linearly, bounce at ends, slow down around key frames, or follow a custom frame path without changing the underlying stack |
Colormap dropdown |
|
Shared by default. More → Color shared can unlock per-panel colormaps for advanced comparisons |
Panel identity markers |
|
Optional panel colors plus row, column, or rectangular group frames make panels easy to reference in notebooks, reports, and agent instructions |
Local panel annotations |
|
Optional multiple in-image labels per panel, placed by corner, normalized point, or normalized region box |
Geometric panel overlays |
|
Optional circle, rectangle, and square overlays in data or relative coordinates, with stroke/fill opacity, dashed/dotted line styles, and z-order, rendered while scrubbing and in exported HTML; More -> Overlay Edit allows live select, move, resize, delete, and reset |
Contrast preset dropdown |
|
Applies percentile ranges such as |
Advanced histogram toggle |
|
Exposes detailed histogram controls only when a user asks for them |
Export button |
|
Writes standalone HTML from live widgets; live Show3D can also request GIF/MP4 animation exports |
Page controls (paged galleries) |
|
Shows, stars, or plays through one page of panels at a time |
Panel layout (multi-panel) |
|
Panels arrange; linked scrub moves all |
Gallery gap and borders |
|
Separately controls the layer between Show3D panels, the outside gallery frame, and each panel’s own inner stroke for live display, HTML, and GIF/MP4 exports |
Panel visibility (multi-panel) |
|
Panels collapse from view without deleting data |
Panel reorder (multi-panel) |
|
Reorders panel display without changing source data, labels, stars, or hidden state |
Viewer chrome preset |
|
Applies shared display presets; see Viewer UI controls |
Control visibility |
|
Permanently remove controls or temporarily collapse them behind the top GUI toggle |
Title visibility |
|
Top title row shows/hides |
Statistics |
|
Optional mean/min/max/std readout |
Panel title visibility |
|
Per-panel labels show/hide, resize, and optionally get title chrome such as background, border, and padding |
Rich panel title spans |
|
Optional structured |
Scale bar visibility |
|
Scale bar shows/hides |
Saved notebook preview frames |
|
Single-panel saved notebooks can reopen as a compact contact sheet of selected frame indices instead of only the current frame |
ROI add / drag |
|
Single-panel stack ROI overlays stay visible while scrubbing; saved notebook previews include all visible ROI overlays and right-side zoom crops; Python can read circle centers/radii and rectangle/square corners in |
FFT toggle |
|
Shows the FFT view for the current frame or visible panel grid |
FFT quality labels |
|
Compact in-panel label reports FFT sharpness, peak count, and peak SNR from the cached FFT magnitude |
FFT window toggle |
|
Apodization on/off before FFT rendering |
Resize / zoom chrome |
|
Resize handles and zoom readouts show/hide; the zoom setting covers every real-space panel and FFT tile/inset |
FFT layout and initial view |
|
Places FFTs below, right, or inside every panel and initializes their shared zoom |
Denoise |
|
The master swaps raw/denoised frames without losing settings; Settings expands the Method/σ/bin editor; an active filter also reshapes FFT |
Filter |
|
View-only low/high/band-pass filtering with a draggable FFT ring; stored frames, statistics, and raw exports remain unchanged |
Sub-pixel alignment |
|
More-menu display alignment for a drifting single-panel stack; the browser estimates row/column shifts against the reference frame and keeps raw data unchanged |
More menu: Flip |
|
Display-only orientation checks for row/column or horizontal/vertical review |
More menu: Rotate |
|
Display-only 0/90/180/270° rotation for the whole stack or the selected frame |
More menu: Compare |
|
Blink, difference, or overlay two frames for point-defect and time-series change detection |
Multi-panel gallery chrome#
Use the same explicit names as Show2D when preparing Show3D panels for slides or publication:
Show3D(
raw_stack,
denoised_stack,
max_cols=2,
inter_panel_gap_px=4,
inter_panel_gap_color="#000000",
gallery_outer_border_px=4,
gallery_outer_border_color="#000000",
panel_inner_border_px=1,
panel_inner_border_color="#000000",
)
inter_panel_gap_px is real space between panel slots. gallery_outer_border_px
is the frame around the whole Show3D gallery. panel_inner_border_px is drawn
inside each panel over the image edge. The older panel_gap argument remains a
compatibility alias for inter_panel_gap_px.
Rich panel labels and math#
Use rich labels when panel titles are scientific variables rather than plain
names. Show3D accepts inline math strings or structured spans in
panel_titles / panel_title_spans:
from quantem.widget import Show3D
Show3D(
raw_stack,
residual_stack,
panel_titles=[
[{"math": r"\lambda=0.03"}, {"text": " raw"}],
[{"math": r"\chi^2"}, {"text": "/pixel residual"}],
],
show_stats=True,
)
Plain strings with $...$ also work:
Show3D(
raw_stack,
residual_stack,
panel_titles=[r"$\lambda=0.03$ raw", r"$\chi^2$/pixel residual"],
)
The frontend renders common Greek symbols plus simple superscripts/subscripts
without MathJax or KaTeX. It also normalizes doubled backslashes from JSON/state
files, so \\lambda displays as λ. Panel titles, the Panels menu, stats
rows, animation labels, saved notebook state, and exported HTML all use the
same rich rendering path.
Use panel_annotations when a label belongs to a local feature or region
inside a panel. Each annotation can use text, math, or spans:
Show3D(
raw_stack,
residual_stack,
panel_titles=[
[{"math": r"\lambda=0.03"}, {"text": " raw"}],
[{"math": r"\chi^2"}, {"text": "/pixel"}],
],
panel_annotations=[
{"panel": 0, "math": r"\lambda", "position": "top-left", "variant": "pill"},
{
"panel": 1,
"spans": [{"math": r"\chi^2"}, {"text": " high", "color": "#f87171"}],
"box": [0.56, 0.48, 0.32, 0.16],
"variant": "callout",
},
],
)
Circle and rectangle overlays#
Show3D accepts the same panel_overlays API as Show2D. Use it for
reproducible geometry that should stay fixed while a stack scrubs or plays.
Coordinates are data pixels by default and follow (row, col) ordering.
from quantem.widget import Show3D
Show3D(
raw_stack,
denoised_stack,
residual_stack,
panel_titles=["raw", "denoised", "residual"],
panel_overlays={
"raw": {
"shape": "circle",
"center": (96, 88),
"radius": 14,
"stroke": "#60a5fa",
"stroke_width": 3,
"line_style": "dashed",
},
"denoised": [
{
"shape": "rect",
"box": (48, 58, 126, 146),
"stroke": "#facc15",
"fill": "#facc15",
"fill_opacity": 0.12,
"line_style": "dotted",
},
{
"shape": "square",
"center": (96, 88),
"size": 42,
"stroke": "#34d399",
"dash": [8, 3, 2, 3],
"z_order": 1,
},
],
},
)
Pass overlays=[...] to broadcast the same geometry to every panel, or add
panel="raw" / panel=0 to each flat-list entry when building the overlay
list programmatically. Set coords="relative" for normalized 0-1 geometry
that follows panels with different pixel shapes.
Overlay strokes are solid by default. Set line_style="dashed",
line_style="dotted", or line_style="dashdot", or pass a custom
dash=[on, off, ...] pattern.
When overlays are present, the live widget and exported HTML show
More -> Overlay Edit. Turn it on to select an overlay, drag inside the shape
to move it, drag an edge to resize it, press Delete to remove the selected
overlay, or choose Reset Overlays to restore the constructor state. Use ROIs
instead when the geometry should drive statistics, FFT crops, or Python
readback through get_roi_geometries().
Presentation and export chrome#
ui_mode="presentation" starts Show3D with controls collapsed while preserving
the top Controls button. The Export button also remains visible in that
collapsed title chrome:
w = Show3D(
raw_stack,
residual_stack,
panel_titles=[r"$\lambda=0.03$ raw", r"$\chi^2$/pixel residual"],
ui_mode="presentation",
)
w.export_html("lambda_movie.html", encoding="uint8")
In a live Python-backed widget, the Export menu can write HTML and request GIF/MP4 animation exports through the Python backend. In standalone HTML, the page can download itself as HTML; GIF/MP4 entries remain visible but disabled with a backend-required explanation until browser-side animation encoding is implemented.
The denoise family matches Show2D. See
Which denoise filter should I use?
for the recommendation ladder (sparse EDS, very sparse, decent-dose HAADF,
quantitative). An active denoise reshapes the FFT magnitude too, so set
denoise="none" for quantitative FFT work.
Frequency Filter follows the same scientist-facing contract as Show2D while
remaining a separate control from Denoise. For example, a reconstruction stack
with a slow background can start as Show3D(stack, show_fft=True, frequency_filter="highpass", frequency_filter_cutoff=0.08). A lattice stack
can use "bandpass" with frequency_filter_center and
frequency_filter_width. Values are fractions of Nyquist from 0 to 1, and the
cyan FFT ring can be dragged during scrubbing or playback. The browser applies
Denoise first and Filter second; the raw stack and quantitative exports are
never replaced by the view. Filter lives under More and is off by default. Its
FFT overlay dims rejected frequencies and labels the clear region as Inside
kept, Outside kept, or Band kept.
Sub-pixel alignment for drifting stacks#
For a single-panel stack that is already available in the browser, use
subpixel_align=True or open More → Sub-pixel align. The first version
aligns the displayed frames to subpixel_align_reference (default frame 0) with
phase-correlation registration and bilinear display shifts. It is intentionally
view-only: the original stack, raw exports, and quantitative data remain
unchanged.
The More menu reports the reference frame, the approximate row/column padding implied by the shifts, and whether WebGPU or CPU FFT was used. Multi-panel, RGB, or streamed-only stacks currently show an explanatory status instead of silently aligning the wrong data.
Playback dynamics for time-series review#
Show3D playback is not only a convenience for movies. For experimental
time-series data, a scientist often wants to probe temporal behavior: a defect
appears slowly, a reconstruction accelerates, a relaxation comes back, or only
a short sub-range matters. The lightweight API already exposes the core pieces:
fps, loop, boomerang, loop_start, loop_end, and playback_path.
The UI path is a compact More → Playback Dynamics section, not another crowded toolbar row. The presets write those existing traits, so the same behavior remains scriptable and serializable:
Preset |
Intended user behavior |
|---|---|
Linear |
Step forward through the loop range at constant |
Slow |
Lower |
Bounce |
Ping-pong with |
Focus range |
Set |
Hold key frames |
Populate |
Custom path |
Let a notebook or agent provide exact frame indices for non-uniform timing |
The stored frame stack is never resampled or duplicated. These controls only change the order and cadence of frame display. Exported HTML and saved widget state should preserve the selected playback dynamics, while a reopened notebook should not unexpectedly start playing unless the user explicitly requests that behavior.
FFT quality labels#
Pass show_fft=True to show the FFT view. By default, fft_metrics=True
adds a small white label inside the FFT panel with sharpness, peak count, and
peak SNR. In multi-panel FFT views, Show3D summarizes the visible FFT tiles.
The metrics reuse the cached FFT magnitude used for rendering, so frame
playback, zoom, and pan do not trigger an extra FFT for the label. Set
fft_metrics=False for a clean FFT image.
The first FFT for a frame or ROI may take a moment on large data. After that, Show3D reuses the cached FFT magnitude when you return to the same frame and when you redraw, zoom, pan, scrub, or show metric labels.
Every visible FFT tile or overlay inset shows the shared live magnification as
an N.N× badge, even for uncalibrated arrays. Wheel or pinch zoom updates it;
double-click, double-tap, or Reset returns to 1.0×. Pass
fft_overlay_zoom=2.0 to initialize any FFT layout at 2.0×, and set
show_zoom_indicator=False to hide both real-space and FFT zoom badges.
Reuse ROI coordinates across a stack#
Single-panel Show3D uses the same ROI state contract as Show2D. ROIs are synced
in roi_list, saved by state_dict(), and exposed through
get_roi_geometries() for analysis code or agents:
w = Show3D(stack).set_roi(row=72, col=65, radius=12)
roi = w.get_roi_geometries()[0]
roi["center"] # {"row": 72.0, "col": 65.0}
roi["radius"] # 12.0
Rectangle and square ROIs include clockwise corners; annular ROIs include
radius_inner and radius_outer. Saved notebook previews of a single-panel
Show3D frame show all visible ROI overlays and one zoom crop per visible ROI,
so a report reader can compare the marked sites without rerunning the notebook.
Save a contact sheet of selected frames#
By default, a lightweight saved notebook preview shows the current Show3D frame. For single-panel stacks, pass explicit zero-based frame indices when a cold-reopen report should show several representative frames:
w = Show3D(
stack,
notebook_preview_frames=[0, 12, 25, 80],
notebook_preview_ncols=3,
)
A notebook can also decide this interactively before saving:
w.set_notebook_preview_frames([0, w.slice_idx, stack.shape[0] - 1], ncols=3)
Multi-panel Show3D intentionally keeps the saved preview to the current frame of each visible panel; combining many movie panels with many saved frames is better handled as an explicit report or animation export.
Live stack updates#
Use set_image() to replace the stack in an already displayed widget while a
notebook kernel is still running. Keep a reference to the widget, display it
once, then call set_image(...) whenever new stack data should be rendered.
For live acquisitions or reconstruction loops, construct the widget with
offline=False so frames travel over the live Jupyter Comm channel instead of
the saved/offline notebook-data path:
import numpy as np
from quantem.widget import Show3D
frames = [first_frame]
w = Show3D(first_frame[None], labels=["frame 1"], offline=False)
w
for next_frame in acquisition:
frames.append(next_frame)
w.set_image(
np.stack(frames),
labels=[f"frame {i + 1}" for i in range(len(frames))],
)
w.slice_idx = len(frames) - 1
In a real JupyterLab browser session this updates the displayed frame as each
set_image() call is processed. A background thread is optional for UI
ergonomics, but is not required for the widget update itself.
set_image() is the re-render trigger. Mutating the original NumPy array in
place does not notify the frontend. The method writes a fresh current-frame
transfer, bumps frame_seq, invalidates playback buffers, clamps the current
slice and loop range, and resets stale panel-specific state when replacing a
multi-panel view with a single stack.
Important
Do not use the default tiny-stack constructor path for acquisition-style live
updates. Small stacks may auto-enable the offline notebook representation, which
is intended for saved notebooks and static exports. Pass offline=False when the
stack will grow over time.
Watch a growing frame folder#
Use Show3D.from_folder(...) when each matching image file is the next frame
in one time series, focal series, or reconstruction history. New files append
to the single displayed stack; they do not create additional gallery panels or
replace frames that are already loaded.
from quantem.widget import Show3D
w = Show3D.from_folder(
"/data/session/reconstruction",
pattern="frame_*.tif",
watch_interval=2.0,
title="Live reconstruction",
)
w
watch=True is the default. The first folder scan establishes deterministic
frame order, then the watcher appends newly readable files without rebuilding
the widget or rereading unchanged source files. Use Show2D instead when each
file should be a separate comparison panel.
Folder size never creates Show3D pages. Whether the folder has 2, 20, or 200
files, each file extends the frame axis of one stack and the frame
slider/playback controls remain its navigation. page_size= and page_labels=
are therefore rejected by Show3D.from_folder(...); use
Show2D.from_folder(..., page_size=20) for independent paged images. Explicit
5-D or list-of-page Show3D comparison data remain a separate constructor mode.
An empty watched folder stays
mounted and changes into the real stack in the same widget model after the
first stable frame. The compact title-area badge reports Watching,
Updating, Waiting for file completion, Watch error, or Stopped; fixed
watch=False snapshots do not show it.
new_frames = w.poll_folder() # scan now; return newly appended indices
w.stop_folder_watch() # pause background scans
w.watch_folder(interval=1.0) # resume with a different interval
w.close() # stop watching and close the widget
Folder watching is append-only. Files already represented in the stack are not
duplicated, incomplete files wait for a later poll, and source removals or
rewrites do not alter existing frames silently. An incompatible shape is
reported without blocking a later compatible frame. Pass watch=False when a
fixed folder must remain fixed.
Show3D.from_folder(...) reads full-resolution source frames. ShowFolder
uses cached thumbnails to browse and select a session quickly; those thumbnails
are not the data used by the folder-backed Show3D stack.
Maintainer real-time signoff follows S3D-17: append genuine EMD frames after the stack is visibly mounted and verify the same browser canvas, playback state, and frame controls update.
Panel visibility#
Use panel visibility when a secondary panel is useful for validation but should not take space in the first view. For example, an SSB reconstruction can keep the mean diffraction pattern in the widget while hiding it from the canvas:
w = Show3D(
ssb_stack,
mean_dp_stack,
panel_titles=["SSB reconstruction", "Mean DP"],
hidden_panels=["Mean DP"],
)
Panel references can be zero-based indices or exact panel titles:
w.hide_panel("Mean DP")
w.hide_panel(1)
w.show_panel("Mean DP")
w.show_all_panels()
Hidden panels stay in the widget state and standalone HTML export. They are not
removed from the data, and readers can restore them from the Panels menu.
Use panel reordering when the comparison order should change without copying or rebuilding the source stacks:
w.set_panel_order(["Probe", "SSB reconstruction", "Mean DP"])
w.move_panel("SSB reconstruction", 0)
w.reset_panel_order()
Panel order is saved in widget state and standalone HTML. It is display-only: hidden panels, stars, titles, and per-panel contrast remain keyed by the original source panel index.
Use panel_groups when several panels should be read as one comparison block.
The group box follows the displayed panel positions, so it still works after
panel hiding or reordering:
w = Show3D(
bf_raw,
df_raw,
bf_denoised,
df_denoised,
panel_titles=["BF raw", "DF raw", "BF denoised", "DF denoised"],
max_cols=2,
panel_groups=[
{"panels": [0, 1], "color": "#2563eb", "label": "raw"},
{"start": 2, "end": 3, "color": "#16a34a", "label": "denoised"},
],
)
The statistics readout is off by default. Turn on show_stats=True in Python,
or use the Stats switch in the widget, when mean/min/max/std values are useful.
Paged galleries#
Use pages when each view is itself a small multi-panel movie. This is useful for reconstruction sweeps, denoising parameters, or iteration checkpoints where each page should show the same panel layout while the user scrubs pages:
# Shape: pages, panels_per_page, frames, rows, cols
w = Show3D(
stacks_5d,
panel_titles=["raw", "filtered", "residual", "probe"],
page_labels=["lambda 0.01", "lambda 0.03", "lambda 0.10"],
)
You can also pass explicit page dictionaries:
w = Show3D([
{"title": "iteration 10", "stacks": [raw_10, filtered_10, residual_10]},
{"title": "iteration 20", "stacks": [raw_20, filtered_20, residual_20]},
])
Paged Show3D keeps the data in the normal multi-panel transport internally, so
HTML export, notebook state, panel hiding, panel stars, playback, FFT, and GIF/MP4
export use the same paths as ordinary Show3D. In page mode, visible_panels
returns only panels from the active page, and to_show2d() converts the current
visible page into a Show2D gallery. The page row has its own play/pause button
and FPS menu; the lower frame playback controls still scrub time or depth
inside the active page. Manual page scrubbing pauses page playback, and Show3D
keeps rendering work scoped to the active visible page.
Paged views use independent automatic percentile clipping by default because
separate reconstructions often have different numerical ranges. This preserves
structure on every page instead of letting one high-amplitude reconstruction
set the contrast for all others. Pass link_contrast=True when identical color
limits are scientifically required for direct amplitude comparison. Ordinary
non-paged multi-panel views keep linked contrast by default.
w.page_idx = 2
w.star_page(2)
show2d_page = w.to_show2d(frame=w.slice_idx)
Compare depth profiles across pages#
When every page contains one panel, the line-profile kymograph follows the active page. Draw the profile once, enable Kymograph, and scrub the page row to compare the same spatial line through raw/corrected volumes, reconstruction methods, or experimental conditions. The kymograph title includes the active page label; its horizontal line coordinate and depth/time axis stay matched.
# Shape: pages, 1 panel per page, depth, rows, cols
comparison = np.stack([single_slice, multislice], axis=0)[:, None]
w = Show3D(
comparison,
page_labels=["single-slice", "multislice"],
panel_titles=["object phase"],
dim_label="Depth",
)
w.set_profile((row0, col0), (row1, col1))
# Numerical form: page, depth, distance along the line
matched_profiles = w.profile_all_pages()
For pages containing several panels, pass panel_slot= to
profile_all_pages() or panel= to profile_all_frames() when extracting a
specific numerical profile. The interactive kymograph intentionally remains a
single-panel-page tool so its line and depth axes are unambiguous.
Full-resolution folder export (advanced)#
Use the folder export when a microscopist needs to scrub a large stack at the
native detector/reconstruction size instead of opening a reduced one-file HTML.
This writes a small index.html beside an offline_stack.u8 data file and a
manifest.json:
from quantem.widget import Show3D
w = Show3D(stack, title="800C 1.3Mx full-resolution review", debug=True)
w.export_sidecar("/data/reports/800C_1.3Mx_fullres")
Serve the folder over Range-capable local HTTP; do not open the HTML with
file:// because the browser must fetch the data file:
# Use your project or lab helper that supports HTTP Range requests.
python scripts/serve_sidecar_range.py \
--dir /data/reports/800C_1.3Mx_fullres \
--port 8803 --bind 127.0.0.1
Then open http://127.0.0.1:8803/index.html. The viewer shell should appear
immediately. The browser then loads the full stack into memory, shows the load
status banner, and swaps to the cached playback path when the stack is ready.
Changing Color or the histogram range repaints the current microscope view
immediately, marks the playback cache as updating, and rebuilds the remaining
frames in the background. Scrubbing during that rebuild should still advance
the current frame; once the banner clears, playback uses the cached full-stack
path again.
This workflow preserves the source spatial shape used to construct the widget. If you intentionally want a smaller browse artifact, make that explicit with a separate downsampled/single-file export rather than treating it as the full-resolution review copy. For the end-to-end browser checklist and example timing report, see the advanced tutorial.
When sharing this export, send or copy the whole folder. index.html is not a
standalone result for this mode; it needs offline_stack.u8 and manifest.json
beside it. The colleague should serve the received folder with the same Range
helper and open the local URL.
Animation exports#
Use HTML when collaborators should keep scrubbing, zooming, and changing contrast. Use GIF when the result needs to drop into PowerPoint, email, or a static report. MP4 is available when a video file is specifically required:
w.save_gif("movie.gif", quality="medium", fps=6)
w.save_mp4("movie.mp4", quality="high", fps=12)
For array-first workflows that do not need to construct a widget, use
quantem.widget.movie.save_gif(...) or quantem.widget.movie.save_mp4(...).
quality="low", "medium", and "high" control the exported spatial
resolution. GIF is always palette-limited, so medium is usually the practical
slide/email choice; high is sharper but larger. Pass show_frame_labels=True
when panel titles should include the same live-style frame label and count that
the widget canvas shows. GIF/MP4 exports keep the panel labels, scale bar, and
zoom readout styling consistent with the static/offline widget image output.
The widget Export menu keeps the common path explicit: choose GIF,
Interactive HTML, or secondary MP4 video, then set frame range,
maximum frame count, fps, spatial size, and quality before exporting. The size
shown in that menu is estimated render size before compression, so the final
GIF/MP4 file can vary with image texture and palette/video compression.
w.save_gif("movie_slides.gif", quality="medium", fps=8, slides_preset=True)
The GIF/MP4 path exports the full panel frames. Browser-only zoom and pan gestures are view state, so use HTML export when collaborators need to continue zooming, panning, or changing contrast interactively.
Use the Python API for frame labels, background color, bounce playback, and other
presentation-specific choices. frame_start and frame_stop follow normal
zero-based Python slice bounds, while max_frames evenly samples the selected
range:
w.save_gif(
"movie.gif",
quality="medium",
fps=6,
frame_start=0,
frame_stop=48,
every_n=2,
max_frames=20,
max_edge_px=768,
playback="bounce",
show_frame_labels=True,
background="black",
)
Note
export_html(quantized=True) writes the smaller single-file uint8 pack; the
default writes exact float32 into one HTML file. For multi-GB Show3D reviews,
use the folder export above instead of forcing one huge HTML file. See the
widget export tutorial.