The amazing world of escape sequences - how to make a inline updating progress bar

I have always wondered how in the world people make the inline updating progress bars like the following:

Intro example

Well, if you wondered the same thing, at the end of this short post, you will wonder no more and maybe learn a thing or two about escape sequences.

Escape sequences are very handy, and if you’ve printed anything to the screen, you’ll know and love the family members such as \n for newlines and \t for tabs.

To draw a progress bar, the core idea revolves around the \b sequence. However, instead of marching ever forward and leaving a mess of characters in its wake, \b is special in that it positions the cursor back one space. For a elaborate way of printing b, we can write 'a\bb'. How this works is that it would first print a, then move the cursor back one space, and print b, thereby effectively overwriting a.

Another (language specific) fact we need to know is how to print on the same line. In Python, there are two ways of doing this. First we can do print('hello',) (note the comma at the end). However, this method currently only works for Python2, and does not work for Python3. The second and more cross platform method is to use sys.stdout.write, which, as the name suggests, writes to the standard out stream.

Finally, there is a super nifty sys.stdout.flush() function, which gives you the ability to control when user’s screen gets updated. If there are no flushes in the code, there won’t be any response from the progress bar. At program exit, it will draw everything at once.

With those three pieces of knowledge in mind, we can now write our program. The following code draws a basic self updating progress bar:

import time
import sys

sys.stdout.write('['+' '*10+']')
# moved *11 on account of ]
sys.stdout.write('\b'*11)
sys.stdout.flush()
for i in range(10):
    time.sleep(1)
    sys.stdout.write('.')
    sys.stdout.flush()
sys.stdout.write('] Done!\n')

First Progress Bar

For a slight improvement, instead of drawing just a . for each iteration of progress, we instead draw => for a prettier picture. This increases the complexity a bit due to the fact that we need to avoid drawing the second character on the last frame, thereby maintaining the meter width.

import time
import sys

sys.stdout.write('['+' '*10+']')
sys.stdout.write('\b'*10)
sys.stdout.flush()
for i in range(10):
    time.sleep(1)
    sys.stdout.write('\b' + '=')
    if(i < 9):
        sys.stdout.write('>')
    sys.stdout.flush()
sys.stdout.write('] Done!\n')

second progress bar

Finally, to round out the progress bar, we add some percentage numbers at the end, which is more complex by virtue of having to move the cursor back and forth to draw everything each step. It also has the added benefit of placing the cursor (gray rectangle) at the end, which gives the illusion that the progress bar is moving by itself.

import time
import sys

sys.stdout.write('['+' '*10+']  0%')
sys.stdout.flush()
for i in range(10):
    time.sleep(1)
    sys.stdout.write('\b'*(15-i) + '=')
    if(i < 9):
        sys.stdout.write('>')
    sys.stdout.write(' '*(8-i) + '] ' + str(i+1) + '0%')
    sys.stdout.flush()
# overwrite the percentage sign and write Done instead
sys.stdout.write('\b\b\b\bDone!\n')

third progress bar

That’s it folks! Now you know a (hopefully) brand new escape sequence and as an added bonus, how to draw inline self-updating progress bar.

If you want and tried and true package that can turn any iteration into a fancy iteration with progress bar, try tqdm

If you would like to get updates on random technical topics from frontend to backend, subscribe below. See you guys next week!

Back