223

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ɐɹƆ
  • 9,967
  • 2
  • 50
  • 67
Dror Hilman
  • 6,837
  • 9
  • 39
  • 56

8 Answers8

402

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(f"Processing {char}")
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
Ghrua
  • 6,746
  • 5
  • 17
  • 25
  • 39
    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
  • 1
    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
  • 4
    @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
  • 3
    @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
151

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
  • 15,832
  • 10
  • 83
  • 102
  • 4
    What is the `leave=True` argument doing? – Eduardo Pignatelli Sep 11 '19 at 17:01
  • 3
    @EduardoPignatelli it leaves the progress bar printed after reaching completion. Otherwise, `tqdm` cleans up the progress bar from the console output (or any sys.stdout in general if it supports a way of cleaning). – gaborous Sep 13 '19 at 11:14
131

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
  • 6,976
  • 4
  • 60
  • 76
Bhanuka Manesha
  • 1,457
  • 1
  • 8
  • 8
  • 4
    @Mark What if `desc` is dynamic and only derived within each iteration? – jtlz2 Jun 30 '21 at 08:34
  • That is what the `set_description` approach many of the other answers are suggesting is for. This answer is for the static case. – WolfLink Nov 30 '22 at 08:42
  • dynamically deriving `desc` works as of `python 3.8.13`. just use it like this: `for i in tqdm(range(100), desc=f'Processing {i}'): pass` – Nasheed Yasin Dec 10 '22 at 03:25
  • @NasheedYasin: that can't possibly work, because the `i` in the f-string is bound before the loop is entered. – Eric Feb 20 '23 at 01:03
  • @Eric thanks for pointing it out. It won't indeed work. Should I delete my previous comment as it may mislead? – Nasheed Yasin Feb 21 '23 at 02:43
89

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,635
  • 15
  • 17
  • 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
7

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
  • 71
  • 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
    Or `for c in (t := tqdm(my_list)):` – Tim MB Jan 04 '23 at 20:00
5

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
  • 3,168
  • 3
  • 18
  • 35
3

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,434
  • 2
  • 36
  • 39
0

When using the tqdm progress bar in Python, there are different ways to define it, either by using the with statement or by assigning the progress bar to a variable with tqdm() function. In some cases, you may want to update the progress bar inside the loop without adding an extra indentation layer.

To achieve this, you can use the set_postfix() method to update the progress bar with the latest information. To avoid adding an extra indentation layer, you can define the progress bar outside of the loop and use it inside the loop. For example, you can use the enumerate() function to iterate over a list while keeping track of the index, and then pass this to the progress bar like this:

from tqdm import tqdm

object_list = ["a", "b", "c"]

pbar = tqdm(enumerate(object_list), descr="...")
for i, obj in pbar:
    ...
    pbar.set_postfix("foo")
    ...
    pbar.set_postfix("bar")

In this way, you define the tqdm progress bar before the loop, and then you can use the set_postfix() method to update the progress bar inside the loop without adding an extra indentation layer. By using the enumerate() function, you also have access to the current index, which you can pass to the set_postfix() method to display it in the progress bar.

This approach provides the flexibility to update the progress bar inside the loop while keeping the code clean and readable. This answer is based on the comments from @cbrnr and @tim-mb.

MbBrainz
  • 122
  • 6