user225312
user225312

Reputation: 131647

Python Progress Bar

How do I use a progress bar when my script is doing some task that is likely to take time?

For example, a function which takes some time to complete and returns True when done. How can I display a progress bar during the time the function is being executed?

Note that I need this to be in real time, so I can't figure out what to do about it. Do I need a thread for this? I have no idea.

Right now I am not printing anything while the function is being executed, however a progress bar would be nice. Also I am more interested in how this can be done from a code point of view.

Upvotes: 581

Views: 1116558

Answers (30)

Connor
Connor

Reputation: 4844

Tried and true. No external packages. A ready-made piece of code. Jupyter friendly

@imbr variant

[███████████████████..............................] 16/50 Est wait 00:03.46

Completed 50 items in 00:05.09

Features:

  • Colab / Jupyter notebook compatible
  • Vanilla python
  • Shows estimated wait time
  • Shows total execution time on completion
import time
from IPython.display import clear_output

def to_time_str(seconds):
    mins, sec = divmod(seconds, 60)
    return f"{int(mins):02}:{sec:05.2f}"

def progressbar(it, prefix="", size=60):
    count = len(it)
    start = time.time()
    def show(j):
        x = int(size*j/count)
        elapsed = time.time() - start
        remaining = (elapsed / j) * (count - j) if j > 0 else 0
        time_str = to_time_str(remaining)
        
        bar = f"{prefix}[{u'█'*x}{('.'*(size-x))}] {j}/{count} Est wait {time_str}"
        clear_output(wait=True)
        print(bar)
        
    for i, item in enumerate(it):
        yield item
        show(i+1)
    
    total_time = time.time() - start
    clear_output(wait=True)  # Clear the final progress bar
    print(f"Completed {count} items in {to_time_str(total_time)}")

# Example usage
for i in progressbar(range(50)):
    time.sleep(.1)  # any code you need

Tested with Python 3.10+

Upvotes: 0

ncoghlan
ncoghlan

Reputation: 41486

Another self-contained solution, but this one aims to degrade more gracefully than other suggestions when used in a non-interactive context where \r can't reliably return to the beginning of the line (e.g. writing to a logfile or piping output to another application). This means it needs to remember the last output written to the stream, so it's a class rather than a standalone function.

class ProgressBar:
    """Display & update a progress bar"""
    TEXT_ABORTING = "Aborting..."
    TEXT_COMPLETE = "Complete!"
    TEXT_PROGRESS = "Progress"

    def __init__(self, bar_length=25, stream=sys.stdout):
        self.bar_length = bar_length
        self.stream = stream
        self._last_displayed_text = None
        self._last_displayed_summary = None

    def reset(self):
        """Forget any previously displayed text (affects subsequent call to show())"""
        self._last_displayed_text = None
        self._last_displayed_summary = None

    def _format_progress(self, progress, aborting):
        """Internal helper that also reports the number of completed increments and the displayed status"""
        bar_length = self.bar_length
        progress = float(progress)
        if progress >= 1:
            # Report task completion
            completed_increments = bar_length
            status = " " + self.TEXT_COMPLETE
            progress = 1.0
        else:
            # Truncate progress to ensure bar only fills when complete
            progress = max(progress, 0.0) # Clamp negative values to zero
            completed_increments = int(progress * bar_length)
            status = (" " + self.TEXT_ABORTING) if aborting else ""
        remaining_increments = bar_length - completed_increments
        bar_content = f"{'#'*completed_increments}{'-'*remaining_increments}"
        percentage = f"{progress*100:.2f}"
        progress_text = f"{self.TEXT_PROGRESS}: [{bar_content}] {percentage}%{status}"
        return progress_text, (completed_increments, status)

    def format_progress(self, progress, *, aborting=False):
        """Format progress bar, percentage, and status for given fractional progress"""
        return self._format_progress(progress, aborting)[0]

    def show(self, progress, *, aborting=False):
        """Display the current progress on the console"""
        progress_text, progress_summary = self._format_progress(progress, aborting)
        if progress_text == self._last_displayed_text:
            # No change to display output, so skip writing anything
            # (this reduces overhead on both interactive and non-interactive streams)
            return
        interactive = self.stream.isatty()
        if not interactive and progress_summary == self._last_displayed_summary:
            # For non-interactive streams, skip output if only the percentage has changed
            # (this avoids flooding the output on non-interactive streams that ignore '\r')
            return
        if not interactive or aborting or progress >= 1:
            # Final or non-interactive output, so advance to next line
            line_end = "\n"
        else:
            # Interactive progress output, so try to return to start of current line
            line_end = "\r"
        sys.stdout.write(progress_text + line_end)
        sys.stdout.flush() # Ensure text is emitted regardless of stream buffering
        self._last_displayed_text = progress_text
        self._last_displayed_summary = progress_summary

