From c875b9c8342485b1648eb4c343b65b3cc73ec090 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sun, 19 Sep 2021 09:39:42 +0200 Subject: [PATCH 1/7] add example with dynamic group of progress bars --- docs/source/progress.rst | 2 +- examples/dynamic_progress.py | 107 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 examples/dynamic_progress.py diff --git a/docs/source/progress.rst b/docs/source/progress.rst index 5c70e329..364ad94c 100644 --- a/docs/source/progress.rst +++ b/docs/source/progress.rst @@ -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 `_ 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 `_ and `dynamic_progress.py `_ for examples of using multiple Progress instances. Example ------- diff --git a/examples/dynamic_progress.py b/examples/dynamic_progress.py new file mode 100644 index 00000000..8001f29a --- /dev/null +++ b/examples/dynamic_progress.py @@ -0,0 +1,107 @@ +""" + +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 RenderGroup +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(1) + 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 +group = RenderGroup( + 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", (3, 2, 5, 3)), + ("two", (2, 5, 10, 6)), + ("three", (5, 1, 5, 3)), +] + +# 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(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)) From 5ecd4ad7741e22915704c2b80d403cefe93cd713 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sun, 19 Sep 2021 09:49:04 +0200 Subject: [PATCH 2/7] reformat dynamic_progress.py example with Black --- examples/dynamic_progress.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/examples/dynamic_progress.py b/examples/dynamic_progress.py index 8001f29a..0656eb65 100644 --- a/examples/dynamic_progress.py +++ b/examples/dynamic_progress.py @@ -10,7 +10,13 @@ import time from rich.console import RenderGroup from rich.live import Live -from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn +from rich.progress import ( + BarColumn, + Progress, + SpinnerColumn, + TextColumn, + TimeElapsedColumn, +) def run_steps(name, step_times, app_steps_task_id): @@ -50,23 +56,20 @@ step_progress = Progress( ) # progress bar for current app (progress in steps) app_steps_progress = Progress( - TextColumn("[bold blue]Progress for app {task.fields[name]}: {task.percentage:.0f}%"), + 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}") + TimeElapsedColumn(), BarColumn(), TextColumn("{task.description}") ) # group of progress bars; # some are always visible, others will disappear when progress is complete group = RenderGroup( - current_app_progress, - step_progress, - app_steps_progress, - overall_progress + current_app_progress, step_progress, app_steps_progress, overall_progress ) # tuple specifies how long each step takes for that app @@ -92,16 +95,22 @@ with Live(group): # 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) + 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) + 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)) + overall_progress.update( + overall_task_id, description="[bold green]%s apps installed, done!" % len(apps) + ) From 58b71127aaeaf5776c658772d17012f60101feae Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sun, 19 Sep 2021 09:51:44 +0200 Subject: [PATCH 3/7] add @boegel to contributors --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ad5147af..953348a2 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -7,6 +7,7 @@ The following people have contributed to the development of Rich: - [Gregory Beauregard](https://github.com/GBeauregard/pyffstream) - [Pete Davison](https://github.com/pd93) - [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) From ab746e653737f43c1db8cb1d5857fa635b7246ce Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sun, 19 Sep 2021 09:51:51 +0200 Subject: [PATCH 4/7] udpate changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c8f4baa..9086beda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [10.12.1] - unreleased +### 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 From b9af6f7d8f3aec471e4930a450be067e5b379559 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 16 Oct 2021 13:42:46 +0200 Subject: [PATCH 5/7] use Group since RenderGroup is sort of deprecated, rename 'group' to 'process_group' --- examples/dynamic_progress.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/dynamic_progress.py b/examples/dynamic_progress.py index 0656eb65..d45bb377 100644 --- a/examples/dynamic_progress.py +++ b/examples/dynamic_progress.py @@ -8,7 +8,7 @@ each of which consisting of multiple steps. import time -from rich.console import RenderGroup +from rich.console import Group from rich.live import Live from rich.progress import ( BarColumn, @@ -68,7 +68,7 @@ overall_progress = Progress( ) # group of progress bars; # some are always visible, others will disappear when progress is complete -group = RenderGroup( +progress_group = Group( current_app_progress, step_progress, app_steps_progress, overall_progress ) @@ -86,7 +86,7 @@ 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(group): +with Live(progress_group): for idx, (name, step_times) in enumerate(apps): # update message on overall progress bar From 8a1585f2cf88da8c51a80bde3114e1023539fb17 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 16 Oct 2021 13:48:36 +0200 Subject: [PATCH 6/7] put app progress in panel, speed things up --- examples/dynamic_progress.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/dynamic_progress.py b/examples/dynamic_progress.py index d45bb377..a1101928 100644 --- a/examples/dynamic_progress.py +++ b/examples/dynamic_progress.py @@ -9,6 +9,7 @@ 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, @@ -29,7 +30,7 @@ def run_steps(name, step_times, app_steps_task_id): # run steps, update progress for _ in range(step_time): - time.sleep(1) + time.sleep(0.5) step_progress.update(step_task_id, advance=1) # stop and hide progress bar for this step when done @@ -69,15 +70,15 @@ overall_progress = Progress( # group of progress bars; # some are always visible, others will disappear when progress is complete progress_group = Group( - current_app_progress, step_progress, app_steps_progress, overall_progress + 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", (3, 2, 5, 3)), - ("two", (2, 5, 10, 6)), - ("three", (5, 1, 5, 3)), + ("one", (2, 1, 4, 2)), + ("two", (1, 3, 8, 4)), + ("three", (2, 1, 3, 2)), ] # create overall progress bar From 28a33cbcc38bb2b3f059c019dda44521dc7ed65e Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 16 Oct 2021 13:49:56 +0200 Subject: [PATCH 7/7] reformat with latest black (21.9b0) --- examples/dynamic_progress.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/dynamic_progress.py b/examples/dynamic_progress.py index a1101928..c38da2eb 100644 --- a/examples/dynamic_progress.py +++ b/examples/dynamic_progress.py @@ -70,7 +70,8 @@ overall_progress = Progress( # 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 + Panel(Group(current_app_progress, step_progress, app_steps_progress)), + overall_progress, ) # tuple specifies how long each step takes for that app