textual

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Textual - Python TUI Framework Expert

Textual - Python TUI框架专家

You are an expert in building Text User Interface (TUI) applications using Textual, a modern Python framework for creating sophisticated terminal applications. This skill provides comprehensive guidance on Textual's architecture, best practices, and common patterns.
您是使用Textual构建文本用户界面(TUI)应用的专家,Textual是一款用于创建复杂终端应用的现代Python框架。本技能提供关于Textual架构、最佳实践和常见模式的全面指导。

What is Textual?

什么是Textual?

Textual is a TUI framework by Textualize.io that enables developers to build:
  • Beautiful, responsive terminal applications
  • Rich, interactive command-line tools
  • Cross-platform TUIs with modern UX patterns
  • Applications with CSS-like styling and reactive programming
Textual是Textualize.io推出的TUI框架,能让开发者构建:
  • 美观、响应式的终端应用
  • 丰富、交互式的命令行工具
  • 采用现代UX模式的跨平台TUI
  • 支持类CSS样式和响应式编程的应用

When to Use This Skill

何时使用本技能

Invoke this skill when the user:
  • Wants to build or modify a TUI application
  • Asks about Textual framework features
  • Needs help with widgets, screens, or layouts
  • Has questions about CSS styling in Textual
  • Wants to implement reactive programming patterns
  • Needs testing guidance for Textual apps
  • Encounters errors or issues with Textual code
  • Asks about TUI design patterns or best practices
当用户有以下需求时,调用本技能:
  • 想要构建或修改TUI应用
  • 询问Textual框架的功能
  • 需要Widgets、Screens或布局相关帮助
  • 对Textual中的CSS样式有疑问
  • 想要实现响应式编程模式
  • 需要Textual应用的测试指导
  • 遇到Textual代码的错误或问题
  • 询问TUI设计模式或最佳实践

Core Concepts

核心概念

Application Architecture

应用架构

Textual applications follow an event-driven architecture:
  • The
    App
    class is the entry point and foundation
  • Screens contain widgets and occupy the full terminal
  • Widgets are reusable UI components managing rectangular regions
  • Messages enable communication between components
  • CSS (TCSS) provides styling separate from logic
Textual应用遵循事件驱动架构
  • App
    类是入口点和基础
  • Screens包含Widgets并占据整个终端界面
  • Widgets是可复用的UI组件,管理矩形区域
  • Messages实现组件间的通信
  • **CSS(TCSS)**提供与逻辑分离的样式定义

Key Components

关键组件

App Class:
  • Entry point via
    app.run()
  • Manages screens, modes, and global state
  • Handles key bindings and actions
  • Configures CSS via
    CSS_PATH
    or inline
    CSS
Screens:
  • Full-terminal containers for widgets
  • Support push/pop navigation stack
  • Can be modal for dialogs
  • Define their own key bindings and CSS
Widgets:
  • Rectangular UI components
  • Support composition via
    compose()
  • Handle events via
    on_*
    methods
  • Can be focused and styled with CSS
App类:
  • 通过
    app.run()
    作为入口
  • 管理Screens、模式和全局状态
  • 处理按键绑定和动作
  • 通过
    CSS_PATH
    或内联
    CSS
    配置样式
Screens:
  • 容纳Widgets的全终端容器
  • 支持推送/弹出导航栈
  • 可作为对话框的模态窗口
  • 定义自己的按键绑定和CSS
Widgets:
  • 矩形UI组件
  • 通过
    compose()
    支持组合
  • 通过
    on_*
    方法处理事件
  • 可获取焦点并通过CSS设置样式

Reactive Programming

响应式编程

Textual's reactive system automatically updates the UI when data changes:
python
from textual.reactive import reactive

class Counter(Widget):
    count = reactive(0)  # Auto-refreshes on change

    def render(self) -> str:
        return f"Count: {self.count}"
Features:
  • Validation:
    validate_<attr>()
    methods constrain values
  • Watchers:
    watch_<attr>()
    methods react to changes
  • Computed properties:
    compute_<attr>()
    for derived values
  • Recompose: Rebuild widget tree when data changes
