リンドくんたなべ先生、最近「TUI」という言葉をよく聞くんですけど、これって何なんですか?
たなべTUI(Text User Interface)は、ターミナル上でGUI並みに美しいアプリが作れる技術なんだ。
今日は特にPythonの「Textual」というライブラリを使って、驚くほど簡単にリッチなツールが作れることを紹介するよ!
プログラミングを学んでいる皆さん、ターミナルでの作業は好きですか?
コマンドライン操作は確かに軽快で効率的ですが、時には「もっと視覚的に分かりやすくならないかな...」と思うことってありますよね。
実は今、そんな悩みを解決する革新的な技術が注目されています。
それがTUIです。特にPythonの「Textual」ライブラリを使えば、わずか100行程度のコードで、まるでGUIアプリのような美しいツールをターミナル上に作ることができるんです。
この記事では、TUIの基本概念から実際のアプリケーション開発まで、初心者の方でも理解しやすいよう丁寧に解説していきます。
最後には、システムリソースをリアルタイムで監視する、カラフルなダッシュボードの作り方もご紹介しますので、ぜひ最後までお読みください!
↓動画版↓
TUIとは
TUIとは、ターミナル上で動作するグラフィカルなユーザーインターフェースのことです。
これまでのCLI(コマンドライン)の軽さと高速性を保ちながら、GUI(グラフィカルユーザーインターフェース)のような視覚的な分かりやすさを実現した、まさに「いいとこ取り」の技術なんです。
TUIが注目される理由
従来のインターフェースと比較してみましょう。
CLI(Command Line Interface)の特徴
- 軽量で高速
- リソース消費が少ない
- 学習コストが高い
- 視覚的な情報が限定的
GUI(Graphical User Interface)の特徴
- 直感的で分かりやすい
- マウス操作が簡単
- メモリ・CPU消費が大きい
- 開発に時間がかかる
TUI(Text User Interface)の特徴
- CLIの軽さとGUIの視覚性を両立
- 色やレイアウトが使える
- キーボード中心の高速操作
- 開発が比較的簡単
TUIが活躍する場面
TUIが特に威力を発揮するのは以下のような場面です。
- DevOps向けダッシュボード - サーバーリソースの監視やログ解析
- 開発ツール - Git操作(tig)やファイル管理(ranger)
- クラウド管理 - Docker操作(Lazydocker)やKubernetes管理(k9s)
- 社内ツール - データベース管理やバッチ処理の監視
これらのツールは、SSH経由でリモートサーバーを操作する際にも非常に便利で、現代のクラウドネイティブな開発現場でも重宝されているんです。
PythonのTextualライブラリの魅力
リンドくんPythonでTUIを作るには、どんなライブラリを使えばいいんですか?
たなべTextualというライブラリが断然おすすめだよ!
Richライブラリの作者が開発したもので、Web開発者にも馴染みやすいCSSライクな記法でレイアウトを定義できるんだ。
Textualの主要機能
Textualは、Will McGugan氏によって開発されたモダンなTUIフレームワークです。
同氏は人気ライブラリ「Rich」の作者でもあり、TextualはRichの描画エンジンを基盤として開発されています。
主な特徴をご紹介します。
豊富な表現力
- 絵文字やテーブル、Markdownの美しい表示
- グラデーションを含む豊富な色表現
- プログレスバー、グラフなどの視覚的要素
現代的なレイアウトシステム
- CSSライクな記法でレイアウト定義
- FlexboxやGridのような現代的手法
- テーマの簡単な切り替え
リアクティブな仕組み
- データ変更時の自動UI更新
- イベント駆動のインタラクション
- リアルタイムデータ表示
開発者に優しい機能
特に開発者にとって嬉しいのがDevToolsの存在です。
textual run --devコマンドでアプリケーションを起動すると、ブラウザの開発者ツールのような感覚でTUIアプリケーションをデバッグできるんです。
これは本当に便利で、DOM構造やイベントログを確認でき、ホットリロード機能により、コードを変更すると即座にアプリケーションに反映されます。
また、MITライセンスで提供されているため、商用利用も問題ありません。企業での利用や有償ソフトウェアへの組み込みも可能です。
Textualのインストールと最初のTUIアプリ
現代的なPython開発環境として、今回はuvを使用した方法をおすすめします。
もちろん、pipを使っても問題ありません。
環境準備とインストール
まず、プロジェクトディレクトリを作成し、Python環境を準備します。
# プロジェクトディレクトリの作成と初期化
mkdir my-tui-app
cd my-tui-app
uv init -p 3.13 # Python 3.13を指定
# Textualとdev用ツールのインストール
uv add textual textual-dev
uv synctextual-devは開発用ツールが含まれており、デバッグ時に非常に便利なので必ず含めることをおすすめします。
最小構成のTUIアプリケーション
それでは、最小構成のTUIアプリケーションを作ってみましょう。
たった10行程度で動作するTUIアプリケーションが作れるんです。
from textual.app import App
from textual.widgets import Static
class Hello(App):
def compose(self):
yield Static("Hello, TUI!")
Hello().run()このコードをmain.pyとして保存し、以下のコマンドで実行してみてください。
uv run textual run main.py実行すると、ターミナル上に「Hello, TUI!」というテキストが表示されたウィンドウが現れます。
Ctrl+QまたはCtrl+Pを押してアプリケーションを終了できます。
開発モードでのデバッグ
開発モードを試してみましょう。これはデバッグ時に非常に便利です。
# 別のターミナルでコンソールを起動
uv run textual console
# メインターミナルで開発モードで実行
uv run textual run --dev main.pyこれにより、TUI内部で発生しているイベントを確認でき、print関数の出力もコンソールに表示されます。
CSSライクなスタイリング
Textualの素晴らしい点は、CSSライクなスタイリングができることです。
from textual.app import App
from textual.widgets import Static
class StyledApp(App):
CSS = """
Static {
background: blue;
color: white;
padding: 2;
}
"""
def compose(self):
yield Static("スタイル付きTUIアプリ")
StyledApp().run()また、CSS_PATH変数を使って、Pythonコードからスタイル部分を分離することも可能です。
公式では.tcssという拡張子を推奨しています。
実践的なシステム監視ダッシュボードの作成
ここからは、より実用的なTUIアプリケーションとして、システムリソースをリアルタイムで監視するダッシュボードの概要をご紹介します。
必要なライブラリ
システム情報を取得するために、psutilライブラリが必要です。
uv add psutilソースコード
# sys_view.py
import psutil
from textual.app import App, ComposeResult
from textual.color import Gradient
from textual.containers import Container, Horizontal, Vertical
from textual.reactive import reactive
from textual.widgets import Footer, Header, ProgressBar, Sparkline, Static
# ---------- ウィジェット定義 ----------
class Gauge(Container):
"""ラベル + ProgressBar + Sparkline のセット"""
percent = reactive(0.0)
history = reactive(list)
def __init__(self, title: str, *, gradient: Gradient, classes: str = "") -> None:
super().__init__()
self.title_text = title
self.gradient = gradient
self.classes = classes
self.bar: ProgressBar = ProgressBar(
total=100, show_eta=False, gradient=self.gradient
)
self.spark: Sparkline = Sparkline()
def compose(self) -> ComposeResult:
yield Static(self.title_text, classes="gauge-label")
self.bar = ProgressBar(total=100, show_eta=False, gradient=self.gradient)
self.spark = Sparkline()
yield self.bar
yield self.spark
def watch_percent(self, value: float) -> None:
self.bar.update(progress=value)
def watch_history(self, data: list[int]) -> None:
self.spark.data = data
# ---------- メイン部分 ----------
class SystemPulseApp(App):
CSS = """
Screen { layout: vertical; }
Container.gauge { border: round #555555; padding: 1 2; height: auto; }
.gauge-label { text-style: bold; margin-bottom: 1; }
"""
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
rainbow = Gradient.from_colors(
"#881177",
"#aa3355",
"#cc6666",
"#ee9944",
"#eedd00",
"#99dd55",
"#44dd88",
"#22ccbb",
"#00bbcc",
"#0099cc",
"#3366bb",
"#663399",
)
with Vertical():
yield Static("CPU Usage (%)", classes="gauge-label")
self.cpu_gauge = Gauge("CPU使用率 (%)", gradient=rainbow, classes="gauge")
yield self.cpu_gauge
self.mem_gauge = Gauge(
"メモリ使用率 (%)", gradient=rainbow, classes="gauge"
)
yield self.mem_gauge
with Container(classes="gauge"):
yield Static("Network I/O (kB/s)", classes="gauge-label")
self.net_up = Sparkline(id="up")
yield self.net_up
self.net_down = Sparkline(id="down")
yield self.net_down
with Horizontal():
yield Static("↑", classes="arrow")
yield self.net_up
with Horizontal():
yield Static("↓", classes="arrow")
yield self.net_down
yield Footer()
# ---------- ランタイム ----------
async def on_mount(self) -> None:
self.cpu_hist, self.mem_hist = [], []
self.net_hist_up, self.net_hist_down = [], []
self.prev_net = psutil.net_io_counters()
self.set_interval(1.0, self.refresh_stats) # 1 秒ごと更新
async def refresh_stats(self) -> None:
# CPU / MEM
cpu = psutil.cpu_percent()
mem = psutil.virtual_memory().percent
self.cpu_hist.append(cpu)
self.mem_hist.append(mem)
self.cpu_hist, self.mem_hist = self.cpu_hist[-60:], self.mem_hist[-60:]
self.cpu_gauge.percent = cpu
self.mem_gauge.percent = mem
self.cpu_gauge.history = self.cpu_hist
self.mem_gauge.history = self.mem_hist
# NETWORK
now = psutil.net_io_counters()
up = (now.bytes_sent - self.prev_net.bytes_sent) / 1024 # kB/s
down = (now.bytes_recv - self.prev_net.bytes_recv) / 1024
self.prev_net = now
self.net_hist_up.append(up)
self.net_hist_down.append(down)
self.net_hist_up, self.net_hist_down = (
self.net_hist_up[-60:],
self.net_hist_down[-60:],
)
self.net_up.data = self.net_hist_up
self.net_down.data = self.net_hist_down
if __name__ == "__main__":
SystemPulseApp().run()アプリケーションの構成要素
このダッシュボードでは、以下の要素を組み合わせます。
カスタムGaugeウィジェット
- ProgressBarで現在の使用率を表示
- Sparklineで過去60秒の履歴をグラフ表示
- ラベルで数値情報を表示
監視対象
- CPU使用率
- メモリ使用率
- ネットワークトラフィック(上り・下り)
リアルタイム更新
- 1秒ごとの自動更新
- 虹色のプログレスバー(使用率に応じて色が変化)
- 履歴データの蓄積と表示
実装のポイント
レイアウト設計
- Verticalコンテナで各ゲージを縦に配置
- Flexboxライクなレイアウトシステムを活用
- レスポンシブなデザイン
データ取得と更新
psutilを使ったシステム情報の取得- reactive属性による自動UI更新
set_intervalメソッドでの定期実行
視覚的な表現
Gradient.from_colors()での虹色プログレスバー- 使用率に応じた警告色の表示
- 心拍数モニターのようなSparkline表示
このようなシステム監視ツールは、実際の運用現場でも非常に有用です。
特に、SSH経由でリモートサーバーにアクセスして作業する際、軽量でありながら視覚的に分かりやすいツールは作業効率を大幅に向上させます。
まとめ
リンドくんTUIって思った以上に可能性がありますね!これなら自分でも作ってみたくなりました。
たなべその通り!TUIはターミナルの再発明とも言える技術なんだ。
重いGUIアプリを起動する必要もなく、CLIの味気ない画面に我慢する必要もない。まさに現代の開発者が求めていた技術だよね。
今回は、PythonのTextualライブラリを使ったTUI開発について詳しく見てきました。
重要なポイントをおさらいしましょう。
TUIの革新性
- CLIの軽さとGUIの美しさを両立
- 現代のクラウド環境に最適な技術
- SSH接続でのリモート作業に最適
Textualの優位性
- Web開発者にも馴染みやすいCSS記法
- 少ないコードで本格的なアプリケーション
- 充実した開発ツールとデバッグ機能
活用の幅広さ
- システム監視ツール
- データベース管理ツール
- DevOps・SRE現場でのツール開発
- 社内ツールの効率化
Textualの可能性は本当に無限大です。今回紹介したのは基本的な機能のほんの一部に過ぎません。
リアルタイムチャットアプリケーション、ゲーム要素を含んだツール、さらには最近話題のAI技術と組み合わせれば、更に面白いツールが作れそうですね。
あなたも今すぐTUI開発を始めてみませんか?
まずは今回紹介した簡単なHello Worldアプリから始めて、徐々に機能を追加していけば、きっと素晴らしいツールが作れるはずです。





