Simple animations, progress bars, and clearing output

Sometimes you want to print progress in-place, but don't want to keep growing the output area. In terminals, there is the carriage-return ('\r') for overwriting a single line, but the notebook frontend does not support this behavior (yet).

What the notebook does support is explicit clear_output, and you can use this to replace previous output (specifying stdout/stderr or the special IPython display outputs).

A simple example printing our progress iterating through a list:


In [ ]:
import sys
import time

In [ ]:
from IPython.display import clear_output
for i in range(10):
    time.sleep(0.25)
    clear_output()
    print i
    sys.stdout.flush()

The AsyncResult object has a special wait_interactive() method, which prints its progress interactively, so you can watch as your parallel computation completes.

This example assumes you have an IPython cluster running, which you can start from the cluster panel


In [ ]:
from IPython import parallel
rc = parallel.Client()
view = rc.load_balanced_view()

amr = view.map_async(time.sleep, [0.5]*100)

amr.wait_interactive()

You can also use clear_output() to clear figures and plots.

This time, we need to make sure we are using inline pylab (requires matplotlib)


In [ ]:
%pylab inline

In [ ]:
from scipy.special import jn
x = np.linspace(0,5)
f, ax = plt.subplots()
ax.set_title("Bessel functions")

for n in range(1,10):
    time.sleep(1)
    ax.plot(x, jn(x,n))
    clear_output()
    display(f)

# close the figure at the end, so we don't get a duplicate
# of the last plot
plt.close()

A Javascript Progress Bar

clear_output() is still something of a hack, and if you want to do a progress bar in the notebook it is better to just use Javascript/HTML if you can.

Here is a simple progress bar using HTML/Javascript:


In [ ]:
import uuid
from IPython.display import HTML, Javascript, display

divid = str(uuid.uuid4())

pb = HTML(
"""
<div style="border: 1px solid black; width:500px">
  <div id="%s" style="background-color:blue; width:0%%">&nbsp;</div>
</div> 
""" % divid)
display(pb)
for i in range(1,101):
    time.sleep(0.1)
    
    display(Javascript("$('div#%s').width('%i%%')" % (divid, i)))

The above simply makes a div that is a box, and a blue div inside it with a unique ID (so that the javascript won't collide with other similar progress bars on the same page).

Then, at every progress point, we run a simple jQuery call to resize the blue box to the appropriate fraction of the width of its containing box, and voilà a nice HTML/Javascript progress bar!

ProgressBar class

And finally, here is a progress bar class extracted from PyMC, which will work in regular Python as well as in the IPython Notebook


In [ ]:
import sys, time
try:
    from IPython.display import clear_output
    have_ipython = True
except ImportError:
    have_ipython = False

class ProgressBar:
    def __init__(self, iterations):
        self.iterations = iterations
        self.prog_bar = '[]'
        self.fill_char = '*'
        self.width = 40
        self.__update_amount(0)
        if have_ipython:
            self.animate = self.animate_ipython
        else:
            self.animate = self.animate_noipython

    def animate_ipython(self, iter):
        print '\r', self,
        sys.stdout.flush()
        self.update_iteration(iter + 1)

    def update_iteration(self, elapsed_iter):
        self.__update_amount((elapsed_iter / float(self.iterations)) * 100.0)
        self.prog_bar += '  %d of %s complete' % (elapsed_iter, self.iterations)

    def __update_amount(self, new_amount):
        percent_done = int(round((new_amount / 100.0) * 100.0))
        all_full = self.width - 2
        num_hashes = int(round((percent_done / 100.0) * all_full))
        self.prog_bar = '[' + self.fill_char * num_hashes + ' ' * (all_full - num_hashes) + ']'
        pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))
        pct_string = '%d%%' % percent_done
        self.prog_bar = self.prog_bar[0:pct_place] + \
            (pct_string + self.prog_bar[pct_place + len(pct_string):])

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

In [ ]:
p = ProgressBar(1000)
for i in range(1001):
    p.animate(i)

In [ ]: