def __init__(self, data: Iterable[_T] = ()): _events = {'changed': None} # For inheritance: If the mro already provides an EmitterGroup, add... if hasattr(self, 'events') and isinstance(self.events, EmitterGroup): self.events.add(**_events) else: # otherwise create a new one self.events = EmitterGroup(source=self, **_events) self._set: set[_T] = set() self.update(data)
def test_event_group_depr(): events = EmitterGroup(b=None, deprecated={"a": "b"}) with pytest.warns(FutureWarning): assert events.b == events.a with pytest.raises(AttributeError): events.c.connect() with pytest.warns(FutureWarning): assert events["b"] == events["a"] with pytest.raises(KeyError): events["c"].connect()
def __init__(self, key_frames: KeyFrameList) -> None: super().__init__() self._key_frames = key_frames key_frames.events.inserted.connect(self._rebuild_frame_index) key_frames.events.removed.connect(self._rebuild_frame_index) key_frames.events.changed.connect(self._rebuild_frame_index) key_frames.events.reordered.connect(self._rebuild_frame_index) self.events = EmitterGroup(source=self, n_frames=None) self.state_interpolation_map: InterpolationMap = { "camera.angles": Interpolation.SLERP, "camera.zoom": Interpolation.LOG, } # cache of interpolated viewer states self._cache: Dict[int, ViewerState] = {} # map of frame number -> (kf0, kf1, fraction) self._frame_index: Dict[int, Tuple[KeyFrame, KeyFrame, float]] = {} self._rebuild_frame_index()
def test_prune_dead_qt(qtbot): qtcalls = 0 class W(QSpinBox): def _set(self, event): self.setValue(event.value) nonlocal qtcalls qtcalls += 1 wdg = W() mock = Mock() group = EmitterGroup(None, False, boom=None) group.boom.connect(mock) group.boom.connect(wdg._set) assert len(group.boom.callbacks) == 2 group.boom(value=1) assert qtcalls == 1 mock.assert_called_once() mock.reset_mock() with qtbot.waitSignal(wdg.destroyed): wdg.close() wdg.deleteLater() group.boom(value=1) mock.assert_called_once() assert len(group.boom.callbacks) == 1 # we've lost the qt connection assert qtcalls == 1 # qwidget didn't get called again
class A: events = EmitterGroup(boom=None)
class E: events = EmitterGroup(test=None)
def __init__(self): self.events = EmitterGroup(test=None)
class EventedSet(MutableSet[_T]): """An unordered collection of unique elements. Parameters ---------- data : iterable, optional Elements to initialize the set with. Events ------ changed (added: Set[_T], removed: Set[_T]) Emitted when the set changes, includes item(s) that have been added and/or removed from the set. """ events: EmitterGroup def __init__(self, data: Iterable[_T] = ()): _events = {'changed': None} # For inheritance: If the mro already provides an EmitterGroup, add... if hasattr(self, 'events') and isinstance(self.events, EmitterGroup): self.events.add(**_events) else: # otherwise create a new one self.events = EmitterGroup(source=self, **_events) self._set: set[_T] = set() self.update(data) # #### START Required Abstract Methods def __contains__(self, x: Any) -> bool: return x in self._set def __iter__(self) -> Iterator[_T]: return iter(self._set) def __len__(self) -> int: return len(self._set) def _pre_add_hook(self, value): # for subclasses to potentially check value before adding return value def add(self, value: _T) -> None: """Add an element to the set, if not already present.""" if value not in self: value = self._pre_add_hook(value) self._set.add(value) self.events.changed(added={value}, removed={}) def discard(self, value: _T) -> None: """Remove an element from a set if it is a member. If the element is not a member, do nothing. """ if value in self: self._set.discard(value) self.events.changed(added={}, removed={value}) # #### END Required Abstract Methods # methods inherited from Set: # __le__, __lt__, __eq__, __ne__, __gt__, __ge__, __and__, __or__, # __sub__, __xor__, and isdisjoint # methods inherited from MutableSet: # clear, pop, remove, __ior__, __iand__, __ixor__, and __isub__ # The rest are for parity with builtins.set: def clear(self) -> None: if self._set: values = set(self) self._set.clear() self.events.changed(added={}, removed=values) def __repr__(self) -> str: return f"{type(self).__name__}({repr(self._set)})" def update(self, others: Iterable[_T] = ()) -> None: """Update this set with the union of this set and others""" to_add = set(others).difference(self._set) if to_add: to_add = {self._pre_add_hook(i) for i in to_add} self._set.update(to_add) self.events.changed(added=set(to_add), removed={}) def copy(self) -> EventedSet[_T]: """Return a shallow copy of this set.""" return type(self)(self._set) def difference(self, others: Iterable[_T] = ()) -> EventedSet[_T]: """Return set of all elements that are in this set but not other.""" return type(self)(self._set.difference(others)) def difference_update(self, others: Iterable[_T] = ()) -> None: """Remove all elements of another set from this set.""" to_remove = self._set.intersection(others) if to_remove: self._set.difference_update(to_remove) self.events.changed(added={}, removed=set(to_remove)) def intersection(self, others: Iterable[_T] = ()) -> EventedSet[_T]: """Return all elements that are in both sets as a new set.""" return type(self)(self._set.intersection(others)) def intersection_update(self, others: Iterable[_T] = ()) -> None: """Remove all elements of in this set that are not present in other.""" self.difference_update(self._set.symmetric_difference(others)) def issubset(self, others: Iterable[_T]) -> bool: """Returns whether another set contains this set or not""" return self._set.issubset(others) def issuperset(self, others: Iterable[_T]) -> bool: """Returns whether this set contains another set or not""" return self._set.issuperset(others) def symmetric_difference(self, others: Iterable[_T]) -> EventedSet[_T]: """Returns set of elements that are in exactly one of the sets""" return type(self)(self._set.symmetric_difference(others)) def symmetric_difference_update(self, others: Iterable[_T]) -> None: """Update set to the symmetric difference of itself and another. This will remove any items in this set that are also in `other`, and add any items in others that are not present in this set. """ to_add = set(others).difference(self._set) to_remove = self._set.intersection(others) self._set.difference_update(to_remove) self._set.update(to_add) self.events.changed(added=to_add, removed=to_remove) def union(self, others: Iterable[_T] = ()) -> EventedSet[_T]: """Return a set containing the union of sets""" return type(self)(self._set.union(others)) @classmethod def __get_validators__(cls): yield cls.validate @classmethod def validate(cls, v, field: ModelField): """Pydantic validator.""" from pydantic.utils import sequence_like if not sequence_like(v): raise TypeError( trans._( 'Value is not a valid sequence: {value}', deferred=True, value=v, ) ) if not field.sub_fields: return cls(v) type_field = field.sub_fields[0] errors = [] for i, v_ in enumerate(v): _valid_value, error = type_field.validate(v_, {}, loc=f'[{i}]') if error: errors.append(error) if errors: from pydantic import ValidationError raise ValidationError(errors, cls) # type: ignore return cls(v) def _json_encode(self): """Return an object that can be used by json.dumps.""" return list(self)
class EventedSet(MutableSet[_T]): """An unordered collection of unique elements. Parameters ---------- data : iterable, optional Elements to initialize the set with. Events ------ added (value: Set[_T]) emitted after an item or items are added to the set. Will not be emitted if item was already in the set when added. removed (value: Set[_T]) emitted after an item or items are removed from the set. Will not be emitted if the item was not in the set when discarded. """ events: EmitterGroup def __init__(self, data: Iterable[_T] = ()): _events = {'added': None, 'removed': None} # For inheritance: If the mro already provides an EmitterGroup, add... if hasattr(self, 'events') and isinstance(self.events, EmitterGroup): self.events.add(**_events) else: # otherwise create a new one self.events = EmitterGroup(source=self, **_events) self._set: set[_T] = set() self.update(data) # #### START Required Abstract Methods def __contains__(self, x: Any) -> bool: return x in self._set def __iter__(self) -> Iterator[_T]: return iter(self._set) def __len__(self) -> int: return len(self._set) def add(self, value: _T) -> None: if value not in self: self._set.add(value) self.events.added(value={value}) def discard(self, value: _T) -> None: """Remove an element from a set if it is a member. If the element is not a member, do nothing. """ if value in self: self._set.discard(value) self.events.removed(value={value}) # #### END Required Abstract Methods # methods inherited from Set: # __le__, __lt__, __eq__, __ne__, __gt__, __ge__, __and__, __or__, # __sub__, __xor__, and isdisjoint # methods inherited from MutableSet: # clear, pop, remove, __ior__, __iand__, __ixor__, and __isub__ # The rest are for parity with builtins.set: def clear(self) -> None: if self._set: values = set(self._set) self._set.clear() self.events.removed(value=values) def __repr__(self) -> str: return f"{type(self).__name__}({repr(self._set)})" def update(self, others: Iterable[_T] = ()) -> None: """Update this set with the union of this set and others""" to_add = set(others).difference(self._set) if to_add: self._set.update(to_add) self.events.added(value=to_add) def copy(self) -> EventedSet[_T]: """Return a shallow copy of this set.""" return EventedSet(self._set) def difference(self, others: Iterable[_T] = ()) -> EventedSet[_T]: """Return set of all elements that are in this set but not other.""" return EventedSet(self._set.difference(others)) def difference_update(self, others: Iterable[_T] = ()) -> None: """Remove all elements of another set from this set.""" to_remove = self._set.intersection(others) if to_remove: self._set.difference_update(others) self.events.removed(value=to_remove) def intersection(self, others: Iterable[_T] = ()) -> EventedSet[_T]: """Return all elements that are in both sets as a new set.""" return EventedSet(self._set.intersection(others)) def intersection_update(self, others: Iterable[_T] = ()) -> None: """Remove all elements of in this set that are not present in other.""" self.difference_update(self._set.symmetric_difference(others)) def issubset(self, others: Iterable[_T]) -> bool: """Returns whether another set contains this set or not""" return self._set.issubset(others) def issuperset(self, others: Iterable[_T]) -> bool: """Returns whether this set contains another set or not""" return self._set.issuperset(others) def symmetric_difference(self, others: Iterable[_T]) -> EventedSet[_T]: """Returns set of elements that are in exactly one of the sets""" return EventedSet(self._set.symmetric_difference(others)) def symmetric_difference_update(self, others: Iterable[_T]) -> None: """Update set to the symmetric difference of itself and another. This will remove any items in this set that are also in `other`, and add any items in others that are not present in this set. """ to_remove = self._set.intersection(others) to_add = set(others).difference(self) if to_remove: self._set.difference_update(others) self.events.removed(value=to_remove) if to_add: self._set.update(to_add) self.events.added(value=to_add) def union(self, others: Iterable[_T] = ()) -> EventedSet[_T]: """Return a set containing the union of sets""" return EventedSet(self._set.union(others))
class FrameSequence(Sequence[ViewerState]): """Final sequence of of rendered animation frames, based on keyframes. This object acts like an immutable sequence of frames, interpolated from a sequence of (mutable) KeyFrames. It can be indexed at any (valid) frame in the animation, and will inteprolate (and cache) viewer state on demand. If the KeyFrameList changes in any way, the cache is cleared. Parameters ---------- key_frames : KeyFrameList A KeyFrameList from which to render the final frame sequence. """ def __init__(self, key_frames: KeyFrameList) -> None: super().__init__() self._key_frames = key_frames key_frames.events.inserted.connect(self._rebuild_frame_index) key_frames.events.removed.connect(self._rebuild_frame_index) key_frames.events.changed.connect(self._rebuild_frame_index) key_frames.events.reordered.connect(self._rebuild_frame_index) self.events = EmitterGroup(source=self, n_frames=None) self.state_interpolation_map: InterpolationMap = { "camera.angles": Interpolation.SLERP, "camera.zoom": Interpolation.LOG, } # cache of interpolated viewer states self._cache: Dict[int, ViewerState] = {} # map of frame number -> (kf0, kf1, fraction) self._frame_index: Dict[int, Tuple[KeyFrame, KeyFrame, float]] = {} self._rebuild_frame_index() def _rebuild_frame_index(self, event=None): """Create a map of frame number -> (kf0, kf1, fraction)""" self._frame_index.clear() self._cache.clear() if len(self._key_frames) < 2: self.events.n_frames(value=len(self)) return f = 0 for kf0, kf1 in pairwise(self._key_frames): for s in range(kf1.steps): fraction = kf1.ease(s / kf1.steps) self._frame_index[f] = (kf0, kf1, fraction) f += 1 self._frame_index[f] = (kf1, kf1, 0) self.events.n_frames(value=len(self)) def __len__(self) -> int: """The total frame count of the animation""" return len(self._frame_index) def __getitem__(self, key: int) -> ViewerState: """Get the interpolated state at frame `key` in the animation.""" if key < 0: key += len(self) if key not in self._cache: try: kf0, kf1, frac = self._frame_index[key] except KeyError: raise IndexError( f"Frame index ({key}) out of range ({len(self)} frames)" ) if frac == 0: self._cache[key] = kf0.viewer_state else: self._cache[key] = self._interpolate_state( kf0.viewer_state, kf1.viewer_state, frac, self.state_interpolation_map, ) return self._cache[key] def _interpolate_state( self, from_state: ViewerState, to_state: ViewerState, fraction: float, state_interpolation_map: Optional[InterpolationMap] = None, ): """Interpolate a state between two states Parameters ---------- from_state : ViewerState Description of initial viewer state. to_state : ViewerState Description of final viewer state. fraction : float Interpolation fraction, must be between `0` and `1`. A value of `0` will return the initial state. A value of `1` will return the final state. state_interpolation_map : dict Dictionary relating state attributes to interpolation functions. Returns ------- state : dict Description of viewer state. """ from .utils import keys_to_list, nested_get, nested_set interp_map = state_interpolation_map or self.state_interpolation_map state = {} sep = "." from_state = asdict(from_state) to_state = asdict(to_state) for keys in keys_to_list(from_state): v0 = nested_get(from_state, keys) v1 = nested_get(to_state, keys) interp_func = interp_map.get(sep.join(keys), Interpolation.DEFAULT) nested_set(state, keys, interp_func(v0, v1, fraction)) return ViewerState(**state) def iter_frames( self, viewer: napari.viewer.Viewer, canvas_only: bool = True, scale_factor: float = None, ) -> Iterator[np.ndarray]: """Iterate over interpolated viewer states, and yield rendered frames.""" for i, state in enumerate(self): frame = state.render(viewer, canvas_only=canvas_only) if scale_factor is not None: from scipy import ndimage as ndi frame = ndi.zoom(frame, (scale_factor, scale_factor, 1)) frame = frame.astype(np.uint8) yield frame