| name | textual |
| description | How to use the Textual framework for creating Terminal User Interfaces (TUIs) in Python |
Textual TUI Framework
Textual is an async Rapid Application Development framework for Python that allows you to build sophisticated user interfaces in the terminal using an API inspired by modern web development.
Core Concepts
- The App Class: Every Textual application inherits from
textual.app.App.
- Widgets: Reusable components like
Static, Button, Input, DataTable, etc. available under textual.widgets.
- The
compose Method: Instead of explicitly appending children to a layout, Textual uses generator functions (yield) to compose the UI hierarchically.
- Events and Handlers: Actions like button presses are handled using structured naming conventions attached to the Widget class names, e.g.,
def on_button_pressed(self, event: Button.Pressed).
- CSS Styling (TCSS): A stylesheet language identical to CSS but specialized for the terminal. Layout units use specific constraints like percentages (
100%) or fractions (1fr). Note: Fractional units are fr, not rf.
Example Application Structure
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Button, Static
from textual.containers import Vertical, Horizontal
class MyTextualApp(App):
BINDINGS = [
("q", "quit", "Quit application")
]
CSS_PATH = "styles.tcss"
def compose(self) -> ComposeResult:
"""Yield widgets to build the UI."""
yield Header()
yield Vertical(
Static("Welcome to Textual!", id="welcome-text"),
Horizontal(
Button("Click Me!", id="say-hello-btn", variant="primary"),
),
)
yield Footer()
def on_button_pressed(self, event: Button.Pressed) -> None:
"""Event Handler fired when any button is pressed."""
if event.button.id == "say-hello-btn":
self.notify("Hello World!")
def action_quit(self) -> None:
"""Action handler corresponding to the BINDING 'quit'."""
self.exit()
if __name__ == "__main__":
app = MyTextualApp()
app.run()
Styling via TCSS Example
TCSS rules map component classes, IDs, and elements just like standard web CSS, providing a responsive design engine for terminal grids.
#welcome-text {
content-align: center middle;
text-style: bold;
color: $accent;
padding: 1 2;
}
Horizontal {
height: auto;
}
Button {
width: 1fr;
}
Modal Screens
Applications can overlay completely distinct views or modals via Screen objects:
from textual.screen import Screen
class SetupScreen(Screen):
def compose(self) -> ComposeResult:
yield Static("Settings Configurator")
yield Button("Save", id="save-btn")
def on_button_pressed(self, event: Button.Pressed) -> None:
self.dismiss("Saved Data Payload")
def open_setup(self):
self.push_screen(SetupScreen(), callback=self.on_setup_completed)
def on_setup_completed(self, payload: str):
self.notify(f"Setup returned: {payload}")
Clipboard API Limitations
Textual provides a clipboard API (self.app.clipboard and self.app.copy_to_clipboard()), but it has significant limitations that agents must be aware of:
- Internal Text Only: The
self.app.clipboard property only contains text copied from within the app itself. It does not allow you to read text copied from the host OS clipboard.
- Text Only (No Images): The API strictly handles string
text. It cannot read or write binary image data to the clipboard.
- Platform Support:
copy_to_clipboard() uses ANSI OSC 52 escape sequences to push text to the OS clipboard, which is not supported by all terminal emulators (e.g., standard macOS Terminal.app).
- Workaround for Images: If you need to capture images from the OS clipboard (e.g. on macOS), you must bypass Textual and use native OS commands (like
osascript or pbpaste/third-party binaries) wrapped in Python subprocesses.
Official Documentation & Reference
For more detailed information, consult the official Textual documentation:
- Reference: Comprehensive guides and concept explanations.
- Guide: Step-by-step tutorials to get started.
- How-to: Specific recipes for common tasks.
- FAQ: Answers to frequently asked questions.
- API Reference: Detailed documentation for the Textual Python API.