Welcome, guest | Sign In | My Account | Store | Cart
NOTE: Recipes have moved! Please visit GitHub.com/activestate/code for the current versions.

Here is a little class that lets you present percent complete information in the form of a progress bar using the '#' character to represent completed portions, space to represent incomplete portions, and the actual percent done (rounded to integer) displayed in the middle:

[############# 33%                               ]

When you initialize the class, you specify the minimum number (usually 0), the maximum number (your file size, for example), and the number of characters wide you would like the progress bar to be. Note that width includes the brackets [] on the ends of the progress bar as well.

You'd probably want to use this in conjuction with the curses module, or something like that so you can over-write the same portion of the screen to make your updates 'animated'.

Python, 39 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class progressBar:
	def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
		self.progBar = "[]"   # This holds the progress bar string
		self.min = minValue
		self.max = maxValue
		self.span = maxValue - minValue
		self.width = totalWidth
		self.amount = 0       # When amount == max, we are 100% done 
		self.updateAmount(0)  # Build progress bar string

	def updateAmount(self, newAmount = 0):
		if newAmount < self.min: newAmount = self.min
		if newAmount > self.max: newAmount = self.max
		self.amount = newAmount

		# Figure out the new percent done, round to an integer
		diffFromMin = float(self.amount - self.min)
		percentDone = (diffFromMin / float(self.span)) * 100.0
		percentDone = round(percentDone)
		percentDone = int(percentDone)

		# Figure out how many hash bars the percentage should be
		allFull = self.width - 2
		numHashes = (percentDone / 100.0) * allFull
		numHashes = int(round(numHashes))

		# build a progress bar with hashes and spaces
		self.progBar = "[" + '#'*numHashes + ' '*(allFull-numHashes) + "]"

		# figure out where to put the percentage, roughly centered
		percentPlace = (len(self.progBar) / 2) - len(str(percentDone)) 
		percentString = str(percentDone) + "%"

		# slice the percentage into the bar
		self.progBar = self.progBar[0:percentPlace] + percentString + 
self.progBar[percentPlace+len(percentString):]

	def __str__(self):
		return str(self.progBar)

I used this along with curses to display a progress bar while copying a large file over the network. It was better than looking at an apparently "frozen" screen for a long time telling the user that the copy is really happening.

Of course, nothing ties this progress bar to file copy progress. You can use it for anything that takes a while to do and progress can be measured.

16 comments

kyle wub 14 years, 8 months ago  # | flag

How do you implement this into script? How can i implement this into my script?

Josiah Carlson 14 years, 1 month ago  # | flag

If you want to ACTUALLY use it, you're going to have to think for yourself.

import time
prog = progressBar(0, 100, 77)
for i in xrange(101):
    prog.updateAmount(i)
    print prog, "\r",
    time.sleep(.05)
Terry Carroll 13 years, 8 months ago  # | flag

Best not to print it every iteration. It can be a substantial performance hit to print the bar every iteration, whether it changes or not. At 100 iterations, like Josiah's example, it changes every iteration, so it won't matter. But if you have more than a few thousand, it's worth checking and not printing an unchanged bar:

limit = 1000000

prog = progressBar(0, limit, 77)
oldprog = str(prog)
for i in xrange(limit+1):
    prog.updateAmount(i)
    if oldprog != str(prog):
        print prog, "\r",
        oldprog=str(prog)

In this example (which is close to a real use I have, where I'm processing a file with a little over a million lines), the wall-clock time on my system consistently drops by about 80% (from over 2 minutes down to 24 seconds).

Also, remember to print a newline after your last print of the progress bar, or your next print statement will overlay it:

print
John Kirkham 12 years, 11 months ago  # | flag

curses version... This was a great example. Thanks. I'm very new to curses programming, but I thought I'd try implementing this in a curses panel.

This gives me the option of placing (or even moving) the panel to specific parts of the screen, independent of other UI elements in the curses window. Pretty cool - python makes a lot of things easy.

This could be implemented even more easily using pythondialog (http://pythondialog.sourceforge.net/)

which does all the work for you, but pythondialog is based on "dialog" (http://dickey.his.com/dialog/dialog.html)

which is GPL rather than LGPL so it restricts what you can do with it within a commercial setting.

I was going to post my "cursified" version of this example code, but it's getting badly garbled and truncated (or "Preview" is broken.)

I guess it'll have to suffice to say that it can be done easily with the standard python curses modules.

Ritesh Sarraf 12 years, 5 months ago  # | flag

How to use this code for a file remotely. I want to incorporate this class into my program which does the work of downloading debian based packages from the net. How to use it in that case ???

Edvard Majakari 12 years, 4 months ago  # | flag

Minor improvements. I modified the class a little, mainly added self._old_pbar (initialized to '') and added draw() method:

def draw(self):
    # draw progress bar - but only if it has changed

    if self.pbar_str != self._old_pbar:
        self._old_pbar = self.pbar_str
        sys.stdout.write(self.pbar_str + '\r')
        sys.stdout.flush()      # force updating of screen

I also created a new class AsciiProgressBar which derives from ProgressBar (draw() is an abstract method in the base class). I can send the code if somebody's interested.

Walter Brunswick 11 years, 8 months ago  # | flag

Interested? Am I!? Of course I am interested! Please paste the AsciiProgressBar class here or email it to walterbrunswick@sympatico.ca. Thank you!

Walter Brunswick 11 years, 8 months ago  # | flag

Instead... Instead send the entire code of your module, please. Your 'draw' function is not compatible with the existing code.

gururaj shivakumar 11 years, 8 months ago  # | flag

progress bar using curses. it was nice to see a code that works efficiently.. but how to implement this progress bar code onto curses window with color..

zongzhi liu 11 years, 8 months ago  # | flag

Intested in your progress bar. Please paste it here, or send to zz2liu@yahoo.com

gururaj shivakumar 11 years, 8 months ago  # | flag

cursified version. It was really good eg ,but its better to see the cursified versions of this.pls paste that code here...

gururaj shivakumar 11 years, 8 months ago  # | flag

using curses. hey pls paste code of cursified progressbar.. it was really cool to see this, waiting for curses display.

gururaj shivakumar 11 years, 8 months ago  # | flag

moving progressbar in curses. this is the code for cursified progressbar.. import curses if __name__ == "__main__": stdscr=curses.initscr() curses.start_color()

   curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
   curses.init_pair(2, curses.COLOR_RED,curses.COLOR_WHITE)
   curses.init_pair(3,curses.COLOR_BLUE,curses.COLOR_WHITE)
   curses.init_pair(4,curses.COLOR_WHITE,curses.COLOR_WHITE)
   curses.init_pair(5,curses.COLOR_BLUE,curses.COLOR_BLUE)
   curses.init_pair(6,curses.COLOR_BLACK,curses.COLOR_WHITE)
   s=curses.newwin(48,156,0,0)
   s.bkgd(ord(' '),curses.color_pair(1))
   s.box()
   s.refresh()
   s=stdscr.subwin(3,52,26,41)
   s.bkgd(ord(' ' ), curses.color_pair(3))
   s.box()

   s.refresh()


   s=stdscr.subwin(12,68,20,32)
   s.bkgd(ord(' ' ), curses.color_pair(6))
   s.addstr(1, 27, "PROGRESS",curses.color_pair(2))

# s.bkgdset(ord(' '), curses.color_pair(4)) s.box()

   s.refresh()

   diff=42
   s=stdscr.subwin(1,50,27,42)
   s.bkgd(ord(' ' ), curses.color_pair(4))
   #s.bkgdset(ord(' '),curses.color_pair(5))
   s.box()
   s.refresh()
   for x in range(26):

     s = stdscr.subwin(1, 2,27, diff)
     diff=diff+2
     s.bkgd (ord(' '), curses.color_pair(5))
     #limit = 10000
     #prog = progressBar(0, limit, 13)
     #oldprog =str(prog)
     #for i in xrange(limit+1):
     #     prog.updateAmount(i)

     #     print prog, "\r",
     time.sleep(0.20)
                                                                                                                                                                  s.refresh()
Kelvie Wong 11 years, 6 months ago  # | flag

Slightly improved. What a great class!

I tried to avoid the use of string concatenations (especially useful for quick updating progress bars; or when you don't have control over the update rate), and slightly modified the class (and added docstrings and a __call__ :p).

Just thought I'd share.

class progressBar:
    """ Creates a text-based progress bar. Call the object with the `print'
        command to see the progress bar, which looks something like this:

        [=======>        22%                  ]

        You may specify the progress bar's width, min and max values on init.
    """

    def __init__(self, minValue = 0, maxValue = 100, totalWidth=80):
        self.progBar = "[]"   # This holds the progress bar string
        self.min = minValue
        self.max = maxValue
        self.span = maxValue - minValue
        self.width = totalWidth
        self.amount = 0       # When amount == max, we are 100% done
        self.updateAmount(0)  # Build progress bar string

    def updateAmount(self, newAmount = 0):
        """ Update the progress bar with the new amount (with min and max
            values set at initialization; if it is over or under, it takes the
            min or max value as a default. """
        if newAmount &lt; self.min: newAmount = self.min
        if newAmount &gt; self.max: newAmount = self.max
        self.amount = newAmount

        # Figure out the new percent done, round to an integer
        diffFromMin = float(self.amount - self.min)
        percentDone = (diffFromMin / float(self.span)) * 100.0
        percentDone = int(round(percentDone))

        # Figure out how many hash bars the percentage should be
        allFull = self.width - 2
        numHashes = (percentDone / 100.0) * allFull
        numHashes = int(round(numHashes))

        # Build a progress bar with an arrow of equal signs; special cases for
        # empty and full
        if numHashes == 0:
            self.progBar = "[&gt;%s]" % (' '*(allFull-1))
        elif numHashes == allFull:
            self.progBar = "[%s]" % ('='*allFull)
        else:
            self.progBar = "[%s&gt;%s]" % ('='*(numHashes-1),
                                        ' '*(allFull-numHashes))

        # figure out where to put the percentage, roughly centered
        percentPlace = (len(self.progBar) / 2) - len(str(percentDone))
        percentString = str(percentDone) + "%"

        # slice the percentage into the bar
        self.progBar = ''.join([self.progBar[0:percentPlace], percentString,
                                self.progBar[percentPlace+len(percentString):]
                                ])

    def __str__(self):
        return str(self.progBar)

(comment continued...)

Kelvie Wong 11 years, 6 months ago  # | flag

(...continued from previous comment)

    def __call__(self, value):
        """ Updates the amount, and writes to stdout. Prints a carriage return
            first, so it will overwrite the current line in stdout."""
        print '\r',
        self.updateAmount(value)
        sys.stdout.write(str(self))
        sys.stdout.flush()
Thomas Dickey 11 years, 1 month ago  # | flag

dialog is LGPL. I reviewed it last year, found that I had replaced all of the original code (aside from the boilerplate license comments), and released it with an LGPL license.

Created by Randy Pargman on Wed, 11 Dec 2002 (PSF)
Python recipes (4591)
Randy Pargman's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks