进度显示

Rich 可以显示关于长时间运行的任务/文件复制等的进度信息,这些信息会不断更新。 显示的信息是可配置的,默认情况下将显示“任务”的描述、进度条、完成百分比和估计剩余时间。

Rich 进度显示支持多个任务,每个任务都有一个进度条和进度信息。 你可以使用它来跟踪在线程或进程中进行的并发任务。

要查看进度显示的外观,请从命令行尝试以下操作

python -m rich.progress

注意

进度适用于 Jupyter 笔记本,但需要注意的是自动刷新功能已禁用。 你需要显式调用 refresh() 或在调用 update() 时设置 refresh=True。 或者使用 track() 函数,该函数会在每次循环时自动刷新。

基本用法

对于基本用法,请调用 track() 函数,该函数接受一个序列(例如列表或范围对象)以及正在执行的作业的可选描述。 track 函数将从序列中生成值,并在每次迭代时更新进度信息。 以下是一个示例

import time
from rich.progress import track

for i in track(range(20), description="Processing..."):
    time.sleep(1)  # Simulate work being done

高级用法

如果你需要在显示中显示多个任务,或者希望配置进度显示中的列,你可以直接使用 Progress 类。 构建好 Progress 对象后,使用 (add_task()) 添加任务,并使用 update() 更新进度。

Progress 类旨在用作上下文管理器,它会自动启动和停止进度显示。

以下是一个简单的示例

import time

from rich.progress import Progress

with Progress() as progress:

    task1 = progress.add_task("[red]Downloading...", total=1000)
    task2 = progress.add_task("[green]Processing...", total=1000)
    task3 = progress.add_task("[cyan]Cooking...", total=1000)

    while not progress.finished:
        progress.update(task1, advance=0.5)
        progress.update(task2, advance=0.3)
        progress.update(task3, advance=0.9)
        time.sleep(0.02)

与任务相关的total 值是进度达到 100% 所需完成的步骤数。 在这种情况下,步骤的含义取决于你的应用程序; 它可以是读取的文件字节数,或处理的图像数量等。

更新任务

当你调用 add_task() 时,你会得到一个任务 ID。 使用此 ID 在完成某些工作或任何信息发生变化时调用 update()。 通常,你将需要在每次完成一个步骤时更新 completed。 你可以通过直接更新 completed 或设置 advance 来实现,这将添加到当前的 completed 值。

The update() 方法收集关键字参数,这些参数也与任务相关。 使用它来提供你想在进度显示中渲染的任何其他信息。 附加参数存储在 task.fields 中,并且可以在 列类 中引用。

隐藏任务

你可以通过更新任务的 visible 值来显示或隐藏任务。 任务默认情况下是可见的,但你也可以通过调用 add_task() 并设置 visible=False 来添加一个不可见的任务。

瞬时进度

通常,当你退出进度上下文管理器(或调用 stop())时,最后刷新的显示会保留在终端中,光标位于下一行。 你也可以通过在 Progress 构造函数中设置 transient=True 使进度显示在退出时消失。 以下是一个示例

with Progress(transient=True) as progress:
    task = progress.add_task("Working", total=100)
    do_work(task)

瞬时进度显示在任务完成后,如果你希望终端输出更少时非常有用。

不确定进度

当你添加一个任务时,它会自动启动,这意味着它将显示一个 0% 的进度条,剩余时间将从当前时间开始计算。 如果在你可以开始更新进度之前存在很长的延迟,这可能不适用; 你可能需要等待服务器的响应或计算目录中的文件(例如)。 在这些情况下,你可以在调用 add_task() 时设置 start=Falsetotal=None,这将显示一个跳动的动画,让用户知道正在进行一些操作。 这称为不确定进度条。 当你有了步骤数量后,你可以调用 start_task(),它将显示一个 0% 的进度条,然后像往常一样调用 update()

自动刷新

默认情况下,进度信息每秒刷新 10 次。 你可以在 Progress 构造函数上使用 refresh_per_second 参数来设置刷新频率。 如果你知道更新频率不会那么高,则应将其设置为低于 10 的值。

如果你知道更新频率不高,你可能希望完全禁用自动刷新,可以通过在构造函数上设置 auto_refresh=False 来实现。 如果你禁用了自动刷新,则需要在更新任务后手动调用 refresh()

扩展

