Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
PyTermGUI's animation system provides smooth property transitions and dynamic effects for creating engaging user interfaces. The system supports value animations, attribute animations, and custom easing functions.
Central animation manager that schedules and executes animations with timing control and coordination.
class Animator:
"""Animation management system."""
def __init__(self):
"""Initialize animator."""
def schedule(self, animation):
"""
Schedule animation for execution.
Parameters:
- animation: Animation instance to schedule
"""
def run(self):
"""Run animation loop until all animations complete."""
def stop(self):
"""Stop all animations."""
def update(self, delta_time: float):
"""Update all animations by time delta."""
@property
def is_running(self) -> bool:
"""Check if any animations are active."""
def clear(self):
"""Clear all scheduled animations."""
# Global animator instance
animator: AnimatorNumeric value animation with configurable easing and duration.
class FloatAnimation:
"""Floating-point value animation."""
def __init__(self, duration: float, start: float, end: float,
easing: str = "linear"):
"""
Create float animation.
Parameters:
- duration (float): Animation duration in seconds
- start (float): Starting value
- end (float): Ending value
- easing (str): Easing function name
"""
@property
def value(self) -> float:
"""Get current animated value."""
@property
def progress(self) -> float:
"""Get animation progress (0.0 to 1.0)."""
@property
def is_finished(self) -> bool:
"""Check if animation is complete."""
def set_callback(self, callback: Callable[[float], None]):
"""Set callback function called with each value update."""Object attribute animation for animating widget properties.
class AttrAnimation:
"""Object attribute animation."""
def __init__(self, obj: object, attr: str, duration: float,
end_value: float, easing: str = "linear"):
"""
Create attribute animation.
Parameters:
- obj: Target object to animate
- attr (str): Attribute name to animate
- duration (float): Animation duration in seconds
- end_value: Target value for attribute
- easing (str): Easing function name
"""
@property
def current_value(self) -> float:
"""Get current attribute value."""
@property
def is_finished(self) -> bool:
"""Check if animation is complete."""Helper functions for animation management and detection.
def is_animated(target: object, attribute: str) -> bool:
"""
Check if object attribute is animated.
Parameters:
- target: Object to check
- attribute (str): Attribute name to check
Returns:
True if the specified attribute is animated
"""Built-in easing functions for natural animation curves.
# Available easing function names:
EASING_FUNCTIONS = [
"linear",
"ease_in",
"ease_out",
"ease_in_out",
"bounce_in",
"bounce_out",
"elastic_in",
"elastic_out"
]import pytermgui as ptg
import time
# Create a simple float animation
animation = ptg.FloatAnimation(
duration=2.0,
start=0.0,
end=100.0,
easing="ease_in_out"
)
# Set callback to handle value updates
def on_value_change(value):
print(f"Current value: {value:.2f}")
animation.set_callback(on_value_change)
# Schedule and run animation
ptg.animator.schedule(animation)
ptg.animator.run()import pytermgui as ptg
# Create widget and animate its width
container = ptg.Container(width=20, height=10)
# Animate width from 20 to 60 over 1.5 seconds
width_animation = ptg.AttrAnimation(
obj=container,
attr="width",
duration=1.5,
end_value=60,
easing="bounce_out"
)
ptg.animator.schedule(width_animation)
ptg.animator.run()import pytermgui as ptg
# Create widget
window = ptg.Window("Animated Window", width=40, height=15)
# Create multiple animations
animations = [
ptg.AttrAnimation(window, "width", 2.0, 80, "ease_out"),
ptg.AttrAnimation(window, "height", 1.5, 25, "ease_in"),
]
# Schedule all animations
for animation in animations:
ptg.animator.schedule(animation)
# Run animations concurrently
ptg.animator.run()import pytermgui as ptg
# Animate color transitions
class ColorAnimation:
def __init__(self, widget, duration, start_color, end_color):
self.widget = widget
self.start_rgb = start_color.rgb
self.end_rgb = end_color.rgb
# Create component animations
self.r_anim = ptg.FloatAnimation(duration, self.start_rgb[0], self.end_rgb[0])
self.g_anim = ptg.FloatAnimation(duration, self.start_rgb[1], self.end_rgb[1])
self.b_anim = ptg.FloatAnimation(duration, self.start_rgb[2], self.end_rgb[2])
# Set update callback
self.r_anim.set_callback(self.update_color)
def update_color(self, _):
r = int(self.r_anim.value)
g = int(self.g_anim.value)
b = int(self.b_anim.value)
new_color = ptg.RGBColor(r, g, b)
# Update widget color property
self.widget.styles.border = str(new_color)
# Usage
label = ptg.Label("Animated Text")
color_anim = ColorAnimation(
label,
duration=3.0,
start_color=ptg.Color("red"),
end_color=ptg.Color("blue")
)
ptg.animator.schedule(color_anim.r_anim)
ptg.animator.schedule(color_anim.g_anim)
ptg.animator.schedule(color_anim.b_anim)
ptg.animator.run()import pytermgui as ptg
def create_sequence():
"""Create sequence of chained animations."""
button = ptg.Button("Animated Button")
def phase1_done():
# Start second animation when first completes
phase2 = ptg.AttrAnimation(button, "width", 1.0, 20, "bounce_in")
phase2.set_callback(lambda _: print("Animation complete!"))
ptg.animator.schedule(phase2)
# First animation
phase1 = ptg.AttrAnimation(button, "height", 1.0, 5, "ease_out")
phase1.set_callback(lambda _: phase1_done() if phase1.is_finished else None)
ptg.animator.schedule(phase1)
return button
# Create and run sequence
animated_button = create_sequence()
ptg.animator.run()import pytermgui as ptg
def animate_window_entrance(window):
"""Animate window appearing."""
# Start window small and grow it
window.width = 10
window.height = 5
# Animate to full size
width_anim = ptg.AttrAnimation(window, "width", 0.8, 60, "bounce_out")
height_anim = ptg.AttrAnimation(window, "height", 0.6, 20, "ease_out")
ptg.animator.schedule(width_anim)
ptg.animator.schedule(height_anim)
with ptg.WindowManager() as manager:
window = ptg.Window(
"[bold]Welcome!",
"",
ptg.Label("This window animated in"),
ptg.Button("Close", lambda btn: manager.stop()),
title="Animated Window"
)
manager.add(window)
animate_window_entrance(window)
# Run animations alongside window manager
import threading
animation_thread = threading.Thread(target=ptg.animator.run)
animation_thread.start()import pytermgui as ptg
class PulsingLabel(ptg.Label):
"""Label that pulses with animation."""
def __init__(self, text, **attrs):
super().__init__(text, **attrs)
self.start_pulsing()
def start_pulsing(self):
"""Start pulsing animation."""
def pulse_callback(value):
# Use animation value to control text brightness
intensity = int(value)
self.value = f"[{intensity}]{self.original_text}[/{intensity}]"
self.original_text = self.value
# Create pulsing animation (back and forth)
pulse_out = ptg.FloatAnimation(1.0, 210, 240, "ease_in_out")
pulse_out.set_callback(pulse_callback)
def restart_animation():
if pulse_out.is_finished:
# Restart the pulse
new_pulse = ptg.FloatAnimation(1.0, 240, 210, "ease_in_out")
new_pulse.set_callback(pulse_callback)
ptg.animator.schedule(new_pulse)
pulse_out.set_callback(lambda v: (pulse_callback(v), restart_animation()))
ptg.animator.schedule(pulse_out)
# Usage
pulsing_label = PulsingLabel("This text pulses!")
ptg.animator.run()Install with Tessl CLI
npx tessl i tessl/pypi-pytermgui