157

When using the tqdm progress bar: can I add a message to the same line as the progress bar in a loop?

I tried using the "tqdm.write" option, but it adds a new line on every write. I would like each iteration to show a short message next to the bar, that will disappear in the next iteration. Is this possible?

noɥʇʎԀʎzɐɹƆ
  • 8,421
  • 2
  • 44
  • 65
Dror Hilman
  • 5,737
  • 8
  • 35
  • 54

7 Answers7

256

The example shown in Usage of tqdm works well for me.

pbar = tqdm(["a", "b", "c", "d"])
for char in pbar:
    pbar.set_description("Processing %s" % char)

Or alternatively, starting Python 3.8 which supports the walrus operator :=:

for char in (pbar := tqdm(["a", "b", "c", "d"])):
    pbar.set_description("Processing %s" % char)
GoingMyWay
  • 15,546
  • 27
  • 90
  • 132
Ghrua
  • 5,126
  • 4
  • 16
  • 25
  • 30
    Must be marked as correct answer. It is much easier and cleaner than @gabarous's – QtRoS Apr 18 '18 at 12:02
  • 1
    This works, yet it may seem as if the semantics of `set_description` are for a description of the entire loop, whereas the example and/or original question imply status update semantics. – matanster May 03 '19 at 09:44
  • My messages are pretty long and printing them in the same line with the pbar changes the pbar length every time and does not seem so sexy... So how could I print the message in the line below or above the progress bar? – Behnam Jul 03 '19 at 09:12
  • @Behnam you should ask a new question – Monica Heddneck Dec 17 '19 at 03:06
  • 3
    @Benham use [`tqdm.write`](https://tqdm.github.io/docs/tqdm/#write) – jayelm Jan 09 '20 at 01:05
  • @Behnam Or, you can use a fixed-width string set to the maximum length, so the progress bar doesn't change size – divibisan Aug 25 '20 at 17:44
  • The new `:=` operator Python 3.8 can be used to do it in one line: `for char in (pbar := tqdm(LIST)): pbar.set_description("Processing %s" % char)` – oerpli Sep 24 '20 at 11:15
  • 1
    @QtRoS This is essentially the same answer, except for one major missing step: `pbar.refresh()`. Indeed, the `set_description()` method is not meant to be used in an updating loop, it's a way to dynamically set the bar's description after it's already created. `refresh()` ensures the new description will be shown asap, and not wait for the next iteration which may take a while depending on your application. Also this answer is missing the import statements. – gaborous Dec 23 '20 at 02:17
129

You can change the description to show a small message before the progress bar, like this:

from tqdm import trange
from time import sleep
t = trange(100, desc='Bar desc', leave=True)
for i in t:
    t.set_description("Bar desc (file %i)" % i)
    t.refresh() # to show immediately the update
    sleep(0.01)

/EDIT: in the latest releases of tqdm, you can use t.set_description("text", refresh=True) (which is the default) and remove t.refresh() (thanks to Daniel for the tip).

gaborous
  • 14,267
  • 9
  • 79
  • 97
84

Other answers focus on dynamic description, but for a static description you can add a desc argument into the tqdm function.

from tqdm import tqdm

x = [5]*1000
for _ in tqdm(x, desc="Example"):
    pass
 
Example: 100%|██████████████████████████████████| 1000/1000 [00:00<00:00, 1838800.53it/s]
Skippy le Grand Gourou
  • 5,996
  • 4
  • 52
  • 70
Bhanuka Manesha
  • 979
  • 7
  • 8
60

You can use set_postfix to add values directly to the bar.

Example:

from tqdm import tqdm
pbar = tqdm(["a", "b", "c", "d"])
num_vowels = 0
for ichar in pbar:
    if ichar in ['a','e','i','o','u']:
        num_vowels += 1
    pbar.set_postfix({'num_vowels': num_vowels})

The postfix dictionary is integrated into the progress bar:

100%|███████████| 4/4 [00:11<00:00,  2.93s/it, num_vowels=1]

Instead of a dictionary, you can use set_postfix_str to just add a string to the end of the progress bar.

Markus
  • 1,293
  • 12
  • 16
  • Excellent, thank you! Just rather needed `from tqdm.auto import tqdm` in Jupyter à la https://stackoverflow.com/a/67381477/1021819 – jtlz2 Jun 30 '21 at 08:46
6

I personally find it much cleaner to use the with statement:

from tqdm import tqdm

with tqdm(['a','b','c']) as t:
  for c in t:
    t.set_description(f'{c}')
Matt Raymond
  • 61
  • 3
  • 5
  • 1
    True, but it adds another level of indentation, which might be undesirable if you're doing a lot inside the loop. In such a case, I'd rather create `t = tqdm(['a','b','c'])` first and then perform the loop. – cbrnr Jun 10 '21 at 06:24
1

I personally use .set_description() and a progression_bar assignment statement before the for loop:

from tqdm import tqdm

progression_bar = tqdm(["An", "iterable", "object"])
for element in (progression_bar):
    progression_bar.set_description("Processing « %s »" % str(element))
Claude COULOMBE
  • 3,026
  • 1
  • 30
  • 35
0

Although all the answers here are correct, tqdm also provides a set_postfix_str method. The advantage over set_postfix is that you can pass your own formatted string in place of key value pairs. Also set_postfix sorts the key value pairs alphabetically. Here is an MWE.

from tqdm import tqdm
import numpy as np

loop_obj = tqdm(np.arange(10))

for i in loop_obj:
    loop_obj.set_description(f"Count: {i}")  # Adds text before progessbar
    loop_obj.set_postfix_str(f"Next count: {i+1}")  # Adds text after progressbar
learner
  • 2,608
  • 1
  • 14
  • 29