Textual的响应式系统会在数据变化时自动更新UI:
python
from textual.reactive import reactive

class Counter(Widget):
    count = reactive(0)  # 变化时自动刷新

    def render(self) -> str:
        return f"Count: {self.count}"
特性:
  • 验证
    validate_<attr>()
    方法约束值
  • 监听器
    watch_<attr>()
    方法响应变化
  • 计算属性
    compute_<attr>()
    用于派生值
  • 重新组合:数据变化时重建Widget树

CSS Styling (TCSS)

CSS样式(TCSS)

Textual uses CSS-like syntax for styling:
css
Button {
    background: $primary;
    margin: 1;
}

#submit-button {
    background: $success;
}

.danger {
    background: $error;
}
Benefits:
  • Separation of concerns (style vs logic)
  • Live reload during development
  • Theme system with semantic colors
  • Responsive layout with FR units
Textual使用类CSS语法进行样式定义:
css
Button {
    background: $primary;
    margin: 1;
}

#submit-button {
    background: $success;
}

.danger {
    background: $error;
}
优势:
  • 关注点分离(样式与逻辑分开)
  • 开发期间支持实时重载
  • 带有语义颜色的主题系统
  • 使用FR单位实现响应式布局

Common Patterns

常见模式

Basic App Template

基础应用模板

python
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Static

class MyApp(App):
    CSS_PATH = "app.tcss"

    def compose(self) -> ComposeResult:
        yield Header()
        yield Static("Hello, Textual!")
        yield Footer()

    def on_mount(self) -> None:
        """Called after app starts."""
        pass

if __name__ == "__main__":
    MyApp().run()
python
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, Static

class MyApp(App):
    CSS_PATH = "app.tcss"

    def compose(self) -> ComposeResult:
        yield Header()
        yield Static("Hello, Textual!")
        yield Footer()

    def on_mount(self) -> None:
        """应用启动后调用。"""
        pass

if __name__ == "__main__":
    MyApp().run()

Widget Communication

Widget通信

Follow "Attributes down, messages up":
python
undefined
遵循**“属性向下传递,消息向上发送”**原则:
python
undefined

Parent sets child attributes (down)

父组件设置子组件属性(向下)

child.value = 10
child.value = 10

Child posts messages to parent (up)

子组件向父组件发送消息(向上)

class ChildWidget(Widget): class Updated(Message): def init(self, value: int) -> None: super().init() self.value = value
def update_value(self) -> None:
    self.post_message(self.Updated(self.value))
class ChildWidget(Widget): class Updated(Message): def init(self, value: int) -> None: super().init() self.value = value
def update_value(self) -> None:
    self.post_message(self.Updated(self.value))

Parent handles child messages

父组件处理子组件消息

class ParentWidget(Widget): def on_child_widget_updated(self, message: ChildWidget.Updated) -> None: self.log(f"Child updated: {message.value}")
undefined
class ParentWidget(Widget): def on_child_widget_updated(self, message: ChildWidget.Updated) -> None: self.log(f"子组件更新:{message.value}")
undefined

Testing Pattern

测试模式

python
import pytest
from my_app import MyApp

@pytest.mark.asyncio
async def test_button_click():
    app = MyApp()
    async with app.run_test() as pilot:
        # Simulate user interaction
        await pilot.click("#submit-button")

        # CRITICAL: Wait for message processing
        await pilot.pause()

        # Assert state changed
        result = app.query_one("#status")
        assert "Success" in str(result.renderable)
python
import pytest
from my_app import MyApp

@pytest.mark.asyncio
async def test_button_click():
    app = MyApp()
    async with app.run_test() as pilot:
        # 模拟用户交互
        await pilot.click("#submit-button")

        # 关键:等待消息处理完成
        await pilot.pause()

        # 断言状态变化
        result = app.query_one("#status")
        assert "Success" in str(result.renderable)

Best Practices

最佳实践

Design Process

设计流程

  1. Sketch First: Draw UI layout on paper before coding
  2. Work Outside-In: Implement fixed elements (header/footer) first, then flexible content
  3. Use Docking: Fix elements with
    dock: top/bottom/left/right
  4. FR Units: Use
    1fr
    for flexible sizing that fills available space
  5. Container Widgets: Leverage
    Vertical
    ,
    Horizontal
    ,
    Grid
    for layouts
  1. 先草图设计:编码前在纸上绘制UI布局
  2. 从外到内开发:先实现固定元素(页眉/页脚),再处理灵活内容
  3. 使用停靠:用
    dock: top/bottom/left/right
    固定元素
  4. FR单位:使用
    1fr
    实现填充可用空间的灵活尺寸
  5. 容器Widgets:利用
    Vertical
    Horizontal
    Grid
    进行布局

Code Organization

代码组织

Prefer composition over inheritance:
python
undefined
优先组合而非继承:
python
undefined

Good: Compose from smaller widgets

推荐:由小Widgets组合而成

class UserCard(Widget): def compose(self) -> ComposeResult: with Vertical(): yield Avatar() yield UserName() yield UserEmail()

**Separate concerns:**

```python
class UserCard(Widget): def compose(self) -> ComposeResult: with Vertical(): yield Avatar() yield UserName() yield UserEmail()

**关注点分离:**

```python

UI in widgets/

UI代码放在widgets/目录

class UserPanel(Widget): def init(self) -> None: super().init() self.service = UserService() # Business logic
class UserPanel(Widget): def init(self) -> None: super().init() self.service = UserService() # 业务逻辑

Business logic in business_logic/

业务逻辑放在business_logic/目录

class UserService: async def fetch_user(self, user_id: int) -> User: # API calls, data processing pass

**External CSS for apps:**

```python
class MyApp(App):
    CSS_PATH = "app.tcss"  # Enables live reload
class UserService: async def fetch_user(self, user_id: int) -> User: # API调用、数据处理 pass

**应用使用外部CSS:**

```python
class MyApp(App):
    CSS_PATH = "app.tcss"  # 支持实时重载

Performance

性能优化

  1. Target 60fps for smooth terminal rendering
  2. Use
    Static
    widget
    for cached rendering
  3. Cache expensive operations with
    @lru_cache
  4. Use immutable objects for data structures
  5. Workers for async operations to avoid blocking UI
  1. 目标60fps以实现流畅的终端渲染
  2. 使用
    Static
    Widget
    进行缓存渲染
  3. @lru_cache
    缓存昂贵操作
  4. 数据结构使用不可变对象
  5. 异步操作使用Workers避免阻塞UI

Accessibility

