spacr.qt.widgets.timelapse_movie

Watching a track break, rather than reading that one did.

TrackStats can tell you that a field produced 41 tracks with a median length of 6 frames in a 30-frame series. It cannot tell you why, and why is the only thing that changes what you do next: two cells that touch for three frames and come apart with their identities swapped needs a different setting from one cell that leaves the field and comes back.

So this shows the frames. One field plays as a movie; clicking it opens a filmstrip above, which is the same frames laid out at once and scrollable, because a break is easiest to find by scrubbing and easiest to understand by seeing the frames either side of it together.

Everything reuses the rendering already in spacr.qt.widgets.timelapse_preview – render_frame colours mask outlines by track id and draws the trailing polyline, and track_colour is deterministic. That is what makes the colours mean something: an object that keeps its track id keeps its colour in every frame and in every thumbnail, so an identity swap reads as an object that changes colour partway through the strip.

Classes

FilmStrip

One field's frames, side by side and scrollable.

FovMovie

One field of view: a movie, and a filmstrip that opens above it.

TimelapseMoviePanel

Several fields stacked, with one set of controls over all of them.

Module Contents

class spacr.qt.widgets.timelapse_movie.FilmStrip(parent: PySide6.QtWidgets.QWidget | None = None)[source]

Bases: PySide6.QtWidgets.QScrollArea

One field’s frames, side by side and scrollable.

Horizontal only. A vertical scrollbar here would fight the panel’s own, and there is never more than one row.

Parameters:

parent – parent widget.

Create the horizontal thumbnail strip.

The viewport paints nothing: the strip is scaffolding that positions thumbnails, and an auto-filled one is one more opaque rectangle over the page.

Parameters:

parent – parent widget, or None.

highlight(index: int) → None[source]

Ring the frame the movie is on, so the two views agree.

Parameters:

index – zero-based index of the frame to ring; every other cell loses its ring, and an index outside the strip rings none.

set_frames(frames: Sequence[PySide6.QtGui.QPixmap]) → None[source]

Replace the strip. Cheap to call: the pixmaps are already made.

Parameters:

frames – thumbnail pixmaps, one per frame in order; clicking one emits frame_picked with its index.

class spacr.qt.widgets.timelapse_movie.FovMovie(title: str = '', parent: PySide6.QtWidgets.QWidget | None = None)[source]

Bases: PySide6.QtWidgets.QWidget

One field of view: a movie, and a filmstrip that opens above it.

Renders lazily and caches by (frame, objects, tracks). Flipping a toggle on a 30-frame field would otherwise re-render every frame twice – once for the movie and once for the strip – on the GUI thread.

Parameters:
  • title – the caption above the movie. Empty draws none.

  • parent – parent widget.

Create an empty movie view for one field.

Parameters:
  • title – caption shown above the frames.

  • parent – parent widget, or None.

frame_count() → int[source]

How many frames this field has.

Returns:

the frame count, 0 when nothing is loaded.

pause() → None[source]

Stop the timer and put the button back to Play.

play() → None[source]

Start playing, unless there is nothing to animate.

A SINGLE FRAME IS NOT A MOVIE: starting a timer for it would spin the event loop to redraw the same picture.

set_fps(fps: float) → None[source]

Set the playback rate.

Floored at half a frame per second, because the interval is derived by division and a rate of zero is an infinite one.

Parameters:

fps – the wanted frames per second.

set_overlays(*, objects: bool, tracks: bool) → None[source]

Toggle the mask outlines and the track tails independently.

Parameters:
  • objects – whether mask outlines are drawn.

  • tracks – whether track tails are drawn.

set_sequence(images, labels=None, tracks=None, channel: int = 0) → None[source]

Bind one field’s frames, its per-frame labels and its tracks.

labels is expected to be ALREADY relabelled by track id (see relabel_by_track). That is what makes a colour mean a track rather than a per-frame segmentation index, and it is the caller’s job because the relabelling is what the tracker produced.

Parameters:

images – the field’s frames as an array-like stack, first axis time; each frame may carry channels, of which channel is shown. None clears the movie.

set_strip_open(open_: bool) → None[source]

Show or hide the filmstrip under the movie.

Parameters:

open – True to show it.

show_frame(index: int) → None[source]

Put index on the canvas and keep every control agreeing.

Parameters:

index – zero-based frame index; clamped to the frames loaded.

strip_is_open() → bool[source]

Expanded or not – a state, not a question about the screen.

isHidden() and not isVisible(): a widget inside a parent that has not been shown yet reports isVisible() == False however it was configured, so the expanded state would read as collapsed for every movie built before its screen is on screen – and the click that expanded it would appear to do nothing.

toggle_play() → None[source]

Play if paused, pause if playing.

toggle_strip() → None[source]

Open the filmstrip if closed, close it if open.

class spacr.qt.widgets.timelapse_movie.TimelapseMoviePanel(parent: PySide6.QtWidgets.QWidget | None = None)[source]

Bases: PySide6.QtWidgets.QWidget

Several fields stacked, with one set of controls over all of them.

Stacked rather than tabbed on purpose: comparing two fields is the point of showing more than one, and a tab hides the thing you are comparing against.

Parameters:

parent – parent widget.

Build the panel that shows several fields as synchronised movies.

Parameters:

parent – parent widget, or None.

max_fields() → int[source]

How many fields this panel will stack at once.

A CAP, because each field is its own movie with its own timer, and a plate with hundreds would start hundreds of them.

Returns:

the field cap.

movies() → List[FovMovie][source]

The field movies currently stacked.

A LIST COPY, so a caller cannot restack the panel by mutating it.

Returns:

the movies, in display order.

set_fields(fields: Sequence[dict]) → None[source]

Show one movie per entry, up to the user’s ceiling.

Parameters:

fields – dicts of {"title", "images", "labels", "tracks", "channel"}. Extra entries are dropped rather than queued – the ceiling is about memory, so holding the surplus would defeat it.

set_fps(fps: float) → None[source]

Set the playback rate on every stacked field at once.

Parameters:

fps – the wanted frames per second.

set_max_fields(count: int) → None[source]

Cap how many fields are held at once.

Applied by dropping the surplus immediately rather than at the next preview: a user lowering this has just been told the machine is short of memory, and the setting has to give it back now.

Parameters:

count – how many fields may be held at once; clamped to 1 through MAX_FIELDS_CEILING (8), and fields beyond it are dropped at once.