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

例如,一个函数需要一段时间才能完成,完成后返回True。如何在函数执行期间显示进度条?

请注意,我需要这是实时的,所以我不知道该怎么做。我需要一根线吗?我不知道。

现在我没有打印任何东西,而函数正在执行,但一个进度条会很好。此外,我更感兴趣的是从代码的角度如何做到这一点。


当前回答

没有外部包。一段现成的代码。

您可以自定义进度条符号“#”,进度条大小,文本前缀等。

Python 3.3 +

import sys
def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.3+
    count = len(it)
    def show(j):
        x = int(size*j/count)
        print("{}[{}{}] {}/{}".format(prefix, "#"*x, "."*(size-x), j, count), 
                end='\r', file=out, flush=True)
    show(0)
    for i, item in enumerate(it):
        yield item
        show(i+1)
    print("\n", flush=True, file=out)

用法:

import time    
for i in progressbar(range(15), "Computing: ", 40):
    time.sleep(0.1) # any code you need

要填满整个字符空间,请使用unicode u"█"字符替换"#"。使用for i in progressbar(range(100)):…你会得到:

不需要第二个线程。上面的一些解决方案/包需要。 适用于任何可迭代对象,它指的是任何可以使用len()的对象。一个列表,一个字典,比如[' A ', 'b', 'c'…' g '] 使用生成器的工作只需要用list()来包装它。例如,对于i in progressbar(list(your_generator), "Computing: ", 40):除非工作在生成器中完成。在这种情况下,您需要另一种解决方案(如tqdm)。

您还可以通过将out更改为sys来更改输出。例如Stderr。


Python 3.6+ (f-string)

def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.6+
    count = len(it)
    def show(j):
        x = int(size*j/count)
        print(f"{prefix}[{u'█'*x}{('.'*(size-x))}] {j}/{count}", end='\r', file=out, flush=True)
    show(0)
    for i, item in enumerate(it):
        yield item
        show(i+1)
    print("\n", flush=True, file=out)

Python 2 (old-code)

import sys
def progressbar(it, prefix="", size=60, out=sys.stdout):
    count = len(it)
    def show(j):
        x = int(size*j/count)
        out.write("%s[%s%s] %i/%i\r" % (prefix, u"#"*x, "."*(size-x), j, count))
        out.flush()        
    show(0)
    for i, item in enumerate(it):
        yield item
        show(i+1)
    out.write("\n")
    out.flush()

其他回答

在这里寻找等效的解决方案后,我只是为我的需求做了一个简单的进度类。我想我应该把它贴出来。

from __future__ import print_function
import sys
import re


class ProgressBar(object):
    DEFAULT = 'Progress: %(bar)s %(percent)3d%%'
    FULL = '%(bar)s %(current)d/%(total)d (%(percent)3d%%) %(remaining)d to go'

    def __init__(self, total, width=40, fmt=DEFAULT, symbol='=',
                 output=sys.stderr):
        assert len(symbol) == 1

        self.total = total
        self.width = width
        self.symbol = symbol
        self.output = output
        self.fmt = re.sub(r'(?P<name>%\(.+?\))d',
            r'\g<name>%dd' % len(str(total)), fmt)

        self.current = 0

    def __call__(self):
        percent = self.current / float(self.total)
        size = int(self.width * percent)
        remaining = self.total - self.current
        bar = '[' + self.symbol * size + ' ' * (self.width - size) + ']'

        args = {
            'total': self.total,
            'bar': bar,
            'current': self.current,
            'percent': percent * 100,
            'remaining': remaining
        }
        print('\r' + self.fmt % args, file=self.output, end='')

    def done(self):
        self.current = self.total
        self()
        print('', file=self.output)

例子:

from time import sleep

progress = ProgressBar(80, fmt=ProgressBar.FULL)

for x in xrange(progress.total):
    progress.current += 1
    progress()
    sleep(0.1)
progress.done()

将打印以下内容:

[========] 17/80 (21%) 63

下面的代码是一个相当通用的解决方案,也有一个时间消耗和剩余时间估计。你可以使用任何可迭代对象。进度条的大小固定为25个字符,但可以使用完整、半块和四分之一块字符以1%的速度显示更新。输出如下所示:

 18% |████▌                    | \ [0:00:01, 0:00:06]

代码示例:

import sys, time
from numpy import linspace

def ProgressBar(iterObj):
  def SecToStr(sec):
    m, s = divmod(sec, 60)
    h, m = divmod(m, 60)
    return u'%d:%02d:%02d'%(h, m, s)
  L = len(iterObj)
  steps = {int(x):y for x,y in zip(linspace(0, L, min(100,L), endpoint=False),
                                   linspace(0, 100, min(100,L), endpoint=False))}
  qSteps = ['', u'\u258E', u'\u258C', u'\u258A'] # quarter and half block chars
  startT = time.time()
  timeStr = '   [0:00:00, -:--:--]'
  activity = [' -',' \\',' |',' /']
  for nn,item in enumerate(iterObj):
    if nn in steps:
      done = u'\u2588'*int(steps[nn]/4.0)+qSteps[int(steps[nn]%4)]
      todo = ' '*(25-len(done))
      barStr = u'%4d%% |%s%s|'%(steps[nn], done, todo)
    if nn>0:
      endT = time.time()
      timeStr = ' [%s, %s]'%(SecToStr(endT-startT),
                             SecToStr((endT-startT)*(L/float(nn)-1)))
    sys.stdout.write('\r'+barStr+activity[nn%4]+timeStr); sys.stdout.flush()
    yield item
  barStr = u'%4d%% |%s|'%(100, u'\u2588'*25)
  timeStr = '   [%s, 0:00:00]\n'%(SecToStr(time.time()-startT))
  sys.stdout.write('\r'+barStr+timeStr); sys.stdout.flush()

# Example
s = ''
for c in ProgressBar(list('Disassemble and reassemble this string')):
  time.sleep(0.2)
  s += c
print(s)

欢迎提出改进建议或其他意见。干杯!

使用进度库!

pip install progress

下面是我编写的一个自定义子类,用于将ETA/流逝时间格式化为更好读的格式:

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()

您应该将进度条链接到手头的任务(以便它度量进度:D)。例如,如果您正在对一个文件进行ftp传输,您可以告诉ftplib获取一个特定大小的缓冲区,比如128K,然后您将128K所代表的文件大小的百分比添加到进度条中。如果您正在使用CLI,并且进度表有20个字符长,那么当文件传输了1/20时,您将添加一个字符。

如果你的工作不能被分解成可测量的块,你可以在一个新的线程中调用你的函数,并记录它所花费的时间:

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"

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