Skip to content

Patterns

These are useful, but they are not the first things most people need.

To leave or not to leave

You can decide whether progress bars should stay on the screen after completion or disappear using the leave parameter.

import mqdm
import time


xs = ["apples", "pears", "plums", "figs"]

print("These go away when they're done.")
for fruit in xs:
    for n in mqdm.mqdm(range(40), desc=fruit, leave=False):
        time.sleep(0.02)
print("See? Nice and clean.")

print("Now these stick around.")
for fruit in xs:
    for n in mqdm.mqdm(range(40), desc=fruit):
        time.sleep(0.02)
print("Great, cuz I wasn't paying attention.")

Hide tiny inner loops

You can conditionally disable progress bars.

import mqdm
import time
from mqdm import print


number_of_tasks = [22, 4, 9, 8, 21]
min_iters = 10

for x in mqdm.mqdm(number_of_tasks, desc=f"Only showing progress bars with {min_iters}+ iterations" ):

    for y in mqdm.mqdm(range(x), disable=x <= min_iters, leave=False):
        time.sleep(0.1)

    if x <= min_iters:
        print(f"You didn't see me, I had only {x} iterations")

This is a nice way to avoid visual noise when the inner work is too small to be worth drawing every time.

advance (fast) vs update

There are 3 primary ways to manually update a progress bar: update(n, **options), advance(n), and set(**options).

  • set() is the most general way to update progress bar fields.
  • update(n, **kw) is essentially set(advance=n, **kw) - the only difference from set() is that by default, it will increment the progress bar by 1.
  • advance(n) is a performance-tuned way to advance the progress bar without the overhead of a full update. It runs 10–20× faster in tight loops and uses batched advances tied to the configured refresh rate, for efficiency. This is especially helpful for cross-process scenarios.
import mqdm
import time

# `update()` does a full update on every call — flexible but slower.
t0 = time.time()
with mqdm.mqdm("update (generic, slow)") as pbar:
    while time.time() - t0 < 6:
        pbar.update(1)

# `advance()` is tuned specifically for fast increments and is 
# >10-15x faster than `update()` in tight loops.
# The trade-off is that state updates are throttled (default 8 fps)
# so the update is not strictly atomic.
t0 = time.time()
with mqdm.mqdm("advance (fast)") as pbar:
    while time.time() - t0 < 6:
        pbar.advance(1)

# Iterating with `for _ in mqdm.mqdm()` already uses the fast path internally.
t0 = time.time()
with mqdm.mqdm("for _ in mqdm.mqdm()") as pbar:
    for i in pbar((i for i in range(int(6e12)))):
        if time.time() - t0 >= 6:
            break

Iterating with for x in mqdm(...) already uses advance internally.

Update options with set()

import mqdm
import time

# `set()` changes any facet of a live bar. A few useful moves, methodically:
with mqdm.mqdm(total=100, desc="downloading") as bar:

    # 1. jump the counter to an absolute value (not +1) and relabel, in one call
    for pct in range(20, 81, 10):
        time.sleep(0.25)
        bar.set(completed=pct, description=f"downloading · {pct}%")

    # 2. the download was bigger than expected — grow the total mid-run
    print("Rechecking download size...")
    time.sleep(1)
    bar.set(total=160)
    print(f"New size: {bar.total}")
    time.sleep(0.5)
    for pct in range(100, 161, 10):
        time.sleep(0.25)
        bar.set(completed=pct, description=f"downloading · {pct}%")
    print("Download complete.")

    # 3. re-scope the same bar for a new phase: new label, reset count and total
    bar.set(description="verifying", completed=0, total=3)
    for step in ("checksum", "signature", "unpack"):
        time.sleep(0.8)
        bar.set(advance=1, description=f"verifying · {step}")
    print("Done :)")

set() changes any field of a live bar:

  • completed= sets the counter to an absolute value; advance= moves it relatively.
  • total= rescales the bar mid-run.
  • description= relabels it; a callable desc=lambda item, i: ... is reused each step (and on advance's fast path via arg=).
  • visible= hides or shows it; leave=/transient= control whether the row stays after it finishes.
  • any other keyword becomes a custom task field, for use with custom columns.

Pass several at once to change them together, or reuse one bar across phases — step 3 turns the download bar into the verification bar.

Keep bars alive with sustain()

By default, closing a bar will freeze its display and prevent further updates.

You can use sustain() to keep the bars from detaching so that your prints will go above the group of progress bars instead of in between them.

import mqdm
import time

print("Normal behavior")
for i in range(3):
    for _ in mqdm.mqdm(range(4), desc=f"section {i}"):
        time.sleep(0.2)
    print(f"Finished section {i}")
print("Finished all sections in normal behavior")
print()

print("Sustained behavior")
with mqdm.sustain():
    for i in range(3):
        for _ in mqdm.mqdm(range(4), desc=f"section {i}"):
            time.sleep(0.2)
        print(f"Finished section {i}")
    print("Finished all sections in sustained behavior")

Pause for interaction

Use pause when you want to do something like open an interactive shell in the middle of a loop. e.g.

import mqdm
import time

# A long job that hits something worth a closer look partway through. `pause()`
# freezes the live bars so you can drop into a real shell (or debugger) on a
# clean screen, poke at live state, then let the job carry on where it left off.
data = {}
for i in mqdm.mqdm(range(20), desc="processing"):
    time.sleep(0.08)
    data[i] = i * i
    if i == 9:
        with mqdm.pause():
            print("Halfway — let's inspect the data so far in a shell.")
            import IPython
            IPython.embed()
        # bars resume automatically once the shell exits

print("Done — processed", len(data), "items")

Reading files

Show file reading with byte-oriented progress.

import time
import mqdm

import os
import tempfile


fruits = ["apples", "pears", "plums", "figs", "bananas", "kiwis", 
          "mangos", "oranges", "blueberries", "raspberries"]
text = "\n".join(fruits * 1_000_000)

with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp:
    tmp.write(text)
    path = tmp.name

    # Read the file in chunks

    chunk_size = 1024 * 1024  # 1 MB
    with mqdm.mqdm(desc="uploading", bytes=True, total=os.path.getsize(path)) as bar:
        with open(path, "rb") as f:
            while chunk := f.read(chunk_size):
                time.sleep(0.2)
                bar.advance(len(chunk))