直接跳到内容

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'
样式语言CSSTCSS(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

下一步 ​

Textual-vuepy 快速上手已经加载完毕