Loading...
Loading...
Compare original and translation side by side
AppAppapp.run()CSS_PATHCSScompose()on_*app.run()CSS_PATHCSScompose()on_*from textual.reactive import reactive
class Counter(Widget):
count = reactive(0) # Auto-refreshes on change
def render(self) -> str:
return f"Count: {self.count}"validate_<attr>()watch_<attr>()compute_<attr>()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>()Button {
background: $primary;
margin: 1;
}
#submit-button {
background: $success;
}
.danger {
background: $error;
}Button {
background: $primary;
margin: 1;
}
#submit-button {
background: $success;
}
.danger {
background: $error;
}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()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()undefinedundefineddef update_value(self) -> None:
self.post_message(self.Updated(self.value))def update_value(self) -> None:
self.post_message(self.Updated(self.value))undefinedundefinedimport 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)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)dock: top/bottom/left/right1frVerticalHorizontalGriddock: top/bottom/left/right1frVerticalHorizontalGridundefinedundefined
**Separate concerns:**
```python
**关注点分离:**
```python
**External CSS for apps:**
```python
class MyApp(App):
CSS_PATH = "app.tcss" # Enables live reload
**应用使用外部CSS:**
```python
class MyApp(App):
CSS_PATH = "app.tcss" # 支持实时重载Static@lru_cacheStatic@lru_cachecan_focus = True$primary$errorcan_focus = True$primary$errorundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedtextual consoletextual run --dev my_app.pyfrom textual import log
log("Debug message", locals())textual consoletextual run --dev my_app.pyfrom textual import log
log("调试消息", locals())undefinedundefinedundefinedundefinedproject/
├── 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.tomlproject/
├── 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.tomlButtonCheckboxInputRadioButtonSelectSwitchTextAreaLabelStaticPrettyMarkdownMarkdownViewerDataTableListViewTreeDirectoryTreeHeaderFooterTabsTabbedContentVerticalHorizontalGridButtonCheckboxInputRadioButtonSelectSwitchTextAreaLabelStaticPrettyMarkdownMarkdownViewerDataTableListViewTreeDirectoryTreeHeaderFooterTabsTabbedContentVerticalHorizontalGriddef __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."""
passdef __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/* 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;
}/* 停靠布局 */
#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;
}