Python中的双进度条

Tho*_*mas 20 python progress-bar

有没有办法在Python中创建双进度条?我想在彼此内部运行两个循环.对于每个循环,我想要一个进度条.我的程序看起来像:

import time
for i1 in range(5):
    for i2 in range(300):
        # do something, e.g. sleep
        time.sleep(0.01)
        # update upper progress bar
    # update lower progress bar
Run Code Online (Sandbox Code Playgroud)

中间某处的输出应该看起来像

50%|############################                                  |ETA: 0:00:02
80%|##################################################            |ETA: 0:00:04
Run Code Online (Sandbox Code Playgroud)

已经存在的非常酷的进度条模块似乎不支持这一点.

cas*_*dcl 34

使用tqdm嵌套进度条功能,这是一个极低开销,可自定义的进度条库:

$ pip install -U tqdm
Run Code Online (Sandbox Code Playgroud)

然后:

from tqdm import tqdm
import time
for i1 in tqdm(range(5)):
    for i2 in tqdm(range(300)):
        # do something, e.g. sleep
        time.sleep(0.01)
Run Code Online (Sandbox Code Playgroud)

您也可以使用from tqdm import trange,然后替换tqdm(range(...))trange(...).你也可以在笔记本电脑上工作.

  • 这不起作用。要么因为一个错误,要么它不打算这样工作,但它不断向控制台添加行。 (37认同)
  • 如果您使用 Jupyter 笔记本,则需要使用笔记本子模块:“from tqdm.notebook import tqdm” (11认同)
  • 我同意@asu,它不像OP在我的控制台中描述的那样工作,它只是产生大量新行 (8认同)
  • 是的,请参阅 https://github.com/tqdm/tqdm/#faq-and-known-issues - 本质上,如果您的控制台不支持它,那么其他答案都不起作用 - 唯一的选择是询问维护者您的控制台支持回车,或者使用不同的控制台。 (2认同)
  • @asu 我刚刚添加了关于“leave=False”的注释,以防万一这就是你的意思 (2认同)
  • @asu 要在笔记本中使用它,您需要取消注释“tqdm.auto”行。此外,现代控制台*确实*支持编辑上一行。 (2认同)

Ale*_*xus 26

我基本上只想添加到@casper.dcl 的答案中。在稍微不同的情况下,您有两个嵌套的 for 循环并且只需要一个 SINGLE 进度条,您可以执行以下操作。

from tqdm import tqdm
import time
n = 5
m = 300
with tqdm(total=n * m) as pbar:
    for i1 in tqdm(range(n)):
        for i2 in tqdm(range(m)):
            # do something, e.g. sleep
            time.sleep(0.01)
            pbar.update(1)
Run Code Online (Sandbox Code Playgroud)

我知道这不是问题,但它可能对某些人仍然有帮助。

  • 对我来说,为了只实现一个进度条,我必须删除循环中对 tqdm 的调用。只需为循环保留“for i1 in range(n)”即可。 (3认同)

Art*_*oul 15

一旦这里是 @yurenchen 的答案(已被删除),该答案宣传了丰富的库,它有文档中描述的进度条例程。

笔记。另请参阅我在该线程中关于进度条的其他两个答案 -使用 enenlight libusing ASCII art

可以通过安装丰富的库python -m pip install rich

显示三个不同颜色的进度条堆栈的最小示例是:

在线尝试一下!

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)
Run Code Online (Sandbox Code Playgroud)

它产生以下彩色控制台输出(+ aciinema link):

在此输入图像描述


Xaq*_*ron 10

使用启发

import time
import enlighten

manager = enlighten.get_manager()
ticks = manager.counter(total=100, desc="Ticks", unit="ticks", color="red")
tocks = manager.counter(total=20, desc="Tocks", unit="tocks", color="blue")

for num in range(100):
    time.sleep(0.1)  # Simulate work
    print("The quick brown fox jumps over the lazy dog. {}".format(num))
    ticks.update()
    if not num % 5:
        tocks.update()

manager.stop()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Ken*_*nny 9

它需要你移动光标位置.我写了一个hacky的事情来做它.

此脚本依赖于progressbar模块假定您处于一个新线来绘制进度条的事实.只需向上移动光标(使用"向上移动光标1行"的转义码),向下移动(仅使用换行符.我也可以使用转义码,但换行更容易,更快),可以保持多个进度酒吧.

import progressbar, time, sys

def up():
    # My terminal breaks if we don't flush after the escape-code
    sys.stdout.write('\x1b[1A')
    sys.stdout.flush()

def down():
    # I could use '\x1b[1B' here, but newline is faster and easier
    sys.stdout.write('\n')
    sys.stdout.flush()

# Total bar is at the bottom. Move down to draw it
down()
total = progressbar.ProgressBar(maxval=50)
total.start()

