Python进度条

548

当我的脚本执行一些可能需要时间的任务时,如何使用进度条呢?

例如,在完成某个函数并返回True时需要一定的时间。我如何在函数执行期间显示进度条呢?

请注意,我需要实时显示进度条,所以我不知道该怎么办。我需要为此使用thread吗?我不知道。

目前我在函数执行期间没有任何打印输出,但一个进度条会很好。我更关心的是从代码角度如何实现这一点。


你是使用GUI工具包还是仅限于CLI? - Bobby
使用GUI可以做到这一点,但我对CLI部分感兴趣。无论如何,我可以使用第三方库。 - user225312
4
可能是重复的问题,与“在控制台中的文本进度条”有关。请注意,虽然这个问题发布时间早于另一个问题三天,但是链接的那个问题更常被查看。 - Greenstick
这是一个在Jupyter Notebook中的解决方案:https://mikulskibartosz.name/how-to-display-a-progress-bar-in-jupyter-notebook-47bd4c2944bf - Steven C. Howell
我发布了一种新型进度条,你可以打印它,查看吞吐量和预计时间,甚至可以暂停它,除此之外还有非常酷的动画效果!请查看:https://github.com/rsalmei/alive-progress alive-progress - rsalmei
我没有循环,只有一个写入命令。在这种情况下,是否可能?with open(path2file, 'wb+') as f: # with open(path2file, 'w+') as f: f.write(data.read()) - Charlie Parker
47个回答

3
这里有一个简短的解决方案,可以通过编程方式构建加载条(您需要决定它的长度)。
import time

n = 33  # or however many loading slots you want to have
load = 0.01  # artificial loading time!
loading = '.' * n  # for strings, * is the repeat operator

for i in range(n+1):
    # this loop replaces each dot with a hash!
    print('\r%s Loading at %3d percent!' % (loading, i*100/n), end='')
    loading = loading[:i] + '#' + loading[i+1:]
    time.sleep(load)
    if i==n: print()

1
最简单的解决方案 - rakesh

3

我使用了format()方法来创建一个加载条,这是我的解决方案:

import time

loadbarwidth = 23

for i in range(1, loadbarwidth + 1):
    time.sleep(0.1) 

    strbarwidth = '[{}{}] - {}\r'.format(
        (i * '#'),
        ((loadbarwidth - i) * '-'),
        (('{:0.2f}'.format(((i) * (100/loadbarwidth))) + '%'))
    )

    print(strbarwidth ,end = '')

print()

输出:

[#######################] - 100.00%

我发现这种方法非常简单,而且不需要任何额外的库就可以很好地工作。谢谢@Matheus。 - nurub

2

您也可以使用 enlighten。主要优点是您可以同时记录日志,而不会覆盖您的进度条。

import time
import enlighten

manager = enlighten.Manager()
pbar = manager.counter(total=100)

for num in range(1, 101):
    time.sleep(0.05)
    print('Step %d complete' % num)
    pbar.update()

它还处理多个进度条。
import time
import enlighten

manager = enlighten.Manager()
odds = manager.counter(total=50)
evens = manager.counter(total=50)

for num in range(1, 101):
    time.sleep(0.05)
    if num % 2:
        odds.update()
    else:
        evens.update()

2

虽然已经有很多出色的答案,但我想分享一下我的进度条解决方案。

from time import sleep

def progress_bar(progress: float, total: float, width: int = 25):
    percent = width * ((progress + 1) / total)
    bar = chr(9608) * int(percent) + "-" * (width - int(percent))
    print(f"\r|{bar}| {(100/width)*percent:.2f}%", end="\r")

numbers = range(0, 1000)
numbersLen = len(numbers)
for i in numbers:
    sleep(0.01) # Do something usefull here
    progress_bar(i, numbersLen)

编辑:

如果你正在寻找一个能根据终端宽度自动调整宽度并且有可能在末尾显示消息的工具栏,那么这个也可以使用。请注意,如果终端太窄,工具栏会在一行中太宽而断裂,此时消息将消失。

def progressBar(progress: float, total: float, message: str = ""):
    terminalWidth = get_terminal_size().columns
    width = int(terminalWidth / 4)
    percent = width * ((progress + 1) / total)
    bar = chr(9608) * int(percent) + "-" * (width - int(percent))
    if terminalWidth <= 40:
        message = ""
    else:
        message = message + (" " * (int(terminalWidth / 2) - len(message)))
    print(f"\r|{bar}| {(100/width)*percent:.2f}% " + message, end="\r")

感谢@Handygold75,这非常接近我需要的内容,稍作修改你的答案,使其可以在上下文中调用。以下是我的发布。 - Bravhek

2
我喜欢Gabriel的答案,但我对其进行了修改以使其更具灵活性。您可以将进度条长度发送到函数中,从而获得任何所需长度的进度条。您不能使用零或负数长度创建进度条。此外,您可以像Gabriel的答案一样使用此函数(请参见示例#2)。
import sys
import time

def ProgressBar(Total, Progress, BarLength=20, ProgressIcon="#", BarIcon="-"):
    try:
        # You can't have a progress bar with zero or negative length.
        if BarLength <1:
            BarLength = 20
        # Use status variable for going to the next line after progress completion.
        Status = ""
        # Calcuting progress between 0 and 1 for percentage.
        Progress = float(Progress) / float(Total)
        # Doing this conditions at final progressing.
        if Progress >= 1.:
            Progress = 1
            Status = "\r\n"    # Going to the next line
        # Calculating how many places should be filled
        Block = int(round(BarLength * Progress))
        # Show this
        Bar = "[{}] {:.0f}% {}".format(ProgressIcon * Block + BarIcon * (BarLength - Block), round(Progress * 100, 0), Status)
        return Bar
    except:
        return "ERROR"

def ShowBar(Bar):
    sys.stdout.write(Bar)
    sys.stdout.flush()

if __name__ == '__main__':
    print("This is a simple progress bar.\n")

    # Example #1:
    print('Example #1')
    Runs = 10
    for i in range(Runs + 1):
        progressBar = "\rProgress: " + ProgressBar(10, i, Runs)
        ShowBar(progressBar)
        time.sleep(1)

    # Example #2:
    print('\nExample #2')
    Runs = 10
    for i in range(Runs + 1):
        progressBar = "\rProgress: " + ProgressBar(10, i, 20, '|', '.')
        ShowBar(progressBar)
        time.sleep(1)

    print('\nDone.')

# Example #2:
Runs = 10
for i in range(Runs + 1):
    ProgressBar(10, i)
    time.sleep(1)

结果:

这是一个简单的进度条。

示例1

进度:[###-------] 30%

示例2

进度:[||||||||||||........] 60%

完成。


2

如果你的工作无法被分解为可衡量的块,你可以在新线程中调用你的函数并计算它的执行时间:

import thread
import time
import sys

def work():
    time.sleep( 5 )

def locked_call( func, lock ):
    lock.acquire()
    func()
    lock.release()

lock = thread.allocate_lock()
thread.start_new_thread( locked_call, ( work, lock, ) )

# This part is icky...
while( not lock.locked() ):
    time.sleep( 0.1 )

while( lock.locked() ):
    sys.stdout.write( "*" )
    sys.stdout.flush()
    time.sleep( 1 )
print "\nWork Done"

您可以根据需要显然提高时间精度。


在哪里可以进行工作,以便在答案的代码中进行测量? - unseen_rider

2

我猜我有些晚了,不过这对于使用当前版本的Python 3的人应该是有效的,因为它使用了Python 3.6中引入的"f-strings",具体请参见PEP 498

代码

from numpy import interp

class Progress:
    def __init__(self, value, end, title='Downloading',buffer=20):
        self.title = title
        #when calling in a for loop it doesn't include the last number
        self.end = end -1
        self.buffer = buffer
        self.value = value
        self.progress()

    def progress(self):
        maped = int(interp(self.value, [0, self.end], [0, self.buffer]))
        print(f'{self.title}: [{"#"*maped}{"-"*(self.buffer - maped)}]{self.value}/{self.end} {((self.value/self.end)*100):.2f}%', end='\r')

例子

#some loop that does perfroms a task
for x in range(21)  #set to 21 to include until 20
    Progress(x, 21)

输出

Downloading: [########------------] 8/20 40.00%

2
这是我的简单解决方案:
import time

def progress(_cur, _max):
    p = round(100*_cur/_max)
    b = f"Progress: {p}% - ["+"."*int(p/5)+" "*(20-int(p/5))+"]"
    print(b, end="\r")

# USAGE:
for i in range(0,101):
    time.sleep(0.1) 
    progress(i,100)

print("..."*5, end="\r")
print("Done")

2
使用 progress库
pip install progress

这是我编写的一个自定义子类,用于将ETA/Elapsed时间格式化为更易读的格式:
import datetime
from progress.bar import IncrementalBar


class ProgressBar(IncrementalBar):
    '''
    My custom progress bar that:
       - Show %, count, elapsed, eta
       - Time is shown in H:M:S format
    '''

    message = 'Progress'
    suffix  = '%(percent).1f%% (%(index)d/%(max)d) -- %(elapsed_min)s (eta: %(eta_min)s)'

    def formatTime(self, seconds):
        return str(datetime.timedelta(seconds=seconds))

    @property
    def elapsed_min(self):
        return self.formatTime(self.elapsed)

    @property
    def eta_min(self):
        return self.formatTime(self.eta)

if __name__=='__main__':
    counter = 120
    bar     = ProgressBar('Processing', max=counter)

    for i in range(counter):
        bar.next()
        time.sleep(1)

    bar.finish()

2
一个非常简单的方法:
def progbar(count: int) -> None:
    for i in range(count):
        print(f"[{i*'#'}{(count-1-i)*' '}] - {i+1}/{count}", end="\r")
        yield i
    print('\n')

使用方法:

from time import sleep

for i in progbar(10):
    sleep(0.2) #whatever task you need to do

这是一个非常适用于现代 Python 的解决方案,它包含类型提示、f-strings 和无需导入(在实际使用中)。这是我最喜欢的! - ranvel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接