When running directly against the console, only the last line is left visible, but all updates that at least change the reported percentage are written to the screen:

$ python3 -c "from util import ProgressBar; from time import sleep; bar=ProgressBar(); sleep(0.3); bar.show(0.25); sleep(0.3); bar.show(0.26); sleep(0.3); bar.show(0.27); sleep(0.3); bar.show(0.50); sleep(0.3); bar.show(0.75); sleep(0.3); bar.show(1)"
Progress: [#########################] 100.00% Complete!

Writing to a non-interactive stream filters the output to skip updates that don't advance the progress bar, so the maximum number of lines added to a logfile is fixed based on the number of increments in the bar (note that the 26% and 27% lines are skipped below):

$ python3 -c "from util import ProgressBar; bar=ProgressBar(); bar.show(0.25); bar.show(0.26); bar.show(0.27); bar.show(0.50); bar.show(0.75); bar.show(1)" | cat
Progress: [######-------------------] 25.00%
Progress: [############-------------] 50.00%
Progress: [##################-------] 75.00%
Progress: [#########################] 100.00% Complete!

Upvotes: 2

imbr
imbr

Reputation: 7632

No external packages. A ready-made piece of code.

You can customize bar progress symbol "#", bar size, text prefix etc.

Python 3.6+ (f-string) with Time Remaining estimate

import sys
import time

def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.6+
    count = len(it)
    start = time.time() # time estimate start
    def show(j):
        x = int(size*j/count)
        # time estimate calculation and string
        remaining = ((time.time() - start) / j) * (count - j)        
        mins, sec = divmod(remaining, 60) # limited to minutes
        time_str = f"{int(mins):02}:{sec:03.1f}"
        print(f"{prefix}[{u'█'*x}{('.'*(size-x))}] {j}/{count} Est wait {time_str}", end='\r', file=out, flush=True)
    show(0.1) # avoid div/0 
    for i, item in enumerate(it):
        yield item
        show(i+1)
    print("\n", flush=True, file=out)
[████████████████████████████.........................] 24/50 Est wait 00:05.3

Usage:

import time    
for i in progressbar(range(15), "Computing: ", 40):
    time.sleep(0.1) # any code you need

enter image description here


  • Doesn't require a second thread. Some solutions/packages above require.

  • Works with any iterable it means anything that len() can be used on. A list, a dict of anything for example ['a', 'b', 'c' ... 'g']

  • Works with generators only have to wrap it with a list(). For example for i in progressbar(list(your_generator), "Computing: ", 40): Unless the work is done in the generator. In that case you need another solution (like tqdm).

You can also change output by changing out to sys.stderr for example.


Python 3.3+

import sys
def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.3+
    count = len(it)
    def show(j):
        x = int(size*j/count)
        print("{}[{}{}] {}/{}".format(prefix, "#"*x, "."*(size-x), j, count), 
                end='\r', file=out, flush=True)
    show(0)
    for i, item in enumerate(it):
        yield item
        show(i+1)
    print("\n", flush=True, file=out)

Python 2 (old-code)

import sys
def progressbar(it, prefix="", size=60, out=sys.stdout):
    count = len(it)
    def show(j):
        x = int(size*j/count)
        out.write("%s[%s%s] %i/%i\r" % (prefix, u"#"*x, "."*(size-x), j, count))
        out.flush()        
    show(0)
    for i, item in enumerate(it):
        yield item
        show(i+1)
    out.write("\n")
    out.flush()

Upvotes: 192

r01_mage
r01_mage

Reputation: 31

You can use the colored progress bar that use pip. (It's from the rich package, it has the advantage of not using pip install, it's pretty, and simple to use.)

from pip._vendor.rich.progress import Progress

For my use case, which is having an infinite progress bar while i wait my file to be downloaded, i use it like that.

def wait_for_download_finish(file_name,folder):

    " Create a spinning bar until download is finished"

    # Initialize the progress bar 
    bar = Progress()
    # Set a description, and make the bar progress indefinitly
    id = bar.add_task(description="Downloading Files ...",total=None)
    # Start the progress bar
    bar.start()

    # We look for the matching string of the file we wait in the latest file in folder
    latest_file = []
    while file_name not in latest_file:
        # List all the files in folder
        list_of_files = glob.glob(folder + "/*",include_hidden=True, recursive=False) # * means for all. If you need specific format, use *.mkv
        # Get the latest file
        latest_file = max(list_of_files, key=os.path.getctime)
        # Keep the bar going
        bar.advance(id)
        # Add some delay
        time.sleep(1)

    # Stop the progress bar.
    bar.stop()

    print("Download finished!")
    return


# Use the os.path.expand if you want to use the "~"  in your path
wait_for_download_finish("test", os.path.expanduser("~"))

Upvotes: 0

5rod
5rod

Reputation: 458

I just wanted to contribute to all the great answers. You mentioned threading, and I've tested all the progress bar libraries with threading. It doesn't really work in my experience...so I wrote some code to allow you to run a function while showing a progress bar.

This isn't ideal, since it uses exec(), but it does indeed work. Here, I decided to use rich for the progress bar.

def run(func, sleep=0):
    update = lambda progress, task: progress.update(task, advance=1)

    function = inspect.getsource(func)
    mylist = function.split('\n')
    mylist.remove(mylist[0])
    length = len(mylist)
    for num, line in enumerate(mylist):
        mylist[num] = line[8:] + "\nupdate(progress, task)\ntime.sleep(sleep)" #update the progress bar

    myexec = ''
    for line in mylist:
        myexec += line + '\n'

    with Progress() as progress:
        task = progress.add_task("Working...", total=length)
        exec(myexec)

if __name__ == '__main__':
    from rich.progress import Progress
    import inspect
    import time

    def myfunc():
        #chose to import these modules for progress bar example since they take a LONG time to import on my computer
        import pandas
        import openai

    run(myfunc)

I'm not going to explain everything, but basically we take a function as an argument and convert the function to a string using inspect. We then modify the string by adding update(progress, task), which is a lambda function defined previously, that updates the progress bar. I also added another argument for customization and flexibility, sleep, that allows you to choose how long the progress bar waits before continuing to update (after finishing another line in the function). This looks really nice in the terminal!

To actually use this, I recommend saving this as a file. Then you can run this in other python files that you write in the future, by calling filename.run(func, sleep). This is really useful to me! Just want to note again, this isn't ideal since it uses exec(), if anybody knows a better method (since threading didn't work for me) please comment.

Upvotes: 0

rsalmei
rsalmei

Reputation: 3605

Use alive-progress, the coolest progress bar ever! Just pip install alive-progress and you're good to go!

GIF showing an example of alive-progress

To use any progress bar effectively, i.e. gaining both a percentage of completion and an ETA, you need to be able to tell it the total number of items. Then alive-progress will keep track of where your processing currently is, and how long it will take!
Don't worry if you can't estimate the total, alive-progress will still work.

To use it, you can either drive alive-progress' bar directly:

def compute():
    with alive_bar(1000) as bar:  # your expected total
        for item in items:        # the original loop
            print(item)           # your actual processing here
            bar()                 # call `bar()` at the end


compute()

Or, if you prefer to keep the codes isolated, just insert a yield in your processing code (to mark when an item has been processed), then drive the alive-progress' bar like this:

def compute():
    for item in items:
        print(item)
        yield                  # simply insert this :)


with alive_bar(1000) as bar:
    for i in compute():
        bar()

Either way, you'll get an awesome and animated progress bar!

|█████████████▎                      | ▅▃▁ 321/1000 [32%] in 8s (40.1/s, eta: 16s)

And it supports a LOT OF advanced options right out of the box!! some advanced options

Disclosure: I'm the proud author of alive-progress, which has 5K+ ⭐️ on github!
Read the documentation at https://github.com/rsalmei/alive-progress to get to know all the advanced features.

Behold some more animations it can do in both spinner and bar widgets: GIF showing various styles of alive-progress

It also works on Jupyter Notebooks! GIF showing alive-progress on Jupyter Notebooks

You can even design your own animations!! GIF showing animation designer

Upvotes: 289

Jacques MALAPRADE
Jacques MALAPRADE

Reputation: 1013

When running in jupyter notebooks use of normal tqdm doesn't work, as it writes output on multiple lines. Use this instead:

import time
from tqdm.notebook import tqdm

for i in tqdm(range(100)):
    time.sleep(0.5)

enter image description here

Upvotes: 6

A simple oneliner:

K = 628318
for k in range(K):
    # your stuff
    print(end="\r|%-80s|" % ("="*(80*(k+1)//K)))
|=====================================================================       |

80 is the length of the bar. Eventually you want a final print().

It can be also put into a convencience function. Along with yield, one can emulate the behaviour of tqdm:

def progbar(iterobj):
    K = len(iterobj)
    for k, obj in enumerate(iterobj):
        print(end="\r|%-80s|" % ("="*(80*(k+1)//K)))
        yield obj
    print()
    raise StopIteration

for k in progbar(range(628318)):
    # your stuff
    pass

And not to forget the digital progress indicator:

K = 628318
for k in range(K):
    # your stuff
    print(end=f"\r{(k+1)/K*100:6.2f} %")
 94.53 %

It is not to difficult to combine both, if needed.

The keys are the "Carriage Return" \r and the suppression of the default end="\n" in print.

Upvotes: 8

Taitep
Taitep

Reputation: 31

You can use the rich library, wich has really good terminal styling, including progress bars. First run the followng command: pip install rich

Example from docs:

import time
from rich.progress import track

for i in track(range(20), description="Processing..."):
    time.sleep(1)  # Simulate work being done

Upvotes: 2

ChristopheD
ChristopheD

Reputation: 116147

There are specific libraries (like this one here) but maybe something very simple would do:

import time
import sys

toolbar_width = 40

# setup toolbar
sys.stdout.write("[%s]" % (" " * toolbar_width))
sys.stdout.flush()
sys.stdout.write("\b" * (toolbar_width+1)) # return to start of line, after '['

for i in range(toolbar_width):
    time.sleep(0.1) # do real work here
    # update the bar
    sys.stdout.write("-")
    sys.stdout.flush()

sys.stdout.write("]\n") # this ends the progress bar

Note: progressbar2 is a fork of progressbar which hasn't been maintained in years.

Upvotes: 238

Giorgos Xou
Giorgos Xou

Reputation: 2164

pip install progressbar2

Progressbar Progressbar Progressbar Progressbar Progressbar

import os
import time
import progressbar 

os.environ['PYCHARM_HOSTED'] = '1' # https://github.com/WoLpH/python-progressbar/issues/237

class COLOR: # https://stackoverflow.com/a/287944/11465149
    YELLOW    = '\033[93m'
    GREEN     = '\033[92m'
    RED       = '\033[91m'
    BOLD      = '\033[1m'
    ENDC      = '\033[0m'

widgets=[
    'FILE.JSON ',
    COLOR.YELLOW          , progressbar.Percentage()                        , COLOR.ENDC,
    COLOR.RED + COLOR.BOLD, progressbar.Bar(left=' ', marker='━', right=' '), COLOR.ENDC,
    COLOR.YELLOW          , progressbar.Timer()                             , COLOR.ENDC
]

for i in progressbar.progressbar(range(100), widgets=widgets):
    time.sleep(0.01)
    if i == 99:
        widgets[4] = COLOR.GREEN

Use enumerate(...progressbar(max_value=...) + this in case you want to use it as a download progressbar

Upvotes: 6

Rajesh Nakka
Rajesh Nakka

Reputation: 173

Inspired by many of the answers with no package dependency, I am sharing my implementation here. The function to be used in any loop expects the current iteration number, the total number of iterations, and the initial time.

import time    
def simple_progress_bar(i: int, n: int, init_time: float):
    avg_time = (time.time()-init_time)/(i+1)
    percent = ((i+1)/(n))*100
    print(
        end=f"\r|{'='*(int(percent))+'>'+'.'*int(100-int(percent))}|| " + \
        f"||Completion: {percent : 4.3f}% || \t "+ \
        f"||Time elapsed: {avg_time*(i+1):4.3f} seconds || \t " + \
        f"||Remaining time: {(avg_time*(n-(i+1))): 4.3f} seconds."
    )
    return



N = 325
t0 = time.time()
for k in range(N):
    # stuff goes here #
    time.sleep(0.0001)
    # stuff goes here #
    
    simple_progress_bar(k, N, t0)

Upvotes: 0

Bravhek
Bravhek

Reputation: 331

There's a lot of good ansers already, but adding this specidfic based upon @HandyGold75 answer, I wanted it to be callabe under a specific context, with an initial msg, plus a timing feedback in seconds at the end.

from time import sleep, time


class ProgressBar:
    def __init__(self, total: float, width: int = 50, msg: str = ""):
    self.total = total
    self.width = width
    self.start: float = time()
    if msg:
        print(f"{msg}")

    def progress(self, progress: float):
        percent = self.width * ((progress) / self.total)
        bar = chr(9608) * int(percent) + "-" * (self.width - int(percent))
        print(
            f"\r|{bar}| {(100/self.width)*percent:.2f}% "
            f"[{progress} of {self.total}]",
            end="\r",
        )

    def __enter__(self):
        return self.progress

    def __exit__(self, type, value, traceback):
        end: float = time()
        print(f"\nFinished after {end - self.start: .3f} seconds.")


# USAGE
total_loops = 150
with ProgressBar(total=total_loops) as progress:
    for i in range(total_loops):
        sleep(0.01)  # Do something usefull here
        progress(i + 1)

Upvotes: 1

HandyGold75
HandyGold75

Reputation: 53

There are a lot of amazing answers already still I would like to share my solution to the progress bar.

from time import sleep

def progress_bar(progress: float, total: float, width: int = 25):
    percent = width * ((progress + 1) / total)
    bar = chr(9608) * int(percent) + "-" * (width - int(percent))
    print(f"\r|{bar}| {(100/width)*percent:.2f}%", end="\r")

numbers = range(0, 1000)
numbersLen = len(numbers)
for i in numbers:
    sleep(0.01) # Do something usefull here
    progress_bar(i, numbersLen)

EDIT:

If you are looking for a bar that adjusts it's with based on the terminal's width and a possibility for messages at the end then this works too. Note that the message will disappear if the Terminal gets too narrow as the bar will break if it's too wide for 1 line.

def progressBar(progress: float, total: float, message: str = ""):
    terminalWidth = get_terminal_size().columns
    width = int(terminalWidth / 4)
    percent = width * ((progress + 1) / total)
    bar = chr(9608) * int(percent) + "-" * (width - int(percent))
    if terminalWidth <= 40:
        message = ""
    else:
        message = message + (" " * (int(terminalWidth / 2) - len(message)))
    print(f"\r|{bar}| {(100/width)*percent:.2f}% " + message, end="\r")

Upvotes: 2

Syed Waleed Shah
Syed Waleed Shah

Reputation: 1

#doesnt affect actual execution
#based on events and consumption in background
#may be that actual process completes a bit earlier than progress shows 99%
#make an instance with number of elements in a loop
#in each iteration call the method current_progress

import time
from math import ceil
import os
import sys
from threading import Thread
class progress_bar(object):
 def __init__(self,total_elements,length_bar=25):
  self.length_bar=length_bar
  self.total_elements=total_elements
  self.singleweight=(float(1)/float(total_elements))*100
  self.done=0
  self.qt=[0]
  self.call_count=0
  t=Thread(target=self.display_progress)
  t.start()
 def current_progress(self):
  self.done+=1
  self.qt=[self.done]+self.qt
 def display_progress(self):
  while True:
   try:
    done=self.qt.pop()
   except:
    continue
   else:
    self.call_count+=1
    self.progress=self.singleweight*done
    fill=ceil(self.progress)
    bar=int((fill*self.length_bar)/100)*"|"
    bar="["+bar+str(fill)+"%"
    barp=bar
    for i in range(0,self.length_bar+3-(len(bar))):
     barp=barp+"_"
    barp=barp+"]"
    if self.progress <= 100:
     os.system("clear")
     print("Progress:",barp, sep=' ', end='\n', file=sys.stdout, flush=True)
    if self.call_count == self.total_elements:
     break
  else:
   pass

Upvotes: 0

Himanshu Singh Chauhan
Himanshu Singh Chauhan

Reputation: 433

2022 Answer for simple progress bar without external library

import time, sys

def progress(size):
    for item in range(size):
        if(item==0):
            print("[",end="")

        elif(item==size-1):
            print("]",end="\n")

        else:
            #main work goes here
            time.sleep(0.1)
            print("%",end="")
            sys.stdout.flush()

progress(50)

Upvotes: 4

scls
scls

Reputation: 17617

With tqdm (conda install tqdm or pip install tqdm) you can add a progress meter to your loops in a second:

from time import sleep
from tqdm import tqdm
for i in tqdm(range(10)):
    sleep(3)

 60%|██████    | 6/10 [00:18<00:12,  0.33 it/s]

Also, there is a notebook version:

from tqdm.notebook import tqdm
for i in tqdm(range(100)):
    sleep(3)

You can use tqdm.auto instead of tqdm.notebook to work in both a terminal and notebooks.

tqdm.contrib contains some helper functions to do things like enumerate, map, and zip. There are concurrent maps in tqdm.contrib.concurrent.

You can even get progress sent to your phone after disconnecting from a jupyter notebook using tqdm.contrib.telegram or tqdm.contrib.discord.

GIF showing an example of the output of using tqdm.contrib.telegram to display progress bar in Telegram mobile app

Upvotes: 787

&#233;tale-cohomology
&#233;tale-cohomology

Reputation: 1861

Here's a short solution that builds the loading bar programmatically (you must decide how long you want it).

import time

n = 33  # or however many loading slots you want to have
load = 0.01  # artificial loading time!
loading = '.' * n  # for strings, * is the repeat operator

for i in range(n+1):
    # this loop replaces each dot with a hash!
    print('\r%s Loading at %3d percent!' % (loading, i*100/n), end='')
    loading = loading[:i] + '#' + loading[i+1:]
    time.sleep(load)
    if i==n: print()

Upvotes: 3

Gayrat Dadamirzaev
Gayrat Dadamirzaev

Reputation: 1

from IPython.display import clear_output
progress_bar=u"\u001b[7m Loading: "
for i in range(100):
    clear_output(wait=True)
    progress_bar+=u"\u001b[7m "
    print(progress_bar+str(i+1)+"%")
    time.sleep(0.03) #you can change the speed

output

Upvotes: 0

BSalita
BSalita

Reputation: 8931

Progressbar for iterrows. Adaptation of @eusoubrasileiro code for displaying progress when looping through rows of a dataframe. Additionally shows percentage, ith/count, elapsed seconds, iters/sec, remaining seconds. Allows specifying an nth updating count (per).

import time
import sys
def progressbar_iterrows(df, prefix="", size=60, file=sys.stdout, per=1000):
    count = len(df)
    t = 0
    def show(j,elapsed):
        avg = 0 if elapsed == 0 else j/elapsed
        remaining = 0 if avg == 0 else (count-j)/avg
        x = int(size*j/count)
        file.write("%s[%s%s] %i%% %i/%i elapsed:%i %i/sec remaining:%i\r" % (prefix, "#"*x, "."*(size-x), j/count, j, count, elapsed, avg, remaining))
        file.flush()
    file.write("Initializing ...\r")
    file.flush()
    for i, item in df.iterrows():
        yield i,item
        if t == 0:
            t = time.time()
        if i % per == 0:
            show(i,time.time()-t)
    file.write("\n")
    file.flush()

Usage:

    for n,r in progressbar_iterrows(br_b_sections_df, "Processed: "):
        # do something

Output:

Processed: [........................] 0% 5000/28751240 elapsed:12 413/sec remaining:55054

Upvotes: 0

1409aram
1409aram

Reputation: 11

I use wget, you have to install the module tho in cmd prompt in windows or terminal if on mac or linux

pip install wget

It's pretty straight forward, just use the download() function

import wget
url = input("Enter Url to download: ")
wget.download(url)

tqdm is also an option, u have to download the module, too.

pip install tqdm

now make sure to import the module, set the range and pass

from tqdm import tqdm
for i in tqdm(range(int(9e7))):
    pass

Upvotes: 1

Rabi
Rabi

Reputation: 1

Here is a very simple version, in case you have a loop and just want to get an idea of progression of iterations, such as a dot for every, say, 5000 iterations.

my_list = range(0,100000)

counter = 0
for x in my_list:
    #your code here

    counter = counter + 1
    if counter % 5000 == 0:
        print(".", end="") # end="" avoids a newline, keeps dots together

print() #this makes sure whatever you print next is in a new line

my_list is not part of the scheme. Use your own iterable, whatever you are looping over. This version doesn't tell you ahead of time how many total iterations.

Upvotes: -2

JHAR Productions
JHAR Productions

Reputation: 21

A very simple approach:

def progbar(count: int) -> None:
    for i in range(count):
        print(f"[{i*'#'}{(count-1-i)*' '}] - {i+1}/{count}", end="\r")
        yield i
    print('\n')

And the usage:

from time import sleep

for i in progbar(10):
    sleep(0.2) #whatever task you need to do

Upvotes: 2

RandomProgrammer
RandomProgrammer

Reputation: 9

A simple solution here !

void = '-'
fill = '#'
count = 100/length
increaseCount = 0
for i in range(length):
    print('['+(fill*i)+(void*(length-i))+'] '+str(int(increaseCount))+'%',end='\r')
    increaseCount += count
    time.sleep(0.1)
print('['+(fill*(i+1))+(void*(length-(i+1)))+'] '+str(int(increaseCount))+'%',end='\n')

Note : You can modify the fill and the "void" character if you want.

The loading bar (picture)

Upvotes: 0

Niko
Niko

Reputation: 109

The code below is a quite general solution and also has a time elapsed and time remaining estimate. You can use any iterable with it. The progress bar has a fixed size of 25 characters but it can show updates in 1% steps using full, half, and quarter block characters. The output looks like this:

 18% |████▌                    | \ [0:00:01, 0:00:06]

Code with example:

import sys, time
from numpy import linspace

def ProgressBar(iterObj):
  def SecToStr(sec):
    m, s = divmod(sec, 60)
    h, m = divmod(m, 60)
    return u'%d:%02d:%02d'%(h, m, s)
  L = len(iterObj)
  steps = {int(x):y for x,y in zip(linspace(0, L, min(100,L), endpoint=False),
                                   linspace(0, 100, min(100,L), endpoint=False))}
  qSteps = ['', u'\u258E', u'\u258C', u'\u258A'] # quarter and half block chars
  startT = time.time()
  timeStr = '   [0:00:00, -:--:--]'
  activity = [' -',' \\',' |',' /']
  for nn,item in enumerate(iterObj):
    if nn in steps:
      done = u'\u2588'*int(steps[nn]/4.0)+qSteps[int(steps[nn]%4)]
      todo = ' '*(25-len(done))
      barStr = u'%4d%% |%s%s|'%(steps[nn], done, todo)
    if nn>0:
      endT = time.time()
      timeStr = ' [%s, %s]'%(SecToStr(endT-startT),
                             SecToStr((endT-startT)*(L/float(nn)-1)))
    sys.stdout.write('\r'+barStr+activity[nn%4]+timeStr); sys.stdout.flush()
    yield item
  barStr = u'%4d%% |%s|'%(100, u'\u2588'*25)
  timeStr = '   [%s, 0:00:00]\n'%(SecToStr(time.time()-startT))
  sys.stdout.write('\r'+barStr+timeStr); sys.stdout.flush()

# Example
s = ''
for c in ProgressBar(list('Disassemble and reassemble this string')):
  time.sleep(0.2)
  s += c
print(s)

Suggestions for improvements or other comments are appreciated. Cheers!

Upvotes: 5

dododo
dododo

Reputation: 21

This is my simple solution:

import time

def progress(_cur, _max):
    p = round(100*_cur/_max)
    b = f"Progress: {p}% - ["+"."*int(p/5)+" "*(20-int(p/5))+"]"
    print(b, end="\r")

# USAGE:
for i in range(0,101):
    time.sleep(0.1) 
    progress(i,100)

print("..."*5, end="\r")
print("Done")

Upvotes: 2

kakhkAtion
kakhkAtion

Reputation: 2334

Use the progress library!

pip install progress

Here is a custom subclass I wrote to format the ETA/Elapsed times into a better readable format:

import datetime
from progress.bar import IncrementalBar


class ProgressBar(IncrementalBar):
    '''
    My custom progress bar that:
       - Show %, count, elapsed, eta
       - Time is shown in H:M:S format
    '''

    message = 'Progress'
    suffix  = '%(percent).1f%% (%(index)d/%(max)d) -- %(elapsed_min)s (eta: %(eta_min)s)'

    def formatTime(self, seconds):
        return str(datetime.timedelta(seconds=seconds))

    @property
    def elapsed_min(self):
        return self.formatTime(self.elapsed)

    @property
    def eta_min(self):
        return self.formatTime(self.eta)

if __name__=='__main__':
    counter = 120
    bar     = ProgressBar('Processing', max=counter)

    for i in range(counter):
        bar.next()
        time.sleep(1)

    bar.finish()

Upvotes: 3

Brian Khuu
Brian Khuu

Reputation: 1207

The above suggestions are pretty good, but I think most people just want a ready made solution, with no dependencies on external packages, but is also reusable.

I got the best points of all the above, and made it into a function, along with a test cases.

To use it, just copy the lines under "def update_progress(progress)" but not the test script. Don't forget to import sys. Call this whenever you need to display or update the progress bar.

This works by directly sending the "\r" symbol to console to move cursor back to the start. "print" in python does not recongise the above symbol for this purpose, hence we need 'sys'

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


# update_progress test script
print "progress : 'hello'"
update_progress("hello")
time.sleep(1)

print "progress : 3"
update_progress(3)
time.sleep(1)

print "progress : [23]"
update_progress([23])
time.sleep(1)

print ""
print "progress : -10"
update_progress(-10)
time.sleep(2)

print ""
print "progress : 10"
update_progress(10)
time.sleep(2)

print ""
print "progress : 0->1"
for i in range(101):
    time.sleep(0.1)
    update_progress(i/100.0)

print ""
print "Test completed"
time.sleep(10)

This is what the result of the test script shows (The last progress bar animates):

progress : 'hello'
Percent: [----------] 0% error: progress var must be float
progress : 3
Percent: [##########] 100% Done...
progress : [23]
Percent: [----------] 0% error: progress var must be float

progress : -10
Percent: [----------] 0% Halt...

progress : 10
Percent: [##########] 100% Done...

progress : 0->1
Percent: [##########] 100% Done...
Test completed

Upvotes: 109

Matheus Tavares
Matheus Tavares

Reputation: 31

I used format() method to make a load bar. Here is my solution:

import time

loadbarwidth = 23

for i in range(1, loadbarwidth + 1):
    time.sleep(0.1) 

    strbarwidth = '[{}{}] - {}\r'.format(
        (i * '#'),
        ((loadbarwidth - i) * '-'),
        (('{:0.2f}'.format(((i) * (100/loadbarwidth))) + '%'))
    )

    print(strbarwidth ,end = '')

print()

Output:

[#######################] - 100.00%

Upvotes: 3

Matthijs990
Matthijs990

Reputation: 629

use the os_sys lib:

i use it for many types of bars, example:

from os_sys.progress import bar as Bar
bar = Bar('progresing: ', max=20)
for i in range(20):
    #do somthing
    bar.next()
bar.finish()

your output will be:

procesing:  |######                          | 2/10

read more in the discription of os_sys

Upvotes: 0

Related Questions