spacr.qt.widgets.motility_preview¶
Motility live preview — tracks, velocity, straightness, infection split.
Point it at a plate folder holding merged/*.npy (or at the merged
folder itself) and it rebuilds, for a handful of frames, exactly what
spacr.timelapse.automated_motility_assay() computes: per-track
velocity and straightness from the cell-mask centroids, split by infection
state read off the pathogen mask.
Two things this preview refuses to fudge.
Units. The assay converts pixels per frame into physical units with
factor = (1 / pixels_per_um) * (60 / seconds_per_frame) and only when
both are known; otherwise it reports px/frame. A preview that printed
“velocity 4.2” while the user was thinking in µm/s would be worse than
printing nothing, so the calibration fields start unset, every velocity
carries its unit, and while the calibration is unknown the panel says so in
words instead of quietly borrowing the 1.78 px/µm default.
Short tracks. Mean step length and straightness are wildly unstable on a three-point track — straightness is exactly 1.0 for any two-point track, no matter what the cell did. So the track-length distribution is drawn beside the velocity plot with the cutoff marked, and the minimum length is a live setting: it is the knob that actually decides whether the numbers mean anything.
The expensive half (reading merged arrays and extracting centroids) runs once in a worker thread and is cached as a point table. Every metric setting — minimum length, max displacement, straightness threshold, and both calibration fields — recomputes from that cached table on the GUI thread, instantly.
Exceptions¶
The chosen folder is not a usable motility input, with the reason. |
Classes¶
Pixel size and frame interval, either of which may be unknown. |
|
Interactive motility preview — Motility Assay module. |
|
One read of merged arrays into a point table. |
|
Everything the panel pins under the plots. |
Functions¶
Build the |
|
|
Extract one row per object per frame from a group of merged arrays. |
|
Guess (tracked mask plane, pathogen mask plane) from the plane count. |
|
Group |
|
Planes held by one merged array. No Qt: worker-safe. |
|
Render the three panels the user tunes against, as an RGB array. |
|
Return the |
|
Build the cached point table for one (plate, well, field) group. |
|
Resolve a plate's |
|
Apply the assay's centroid QC: fix teleports, drop impossible tracks. |
|
Reduce a per-track table to the numbers shown under the plots. |
|
Per-track velocity and straightness, using the assay's own formulae. |
Module Contents¶
- exception spacr.qt.widgets.motility_preview.MotilityInputError[source]¶
Bases:
ValueErrorThe chosen folder is not a usable motility input, with the reason.
Initialize self. See help(type(self)) for accurate signature.
- class spacr.qt.widgets.motility_preview.Calibration[source]¶
Pixel size and frame interval, either of which may be unknown.
- Variables:
pixels_per_um – image scale in px/µm, or
Nonewhen unset.seconds_per_frame – frame interval in seconds, or
None.
- class spacr.qt.widgets.motility_preview.MotilityPreviewPanel(parent=None, *, threaded: bool = True)[source]¶
Bases:
spacr.qt.widgets.preview_contract.LivePreviewContract,PySide6.QtWidgets.QWidgetInteractive motility preview — Motility Assay module.
Same contract as
LivePreviewPanel, and the shared half is the same code: standaloneQWidget,QThreadworker emitting over signals,LivePreviewContractfor the run/cancel/status protocol,set_propagate_callback()to push tuned values back into the main settings panel, and abuild_*_cardfactory.- Parameters:
parent – parent widget.
threaded – whether the panel’s jobs run off the GUI thread. False runs each one inline, emitting the same signals in the same order, so a test can drive the panel synchronously without the behaviour diverging.
Build the motility preview panel.
Two job runners, and the separation is load-bearing twice: the pending work on the main one is what reports “a plate is still being scanned”, and a plane-count read is not a plate scan; and the second is marked not user-visible so the Home run banner does not announce a read the user never started.
- Parameters:
parent – parent widget, or
None.threaded – run scans and reads on worker threads. Set
Falsein tests: each job then runs inline, emitting the same signals in the same order, so the panel can be driven synchronously without the behaviour diverging.
- apply_settings(settings: dict) None[source]¶
Seed the preview from the main Motility settings dict.
- Parameters:
settings – the Motility settings dict.
tracked_object,max_displacement,straightness_threshold,drop_straight_tracks,channels,pixels_per_umandseconds_per_frameare read when present; errors are logged, not raised.
- calibration() Calibration[source]¶
The current calibration, with unset fields as
None.
- closeEvent(event)[source]¶
Let a running read finish before the widget is torn down.
A
QThreadcollected while running aborts the process; the worker outlives the emit that produced its result by a few instructions.- Parameters:
event – the close event, passed on to the base class after any running worker has been waited for (up to five seconds each).
- dragEnterEvent(event)[source]¶
Accept a drag carrying a tracked timelapse to measure motility from.
- Parameters:
event – the Qt drag event.
- dragMoveEvent(event)[source]¶
Keep accepting while a tracked timelapse to measure motility from stays over the panel.
- Parameters:
event – the Qt drag event.
- dropEvent(event)[source]¶
Take the dropped input and preview it.
- Parameters:
event – the Qt drop event.
- load_folder(path) bool[source]¶
Synchronously open a plate (or
merged) folder.For programmatic callers and tests, mirroring
LivePreviewPanel.load_image. The GUI usesload_folder_async().- Parameters:
path – a plate or
mergedfolder, scanned withscan_plate_payload(); a failure or a plate with no time series is shown in the status line.
- load_folder_async(path) bool[source]¶
Scan a plate on a worker, then install it on the GUI thread.
Both GUI entry points – the drop handler and the Choose-plate dialog – come through here.
- Parameters:
path – a plate or
mergedfolder;Noneor an empty path submits nothing and returnsFalse.- Returns:
Truewhen a job was submitted.
- run_preview() None[source]¶
Read the merged arrays into the cached point table, then score.
The guard, the refusals and the busy state are the shared ones — see
LivePreviewContract.
- set_propagate_callback(cb) None[source]¶
Register a
callback(dict)used to push tuned settings back.- Parameters:
cb – callable taking one settings dict, or
Noneto push nothing.
- class spacr.qt.widgets.motility_preview.MotilityRequest[source]¶
One read of merged arrays into a point table.
- class spacr.qt.widgets.motility_preview.MotilitySummary[source]¶
Everything the panel pins under the plots.
- spacr.qt.widgets.motility_preview.build_motility_preview_card(host)[source]¶
Build the
Motility previewcard + panel pair.Mirrors
spacr.qt.screens.hyperparam.build_hyperparam_card: returns the pair without adding it to any layout.- Parameters:
host – the
AppScreenasking for the card.- Returns:
(panel, card).
- spacr.qt.widgets.motility_preview.build_point_table(merged_dir: str, metas: List[dict], n_channels: int, tracked_plane: int, pathogen_plane: int | None, max_frames: int = 12)[source]¶
Extract one row per object per frame from a group of merged arrays.
Loads each
.npymemory-mapped and touches only the two planes it needs, so a 10-plane 2048² merged array costs two planes of I/O, not ten. Objects keep their mask label ascellID— merged arrays written by the Timelapse module are already relabelled by track id, which is the same assumption the assay makes.- Parameters:
merged_dir – folder holding the merged
.npyarrays.metas – parsed file names of one (plate, well, field) group in time order; each supplies
filename,plateID,wellIDandfieldID, and its position becomesframe.n_channels – number of intensity channels, used to orient each array.
tracked_plane – index of the label plane whose objects are tracked.
pathogen_plane – index of the pathogen mask plane; an object overlapping it is
infected.Nonemarks every object uninfected.
- Returns:
DataFrame with
plateID,wellID,fieldID,cellID,frame,x,y,areaandinfected.
- spacr.qt.widgets.motility_preview.default_plane_layout(n_planes: int, n_channels: int) Tuple[int, int | None][source]¶
Guess (tracked mask plane, pathogen mask plane) from the plane count.
Mirrors the layout
spacr.timelapse._load_masks_from_merged()documents: intensity channels first, then the cell mask, then optionally a nucleus mask, then optionally the pathogen mask.- Parameters:
n_planes – number of planes in a merged array.
n_channels – number of intensity channels stored before the mask planes; values below 1 count as 1.
- Returns:
(cell_plane, pathogen_plane_or_None).
- spacr.qt.widgets.motility_preview.group_merged_files(merged_dir: str) Dict[tuple, List[dict]][source]¶
Group
merged/*.npyby (plate, well, field) and sort each by time.Shares the lightweight parser behind
spacr.timelapse._parse_merged_filename(), so grouping agrees with the assay without importing plotting or model dependencies.- Parameters:
merged_dir – folder of merged
.npyarrays; only groups with at least two time points are returned.
- spacr.qt.widgets.motility_preview.read_plane_count(merged_dir: str, filename: str) int[source]¶
Planes held by one merged array. No Qt: worker-safe.
np.load(..., mmap_mode="r")sounds free and is not. Before it maps anything it OPENS the file and reads the.npyheader, so it is a filesystem round trip on a path the user supplied – and an open is no cheaper than the stat that started this exercise. Measured on one workstation, one stat under/nas_mnt– anautofsmount whose share was asleep – had not returned after twenty seconds.- Parameters:
merged_dir – the
mergedfolder holding the array.filename – the array’s basename inside it.
- Returns:
the plane count, or
0when the array cannot be read. Zero is the same soft failure the two inlineexceptbranches used to give: the spinners keep the values they had and the channel dropdown empties, rather than the panel raising at the user.
- spacr.qt.widgets.motility_preview.render_motility_figure(points, tracks, calibration: Calibration, min_length: int, straightness_threshold: float, width_px: int = 1180, height_px: int = 380) numpy.ndarray[source]¶
Render the three panels the user tunes against, as an RGB array.
Tracks, drawn from the origin so paths are comparable, coloured by infection state.
Track-length distribution, with the minimum-length cutoff drawn on it — this is the plot that says whether the velocity numbers can be trusted.
Velocity and straightness, split by infection state, with the unit in the axis label so
px/frameis never mistaken for µm/s.
- Parameters:
points – point table as returned by
build_point_table(), one row per object per frame; drawn as tracks from the origin.tracks – per-track table as returned by
track_metrics(), for the length histogram and the velocity panel.calibration – supplies the velocity axis unit; an uncalibrated one is labelled px/frame on the plot.
min_length – minimum track length in frames, drawn as the cutoff on the length histogram.
straightness_threshold – straightness threshold of the current settings; not drawn.
- spacr.qt.widgets.motility_preview.resolve_merged_dir(path) str[source]¶
Return the
mergeddirectory forpath.Accepts the plate folder (which holds
merged/) or themergedfolder itself, which is what a user dragging a folder in will most likely grab.- Parameters:
path – a plate folder holding
merged/, themergedfolder itself, or a file inside either (its parent folder is used).- Raises:
MotilityInputError – when neither exists or it holds no
.npy.
- spacr.qt.widgets.motility_preview.run_motility_pass(req: MotilityRequest)[source]¶
Build the cached point table for one (plate, well, field) group.
- Parameters:
req – the merged folder, group file names, channel count, plane indices and frame cap passed to
build_point_table().
- spacr.qt.widgets.motility_preview.scan_plate_payload(path) Dict[str, Any][source]¶
Resolve a plate’s
mergedfolder and group it. No Qt: worker-safe.The expensive half of opening a plate:
resolve_merged_dirlists the candidate folder andgroup_merged_filesreads every name inmerged/and parses it – thousands of entries on a 384-well plate.- Parameters:
path – a plate folder or its
mergedfolder; any failure is caught and reported in theerrorentry.- Returns:
{path, merged, groups, error}.
- spacr.qt.widgets.motility_preview.smooth_and_filter_tracks(points, max_displacement: float)[source]¶
Apply the assay’s centroid QC: fix teleports, drop impossible tracks.
A single frame whose steps in and out both exceed
max_displacementwhile its neighbours are within it is a segmentation glitch, and the assay interpolates it. Any remaining step over the limit means the track links two different objects, and the assay drops the whole track. This reproduces both, so the preview’s track count matches a run’s.- Parameters:
points – point table as returned by
build_point_table(), one row per object per frame; tracks are keyed by plate, well, field andcellID.Noneor empty is returned as is.max_displacement – largest plausible step between frames, in pixels.
- Returns:
(points, n_glitches_fixed, n_tracks_dropped).
- spacr.qt.widgets.motility_preview.summarise(tracks, calibration: Calibration, min_length: int, straightness_threshold: float, glitches: int = 0, dropped: int = 0) MotilitySummary[source]¶
Reduce a per-track table to the numbers shown under the plots.
- Parameters:
tracks – per-track table as returned by
track_metrics(); tracks flaggedtoo_shortare left out of the means.calibration – supplies the velocity unit and whether it is calibrated.
min_length – the minimum track length in frames, recorded in the summary.
straightness_threshold – straightness at or above which a used track counts as highly straight.
- spacr.qt.widgets.motility_preview.track_metrics(points, calibration: Calibration, min_length: int = 3)[source]¶
Per-track velocity and straightness, using the assay’s own formulae.
v_px_per_frameis the mean step length;straightnessis net displacement over path length. Velocity isv_px_per_frame * factorand carriesCalibration.unit.Tracks shorter than
min_lengthframes are kept in the table and flaggedtoo_shortrather than silently dropped, so the length distribution plot can show what the cutoff is discarding.- Parameters:
points – point table as returned by
build_point_table(), one row per object per frame.calibration – pixel size and frame interval; its factor converts px/frame to the reported velocity unit.