for i in range(1,51):
    # Move back up to prepare for sub-bar
    up()

    # I make a new sub-bar for every iteration, thinking it could be things
    # like "File progress", with total being total file progress.
    sub = progressbar.ProgressBar(maxval=50)
    sub.start()
    for y in range(51):
        sub.update(y)
        time.sleep(0.005)
    sub.finish()

    # Update total - The sub-bar printed a newline on finish, so we already
    # have focus on it
    total.update(i)
total.finish()
Run Code Online (Sandbox Code Playgroud)

这当然有点hacky,但它完成了工作.我希望它有用.


Art*_*oul 8

这个答案的启发,我还尝试了enlight python库并编写了我的简单帮助函数pit(),用于将迭代器包装在for循环内(代码顶部),并提供了使用示例(代码底部)以及实时终端截屏。

笔记。另请参阅我在这个线程中关于进度条的其他两个答案 -使用丰富的库使用 ASCII art

与链接答案的主要区别在于,pit()允许在 for 循环内部使用来包装迭代器,而不是使用手动.update()方法,这种迭代器包装功能缺乏englighten,这就是我决定实现自己的原因。

正如人们在接受的答案中看到的那样,其他著名的进度条库如tqdm已经具有将迭代器包装在 for 循环中以及嵌套循环中的多个进度条的功能。

在 Linux 和 Windows 中都可以彩色工作。

在线尝试一下!

# Helper Progress Iterator
# Needs: python -m pip install enlighten

def pit(it, *pargs, **nargs):
    import enlighten
    global __pit_man__
    try:
        __pit_man__
    except NameError:
        __pit_man__ = enlighten.get_manager()
    man = __pit_man__
    try:
        it_len = len(it)
    except:
        it_len = None
    try:
        ctr = None
        for i, e in enumerate(it):
            if i == 0:
                ctr = man.counter(*pargs, **{**dict(leave = False, total = it_len), **nargs})
            yield e
            ctr.update()
    finally:
        if ctr is not None:
            ctr.close()


####### Usage Example ########

import time

def Generator(n):
    for i in range(n):
        yield i

for i in pit(range(2), color = 'red'):
    for j in pit(range(3), color = 'green'):
        for k in pit(Generator(4), total = 4, color = 'blue'):
            for l in pit(Generator(5)):
                print(i, j, k, l)
                time.sleep(0.05)

Run Code Online (Sandbox Code Playgroud)

输出(+ ascii-video):

ASCII码


Mer*_*ury 6

游戏有点晚了,但这里有一个只使用 tqdm 的答案

import re
from time import sleep
from tqdm import trange

class DescStr:
    def __init__(self):
        self._desc = ''

    def write(self, instr):
        self._desc += re.sub('\n|\x1b.*|\r', '', instr)

    def read(self):
        ret = self._desc
        self._desc = ''
        return ret

    def flush(self):
        pass


rng_a = trange(10)
desc = DescStr()
for x in rng_a:
    for y in trange(10, file=desc, desc="Y"):
        rng_a.set_description(desc.read())
        sleep(0.1)
Run Code Online (Sandbox Code Playgroud)

产生:

Y:  90%|######### | 9/10 [00:00<00:00,  9.55it/s]: 100%|##########| 10/10 [00:10<00:00,  
Run Code Online (Sandbox Code Playgroud)


Mar*_*kus 5

这是一个显示外部和内部循环进度的简单方法:

\n
from tqdm import tqdm\nfrom time import sleep\n\npbar = tqdm(range(10))\nfor i in pbar:\n    for j in range(20):\n        pbar.set_postfix({'inner': j})\n        sleep(.2)\n
Run Code Online (Sandbox Code Playgroud)\n

这并不完全符合您的要求:这里的内循环仅显示为递增数字,而进度条显示外循环进度。但这是嵌套循环的有用可视化。

\n

这是一个快照:

\n
 30%|\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88       | 3/10 [00:14<00:33,  4.77s/it, inner=12]\n
Run Code Online (Sandbox Code Playgroud)\n

随着外部循环的进度条缓慢前进,“内部”计数器不断递增。

\n

更新:

\n

您可以将此解决方案与 dominecf 的解决方案结合起来。以下使用 tqdm 作为外部循环,并使用 dominecf 的函数集成内部循环(稍作修改):

\n
import tqdm\nimport time\n\ndef myprogress(curr, N, width=10, bars = u'\xe2\x96\x89\xe2\x96\x8a\xe2\x96\x8b\xe2\x96\x8c\xe2\x96\x8d\xe2\x96\x8e\xe2\x96\x8f '[::-1],\n               full='\xe2\x96\x88', empty=' '): \n    p = curr / N \n    nfull = int(p * width)\n    return "{:>3.0%} |{}{}{}| {:>2}/{}"\\\n        .format(p, full * nfull,\n                bars[int(len(bars) * ((p * width) % 1))],\n                empty * (width - nfull - 1),\n                curr, N)\n\n\npbar = tqdm.tqdm(range(10),\n                 bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}')\nfor i in pbar:\n    for j in range(20):\n        pbar.set_postfix_str(myprogress(j, 20))\n        time.sleep(.2)\n
Run Code Online (Sandbox Code Playgroud)\n

这是一个快照:

\n
 30%|\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88       | 3/10 [00:14<00:34,  4.90s/it, 60% |\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88    | 12/20]                                                                                                                                      \n
Run Code Online (Sandbox Code Playgroud)\n


Art*_*oul 5

受到@dominecf 的简单回答的启发,只是为了好玩,我实现了一个辅助包装函数pbiter(),可以在循环中使用它来显示任何可迭代的进度。pbiter()使用 @dominecf\ 的myprogress().

\n

笔记。另请参阅我在该线程中关于进度条的其他两个答案 -使用englishusing rich lib

\n

不要过多评判这个答案,它只是为了在纯Python中从头开始实现进度的黑客乐趣,这个答案并不意味着在任何生产环境、实际应用程序中使用或tqdm模块enlighten来实现进度。

\n

请参阅我对同一问题的其他答案,该答案显示了如何使用enlighten模块来取得进展。

\n

pbiter()从这个答案可以非常简单地与嵌套循环中的任何迭代一起使用,如下所示:

\n
for a in pbiter(range(12)):\n    for b in pbiter(generator_nums(13)):\n        for c in pbiter(generator_nums(7), total = 7):\n            time.sleep(0.03)\n
Run Code Online (Sandbox Code Playgroud)\n

进度条总长度可以通过是否len(it)可迭代(例如,range(start, stop, step)它始终可用)或通过提供total = ...参数来计算,否则进度会随乘数呈指数衰减0.1(这显示了一个很好的近似值)。在三个示例中,第二个循环上方的嵌套循环具有这种指数行为。

\n

完整代码如下。请参阅代码后面的 ascii-video。

\n

在线尝试一下!

\n
def myprogress(current, whole=1, n=30, bars=u\'\xe2\x96\x95\xe2\x96\x8f\xe2\x96\x8e\xe2\x96\x8d\xe2\x96\x8c\xe2\x96\x8b\xe2\x96\x8a\xe2\x96\x89\', full=\'\xe2\x96\x89\', empty=\'\xe2\x96\x95\'): \n    """ current and whole can be an element of a list being iterated, or just two numbers """\n    p = (whole.index(current))/len(whole)+1e-9 if type(whole)==list else current/whole+1e-9 \n    return f"{full*int(p*n)}{bars[int(len(bars)*((p*n)%1))]}{empty*int((1-p)*n)} {p*100:>6.2f}%" \n\ndef pbiter(it, *, total = None, width = 36, _cfg = {\'idx\': -1, \'pbs\': {}, \'lline\': 0}):\n    try:\n        total = total or len(it)\n    except:\n        total = None\n    \n    _cfg[\'idx\'] += 1\n    idx = _cfg[\'idx\']\n    pbs = _cfg[\'pbs\']\n    pbs[idx] = [0, total, 0]\n    \n    def Show():\n        line2 = \' \'.join([\n            myprogress(e[1][0], max(e[1][0], e[1][1] or\n                max(1, e[1][0]) / max(.1, e[1][2])), width // len(pbs))\n            for e in sorted(pbs.items(), key = lambda e: e[0])\n        ])\n        line = line2 + \' \' * (max(0, _cfg[\'lline\'] - len(line2)) + 0)\n        print(line, end = \'\\r\', flush = True)\n        _cfg[\'lline\'] = len(line2)\n    \n    try:\n        Show()\n        for e in it:\n            yield e\n            pbs[idx][0] += 1\n            pbs[idx][2] += (1. - pbs[idx][2]) * .1\n            Show()\n        pbs[idx][2] = 1.\n        Show()\n    finally:\n        del pbs[idx]\n\ndef test():\n    import time\n\n    def generator_nums(cnt):\n        for i in range(cnt):\n            yield i\n\n    for a in pbiter(range(12)):\n        for b in pbiter(generator_nums(13)):\n            for c in pbiter(generator_nums(7), total = 7):\n                time.sleep(0.03)\n\ntest()\n
Run Code Online (Sandbox Code Playgroud)\n

ASCII 视频输出(另请参阅 asciinema视频页面):

\n

在此输入图像描述

\n

如果由于某种原因你没有循环并且仍然想使用 my pbiter(),那么你可以通过常规的内置next()使用它操作来使用它,如下所示:

\n
# Create 3 progress bars, they are at 0% point now\na = pbiter(range(5))\nb = pbiter(range(4))\nc = pbiter(range(3))\n# Some lines of code later, advance progress "a"\nnext(a)\n# And later ...\nnext(b)\n# And later ...\nnext(b)\n# Later ...\nnext(a); next(c)\n# Later ...\nnext(c); next(b)\n
Run Code Online (Sandbox Code Playgroud)\n

换句话说,您可以在任何代码位置以任何顺序手动创建和推进进度条。

\n