"
命令行也能长出按钮和输入框,Textual 让写软件不再需要前端。
提到「做个带界面的小程序」,大多数人第一反应是:装个 PyQt、写点 HTML,或者干脆放弃。但有一个 Python 库,让你只用终端,就能写出带按钮、表格、输入框、甚至鼠标交互的全屏应用——它就是 Textual。
📌 本文看点
01
什么是 Textual
02
四大核心能力
03
动手写待办应用
WHAT IS IT
Textual 是 Rich(就是你之前认识的那个命令行美化库)作者 Will McGugan打造的一个 TUI 框架(终端用户界面)。
它的核心思路很妙:用 Python 写逻辑,用 CSS 写样式。你写的代码跑在终端里,但呈现出来的却是一个有布局、有组件、能点能输的「软件界面」。
为什么要选它,而不是传统方案?
PyQt / Tkinter
不用装庞大的 GUI 运行环境,打包分发都更轻。
Web 框架
不需要浏览器、不需要前端三件套,一个终端窗口就够。
跨平台
Linux / macOS / Windows 的终端(甚至 Windows Terminal)都能跑,而且原生支持鼠标和键盘。
换句话说,当你只想给团队做一个内部小工具、给运维做一个监控面板、或者给自己写个本地效率工具时,Textual 是那条成本最低、交付最快的路。
CORE FEATURES
1. 类 CSS 的样式系统
Textual 用一套和网页 CSS 很像的语法来排版终端组件:可以设置颜色、边距、对齐,还支持 grid和 flex布局。最爽的是——它支持热重载,改完样式保存,界面立刻更新,不用重启。
2. 开箱即用的组件库
按钮 Button、输入框 Input、数据表格 DataTable、树 Tree、标签页 TabbedContent、进度条、甚至是 Markdown 渲染组件,全都内置。你几乎不用从零画组件。
3. 事件驱动
像前端一样响应用户操作:on_button_pressed(按钮被点)、on_input_submitted(输入框回车)、on_mount(界面加载完成)……逻辑清晰,好维护。
4. 异步友好
底层基于 asyncio,耗时的网络请求、文件读写放在后台,界面也不会卡住。
GET STARTED
安装只要一行:
pip install textual
装完可以先体验官方自带的组件大赏,直接在终端里跑起来看效果:
python -m textual
这会打开一个展示各种组件和样式的演示界面,非常适合「先看再学」。

最小可运行例子长这样——一个会响应点击的窗口:
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Static, Button
class HelloApp(App):
def compose(self) -> ComposeResult:
yield Header()
yield Static("Hello, Textual!",id="msg")
yield Button("点我",id="btn")
yield Footer()
def on_button_pressed(self, event: Button.Pressed) -> None:
self.query_one("#msg", Static).update("你点到我了!")
HelloApp().run()

保存为 app.py,执行 python app.py,一个带标题栏、正文和按钮的终端窗口就出现了。
CASE STUDY
下面这个例子稍微完整一点——一个能在终端里增删待办的小应用,覆盖输入、按钮、列表三种组件。复制即可运行:
from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.widgets import Header, Footer, Input, Button, ListView, ListItem, Label
class TodoApp(App):
CSS = """
#row { height: 3; }
Input { width: 1fr; }
Button { width: 12; }
"""
def compose(self) -> ComposeResult:
yield Header()
yield Vertical(
Input(placeholder="输入待办,回车添加",id="todo-input"),
Button("添加",id="add-btn"),
ListView(id="todo-list"),
)
yield Footer()
def on_button_pressed(self, event: Button.Pressed) -> None:
self._add()
def on_input_submitted(self, event: Input.Submitted) -> None:
self._add()
def _add(self) -> None:
inp = self.query_one("#todo-input", Input)
text = inp.value.strip()
if not text:
return
self.query_one("#todo-list", ListView).append(
ListItem(Label(text))
)
inp.value = ""
if __name__ == "__main__":
TodoApp().run()

「运行后,在输入框打字、回车或点「添加」,待办就会实时出现。短短三十多行,一个能用的小工具就成型了——这正是 Textual 的魅力:把「写界面」的门槛,降到和普通脚本一样低。」
THE END
Textual 适合那些「不想碰前端、却又想要个界面」的场景:内部运维面板、本地数据查看器、CLI 工具的增强版外壳。它不取代 Web 或桌面 GUI,但在「轻量交付」这个区间里,几乎没有对手。
「如果你已经用熟了 Rich,那 Textual 几乎是顺理成章的下一步——毕竟,会画彩色文字,下一步自然是想画整个界面。」
如果你觉得今天这篇有收获,欢迎点赞、在看、转发三连,我们下篇见。