Textual-vuepy 快速上手
Textual-vuepy 是基于 Vue.py 和 Textual 构建终端 TUI(Text User Interface)应用的组件库。它让你使用 Vue.py 的响应式开发体验来编写运行在终端中的交互式应用:
- 丰富的终端组件 — 直接映射 Textual 所有内置 Widget,开箱即用
- 响应式 SFC 开发 — 与 IPywUI/Panel-vuepy 使用完全相同的
.vue文件语法 - TCSS 样式支持 — 在
<style lang="tcss">块中编写 Textual CSS - VueUse 工具集 —
onKeyStroke、useMouse等键鼠事件组合式函数 - 一键运行 — 通过
vuepy run命令行直接运行.vue文件
安装
sh
pip install 'vuepy-core[textual]'运行应用
命令行运行(推荐)
使用 vuepy run 命令直接运行 .vue 文件(默认使用 textual 后端):
sh
vuepy run App.vue开发模式(启用 Textual devtools):
sh
vuepy run App.vue --dev通过 textual-serve 以 Web 方式访问(需安装 pip install textual-serve):
sh
vuepy run App.vue --servable --serve-port 8000查看所有内置可运行的应用名称:
sh
vuepy run --help代码方式运行
import_sfc
使用插件
python
from vuepy import create_app, import_sfc
App = import_sfc('App.vue')
app = create_app(App, backend='textual')
app.mount()第一个 Textual-vuepy 应用
创建 App.vue 文件:
运行:
sh
vuepy run App.vue与 IPywUI 的关键差异
| 特性 | IPywUI(Jupyter) | Textual-vuepy(终端) |
|---|---|---|
| 安装 | vuepy-core[ipywidgets] | vuepy-core[textual] |
| 运行环境 | JupyterLab | 终端 |
| 后端参数 | backend='ipywidgets'(默认) | backend='textual' |
| 样式语言 | CSS | TCSS(Textual CSS) |
<style> 块 | 不支持 | ✅ 支持 |
| HTML 转义 | ✅ 转义(HTML 渲染) | ❌ 不转义(终端渲染) |
| 访问底层对象 | - | app.tt_app(Textual App 实例) |
| 通知/消息 | - | app.message("text") |
ref 获取底层 Widget
通过 ref 引用组件,再调用 .value.unwrap() 获取底层 Textual Widget 对象:
TCSS 样式
在 <style lang="tcss"> 块中编写 Textual CSS:
也可以在模板中用 :style 绑定内联 TCSS:
vue
<Button :style="f'width: {width.value}fr;'" label="动态宽度" />键鼠事件
VueUse 组合式函数
Textual-vuepy 提供了 VueUse 风格的组合式函数:
vue
<script lang="py">
from textual_vuepy.vueuse import onKeyStroke, useMouse
# 监听键盘按键
def on_ctrl_q(event):
app.tt_app.exit()
onKeyStroke('ctrl+q', on_ctrl_q)
# 追踪鼠标坐标
mouse_x, mouse_y = useMouse()
</script>直接监听键鼠事件
不引入组合式函数时,可以直接在组件上用 @keyup.<按键名> 绑定按键,用 @mouse_move、@click 等绑定鼠标事件:
vue
<template>
<VBox
id="pad"
ref="pad"
:can_focus="True"
@keyup.r="pick('darkred')"
@keyup.g="pick('darkgreen')"
@keyup.comma="mark('comma —— , 的按键名')"
@keyup.ctrl.a="mark('ctrl+a —— 写成 @keyup.ctrl.a')"
@keyup.shift.a="mark('大写 A —— 写成 @keyup.shift.a')"
@mouse_move="on_move"
>
<Label :label="msg.value" />
<Label :label="f'鼠标: ({pos.value[0]}, {pos.value[1]})'" />
</VBox>
</template>
<script lang="py">
from vuepy import onMounted, ref
pad = ref(None)
msg = ref("按 r / g 换色,再试试 , 、ctrl+a 、A")
pos = ref((0, 0))
def pick(color):
app.tt_app.screen.styles.background = color
msg.value = f"背景色: {color}"
def mark(name):
msg.value = f"按下了 {name}"
def on_move(event):
pos.value = (event.screen_x, event.screen_y)
@onMounted
def focus_pad():
pad.value.unwrap().focus() # @keyup 需要该组件持有焦点
</script>
<style lang="tcss">
#pad {
width: 1fr;
height: 1fr;
align: center middle;
}
</style>两个要点:
- 需要焦点:
@keyup是挂在组件上的 Textual binding,只有该组件或它的子组件持有焦点时才触发,所以容器要:can_focus="True"并在onMounted里focus()。想要不依赖焦点的全局快捷键,用onKeyStroke。 - 按键名用
vuepy run keys探测:按下目标按键,日志里name=的值就是要写的名字(,是comma);组合键把分隔修饰键的下划线换成.,ctrl_a写成@keyup.ctrl.a。
按键名规则、焦点冒泡细节和对照表见 组件总览 · @keyup 监听按键。
Provide / Inject
通过 TextualProvides.APP_MIXIN 向 Textual App 注入 Mixin,实现自定义按键处理、命令等:
vue
<script lang="py">
from vuepy.compiler_sfc.codegen_backends.textual import TextualProvides
class KeyHandlerMixin:
def _on_key(self, event) -> None:
# 将按键信息写入日志
log_ref.value.unwrap().write(repr(event))
app.provide(TextualProvides.APP_MIXIN, KeyHandlerMixin)
</script>内置应用
安装 textual_vuepy 后,以下应用已通过 VuepyAppStore 注册,可直接按名称运行:
| 应用名 | 说明 |
|---|---|
playground | 交互式 SFC 编辑器,支持实时预览和文件热重载 |
keys | 按键查看器,显示所有键盘事件信息 |
sh
# 启动内置 Playground
vuepy run playground
# 启动按键查看器
vuepy run keys