Track several jobs at once with MultiBar¶
A download, an extraction, a build, and a test run – four jobs, each progressing at its own pace, finishing at different times – need more than one bar, laid out and cleaned up as a group rather than by hand.
"""Several named jobs, in one `MultiBar`, finishing at different times.
Reach for `MultiBar` instead of hand-rolled `line_offset` bars when the jobs
are a single logical group -- it lays them out for you, and its context
manager waits for every job to finish before letting the `with` block exit.
Add a job with a subscript (`multibar[label]`); each one here reaches its
own target and calls `finish()` independently, so you can watch them
complete one at a time. Finished bars stay on screen rather than
disappearing -- `remove_finished` defaults to an hour.
"""
import random
import sys
import time
import progressbar
random.seed(0)
JOBS = {
'download': 18,
'extract': 10,
'build': 26,
'test': 22,
}
def main() -> None:
with progressbar.MultiBar(fd=sys.stdout) as multibar:
for name, total in JOBS.items():
multibar[name].max_value = total
remaining = dict(JOBS)
while remaining:
name = random.choice(list(remaining))
multibar[name].increment()
if multibar[name].value >= remaining[name]:
multibar[name].finish()
del remaining[name]
time.sleep(0.01)
if __name__ == '__main__':
main()
MultiBar is a dict of label to ProgressBar: subscripting it with
a new label – multibar[name] – creates that job’s bar on first
access, so there’s no separate registration step. A background thread
renders every job’s bar together, redrawing whichever rows changed. The
with block waits for that thread on exit. Each job calls its own
finish() independently once it reaches its own target (as every job
here does), so its row freezes at its own finished state – elapsed
time, final count – while the others keep moving, rather than one
shared bar where everything completes in lockstep.
Caveats¶
By default, exiting the with block waits forever for every job to
finish. Pass join_timeout= (seconds, or a datetime.timedelta) to
MultiBar() to bound that wait (details: MultiBar).
Rendering multiple bars in place relies on the terminal understanding cursor-movement escapes. JetBrains IDEs (PyCharm, IntelliJ) need “Enable terminal in output console” turned on in the run configuration for this to render correctly. IDLE’s output pane doesn’t support it at all.