Show3DSlices#

Linked top and arbitrary-angle side cuts through a 3D volume. See the Show3DSlices tutorial.

Viewer UI#

Show3DSlices supports the shared ui_mode, show_title, show_controls, controls_collapsed, show_stats, show_crosshair, and show_scale_bar names. The saved-state scale-bar trait remains scale_bar_visible for compatibility. See Viewer UI controls.

Reference#

class quantem.widget.show3dslices.Show3DSlices(*args: Any, **kwargs: Any)#

Bases: AnyWidget

Linked top-slice and oblique vertical slice viewer for 3D volumes.

Renders an XY slice plus one arbitrary-angle vertical plane through a (nz, ny, nx) volume in linked canvases with synchronized context, optional FFT, and playback over depth or plane position. Pass a 4D array of shape (panel, nz, ny, nx) to compare several volumes with one panel selector while preserving the same slice, side-plane, contrast, and camera controls. Designed for multislice ptychography reconstructions and other small-to-medium 3D volumes where the operator wants to scrub through depth while marching an angled plane through lateral regions. The raw volume data is sent once over the Jupyter Comm channel and re-sliced in JS for each scrubber update.

Features#

  • Linked XY plus arbitrary-angle vertical slice canvases

  • Comparable volume pages with shared slice geometry and page playback

  • Playback over Z slice, oblique plane position, or both

  • Adjustable oblique endpoints, angle, and perpendicular position bounds

  • Log scale, percentile auto-contrast, manual vmin / vmax

  • Per-plane statistics (opt-in via show_stats)

  • Anisotropic scale bars via pixel_size_axes (e.g. nz << nxy multislice)

  • Depth-axis z_stretch for non-cubic volumes (CSS scaling, zero memory)

  • panel_width_px constructor sizing for compact or wide panel layouts

  • Display-only global slice post-alignment with Auto/manual row/col slopes

  • FFT panel with optional Hann window for the active plane

  • PNG / PDF / TIFF single-slice export with plane and index selection

  • JSON state save/load via state_dict / load_state_dict / save

  • Explicit free to release RAM held by traitlets observers

Config Contract#

The config argument is an explicit QuantEM reconstruction convenience, not a general-purpose transform registry. It accepts either a parsed config.json mapping or a path to that JSON file. Show3DSlices reads only the following keys:

  • reconstruction.slice_thickness_A and reconstruction.obj_sampling_A_per_px for pixel_size when pixel_size is not provided.

  • data.rotation_deg for rotation_deg when rotation_deg is not provided and apply_config_transforms is True.

  • object.cropped_shape for a centered post-rotation crop when post_crop is not provided and apply_config_transforms is True. If that key is absent, it falls back to reconstruction.obj_padding_px and then input.padding.

Explicit constructor arguments always win over config-derived values. To use only config sampling metadata, pass apply_config_transforms=False. To force no config rotation or post-crop while still leaving transforms enabled, pass rotation_deg=0 and/or post_crop=0 explicitly.

The spatial transform order is fixed and intentionally visible: crop -> padding -> rotation_deg -> post_crop. The resulting float32 volume is then sent to the browser. No hidden JS-side rotation, crop, or precision-changing transform is applied from config.

param data:

3D array of shape (nz, ny, nx) or 4D array of shape (panel, nz, ny, nx). Also accepts a quantem Dataset3d (uses .array and .sampling automatically). A 5D array uses shape (page, panel, nz, ny, nx). A 4D stack becomes one-volume pages when page_labels is provided; without page_labels it retains the legacy multi-panel meaning.

type data:

array_like

param data_b:

Back-compatible two-panel shortcut. When provided, data and data_b must be matching 3D volumes and the frontend exposes a panel selector.

type data_b:

array_like, optional

param title:

Title displayed above the viewer.

type title:

str, optional

param panel_titles:

Per-panel labels for 4D input or data_b comparison. Length must match the number of panels. For 5D pages, pass one title per panel slot or one title per flattened panel.

type panel_titles:

sequence of str, optional

param page_labels:

Labels for comparable volumes/pages. Page switching keeps the same top/side slice geometry, zoom, and display controls for direct depth comparison.

type page_labels:

sequence of str, optional

param ui_mode:

Shared viewer UI preset. Explicit show_* keyword arguments override preset values.

type ui_mode:

{“interactive”, “presentation”, “report”, “minimal”}, default “interactive”

param show_title:

Show the top title row.

type show_title:

bool, default True

param cmap:

Colormap name. One of the project’s valid colormaps ("inferno", "viridis", "magma", "gray", "plasma", …).

type cmap:

str, default “plasma”

param sampling:

Voxel sampling for the scale bars. Pass a scalar for isotropic data, or a 3-tuple (pz, py, px) for anisotropic data (e.g. multislice ptycho with z-thickness >> xy-sampling). Pairs with units; nm is converted to Å. Matches the quantem Dataset / Show2D / Show4DSTEM convention.

type sampling:

float or sequence of 3 floats, optional

param units:

Unit(s) for sampling (e.g. "A", "nm"). One value applies to all axes; a sequence sets each axis.

type units:

str or sequence of str, optional

param pixel_size:

Back-compatible scale-bar sampling in Å/pixel. Prefer sampling and units for new code. A 3-tuple is interpreted as (pz, py, px).

type pixel_size:

float or sequence of 3 floats, optional

param config:

Parsed QuantEM reconstruction config.json data, or a path to it. This is only used for the keys listed in “Config Contract” above. It can infer sampling metadata, scan-alignment rotation, and post-rotation FOV crop, but explicit constructor arguments override every inferred value.

type config:

mapping or path-like, optional

param apply_config_transforms:

If True, use config-derived rotation_deg and post_crop only when those arguments are omitted. If False, config may still fill pixel_size but will not rotate or crop the volume.

type apply_config_transforms:

bool, default True

param crop:

Spatial crop applied before padding and rotation. 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 padding:

Spatial padding applied after crop and before rotation. This mirrors Show3D and is useful when rotating a scan-aligned view without clipping real signal at the canvas edge.

type padding:

int or tuple[int, int], default 0

param pad_mode:

Padding fill mode. "median" uses a single stack median so borders are contrast-neutral; "constant" fills with 0.

type pad_mode:

{“median”, “constant”}, default “median”

param rotation_deg:

In-plane row/column rotation in degrees, applied after crop/padding. If omitted and config is provided, uses data.rotation_deg from the config; otherwise defaults to 0. Uses built-in float32 bilinear interpolation with nearest-edge fill, no SciPy dependency, and keeps the output shape fixed.

type rotation_deg:

float, optional

param post_crop:

Spatial crop applied after rotation. If omitted and config is provided, uses object.cropped_shape or reconstruction padding from the config; otherwise defaults to 0.

type post_crop:

int or tuple[int, int] or tuple[int, int, int, int], optional

param show_stats:

Compute per-slice statistics traits on each slice change (widget.stats_mean, stats_min, stats_max, stats_std, each a legacy list of 3 floats). Python-side only; the JS widget does not render a stats bar. Set False to skip 12 reductions per slice scrub on multi-MB volumes when you don’t need the values.

type show_stats:

bool, default False

param show_controls:

Show secondary controls for color, colorbar, smoothing, crosshair, z-stretch, contrast, and playback. The slice toolbar keeps only FFT and Reset Zoom visible.

type show_controls:

bool, default True

param controls_collapsed:

Start with controls hidden while keeping a recoverable Controls button in the frontend.

type controls_collapsed:

bool, default False

param show_crosshair:

Show slice intersection guides across orthogonal panels.

type show_crosshair:

bool, default True

param show_fft:

Toggle FFT panel for the active plane.

type show_fft:

bool, default False

param fft_window:

Apply a 2D Hann window to each displayed slice before FFT. Useful for suppressing edge leakage/streaking in reciprocal-space panels.

type fft_window:

bool, default False

param log_scale:

Use signed log1p for intensity mapping.

type log_scale:

bool, default False

param auto_contrast:

Use percentile-based contrast (2nd-98th). On for ptycho phase data (long-tailed histogram - manual contrast usually crushes the signal).

type auto_contrast:

bool, default True

param vmin:

Manual contrast limits.

type vmin:

float, optional

param vmax:

Manual contrast limits.

type vmax:

float, optional

param slice_alignment:

Display-only global post-alignment mode. "auto" estimates one row/col shift-per-slice slope from adjacent slice registration when no slopes are provided; "manual" applies the provided slopes directly. The raw volume is not modified.

type slice_alignment:

{“off”, “auto”, “manual”}, default “off”

param row_shift_px_per_slice:

Global display shift applied per slice relative to the middle slice, using the project-wide row/col convention. Positive row shifts move deeper slices down; positive col shifts move them right.

type row_shift_px_per_slice:

float, default 0.0

param col_shift_px_per_slice:

Global display shift applied per slice relative to the middle slice, using the project-wide row/col convention. Positive row shifts move deeper slices down; positive col shifts move them right.

type col_shift_px_per_slice:

float, default 0.0

param fps:

Playback speed when scrubbing one axis, capped at 30.

type fps:

float, default 30.0

param play_axis:

Which control to animate (0=slice/Z, 1=oblique plane position, 3=both). Legacy saved states with 2 are mapped to 1.

type play_axis:

int, default 0

param dim_labels:

Labels for data axes 0, 1, 2 in that order. Default [“slice”, “row”, “col”] matches the project-wide detector-plane convention (axis 0 = multislice depth, axis 1 = row, axis 2 = col). Pass any 3-string list to override.

type dim_labels:

list of str, optional

param offline:

Pack volume_bytes as uint8 plus global min/max metadata for static nbconvert HTML reports. Leave False for live notebooks that need exact float32 values in the browser.

type offline:

bool, default False

Example

Instantiate Show3DSlices on a multislice ptycho object, scrub through depth, and export a slice to PNG:

>>> import numpy as np
>>> from quantem.widget import Show3DSlices
>>> volume = np.random.rand(14, 256, 256).astype(np.float32)
>>> w = Show3DSlices(volume, title="multislice object", cmap="viridis",
...                  sampling=(2.0, 0.2, 0.2), units="A", z_stretch=8)
>>> w.play()  
>>> w.save_image("xy_slice7.png", plane="xy", slice_idx=7)  

Notes

  • The raw volume data is sent once over the Comm channel; subsequent slice moves, page changes, and panel switches only ship trait updates, not the data.

  • One volume is active at a time so every page reuses the same top/side cut, camera, and display state. For simultaneous side-by-side 3D volume rendering, use Show3DVolume.

  • Saved states that include dual_mode / show_diff keys are still rejected because those imply a different rendering contract.

  • Call free() before discarding the widget to release RAM pinned by traitlets observers.

state_dict() dict#

Return a JSON-serializable snapshot of every user-tunable trait.

Captures display config (cmap, log scale, contrast, FFT, orthographic, smooth, flip, crosshair), scale-bar settings (pixel_size, pixel_size_axes, z_stretch), playback config (fps, loop, reverse, boomerang, play axis), the three slice positions, and the dimension labels. The raw volume data is NOT included; pair the snapshot with the original data argument on restore.

Returns:

Mapping of trait name -> serializable value. Suitable for json.dump or use with save / load_state_dict.

Return type:

dict

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume, cmap="viridis")  
>>> state = w.state_dict()  
>>> w2 = Show3DSlices(volume)  
>>> w2.load_state_dict(state)  

Notes

  • Schema versioning is handled inside save; the dict returned here is the unversioned inner payload.

collapse_controls() Self#

Collapse controls behind the frontend Controls button.

expand_controls() Self#

Expand frontend controls when show_controls is enabled.

toggle_controls() Self#

Toggle whether frontend controls start collapsed.

set_page(page: int) Self#

Show a zero-based page while preserving the active panel slot.

next_page() Self#

Advance to the next page, clamped at the final page.

previous_page() Self#

Move to the previous page, clamped at page zero.

estimate_slice_alignment(*, panel: int | None = None, apply: bool = True, force: bool = False) dict#

Estimate one global row/col post-alignment slope through the stack.

Adjacent slices are registered after median subtraction, Gaussian high-pass filtering, and Hann windowing. The adjacent shifts are accumulated, then a straight line is fit versus slice index. The fitted slope is the display shift to apply per deeper slice, centered around the middle slice when rendered.

Parameters:
  • panel (int or None, optional) – Panel index to estimate for 4D panel stacks. Defaults to the active panel.

  • apply (bool, default True) – If True, store the fitted slopes in row_shift_px_per_slice and col_shift_px_per_slice and switch slice_alignment to "auto".

  • force (bool, default False) – Recompute even when this panel already has a cached estimate.

Returns:

JSON-serializable diagnostics with fitted shifts, cumulative trajectory, adjacent shifts, fit R² values, and correlation quality.

Return type:

dict

reset_slice_alignment() Self#

Turn off post-alignment and discard cached alignment results.

save(path: str) None#

Write the current widget state to a versioned JSON file.

Wraps state_dict in a small envelope that records the widget type ("Show3DSlices") and a schema version so load_state_dict can refuse states that belong to a different widget. The raw volume data is NOT written; only the display / playback / slice configuration.

Parameters:

