CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-textual

Modern Text User Interface framework for building cross-platform terminal and web applications with Python

Overall
score

93%

Overview
Eval results
Files

events.mddocs/

Event System

Comprehensive event handling system for user interactions, widget lifecycle, and system events, including keyboard input, mouse interactions, focus management, and custom message passing.

Capabilities

Base Event Classes

Foundation classes for all events and messages in the Textual framework.

class Message:
    """Base class for all messages."""
    
    def __init__(self) -> None:
        """Initialize a message."""
    
    def prevent_default(self) -> None:
        """Prevent the default handler for this message."""
    
    def stop(self) -> None:
        """Stop message propagation."""
    
    # Properties
    sender: MessageTarget | None
    time: float
    handler_name: str | None

class Event(Message):
    """Base class for all events."""
    
    def __init__(self) -> None:
        """Initialize an event."""
    
    # Properties
    bubble: bool  # Whether the event bubbles up the DOM
    verbose: bool  # Whether to include in verbose logging

class InputEvent(Event):
    """Base class for input-related events."""
    pass

Keyboard Events

Events for handling keyboard input and key combinations.

class Key(InputEvent):
    """Keyboard key press event."""
    
    def __init__(self, key: str, character: str | None = None):
        """
        Initialize a key event.
        
        Parameters:
        - key: The key identifier (e.g., "enter", "escape", "ctrl+c")
        - character: The character representation (if printable)
        """
    
    @property
    def is_printable(self) -> bool:
        """Whether the key produces a printable character."""
    
    # Properties
    key: str  # Key identifier
    character: str | None  # Character representation
    name: str  # Human-readable key name
    aliases: list[str]  # Alternative key names

Mouse Events

Events for handling mouse interactions including clicks, movement, and scrolling.

class MouseEvent(InputEvent):
    """Base class for mouse events."""
    
    def __init__(self, x: int, y: int, button: int = 0):
        """
        Initialize a mouse event.
        
        Parameters:
        - x: Mouse X coordinate
        - y: Mouse Y coordinate  
        - button: Mouse button number
        """
    
    # Properties
    x: int  # Mouse X coordinate
    y: int  # Mouse Y coordinate
    button: int  # Mouse button (0=left, 1=middle, 2=right)
    screen_x: int  # Screen-relative X
    screen_y: int  # Screen-relative Y

class MouseDown(MouseEvent):
    """Mouse button pressed down event."""
    pass

class MouseUp(MouseEvent):
    """Mouse button released event."""
    pass

class Click(MouseEvent):
    """Mouse click event (down + up)."""
    pass

class MouseMove(MouseEvent):
    """Mouse movement event."""
    pass

class MouseScrollDown(MouseEvent):
    """Mouse scroll down event."""
    pass

class MouseScrollUp(MouseEvent):
    """Mouse scroll up event."""
    pass

Focus and Visibility Events

Events related to widget focus state and visibility changes.

class Focus(Event):
    """Widget gained focus event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize a focus event.
        
        Parameters:
        - widget: The widget that gained focus
        """
    
    # Properties
    widget: Widget

class Blur(Event):
    """Widget lost focus event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize a blur event.
        
        Parameters:
        - widget: The widget that lost focus  
        """
    
    # Properties
    widget: Widget

class Show(Event):
    """Widget became visible event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize a show event.
        
        Parameters:
        - widget: The widget that became visible
        """

class Hide(Event):
    """Widget became hidden event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize a hide event.
        
        Parameters:
        - widget: The widget that became hidden
        """

Widget Lifecycle Events

Events that occur during widget creation, mounting, and destruction.

class Mount(Event):
    """Widget was mounted to the DOM event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize a mount event.
        
        Parameters:
        - widget: The widget that was mounted
        """

class Unmount(Event):
    """Widget was unmounted from the DOM event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize an unmount event.
        
        Parameters:
        - widget: The widget that was unmounted
        """

class Compose(Event):
    """Widget composition completed event."""
    pass

class Ready(Event):
    """Application is ready to receive events."""
    pass

class Load(Event):  
    """Application/widget loading completed event."""
    pass

System and Layout Events

Events related to system state and layout changes.

class Resize(Event):
    """Screen or widget resize event."""
    
    def __init__(self, size: Size, virtual_size: Size):
        """
        Initialize a resize event.
        
        Parameters:
        - size: New size of the widget/screen
        - virtual_size: Virtual size including scrollable area
        """
    
    # Properties
    size: Size
    virtual_size: Size

class Idle(Event):
    """Application idle event (no recent input)."""
    pass

class ExitApp(Event):
    """Application exit requested event."""
    
    def __init__(self, result: Any = None):
        """
        Initialize an exit event.
        
        Parameters:
        - result: Exit result value
        """
    
    # Properties
    result: Any

class Callback(Event):
    """Callback function invocation event."""
    
    def __init__(self, callback: Callable, *args, **kwargs):
        """
        Initialize a callback event.
        
        Parameters:
        - callback: Function to call
        - *args: Positional arguments
        - **kwargs: Keyword arguments
        """

class Timer(Event):
    """Timer callback event."""
    
    def __init__(self, timer: Timer, time: float):
        """
        Initialize a timer event.
        
        Parameters:
        - timer: The timer instance
        - time: Current time
        """

Custom Widget Events

Common event patterns used by built-in widgets.

