211

To implement a status bar like below:

[==========                ]  45%
[================          ]  60%
[==========================] 100%

I want to this to be printed out to stdout, and keep refreshing it, not print to another line. How to do this?

martineau
  • 119,623
  • 25
  • 170
  • 301
Stan
  • 37,207
  • 50
  • 124
  • 185
  • 2
    I've published a new kind of progress bar, which you can print, see throughput and eta, even pause it, besides the very cool animations! Please take a look: https://github.com/rsalmei/alive-progress ![alive-progress](https://raw.githubusercontent.com/rsalmei/alive-progress/master/img/main.gif) – rsalmei Aug 23 '19 at 06:44

23 Answers23

307

The '\r' character (carriage return) resets the cursor to the beginning of the line and allows you to write over what was previously on the line.

from time import sleep
import sys

for i in range(21):
    sys.stdout.write('\r')
    # the exact output you're looking for:
    sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
    sys.stdout.flush()
    sleep(0.25)

I'm not 100% sure if this is completely portable across all systems, but it works on Linux and OSX at the least.

Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • 7
    This is better than the marked answer, because it works with Python 3, too. – Johann Hagerer Jun 02 '16 at 09:32
  • any idea why I get strange characters at the end of a line at times, even if i null-terminate the string that is being written? – reservoirman Feb 20 '17 at 20:19
  • 40
    As written, the code does not make it clear how to adapt this to whatever size you are iterating over (not 21). I would let `n=21`, replace `range(21)` with `range(n)`, then add `j = (i + 1) / n` inside the loop, and replace the `write` statement with this slight modification: `sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j))`. Now the only change you need to make is to `n=21` before the loop (more likely `n=len(iterable)`), then enumerate over the iterable object. I recommended this edit but it was rejected; apparently functionality "deviates from the original intent of the post". – Steven C. Howell Oct 17 '17 at 20:55
  • I used this answer because I'm on Python 3. – Michael Mathews Nov 02 '17 at 17:56
  • 2
    Adaptive with arbitrary size of n, `sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))`, Python 3 only – GabrielChu Jan 08 '18 at 22:53
  • 1
    What is `%-20s`? – User Dec 15 '18 at 17:51
  • 3
    @StevenC.Howell why you don't post it as an answer citing Mark? it's another version and it's very helpful to get it ready – G M Jul 05 '19 at 15:54
  • in Python 3 one can do with just 1 line within the loop and without `sys`: `print ("[%-20s] %d%%" % ('='*i, 5*i), end = '\r')` – the.real.gruycho Oct 19 '20 at 14:12
178

There's a Python module that you can get from PyPI called progressbar that implements such functionality. If you don't mind adding a dependency, it's a good solution. Otherwise, go with one of the other answers.

A simple example of how to use it:

import progressbar
from time import sleep
bar = progressbar.ProgressBar(maxval=20, \
    widgets=[progressbar.Bar('=', '[', ']'), ' ', progressbar.Percentage()])
bar.start()
for i in xrange(20):
    bar.update(i+1)
    sleep(0.1)
bar.finish()

To install it, you can use easy_install progressbar, or pip install progressbar if you prefer pip.

Ade Miller
  • 13,575
  • 1
  • 42
  • 75
icktoofay
  • 126,289
  • 21
  • 250
  • 231
  • All answers are awesome, however, I like module the most. Thanks to everyone. – Stan Jun 09 '10 at 00:58
  • Sadly neither `progressbar` nor `fish` seems to work with python3. – Jim Garrison Sep 16 '14 at 06:47
  • Nevermind, I [fixed `fish` to work with python3](https://github.com/garrison/fish/tree/py3k). – Jim Garrison Sep 16 '14 at 07:18
  • @JimRaynor: [Works (without `bar.start()`) on my machine.](http://blog.codinghorror.com/content/images/uploads/2007/03/6a0120a85dcdae970b0128776ff992970c-pi.png) – icktoofay Apr 13 '15 at 06:13
  • Module free alternatives can be found on this thread: http://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console/34325723#34325723 – Greenstick May 20 '16 at 20:26
  • 3
    This module hasn't been updated in over 2 years. Don't use it for new software! – Navin Jul 05 '17 at 23:02
  • 4
    Installation: `sudo -H pip install progressbar2`. – Martin Thoma Aug 09 '17 at 07:20
  • The latest answers are module free, and should be the way to go. Up vote them! – Tian Aug 05 '20 at 01:33
  • Works awasome! Just replace `xrange` with `range` for Python3. [link](https://stackoverflow.com/questions/94935/what-is-the-difference-between-range-and-xrange-functions-in-python-2-x) – Sany Dec 16 '21 at 12:14
125

I found useful library tqdm (https://github.com/tqdm/tqdm/, previously: https://github.com/noamraph/tqdm). It automatically estimates time of completion and can be used as iterator.

Usage:

import tqdm
import time

for i in tqdm.tqdm(range(1000)):
    time.sleep(0.01)
    # or other long operations

Results in:

|####------| 450/1000  45% [elapsed: 00:04 left: 00:05, 99.15 iters/sec]

tqdm can wrap any iterable.

omikron
  • 2,745
  • 1
  • 25
  • 34
26

Here you can use following code as a function:

def drawProgressBar(percent, barLen = 20):
    sys.stdout.write("\r")
    progress = ""
    for i in range(barLen):
        if i < int(barLen * percent):
            progress += "="
        else:
            progress += " "
    sys.stdout.write("[ %s ] %.2f%%" % (progress, percent * 100))
    sys.stdout.flush()

With use of .format:

def drawProgressBar(percent, barLen = 20):
    # percent float from 0 to 1. 
    sys.stdout.write("\r")
    sys.stdout.write("[{:<{}}] {:.0f}%".format("=" * int(barLen * percent), barLen, percent * 100))
    sys.stdout.flush()
lode
  • 504
  • 6
  • 21
Jacob CUI
  • 1,327
  • 15
  • 11
  • For me the first line does not get amended by the cursor - only the second line does. Hence multiple lines obtained for progress bar, and one for eg `] percent * 100 %` – unseen_rider Oct 15 '17 at 16:04
  • second function works good but i had to add: if percent == 1: print('') – luky Aug 21 '21 at 20:13
26

You can use \r (carriage return). Demo:

import sys
total = 10000000
point = total / 100
increment = total / 20
for i in xrange(total):
    if(i % (5 * point) == 0):
        sys.stdout.write("\r[" + "=" * (i / increment) +  " " * ((total - i)/ increment) + "]" +  str(i / point) + "%")
        sys.stdout.flush()
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
17

Try this function using only the built-in sys:

import sys

def print_progress_bar(index, total, label):
    n_bar = 50  # Progress bar width
    progress = index / total
    sys.stdout.write('\r')
    sys.stdout.write(f"[{'=' * int(n_bar * progress):{n_bar}s}] {int(100 * progress)}%  {label}")
    sys.stdout.flush()

Usage:

foo_list = ["a", "b", "c", "d"]
total = len(foo_list)

for index, item in enumerate(foo_list):
    print_progress_bar(index, total, "foo bar")
    sleep(0.5)

enumerate(foo_list) gives you access to the index value during a loop.

Output:

[================================================  ] 96%  foo bar  
HyperionX
  • 1,332
  • 2
  • 17
  • 31
STG
  • 305
  • 3
  • 11
14

enter image description here

None of the answers posted completely addressed my needs. So I wrote my own as shown above. The features I needed:

  • Pass only the step number and total number of steps and it does the difficult job of calculating percentage complete.
  • Using 60 characters, divide them into 480 "ticks" to yield 0.21 % per tick. Without ticks, each character would only be 1.67 %.
  • Support for prepending a title.
  • Optional percentage complete at end of line.
  • Variable length progress bar that defaults to 60 characters or 480 "ticks".
  • Set progress bar color, the default is green.

How to Call the Progress Display

Calling the progress display is pretty straight forward. For the above sample .gif the function was called using:

percent_complete(step, total_steps, title="Convert Markdown")

The total_steps was about 2,500 for len(rows) in Stack Exchange Data Dump in CSV format. The step was the current row number as each Stack Exchange Markdown Q&A was converted to Kramdown (for GitHub Pages).

Python Code

The code is straight forward, but a bit longer than the other answers:

def percent_complete(step, total_steps, bar_width=60, title="", print_perc=True):
    import sys

    # UTF-8 left blocks: 1, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8
    utf_8s = ["█", "▏", "▎", "▍", "▌", "▋", "▊", "█"]
    perc = 100 * float(step) / float(total_steps)
    max_ticks = bar_width * 8
    num_ticks = int(round(perc / 100 * max_ticks))
    full_ticks = num_ticks / 8      # Number of full blocks
    part_ticks = num_ticks % 8      # Size of partial block (array index)
    
    disp = bar = ""                 # Blank out variables
    bar += utf_8s[0] * int(full_ticks)  # Add full blocks into Progress Bar
    
    # If part_ticks is zero, then no partial block, else append part char
    if part_ticks > 0:
        bar += utf_8s[part_ticks]
    
    # Pad Progress Bar with fill character
    bar += "▒" * int((max_ticks/8 - float(num_ticks)/8.0))
    
    if len(title) > 0:
        disp = title + ": "         # Optional title to progress display
    
    # Print progress bar in green: https://stackoverflow.com/a/21786287/6929343
    disp += "\x1b[0;32m"            # Color Green
    disp += bar                     # Progress bar to progress display
    disp += "\x1b[0m"               # Color Reset
    if print_perc:
        # If requested, append percentage complete to progress display
        if perc > 100.0:
            perc = 100.0            # Fix "100.04 %" rounding error
        disp += " {:6.2f}".format(perc) + " %"
    
    # Output to terminal repetitively over the same line using '\r'.
    sys.stdout.write("\r" + disp)
    sys.stdout.flush()

Python Code Notes

A few points:

  • The [ .... ] bracket placeholders requirement in the question are not necessary because there is the fill characters that serve the same purpose. This saves two extra characters to make the progress bar wider.
  • The bar_width keyword parameter can be used depending on screen width. The default of 60 seems a good fit for most purposes.
  • The print_perc=True keyword parameter default can be overridden by passing print_perc=False when calling the function. This would allow a longer progress bar.
  • The title="" keyword parameter defaults to no title. Should you desire one use title="My Title" and : will automatically be added to it.
  • When your program finishes remember to call sys.stdout.write("\r") followed by sys.stdout.flush() to clear the progress display line.

Summary

This answer is a bit longer than the others but it's important to note it's a full solution, not part of a solution that you need to add more code to.

Another point is this solution has no dependencies and nothing extra to install. The UTF-8 character set is supported by Python and gnome-terminal was no extra setup required. If you are using Python 2.7 you might require # -*- coding: utf-8 -*- after the shebang. IE as the second line of your program.

The function could be converted to a class with separate init, update, pause (for printing debug stuff to the screen), resume and close methods.

This function was converted from a bash script:

The bash script would display Sony TV volume with libnotify-bin (pop-up bubble message) whenever TV volume was changed. If you are interested in a bash progress bar, please visit the Stack Overflow link.

Edit January 30, 2022

  • Change from 4 ticks to 8 ticks per character.
  • Remove breaks between full blocks.
  • Add color support.
WinEunuuchs2Unix
  • 1,801
  • 1
  • 17
  • 34
  • 1
    Need to cast full_ticks to an int. ```bar += utf_8s[0] * int(full_ticks) # Add full blocks into Progress Bar``` – kblst Jun 27 '22 at 15:13
  • @kblst Thanks for pointing out `int(full_ticks)` enhancement. What Python version are you using? Are you using `bar_width=60` default or overriding with a different value? – WinEunuuchs2Unix Jun 27 '22 at 22:28
  • I'm using Python3.9 -- I believe the autocast to int in this case was removed some versions back. Using your defaults otherwise. – kblst Jun 28 '22 at 16:41
  • 1
    @kblst Thank you for your input. I've updated the answer with `int(full_ticks)` – WinEunuuchs2Unix Jun 29 '22 at 11:39
  • If you use a check for `len(title)` and `print_perc` at the beginning you could correct `bar_width` so it has always the expected width. The correction for percentage is 9 and for the title we need to subtract the length and subtract 2 for the colon and the space. – MaKaNu Nov 11 '22 at 15:49
  • @MaKaNu I'm not sure if you are saying the code is broken for you? – WinEunuuchs2Unix Nov 12 '22 at 17:56
  • No the code is not broken for me, but `bar_width` get "extended" with title and percentage. Example: `bar_width=80`, `title="bar"` and `print_perc=True` results in a used space of 94 charackters. – MaKaNu Nov 13 '22 at 11:53
  • @MaKaNu but you can change `bar_width=80` to `bar_width=66` and then it fits on a 80 character screen OK? – WinEunuuchs2Unix Nov 13 '22 at 21:18
  • that is right but if I change the title I have always to calculate the `bar_width` again. If I update `bar_width` at the beginning of the function the complete length depends on vlaue of `title` etc. It might be just an opinion, but I think the complete width should be the actuall width which I set. – MaKaNu Nov 13 '22 at 21:24
  • @MaKaNu I see now. You make some very good points. Another option is the width of the terminal can be automatically retrieved and then `bar_width` can be automatically calculated. I'll think about that... – WinEunuuchs2Unix Nov 16 '22 at 02:04
8
def printProgressBar(value,label):
    n_bar = 40 #size of progress bar
    max = 100
    j= value/max
    sys.stdout.write('\r')
    bar = '█' * int(n_bar * j)
    bar = bar + '-' * int(n_bar * (1-j))

    sys.stdout.write(f"{label.ljust(10)} | [{bar:{n_bar}s}] {int(100 * j)}% ")
    sys.stdout.flush()

call:

printProgressBar(30,"IP")

IP | [████████████----------------------------] 30%

J. Ceron
  • 1,188
  • 10
  • 8
8

To be pure python and not make system calls:

from time import sleep

for i in range(21):
    spaces = " " * (20 - i)
    percentage = 5*i
    print(f"\r[{'='*i}{spaces}]{percentage}%", flush=True, end="")
    sleep(0.25)
Seth
  • 1,769
  • 4
  • 26
  • 39
6

based on the above answers and other similar questions about CLI progress bar, I think I got a general common answer to all of them. Check it at https://stackoverflow.com/a/15860757/2254146

Here is a copy of the function, but modified to fit your style:

import time, sys

# update_progress() : Displays or updates a console progress bar
## Accepts a float between 0 and 1. Any int will be converted to a float.
## A value under 0 represents a 'halt'.
## A value at 1 or bigger represents 100%
def update_progress(progress):
    barLength = 20 # Modify this to change the length of the progress bar
    status = ""
    if isinstance(progress, int):
        progress = float(progress)
    if not isinstance(progress, float):
        progress = 0
        status = "error: progress var must be float\r\n"
    if progress < 0:
        progress = 0
        status = "Halt...\r\n"
    if progress >= 1:
        progress = 1
        status = "Done...\r\n"
    block = int(round(barLength*progress))
    text = "\rPercent: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), progress*100, status)
    sys.stdout.write(text)
    sys.stdout.flush()

Looks like

Percent: [====================] 99.0%

Community
  • 1
  • 1
Brian Khuu
  • 1,167
  • 1
  • 9
  • 5
6

If you are developing a command line interface, I suggest you to take a look at click which is very nice:

import click
import time

for filename in range(3):
    with click.progressbar(range(100), fill_char='=', empty_char=' ') as bar:
        for user in bar:
            time.sleep(0.01)

Here the output you get:

$ python test.py
  [====================================]  100%
  [====================================]  100%
  [=========                           ]   27%
nowox
  • 25,978
  • 39
  • 143
  • 293
5

As described in Mark Rushakoff's solution, you can output the carriage return character, sys.stdout.write('\r'), to reset the cursor to the beginning of the line. To generalize that solution, while also implementing Python 3's f-Strings, you could use

from time import sleep
import sys

n_bar = 50
iterable = range(33)  # for demo purposes
n_iter = len(iterable)
for i, item in enumerate(iterable):
    j = (i + 1) / n_iter

    sys.stdout.write('\r')
    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%")
    sys.stdout.flush()

    sleep(0.05)  
    # do something with <item> here
Steven C. Howell
  • 16,902
  • 15
  • 72
  • 97
3

I came upon this thread today and after having tried out this solution from Mark Rushakoff

from time import sleep
import sys

for i in range(21):
sys.stdout.write('\r')
# the exact output you're looking for:
sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
sys.stdout.flush()
sleep(0.25)

I can say that this works fine on W7-64 with python 3.4.3 64-bit but only in the native console. However when using the built-in console of spyder 3.0.0dev, the line breaks are still/again present. As this took me some time to figure out, I'd like to report this observation here.

Mr.Bum
  • 41
  • 3
3

Easiest is still

import sys
total_records = 1000
for i in range (total_records):
    sys.stdout.write('\rUpdated record: ' + str(i) + ' of ' + str(total_records))
    sys.stdout.flush()

Key is to convert the integer type to string.

bfree67
  • 669
  • 7
  • 6
2

Building on some of the answers here and elsewhere, I've written this simple function which displays a progress bar and elapsed/estimated remaining time. Should work on most unix-based machines.

import time
import sys

percent = 50.0
start = time.time()
draw_progress_bar(percent, start)


def draw_progress_bar(percent, start, barLen=20):
sys.stdout.write("\r")
progress = ""
for i in range(barLen):
    if i < int(barLen * percent):
        progress += "="
    else:
        progress += " "

elapsedTime = time.time() - start;
estimatedRemaining = int(elapsedTime * (1.0/percent) - elapsedTime)

if (percent == 1.0):
    sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: Done!\n" % 
        (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60))
    sys.stdout.flush()
    return
else:
    sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: %im%02is " % 
        (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60,
         estimatedRemaining/60, estimatedRemaining%60))
    sys.stdout.flush()
    return
Sam Jordan
  • 21
  • 1
2

This is quite a simple approach can be used with any loop.

#!/usr/bin/python
for i in range(100001):
    s =  ((i/5000)*'#')+str(i)+(' %')
    print ('\r'+s),
NILESH KUMAR
  • 413
  • 5
  • 10
2

Using @Mark-Rushakoff answer, I worked out a simpler approach, no need to call the sys library. It works with Python 3. Tested in Windows:

from time import sleep
for i in range(21):
    # the exact output you're looking for:
    print ("\r[%-20s] %d%%" % ('='*i, 5*i), end='')
    sleep(0.25)
Stein
  • 719
  • 7
  • 9
  • As described in [the answer to a different question](https://stackoverflow.com/a/3263763/3585557) `print` is a thin wrapper that formats input and calls the write function of a given object. By default this object is `sys.stdout`. – Steven C. Howell Jul 22 '19 at 16:05
1

Try PyProg. PyProg is an open-source library for Python to create super customizable progress indicators & bars.

It is currently at version 1.0.2; it is hosted on Github and available on PyPI (Links down below). It is compatible with Python 3 & 2 and it can also be used with Qt Console.

It is really easy to use. The following code:

import pyprog
from time import sleep

# Create Object
prog = pyprog.ProgressBar(" ", " ", total=34, bar_length=26, complete_symbol="=", not_complete_symbol=" ", wrap_bar_prefix=" [", wrap_bar_suffix="] ", progress_explain="", progress_loc=pyprog.ProgressBar.PROGRESS_LOC_END)
# Update Progress Bar
prog.update()

for i in range(34):
    # Do something
    sleep(0.1)
    # Set current status
    prog.set_stat(i + 1)
    # Update Progress Bar again
    prog.update()

# Make the Progress Bar final
prog.end()

will produce exactly what you want (even the bar length!):

[===========               ] 45%
[===============           ] 60%
[==========================] 100%

For more options to customize the progress bar, go to the Github page of this website.

I actually made PyProg because I needed a simple but super customizable progress bar library. You can easily install it with: pip install pyprog.

PyProg Github: https://github.com/Bill13579/pyprog
PyPI: https://pypi.python.org/pypi/pyprog/

Bill Kudo
  • 21
  • 1
1

Here Is a simple progress bar code with 0 imports

#!/usr/bin/python3
def progressbar(current_value,total_value,bar_lengh,progress_char): 
    percentage = int((current_value/total_value)*100)                                                # Percent Completed Calculation 
    progress = int((bar_lengh * current_value ) / total_value)                                       # Progress Done Calculation 
    loadbar = "Progress: [{:{len}}]{}%".format(progress*progress_char,percentage,len = bar_lengh)    # Progress Bar String
    print(loadbar, end='\r')                                                                         # Progress Bar Output

if __name__ == "__main__":
    the_list = range(1,301) 
    for i in the_list:
        progressbar(i,len(the_list),30,'■')
    print("\n")

Progress: [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■]100%

0

Here is something I have made using the solution by @Mark-Rushakoff. To adaptively adjust to the terminal width.

from time import sleep
import os
import sys
from math import ceil

l = list(map(int,os.popen('stty size','r').read().split()))
col = l[1]
col = col - 6

for i in range(col):
    sys.stdout.write('\r')
    getStr = "[%s " % ('='*i)
    sys.stdout.write(getStr.ljust(col)+"]"+"%d%%" % (ceil((100/col)*i)))
    sys.stdout.flush()
    sleep(0.25)
print("")
swarnava112
  • 421
  • 8
  • 19
0

Per Steven C. Howell's comment on Mark Rushakoff's answer

j = (i + 1) / n
stdout.write('\r')
stdout.write('[%-20s] %d%%' % ('='*int(20*j), 100*j))
stdout.flush()

where i is the current item and n is the total number of items

Justin
  • 945
  • 12
  • 26
0

For Python 3.6 the following works for me to update the output inline:

for current_epoch in range(10):
    for current_step) in range(100):
        print("Train epoch %s: Step %s" % (current_epoch, current_step), end="\r")
print()
phi
  • 550
  • 8
  • 11
0
import progressbar
import time

# Function to create  
def animated_marker():
    widgets = ['Loading: ', progressbar.Bar('=', '[', ']', '-'), progressbar.Percentage()]
    bar = progressbar.ProgressBar(max_value=200,widgets=widgets).start() 
      
    for i in range(200): 
        time.sleep(0.1)
        bar.update(i+1)
    bar.finish()

# Driver's code 
animated_marker()
wuerfelfreak
  • 2,363
  • 1
  • 14
  • 29