568

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?

Community
  • 1
  • 1
bobber205
  • 12,948
  • 27
  • 74
  • 100
  • Hmm, look like a duplicate of this question asked yesterday: http://stackoverflow.com/questions/3160699/python-progress-bar/3162864 So, you should use fish http://pypi.python.org/pypi/fish/ – Etienne Jul 04 '10 at 02:33
  • 36
    "just use a GUI" misunderstands that GUIs are great in some situations (quick learning curve, ad-hoc exploratory or interactive or one-off activities) while command-line tools are great for others (expert users, composing ad-hoc applications on the fly to perform a carefully defined operation many times.) – Jonathan Hartley May 11 '16 at 02:54
  • 30
    I voted to reopen. The question doesn't strike me as too broad. – Franck Dernoncourt Jun 26 '18 at 03:01
  • 1
    I think what you're looking for is [tqdm](https://github.com/tqdm/tqdm)... though I also don't know why SO is prompting me to review reopen votes on year-old questions. – kungphu Jun 24 '19 at 23:36
  • I think here is the [best answer](https://stackoverflow.com/a/34482761/1207193) if you don't want a external package. – imbr Jun 17 '20 at 18:04
  • Why don't you just use a cool progress bar, which you can see the throughput and ETA, even pause it, all with very cool animations! Here: https://github.com/rsalmei/alive-progress ![demo](https://raw.githubusercontent.com/rsalmei/alive-progress/master/img/alive-demo.gif) – rsalmei Jun 24 '20 at 17:48
  • Also see the [FiraCode progress bar support](https://github.com/tonsky/FiraCode#:~:text=Fira%20Code%20is%20the%20first,bars). – Mahozad Dec 04 '21 at 10:15

31 Answers31

740

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)
Diogo
  • 590
  • 6
  • 23
Greenstick
  • 8,632
  • 1
  • 24
  • 29
  • 24
    This snippet works great! I did encounter a couple minor issues so I made some minor edits (PEP-8, default encoding for non-ascii character) and threw them in a gist here: https://gist.github.com/aubricus/f91fb55dc6ba5557fbab06119420dd6a – Aubricus Nov 03 '16 at 18:01
  • 3
    Worth noting that the UTF-8 declaration is not necessary unless your using Python 2 @Aubricus – Greenstick Nov 04 '16 at 18:35
  • SyntaxError: Non-ASCII character '\xe2' on line 5, but no encoding declared – MattCochrane Jan 02 '17 at 08:52
  • 2
    @MattClimbs This is written for Python 3 which uses UTF-8 encoding by default. You can either change the default fill parameter of the function, which is a UTF-8 character, or use the UTF-8 declaration. See the gist in the comment above for an example of what a UTF-8 declaration should look like. – Greenstick Jan 03 '17 at 17:56
  • I have just done a simpler little progress bar solution on [this question](https://stackoverflow.com/a/46141777/7434365) if anyone is interested in something easier with an explanation :). p.s good answer Greenstick – Joe Iddon Sep 10 '17 at 18:05
  • Hi @Greenstick, I posted a follow up question to this one [here](https://stackoverflow.com/questions/46751428/text-progress-bar-in-console-w-title-above?noredirect=1#comment80449134_46751428). I'm trying to add a title above the progress bar. It would be great to get your input on this as you were the one that wrote the print_progress code I'm using. Much appreciated! – herteladrian Oct 15 '17 at 04:44
  • I've just modified your code a little for [this answer](https://stackoverflow.com/a/46751803/4014959) to herteladrian's question ; I hope you don't mind. – PM 2Ring Oct 15 '17 at 04:49
  • For Python 2, the required heading at top of file is `# -*- coding: utf-8 -*- ` as per https://stackoverflow.com/questions/3170211/why-declare-unicode-by-string-in-python – Stan James Oct 30 '17 at 20:44
  • I'm trying to use it, but i'm getting this error: `overflowError: cannot fit 'int' into an index-sized integer` does anyone know why? – robsanna Jul 05 '18 at 12:18
  • @roberto.sannazzaro It’s hard to know exactly what the error is without the code and trace, but the error is from a memory overflow. If you have any large numbers in your code or that you are passing as arguments into the function that would likely be the issue. – Greenstick Jul 06 '18 at 02:14
  • Print on every iteration is extra-heavy operation –  Apr 20 '19 at 05:08
  • @YuriS.Cherkasov You can always place a conditional before calling the progress bar in the loop. Something like `if i % n == 0:` where n is the frequency at which you'd like to print (e.g. `n = 10` for every 10 iterations). But that comes down to appropriate usage, which is at the users discretion. – Greenstick Apr 21 '19 at 22:13
  • Is this even possible with the logging module instead of raw print statements? – Alex Jansen May 07 '19 at 05:28
  • Thanks @Aubricus, that worked for me on python 2.7 :-) – jobima Jun 10 '19 at 09:30
  • 1
    Thanks, nice summary, also detection of terminal size could be useful for this function `# Size of terminal rows, columns = [int(x) for x in os.popen('stty size', 'r').read().split()]` `columns` should be passed to length to adjust progress bar size to terminal window. Though length of progressing part of bar should be decreased (by length of prefix, suffix, percent and additional characters in this string `'\r%s |%s| %s%% %s'` – Arleg Jul 11 '19 at 20:18
  • @Greenstick I have slightly modified version with added sys.stdout.flush() which was necessary to make it work properly. Sorry for discrepancies with original one. Ohh, it doesn't fit in characters limt( How can I send it to you? – Arleg Jul 12 '19 at 11:13
  • @Arleg Double-checked and it seems to work on my end. It may be that, to get a full progress bar 0 - 100%, you do have to call the initial 0% progress bar outside the iterations and then increment the index (if using a 0-based index) prior to passing it into the `printProgressBar` function. This ensures that the `if iteration == total:` conditional prints the last line. – Greenstick Jul 12 '19 at 19:34
  • 1
    @Arleg I've decided against the `autoresize` option. It's very resource intensive – given we're spooling a `subprocess` to query the terminal window size on each call of the `printProgressBar` function. We could query the window size once and cache the width as an environment variable, but this isn't a good solution – if the terminal were resized during iteration the progress bar wouldn't resize accordingly. For future reference, here's a [gist](https://gist.github.com/greenstick/b23e475d2bfdc3a82e34eaa1f6781ee4) with the `autoresize` option. – Greenstick Jul 12 '19 at 19:52
  • @Greenstick you are right, I've forgotten that infer terminal width outside the function) Sorry for disturbance – Arleg Jul 12 '19 at 23:23
  • Would you consider making this into a small Python module? – Mohan Jul 20 '19 at 06:29
  • @Mohan Sure, I can do that. I've got some other similar functions (like loading ellipses, etc.) for the CLI that I can throw in. That said, there are great existing libraries out there – have you checked out tdqm? – Greenstick Jul 20 '19 at 23:02
  • Yes, sorry, I read this reply before the ones below. Given tqdm exists please ignore my comment! – Mohan Jul 22 '19 at 11:24
  • 13
    To get this to work in some IDEs (e.g. PyCharm on Windows) you may need to change `end = '\r'` to `end = ''`. – thomas88wp Aug 19 '19 at 02:39
  • 1
    I think this still a [better answer](https://stackoverflow.com/a/34482761/1207193) 2020+ – imbr Jun 17 '20 at 17:53
  • 1
    Right-aligning the percent number would prevent the layout shifts from 9 to 10 and 99 to 100. `percent.rjust(4 + decimals if decimals > 0 else 3)` could do the trick. – confusius Sep 12 '20 at 11:50
  • Very nice, but (for the updated version) it can throw a ZeroDivisionError in the calculation of the percentage if there is no element in the stream. – Cedric Feb 03 '22 at 16:08
  • @Cedric could share a gist that reproduces the issue? Thanks. – Greenstick Feb 04 '22 at 22:26
  • @Greenstick Of course: 1. Create empty list, 2. use this on progressBar() -> ZeroDivisionError in the first line of printProgressBar() -> you can fix this by doing `percentage = 1 if total == 0 else (iteration / float(total))` followed by `percent = ("{0:." + str(decimals) + "f}").format(100 * percentage)` or something like this. (Note that filledLength migh also throw I changed it to `filled_length = int(length if total == 0 else (length * iteration // total))` therefore) – Cedric Feb 05 '22 at 15:03
  • this doesn't work when having threads – Jawad May 02 '22 at 15:25
  • @Jawad Is this while you are multiprocessing or using subprocesses? If so, it may have more to do with how you're handling that – I've used this in both cases and it works fine (although if you can reproduce it with a gist I'd take a look). Of course, this is after all a code snippet – if you're looking for a robust progress bar library, I'd recommend `tdqm`. – Greenstick May 02 '22 at 19:06
  • I get this: `ZeroDivisionError: float division by zero` using first version, but the one-liner works! – geoidesic Jul 15 '22 at 15:47
339

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()
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
Stephen
  • 47,994
  • 7
  • 61
  • 70
246

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]

tqdm repl session

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • What the python shell do you use? – Enbugger Mar 31 '19 at 01:09
  • 2
    @xotonic the link says it is [ptpython](https://github.com/jonathanslenders/ptpython) – jfs Mar 31 '19 at 04:45
  • 1
    sure, pull a lib for 20 lines function :D – iperov Oct 17 '21 at 10:37
  • @iperov: there are trade-offs, as usual. Unless you can list specific reasons not to, use the code that somebody else has to maintain. I can remember both: rolling out my own code (just a few lines to display progress) and using tqdm in various circumstances (there are 1k+ commits for a reason). – jfs Oct 17 '21 at 13:31
  • @jfs sure. but I often find that library developers make mistakes, and my project that depends on them crashes. That's why I prefer to make my own function implementations, which have more predictable behavior. – iperov Oct 18 '21 at 11:09
  • 1
    this is perfect, had to change my df.iterrows to range(len(df)) then call df.iloc[i].values[0] but this is exactly what I was looking for! Thank you jfs! – ChrisDanger Apr 05 '22 at 15:50
127

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%

aviraldg
  • 9,531
  • 6
  • 41
  • 56
  • Do '\r' + myPercentage printout? – bobber205 Jul 04 '10 at 00:37
  • 22
    Do `\r` and then write the whole line out again. Basically: `print("\rProgress: [{0:50s}] {1:.1f}%".format('#' * int(amtDone * 50), amtDone * 100))`, where `amtDone` is a float between 0 and 1. – Mike DeSimone Jul 04 '10 at 00:46
  • @MikeDeSimone, that looks like the best bar short of using curses. – mlissner Jan 15 '12 at 06:25
  • I don't see what curses would buy you there. Seems like overkill. Besides, this should work in a curses window, too; I won't tell. – Mike DeSimone Jan 17 '12 at 05:31
  • 15
    Better to use `sys.stdout.write` than `print`. With `print` I got newlines. – Gill Bates Dec 30 '12 at 22:55
  • 15
    append a comma `,` at the end of the `print` works for me. – Chunliang Lyu Dec 31 '12 at 08:04
  • 13
    in python3 use print(...., end='') and you won't have any newlines – graywolf Feb 21 '13 at 15:23
  • flushing the output buffer after the print finally got rid of my newlines. Try: `sys.stdout.flush()` (thanks to @ned-batchelder: http://stackoverflow.com/a/4009695/1823536) – Cpt. Senkfuss Dec 18 '13 at 19:55
  • I use Python 2.7. This worked for me: print "\r{0}%".format(i+1), – Sri Jan 14 '14 at 22:48
  • You can use something like this to have a fixed width window that fills in at 10% increments: `print '\r[%-10s] %0.2f%%' % ('#' * int(progress/10), progress),` – Shane Apr 15 '14 at 04:07
  • 15
    Summarizing for Python3 former contribs: `print("\rProgress: [{0:50s}] {1:.1f}%".format('#' * int(workdone * 50), workdone*100), end="", flush=True)`, where `workdone` is a float between 0 and 1, e.g., `workdone = parsed_dirs/total_dirs` – khyox Dec 11 '14 at 12:35
  • `print` with `,` or `end='\r'` did not work for me in the Jupyter notebook. `sys.stdout.write` did. – joelostblom Sep 12 '15 at 20:43
  • With fixed width: ```print('\r[ {0}{1} ] {2}%'.format('#' * progress, ' ' * int(100 - progress), progress))``` – Elephant Feb 28 '20 at 10:57
84

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

enter image description here

idmean
  • 14,540
  • 9
  • 54
  • 83
Vladimir Ignatev
  • 2,054
  • 1
  • 20
  • 34
73

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)
The Unfun Cat
  • 29,987
  • 31
  • 114
  • 156
36

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...

JoeLinux
  • 4,198
  • 1
  • 29
  • 31
18

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

scripts
  • 1,452
  • 1
  • 19
  • 24
12
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% ] ===================

HibernatedGuy
  • 1,939
  • 2
  • 12
  • 15
12

Here's a nice example of a progressbar written in Python: http://nadiana.com/animated-terminal-progress-bar-in-python

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

Wolph
  • 78,177
  • 11
  • 137
  • 148
  • 14
    `curses`? Easier? Hmmm.... – aviraldg Jul 04 '10 at 00:45
  • An excellent article, I was going to give a link to it but couldn't find in my bookmarks :) – Andy Mikhailenko Jul 04 '10 at 00:46
  • @Aviral Dasgupta: fair enough, easier might not be the right word here. It can save you a lot of work though, but it really depends on what you're looking for. – Wolph Jul 04 '10 at 00:54
  • Not looking for anything near this involved, but thanks anyway. :) – bobber205 Jul 04 '10 at 01:10
  • 2
    Dead link, that's the price of not posting the link'ed content in your answer -__- – ThorSummoner Feb 01 '15 at 03:53
  • @ThorSummoner: have you seen the second link? As for not posting the content, it's quite a large script. Too much for a stackoverflow answer. The new script is available on both pypi and Github so it won't easily disappear. – Wolph Feb 01 '15 at 12:43
  • I feel it's worth amending the answer to not contain a dead link. I did check the github repo, I'm not settled on it yet. – ThorSummoner Feb 01 '15 at 20:04
10

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]
Tux
  • 101
  • 1
  • 4
7

and, just to add to the pile, here's an object you can use:

Add the following to a new file progressbar.py

import sys

class ProgressBar(object):
    CHAR_ON  = '='
    CHAR_OFF = ' '

    def __init__(self, end=100, length=65):
        self._end = end
        self._length = length
        self._chars = None
        self._value = 0

    @property
    def value(self):
        return self._value
    
    @value.setter
    def value(self, value):
        self._value = max(0, min(value, self._end))
        if self._chars != (c := int(self._length * (self._value / self._end))):
            self._chars = c
            sys.stdout.write("\r  {:3n}% [{}{}]".format(
                int((self._value / self._end) * 100.0),
                self.CHAR_ON  * int(self._chars),
                self.CHAR_OFF * int(self._length - self._chars),
            ))
            sys.stdout.flush()

    def __enter__(self):
        self.value = 0
        return self

    def __exit__(self, *args, **kwargs):
        sys.stdout.write('\n')

Can be included in your program with:

import time
from progressbar import ProgressBar

count = 150
print("starting things:")

with ProgressBar(count) as bar:
    for i in range(count + 1):
        bar.value += 1
        time.sleep(0.01)

print("done")

Results in:

starting things:
  100% [=================================================================]
done

This may be "over the top", but is handy when used frequently.

FraggaMuffin
  • 3,915
  • 3
  • 22
  • 26
  • Thanks for this. Small fix, the plotProgress method should use the line sys.stdout.flush() else the progress bar might not be drawn until the task has been completed (as occurs in the mac terminal). – osnoz Dec 04 '14 at 16:49
  • I love this!!! Fairly easy to use!!! Thankyou – Microos Mar 08 '17 at 04:40
6

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.

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
5

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

Chris Cui
  • 51
  • 1
  • 1
4

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%

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

And a lot of tutorials waiting to be googled.

Andy Mikhailenko
  • 1,571
  • 1
  • 11
  • 10
4

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

Ib33X
  • 6,764
  • 4
  • 28
  • 30
  • 2
    Your link is broken — the actual line in source code is 1274th, not the 1124th! So, the right link is this one: https://github.com/reddit/reddit/blob/master/r2/r2/lib/utils/utils.py#L1274 – Vladimir Ignatev May 09 '16 at 19:39
  • This variant has the best design on my taste: it uses iterators and works possibly with any kind of measurable work, it shows elapsed time. – Vladimir Ignatev May 09 '16 at 19:43
4

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
3

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)
Malcolm Box
  • 3,968
  • 1
  • 26
  • 44
2

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
|█████     |
ryan
  • 21
  • 1
2

With the great advices above I work out the progress bar.

However I would like to point out some shortcomings

  1. 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='')
Storm-Eyes
  • 811
  • 1
  • 9
  • 10
2
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

Ramchandra Apte
  • 4,033
  • 2
  • 24
  • 44
2

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 "")
Rodrigo López
  • 4,039
  • 1
  • 19
  • 26
1

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()
emd_22
  • 11
  • 1
1

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()
1

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()
Aimin Huang
  • 155
  • 1
  • 6
1

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   ] 
jenkins
  • 11
  • 3
1

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)
Ivan Chaer
  • 6,980
  • 1
  • 38
  • 48
0

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 to prog2 = int(prog/5))
Cold Diamondz
  • 523
  • 3
  • 6
  • 12
0

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.

0

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)):
...
Antoine Boucher
  • 85
  • 1
  • 1
  • 9