path (str) – Destination JSON file path. Parent directories must already exist.

Return type:

None

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume, cmap="viridis", sampling=(2.0, 0.2, 0.2), units="A")  
>>> w.save("slices_state.json")  

Notes

  • To restore, instantiate Show3DSlices with the (possibly different) volume and call w.load_state_dict(json.loads(open(path).read())).

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) Path#

Write a standalone HTML viewer for sharing.

The exact export embeds the current float32 volume bytes and preserves numerical precision. The quantized export writes the existing offline uint8 representation plus global min/max metadata, which makes a much smaller single-file report for visual sharing. Preferred export options are mode="single", encoding="full" or encoding="uint8", and downsample=None. quantized is kept as a compatibility alias for encoding="uint8".

Parameters:
  • path (str or pathlib.Path, optional) – Destination HTML path. Defaults to the current working directory with the widget title, volume shape, and export mode in the name.

  • quantized (bool, default False) – If True, write the uint8 offline pack. If False, write exact float32 bytes.

  • title (str, optional) – Browser page title. Defaults to the widget title or class name.

Returns:

The written HTML file.

Return type:

pathlib.Path

load_state_dict(state: dict) None#

Apply a saved state_dict snapshot to this widget.

Restores display, playback, slice, and labeling configuration from a dict produced by state_dict. States that include the deprecated dual_mode / show_diff keys are rejected with a hint to use Show3DVolume for two-volume comparisons. Unknown keys (typically from a newer widget version or a typo) emit a UserWarning and are dropped. Deprecated keys (title_b, linked_contrast, compact) are silently ignored.

vmin / vmax are cleared first so either bound can be set regardless of the current contrast limits. Forward-compat: a saved state with only the scalar pixel_size (no pixel_size_axes) mirrors the scalar across all three axes so depth scale bars stay in sync with the lateral one.

Parameters:

state (dict) – Mapping previously returned by state_dict (or its on-disk equivalent).

Returns:

Mutates the widget in place.

Return type:

None

Example

>>> import json
>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume)  
>>> w.load_state_dict(json.load(open("slices_state.json")))  

Notes

  • Saved files from a different widget type are caught earlier in the save envelope check, not here.

free() None#

Release RAM held by this widget.

Drops the numpy volume and clears the volume_bytes sync trait, then triggers a gc.collect(). del widget alone does NOT free memory: traitlets installs strong observer references that pin the widget’s refcount until free is called.

Returns:

Mutates the widget in place. After this call, the volume is gone and rendering will be blank; rebuild a new widget for further use.

Return type:

None

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume)  
>>> w.free()  

Notes

  • Idempotent: calling free twice is a no-op.

summary() None#

Print a one-screen status report for the current widget.

Sections include: title and volume shape (with pixel-size readout when set), current slice positions per axis with the user’s dim_labels, raw data min/max/mean, and display config (cmap, contrast, log/linear, FFT, Hann window). Useful for notebook reproducibility and bug reports.

Returns:

Prints to stdout.

Return type:

None

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume, title="multislice")  
>>> w.summary()  
play() Self#

Start slice playback along the current play_axis.

Sets the playing trait to True. The JS animation loop scrubs the control selected by play_axis (0=slice/Z, 1=oblique plane position, 3=both) at fps frames per second.

Returns:

The widget, for chaining (w.play()).

Return type:

Self

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume, fps=10, play_axis=0)  
>>> w.play()  
pause() Self#

Pause playback at the current slice indices.

Sets playing to False without resetting slice_z / slice_y / slice_x. Per-slice statistics (if show_stats is on) are refreshed for the current slice on pause.

Returns:

The widget, for chaining.

Return type:

Self

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume)  
>>> w.play().pause()  
stop() Self#

Stop playback and recenter all three slice indices.

Sets playing to False and resets slice_z / slice_y / slice_x to the geometric center of the volume (nz // 2 etc.). Use pause to keep the current slice indices.

Returns:

The widget, for chaining.

Return type:

Self

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume)  
>>> w.play().stop()  
save_image(path: str | Path, *, plane: str | None = None, slice_idx: int | None = None, format: str | None = None, dpi: int = 150) Path#

Save a single 2D slice of the volume as PNG, PDF, or TIFF.

Extracts the requested axis-aligned slice, colorizes it with the current cmap and contrast (vmin / vmax or 2-98 percentile auto-contrast), and writes it to path. log_scale and flip are honored so the saved file matches what the browser shows for that plane.

