Welcome, guest | Sign In | My Account | Store | Cart

A small proxy iterator showing a progress bar on the console. Only works with iterators that provide a length (i.e. len(iterator) must be defined).

Python, 15 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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) # long computation

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.

7 comments

kamidox 14 years, 3 months ago  # | flag

I have tried in my system(python 2.6.2), I have to flush stdout to make it work well, or I can not see the animate.

Michael Grünewald (author) 14 years, 3 months ago  # | flag

@kamidox Huang: What's your operating system? Maybe this recipe does only work under Windows.

Richard F. Burghause 14 years, 3 months ago  # | flag

I've tried this on OS X (Python 2.6) ... it does not flush automatically. There's (at least) two ways to address this issue:

  1. Run Python with the -u (unbuffered stdout) option
  2. add a sys.stdout.flush() after the print statement

Both ways work fine for me, though I find the second one a little bit more handy.

Richard F. Burghause 14 years, 3 months ago  # | flag

To run this under Python 3, a few changes are necessary:

  1. size*i/count will return a float, which won't work with string multiplication. Cast to int - OK
  2. print is a function now, the trailing comma to prevent a newline has been replaced by setting the keyword argument end=''

And that's it. I've tested it on 3.1 (OS X 10.6), works fine there (not tested anywhere else).

import sys

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

    _show(0)
    for i, item in enumerate(it):
        yield item
        _show(i+1)
    print()
Michael Grünewald (author) 14 years, 3 months ago  # | flag

Thanks for all the feedback. I updated the recipe to run under Python 2.x and 3.x on all platforms. I used sys.stdout.write instead of print because it behaves in the same way on all versions of Python. std.stdout.flush() does not hurt under Windows so I included it.

(Tested with Python 2.6 and Python 3.1 under win32/x86.)

Sridhar Ratnakumar 13 years, 10 months ago  # | flag

I adopted the iterator approach for PyPM's progress bar; re-usable via the applib package:

from applib import textui

packages = [...]
for pkgfile in textui.ProgressBar.iterate(packages):
    time.sleep(1) # do something with packages

Link to textui.py: http://bitbucket.org/srid/applib/src/tip/applib/textui.py#cl-14

Tested on Windows, Linux and Mac. Also works on Python 3. Here's a sample screenshot:

Screenshot

Nir Soffer 13 years, 10 months ago  # | flag

Great I didn't you can do this so easy at the console. However, I'm not sure about the iterator stuff. A progress bar will be more useful when you can change the state of the progress bar when you make actual progress, not on regular intervals. You don't want the progress to advance on each step.

I had very good experience with this: http://developer.apple.com/mac/library/documentation/cocoa/reference/ApplicationKit/Classes/NSProgressIndicator_Class/Reference/Reference.html