Events
Paths and scenes run on their own once activated, but effects often need something to happen at a particular moment:
when a character lands, start a scene; when a scene ends, start the next path. Each character's
EventHandler, character.event_handler, runs actions when events happen to
that character's paths, waypoints, and scenes.
Registering an event
from terminaltexteffects import EventHandler
character.event_handler.register_event(
EventHandler.Event.PATH_COMPLETE, # when this happens...
fall, # ...to this path, waypoint, or scene...
EventHandler.Action.SET_LAYER, # ...do this...
0, # ...with this target.
)
terminaltexteffects also exports the enums as Event and Action, so you can write Event.PATH_COMPLETE.
You register events once, during setup. They then fire every time their event happens, for the rest of the effect. Several actions can be registered for the same event; they run in the order you registered them.
Events
| Event | Fires when | Caller |
|---|---|---|
PATH_ACTIVATED |
A path is activated. | Path |
SEGMENT_ENTERED |
The character starts the segment that ends at a waypoint. | Waypoint |
SEGMENT_EXITED |
The character reaches a waypoint. | Waypoint |
PATH_HOLDING |
The character reaches the end of a path with hold_time, before holding. |
Path |
PATH_COMPLETE |
A path finishes, after any hold. | Path |
SCENE_ACTIVATED |
A scene is activated. | Scene |
SCENE_COMPLETE |
A scene finishes. | Scene |
The caller is the object the event is about. For path and scene activation and completion, you can pass the path or scene's ID string instead of the object.
Activation events fire immediately when the path or scene is activated, even during setup and even if the character isn't active yet. Register events for a path or scene before you activate it, or the activation event is already over.
Segment events fire once per traversal of the segment. A looping path fires them again on each loop.
Actions
| Action | Target | Does |
|---|---|---|
ACTIVATE_PATH |
Path or path ID |
Starts that path from the character's current position. |
DEACTIVATE_PATH |
Path, path ID, or None |
Stops that path, or whichever path is active. |
ACTIVATE_SCENE |
Scene or scene ID |
Starts that scene. |
DEACTIVATE_SCENE |
Scene, scene ID, or None |
Stops that scene, or whichever scene is active. |
SET_LAYER |
int |
Moves the character to that layer. |
SET_COORDINATE |
Coord |
Moves the character to that position instantly. |
RESET_APPEARANCE |
none | Restores the input symbol and its input colors. |
CALLBACK |
EventHandler.Callback |
Calls your function. |
Callbacks
When no built-in action fits, use a callback. EventHandler.Callback(function, *args) calls
function(character, *args) when the event fires:
def hide(character, terminal):
terminal.set_character_visibility(character, is_visible=False)
character.event_handler.register_event(
Event.PATH_COMPLETE, fall, Action.CALLBACK, EventHandler.Callback(hide, self.terminal)
)
Callbacks can do anything, including activating other paths. Keep them short: they run inside the character's tick.
The Hop example
Hop makes each column of text hop to the top of the canvas and fall back, one column after another. Events do all the sequencing:
PATH_ACTIVATEDon the rise sets layer 1, so rising characters pass in front.PATH_HOLDINGon the rise calls a callback that changes the symbol to*while the character waits at the top.PATH_COMPLETEon the rise activates the fall path.PATH_COMPLETEon the fall restores the input symbol and sets layer 0.
The iterator only activates each column's rise. Save it as hop.py in your community effects directory:
"""Hop: characters hop to the top of the canvas and back, one column at a time.
An Effect Guide example for the Events chapter. Events chain the two paths, change the character's
symbol at the top, and restore it when the character lands.
"""
from __future__ import annotations
from dataclasses import dataclass
from terminaltexteffects import Coord, EffectCharacter, EventHandler, easing
from terminaltexteffects.engine.base_config import BaseConfig
from terminaltexteffects.engine.base_effect import BaseEffect, BaseEffectIterator
from terminaltexteffects.utils import argutils
Event = EventHandler.Event
Action = EventHandler.Action
def get_effect_resources() -> tuple[str, type[BaseEffect], type[BaseConfig]]:
"""Return the command name, effect class, and configuration class TTE registers."""
return "hop", Hop, HopConfig
@dataclass
class HopConfig(BaseConfig):
"""Configuration for the Hop effect."""
parser_spec = argutils.ParserSpec(
name="hop",
help="Characters hop to the top of the canvas and back.",
description="hop | Characters hop to the top of the canvas and back.",
epilog="Example: tte hop",
)
class HopIterator(BaseEffectIterator[HopConfig]):
"""Produce the frames of the Hop effect."""
def __init__(self, effect: Hop) -> None:
"""Give every character a hop, and queue the columns."""
super().__init__(effect)
top = self.terminal.canvas.top
for character in self.terminal.get_characters():
self.terminal.set_character_visibility(character, is_visible=True)
rise = character.motion.new_path(speed=0.5, ease=easing.out_quad, hold_time=10, path_id="rise")
rise.new_waypoint(Coord(character.input_coord.column, top))
fall = character.motion.new_path(speed=0.5, ease=easing.out_bounce)
fall.new_waypoint(character.input_coord)
handler = character.event_handler
# Rising characters pass in front of the others.
handler.register_event(Event.PATH_ACTIVATED, rise, Action.SET_LAYER, 1)
# At the top, show a different symbol while the path holds.
handler.register_event(
Event.PATH_HOLDING,
rise,
Action.CALLBACK,
EventHandler.Callback(lambda char: char.animation.set_appearance("*")),
)
# When the rise completes, fall back home.
handler.register_event(Event.PATH_COMPLETE, rise, Action.ACTIVATE_PATH, fall)
# On landing, restore the input symbol and return to the base layer.
handler.register_event(Event.PATH_COMPLETE, fall, Action.RESET_APPEARANCE)
handler.register_event(Event.PATH_COMPLETE, fall, Action.SET_LAYER, 0)
self.columns: list[list[EffectCharacter]] = self.terminal.get_characters_grouped(order="column_left_to_right")
self.columns.reverse()
self.frame_count = 0
def __next__(self) -> str:
"""Start one column hopping every third frame."""
if self.columns or self.active_characters:
if self.columns and self.frame_count % 3 == 0:
for character in self.columns.pop():
character.motion.activate_path("rise")
self.active_characters.add(character)
self.frame_count += 1
self.update()
return self.frame
raise StopIteration
class Hop(BaseEffect[HopConfig]):
"""Characters hop to the top of the canvas and back."""
@property
def _config_cls(self) -> type[HopConfig]:
return HopConfig
@property
def _iterator_cls(self) -> type[HopIterator]:
return HopIterator

When events fire in a tick
During a character's tick, motion moves first and animation steps second (see Ticking a character), and each fires its own events as it goes, so path events for a tick run before scene events for that tick.
An action that activates a path or scene changes what the character is doing straight away. The engine stops processing the old path for the rest of that tick, so the new path starts moving on the next tick.
Where to go next
- Tutorial step 4: Layer uses
PATH_ACTIVATEDandPATH_COMPLETEto change layers. - Motion shows
chain_paths(), a shortcut forPATH_COMPLETEandACTIVATE_PATH.