Double Progress Bar in Python
Is there a way to create a double progress bar in Python? I want to run two loops inside each other. For each loop I want to have a progress bar. My program looks like:
import time
for i1 in range(5):
for i2 in range(300):
# do something, e.g. sleep
time.sleep(0.01)
# update upper progress bar
# update lower progress bar
The output somewhere in the middle should look something like
50%|############################ |ETA: 0:00:02
80%|################################################## |ETA: 0:00:04
The already existing really cool progressbar module doesn't seem to support that.
Solution 1:
Use the nested progress bars feature of tqdm, an extremely low overhead, very customisable progress bar library:
$ pip install -U tqdm
Then:
from tqdm import tqdm
# from tqdm.auto import tqdm # notebook compatible
import time
for i1 in tqdm(range(5)):
for i2 in tqdm(range(300), leave=False):
# do something, e.g. sleep
time.sleep(0.01)
(The leave=False
is optional - needed to discard the nested bars upon completion.)
You can also use from tqdm import trange
and then replace tqdm(range(...))
with trange(...)
. You can also get it working in a notebook.
Solution 2:
I basically just want to add to the answer of @casper.dcl. In the slightly different case, where you have two nested for loops and want just a SINGLE progress bar you can do the following.
from tqdm import tqdm
import time
n = 5
m = 300
with tqdm(total=n * m) as pbar:
for i1 in tqdm(range(n)):
for i2 in tqdm(range(m)):
# do something, e.g. sleep
time.sleep(0.01)
pbar.update(1)
I know that was not the question, but it might be still helpful for some folks.
Solution 3:
It would require you to move the cursor position. I have written you a hacky thing to do it.
This script relies on the fact that the progressbar module assumes that you are on a fresh line to draw the progress bar. By simply moving the cursor up (using the escape code for "move cursor 1 row up"), and down (just using a newline. I could also use an escape code, but newline is easier and faster), one can maintain multiple progress bars.
import progressbar, time, sys
def up():
# My terminal breaks if we don't flush after the escape-code
sys.stdout.write('\x1b[1A')
sys.stdout.flush()
def down():
# I could use '\x1b[1B' here, but newline is faster and easier
sys.stdout.write('\n')
sys.stdout.flush()
# Total bar is at the bottom. Move down to draw it
down()
total = progressbar.ProgressBar(maxval=50)
total.start()
for i in range(1,51):
# Move back up to prepare for sub-bar
up()
# I make a new sub-bar for every iteration, thinking it could be things
# like "File progress", with total being total file progress.
sub = progressbar.ProgressBar(maxval=50)
sub.start()
for y in range(51):
sub.update(y)
time.sleep(0.005)
sub.finish()
# Update total - The sub-bar printed a newline on finish, so we already
# have focus on it
total.update(i)
total.finish()
This is of course a bit hacky, but it gets the job done. I hope that it is useful.
Solution 4:
Use enlighten:
import time
import enlighten
manager = enlighten.get_manager()
ticks = manager.counter(total=100, desc="Ticks", unit="ticks", color="red")
tocks = manager.counter(total=20, desc="Tocks", unit="tocks", color="blue")
for num in range(100):
time.sleep(0.1) # Simulate work
print("The quick brown fox jumps over the lazy dog. {}".format(num))
ticks.update()
if not num % 5:
tocks.update()
manager.stop()