进度条将仅使用终端宽度所需的宽度来显示任务信息。 如果你在 Progress 构造函数上设置了 expand 参数,那么 Rich 将会将进度显示拉伸到整个可用宽度。

你可以使用 Progress 构造函数的位置参数来自定义进度显示中的列。 列的指定方式是格式字符串或 ProgressColumn 对象。

格式字符串将使用单个值“task” 进行渲染,该值将是一个 Task 实例。 例如 "{task.description}" 将在列中显示任务描述,而 "{task.completed} of {task.total}" 将显示已完成的总步骤数。 通过关键字参数传递给~rich.progress.Progress.update 的附加字段将存储在 task.fields 中。 你可以使用以下语法将它们添加到格式字符串中:"extra info: {task.fields[extra]}"

默认列等效于以下列

progress = Progress(
    TextColumn("[progress.description]{task.description}"),
    BarColumn(),
    TaskProgressColumn(),
    TimeRemainingColumn(),
)

要创建包含你自己的列的 Progress,除了默认列之外,请使用 get_default_columns()

progress = Progress(
    SpinnerColumn(),
    *Progress.get_default_columns(),
    TimeElapsedColumn(),
)

以下列对象可用

要实现自己的列,请扩展 ProgressColumn 类并像使用其他列一样使用它。

表格列

Rich 为 Progress 实例中的任务构建一个 Table。您可以通过在 Column 构造函数中指定 table_column 参数来定制此任务表的列的创建方式,该参数应该是一个 Column 实例。

以下示例演示了一个进度条,其中描述占据终端宽度的三分之一,进度条占据剩余的三分之二。

from time import sleep

from rich.table import Column
from rich.progress import Progress, BarColumn, TextColumn

text_column = TextColumn("{task.description}", table_column=Column(ratio=1))
bar_column = BarColumn(bar_width=None, table_column=Column(ratio=2))
progress = Progress(text_column, bar_column, expand=True)

with progress:
    for n in progress.track(range(100)):
        progress.print(n)
        sleep(0.1)

重定向 stdout/stderr

为了避免破坏进度显示的视觉效果,Rich 将重定向 stdoutstderr,以便您可以使用内置的 print 语句。此功能默认情况下启用,但您可以通过将 redirect_stdoutredirect_stderr 设置为 False 来禁用它。

定制

如果 Progress 类没有提供您在进度显示方面需要的功能,您可以覆盖 get_renderables 方法。例如,以下类将在进度显示周围渲染一个 Panel

from rich.panel import Panel
from rich.progress import Progress

class MyProgress(Progress):
    def get_renderables(self):
        yield Panel(self.make_tasks_table(self.tasks))

从文件读取

Rich 提供了一种在读取文件时生成进度条的简单方法。如果您调用 open(),它将返回一个上下文管理器,在您读取时显示进度条。当您无法轻松修改执行读取的代码时,这特别有用。

以下示例演示了如何在读取 JSON 文件时显示进度。

import json
import rich.progress

with rich.progress.open("data.json", "rb") as file:
    data = json.load(file)
print(data)

如果您已经有一个文件对象,您可以调用 wrap_file(),它将返回一个上下文管理器,它将您的文件包装起来,以便它显示进度条。如果您使用此函数,您将需要设置您预期读取的字节数或字符数。

以下是一个从互联网读取 url 的示例

from time import sleep
from urllib.request import urlopen

from rich.progress import wrap_file

response = urlopen("https://www.textualize.io")
size = int(response.headers["Content-Length"])

with wrap_file(response, size) as file:
    for line in file:
        print(line.decode("utf-8"), end="")
        sleep(0.1)

如果您希望从多个文件读取,您可以使用 open()wrap_file() 将文件进度添加到现有的 Progress 实例中。

请参阅 cp_progress.py <https://github.com/willmcgugan/rich/blob/master/examples/cp_progress.py>,了解 cp 命令的最小克隆,该命令在复制文件时显示进度条。

多个进度

您不能在单个 Progress 实例中为每个任务使用不同的列。但是,您可以在 实时显示 中使用任意多个 Progress 实例。请参阅 live_progress.pydynamic_progress.py,了解使用多个 Progress 实例的示例。

示例

请参阅 downloader.py,了解进度显示的实际应用。此脚本可以使用进度条、传输速度和文件大小下载多个并发文件。