class Changed(Event):
    """Generic value changed event."""
    
    def __init__(self, widget: Widget, value: Any):
        """
        Initialize a changed event.
        
        Parameters:
        - widget: Widget that changed
        - value: New value
        """
    
    # Properties
    widget: Widget
    value: Any

class Pressed(Event):
    """Generic button/item pressed event."""
    
    def __init__(self, widget: Widget):
        """
        Initialize a pressed event.
        
        Parameters:
        - widget: Widget that was pressed
        """

class Selected(Event):
    """Generic selection event."""
    
    def __init__(self, widget: Widget, item: Any):
        """
        Initialize a selected event.
        
        Parameters:
        - widget: Widget with selection
        - item: Selected item
        """

class Toggled(Event):
    """Generic toggle state changed event."""
    
    def __init__(self, widget: Widget, toggled: bool):
        """
        Initialize a toggled event.
        
        Parameters:
        - widget: Widget that was toggled
        - toggled: New toggle state
        """

Usage Examples

Basic Event Handling

from textual.app import App
from textual.widgets import Button, Input
from textual.events import Key

class EventApp(App):
    def compose(self):
        yield Input(placeholder="Type here...", id="input")
        yield Button("Click me!", id="button")
    
    def on_key(self, event: Key):
        """Handle any key press."""
        self.log(f"Key pressed: {event.key}")
        
        if event.key == "ctrl+q":
            self.exit()
    
    def on_button_pressed(self, event: Button.Pressed):
        """Handle button presses."""
        self.log(f"Button {event.button.id} was pressed!")
    
    def on_input_changed(self, event: Input.Changed):
        """Handle input value changes."""
        self.log(f"Input value: {event.value}")
    
    def on_input_submitted(self, event: Input.Submitted):
        """Handle input submission (Enter key)."""
        self.log(f"Input submitted: {event.value}")

Event Handling with Decorators

from textual.app import App
from textual.widgets import Button, Input
from textual import on

class DecoratorApp(App):
    def compose(self):
        yield Input(id="name-input")
        yield Button("Submit", id="submit-btn")
        yield Button("Clear", id="clear-btn")
    
    @on(Button.Pressed, "#submit-btn")
    def handle_submit(self, event: Button.Pressed):
        """Handle submit button with CSS selector."""
        input_widget = self.query_one("#name-input", Input)
        self.log(f"Submitting: {input_widget.value}")
    
    @on(Button.Pressed, "#clear-btn")
    def handle_clear(self, event: Button.Pressed):
        """Handle clear button."""
        input_widget = self.query_one("#name-input", Input)
        input_widget.value = ""
    
    @on(Input.Changed, "#name-input")
    def handle_name_change(self, event: Input.Changed):
        """Handle name input changes."""
        submit_btn = self.query_one("#submit-btn", Button)
        submit_btn.disabled = len(event.value.strip()) == 0

Custom Event Creation

from textual.app import App
from textual.widget import Widget
from textual.message import Message
from textual.widgets import Button

class CustomWidget(Widget):
    """A widget that sends custom events."""
    
    class DataReady(Message):
        """Custom event sent when data is ready."""
        
        def __init__(self, widget: Widget, data: dict):
            super().__init__()
            self.widget = widget
            self.data = data
    
    def compose(self):
        yield Button("Load Data", id="load")
    
    def on_button_pressed(self, event: Button.Pressed):
        """Handle button press and send custom event."""
        if event.button.id == "load":
            # Simulate data loading
            data = {"result": "success", "items": [1, 2, 3]}
            
            # Send custom event
            self.post_message(self.DataReady(self, data))

class CustomEventApp(App):
    def compose(self):
        yield CustomWidget(id="custom")
    
    def on_custom_widget_data_ready(self, event: CustomWidget.DataReady):
        """Handle custom data ready event."""
        self.log(f"Data received: {event.data}")

Mouse Event Handling

from textual.app import App
from textual.widgets import Static
from textual.events import MouseDown, MouseUp, MouseMove

class MouseApp(App):
    def compose(self):
        yield Static("Click and drag on me!", id="target")
    
    def on_mouse_down(self, event: MouseDown):
        """Handle mouse button press."""
        self.log(f"Mouse down at ({event.x}, {event.y}), button {event.button}")
    
    def on_mouse_up(self, event: MouseUp):
        """Handle mouse button release."""
        self.log(f"Mouse up at ({event.x}, {event.y})")
    
    def on_mouse_move(self, event: MouseMove):
        """Handle mouse movement."""
        # Only log occasionally to avoid spam
        if event.x % 5 == 0 and event.y % 5 == 0:
            self.log(f"Mouse at ({event.x}, {event.y})")

Event Prevention and Stopping

from textual.app import App
from textual.widgets import Input
from textual.events import Key

class PreventionApp(App):
    def compose(self):
        yield Input(placeholder="Try typing numbers...", id="text-only")
    
    def on_key(self, event: Key):
        """Prevent numeric input in text field."""
        # Check if the focused widget is our text-only input
        focused = self.focused
        if focused and focused.id == "text-only":
            # Prevent numeric characters
            if event.character and event.character.isdigit():
                event.prevent_default()  # Stop default handling
                event.stop()  # Stop event propagation
                self.log("Numeric input blocked!")

Install with Tessl CLI

npx tessl i tessl/pypi-textual

docs

content.md

core-framework.md

events.md

index.md

styling.md

testing.md

widgets.md

tile.json