napari_track_edit.application_menus.copy_from_source_widget

Attributes

COPY_ILLUSTRATION

Classes

ScaledSvgWidget

QSvgWidget that fills the available width and keeps the aspect ratio of the

CopyFromSourceWidget

Widget to copy detections from a source layer into the current tracks.

Module Contents

napari_track_edit.application_menus.copy_from_source_widget.COPY_ILLUSTRATION
class napari_track_edit.application_menus.copy_from_source_widget.ScaledSvgWidget(path: str)

Bases: qtpy.QtSvgWidgets.QSvgWidget

QSvgWidget that fills the available width and keeps the aspect ratio of the drawing, so the illustration adapts to the width of the menu panel.

_ratio
hasHeightForWidth() → bool
heightForWidth(width: int) → int
sizeHint() → qtpy.QtCore.QSize
class napari_track_edit.application_menus.copy_from_source_widget.CopyFromSourceWidget(viewer: napari.Viewer)

Bases: qtpy.QtWidgets.QWidget

Widget to copy detections from a source layer into the current tracks.

A Points or Labels layer holding detections can be connected as a copy source (the chain button). While connected, right-clicking a detection on the target track layer copies the detection under the cursor from the source layer into the tracks: onto the background it is added with the current tracklet id, on top of an existing label it replaces that label unless preserve labels is active.

viewer
tracks_viewer
_source_layer = None
_target_layer = None
_target_callback = None
_mode = None
_synced_tracks = None
source_layer_dropdown
chain_btn
copy_controls_box
auto_new_track_checkbox
channel_hint
illustration
_set_chain_icon(connected: bool) → None

Show a closed chain icon when connected, an open (broken) chain when not.

_reset_chain() → None

Set the chain button back to the disconnected (unchecked) state without triggering a toggle.

_update_source_controls() → None

Enable the chain button whenever there are tracks to copy detections into.

_sync_to_tracks(*args) → None

Keep the chain button and the allowed source layer type in sync with the current tracks object. Whenever the tracks object changes (a different tracking result is selected), derive the mode from it - ‘labels’ if it has a segmentation, else ‘points’ - and drop any stale source connection.

_on_source_dropdown_changed(name=None) → None

React to the user picking a different source layer. Selecting a layer other than the currently connected one disconnects the old source and resets the chain button to its unchecked state, so the user can connect the newly selected layer with a fresh click.

_on_chain_toggled(checked: bool) → None

Connect (closed chain) or disconnect (open chain) the selected source layer.

_setup_source_connection(source_layer: napari.layers.Labels | napari.layers.Points) → None

Keep the detections layer visible and attach a right-click callback to the target track layer that copies the detection under the cursor into the tracks.

_teardown_source_connection() → None

Disconnect the right-click callback from the previously connected target layer.

_make_target_callback() → callable

Create the mouse callback that copies a detection on right-click.

_copy_detection(event) → None

Copy the label or point that is under the cursor in the source layer into the tracks as a new node with the current tracklet id.

The event may come from the target layer in the main viewer or from one of its orthogonal-view copies; the source layer is always looked up by the event’s world position, which the orthogonal views share with the main viewer.

_leading_axes(layer: napari.layers.Labels | napari.layers.Points) → int

Return how many leading axes the source layer has that the tracks do not.

A source layer may hold several ‘channels’: alternative segmentations of the same objects, stacked on extra axes in front of the ones the tracks use. Napari aligns layers on their trailing dimensions, so those axes come directly in front of the tracks’ axes in the viewer (see TracksDims), and their sliders pick which of the alternatives a copy reads from. This count is in the layer’s own data axes, as returned by world_to_data.

_copy_label(layer: napari.layers.Labels, event) → None

Copy the label under the cursor (in the clicked time point) into the target segmentation via an UserUpdateSegmentation action.The label is read from the source data at the clicked coordinates.

If the source carries extra leading axes, the click reads from the one the sliders are on, so which segmentation option gets copied follows the channel the user has selected.

_clicked_track_label(event) → int

Return the node id at the clicked location in the target segmentation, or 0 if the click was on the background (or outside the data).

_copy_point(layer: napari.layers.Points, event) → None

Copy the point under the cursor into the tracks as a point node via an UserAddNode action.

