Python Progress Bar

2018-12-31 18:45发布

问题:

How do I use a progress bar when my script is doing some task that is likely to take time?

For example, a function which takes some time to complete and returns True when done. How can I display a progress bar during the time the function is being executed?

Note that I need this to be in real time, so I can\'t figure out what to do about it. Do I need a thread for this? I have no idea.

Right now I am not printing anything while the function is being executed, however a progress bar would be nice. Also I am more interested in how this can be done from a code point of view.

回答1:

There are specific libraries (like this one here) but maybe something very simple would do:

import time
import sys

toolbar_width = 40

# setup toolbar
sys.stdout.write(\"[%s]\" % (\" \" * toolbar_width))
sys.stdout.flush()
sys.stdout.write(\"\\b\" * (toolbar_width+1)) # return to start of line, after \'[\'

for i in xrange(toolbar_width):
    time.sleep(0.1) # do real work here
    # update the bar
    sys.stdout.write(\"-\")
    sys.stdout.flush()

sys.stdout.write(\"\\n\")

Note: this progressbar is a fork of progressbar which hasn\'t been maintained in years.



回答2:

With tqdm you can add a progress meter to your loops in a second:

In [20]: import time

In [21]: from tqdm import tqdm

In [23]: for i in tqdm(range(10)):
   ....:     time.sleep(3)

 60%|██████    | 6/10 [00:18<00:12,  0.33 it/s]


回答3:

The above suggestions are pretty good, but I think most people just want a ready made solution, with no dependencies on external packages, but is also reusable.

I got the best points of all the above, and made it into a function, along with a test cases.

To use it, just copy the lines under \"def update_progress(progress)\" but not the test script. Don\'t forget to import sys. Call this whenever you need to display or update the progress bar.

This works by directly sending the \"\\r\" symbol to console to move cursor back to the start. \"print\" in python does not recongise the above symbol for this purpose, hence we need \'sys\'

import time, sys

# update_progress() : Displays or updates a console progress bar
## Accepts a float between 0 and 1. Any int will be converted to a float.
## A value under 0 represents a \'halt\'.
## A value at 1 or bigger represents 100%
def update_progress(progress):
    barLength = 10 # Modify this to change the length of the progress bar
    status = \"\"
    if isinstance(progress, int):
        progress = float(progress)
    if not isinstance(progress, float):
        progress = 0
        status = \"error: progress var must be float\\r\\n\"
    if progress < 0:
        progress = 0
        status = \"Halt...\\r\\n\"
    if progress >= 1:
        progress = 1
        status = \"Done...\\r\\n\"
    block = int(round(barLength*progress))
    text = \"\\rPercent: [{0}] {1}% {2}\".format( \"#\"*block + \"-\"*(barLength-block), progress*100, status)
    sys.stdout.write(text)
    sys.stdout.flush()


# update_progress test script
print \"progress : \'hello\'\"
update_progress(\"hello\")
time.sleep(1)

print \"progress : 3\"
update_progress(3)
time.sleep(1)

print \"progress : [23]\"
update_progress([23])
time.sleep(1)

print \"\"
print \"progress : -10\"
update_progress(-10)
time.sleep(2)

print \"\"
print \"progress : 10\"
update_progress(10)
time.sleep(2)

print \"\"
print \"progress : 0->1\"
for i in range(100):
    time.sleep(0.1)
    update_progress(i/100.0)

print \"\"
print \"Test completed\"
time.sleep(10)

This is what the result of the test script shows (The last progress bar animates):

progress : \'hello\'
Percent: [----------] 0% error: progress var must be float
progress : 3
Percent: [##########] 100% Done...
progress : [23]
Percent: [----------] 0% error: progress var must be float

progress : -10
Percent: [----------] 0% Halt...

progress : 10
Percent: [##########] 100% Done...

progress : 0->1
Percent: [##########] 99.0%
Test completed


回答4:

for a similar application (keeping track of the progress in a loop) I simply used the python-progressbar:

Their example goes something like this,

from progressbar import *               # just a simple progress bar


widgets = [\'Test: \', Percentage(), \' \', Bar(marker=\'0\',left=\'[\',right=\']\'),
           \' \', ETA(), \' \', FileTransferSpeed()] #see docs for other options

pbar = ProgressBar(widgets=widgets, maxval=500)
pbar.start()

for i in range(100,500+1,50):
    # here do something long at each iteration
    pbar.update(i) #this adds a little symbol at each iteration
pbar.finish()
print


回答5:

I\'ve just made a simple progress class for my needs after searching here for a equivalent solution. I thought I might a well post it.

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)

Example :

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

Will print the following:

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



回答6:

Try progress from https://pypi.python.org/pypi/progress.

from progress.bar import Bar

bar = Bar(\'Processing\', max=20)
for i in range(20):
    # Do some work
    bar.next()
bar.finish()

The result will be a bar like the following:

Processing |#############                   | 42/100


回答7:

I like Brian Khuu\'s answer for its simplicity and not needing external packages. I changed it a bit so I\'m adding my version here:

import sys
import time


def updt(total, progress):
    \"\"\"
    Displays or updates a console progress bar.

    Original source: https://stackoverflow.com/a/15860757/1391441
    \"\"\"
    barLength, status = 20, \"\"
    progress = float(progress) / float(total)
    if progress >= 1.:
        progress, status = 1, \"\\r\\n\"
    block = int(round(barLength * progress))
    text = \"\\r[{}] {:.0f}% {}\".format(
        \"#\" * block + \"-\" * (barLength - block), round(progress * 100, 0),
        status)
    sys.stdout.write(text)
    sys.stdout.flush()


runs = 300
for run_num in range(runs):
    time.sleep(.1)
    updt(runs, run_num + 1)

It takes the total number of runs (total) and the number of runs processed so far (progress) assuming total >= progress. The result looks like this:

[#####---------------] 27%


回答8:

Many of the answers above rely on external packages, but I also think (as some above stated) that most people just want a ready-made solution. The code below can be adapted to fit your needs by customizing the string part.

It is simpler and works without the need of a second thread to update the bar. Some packages above do that. A second thread can be a problem, for an ipython notebook, for example.

The code below only works with iterators that provide a length (i.e. len(iterator) must be defined).

import sys

def progressbar(it, prefix=\"\", size=60):
    count = len(it)
    def _show(_i):
        x = int(size*_i/count)
        sys.stdout.write(\"%s[%s%s] %i/%i\\r\" % (prefix, \"#\"*x, \".\"*(size-x), _i, count))
        sys.stdout.flush()

    _show(0)
    for i, item in enumerate(it):
        yield item
        _show(i+1)
    sys.stdout.write(\"\\n\")
    sys.stdout.flush()

Example:

import time

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

Output:

Computing: [........................................] 0/15

...

Computing: [########................................] 3/15

...

Computing: [########################################] 15/15

it can be any iterable object with a len, e.g. [\'a\', \'b\', \'c\']` works just fine.



回答9:

I really like the python-progressbar, as it is very simple to use.

For the most simple case, it is just:

import progressbar
import time

progress = progressbar.ProgressBar()
for i in progress(range(80)):
    time.sleep(0.01)

The appearance can be customized and it can display the estimated remaining time. For an example use the same code as above but with:

progress = progressbar.ProgressBar(widgets=[progressbar.Bar(\'=\', \'[\', \']\'), \' \',
                                            progressbar.Percentage(), \' \',
                                            progressbar.ETA()])


回答10:

If it is a big loop with a fixed amount of iterations that is taking a lot of time you can use this function I made. Each iteration of loop adds progress. Where count is the current iteration of the loop, total is the value you are looping to and size(int) is how big you want the bar in increments of 10 i.e. (size 1 =10 chars, size 2 =20 chars)

import sys
def loadingBar(count,total,size):
    percent = float(count)/float(total)*100
    sys.stdout.write(\"\\r\" + str(int(count)).rjust(3,\'0\')+\"/\"+str(int(total)).rjust(3,\'0\') + \' [\' + \'=\'*int(percent/10)*size + \' \'*(10-int(percent/10))*size + \']\')

example:

for i in range(0,100):
     loadingBar(i,100,2)
     #do some code 

output:

i = 50
>> 050/100 [==========          ]


回答11:

Use this library: fish (GitHub).

Usage:

>>> import fish
>>> while churning:
...     churn_churn()
...     fish.animate()

Have fun!



回答12:

I like this page.

Starts with simple example and moves onto a multi-threaded version. Works out of the box. No 3rd party packages required.

The code will look something like this:

import time
import sys

def do_task():
    time.sleep(1)

def example_1(n):
    for i in range(n):
        do_task()
        print \'\\b.\',
        sys.stdout.flush()
    print \' Done!\'

print \'Starting \',
example_1(10)

Or here is example to use threads in order to run the spinning loading bar while the program is running:

import sys
import time
import threading

class progress_bar_loading(threading.Thread):

    def run(self):
            global stop
            global kill
            print \'Loading....  \',
            sys.stdout.flush()
            i = 0
            while stop != True:
                    if (i%4) == 0: 
                        sys.stdout.write(\'\\b/\')
                    elif (i%4) == 1: 
                        sys.stdout.write(\'\\b-\')
                    elif (i%4) == 2: 
                        sys.stdout.write(\'\\b\\\\\')
                    elif (i%4) == 3: 
                        sys.stdout.write(\'\\b|\')

                    sys.stdout.flush()
                    time.sleep(0.2)
                    i+=1

            if kill == True: 
                print \'\\b\\b\\b\\b ABORT!\',
            else: 
                print \'\\b\\b done!\',


kill = False      
stop = False
p = progress_bar_loading()
p.start()

try:
    #anything you want to run. 
    time.sleep(1)
    stop = True
except KeyboardInterrupt or EOFError:
         kill = True
         stop = True


回答13:

It is quite straightforward in Python3:

   import time
   import math

    def show_progress_bar(bar_length, completed, total):
        bar_length_unit_value = (total / bar_length)
        completed_bar_part = math.ceil(completed / bar_length_unit_value)
        progress = \"*\" * completed_bar_part
        remaining = \" \" * (bar_length - completed_bar_part)
        percent_done = \"%.2f\" % ((completed / total) * 100)
        print(f\'[{progress}{remaining}] {percent_done}%\', end=\'\\r\')

    bar_length = 30
    total = 100
    for i in range(0, total + 1):
        show_progress_bar(bar_length, i, total)
        time.sleep(0.1)

    print(\'\\n\')


回答14:

If your work can\'t be broken down into measurable chunks, you could call your function in a new thread and time how long it takes:

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\"

You can obviously increase the timing precision as required.



回答15:

Here\'s a short solution that builds the loading bar programmatically (you must decide how long you want it).

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)


回答16:

I like Gabriel answer, but i changed it to be flexible. You can send bar-length to the function and get your progress bar with any length that you want. And you can\'t have a progress bar with zero or negative length. Also, you can use this function like Gabriel answer (Look at the Example #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)

Result:

This is a simple progress bar.

Example #1

Progress: [###-------] 30%

Example #2

Progress: [||||||||||||........] 60%

Done.



回答17:

Try PyProg. PyProg is an open-source library for Python to create super customizable progress indicators & bars.

It is currently at version 1.0.2; it is hosted on Github and available on PyPI (Links down below). It is compatible with Python 3 & 2 and it can also be used with Qt Console.

It is really easy to use. The following code:

import pyprog
from time import sleep

# Create Object
prog = pyprog.ProgressBar(\" \", \"\", 34)
# Update Progress Bar
prog.update()

for i in range(34):
    # Do something
    sleep(0.1)
    # Set current status
    prog.set_stat(i + 1)
    # Update Progress Bar again
    prog.update()

# Make the Progress Bar final
prog.end()

will produce:

Initial State:
Progress: 0% --------------------------------------------------

When half done:
Progress: 50% #########################-------------------------

Final State:
Progress: 100% ##################################################

I actually made PyProg because I needed a simple but super customizable progress bar library. You can easily install it with: pip install pyprog.

PyProg Github: https://github.com/Bill13579/pyprog
PyPI: https://pypi.python.org/pypi/pyprog/



回答18:

The code below is a quite general solution and also has a time elapsed and time remaining estimate. You can use any iterable with it. The progress bar has a fixed size of 25 characters but it can show updates in 1% steps using full, half, and quarter block characters. The output looks like this:

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

Code with example:

import sys, time
from numpy import linspace

def ProgressBar(iterObj, refreshTime=10):
  #refreshTime=10: refresh the time estimate at least every 10 sec.
  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(np.linspace(0,L,  min(100,L),endpoint=False), 
                                     np.linspace(0,100,min(100,L),endpoint=False))}
  qSteps  = [\'\', u\'\\u258E\',u\'\\u258C\',u\'\\u258A\'] # quarter and half block chars
  startT  = endT = time.time()
  timeStr = \' [0:00:00, -:--:--]\'
  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+timeStr); sys.stdout.flush()
    elif time.time()-endT > refreshTime:
      endT    = time.time()
      timeStr = \' [%s, %s]\'%(SecToStr(endT-startT), SecToStr((endT-startT)*(L/float(nn)-1)))
      sys.stdout.write(\'\\r\'+barStr+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 op in ProgressBar(list(\'Disassemble and reassemble this string\')):
  time.sleep(0.5)
  s += op
print s

Suggestions for improvements or other comments are welcome. Have fun.



回答19:

When running in jupyter notebooks use of normal tqdm doesn\'t work, as it writes output on multiple lines. Use this instead:

import time
from tqdm import tqdm_notebook as tqdm

for i in tqdm(range(100))
    time.sleep(0.5)


回答20:

You should link the progress bar to the task at hand (so that it measures the progress :D). For example, if you are FTPing a file, you can tell ftplib to grab a certain size buffer, let\'s say 128K, and then you add to your progress bar whatever percentage of the filesize 128k represents. If you are using the CLI, and your progress meter is 20 characters long, you would add one character when 1/20th of the file had transferred.



回答21:

@Massagran: It works well in my programs. Furthermore, we need to add a counter to indicate the loop times. This counter plays as the argument of the method update. For example: read all lines of a test file and treat them on something. Suppose that the function dosth() do not concern in the variable i.

lines = open(sys.argv[1]).readlines()
i = 0
widgets=[Percentage(), Bar()]
pbar = ProgressBar(widgets=widgets,maxval=len(lines)).start()
pbar.start()
for line in lines:<pre>
    dosth();
    i += 1
    pbar.update(i)</pre>
pbar.finish()

The variable i controls the status of pbar via the method update



回答22:

You can also use enlighten. The main advantage is you can log at the same time without overwriting your progress bar.

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

It also handles multiple progress bars.

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


回答23:

a little more generic answer of jelde015 (credit to him of course)

for updating the loading bar manually will be:

import sys
from math import *


def loadingBar(i, N, size):
    percent = float(i) / float(N)
    sys.stdout.write(\"\\r\"
                     + str(int(i)).rjust(3, \'0\')
                     +\"/\"
                     +str(int(N)).rjust(3, \'0\')
                     + \' [\'
                     + \'=\'*ceil(percent*size)
                     + \' \'*floor((1-percent)*size)
                     + \']\')

and calling it by:

loadingBar(7, 220, 40)

will result:

007/220 [=                                       ]  

just call it whenever you want with the current i value.

set the size as the number of chars the bar should be



标签: python