Textual (TUI) Intermediate¶
🖥️ GUI · Level 3
When you'd use this
Modern terminal user interfaces — rich widgets, CSS styling and async.
Build rich text-based user interfaces that run in the terminal — for dev tools and SSH-friendly apps.
What is Textual?¶
Introduces Textual and where it fits in Textual (TUI).
Textual builds beautiful terminal applications with CSS-like styling, mouse support and rich widgets — like a web framework for the terminal.
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Static, Input, Button, DataTable
from textual.containers import Container, Horizontal
class TodoApp(App):
CSS = """
Screen { layout: vertical; }
#input-area { height: 3; layout: horizontal; }
#input-area Input { width: 1fr; }
#input-area Button { width: 15; }
DataTable { height: 1fr; }
"""
BINDINGS = [("q", "quit", "Quit"), ("d", "delete", "Delete")]
def compose(self) -> ComposeResult:
yield Header()
with Horizontal(id="input-area"):
yield Input(placeholder="Add a task...", id="task-input")
yield Button("Add", variant="primary", id="add-btn")
yield DataTable(id="tasks")
yield Footer()
def on_mount(self):
table = self.query_one("#tasks", DataTable)
table.add_columns("ID", "Task", "Status")
self._next_id = 1
def on_button_pressed(self, event: Button.Pressed):
if event.button.id == "add-btn":
self._add_task()
def on_input_submitted(self, event: Input.Submitted):
self._add_task()
def _add_task(self):
input_widget = self.query_one("#task-input", Input)
task = input_widget.value.strip()
if task:
table = self.query_one("#tasks", DataTable)
table.add_row(str(self._next_id), task, "Pending")
self._next_id += 1
input_widget.value = ""
if __name__ == "__main__":
TodoApp().run()
Practice Exercises¶
- Build a dashboard showing system stats (CPU, memory, disk) with live updates.
- Build a file browser with tree widget and preview pane.
- Build a log viewer that tails a log file with color-coded levels.
- Build a database explorer — connect, browse tables, run queries.
💬 Discussion
Have a question about this topic? Found an error? Share your thoughts below.