直接跳到内容

Textual-vuepy 组件总览 ​

Textual-vuepy 将 Textual 全部内置 Widget 封装为 Vue.py 组件,让你在 .vue SFC 文件中以声明式方式构建终端 TUI 应用。

安装:pip install 'vuepy-core[textual]'

导入:在 create_app 时指定 backend='textual',vtextual 插件将自动注册所有组件。


布局组件 ​

组件说明常用属性
VBox垂直容器,子组件从上到下排列style, id
HBox水平容器,子组件从左到右排列style, id
SlotVBox 的别名,用于 slot 占位—
Collapsible可折叠容器title, collapsed
TabbedContent标签页容器(配合 TabPane)initial
TabPane标签页内容区域title, id
ContentSwitcher根据 ID 切换显示内容initial

基础组件 ​

组件v-model 默认绑定说明常用属性
Buttonlabel按钮label, disabled, variant
Labellabel文本标签,支持 Rich markuplabel
Staticrenderable静态文本/Rich 渲染renderable, markup
Linkurl超链接url, label
Placeholder—占位符label

表单组件 ​

组件v-model 默认绑定说明常用属性
Inputvalue单行输入框value, placeholder, password, disabled, compact
TextAreatext多行文本编辑器text, language, code_editor
MaskedInputvalue格式化输入框(掩码)template, value
Checkboxvalue复选框label, value, disabled
RadioButtonvalue单选按钮label, value
RadioSetpressed单选按钮组—
Selectvalue下拉选择框options, value, prompt
SelectionListselected多选列表—
Switchvalue开关value, animate

数据展示 ​

组件v-model 默认绑定说明常用属性
DataTabledata数据表格cols, rows
MarkdownmarkdownMarkdown 渲染markdown
MarkdownViewermarkdownMarkdown 查看器(含目录)markdown, show_table_of_contents
PrettyobjectPython 对象格式化展示object
Sparklinedata迷你折线图data, summary_function
Digitsvalue大号数字显示(类 LCD)value
RichLoglines富文本滚动日志markup, wrap, highlight
Loglines纯文本滚动日志—

组件说明常用属性
Tabs顶部标签页导航active
Tab单个标签(在 Tabs 内使用)label, id
ListView可滚动列表视图initial_index
ListItem列表项(在 ListView 内使用)—
OptionList可选项列表—
Option选项项(在 OptionList 内使用)prompt, id
SelectionList多选列表—
Selection多选项(在 SelectionList 内使用)prompt, value
Tree树形结构label
DirectoryTree目录树path

反馈组件 ​

组件说明常用属性 / 方法
Dialog模态对话框ref 调用 .open() / .close(),或 v-model:value
LoadingIndicator加载动画指示器—
ProgressBar进度条progress, total, show_eta
Tooltip工具提示message

结构/布局辅助 ​

组件说明
Header应用顶部标题栏
Footer应用底部状态栏(显示快捷键)
HelpPanel侧边帮助面板
KeyPanel按键面板(显示已注册的快捷键)
Rule水平分隔线
Welcome欢迎页面
Display直接显示已有的 Textual Widget 实例

自定义 SFC 组件 ​

Textual-vuepy 提供两个开箱即用的 SFC 自定义组件(位于 textual_vuepy/components/):

ShimmerText — 闪光文字 ​

显示带有流光高亮动画的文字(适合 AI 思考状态展示)。

Prop类型默认值说明
textstr"Thinking"显示的文字
highlight_widthint3高亮区域宽度(字符数)
intervalfloat0.1动画帧间隔(秒)
runningboolFalse是否运行动画

Spinner — 加载旋转器 ​

显示带旋转字符动画的加载指示器。

Prop类型默认值说明
textstr"Loading..."旋转符号后的文字
intervalfloat0.1动画帧间隔(秒)
runningboolFalse是否运行动画
stylestr""TCSS 样式字符串

内置应用(VuepyAppStore) ​

安装 textual_vuepy 后自动注册以下应用,可通过 vuepy run <name> 启动:

