Search code examples
pythonprogress-bartqdm

python progress bar created by 'tqdm' followed by unexpected values/symbol after each iteration


I am using 'tqdm' package to establish a progress bar. However, after every iteration of odd numbers (1,3,5) an unexpected value/number occurs (in this case number 5 appears when argument 'ascii' is equal to True).

Fortschritt:  17%|####5                      | 1/6 [00:02<00:11,  2.20s/it]
Fortschritt:  33%|#########                  | 2/6 [00:07<00:14,  3.54s/it]
Fortschritt:  50%|#############5             | 3/6 [00:13<00:13,  4.65s/it]

The code is relative complex so I would not copy them here... if it is necessary for finding out and solving the problem, I will reduce it and paste here afterwards.

any help will be appreciated. :)

---------code supplemented----------

from os import makedirs
from tqdm import tqdm
import time

Workflow = ['0','1', '2', '3', '4', '5', '6']
Probenliste = ['A','B']
Oligos= {'Oligo1':'Sequence1', 'Oligo2':'Sequence2'}

for schritt in tqdm(range(len(Workflow)-1), desc='Fortschritt', ascii=True, ncols=75):
    schritt_name = Workflow[schritt+1]
    makedirs(schritt_name)
    for n in range(len(Probenliste)):
        probe = Probenliste[n]
        for primer_name, sequence in Oligos.items():
            time.sleep(1)

Solution

  • According to [GitHub]: tqdm/tqdm - Documentation (emphasis is mine):

    • ascii : bool or str, optional

      If unspecified or False, use unicode (smooth blocks) to fill the meter. The fallback is to use ASCII characters " 123456789#".

    I played a bit with the various parameters, and I discovered this empirically (I don't have a rigorous explanation), as I didn't dive enough into the code:

    • The "funky" values come from the string: " 123456789#" (when ascii is True)
    • They depend on:
      1. iterable (its length)
      2. The length of the bar (called meter) itself, which depends on the length of desc and the value of ncols
      • Looks like a division (remainder) between the element index in #1. and #2.

    The behavior can be reproduced using much simpler code.

    code00.py:

    #!/usr/bin/env python
    
    import sys
    import time
    
    from tqdm import tqdm
    
    
    def main(*argv):
        data = " " * 6
    
        cols = (
            49,  # 8, 6, 5, 3, 1, ...
            48,  # 6, 3,  , ...
            47,  # 5,  , ...
            46,  # 3, 6,  , ...
            45,  # 1, 3, 5, 6, 8, ...
            44,  # 
        )
    
        sleep_time = 0.5
    
        print("Various lengths:")
        ascii = True
        #ascii = " #"  # !!! DECOMMENT THIS LINE for the problem to go away !!!
        for col in cols:
            for _ in tqdm(data, desc="X", ascii=ascii, ncols=col):
                time.sleep(sleep_time)
    
        print("\nA combination of parameters that displays all digits")
        for _ in tqdm(range(10), desc="X", ascii=True, ncols=50):
            time.sleep(sleep_time)
    
    
    if __name__ == "__main__":
        print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                       64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        rc = main(*sys.argv[1:])
        print("\nDone.")
        sys.exit(rc)
    

    Output (although only the final one which is not very relevant, as it doesn't capture the behavior):

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q071951854]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py
    Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32
    
    Various lengths:
    X: 100%|###########| 6/6 [00:03<00:00,  1.95it/s]
    X: 100%|##########| 6/6 [00:03<00:00,  1.96it/s]
    X: 100%|#########| 6/6 [00:03<00:00,  1.95it/s]
    X: 100%|########| 6/6 [00:03<00:00,  1.95it/s]
    X: 100%|#######| 6/6 [00:03<00:00,  1.96it/s]
    X: 100%|######| 6/6 [00:03<00:00,  1.95it/s]
    
    A combination of parameters that displays all digits
    X: 100%|##########| 10/10 [00:05<00:00,  1.95it/s]
    
    Done.
    

    The fix:

    Use the string " #" (for ascii) which only contains the initial char (SPACE) and the final one (POUND, #). This way, there won't be any possible intermediate char.