ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Rich 终端控制码详解:Control 渲染对象与 ANSI 控制序列实战指南

Rich 终端控制码详解:Control 渲染对象与 ANSI 控制序列实战指南 Rich 终端控制码详解Control 渲染对象与 ANSI 控制序列实战指南【免费下载链接】richRich is a Python library for rich text and beautiful formatting in the terminal.项目地址: https://gitcode.com/gh_mirrors/ri/richrich.control是 Rich 中负责非打印控制码如响铃、光标移动、清屏、切换备用屏幕、修改窗口标题的核心模块。它把终端底层的 ANSI 转义序列封装为可渲染的Control对象供Console在渲染管线中直接输出同时在ControlType枚举、strip_control_codes、escape_control_codes等配套工具的配合下实现了控制码的生成、传递、过滤与清理。读完本文你将掌握如何用Control直接操作终端光标与屏幕、理解 Rich 渲染管线中控制码的流转机制并能安全地清洗或转义文本中的控制字符。模块定位渲染管线中的非打印段Rich 的渲染流程最终把一切可渲染对象转换为 Segment一段带样式的文本再由Console写入终端。但有些操作——移动光标、清屏、响铃、切换备用屏幕、修改窗口标题——既不是文本也不是样式它们是控制码不可打印、但会改变终端状态或光标位置。rich/control.py 就是为这类需求而存在的。官方文档对它的定位是A renderable that inserts a control code (non printable but may move cursor).它对外暴露三个层次的能力Control类一个符合 Rich 渲染协议__rich_console__的可渲染对象内部持有一个携带控制码的Segment模块级常量STRIP_CONTROL_CODES需要剥离的控制码、CONTROL_ESCAPE控制码→转义文本映射、CONTROL_CODES_FORMATControlType→ ANSI 序列生成函数两个文本工具函数strip_control_codes删除控制码与escape_control_codes转义控制码。ControlType控制码的语义枚举控制码的语义类型定义在 rich/segment.py 的ControlType枚举中它是一个IntEnum共 16 种枚举成员值语义BELL1响铃BELCARRIAGE_RETURN2回车HOME3光标回原位CLEAR4清屏SHOW_CURSOR5显示光标HIDE_CURSOR6隐藏光标ENABLE_ALT_SCREEN7启用备用屏幕DISABLE_ALT_SCREEN8关闭备用屏幕CURSOR_UP9光标上移CURSOR_DOWN10光标下移CURSOR_FORWARD11光标右移CURSOR_BACKWARD12光标左移CURSOR_MOVE_TO_COLUMN13移到指定列CURSOR_MOVE_TO14移到绝对坐标ERASE_IN_LINE15擦除行内内容SET_WINDOW_TITLE16设置窗口标题与之配套的是ControlCode类型别名rich/segment.pyControlCode Union[ Tuple[ControlType], Tuple[ControlType, Union[int, str]], Tuple[ControlType, int, int], ]即一个控制码可以是裸枚举无参数、枚举加单个参数整数或字符串、枚举加两个整数参数坐标场景。Control 类把控制码变成可渲染的 Segment构造函数与 ANSI 序列生成Control的构造rich/control.py接受任意数量的ControlType枚举或(ControlType, 参数...)元组内部通过CONTROL_CODES_FORMAT映射表把每个控制码渲染为对应的 ANSI 转义序列最终打包成一个Segmentdef __init__(self, *codes: Union[ControlType, ControlCode]) - None: control_codes: List[ControlCode] [ (code,) if isinstance(code, ControlType) else code for code in codes ] _format_map CONTROL_CODES_FORMAT rendered_codes .join( _format_mapcode for code, *parameters in control_codes ) self.segment Segment(rendered_codes, None, control_codes)注意两个关键点Segment的第三个字段就是control_codes列表因此控制码信息会随Segment一起在渲染管线中流转下游可以通过segment.is_control判断该段是否携带控制码Control对象通过__rich_console__rich/control.py参与渲染只要segment.text非空就 yield 该段。控制码 → ANSI 序列对照表映射逻辑集中在 CONTROL_CODES_FORMAT这也是理解 Rich 底层行为的核心表格ControlType生成的 ANSI 序列含义BELL\x07响铃CARRIAGE_RETURN\r回车HOME\x1b[H光标回左上角CLEAR\x1b[2J清屏ENABLE_ALT_SCREEN\x1b[?1049h进入备用屏幕DISABLE_ALT_SCREEN\x1b[?1049l退出备用屏幕SHOW_CURSOR\x1b[?25h显示光标HIDE_CURSOR\x1b[?25l隐藏光标CURSOR_UP\x1b[{param}A上移 param 行CURSOR_DOWN\x1b[{param}B下移 param 行CURSOR_FORWARD\x1b[{param}C右移 param 列CURSOR_BACKWARD\x1b[{param}D左移 param 列CURSOR_MOVE_TO_COLUMN\x1b[{param1}G移到第 param1 列0 基坐标ERASE_IN_LINE\x1b[{param}K按 param 模式擦除行CURSOR_MOVE_TO\x1b[{y1};{x1}H移到 (x, y)0 基输出时 1SET_WINDOW_TITLE\x1b]0;{title}\x07设置终端窗口标题从源码结构看所有坐标类控制码都采用0 基输入、1 基输出的约定例如move_to生成的\x1b[{y1};{x1}H会在内部对行列各加 1这与 ANSI 光标定位序列从 1 开始计数的规范保持一致。常用类方法速查Control提供了一组类方法让调用方不必手写枚举与参数Control.bell()响铃等价于Control(ControlType.BELL)Control.home()光标回原位\x1b[HControl.clear()清屏\x1b[2JControl.move(x0, y0)相对当前位置移动光标rich/control.py。x0生成CURSOR_FORWARD、x0生成CURSOR_BACKWARDy 同理映射为CURSOR_DOWN/CURSOR_UP均取绝对值x、y都为 0 时返回空控制段Control.move_to_column(x, y0)移到绝对列 x生成\x1b[{x1}G可选地附加 y 方向偏移rich/control.pyControl.move_to(x, y)移到绝对坐标 (x, y)生成\x1b[{y1};{x1}Hrich/control.pyControl.show_cursor(show)showTrue显示光标否则隐藏Control.alt_screen(enable)enableTrue时同时发送启用备用屏幕 光标回原位两个控制码关闭时只发送禁用序列rich/control.pyControl.title(title)设置终端窗口标题序列为\x1b]0;{title}\x07rich/control.py。此外Control实现了__str__直接返回底层Segment的文本方便调试时查看实际输出的 ANSI 序列。Segment 层面的控制码流转控制码不是附加在文本上的样式而是Segment的独立字段。在 rich/segment.py 中Segment是(text, style, control)三元组class Segment(NamedTuple): text: str style: Optional[Style] None control: Optional[Sequence[ControlCode]] None由此衍生出几个渲染管线关键行为Segment.cell_lengthrich/segment.py携带 control 的段不占用任何终端格子cell_length恒为 0——这保证控制码不会干扰 Rich 的宽度计算与换行Segment.is_controlrich/segment.py判断段是否携带控制码Segment.filter_control(segments, is_control)rich/segment.py从段序列中筛出或剔除所有控制段供需要只取可见文本或只取控制码的场景使用在adjust_line_lengthrich/segment.py等裁剪逻辑中控制段会被原样保留且不参与宽度累计因此控制码在换行、裁剪、对齐后不会丢失或错位。Console 集成面向用户的入口虽然可以手动构造Control日常开发更多通过Console的封装方法使用。它们的底层调用链都可以在 rich/console.py 中看到Console 方法底层实现位置console.bell()self.control(Control.bell())console.pyconsole.clear(homeTrue)Control.clear() 可选Control.home()console.pyconsole.show_cursor(show)Control.show_cursor(show)仅is_terminal时生效console.pyconsole.set_alt_screen(enable)Control.alt_screen(enable)跳过 legacy Windowsconsole.pyconsole.set_window_title(title)Control.title(title)仅is_terminal时生效console.pyconsole.control(*controls)把控制段直接追加进输出缓冲console.py其中console.control()是所有控制码的最终落点只要不是 dumb terminal就把每个Control的segment直接写入缓冲。set_window_title的文档还特别提醒Rich没有恢复窗口标题的手段设置后标题会持续到程序退出fishshell 与 Windows Terminal 会自行重置多数终端不会且部分终端需要配置或根本不支持该功能——返回值只表示控制码是否写入不代表标题真的改变。Console.screen()console.py则是备用屏幕的安全用法以上下文管理器进入/退出备用屏幕模式退出时自动关闭避免程序异常退出后终端停留在备用屏幕。真实调用链Live 渲染与 ScreenUpdate控制码在 Rich 内部的应用远超响铃这类小功能进度条、Live、全屏应用都依赖它live_render.py 在计算行偏移时返回携带CURSOR_UP/CURSOR_MOVE_TO等控制码的Control对象偏移为 0 时返回空Control()用于把光标移回上一帧起点实现原地刷新live.py 在刷新与退出时分别打印空Control()和Control.home()配合光标回位完成整帧重绘ScreenUpdateconsole.py逐行生成Control.move_to(x, offset)把渲染好的多行内容钉在屏幕的指定坐标上——这是console.screen()全屏输出实现的基础。由此可见Control是 Rich 实现动态刷新原地更新全屏输出等高级能力的地基所有动画效果最终都归结为在正确位置插入正确的控制码。文本清洗strip_control_codes 与 escape_control_codes当处理外部输入的字符串时控制码可能带来安全隐患或显示污染。rich.control为此提供了两个工具函数strip_control_codes删除控制码strip_control_codes 利用str.translate一次性剔除五类控制字符。其清洗名单定义在 STRIP_CONTROL_CODES码点名称效果7Bell响铃8Backspace退格11Vertical tab垂直制表12Form feed换页13Carriage return回车它在 Rich 内部被广泛用于文本净化Text构造与拼接时通过 text.py 与 text.py 调用Text.from_markup等路径也会在 text.py 清洗内容确保不可见字符不会悄悄写进终端。escape_control_codes转义控制码escape_control_codes 则把同一批控制码替换为可读的转义文本如\r→\\r映射表见 CONTROL_ESCAPE。它适用于需要展示而非执行的场景_inspect模块在 rich/_inspect.py 用它转义对象文档字符串中的控制字符避免恶意/异常文本在检查输出时触发终端行为。两个函数都采用text.translate实现性能开销低且都安全处理空串与不含控制码的普通文本。测试佐证行为即契约test_control.py 用一组断言把本模块的行为固化为契约是验证上述原理的最佳参考Control(ControlType.BELL)的字符串形式就是\x07test_controlstrip_control_codes(foo\rbar) foobar普通文本原样保留test_strip_control_codesescape_control_codes(foo\rbar) foo\\rbartest_escape_control_codesControl.move_to(5, 10)生成\x1b[11;6H且segment.control [(ControlType.CURSOR_MOVE_TO, 5, 10)]——验证了 0 基输入 1 输出test_control_move_toControl.move(3, 4)生成\x1b[3C\x1b[4Bmove(0, 0)生成空段test_control_moveControl.move_to_column(10, 20)生成\x1b[11G\x1b[20By 为负时改为CURSOR_UPtest_move_to_columnControl.title(hello)生成\x1b]0;hello\x07test_title。实战示例示例 1用类方法操作终端from rich.console import Console console Console() console.bell() # 响铃 console.set_window_title(Rich Demo) # 修改窗口标题 console.show_cursor(False) # 隐藏光标仅真实终端生效 console.set_alt_screen(True) # 进入备用屏幕 console.print(Fullscreen content) console.set_alt_screen(False) # 退出备用屏幕推荐用 console.screen() console.show_cursor(True) # 恢复光标示例 2直接构造 Control 对象from rich.console import Console from rich.control import Control from rich.segment import ControlType console Console() # 光标相对移动右移 3 列下移 4 行 console.control(Control.move(3, 4)) # 光标绝对定位到 (5, 10) console.control(Control.move_to(5, 10)) # 混合控制码清屏 光标回原位 console.control(Control.clear(), Control.home()) # 直接传枚举/参数元组等价于上述封装 console.control(Control((ControlType.CURSOR_FORWARD, 3)))示例 3清洗用户输入中的控制码from rich.control import escape_control_codes, strip_control_codes from rich.text import Text user_input progress: 50%\r80% print(repr(strip_control_codes(user_input))) # progress: 50%80% print(repr(escape_control_codes(user_input))) # progress: 50%\\r80% # 用于 Text 时Rich 本身就会在构造阶段做 strip safe Text(user_input)使用前提与限制控制码是否真正生效取决于终端show_cursor、set_alt_screen、set_window_title等方法都以is_terminal为前提重定向到文件或管道时静默跳过console.pylegacy Windows 终端被set_alt_screen显式排除console.py设置窗口标题是一次性操作Rich 不提供还原 API标题可能被 shell、插件等其他软件覆盖在实现自定义渲染对象时如果需要在文本流中插入非打印控制正确做法是构造携带control字段的Segment或直接用Control而不是把控制序列拼进text——否则会影响宽度计算并可能被换行逻辑破坏。小结rich.control是 Rich 终端底层能力与上层 API 之间的桥梁ControlType定义语义CONTROL_CODES_FORMAT负责生成 ANSI 序列Control把它们封装为可渲染的SegmentConsole.control()完成最终写入strip_control_codes与escape_control_codes则守护输入安全。无论是想深入理解 Rich 的动态渲染原理还是在自定义渲染对象中直接操纵光标与屏幕rich/control.py 都是最值得精读的模块之一。【免费下载链接】richRich is a Python library for rich text and beautiful formatting in the terminal.项目地址: https://gitcode.com/gh_mirrors/ri/rich创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表