名称说明
playground交互式 SFC 编辑器 + 实时预览,支持拖拽分隔条、文件热重载、目录树
keys按键事件查看器,显示每次按键的 Key 事件详情
sh
vuepy run playground  # 启动 SFC Playground
vuepy run keys        # 启动按键查看器

VueUse 组合式函数 ​

从 textual_vuepy.vueuse 导入:

函数说明返回值
onKeyStroke(key, cb)注册全局按键监听(在 onMounted 后生效),cb 不接收参数—
useMouse()追踪鼠标屏幕坐标(x: Ref[int], y: Ref[int])

onKeyStroke 的 key 用的是 Textual 按键名(, 要写成 comma),可以用 vuepy run keys 探测; 被 Textual 占用的键(ctrl+c / ctrl+q / ctrl+p)监听不到。详见 VueUse 组合式函数。


事件处理 ​

Textual-vuepy 支持 Textual 原生事件,通过 @事件名 绑定:

事件名使用蛇形命名(snake_case),对应 Textual Message 类名转小写,如:

  • Input.Submitted → @input_submitted
  • DirectoryTree.FileSelected → @directory_tree_file_selected
  • Button.Pressed → @click(Button 有简化别名)

@keyup 监听按键 ​

按键事件不走 Message 名,而是用 @keyup.<按键名> 绑定,修饰键以 . 连接、最后一段是主键:

底层是 Textual 的 widget binding:@keyup.ctrl.shift.c 会在该组件上注册 ctrl+shift+c 绑定,按键触发时调用对应表达式。

用 vuepy run keys 探测按键名 ​

按键名是 Textual 的名字,写错了不会报错、只是不触发。不确定时用内置的 keys 应用探一下:

sh
vuepy run keys

按下目标按键或组合键,日志会打印本次的 Key 事件,其中 name= 的值就是写进 @keyup. 的按键名:

text
Key(key='comma', character=',', name='comma', is_printable=True)
Key(key='question_mark', character='?', name='question_mark', is_printable=True)
Key(key='ctrl+a', character=None, name='ctrl_a', is_printable=False)
Key(key='ctrl+shift+c', character=None, name='ctrl_shift_c', is_printable=False)
Key(key='f5', character=None, name='f5', is_printable=False)

对应写法:

实际按键keys 应用里的 name=模板写法
,comma@keyup.comma
?question_mark@keyup.question_mark
[left_square_bracket@keyup.left_square_bracket
f5f5@keyup.f5
大写 Aupper_a@keyup.shift.a
ctrl+actrl_a@keyup.ctrl.a
ctrl+shift+cctrl_shift_c@keyup.ctrl.shift.c

规则:把 name= 里分隔修饰键的下划线换成 .,按键名自身的下划线原样保留(question_mark、left_square_bracket)。@keyup.ctrl_a 这种写法不会报错也不会触发,必须写成 @keyup.ctrl.a。

大写字母是唯一的例外:keys 应用显示 name='upper_a',但模板里要写 @keyup.shift.a(@keyup.upper_a 不触发)。

完整的符号名对照表见 VueUse 文档(onKeyStroke 与 @keyup 用的是同一套按键名,区别只是 onKeyStroke 直接写 'ctrl+a')。

焦点决定能不能触发 ​

widget binding 只在该组件自身或它的子组件持有焦点时被检查,因此:

  • 容器默认不可聚焦,要加 :can_focus="True",并在 onMounted 里主动 focus()。应用启动时焦点默认落在最外层滚动容器上,它是你这个容器的祖先而不是子组件,不会触发绑定;
  • 按键沿焦点链向祖先冒泡,所以把 @keyup 挂在外层容器、焦点停在里面的 Button 上也能触发;
  • 焦点在 Input / TextArea 上时,可打印字符被输入控件消费,只有组合键能冒泡到祖先容器;
  • 什么都没聚焦时不触发;需要全局快捷键请改用 onKeyStroke(注册在 App 上)。

Textual 占用的按键

ctrl+c(提示退出)、ctrl+q(退出应用)、ctrl+p(命令面板)被 Textual 自己占用,监听不到,挑其他键即可(例如 ctrl+t)。


深入了解 ​

Textual-vuepy 组件总览已经加载完毕