Text progress bar in terminal with block characters
PythonConsoleProgressUpdatingPython Problem Overview
I wrote a simple console app to upload and download files from an FTP server using the ftplib.
I would like the app to show some visualization of its download/upload progress for the user; each time a data chunk is downloaded, I would like it to provide a progress update, even if it's just a numeric representation like a percentage.
Importantly, I want to avoid erasing all the text that's been printed to the console in previous lines (i.e. I don't want to "clear" the entire terminal while printing the updated progress).
This seems a fairly common task – how can I go about making a progress bar or similar visualization that outputs to my console while preserving prior program output?
Python Solutions
Solution 1 - Python
Python 3
A Simple, Customizable Progress Bar
Here's an aggregate of many of the answers below that I use regularly (no imports required).
Note: All code in this answer was created for Python 3; see end of answer to use this code with Python 2.
# Print iterations progress
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
"""
Call in a loop to create terminal progress bar
@params:
iteration - Required : current iteration (Int)
total - Required : total iterations (Int)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
"""
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
# Print New Line on Complete
if iteration == total:
print()
Sample Usage
import time
# A List of Items
items = list(range(0, 57))
l = len(items)
# Initial call to print 0% progress
printProgressBar(0, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
for i, item in enumerate(items):
# Do stuff...
time.sleep(0.1)
# Update Progress Bar
printProgressBar(i + 1, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
Sample Output
Progress: |█████████████████████████████████████████████-----| 90.0% Complete
Update
There was discussion in the comments regarding an option that allows the progress bar to adjust dynamically to the terminal window width. While I don't recommend this, here's a gist that implements this feature (and notes the caveats).
Single-Call Version of The Above
A comment below referenced a nice answer posted in response to a similar question. I liked the ease of use it demonstrated and wrote a similar one, but opted to leave out the import of the sys
module while adding in some of the features of the original printProgressBar
function above.
Some benefits of this approach over the original function above include the elimination of an initial call to the function to print the progress bar at 0% and the use of enumerate
becoming optional (i.e. it is no longer explicitly required to make the function work).
def progressBar(iterable, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
"""
Call in a loop to create terminal progress bar
@params:
iterable - Required : iterable object (Iterable)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
"""
total = len(iterable)
# Progress Bar Printing Function
def printProgressBar (iteration):
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
# Initial Call
printProgressBar(0)
# Update Progress Bar
for i, item in enumerate(iterable):
yield item
printProgressBar(i + 1)
# Print New Line on Complete
print()
Sample Usage
import time
# A List of Items
items = list(range(0, 57))
# A Nicer, Single-Call Usage
for item in progressBar(items, prefix = 'Progress:', suffix = 'Complete', length = 50):
# Do stuff...
time.sleep(0.1)
Sample Output
Progress: |█████████████████████████████████████████████-----| 90.0% Complete
Python 2
To use the above functions in Python 2, set the encoding to UTF-8 at the top of your script:
# -*- coding: utf-8 -*-
And replace the Python 3 string formatting in this line:
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
With Python 2 string formatting:
print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = printEnd)
Solution 2 - Python
Writing '\r' will move the cursor back to the beginning of the line.
This displays a percentage counter:
import time
import sys
for i in range(100):
time.sleep(1)
sys.stdout.write("\r%d%%" % i)
sys.stdout.flush()
Solution 3 - Python
tqdm: add a progress meter to your loops in a second:
>>> import time
>>> from tqdm import tqdm
>>> for i in tqdm(range(100)):
... time.sleep(1)
...
|###-------| 35/100 35% [elapsed: 00:35 left: 01:05, 1.00 iters/sec]
Solution 4 - Python
Write a \r
to the console. That is a "carriage return" which causes all text after it to be echoed at the beginning of the line. Something like:
def update_progress(progress):
print '\r[{0}] {1}%'.format('#'*(progress/10), progress)
which will give you something like: [ ########## ] 100%
Solution 5 - Python
It is less than 10 lines of code.
The gist here: https://gist.github.com/vladignatyev/06860ec2040cb497f0f3
import sys
def progress(count, total, suffix=''):
bar_len = 60
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percents, '%', suffix))
sys.stdout.flush() # As suggested by Rom Ruben
Solution 6 - Python
Try the click library written by the Mozart of Python, Armin Ronacher.
$ pip install click # both 2 and 3 compatible
To create a simple progress bar:
import click
with click.progressbar(range(1000000)) as bar:
for i in bar:
pass
This is what it looks like:
# [###-------------------------------] 9% 00:01:14
Customize to your hearts content:
import click, sys
with click.progressbar(range(100000), file=sys.stderr, show_pos=True, width=70, bar_template='(_(_)=%(bar)sD(_(_| %(info)s', fill_char='=', empty_char=' ') as bar:
for i in bar:
pass
Custom look:
(_(_)===================================D(_(_| 100000/100000 00:00:02
There are even more options, see the API docs:
click.progressbar(iterable=None, length=None, label=None, show_eta=True, show_percent=None, show_pos=False, item_show_func=None, fill_char='#', empty_char='-', bar_template='%(label)s [%(bar)s] %(info)s', info_sep=' ', width=36, file=None, color=None)
Solution 7 - Python
I realize I'm late to the game, but here's a slightly Yum-style (Red Hat) one I wrote (not going for 100% accuracy here, but if you're using a progress bar for that level of accuracy, then you're WRONG anyway):
import sys
def cli_progress_test(end_val, bar_length=20):
for i in xrange(0, end_val):
percent = float(i) / end_val
hashes = '#' * int(round(percent * bar_length))
spaces = ' ' * (bar_length - len(hashes))
sys.stdout.write("\rPercent: [{0}] {1}%".format(hashes + spaces, int(round(percent * 100))))
sys.stdout.flush()
Should produce something looking like this:
Percent: [############## ] 69%
... where the brackets stay stationary and only the hashes increase.
This might work better as a decorator. For another day...
Solution 8 - Python
Check this library: clint
it has a lot of features including a progress bar:
from time import sleep
from random import random
from clint.textui import progress
if __name__ == '__main__':
for i in progress.bar(range(100)):
sleep(random() * 0.2)
for i in progress.dots(range(100)):
sleep(random() * 0.2)
this link provides a quick overview of its features
Solution 9 - Python
Here's a nice example of a progressbar written in Python: http://nadiana.com/animated-terminal-progress-bar-in-python</s>
But if you want to write it yourself. You could use the curses
module to make things easier :)
[edit] Perhaps easier is not the word for curses. But if you want to create a full-blown cui than curses takes care of a lot of stuff for you.
[edit] Since the old link is dead I have put up my own version of a Python Progressbar, get it here: https://github.com/WoLpH/python-progressbar
Solution 10 - Python
import time,sys
for i in range(100+1):
time.sleep(0.1)
sys.stdout.write(('='*i)+(''*(100-i))+("\r [ %d"%i+"% ] "))
sys.stdout.flush()
output
[ 29% ] ===================
Solution 11 - Python
Install tqdm
.(pip install tqdm
)
and use it as follows:
import time
from tqdm import tqdm
for i in tqdm(range(1000)):
time.sleep(0.01)
That's a 10 seconds progress bar that'll output something like this:
47%|██████████████████▊ | 470/1000 [00:04<00:05, 98.61it/s]
Solution 12 - Python
and, just to add to the pile, here's an object you can use
import sys
class ProgressBar(object):
DEFAULT_BAR_LENGTH = 65
DEFAULT_CHAR_ON = '='
DEFAULT_CHAR_OFF = ' '
def __init__(self, end, start=0):
self.end = end
self.start = start
self._barLength = self.__class__.DEFAULT_BAR_LENGTH
self.setLevel(self.start)
self._plotted = False
def setLevel(self, level):
self._level = level
if level < self.start: self._level = self.start
if level > self.end: self._level = self.end
self._ratio = float(self._level - self.start) / float(self.end - self.start)
self._levelChars = int(self._ratio * self._barLength)
def plotProgress(self):
sys.stdout.write("\r %3i%% [%s%s]" %(
int(self._ratio * 100.0),
self.__class__.DEFAULT_CHAR_ON * int(self._levelChars),
self.__class__.DEFAULT_CHAR_OFF * int(self._barLength - self._levelChars),
))
sys.stdout.flush()
self._plotted = True
def setAndPlot(self, level):
oldChars = self._levelChars
self.setLevel(level)
if (not self._plotted) or (oldChars != self._levelChars):
self.plotProgress()
def __add__(self, other):
assert type(other) in [float, int], "can only add a number"
self.setAndPlot(self._level + other)
return self
def __sub__(self, other):
return self.__add__(-other)
def __iadd__(self, other):
return self.__add__(other)
def __isub__(self, other):
return self.__add__(-other)
def __del__(self):
sys.stdout.write("\n")
if __name__ == "__main__":
import time
count = 150
print "starting things:"
pb = ProgressBar(count)
#pb.plotProgress()
for i in range(0, count):
pb += 1
#pb.setAndPlot(i + 1)
time.sleep(0.01)
del pb
print "done"
results in:
starting things:
100% [=================================================================]
done
This would most commonly be considered to be "over the top", but it's handy when you're using it a lot
Solution 13 - Python
Run this at the Python command line (not in any IDE or development environment):
>>> import threading
>>> for i in range(50+1):
... threading._sleep(0.5)
... print "\r%3d" % i, ('='*i)+('-'*(50-i)),
Works fine on my Windows system.
Solution 14 - Python
Try to install this package: pip install progressbar2
:
import time
import progressbar
for i in progressbar.progressbar(range(100)):
time.sleep(0.02)
progresssbar github: https://github.com/WoLpH/python-progressbar
Solution 15 - Python
A very simple solution is to put this code into your loop:
Put this in the body (i.e. top) of your file:
import sys
Put this in the body of your loop:
sys.stdout.write("-") # prints a dash for each iteration of loop
sys.stdout.flush() # ensures bar is displayed incrementally
Solution 16 - Python
- http://code.activestate.com/recipes/168639-progress-bar-class/ (2002)
- http://code.activestate.com/recipes/299207-console-text-progress-indicator-class/ (2004)
- http://pypi.python.org/pypi/progressbar (2006)
And a lot of tutorials waiting to be googled.
Solution 17 - Python
I am using progress from reddit. I like it because it can print progress for every item in one line, and it shouldn't erase printouts from the program.
Edit: fixed link
Solution 18 - Python
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
In summary, the code is this:
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 = 10 # 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%
Solution 19 - Python
I recommend using tqdm - https://pypi.python.org/pypi/tqdm - which makes it simple to turn any iterable or process into a progress bar, and handles all messing about with terminals needed.
From the documentation: "tqdm can easily support callbacks/hooks and manual updates. Here’s an example with urllib"
import urllib
from tqdm import tqdm
def my_hook(t):
"""
Wraps tqdm instance. Don't forget to close() or __exit__()
the tqdm instance once you're done with it (easiest using `with` syntax).
Example
-------
>>> with tqdm(...) as t:
... reporthook = my_hook(t)
... urllib.urlretrieve(..., reporthook=reporthook)
"""
last_b = [0]
def inner(b=1, bsize=1, tsize=None):
"""
b : int, optional
Number of blocks just transferred [default: 1].
bsize : int, optional
Size of each block (in tqdm units) [default: 1].
tsize : int, optional
Total size (in tqdm units). If [default: None] remains unchanged.
"""
if tsize is not None:
t.total = tsize
t.update((b - last_b[0]) * bsize)
last_b[0] = b
return inner
eg_link = 'http://www.doc.ic.ac.uk/~cod11/matryoshka.zip'
with tqdm(unit='B', unit_scale=True, miniters=1,
desc=eg_link.split('/')[-1]) as t: # all optional kwargs
urllib.urlretrieve(eg_link, filename='/dev/null',
reporthook=my_hook(t), data=None)
Solution 20 - Python
import sys
def progresssbar():
for i in range(100):
time.sleep(1)
sys.stdout.write("%i\r" % i)
progressbar()
NOTE: if you run this in interactive interepter you get extra numbers printed out
Solution 21 - Python
lol i just wrote a whole thingy for this heres the code keep in mind you cant use unicode when doing block ascii i use cp437
import os
import time
def load(left_side, right_side, length, time):
x = 0
y = ""
print "\r"
while x < length:
space = length - len(y)
space = " " * space
z = left + y + space + right
print "\r", z,
y += "█"
time.sleep(time)
x += 1
cls()
and you call it like so
print "loading something awesome"
load("|", "|", 10, .01)
so it looks like this
loading something awesome
|█████ |
Solution 22 - Python
With the great advices above I work out the progress bar.
However I would like to point out some shortcomings
-
Every time the progress bar is flushed, it will start on a new line
print('\r[{0}]{1}%'.format('#' * progress* 10, progress))
like this:
[] 0%
[#]10%
[##]20%
[###]30%
2.The square bracket ']' and the percent number on the right side shift right as the '###' get longer.
3. An error will occur if the expression 'progress / 10' can not return an integer.
And the following code will fix the problem above.
def update_progress(progress, total):
print('\r[{0:10}]{1:>2}%'.format('#' * int(progress * 10 /total), progress), end='')
Solution 23 - Python
Code for python terminal progress bar
import sys
import time
max_length = 5
at_length = max_length
empty = "-"
used = "%"
bar = empty * max_length
for i in range(0, max_length):
at_length -= 1
#setting empty and full spots
bar = used * i
bar = bar+empty * at_length
#\r is carriage return(sets cursor position in terminal to start of line)
#\0 character escape
sys.stdout.write("[{}]\0\r".format(bar))
sys.stdout.flush()
#do your stuff here instead of time.sleep
time.sleep(1)
sys.stdout.write("\n")
sys.stdout.flush()
Solution 24 - Python
The python module progressbar is a nice choice. Here is my typical code:
import time
import progressbar
widgets = [
' ', progressbar.Percentage(),
' ', progressbar.SimpleProgress(format='(%(value_s)s of %(max_value_s)s)'),
' ', progressbar.Bar('>', fill='.'),
' ', progressbar.ETA(format_finished='- %(seconds)s -', format='ETA: %(seconds)s', ),
' - ', progressbar.DynamicMessage('loss'),
' - ', progressbar.DynamicMessage('error'),
' '
]
bar = progressbar.ProgressBar(redirect_stdout=True, widgets=widgets)
bar.start(100)
for i in range(100):
time.sleep(0.1)
bar.update(i + 1, loss=i / 100., error=i)
bar.finish()
Solution 25 - Python
i wrote a simple progressbar:
def bar(total, current, length=10, prefix="", filler="#", space=" ", oncomp="", border="[]", suffix=""):
if len(border) != 2:
print("parameter 'border' must include exactly 2 symbols!")
return None
print(prefix + border[0] + (filler * int(current / total * length) +
(space * (length - int(current / total * length)))) + border[1], suffix, "\r", end="")
if total == current:
if oncomp:
print(prefix + border[0] + space * int(((length - len(oncomp)) / 2)) +
oncomp + space * int(((length - len(oncomp)) / 2)) + border[1], suffix)
if not oncomp:
print(prefix + border[0] + (filler * int(current / total * length) +
(space * (length - int(current / total * length)))) + border[1], suffix)
as you can see, it have: length of bar, prefix and suffix, filler, space, text in bar on 100%(oncomp) and borders
here an example:
from time import sleep, time
start_time = time()
for i in range(10):
pref = str((i+1) * 10) + "% "
complete_text = "done in %s sec" % str(round(time() - start_time))
sleep(1)
bar(10, i + 1, length=20, prefix=pref, oncomp=complete_text)
out in progress:
30% [###### ]
out on complete:
100% [ done in 9 sec ]
Solution 26 - Python
Putting together some of the ideas I found here, and adding estimated time left:
import datetime, sys
start = datetime.datetime.now()
def print_progress_bar (iteration, total):
process_duration_samples = []
average_samples = 5
end = datetime.datetime.now()
process_duration = end - start
if len(process_duration_samples) == 0:
process_duration_samples = [process_duration] * average_samples
process_duration_samples = process_duration_samples[1:average_samples-1] + [process_duration]
average_process_duration = sum(process_duration_samples, datetime.timedelta()) / len(process_duration_samples)
remaining_steps = total - iteration
remaining_time_estimation = remaining_steps * average_process_duration
bars_string = int(float(iteration) / float(total) * 20.)
sys.stdout.write(
"\r[%-20s] %d%% (%s/%s) Estimated time left: %s" % (
'='*bars_string, float(iteration) / float(total) * 100,
iteration,
total,
remaining_time_estimation
)
)
sys.stdout.flush()
if iteration + 1 == total:
print
# Sample usage
for i in range(0,300):
print_progress_bar(i, 300)
Solution 27 - Python
For python 3:
def progress_bar(current_value, total):
increments = 50
percentual = ((current_value/ total) * 100)
i = int(percentual // (100 / increments ))
text = "\r[{0: <{1}}] {2}%".format('=' * i, increments, percentual)
print(text, end="\n" if percentual == 100 else "")
Solution 28 - Python
Well here is code that works and I tested it before posting:
import sys
def prg(prog, fillchar, emptchar):
fillt = 0
emptt = 20
if prog < 100 and prog > 0:
prog2 = prog/5
fillt = fillt + prog2
emptt = emptt - prog2
sys.stdout.write("\r[" + str(fillchar)*fillt + str(emptchar)*emptt + "]" + str(prog) + "%")
sys.stdout.flush()
elif prog >= 100:
prog = 100
prog2 = prog/5
fillt = fillt + prog2
emptt = emptt - prog2
sys.stdout.write("\r[" + str(fillchar)*fillt + str(emptchar)*emptt + "]" + str(prog) + "%" + "\nDone!")
sys.stdout.flush()
elif prog < 0:
prog = 0
prog2 = prog/5
fillt = fillt + prog2
emptt = emptt - prog2
sys.stdout.write("\r[" + str(fillchar)*fillt + str(emptchar)*emptt + "]" + str(prog) + "%" + "\nHalted!")
sys.stdout.flush()
Pros:
- 20 character bar (1 character for every 5 (number wise))
- Custom fill characters
- Custom empty characters
- Halt (any number below 0)
- Done (100 and any number above 100)
- Progress count (0-100 (below and above used for special functions))
- Percentage number next to bar, and it's a single line
Cons:
- Supports integers only (It can be modified to support them though, by making the division an integer division, so just change
prog2 = prog/5
toprog2 = int(prog/5)
)
Solution 29 - Python
Here's my Python 3 solution:
import time
for i in range(100):
time.sleep(1)
s = "{}% Complete".format(i)
print(s,end=len(s) * '\b')
'\b' is a backslash, for each character in your string. This does not work within the Windows cmd window.
Solution 30 - Python
function from Greenstick for 2.7:
def printProgressBar (iteration, total, prefix = '', suffix = '',decimals = 1, length = 100, fill = '#'):
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print'\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix),
sys.stdout.flush()
# Print New Line on Complete
if iteration == total:
print()
Solution 31 - Python
https://pypi.python.org/pypi/progressbar2/3.30.2
Progressbar2 is a good library for ascii base progressbar for the command line import time import progressbar
bar = progressbar.ProgressBar()
for i in bar(range(100)):
time.sleep(0.02)
bar.finish()
https://pypi.python.org/pypi/tqdm
tqdm is a alternative of progressbar2 and i think it use in pip3 but i am not sure of that
from tqdm import tqdm
for i in tqdm(range(10000)):
...