Source code for arcade.gui.experimental.transition

from abc import ABC, abstractmethod
from typing import Any, Callable, List, Optional, TypeVar

from pyglet.event import EventDispatcher

from arcade.anim import Easing

T = TypeVar("T", bound="TransitionBase")


[docs] class TransitionBase(ABC):
[docs] @abstractmethod def tick(self, subject, dt) -> float: """ Update :return: dt, which is not consumed """ pass
@property @abstractmethod def finished(self) -> bool: raise NotImplementedError() def __add__(self, other): return TransitionChain(self, other) def __or__(self, other): return TransitionParallel(self, other)
[docs] class EventTransitionBase(TransitionBase, EventDispatcher): """ Extension of TransitionBase, providing hooks via - on_tick(subject, progress: float) - on_finish(subject) :param duration: Duration of the transition in seconds :param delay: Start transition after x seconds """ def __init__( self, *, duration: float, delay=0.0, ): self._duration = duration self._elapsed = -delay self.register_event_type("on_tick") self.register_event_type("on_finish")
[docs] def tick(self, subject, dt) -> float: self._elapsed += dt if self._elapsed >= 0: progress = min(self._elapsed / self._duration, 1) if self._duration else 1 self.dispatch_event("on_tick", subject, progress) if self.finished: self.dispatch_event("on_finish", subject) return max(0.0, self._elapsed - self._duration)
[docs] def on_tick(self, subject, progress): pass
[docs] def on_finish(self, subject): pass
@property def finished(self): return self._elapsed >= self._duration
[docs] class TransitionDelay(EventTransitionBase): def __init__(self, duration: float): super().__init__(duration=duration)
[docs] class TransitionAttr(EventTransitionBase): """ Changes an attribute over time. :param start: start value, if None, the subjects value is read via `getattr` :param end: target value :param attribute: attribute to set :param duration: Duration of the transition in seconds :param ease_function: :param delay: Start transition after x seconds :param mutation_function: function to be used to set new value """ def __init__( self, *, end, attribute, duration: float, start=None, ease_function=Easing.LINEAR, delay=0.0, mutation_function: Callable[[Any, str, float], None] = setattr, ): super().__init__(duration=duration, delay=delay) self._start: Optional[float] = start self._end = end self._attribute = attribute self._ease_function = ease_function self._mutation_function = mutation_function
[docs] def on_tick(self, subject, progress): if self._start is None: self._start = getattr(subject, self._attribute) factor = self._ease_function(progress) new_value = self._start + (self._end - self._start) * factor self._mutation_function(subject, self._attribute, new_value)
[docs] class TransitionAttrIncr(TransitionAttr): """ Changes an attribute over time. :param increment: difference the value should be changed over time (can be negative) :param attribute: attribute to set :param duration: Duration of the transition in seconds :param ease_function: :param delay: Start transition after x seconds :param mutation_function: function to be used to set new value """ def __init__( self, *, increment: float, attribute, duration: float, ease_function=Easing.LINEAR, delay=0.0, mutation_function: Callable[[Any, str, float], None] = setattr, ): super().__init__(end=increment, attribute=attribute, duration=duration, delay=delay) self._attribute = attribute self._ease_function = ease_function self._mutation_function = mutation_function
[docs] def on_tick(self, subject, progress): if self._start is None: self._start = getattr(subject, self._attribute) self._end += self._start factor = self._ease_function(progress) new_value = self._start + (self._end - self._start) * factor self._mutation_function(subject, self._attribute, new_value)
[docs] class TransitionAttrSet(EventTransitionBase): """ Set the attribute when expired. :param value: value to set :param attribute: attribute to set :param duration: Duration of the transition in seconds """ def __init__(self, *, value: float, attribute, duration: float, mutation_function=setattr): super().__init__(duration=duration) self._attribute = attribute self._value = value self._mutation_function = mutation_function
[docs] def on_finish(self, subject): setattr(subject, self._attribute, self._value)
[docs] class TransitionParallel(TransitionBase): """ A transition assembled by multiple transitions. Executing them in parallel. """ def __init__(self, *transactions: TransitionBase): super().__init__() self._transitions: List[TransitionBase] = list(transactions)
[docs] def add(self, transition: T) -> T: self._transitions.append(transition) return transition
[docs] def tick(self, subject, dt): remaining_dt = dt for transition in self._transitions[:]: r = transition.tick(subject, dt) remaining_dt = min(remaining_dt, r) if transition.finished: self._transitions.remove(transition) return remaining_dt
@property def finished(self) -> bool: return not self._transitions
[docs] class TransitionChain(TransitionBase): """ A transition assembled by multiple transitions. Executing them sequential. """ def __init__(self, *transactions: TransitionBase): super().__init__() self._transitions: List[TransitionBase] = list(transactions)
[docs] def add(self, transition: T) -> T: self._transitions.append(transition) return transition
[docs] def tick(self, subject, dt): while dt and not self.finished: transition = self._transitions[0] dt = transition.tick(subject, dt) if transition.finished: self._transitions.pop(0) return max(0.0, dt)
@property def finished(self) -> bool: return not self._transitions