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 essentiallyset(advance=n, **kw)- the only difference fromset()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 callabledesc=lambda item, i: ...is reused each step (and onadvance's fast path viaarg=).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))