Textual 命令面板(Command Palette)完全指南:内置模糊搜索与自定义命令提供器
发布时间:2026/9/19 1:30:42 锦皓数字建站
完全指南:内置模糊搜索与自定义命令提供器`)
Textual 命令面板Command Palette完全指南内置模糊搜索与自定义命令提供器【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textualTextual 为每个应用内置了一个命令面板command palette用户只需按下快捷键即可在一个搜索输入框中快速检索并执行应用内的各种功能无需在菜单或界面中翻找。本文将完整讲解命令面板的启动与交互方式、模糊匹配原理、如何通过get_system_commands与Provider两种机制添加自定义命令、屏幕级命令的作用域规则以及禁用面板和修改快捷键的配置方法读完即可在自己的 Textual 应用中构建类 VS Code 的命令体验。启动命令面板与基本交互Textual 应用默认启用命令面板按下 ctrlp 即可召唤一个模态屏幕其中包含一个输入框和一个可滚动的命令列表。输入关键字时Textual 会实时给出命令建议使用 up / down 在结果中移动高亮按 enter 执行选中的命令按 escape 关闭面板。从命令面板的底层实现看它本身是一个继承自SystemModalScreen的独立屏幕类CommandPalette内部由输入框CommandInput、结果列表CommandList与忙碌指示器LoadingIndicator构成并注册了完整的键盘绑定见 command.py| 按键 | 作用 | | :- | :- | |down/up| 在命令列表上/下移动 | |enter| 执行当前高亮的命令 | |escape| 退出命令面板 | |pagedown/pageup| 整页滚动命令列表 | |ctrlend/shiftend| 跳到最后一个命令 | |ctrlhome/shifthome| 跳到第一个命令 |当结果尚未就绪时面板会显示加载指示器搜索超时 0.5 秒仍无结果时会显示 No matches found 提示相关常量与逻辑见 command.py。模糊搜索为何 th 能找到 Change theme命令的匹配基于模糊搜索fuzzy search只要输入的关键字按顺序出现在命令标题中即可命中而不要求从命令的开头匹配。例如 Change theme 命令输入 ch对应change能命中输入 th对应theme同样能命中。这种方案让用户用更少的按键快速锁定目标命令。模糊匹配的具体实现在textual.fuzzy.Matcher中Matcher.match(candidate)返回一个0到1之间的分数0表示不匹配1表示完全一致中间值表示匹配的置信度Matcher.highlight(candidate)则返回一个带高亮样式的Content对象用于在结果列表中标注命中的字符。评分算法见 fuzzy.py会奖励首字母命中、惩罚字母之间的断档分组且结果带有 LRU 缓存以提升连续输入的响应速度。命令列表最终按分数降序排列见 command.py。系统命令通过 get_system_commands 扩展Textual 应用默认带有一批系统命令system commands它们在App.get_system_commands方法中声明。基类默认提供的命令包括Theme切换当前主题Quit尽快退出应用Keys显示/隐藏按键与组件帮助面板Maximize / Minimize最大化/还原当前聚焦组件仅在可最大化时出现Screenshot将当前屏幕保存为 SVG 截图。要添加自己的系统命令在App子类中定义get_system_commands方法即可。Textual 会以用户召唤命令面板时处于激活状态的Screen为参数调用它。方法中通过yield返回SystemCommand对象其字段为| 字段 | 说明 | | :- | :- | |title| 命令标题参与模糊搜索 | |help| 帮助文本显示在标题下方 | |callback| 用户选中该命令时执行的回调 | |discover| 布尔值默认True为True时即使输入框为空也显示该命令为False时仅在输入内容后才显示 |下面的例子为应用添加一个响铃命令完整代码见 command01.pyfrom typing import Iterable from textual.app import App, SystemCommand from textual.screen import Screen class BellCommandApp(App): An app with a bell command. def get_system_commands(self, screen: Screen) - Iterable[SystemCommand]: yield from super().get_system_commands(screen) # 保留基类默认命令 yield SystemCommand(Bell, Ring the bell, self.bell) # 新增命令 if __name__ __main__: app BellCommandApp() app.run()需要注意的是get_system_commands之所以生效是因为App.COMMANDS类变量默认包含一个懒加载的SystemCommandsProvider见 app.py 与 system_commands.py该 Provider 会遍历get_system_commands的产出并依次做 discover/search。如果你完全替换了COMMANDS集合且未包含该 Provider自定义系统命令将不会出现。命令提供器Provider更高级的集成方式对于更复杂的集成可以自定义命令提供器command provider。做法是定义一个继承command.Provider的类再把它加入App类上的COMMANDS类变量。下面这个示例通过命令面板打开当前目录下的 Python 文件应用初始显示空白屏幕但当你召唤命令面板并输入文件名时会出现 open xxx.py 命令选中即可把文件以语法高亮形式显示在界面上完整代码见 command02.pyfrom __future__ import annotations from functools import partial from pathlib import Path from textual.app import App, ComposeResult from textual.command import Hit, Hits, Provider from textual.containers import VerticalScroll from textual.widgets import Static class PythonFileCommands(Provider): A command provider to open a Python file in the current working directory. def read_files(self) - list[Path]: Get a list of Python files in the current working directory. return list(Path(./).glob(*.py)) async def startup(self) - None: Called once when the command palette is opened, prior to searching. worker self.app.run_worker(self.read_files, threadTrue) self.python_paths await worker.wait() async def search(self, query: str) - Hits: Search for Python files. matcher self.matcher(query) app self.app assert isinstance(app, ViewerApp) for path in self.python_paths: command fopen {str(path)} score matcher.match(command) if score 0: yield Hit( score, matcher.highlight(command), partial(app.open_file, path), helpOpen this file in the viewer, ) class ViewerApp(App): Demonstrate a command source. COMMANDS App.COMMANDS | {PythonFileCommands} # 自定义 Provider 与默认 Provider 合并 def compose(self) - ComposeResult: with VerticalScroll(): yield Static(idcode, expandTrue) def open_file(self, path: Path) - None: Open and display a file with syntax highlighting. from rich.syntax import Syntax syntax Syntax.from_path( str(path), line_numbersTrue, word_wrapFalse, indent_guidesTrue, themegithub-dark, ) self.query_one(#code, Static).update(syntax) if __name__ __main__: app ViewerApp() app.run()Provider 基类提供了focused当前聚焦组件、screen面板被召唤时的激活屏幕、app应用实例与match_style命中高亮样式等属性并提供了便捷方法matcher()用于基于用户输入创建模糊匹配器默认不区分大小写。一个 Provider 可以重写四个方法startup、search、discover和shutdown。它们都应当是协程async def其中只有search是必须实现的其余三个可选。startup 方法搜索前的准备startup在命令面板打开时被调用适合执行搜索之前需要完成的准备工作。在上面的示例中它通过run_worker(..., threadTrue)在后台线程中列出当前工作目录的.py文件再await等待结果从而避免阻塞界面。若startup失败Provider 的初始化会被标记为不成功后续search将不会执行见 command.py。search 方法产出匹配结果search(query)负责查找与用户输入匹配的结果通过yield产生Hit对象。匹配的具体算法由 Provider 作者自行决定官方推荐使用self.matcher(query)获取内建模糊匹配器其match()返回分数0表示无命中应丢弃该候选0表示命中置信度1为完全匹配。Hit对象包含| 字段 | 说明 | | :- | :- | |score| 匹配分数参与结果排序 | |match_display| 命中的显示内容字符串或 Rich 可渲染对象可用matcher.highlight()生成带高亮的版本 | |command| 用户选中该命令时执行的回调 | |text| 命中的纯文本形式非纯文本渲染时建议显式提供 | |help| 可选帮助文本 |示例中回调通过functools.partial绑定文件路径最终调用ViewerApp.open_file。搜索完成后所有 Provider 的命中会被汇入队列并按分数降序批量渲染相关流程见 command.py。discover 方法空输入时的默认建议discover负责在命令面板输入框为空时提供发现命中discovery hits帮助用户发现最重要的命令。它与search的区别在于discover不接受参数不像search那样接收查询字符串discover产出的是DiscoveryHit而非Hit。DiscoveryHit包含显示内容、可选帮助文本和选中后执行的回调它的分数恒为0显示顺序取决于 Provider 产出的顺序见 command.py。由于 discover 命中的是面板打开瞬间就展示的内容应保证生成开销极小耗时较长的候选应留给search。测试 tests/command_palette/test_discover.py 验证了Provider 声明了 discover 命中时面板打开即显示命令列表这一行为。shutdown 方法关闭时的清理shutdown在命令面板关闭时被调用适合优雅地关闭startup中创建的资源文件句柄、连接等。命令面板卸载时会并发调用所有 Provider 的_shutdown见 command.py。错误隔离Provider 异常不会退出应用与 Textual 其他位置不同命令 Provider 中抛出的异常不会导致应用退出。这是刻意设计防止一个损坏的Provider类让整个命令面板不可用。Provider 中的错误会被记录到开发者控制台相关实现见 command.py。屏幕命令作用域限定在当前屏幕除了应用级COMMANDS还可以在Screen子类上定义COMMANDS类变量来关联屏幕级命令见 screen.py。屏幕级命令仅在对应屏幕激活时参与匹配非常适合实现只适用于某个特定屏幕、不适用于整个应用的命令。命令面板的 Provider 集合是应用 Provider 与当前屏幕 Provider 的并集见 command.py。tests/command_palette/test_declare_sources.py 中的多个用例验证了这一点未声明任何 Provider 时默认只有SystemCommandsProvider应用与屏幕各自声明的 Provider 会正确合并通过构造参数直接注入 Provider 时则只使用注入的集合。禁用命令面板命令面板默认启用。如果不需要它在应用类上设置ENABLE_COMMAND_PALETTE False即可class NoPaletteApp(App): ENABLE_COMMAND_PALETTE False该开关定义于 app.py。在App.__init__中只有ENABLE_COMMAND_PALETTE为真且未自定义同名绑定键时才会自动注册打开面板的快捷键见 app.py因此关闭面板的同时相关按键也不会再被占用。修改打开命令面板的快捷键打开命令面板的默认按键由类变量COMMAND_PALETTE_BINDING控制默认值为ctrlp见 app.py。0.77.0 版本之前Textual 使用ctrlbackslash作为默认绑定如需恢复旧绑定class NewPaletteBindingApp(App): COMMAND_PALETTE_BINDING ctrlbackslash若还想定制页脚Footer中该按键的显示文本可使用COMMAND_PALETTE_DISPLAY类变量默认None表示使用按键默认显示方式见 app.py。小结Textual 的命令面板提供了一套开箱即用的快速命令入口默认的ctrlp快捷键、基于Matcher的模糊搜索、按分数排序的结果列表以及App与Screen两个层级的 Provider 声明机制。扩展方式可以总结为两条路径轻量级在App中重写get_system_commands并yield若干SystemCommand重量级继承Provider实现startup/search/discover/shutdown再注册进COMMANDS。配合ENABLE_COMMAND_PALETTE与COMMAND_PALETTE_BINDING两个类变量可以按需开关面板或自定义快捷键。文中所有示例均为仓库中真实可运行的代码见 command01.py 与 command02.py相关的生命周期、排序与错误隔离实现可进一步阅读 src/textual/command.py、src/textual/fuzzy.py 与 src/textual/system_commands.py 的源码。【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textual创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。