使用 rich 终端#
颜色系统#
终端写入颜色有几种“标准”,但并非所有标准都被普遍支持。Rich会自动检测合适的颜色系统,或者你可以通过为 :class:~rich.console.Console
构造函数提供 color_system
参数来手动设置它。
你可以将 color_system
设置为以下值之一:
None
:完全禁用颜色。"auto"
:将自动检测颜色系统。"standard"
:可以显示8种颜色,包括正常和亮色变化,总共16种颜色。"256"
:可以显示来自"standard"的16种颜色加上一个固定的240种颜色调色板。"truecolor"
:可以显示1670万种颜色,这很可能是你的显示器能显示的所有颜色。"windows"
:在传统的Windows终端中可以显示8种颜色。新的Windows终端可以显示"truecolor"。
警告
在设置颜色系统时要小心,如果你设置的颜色系统高于你的终端支持的级别,你的文本可能无法阅读。
打印#
要向终端写入富文本内容,请使用 print()
方法。Rich会通过对象的 __str__
方法将其转换为字符串,并执行一些简单的语法高亮。它还会美观地打印任何容器,如字典和列表。如果你打印一个字符串,它将渲染 console_markup
。以下是一些示例:
from rich.console import Console
console = Console()
console.print([1, 2, 3])
console.print("[blue underline]看起来像一个链接")
# console.print(locals())
console.print("FOO", style="white on blue")
[1, 2, 3]
看起来像一个链接
FOO
你还可以使用 print()
来渲染支持Console Protocol 的对象,包括Rich的内置对象,如 Text
、Table
和 Syntax
,以及其他自定义对象。
记录日志#
~rich.console.Console.log
方法提供了与print相同的功能,但增加了一些对正在运行的应用程序进行调试有用的功能。日志记录在左侧的一列中写入当前时间,并在右侧的一列中写入调用该方法的文件和行。下面是一个示例:
from rich.console import Console
console = Console()
console.log("这是一条日志信息")
[08:44:49] 这是一条日志信息 2188820476.py:4
打印 JSON#
console.print_json('[false, true, null, "foo"]')
[ false, true, null, "foo" ]
from rich.json import JSON
console.log(JSON('["foo", "bar"]'))
[ 4213963421.py:3 "foo", "bar" ]
低级别输出#
除了 print()
和 log()
方法外,Rich 还提供 ~rich.console.Console.out
方法,它提供了一种更低级别的方式向终端写入内容。out()
方法将所有位置参数转换为字符串,并且不会进行美观打印、自动换行或应用标记到输出,但可以应用基本样式,并且可以选择性地进行高亮显示。
下面是一个示例:
console.out("Locals", locals())
Locals {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', 'from rich.console import Console\nconsole = Console()\n\nconsole.print([1, 2, 3])\nconsole.print("[blue underline]看起来像一个链接")\n# console.print(locals())\nconsole.print("FOO", style="white on blue")', 'from rich.console import Console\n\nconsole = Console()\nconsole.log("这是一条日志信息")', 'console.print_json(\'[false, true, null, "foo"]\')', 'from rich.json import JSON\n\nconsole.log(JSON(\'["foo", "bar"]\'))', 'console.out("Locals", locals())'], '_oh': {}, '_dh': [PosixPath('/home/runner/work/pybook/pybook/doc/topics/tools/tasks/rich/console')], 'In': ['', 'from rich.console import Console\nconsole = Console()\n\nconsole.print([1, 2, 3])\nconsole.print("[blue underline]看起来像一个链接")\n# console.print(locals())\nconsole.print("FOO", style="white on blue")', 'from rich.console import Console\n\nconsole = Console()\nconsole.log("这是一条日志信息")', 'console.print_json(\'[false, true, null, "foo"]\')', 'from rich.json import JSON\n\nconsole.log(JSON(\'["foo", "bar"]\'))', 'console.out("Locals", locals())'], 'Out': {}, 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7fc25ce13860>>, 'exit': <IPython.core.autocall.ZMQExitAutocall object at 0x7fc26c0f7410>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x7fc26c0f7410>, 'open': <function open at 0x7fc2720eaa20>, '_': '', '__': '', '___': '', '_i': 'from rich.json import JSON\n\nconsole.log(JSON(\'["foo", "bar"]\'))', '_ii': 'console.print_json(\'[false, true, null, "foo"]\')', '_iii': 'from rich.console import Console\n\nconsole = Console()\nconsole.log("这是一条日志信息")', '_i1': 'from rich.console import Console\nconsole = Console()\n\nconsole.print([1, 2, 3])\nconsole.print("[blue underline]看起来像一个链接")\n# console.print(locals())\nconsole.print("FOO", style="white on blue")', 'Console': <class 'rich.console.Console'>, 'console': <console width=115 ColorSystem.TRUECOLOR>, '_i2': 'from rich.console import Console\n\nconsole = Console()\nconsole.log("这是一条日志信息")', '_i3': 'console.print_json(\'[false, true, null, "foo"]\')', '_i4': 'from rich.json import JSON\n\nconsole.log(JSON(\'["foo", "bar"]\'))', 'JSON': <class 'rich.json.JSON'>, '_i5': 'console.out("Locals", locals())'}
rich 规则#
rule()
方法将绘制一条带有可选标题的水平线,这是一种将终端输出划分为不同部分的好方法。
console.rule("[bold red]Chapter 2")
──────────────────────────────────────────────────── Chapter 2 ────────────────────────────────────────────────────
文件输出#
你可以告诉 Console
对象通过在构造函数上设置 file
参数来写入文件,该参数应该是一个用于写入文本的类似文件的对象。你可以使用此方法将内容写入文件,而不会显示在终端上。下面是一个示例:
import sys
from rich.console import Console
from datetime import datetime
with open("report.txt", "wt") as report_file:
console = Console(file=report_file)
console.rule(f"Report Generated {datetime.now().ctime()}")
注意,当写入文件时,如果你不希望将输出包装到当前控制台宽度,你可能需要显式设置 width
参数。
捕获输出#
在某些情况下,你可能希望捕获 Console
的输出,而不是直接将其写入终端。你可以使用 capture()
方法来实现这一点,该方法返回一个上下文管理器。在退出此上下文管理器时,调用 get()
方法以返回本来会被写入终端的字符串。下面是一个示例:
from rich.console import Console
console = Console()
with console.capture() as capture:
console.print("[bold red]Hello[/] World")
str_output = capture.get()
print(str_output)
Hello World
捕获输出的另一种方法是将 Console
的文件设置为 io.StringIO
。如果你在单元测试中测试控制台输出,这是推荐的方法。下面是一个示例:
from io import StringIO
from rich.console import Console
console = Console(file=StringIO())
console.print("[bold red]Hello[/] World")
str_output = console.file.getvalue()
Hello World
Emoji 表情#
将名称放在两个冒号之间即可在控制台输出中插入 emoji 表情符。示例如下:
console.print(":smiley: :vampire: :pile_of_poo: :thumbs_up: :raccoon:")
😃 🧛 💩 👍 🦝
表格(Tables)#
Rich 可以使用 Unicode 框字符来呈现多变的表格。Rich 包含多种边框,样式,单元格对齐等格式设置的选项。下面是一个简单的示例:
from rich.console import Console
from rich.table import Column, Table
console = Console()
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Date", style="dim", width=12)
table.add_column("Title")
table.add_column("Production Budget", justify="right")
table.add_column("Box Office", justify="right")
table.add_row(
"Dec 20, 2019", "Star Wars: The Rise of Skywalker", "$275,000,000", "$375,126,118"
)
table.add_row(
"May 25, 2018",
"[red]Solo[/red]: A Star Wars Story",
"$275,000,000",
"$393,151,347",
)
table.add_row(
"Dec 15, 2017",
"Star Wars Ep. VIII: The Last Jedi",
"$262,000,000",
"[bold]$1,332,539,889[/bold]",
)
console.print(table)
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ┃ Date ┃ Title ┃ Production Budget ┃ Box Office ┃ ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ │ Dec 20, 2019 │ Star Wars: The Rise of Skywalker │ $275,000,000 │ $375,126,118 │ │ May 25, 2018 │ Solo: A Star Wars Story │ $275,000,000 │ $393,151,347 │ │ Dec 15, 2017 │ Star Wars Ep. VIII: The Last Jedi │ $262,000,000 │ $1,332,539,889 │ └──────────────┴───────────────────────────────────┴───────────────────┴────────────────┘
进度条(Progress Bars)#
Rich 可以渲染多种“无闪烁”的进度条图形,以跟踪长时间运行的任务。
基本用法:用 track
函数调用任何程序并迭代结果。下面是一个例子:
from rich.progress import track
def do_step(step):
...
for step in track(range(100)):
do_step(step)
状态动画(Status)#
对于那些很难计算进度的情况,你可以使用 status
方法,它会展示一个“环形旋转(spinner)”的动画和文字信息。这个动画并不会妨碍你正常使用控制台。下面是个例子:
from time import sleep
from rich.console import Console
console = Console()
tasks = [f"task {n}" for n in range(1, 3)]
with console.status("[bold green]Working on tasks...") as status:
while tasks:
task = tasks.pop(0)
sleep(1)
console.log(f"{task} complete")
你可以通过 spinner
参数指定一种动画效果。执行以下命令来查看所有可选值:
python3 -m rich.spinner
树(Tree)#
Rich 可以渲染一个包含引导线的树(tree
)。对于展示文件目录结构和其他分级数据来说,树是理想选择。
树的标签可以是简单文本或任何 Rich 能渲染的东西。执行以下命令查看演示:
!python -m rich.tree
📁 Renderables
├── 📁 Atomic
│ ╠══ 📄 Syntax
│ ║ 1 class Segment(NamedTuple):
│ ║ 2 text: str = ""
│ ║ 3 style: Optional[Style] = None
│ ║ 4 is_control: bool = False
│ ║ 5
│ ╚══ 📄 Markdown
│ ╭──────────────────────────────────────────────────────────────────────╮
│ │ example.md │
│ │ │
│ │ ▌ Hello, World! │
│ │ ▌ Markdown all the things │
│ ╰──────────────────────────────────────────────────────────────────────╯
└── 📁 Containers
┣━━ 📄 Panels
┃ ╭──────────────╮
┃ │ Just a panel │
┃ ╰──────────────╯
┗━━ 📄 Table
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ Released ┃ Title ┃ Box Office ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ Dec 20, 2019 │ Star Wars: The Rise of Skywalker │ $952,110,690 │
│ May 25, 2018 │ Solo: A Star Wars Story │ $393,151,347 │
│ Dec 15, 2017 │ Star Wars Ep. V111: The Last Jedi │ $1,332,539,889 │
│ Dec 16, 2016 │ Rogue One: A Star Wars Story │ $1,332,439,889 │
└──────────────┴───────────────────────────────────┴────────────────┘
列(Columns)#
Rich 可以将内容通过排列整齐的,具有相等或最佳的宽度的列来呈现。下面是(macOS / Linux)ls命令的一个非常基本的克隆,用于用列来显示目录列表:
import os
import sys
from rich import print
from rich.columns import Columns
directory = os.listdir(".")
print(Columns(directory))
intro.ipynb index.md use.ipynb
Markdown#
Rich 可以呈现markdown,并可相当不错的将其格式转移到终端。
为了渲染 markdown,请导入Markdown类,并使用包含 markdown 代码的字符串来构造它,然后将其打印到控制台。例子如下:
from rich.console import Console
from rich.markdown import Markdown
console = Console()
with open("README.md") as readme:
markdown = Markdown(readme.read())
console.print(markdown)
%load_ext rich
from rich.console import Console
from rich.markdown import Markdown
markdown = Markdown("# hi\n## fg")
markdown
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ hi ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ fg
语法高亮(Syntax Highlighting)#
Rich 使用 pygments
库来实现语法高亮显示。用法类似于渲染 markdown。构造 Syntax
对象并将其打印到控制台。下面是一个例子:
from rich.console import Console
from rich.syntax import Syntax
my_code = '''
def iter_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]:
"""Iterate and generate a tuple with a flag for first and last value."""
iter_values = iter(values)
try:
previous_value = next(iter_values)
except StopIteration:
return
first = True
for value in iter_values:
yield first, False, previous_value
first = False
previous_value = value
yield first, True, previous_value
'''
syntax = Syntax(my_code, "python", theme="monokai", line_numbers=True)
console = Console()
console.print(syntax)
1 2 def iter_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]: 3 """Iterate and generate a tuple with a flag for first and last value.""" 4 iter_values = iter(values) 5 try: 6 previous_value = next(iter_values) 7 except StopIteration: 8 return 9 first = True 10 for value in iter_values: 11 yield first, False, previous_value 12 first = False 13 previous_value = value 14 yield first, True, previous_value 15
栈回溯信息(Tracebacks)#
Rich 可以渲染出漂亮的栈回溯信息,它比标准的 Python 格式更容易阅读,且能显示更多的代码。您可以将 Rich 设置为默认的栈回溯处理程序,这样所有未捕获的异常都将由 Rich 为渲染。
下面是在 OSX(在 Linux 上也类似)系统的效果:
from rich.console import Console
def foo(n):
return bar(n)
def bar(n):
return foo(n)
console = Console()
try:
foo(1)
except Exception:
console.print_exception(max_frames=20)
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮ │ in <module>:15 │ │ │ │ 12 console = Console() │ │ 13 │ │ 14 try: │ │ ❱ 15 │ foo(1) │ │ 16 except Exception: │ │ 17 │ console.print_exception(max_frames=20) │ │ 18 │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ ... 2958 frames hidden ... │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ │ │ in bar:9 │ │ │ │ 6 │ │ 7 │ │ 8 def bar(n): │ │ ❱ 9 │ return foo(n) │ │ 10 │ │ 11 │ │ 12 console = Console() │ │ │ │ in foo:5 │ │ │ │ 2 │ │ 3 │ │ 4 def foo(n): │ │ ❱ 5 │ return bar(n) │ │ 6 │ │ 7 │ │ 8 def bar(n): │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ RecursionError: maximum recursion depth exceeded