Files
2025-11-30 09:08:16 +08:00

4.5 KiB

Common Widgets

Display Widgets

Label / Static

Display static or updatable text:

from textual.widgets import Label, Static

# Label is just an alias for Static
label = Label("Hello World")
static = Static("Initial text")

# Update later
static.update("New text")
static.update("[bold]Rich markup[/bold]")

Placeholder

Useful for prototyping layouts:

from textual.widgets import Placeholder

# Shows widget ID and size info
yield Placeholder("Custom label", id="p1")
yield Placeholder(variant="size")  # Shows dimensions
yield Placeholder(variant="text")  # Shows placeholder text

Input Widgets

Button

from textual.widgets import Button

yield Button("Click Me", id="my-button")
yield Button("Disabled", disabled=True)

# Handle click
def on_button_pressed(self, event: Button.Pressed) -> None:
    button_id = event.button.id
    self.notify(f"{button_id} clicked!")

Input

Single-line text input:

from textual.widgets import Input

yield Input(placeholder="Enter text...", id="name-input")

def on_input_changed(self, event: Input.Changed) -> None:
    self.text_value = event.value

def on_input_submitted(self, event: Input.Submitted) -> None:
    # User pressed Enter
    self.process_input(event.value)

TextArea

Multi-line text editor:

from textual.widgets import TextArea

text_area = TextArea()
text_area.load_text("Initial content")

# Get content
content = text_area.text

Switch

Toggle switch (like checkbox):

from textual.widgets import Switch

yield Switch(value=True)  # Initially on

def on_switch_changed(self, event: Switch.Changed) -> None:
    is_on = event.value
    self.toggle_feature(is_on)

Data Display

DataTable

Display tabular data:

from textual.widgets import DataTable

table = DataTable()

# Add columns
table.add_columns("Name", "Age", "Country")

# Add rows
table.add_row("Alice", 30, "USA")
table.add_row("Bob", 25, "UK")

# Add row with custom label
from rich.text import Text
label = Text("1", style="bold cyan")
table.add_row("Charlie", 35, "Canada", label=label)

# Configuration
table.zebra_stripes = True  # Alternating row colors
table.cursor_type = "row"  # "cell", "row", "column", or "none"
table.show_header = True
table.show_row_labels = True

# Handle selection
def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
    row_key = event.row_key
    row_data = table.get_row(row_key)

def on_data_table_cell_selected(self, event: DataTable.CellSelected) -> None:
    value = event.value
    coordinate = event.coordinate

Layout Containers

Container

Generic container for grouping widgets:

from textual.containers import Container

with Container(id="sidebar"):
    yield Label("Title")
    yield Button("Action")

Vertical / Horizontal / VerticalScroll / HorizontalScroll

Directional containers:

from textual.containers import Vertical, Horizontal, VerticalScroll

with Horizontal():
    yield Button("Left")
    yield Button("Right")

with VerticalScroll():
    for i in range(100):
        yield Label(f"Item {i}")

Grid

Grid layout container:

from textual.containers import Grid

with Grid(id="my-grid"):
    yield Label("A")
    yield Label("B")
    yield Label("C")
    yield Label("D")

# Style in CSS:
# Grid {
#     grid-size: 2 2;  /* 2 columns, 2 rows */
# }

App Widgets

Standard app chrome:

from textual.widgets import Header, Footer

def compose(self) -> ComposeResult:
    yield Header()
    # ... content ...
    yield Footer()

Footer automatically shows key bindings defined in BINDINGS.

Custom Widgets

Create reusable components:

from textual.widget import Widget
from textual.widgets import Label, Button

class Card(Widget):
    """A card widget with title and content."""

    DEFAULT_CSS = """
    Card {
        width: 30;
        height: 15;
        border: round white;
        padding: 1;
    }
    """

    def __init__(self, title: str, content: str) -> None:
        super().__init__()
        self.title = title
        self.content = content

    def compose(self) -> ComposeResult:
        yield Label(self.title, classes="card-title")
        yield Label(self.content, classes="card-content")
        yield Button("Select", id=f"select-{self.title}")

Render Method

For simple custom widgets that just render text:

from textual.widget import Widget

class FizzBuzz(Widget):
    def render(self) -> str:
        return "FizzBuzz!"