Animated Terminal Progress Bar in Python

In my previous article python terminal controller, I explained a simple way to control display in the terminal and print in colors. In this article I'm going to use the terminal module to draw a fancy animated progress bar.

progressbar

The following module simply draws an animated progress bar in the terminal along with a message. The progress bar will auto adjust itself to fit. And the message can spread over multiple lines.

# -*- coding: utf-8 -*-
# Copyright: 2009 Nadia Alramli
# License: BSD
"""Draws an animated terminal progress bar
Usage:
    p = ProgressBar("blue")
    p.render(percentage, message)
"""
 
import terminal
import sys
 
class ProgressBar(object):
    """Terminal progress bar class"""
    TEMPLATE = (
     '%(percent)-2s%% %(color)s%(progress)s%(normal)s%(empty)s %(message)s\n'
    )
    PADDING = 7
 
    def __init__(self, color=None, width=None, block='█', empty=' '):
        """
        color -- color name (BLUE GREEN CYAN RED MAGENTA YELLOW WHITE BLACK)
        width -- bar width (optinal)
        block -- progress display character (default '█')
        empty -- bar display character (default ' ')
        """
        if color:
            self.color = getattr(terminal, color.upper())
        else:
            self.color = ''
        if width and width < terminal.COLUMNS - self.PADDING:
            self.width = width
        else:
            # Adjust to the width of the terminal
            self.width = terminal.COLUMNS - self.PADDING
        self.block = block
        self.empty = empty
        self.progress = None
        self.lines = 0
 
    def render(self, percent, message = ''):
        """Print the progress bar
        percent -- the progress percentage %
        message -- message string (optional)
        """
        inline_msg_len = 0
        if message:
            # The length of the first line in the message
            inline_msg_len = len(message.splitlines()[0])
        if inline_msg_len + self.width + self.PADDING > terminal.COLUMNS:
            # The message is too long to fit in one line.
            # Adjust the bar width to fit.
            bar_width = terminal.COLUMNS - inline_msg_len -self.PADDING
        else:
            bar_width = self.width
 
        # Check if render is called for the first time
        if self.progress != None:
            self.clear()
        self.progress = (bar_width * percent) / 100
        data = self.TEMPLATE % {
            'percent': percent,
            'color': self.color,
            'progress': self.block * self.progress,
            'normal': terminal.NORMAL,
            'empty': self.empty * (bar_width - self.progress),
            'message': message
        }
        sys.stdout.write(data)
        sys.stdout.flush()
        # The number of lines printed
        self.lines = len(data.splitlines())
 
    def clear(self):
        """Clear all printed lines"""
        sys.stdout.write(
            self.lines * (terminal.UP + terminal.BOL + terminal.CLEAR_EOL)
        )

This module is very easy to use, for example:

from progressbar import ProgressBar
import time
p = ProgressBar()
for i in range(101):
    p.render(i, 'step %s' % i)
    time.sleep(0.1)

default progressbar

You can pass in any character to be used as a progress display. You can change the color and width as well. Here is another fancy example:

from progressbar import ProgressBar
import time
p = ProgressBar('green', width=20, block='▣', empty='□')
for i in range(101):
    p.render(i, 'step %s\nProcessing...\nDescription: write something.' % i)
    time.sleep(0.1)

fancy progressbar

Here is a useful list of unicode characters. Any of these characters can be used to draw the progress bar if needed.

AttachmentSize
Progress Bar Module2.49 KB

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Using as GPL

Hello
Can I use your code as GPL?
My project(PyGlossary) is under GPLv3 and I need to relicense your code to GPL and use in my own project.

Sure

Sure, no problem at all.

FYI You don't need to

FYI You don't need to relicense to include BSD code in a GPL project.

Errors on Leopard 10.5.8

Hi, thanks for making this. I'm using a Mac and I got this error when I used it:

Warning: setupterm: could not find terminfo database
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'
function __init__ in progressbar.py at line 35
self.width = terminal.COLUMNS - self.PADDING

I'm using Python 2.5.1

Ooops

Sorry, I was running the app on Textmate. I ran it using the terminal and it worked perfectly! However, the number of records I'm processing is in the range of 20,000+. How do I make the display more or less accurate?


p = ProgressBar()
while count < len(contents):
if contents[count][0] != '':
cursor.execute("""
UPDATE accountscf SET cf_580 = %s, cf_568 = %s, cf_569 = %s, cf_571 = %s, cf_572 = %s
WHERE cf_535 = %s""" % (contents[count][1], contents[count][2], contents[count][3], contents[count][4], contents[count][5], contents[count][0]))
p.render(count, 'step %s' % count)
time.sleep(0.1)
count += 1

Use the percentage instead.

Use the percentage instead. For example:

total = 20000
p.render((count * 100) / total, 'step %s' % count)

animated spinner

Very nice. I love the filled in squares version. On a related note here is a spinner I did for when you don't know how long it will take:
http://www.pixelbeat.org/talks/python/spinner.py

I just discovered this today

I just discovered this today and I absolutely love the simplicity of it. I have implemented progress bars for command-line tools at work in the past but none were this elegant. I am already looking into how I can integrate it and rip out my old kludgy implementation. Thank you for sharing!

progressbar module in python archives

Nice script! I dont know if this is common knowledge. But, there is a well covered python module at http://pypi.python.org/pypi/progressbar/2.2

Interesting I didn't know

Interesting I didn't know about this module before. I'll check it out.

I was just about to post that

I was just about to post that link, but Ros beat me to it.

What about merging your additional features back into the progressbar package in PyPi?

I didn't know about this

I didn't know about this package before so I need to try it first. I wrote this module for personal use at first, then I decided to share it here.

BTW

It is great to see my page in

It is great to see my blog on my favorite website :) - A fellow redditor

Redirection

I'd love to use this in a project, but I noticed that when I export the stdout to a file, it just looks all messed up. Is there some way to detect if they are doing that and then print something else instead of using the Progress Bar?

Thanks.

I don't think it is possible.

I don't think it is possible. A workaround is to modify the progressbar module to use sys.stderr instead of sys.stdout. But it's not a good idea since the progress is not an error.

I only just read this

I only just read this article, so this is a bit late but thought I'd add it anyway.

You can use the following code to determine if stdout is a terminal and only display the progress bar if it is.
os.isatty(sys.stdout.fileno())

Nice one!

Nice work, Nadia.

Love the customizability and the code. Thank you for this :)

m new ... but how do i put

m new ... but how do i put the code to copy folder or do something...
if the logic is inside the for-loop then wouldn`t it do the n times

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options