0

So I'm using the Python tqdm library to display a progress bar in the terminal when I execute a Python script that includes a for loop.

For example, when I do this:

import time
from tqdm import tqdm

for i in tqdm(range(10)):
    time.sleep(1)
    print(' '*5+str(i))

I get this displayed in the terminal after a few seconds:

enter image description here

, as expected. Here the print statement is not important, is just a computation (I used print just to display the fact that the computation was done).

Now I add a continue statement in this way:

for i in tqdm(range(10)):
    time.sleep(1)
    if i < 5:
        continue
    print(' '*5+str(i))

and I get this:

enter image description here

So, I understand that the print statement is skipped for each iteration below 5, but why the progress bar is not displayed? Is tqdm somehow adding a "display a progress bar" statement at the end of the loop and thus this order is been skipped like the print statement? I feel like it's like if tqdm is still storing the data of the progress bar (the one that should have been displayed), so it knows where to follow after the continue statement is no longer executed.

In any case I would like to know if there is a way of having the progress bar printed in each iteration, even with a continue statement inside the for loop and even if my loop won't execute print(' '*5+str(i)) until it surpasses the fifth iteration of the loop. Just to be clear, I expect to see something like this:

enter image description here

Notice that this displayed the progress bar through all the iterations in the for loop but didn't add the printed number of iterations until the fifth one.

Swike
  • 171
  • 8

3 Answers3

2

Here's a bit of a hacky solution utilizing bar_format:

for i in tqdm(range(10), bar_format='\n{l_bar}{bar:20}{r_bar}'):
    time.sleep(1)
    if i < 5:
        continue
    print(' ' * 5 + str(i), end='', flush=True)

Output:


  0%|                    | 0/10 [00:00<?, ?it/s]
 10%|██                  | 1/10 [00:01<00:09,  1.01s/it]
 20%|████                | 2/10 [00:02<00:08,  1.01s/it]
 30%|██████              | 3/10 [00:03<00:07,  1.01s/it]
 40%|████████            | 4/10 [00:04<00:06,  1.00s/it]
 50%|██████████          | 5/10 [00:05<00:05,  1.00s/it]     5
 60%|████████████        | 6/10 [00:06<00:04,  1.01s/it]     6
 70%|██████████████      | 7/10 [00:07<00:03,  1.00s/it]     7
 80%|████████████████    | 8/10 [00:08<00:02,  1.00s/it]     8
 90%|██████████████████  | 9/10 [00:09<00:01,  1.00s/it]     9
100%|████████████████████| 10/10 [00:10<00:00,  1.00s/it]

Note the end='' and flush=True.

Though it produces a redundant empty line at the start.

Sidenote: for tqdm(range(10), ...) there's a shortcut trange:

from tqdm import trange
for i in trange(10, bar_format='\n{l_bar}{bar:20}{r_bar}'):
    ...
Yevhen Kuzmovych
  • 10,940
  • 7
  • 28
  • 48
0

tqdm wants to overwrite its previous output. It does this staircase effect because your print is manually adding a newline when tqdm is not expecting that.

If you want to send out a message such that it does not interfere with the progress bar, you can use write, which will put your message above the progress bar.

iterator = tqdm(range(10))
for i in iterator:
    time.sleep(0.1)
    if i < 5:
        continue
    iterator.write(f"iteration {i}")
iteration 5
iteration 6
iteration 7
iteration 8
iteration 9
100%|████████████████████████████████████| 10/10 [00:01<00:00,  9.20it/s]

Or if you want to have this behaviour of freezing the progress bar on its steps, you'll have to always print something afterwards, though i wouldn't really recommend this very much.

for i in tqdm(range(10), ncols=60):
    time.sleep(1)
    if i < 5:
        print()
        continue
    print(' '*5+str(i))
  0%|                                | 0/10 [00:00<?, ?it/s]
 10%|██▍                     | 1/10 [00:00<00:00,  9.41it/s]
 20%|████▊                   | 2/10 [00:00<00:00,  9.27it/s]
 30%|███████▏                | 3/10 [00:00<00:00,  9.23it/s]
 40%|█████████▌              | 4/10 [00:00<00:00,  9.26it/s]
 50%|████████████            | 5/10 [00:00<00:00,  9.18it/s]     5
 60%|██████████████▍         | 6/10 [00:00<00:00,  9.21it/s]     6
 70%|████████████████▊       | 7/10 [00:00<00:00,  9.21it/s]     7
 80%|███████████████████▏    | 8/10 [00:00<00:00,  9.18it/s]     8
 90%|█████████████████████▌  | 9/10 [00:00<00:00,  9.16it/s]     9
100%|███████████████████████| 10/10 [00:01<00:00,  9.20it/s]
Talon
  • 1,775
  • 1
  • 7
  • 15
0

It's a single progress bar are should be on single line, but tqdm doesn't handle other facilities writing to the console, so you get a stepped effect. As @Talon said, you can use tqdm's write() method which removes the progress bar, writes to the console, and redraws the progress bar.

Alternatively, you can use a different progress bar library that supports the native behavior, like Enlighten

import time
import enlighten

manager = enlighten.Manager()
pbar = manager.counter(total=10)

for i in pbar(range(10)):
    time.sleep(1)
    print(' '*5+str(i))
aviso
  • 2,371
  • 1
  • 14
  • 15