Textual

PythonでモダンなTUIアプリケーションを構築するためのフレームワーク。CSS、DOM、リアクティブなプログラミングモデルを採用し、Web開発の概念をターミナル環境に持ち込んでいます。

pythontuiframeworkcssdomreactive

GitHub概要

Textualize/textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.

スター30,007
ウォッチ174
フォーク932
作成日:2021年4月8日
言語:Python
ライセンス:MIT License

トピックス

cliframeworkpythonrichterminaltui

スター履歴

Textualize/textual Star History
データ取得日時: 2025/7/25 06:22

フレームワーク

Textual

概要

TextualはPythonでモダンなTUI(テキストベースユーザーインターフェース)アプリケーションを構築するための革新的なフレームワークです。Web開発の概念(CSS、DOM、リアクティブプログラミング)をターミナル環境に持ち込み、直感的で高性能なTUIアプリケーションの開発を可能にします。

詳細

Textualは2021年にTextualize社によって開発され、PythonのTUI開発に新しいパラダイムを提供しました。従来のcursesベースのアプローチとは異なり、モダンなWeb開発のベストプラクティスを採用しています。

主要な特徴

  • CSS-like スタイリング: CSS風の構文でアプリケーションの見た目をカスタマイズ
  • DOM構造: HTMLのようなDOM(Document Object Model)でUI階層を管理
  • リアクティブ属性: データの変更が自動的にUIに反映される仕組み
  • 豊富なウィジェット: ボタン、入力フィールド、テーブル、ツリーなど多様な組み込みウィジェット
  • イベント駆動: メッセージベースのイベントシステム
  • アニメーション: スムーズなアニメーション効果
  • テーマシステム: カスタマイズ可能なテーマとカラースキーム
  • Web展開: ターミナルアプリをWebブラウザでも実行可能

アーキテクチャ

Textualのアーキテクチャは以下の主要コンポーネントで構成されています:

  • App: アプリケーションのエントリーポイント
  • Screen: 画面全体を表す特別なWidget
  • Widget: UI要素の基底クラス
  • MessagePump: イベント処理の基盤
  • Compositor: レンダリングシステム
  • CSS Engine: スタイリングエンジン

メリット・デメリット

メリット

  • モダンなWeb開発の知識を活用できる
  • 高性能なレンダリングとアニメーション
  • 豊富なドキュメントとコミュニティサポート
  • クロスプラットフォーム対応(Linux、macOS、Windows)
  • Webブラウザでの実行も可能
  • 豊富な組み込みウィジェット
  • 直感的なCSS風スタイリング
  • 強力な開発ツール(デバッグコンソール、ライブリロードなど)

デメリット

  • 比較的新しいフレームワークのため学習リソースが限定的
  • 大規模アプリケーションでの実績がまだ少ない
  • Richライブラリへの依存
  • 軽量なアプリケーションには機能過多の場合がある

主要リンク

書き方の例

from textual.app import App, ComposeResult
from textual.containers import Container
from textual.widgets import Button, Header, Footer, Static

class CounterApp(App):
    \"\"\"A simple counter app.\"\"\"
    
    CSS_PATH = "counter.tcss"
    
    def __init__(self):
        super().__init__()
        self.counter = 0
    
    def compose(self) -> ComposeResult:
        \"\"\"Create child widgets for the app.\"\"\"
        yield Header()
        yield Footer()
        yield Container(
            Static(f"Count: {self.counter}", id="counter"),
            Button("Increment", id="increment"),
            Button("Decrement", id="decrement"),
            id="app-grid",
        )
    
    def on_button_pressed(self, event: Button.Pressed) -> None:
        \"\"\"Event handler called when a button is pressed.\"\"\"
        if event.button.id == "increment":
            self.counter += 1
        elif event.button.id == "decrement":
            self.counter -= 1
        
        # Update the counter display
        counter_widget = self.query_one("#counter", Static)
        counter_widget.update(f"Count: {self.counter}")

if __name__ == "__main__":
    app = CounterApp()
    app.run()

CSSファイル (counter.tcss):

#app-grid {
    display: grid;
    grid-size: 1 3;
    grid-rows: 1fr;
    margin: 1 2;
    min-height: 0;
}

#counter {
    content-align: center middle;
    text-style: bold;
}

Button {
    margin: 1 2;
}