The point is looked up by the clicked coordinates (the closest point of the clicked time point, within its own radius).

_add_node(t: int, position: numpy.ndarray) → None

Add a point node to the tracks with the current tracklet id, at the given position (used for Points sources).

_add_segmentation_node(t: int, spatial_coords: tuple[numpy.ndarray, ...], clicked_value: int = 0) → None

Copy a label into the target segmentation at time t.

What a copy does depends on what was under the cursor in the target segmentation and on the ‘preserve labels’ setting of the target TrackLabels layer:

  • Clicked on an existing label (clicked_value != 0): that label is replaced by the copied one, even where the copied pixels do not fully cover it, and the result belongs to the currently active tracklet (see _replace_label). With ‘preserve labels’ on, replacing is not allowed and nothing is copied.

  • Clicked on the background (clicked_value == 0): the copied label is added with the current tracklet id, either in full (‘preserve labels’ off, existing labels are overwritten) or only where the target is still empty (‘preserve labels’ on).

Parameters:
  • t (int) – The time point of the copied label.

  • spatial_coords (tuple[np.ndarray, ...]) – The spatial (non-time) coordinates of the label pixels, as returned by np.where on a single time frame.

  • clicked_value (int) – The node id at the clicked location in the target segmentation, or 0 if the click was on the background.

_replace_label(t: int, spatial_coords: tuple[numpy.ndarray, ...], node: int, preserve: bool) → None

Replace the label that was clicked on with the copied one.

The copy always belongs to the currently active tracklet. Which node ends up holding the copied pixels depends on the active tracklet:

  • the clicked node is the active tracklet’s node in this frame: it keeps its id (and with it its edges) and its pixels become exactly the copied ones.

  • the active tracklet has no node in this frame: the clicked node is deleted and the copied pixels become a new node of the active tracklet.

  • the active tracklet has a different node in this frame: the clicked node is deleted and, since a tracklet can only have one node per frame, the copied pixels either grow that node or, with ‘automatically start new tracks’ on, become a new node with a new tracklet id.

With ‘preserve labels’ on, only a label of the active tracklet may be replaced - the labels of other tracklets are protected.

A segmentation update can only paint with a single value, so replacing takes more than one action: the copied pixels are painted first, so the node is never momentarily empty, and whatever the copy does not cover is deleted or erased afterwards. The actions are committed together, so one undo reverts the copy.

_add_label(t: int, spatial_coords: tuple[numpy.ndarray, ...], preserve: bool) → None

Copy a label onto the background with the current tracklet id.

With ‘preserve labels’ on, only the pixels that are not part of an existing label are copied. The copy grows the current tracklet’s node in this frame if it has one, unless ‘automatically start new tracks’ is checked: then it starts a new track.

_commit(actions: list, node_to_select: int | None) → None

Record the actions of one copy as a single undoable step, refresh the views and select the node the copy landed in.

The actions are created with _top_level=False so that a copy that takes more than one segmentation update (replacing a label needs a paint and an erase) is still undone in one go.

_paint(t: int, spatial_coords: tuple[numpy.ndarray, ...], new_value: int, track_id: int) → list | None

Paint the given pixels of frame t with new_value, creating the node if it does not exist yet and shrinking (or deleting) the nodes that are overwritten.

Returns the actions that were performed, for the caller to commit (see _commit); the list is empty if there was nothing to paint. Returns None if the paint was not allowed or the user declined to force it, so the caller can revert what it already did.

_erase_outside(t: int, spatial_coords: tuple[numpy.ndarray, ...], node: int, track_id: int) → list

Erase the pixels of node in frame t that are not among the copied ones, so that the node is left with exactly the copied pixels.

Returns the actions that were performed, for the caller to commit (see _commit); the list is empty if the copy covered the whole label already.

_current_track_node(t: int, track_id: int) → int | None

Return the node of track_id present in frame t, or None if there is none.

_get_target_layer() → napari.layers.Labels | napari.layers.Points | None

Return the track layer that copies go into, based on the active mode: the TrackLabels layer for ‘labels’, the TrackPoints layer for ‘points’.

_update_copy_controls_visibility(event=None) → None

Show the copy controls while a source layer is connected, and tell the user about the extra axes of a multi-channel source.