Skip to main content

MultiProgress

Struct MultiProgress 

Source
pub struct MultiProgress { /* private fields */ }
Expand description

Manages multiple progress bars, potentially from different threads.

§ProgressBar lifecycle in a MultiProgress

This section was written to help you avoid unexpected behavior when using MultiProgress. The two most common issues that users face are:

  1. Inadvertent draws prior to adding to the MultiProgress
  2. ProgressBars getting dropped too soon

§Inadvertent draws

MultiProgress can only coordinate drawing progress bars on the screen if it is aware of them. A common bug is to create a ProgressBar, accidentally cause it to draw (or tick), and then later add it to the MultiProgress. This can lead to screen corruption since MultiProgress has no way to “undo” whatever the ProgressBar did before the bar came under its purview.

Here’s an example of potentially problematic code. The bar is created at (1) but added to the MultiProgress at (2).

// Bad code, do not use!
let m = MultiProgress::new();
let pb = ProgressBar::new(100);      // (1)
// It's awfully tempting to touch
// `pb`, before it's added to `m`...
m.add(pb);                           // (2)

Instead, create the ProgressBar and add it to the MultiProgress as a single call:

// Better code
let m = MultiProgress::new();
let pb = m.add(ProgressBar::new(100));
// Then style/exercise it as you please:
// e.g. pb.set_style()

Future work may deprecate the “bad” API and steer users toward the “good” model. See https://github.com/console-rs/indicatif/issues/677 for example.

§Premature drops

Consider this code, with an overall “total” ProgressBar and an individual ProgressBar for each of 5 jobs. The intention is that when each job bar finishes, it stays on the screen with the “DONE!” message. Also, during job processing, we call MultiProgress::suspend to temporarily clear the terminal and manually print some extra messages.

use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
use std::borrow::Cow;

fn main() {
    let m = MultiProgress::new();
    let sty = ProgressStyle::with_template(
        "{prefix:<10} [{elapsed}] {bar:20.red/blue} {pos:>7}/{len:7} {msg}",
    )
    .unwrap()
    .progress_chars("##-");

    let total = m.add(ProgressBar::new(10));
    total.set_style(sty.clone());
    total.set_prefix("total");
    for i in 0..5 {
        let name = format!("Job #{i}");
        let pb = m.insert_before(
            &total,
            ProgressBar::new(3).with_finish(ProgressFinish::WithMessage(Cow::Borrowed("DONE!"))),
        );

        pb.set_style(sty.clone());
        pb.set_prefix(name);
        for _ in 0..3 {
            // Temporarily clear the screen so we can print a message to the terminal
            m.suspend(|| {
                eprintln!("from job #{i}...");
            });

            pb.inc(1);
        }
        pb.finish_using_style();
        total.inc(1);
    }

    total.finish();
}

The issue is that at the end of each loop iteration, pb is dropped. Conceptually MultiProgress only maintains weak references to ProgressBars. At the next loop iteration, suspend causes MultiProgress to clear the screen. MultiProgress’ “zombie” algorithm ensures the dropped (zombie) bar is not left behind on the screen. But MultiProgress can’t reconstitute the ‘finish’ state (i.e. “DONE!” text), since the bar no longer exists.

The solution is to ensure each ProgressBar lives long enough:

// Vec to hold handles
let mut pbs = vec![];
for i in 0..5 {
    let name = format!("Job #{i}");
    let pb = m.insert_before(
        &total,
        ProgressBar::new(3).with_finish(ProgressFinish::WithMessage(Cow::Borrowed("DONE!"))),
    );
    // Stash a handle to the pb to keep it alive till end of loop
    pbs.push(pb.clone());

    pb.set_style(sty.clone());
    pb.set_prefix(name);
    // ... snipped ...
}

§The “zombie” algorithm

The “zombie” algorithm is a compromise. If the user lets a ProgressBar drop, then it is taken as a strong hint that we can forget about it. But, the MultiProgress::println method advertises the ability to print a message above all progress bars.

As a compromise, MultiProgress will keep track of how many lines of text were last printed to the screen, even for ProgressBars that have dropped. But the next time MultiProgress clears the screen, e.g. for a MultiProgress::suspend or MultiProgress::println, any so-called “zombie lines” at the head of the list are wiped but then not re-drawn. If you really want those lines to be persisted on screen, then keep the ProgressBars around longer, as described in the previous section.

Implementations§

Source§

impl MultiProgress

Source

pub fn new() -> Self

Creates a new multi progress object.

Progress bars added to this object by default draw directly to stderr, and refresh a maximum of 15 times a second. To change the refresh rate set the draw target to one with a different refresh rate.

Source

pub fn with_draw_target(draw_target: ProgressDrawTarget) -> Self

Creates a new multi progress object with the given draw target.

Source

pub fn set_draw_target(&self, target: ProgressDrawTarget)

Sets a different draw target for the multiprogress bar.

Use MultiProgress::with_draw_target to set the draw target during creation.

Source

pub fn set_move_cursor(&self, move_cursor: bool)

Set whether we should try to move the cursor when possible instead of clearing lines.

This can reduce flickering, but do not enable it if you intend to change the number of progress bars.

Source

pub fn set_alignment(&self, alignment: MultiProgressAlignment)

Set alignment flag

Source

pub fn add(&self, pb: ProgressBar) -> ProgressBar

Adds a progress bar.

The progress bar added will have the draw target changed to a remote draw target that is intercepted by the multi progress object overriding custom ProgressDrawTarget settings.

The progress bar will be positioned below all other bars currently in the MultiProgress.

Adding a progress bar that is already a member of the MultiProgress will have no effect.

Source

pub fn insert(&self, index: usize, pb: ProgressBar) -> ProgressBar

Inserts a progress bar.

The progress bar inserted at position index will have the draw target changed to a remote draw target that is intercepted by the multi progress object overriding custom ProgressDrawTarget settings.

If index >= MultiProgressState::objects.len(), the progress bar is added to the end of the list.

Inserting a progress bar that is already a member of the MultiProgress will have no effect.

Source

pub fn insert_from_back(&self, index: usize, pb: ProgressBar) -> ProgressBar

Inserts a progress bar from the back.

The progress bar inserted at position MultiProgressState::objects.len() - index will have the draw target changed to a remote draw target that is intercepted by the multi progress object overriding custom ProgressDrawTarget settings.

If index >= MultiProgressState::objects.len(), the progress bar is added to the start of the list.

Inserting a progress bar that is already a member of the MultiProgress will have no effect.

Source

pub fn insert_before( &self, before: &ProgressBar, pb: ProgressBar, ) -> ProgressBar

Inserts a progress bar before an existing one.

The progress bar added will have the draw target changed to a remote draw target that is intercepted by the multi progress object overriding custom ProgressDrawTarget settings.

Inserting a progress bar that is already a member of the MultiProgress will have no effect.

Source

pub fn insert_after(&self, after: &ProgressBar, pb: ProgressBar) -> ProgressBar

Inserts a progress bar after an existing one.

The progress bar added will have the draw target changed to a remote draw target that is intercepted by the multi progress object overriding custom ProgressDrawTarget settings.

Inserting a progress bar that is already a member of the MultiProgress will have no effect.

Source

pub fn remove(&self, pb: &ProgressBar)

Removes a progress bar.

The progress bar is removed only if it was previously inserted or added by the methods MultiProgress::insert or MultiProgress::add. If the passed progress bar does not satisfy the condition above, the remove method does nothing.

Source

pub fn println<I: AsRef<str>>(&self, msg: I) -> Result<()>

Print a log line above all progress bars in the MultiProgress

If the draw target is hidden (e.g. when standard output is not a terminal), println() will not do anything.

Source

pub fn suspend<F: FnOnce() -> R, R>(&self, f: F) -> R

Hide all progress bars temporarily, execute f, then redraw the MultiProgress

Executes ‘f’ even if the draw target is hidden.

Useful for external code that writes to the standard output.

Note: The internal lock is held while f is executed. Other threads trying to print anything on the progress bar will be blocked until f finishes. Therefore, it is recommended to avoid long-running operations in f.

Source

pub fn clear(&self) -> Result<()>

Source

pub fn is_hidden(&self) -> bool

Trait Implementations§

Source§

impl Clone for MultiProgress

Source§

fn clone(&self) -> MultiProgress

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultiProgress

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for MultiProgress

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.