Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If the number of progress bar exceed terminal height, newlines appears instead of clearing itself. #496

Closed
pombadev opened this issue Dec 3, 2022 · 3 comments · Fixed by #563
Assignees

Comments

@pombadev
Copy link

pombadev commented Dec 3, 2022

Similar to #144 , if progress bars exceeds terminal's height i.e if there are more progress bars than it is able fit in visible at once, newlines are created.
Fix provided in #144 doesn't help either.

Reproduction steps:

indicatif = "0.17.2"
use std::thread;
use std::time::Duration;

use indicatif::{MultiProgress, ProgressBar, ProgressStyle};

fn main() {
    let m = MultiProgress::new();
    let sty = ProgressStyle::with_template(
        "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
    )
    .unwrap()
    .progress_chars("##-");

    m.println("starting!").unwrap();

    let mut h = vec![];

    for _ in 0..50 {
        let pb = m.add(ProgressBar::new(128));
        pb.set_style(sty.clone());

        let m_clone = m.clone();
        let h1 = thread::spawn(move || {
            for i in 0..128 {
                pb.set_message(format!("item #{}", i + 1));
                pb.inc(1);
                thread::sleep(Duration::from_millis(50));
            }
            m_clone.println("pb1 is done!").unwrap();
            pb.finish_with_message("done");
        });

        h.push(h1);
    }

    for k in h {
        k.join().unwrap();
    }

    // m.clear().unwrap();
}

Use this code and run it

  • in a terminal with height tall enough that it is able to display all progress bars at once, no issues.
  • in a terminal with reduced height so that all progress bars are not visible at once, newlines occurs.
Screencast.from.2022-12-02.23-33-24.webm
@djc
Copy link
Member

djc commented Dec 12, 2022

@chris-laplante any ideas?

@chris-laplante
Copy link
Collaborator

@chris-laplante any ideas?

Yes, I think we can handle this like how tqdm handles it: tqdm/tqdm#918. Basically, just like how we autodetect terminal width we should also look at terminal height and only try to draw as many progress bars as will fit on the screen.

@chris-laplante chris-laplante self-assigned this Dec 12, 2022
@peschu123
Copy link

peschu123 commented Jan 5, 2023

I was playing around with async and I think I have the same problem with the code below.

It looks quite a bit more weird (in the error case). Also I use multiple progress bars for tasks and one main progress bar at the bottom to track overall progress.

Just change the "ITEMS" variable to a lower or higher value.

use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use rand::prelude::*;
use std::convert::TryInto;
use tokio::task::JoinSet;
use tokio::time::{sleep, Duration};
use uuid::Uuid;

#[tokio::main]
async fn main() {
    const ITEMS: usize = 30;
    let uuids: Vec<(Uuid, usize)> = (0..ITEMS)
        .into_iter()
        // generate new uuid and an index
        .map(|x| (Uuid::new_v4(), x))
        .collect();

    let multi_pb = MultiProgress::new();
    let items_u64: u64 = ITEMS.try_into().unwrap();
    let main_pb = multi_pb.add(ProgressBar::new(items_u64));
    main_pb.set_style(
        ProgressStyle::with_template(
            "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
        )
        .unwrap()
        .progress_chars("##-"),
    );

    main_pb.set_message("total  ");
    // Make the main progress bar render immediately rather than waiting for the
    // first task to finish.
    main_pb.tick();

    let mut set = JoinSet::new();
    for (uuid, index) in uuids {
        let steps = 100;
        let task_pb = multi_pb.insert_before(&main_pb, ProgressBar::new(steps));
        task_pb.set_style(
            ProgressStyle::with_template(
                "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
            )
            .unwrap()
            .progress_chars("##-"),
        );
        set.spawn(do_stuff(uuid, index, steps, task_pb));
    }

    let mut seen = [false; ITEMS];
    while let Some(res) = set.join_next().await {
        let idx = res.unwrap();
        seen[idx] = true;
        main_pb.inc(1);
    }
    main_pb.finish();
}

async fn do_stuff(uuid: Uuid, index: usize, steps: u64, task_pb: ProgressBar) -> usize {
    let msg = format!("app #{} with id:{}", index, uuid);
    task_pb.set_message(msg.clone());

    // calculate "tick size" for progress bar to use with sleep (millisecs / steps)
    let num = rand::thread_rng().gen_range(steps..=10000);
    let tick = num / steps;

    for _ in 0..steps {
        sleep(Duration::from_millis(tick)).await;
        task_pb.inc(1);
    }
    let msg = format!("DONE {}", &msg);
    task_pb.finish_with_message(msg);
    index
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants