Scripts2026年4月14日·1 分钟阅读

Textual — Rapid Application Development Framework for the Terminal

Textual is a Python framework for building sophisticated TUI applications with CSS-based styling, async event handling, and 20+ built-in widgets. The same app can run in the terminal or in a web browser.

Introduction

Textual is the most ambitious terminal UI framework in the Python ecosystem. Built on top of Rich, it brings web-app-style development (CSS styling, reactive state, async events) to the terminal. Applications can target both the TTY and the browser (via textual-web).

With over 26,000 GitHub stars, Textual powers modern CLI tools like Harlequin (SQL IDE), Posting (HTTP client), and Memray (memory profiler UI).

What Textual Does

Textual gives you widgets (Button, Input, DataTable, Tree, etc.), CSS-based styling, an async event loop, reactive state (watch properties and rerender automatically), and a full devtools stack. You write a class, compose widgets, style with CSS, and run anywhere Python runs.

Architecture Overview

[Textual App]
    |
   [Compose Tree]
    Widgets & containers
    |
   [CSS Stylesheet]
    Layout, colors, borders
    |
   [Event Loop (asyncio)]
    on_mount / on_key / on_click
    |
   [Message Queue]
    Reactive updates
    |
   [Driver]
    +-- Terminal (Linux/macOS/Windows)
    +-- Browser (textual-web)

Self-Hosting & Configuration

from textual.app import App, ComposeResult
from textual.widgets import Input, DataTable, Header, Footer
from textual.containers import Vertical

class SearchApp(App):
    CSS_PATH = "search.tcss"
    BINDINGS = [("q", "quit", "Quit")]

    def compose(self) -> ComposeResult:
        yield Header()
        with Vertical():
            yield Input(placeholder="Search...", id="q")
            yield DataTable(id="results")
        yield Footer()

    def on_mount(self) -> None:
        table = self.query_one(DataTable)
        table.add_columns("Name", "Stars")

    async def on_input_submitted(self, event: Input.Submitted) -> None:
        rows = await fetch_results(event.value)
        table = self.query_one(DataTable)
        table.clear()
        for row in rows:
            table.add_row(*row)

SearchApp().run()

Key Features

  • Widget library — Button, Input, DataTable, Tree, Tabs, and 20+ more
  • CSS styling — real cascading stylesheets for terminals (.tcss)
  • Reactive state — properties that trigger re-renders on change
  • Async event loop — asyncio-native, play nicely with aiohttp/httpx
  • DevTools — live reload + console for debugging
  • Cross-target — run in terminal or browser via textual-web
  • Theming — built-in dark/light themes, easy custom palettes
  • Testable — Pilot object lets you script UI interactions in pytest

Comparison with Similar Tools

Feature Textual Rich urwid Curses Blessed
Widgets 20+ No Many Low-level Low-level
CSS Styling Yes BBCode markup Manual No No
Async Yes No Limited No No
Web Target Yes No No No No
Learning Curve Moderate Low High Very High Moderate
Best For Full TUI apps Pretty CLI output Legacy TUI Custom TUI Shell scripts

FAQ

Q: Can Textual apps really run in a browser? A: Yes. textual-web serves your app over WebSockets and renders it with an HTML canvas. Same code, zero changes.

Q: Is Textual production ready? A: Yes (v0.x but actively used). Posting, Harlequin, and dozens of commercial tools ship Textual apps.

Q: How do I test a Textual app? A: Use App.run_test() which returns a Pilot for scripting keystrokes and mouse clicks. Combine with pytest.

Q: Does it work on Windows? A: Yes, including Windows Terminal and legacy cmd.exe (with reduced fidelity).

Sources

讨论

登录后参与讨论。
还没有评论,来写第一条吧。

相关资产