Source code for phenotypic.sdk_.mixin._point_picker_mixin

"""Marker mixin for operations whose centres are picked interactively."""

from __future__ import annotations

from typing import TYPE_CHECKING, ClassVar, TypeVar

if TYPE_CHECKING:
    from phenotypic._core._image import Image

_T = TypeVar("_T", bound="PointPickerMixin")


[docs] class PointPickerMixin: """Shared plumbing for operations parameterised by user-picked ``(y, x)`` points. Operations that take a list of point coordinates as a primary parameter (currently :class:`~phenotypic.detect.ManualPointDetector` and :class:`~phenotypic.refine.ManualRefine`) inherit from this mixin to pick up two things at once: * **A blocking napari picker.** :meth:`napari` opens a desktop :class:`~phenotypic.sdk_.napari_.PointPickerWidget` and writes the confirmed picks back to the operation. Closing the viewer without confirming is a no-op (existing centres are preserved). * **A GUI introspection hook.** The ``_point_picker_param_name`` class attribute tells the Dash builder which field to replace with an interactive picker button instead of a free-form text input. Subclasses that store coordinates under a name other than ``"centers"`` can override :attr:`_point_picker_param_name`; :meth:`napari` uses that attribute. Coordinate coercion (list/tuple/``np.ndarray`` → a canonical form) moves to a ``field_validator`` on the coordinate field of each consuming operation as part of the pydantic migration — this mixin no longer overrides ``__setattr__`` (which would clash with pydantic's). Attributes: _point_picker_param_name: Name of the field holding the picked ``(y, x)`` coordinates. Defaults to ``"centers"``. """ _point_picker_param_name: ClassVar[str] = "centers"
[docs] def napari(self: _T, image: Image) -> _T: """Interactively pick coordinates using a napari viewer. Opens a blocking napari viewer displaying the plate image layers. Click points on the image, then click **Confirm** in the dock widget. The picked coordinates are stored in the attribute named by :attr:`_point_picker_param_name`. If the viewer is closed without confirming any points, existing coordinates are preserved. Args: image: The Image to display for coordinate selection. Returns: The mixin-bearing instance, for method chaining. Raises: ImportError: If napari is not installed. """ from phenotypic.sdk_.napari_ import PointPickerWidget points = PointPickerWidget(max_points=None).run(image) if len(points) > 0: setattr(self, self._point_picker_param_name, points) return self