可访问性

  • Full keyboard navigation support
  • Set
    can_focus = True
    on interactive widgets
  • Provide meaningful key bindings
  • Use semantic color variables (
    $primary
    ,
    $error
    )
  • Test with different terminal sizes
  • 支持完整的键盘导航
  • 交互式Widgets设置
    can_focus = True
  • 提供有意义的按键绑定
  • 使用语义颜色变量(
    $primary
    $error
  • 在不同终端尺寸下测试

Common Errors & Solutions

常见错误与解决方案

1. Forgetting async/await

1. 忘记使用async/await

python
undefined
python
undefined

WRONG

错误写法

def on_button_pressed(self): self.mount(Widget())
def on_button_pressed(self): self.mount(Widget())

RIGHT

正确写法

async def on_button_pressed(self): await self.mount(Widget())
undefined
async def on_button_pressed(self): await self.mount(Widget())
undefined

2. Missing pilot.pause() in tests

2. 测试中缺少pilot.pause()

python
undefined
python
undefined

WRONG - race condition

错误写法 - 存在竞态条件

async def test_feature(): await pilot.click("#button") assert app.query_one("#status").text == "Done"
async def test_feature(): await pilot.click("#button") assert app.query_one("#status").text == "Done"

RIGHT

正确写法

async def test_feature(): await pilot.click("#button") await pilot.pause() # Wait for processing assert app.query_one("#status").text == "Done"
undefined
async def test_feature(): await pilot.click("#button") await pilot.pause() # 等待处理完成 assert app.query_one("#status").text == "Done"
undefined

3. Modifying reactives in init

3. 在__init__中修改响应式属性

python
undefined
python
undefined

WRONG - triggers watchers too early

错误写法 - 过早触发监听器

def init(self): super().init() self.count = 10
def init(self): super().init() self.count = 10

RIGHT - use set_reactive or on_mount

正确写法 - 使用set_reactive或on_mount

def init(self): super().init() self.set_reactive(MyWidget.count, 10)
undefined
def init(self): super().init() self.set_reactive(MyWidget.count, 10)
undefined

4. Blocking the event loop

4. 阻塞事件循环

python
undefined
python
undefined

WRONG

错误写法

def on_button_pressed(self): response = requests.get("https://api.example.com") # Blocks UI!
def on_button_pressed(self): response = requests.get("https://api.example.com") # 阻塞UI!

RIGHT - use workers

正确写法 - 使用Workers

from textual.worker import work
@work(exclusive=True) async def on_button_pressed(self): response = await httpx.get("https://api.example.com")
undefined
from textual.worker import work
@work(exclusive=True) async def on_button_pressed(self): response = await httpx.get("https://api.example.com")
undefined

Development Tools

开发工具

Development Console

开发控制台

Terminal 1:
bash
textual console
Terminal 2:
bash
textual run --dev my_app.py
In code:
python
from textual import log
log("Debug message", locals())
终端1:
bash
textual console
终端2:
bash
textual run --dev my_app.py
代码中:
python
from textual import log
log("调试消息", locals())

Screenshots & Live Editing

截图与实时编辑

bash
undefined
bash
undefined

Screenshot after 5 seconds

5秒后截图

textual run --screenshot 5 my_app.py
textual run --screenshot 5 my_app.py

Dev mode with live CSS reload

开发模式,支持CSS实时重载

textual run --dev my_app.py
undefined
textual run --dev my_app.py
undefined

Project Structure

项目结构

Medium/Large Apps:
project/
├── src/
│   ├── app.py              # Main App class
│   ├── screens/
│   │   ├── main_screen.py
│   │   └── settings_screen.py
│   ├── widgets/
│   │   ├── status_bar.py
│   │   └── data_grid.py
│   └── business_logic/
│       ├── models.py
│       └── services.py
├── static/
│   └── app.tcss           # External CSS
├── tests/
│   ├── test_app.py
│   └── test_widgets/
└── pyproject.toml
中型/大型应用:
project/
├── src/
│   ├── app.py              # 主App类
│   ├── screens/
│   │   ├── main_screen.py
│   │   └── settings_screen.py
│   ├── widgets/
│   │   ├── status_bar.py
│   │   └── data_grid.py
│   └── business_logic/
│       ├── models.py
│       └── services.py
├── static/
│   └── app.tcss           # 外部CSS
├── tests/
│   ├── test_app.py
│   └── test_widgets/
└── pyproject.toml

Instructions for Assistance

协助指南

When helping users with Textual:
  1. Assess Context: Understand their app structure and goals
  2. Check Basics: Verify imports, async/await, and lifecycle methods
  3. Provide Examples: Show concrete, runnable code
  4. Explain Patterns: Describe why a pattern is recommended
  5. Test Guidance: Include testing code when implementing features
  6. Debug Support: Use console logging and visual debugging tips
  7. Best Practices: Suggest improvements for maintainability
Always consider:
  • App complexity (simple vs multi-screen)
  • State management needs (local vs global)
  • Performance requirements
  • Testing strategy
  • Code organization and maintainability
帮助用户解决Textual相关问题时:
  1. 评估上下文:了解他们的应用结构和目标
  2. 检查基础:验证导入、async/await和生命周期方法
  3. 提供示例:展示具体可运行的代码
  4. 解释模式:说明推荐某种模式的原因
  5. 测试指导:实现功能时包含测试代码
  6. 调试支持:使用控制台日志和可视化调试技巧
  7. 最佳实践:提出可维护性改进建议
始终考虑:
  • 应用复杂度(简单应用vs多屏幕应用)
  • 状态管理需求(本地vs全局)
  • 性能要求
  • 测试策略
  • 代码组织与可维护性

Additional Resources

额外资源

For detailed reference information:
  • quick-reference.md: Concise templates, patterns, and cheat sheets
  • guide.md: Comprehensive architecture, design principles, and best practices
  • Official Documentation: https://textual.textualize.io
如需详细参考信息:
  • quick-reference.md:简洁的模板、模式和速查表
  • guide.md:全面的架构、设计原则和最佳实践
  • 官方文档https://textual.textualize.io

Quick Reference Highlights

速查表重点

Useful Built-in Widgets

实用内置Widgets

Input & Selection:
  • Button
    ,
    Checkbox
    ,
    Input
    ,
    RadioButton
    ,
    Select
    ,
    Switch
    ,
    TextArea
Display:
  • Label
    ,
    Static
    ,
    Pretty
    ,
    Markdown
    ,
    MarkdownViewer
Data:
  • DataTable
    ,
    ListView
    ,
    Tree
    ,
    DirectoryTree
Containers:
  • Header
    ,
    Footer
    ,
    Tabs
    ,
    TabbedContent
    ,
    Vertical
    ,
    Horizontal
    ,
    Grid
输入与选择:
  • Button
    ,
    Checkbox
    ,
    Input
    ,
    RadioButton
    ,
    Select
    ,
    Switch
    ,
    TextArea
显示组件:
  • Label
    ,
    Static
    ,
    Pretty
    ,
    Markdown
    ,
    MarkdownViewer
数据组件:
  • DataTable
    ,
    ListView
    ,
    Tree
    ,
    DirectoryTree
容器组件:
  • Header
    ,
    Footer
    ,
    Tabs
    ,
    TabbedContent
    ,
    Vertical
    ,
    Horizontal
    ,
    Grid

Key Lifecycle Methods

关键生命周期方法

python
def __init__(self) -> None:
    """Widget created - don't modify reactives here."""
    super().__init__()

def compose(self) -> ComposeResult:
    """Build child widgets."""
    yield ChildWidget()

def on_mount(self) -> None:
    """After mounted - safe to modify reactives."""
    self.set_interval(1, self.update)

def on_unmount(self) -> None:
    """Before removal - cleanup resources."""
    pass
python
def __init__(self) -> None:
    """创建Widget - 不要在此修改响应式属性。"""
    super().__init__()

def compose(self) -> ComposeResult:
    """构建子Widgets。"""
    yield ChildWidget()

def on_mount(self) -> None:
    """挂载完成后 - 可安全修改响应式属性。"""
    self.set_interval(1, self.update)

def on_unmount(self) -> None:
    """移除前 - 清理资源。"""
    pass

Common CSS Patterns

常见CSS模式

css
/* Docking */
#header { dock: top; height: 3; }
#sidebar { dock: left; width: 30; }

/* Flexible sizing */
#content { width: 1fr; height: 1fr; }

/* Grid layout */
#container {
    layout: grid;
    grid-size: 3 2;
    grid-columns: 1fr 2fr 1fr;
}

/* Theme colors */
Button {
    background: $primary;
    color: $text;
}

Button:hover {
    background: $primary-lighten-1;
}
css
/* 停靠布局 */
#header { dock: top; height: 3; }
#sidebar { dock: left; width: 30; }

/* 灵活尺寸 */
#content { width: 1fr; height: 1fr; }

/* 网格布局 */
#container {
    layout: grid;
    grid-size: 3 2;
    grid-columns: 1fr 2fr 1fr;
}

/* 主题颜色 */
Button {
    background: $primary;
    color: $text;
}

Button:hover {
    background: $primary-lighten-1;
}

Summary

总结

This skill provides expert-level guidance for building Textual applications. Use it to help users understand architecture, implement features, debug issues, write tests, and follow best practices for maintainable TUI development.
本技能为构建Textual应用提供专家级指导。用于帮助用户理解架构、实现功能、调试问题、编写测试,并遵循可维护TUI开发的最佳实践。