Plane / index mapping for this legacy export helper:

  • "xy": slice along Z, slice_idx indexes into nz; defaults to current slice_z.

  • "xz": slice along Y, slice_idx indexes into ny; defaults to current slice_y.

  • "yz": slice along X, slice_idx indexes into nx; defaults to current slice_x.

Parameters:
  • path (str or pathlib.Path) – Output file path. Parent directories are created if needed.

  • plane (str | None, optional) – One of "xy", "xz", "yz". Defaults to "xy". The interactive browser UI now exposes Top plus an arbitrary Side plane; this helper keeps the older axis-aligned export names for backwards compatibility.

  • slice_idx (int | None, optional) – Slice index along the chosen axis. Defaults to the current position for that plane.

  • format (str | None, optional) – One of "png", "pdf", "tiff". If omitted, inferred from the file extension; defaults to "png" if no extension. Mismatched extension and explicit format is allowed: the container written follows format.

  • dpi (int, default 150) – DPI metadata written into the file.

Returns:

The written file path.

Return type:

pathlib.Path

Example

>>> from quantem.widget import Show3DSlices
>>> w = Show3DSlices(volume, cmap="viridis")  
>>> w.save_image("xy_top.png", plane="xy", slice_idx=0)  
>>> w.save_image("xz_mid.pdf", plane="xz")  

Notes

  • PDF output is converted to RGB internally (no alpha channel).

  • Slice indices outside the valid range for the chosen plane raise IndexError; unsupported planes or extensions raise ValueError.

Interactive controls#

Control

Trait

Expected effect

Page slider / play

page_idx, n_pages, page_labels

Switches or plays comparable volumes while preserving the same slice geometry and view state

Panel menu

active_panel, panels_per_page, panel_titles

Selects one panel slot within the active page

Crosshair drag

slice_x, slice_y, slice_z

Moves the cut position; all panels update together

Oblique line drag

oblique_profile_line, oblique_angle, slice_x, slice_y

Drag either endpoint to rotate/resize the side cut, or drag the line body to translate the whole cut freely in row/col while staying inside the top slice

Oblique angle

oblique_angle

Rotates the oblique cut plane

Colormap dropdown

cmap

Recolors all panels

Contrast min / max

vmin, vmax

Display clamp changes

Auto-contrast toggle

auto_contrast

Re-fits the percentile range

Log-scale toggle

log_scale

Log intensity mapping

Slice alignment

slice_alignment, row_shift_px_per_slice, col_shift_px_per_slice

Open Advanced and enable Slice alignment. The first enable estimates one global row/col shift per slice automatically; later raw/aligned toggles reuse the cached estimate and aligned display volume. The sliders refine the cached result without modifying the raw volume.

FFT toggle

show_fft, fft_window

Panels show power spectra

Z-stretch

z_stretch

Depth axis scaled for anisotropic voxels

Scale bar toggle

scale_bar_visible

Calibrated bar shows/hides

Export button

export_request, export_status

Writes a standalone HTML viewer (exact / quantized)

Automatic slice alignment high-pass filters adjacent reconstructed slices with a Gaussian sigma capped at 12 pixels, then refines each translation on a 20x local matrix-DFT grid. The 12-pixel scale is an empirical preprocessing setting, not a physical microscope parameter. The subpixel method follows Guizar-Sicairos, Thurman, and Fienup (2008), DOI 10.1364/OL.33.000156. The resulting row/col trajectory is reduced to one global linear shift per slice. This is reversible display post-processing; it does not add sample tilt to the ptychographic forward model or modify the reconstructed volume.

Compare volumes with pages#

Pass a 4D stack plus page_labels when every page contains one volume. The same top slice, side cut, oblique angle, zoom, contrast, and playback settings remain active as the page changes:

# Shape: page, depth, rows, cols
w = Show3DSlices(
    np.stack([raw_volume, aligned_volume]),
    page_labels=["raw", "aligned"],
    panel_titles=["object phase"],
)

Use a 5D array when each page has the same small panel set:

# Shape: page, panel, depth, rows, cols
w = Show3DSlices(
    comparison_5d,
    page_labels=["single-slice", "multislice"],
    panel_titles=["object", "absolute error"],
)

Explicit page dictionaries are also accepted:

w = Show3DSlices([
    {"title": "before", "volume": before},
    {"title": "after", "volume": after},
])

set_page(), next_page(), and previous_page() provide the same workflow from Python. Existing 4D calls without page_labels keep their legacy multi-panel meaning, so saved notebooks do not silently change interpretation.