#| label: asciicast-setup #| include: false #| cache: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>", out.width = "100%", cache = TRUE, asciicast_at = "all", asciicast_cpp11_linkingto = "[[cpp11::linking_to(\"cli\")]]" ) asciicast::init_knitr_engine( echo = TRUE, echo_input = FALSE )
#| label: asciicast-tick-time #| include: false #| cache: false set.seed(1) Sys.setenv(CLI_TICK_TIME = "100")
#| label: progress-setup setup #| cache: false #| include: false library(cli) options(cli.progress_show_after = 0) options(cli.progress_clear = FALSE) options(cli.progress_format_iterator = NULL) options(cli.progress_format_iterator_nototal = NULL) options(cli.progress_format_tasks = NULL) options(cli.progress_format_tasks_nototal = NULL) options(cli.progress_format_download = NULL) options(cli.progress_format_download_nototal = NULL)
library(cli) options(cli.progress_show_after = 0) options(cli.progress_clear = FALSE)
This document discusses the structure and simplest uses of the cli progress bar API. For more advanced usage and the C progress bar API, see the 'Advanced cli progress bars' article and the manual pages.
From version 3.0.0 cli provides a set of functions to create progress bars. The main goals of the progress bar API are:
Add a progress bar in three steps:
cli_progress_bar()
to add create a progress bar.cli_progress_update()
to update it.cli_progress_done()
to terminate it.For example:
#| label: classic-example #| fig-alt: "Progress bar, that contains, from left to right, the specified label, the bar with green squares, the progress percentage, and the ETA." clean <- function() { cli_progress_bar("Cleaning data", total = 100) for (i in 1:100) { Sys.sleep(5/100) cli_progress_update() } cli_progress_done() } clean()
The traditional API provides full control w.r.t when to create, update and terminate a progress bar.
For conciseness, the progress bar functions refer to the current progress bar by default. Every function has at most one current progress bar at any time. The current progress bar of a function is terminated when the function creates another progress bar or when the function returns, errors or is interrupted.
The current progress bar lets us omit the cli_progress_done()
call:
#| label: current #| fig-alt: "Two progress bars, after the first finishes, the second starts and then finishes as well." clean <- function() { cli_progress_bar("Cleaning data #1", total = 100) for (i in 1:100) { Sys.sleep(3/100) cli_progress_update() } cli_progress_bar("Cleaning data #2", total = 100) for (i in 1:100) { Sys.sleep(3/100) cli_progress_update() } } clean()
In some cases the total number of progress units is unknown, so simply
omit them from cli_progress_bar()
(or set them to NA
). cli uses a
different display when total
is unknown:
#| label: unknown-total-seed #| include: false #| cache: false set.seed(1)
#| label: unknown-total #| fig-alt: "Example progress bar where the total number of units is unknown. It has a spinner, the specified label, shows how many units are done, how many units are completed per second and the elapsed time." walk_dirs <- function() { cli_progress_bar("Walking directories") while (TRUE) { if (runif(1) < 0.01) break Sys.sleep(0.01) cli_progress_update() } cli_progress_update(force = TRUE) } walk_dirs()
By default, cli does not show progress bars that are terminated within two
seconds after their creation. The end user can configure this limit
with the cli.progress_show_after
global option.
For example, in this document we set the limit to zero seconds, so progress bars are shown at their first update.
cli_progress_along()
cli_progress_along()
is currently experimental.
To add a progress bar to a call to lapply()
or another mapping function,
wrap the input sequence into cli_progress_along()
:
lapply(cli_progress_along(X), fun)
cli_progress_along()
works similarly to seq_along()
, it returns an index vector.
If you use cli_progress_along()
, then lapply()
will pass the indices
of the elements in X
to fun
, instead of the elements themselves.
cli_progress_along()
expects that the index vector will be used only once,
from beginning to end. It is best to never assign the return value of
cli_progress_along()
to a variable.
An example:
#| label: tickalong #| fig-alt: "Progress bar with green squares, that also shows the progress percentage and the ETA." f <- function() { rawabc <- lapply( cli_progress_along(letters), function(i) { charToRaw(letters[i]) Sys.sleep(0.5) } ) } f()
cli_progress_along()
uses ALTREP, so it only works from R 3.5.0 and later.
On older R versions it is equivalent to seq_along()
and it does not
create a progress bar.
for
loopsYou can also use cli_progress_along()
in for
loops, with the additional
complication that if you use break
, then you might need to terminate the
progress bar explicitly:
for (i in cli_progress_along(seq)) { ... if (cond) cli_progress_done() && break ... }
cli_progress_done()
always returns TRUE
to allow this form.
Alternatively, you can terminate the progress bar right after loop:
for (i in cli_progress_along(seq)) { ... if (cond) break ... } cli_progress_done()
If the function containing the for
loop returns after the loop, or
you create another progress bar with cli_progress_along()
or cli_progress_bar()
,
then no explicit cli_progress_done()
is needed.
Often you don't need the full power of the progress bar API, and only want
to show a status message. The cli_progress_message()
and
cli_progress_step()
functions are tailored for this.
cli_progress_message()
shows a (potentially templated) message in the
status bar. For convenience, the progress bar rules still apply here by
default:
#| label: cli_progress_message #| fig-alt: "The three messages are shown, each on its own line, the third one is iterated over 5 steps." f <- function() { cli_progress_message("Task one is running...") Sys.sleep(2) cli_progress_message("Task two is running...") Sys.sleep(2) step <- 1L cli_progress_message("Task three is underway: step {step}") for (step in 1:5) { Sys.sleep(0.5) cli_progress_update() } } f()
Status messages may use glue interpolation, cli styling and pluralization,
as usual. You can call cli_progress_update()
to update a status message.
cli_progress_step()
is slightly different from
cli_progress_message()
:
#| label: cli_progress_step_simple #| fig-alt: "Four progress steps are shown, each on its own line. Each steps show up as an 'i' (info) step first, and stays like that while it is running. When it is done, the 'i' is turned into a tick mark, and the running time of the step is added to the line at the end, in grey." f <- function() { cli_progress_step("Downloading data") Sys.sleep(2) cli_progress_step("Importing data") Sys.sleep(1) cli_progress_step("Cleaning data") Sys.sleep(2) cli_progress_step("Fitting model") Sys.sleep(3) } f()
As usual, you can use cli_progress_step()
to update an existing status
message.
#| label: cli_progress_step #| fig-alt: "First the 'About to start..'. message is shown in its own line. Then a progress bar starts on the next line. The progress bar takes 10 steps. After the fifth step, the progress bar is overwritten with the 'Already half way!' message and the progress bar is moved down to the third line." f <- function(n = 10) { cli_alert_info("About to start downloads of {n} file{?s}") i <- 0 cli_progress_step("Got {i}/{n} {qty(i)}file{?s}.") for (i in seq_len(n)) { Sys.sleep(0.5) if (i == 5) cli_alert_info("Already half way!") cli_progress_update() } } f()
If you can update the status message frequently enough, then you can also add a spinner to it:
#| label: cli_progress_step_spinner #| fig-alt: "Four steps are shown, each on its own line. While each step is running, its line has a spinner. Once it is done, the spinner turns into a tick mark and the running time of the step is added to its line at the end." f <- function() { cli_progress_step("Downloading data", spinner = TRUE) for (i in 1:100) { cli_progress_update(); Sys.sleep(2/100) } cli_progress_step("Importing data", spinner = TRUE) for (i in 1:100) { cli_progress_update(); Sys.sleep(1/100) } cli_progress_step("Cleaning data", spinner = TRUE) for (i in 1:100) { cli_progress_update(); Sys.sleep(2/100) } cli_progress_step("Fitting model", spinner = TRUE) for (i in 1:100) { cli_progress_update(); Sys.sleep(3/100) } } f()
cli_progress_step()
automatically handles errors, and styles the
status message accordingly:
#| label: step-error #| fig-alt: "Two steps are shown, each in its own line. While a step is running its line is an 'i' (info) line. The first step finishes successfully and its 'i' mark is turned into a tick mark. The second step fails with an error and its line is overwritten with the error message, and moved down to the third line, and marked with an 'x' (error) mark." #| asciicast_rows: 3 f <- function() { cli_progress_step("First step, this will succeed") Sys.sleep(1) cli_progress_step("Second step, this will fail") Sys.sleep(1) stop("Something is wrong here") } f()
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.