Merge pull request #1504 from boegel/dynamic_progress_example

add example with dynamic group of progress bars
This commit is contained in:
Will McGugan 2021-11-20 10:51:53 +00:00 committed by GitHub
commit 666d0cf2b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 124 additions and 1 deletions

View File

@ -30,6 +30,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added json.dumps parameters to print_json https://github.com/willmcgugan/rich/issues/1638
### Added
- Added dynamic_progress.py to examples
### Fixed
- Fixed an edge case bug when console module try to detect if they are in a tty at the end of a pytest run

View File

@ -8,6 +8,7 @@ The following people have contributed to the development of Rich:
- [Pete Davison](https://github.com/pd93)
- [James Estevez](https://github.com/jstvz)
- [Oleksis Fraga](https://github.com/oleksis)
- [Kenneth Hoste](https://github.com/boegel)
- [Finn Hughes](https://github.com/finnhughes)
- [Josh Karpel](https://github.com/JoshKarpel)
- [Andrew Kettmann](https://github.com/akettmann)

View File

@ -192,7 +192,7 @@ If the :class:`~rich.progress.Progress` class doesn't offer exactly what you nee
Multiple Progress
-----------------
You can't have different columns per task with a single Progress instance. However, you can have as many Progress instance as you like in a :ref:`live`. See `live_progress.py <https://github.com/willmcgugan/rich/blob/master/examples/live_progress.py>`_ for an example of using multiple Progress instances.
You can't have different columns per task with a single Progress instance. However, you can have as many Progress instance as you like in a :ref:`live`. See `live_progress.py <https://github.com/willmcgugan/rich/blob/master/examples/live_progress.py>`_ and `dynamic_progress.py <https://github.com/willmcgugan/rich/blob/master/examples/dynamic_progress.py>`_ for examples of using multiple Progress instances.
Example
-------

View File

@ -0,0 +1,118 @@
"""
Demonstrates how to create a dynamic group of progress bars,
showing multi-level progress for multiple tasks (installing apps in the example),
each of which consisting of multiple steps.
"""
import time
from rich.console import Group
from rich.panel import Panel
from rich.live import Live
from rich.progress import (
BarColumn,
Progress,
SpinnerColumn,
TextColumn,
TimeElapsedColumn,
)
def run_steps(name, step_times, app_steps_task_id):
"""Run steps for a single app, and update corresponding progress bars."""
for idx, step_time in enumerate(step_times):
# add progress bar for this step (time elapsed + spinner)
action = step_actions[idx]
step_task_id = step_progress.add_task("", action=action, name=name)
# run steps, update progress
for _ in range(step_time):
time.sleep(0.5)
step_progress.update(step_task_id, advance=1)
# stop and hide progress bar for this step when done
step_progress.stop_task(step_task_id)
step_progress.update(step_task_id, visible=False)
# also update progress bar for current app when step is done
app_steps_progress.update(app_steps_task_id, advance=1)
# progress bar for current app showing only elapsed time,
# which will stay visible when app is installed
current_app_progress = Progress(
TimeElapsedColumn(),
TextColumn("{task.description}"),
)
# progress bars for single app steps (will be hidden when step is done)
step_progress = Progress(
TextColumn(" "),
TimeElapsedColumn(),
TextColumn("[bold purple]{task.fields[action]}"),
SpinnerColumn("simpleDots"),
)
# progress bar for current app (progress in steps)
app_steps_progress = Progress(
TextColumn(
"[bold blue]Progress for app {task.fields[name]}: {task.percentage:.0f}%"
),
BarColumn(),
TextColumn("({task.completed} of {task.total} steps done)"),
)
# overall progress bar
overall_progress = Progress(
TimeElapsedColumn(), BarColumn(), TextColumn("{task.description}")
)
# group of progress bars;
# some are always visible, others will disappear when progress is complete
progress_group = Group(
Panel(Group(current_app_progress, step_progress, app_steps_progress)),
overall_progress,
)
# tuple specifies how long each step takes for that app
step_actions = ("downloading", "configuring", "building", "installing")
apps = [
("one", (2, 1, 4, 2)),
("two", (1, 3, 8, 4)),
("three", (2, 1, 3, 2)),
]
# create overall progress bar
overall_task_id = overall_progress.add_task("", total=len(apps))
# use own live instance as context manager with group of progress bars,
# which allows for running multiple different progress bars in parallel,
# and dynamically showing/hiding them
with Live(progress_group):
for idx, (name, step_times) in enumerate(apps):
# update message on overall progress bar
top_descr = "[bold #AAAAAA](%d out of %d apps installed)" % (idx, len(apps))
overall_progress.update(overall_task_id, description=top_descr)
# add progress bar for steps of this app, and run the steps
current_task_id = current_app_progress.add_task("Installing app %s" % name)
app_steps_task_id = app_steps_progress.add_task(
"", total=len(step_times), name=name
)
run_steps(name, step_times, app_steps_task_id)
# stop and hide steps progress bar for this specific app
app_steps_progress.update(app_steps_task_id, visible=False)
current_app_progress.stop_task(current_task_id)
current_app_progress.update(
current_task_id, description="[bold green]App %s installed!" % name
)
# increase overall progress now this task is done
overall_progress.update(overall_task_id, advance=1)
# final update for message on overall progress bar
overall_progress.update(
overall_task_id, description="[bold green]%s apps installed, done!" % len(apps)
)