Open MoGuGuai-hzr opened 1 year ago
I don't have an opinion off the top of my head. Just try to implement it?
I don't have an opinion off the top of my head. Just try to implement it?
Sorry~, perhaps I didn't express myself well. Using MultiProgress
, it seems that I need to reset each sub-progress bar line by line in order to achieve the scrolling effect. And in order to pass the new message to each sub-progress bar, I need to save the previous message and manually clone a copy for them.
Is there a more elegant way to achieve this scrolling effect? It's like every time I use println
, the information in the terminal automatically moves up.
This may not be feasible, as you know, I lack knowledge of terminal display.
What you could do to achieve that effect is, for keeping track of the message, use VecDeque
, where you:
log_lines.push_back(line)
whenever you receive a new line of log message.log_lines.pop_front()
when it exceeds the maximum number of lines to scroll.Use let = log_lines.join("\n")
(from Join::join
to create the message to set on the progress bar, which means the progress bar template will have a \n
in it (to put the message below the bar).
ProgressBar::println
places the message above the progress bar, not below, so that would also mean a change to indicatif
if "below the progress bar" printing is to be supported.
Note that for 1., it would mean either:
\n
in them)vec_deque
I can't think of a way to avoid allocations for the message when setting it on the progress bar with the current API.
I did something kind off similar on a project, that's not open-sourced so I can't share the code, I had a MultiProgress
, and I added 2 ProgressBars
that I used for reference, and had to juggle insert_after
and insert_before
.
What you could do to achieve that effect is, for keeping track of the message, use
VecDeque
, where you:1. [`log_lines.push_back(line)`](https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.push_back) whenever you receive a new line of log message. 2. [`log_lines.pop_front()`](https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.pop_front) when it exceeds the maximum number of lines to scroll. 3. Use `let = log_lines.join("\n")` (from [`Join::join`](https://doc.rust-lang.org/std/slice/trait.Join.html#tymethod.join) to create the message to set on the progress bar, which means the progress bar template will have a `\n` in it (to put the message below the bar). [`ProgressBar::println`](https://docs.rs/indicatif/latest/indicatif/struct.ProgressBar.html#method.println) places the message above the progress bar, not below, so that would also mean a change to `indicatif` if "below the progress bar" printing is to be supported.
Note that for 1., it would mean either:
* all log lines must be one line (no `\n` in them) * splitting log messages into lines before adding them to the `vec_deque`
I can't think of a way to avoid allocations for the message when setting it on the progress bar with the current API.
I apologize for the delayed response as it appears my email forgot about me.
As for what was previously mentioned, I implemented the function using an unsightly piece of code as follows.
use std::{thread, time};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
const N: usize = 3;
struct MyMultiProgress {
m: MultiProgress,
main: ProgressBar,
pbs: Vec<ProgressBar>,
msgs: Vec<String>,
cnt: usize,
}
impl MyMultiProgress {
fn new(len: u64) -> Self {
let spinner_style = ProgressStyle::with_template("{wide_msg}").unwrap();
let m = MultiProgress::new();
let mut pbs = Vec::with_capacity(N);
for _ in 0..N {
let pb = m.add(ProgressBar::new(0));
pb.set_style(spinner_style.clone());
pbs.push(pb);
}
let main = m.add(ProgressBar::new(len));
Self {
m,
main,
pbs,
msgs: Vec::with_capacity(N),
cnt: 0,
}
}
fn add_msg(&mut self, msg: String) {
if self.msgs.len() < N {
self.msgs.push(msg);
} else {
self.msgs[self.cnt % N] = msg;
}
self.cnt += 1;
self.show();
}
fn show(&mut self) {
if self.msgs.len() < N {
for i in 0..self.msgs.len() {
self.pbs[i].set_message(self.msgs[i].clone());
}
} else {
for i in 0..N {
self.pbs[i].set_message(self.msgs[(self.cnt + i) % N].clone());
}
}
}
fn inc(&mut self, n: u64) {
self.main.inc(n);
}
fn clean(&mut self) {
for pb in &mut self.pbs {
pb.finish();
}
self.main.finish();
self.m.clear().unwrap();
}
}
#[test]
fn multiple_bar() {
let interval = time::Duration::from_millis(1000);
let mut m = MyMultiProgress::new(100);
for i in 0..100 {
m.add_msg(format!("hello: {}", i));
m.inc(1);
thread::sleep(interval);
}
m.clean();
}
However, besides the inconvenience of having to copy strings every time, there are some unexpected issues, such as messages not immediately displaying on the terminal every time they are set.
Specifically, if the interval is set to 1000ms, it works perfectly.
However, if it is set to 100ms or lower, there may be some overlapping.
I did something kind off similar on a project, that's not open-sourced so I can't share the code, I had a
MultiProgress
, and I added 2ProgressBars
that I used for reference, and had to juggleinsert_after
andinsert_before
.
Does this resemble what I showed above?
heya, I'm not sure why the numbers overlap
are you using steady_tick
?
(can't see it in the code above, but just in case)
if you are, try not using it, and calling .tick()
inside the show()
method after updating the progress bars
otherwise it needs some investigation into indicatif
's inner state and rendering, which I won't be able to get to today
I did not use steady_tick
, I simply put it to sleep
.
I try to use .tick()
for each progress bar after set_message
, but there was no noticeable improvement.
This probably happens because of indicatif's drawing rate limits.
I am using Docker and I find its progress bar very cool. Can Indicatif implement this feature?
Maybe I can use
MultiProgress
to achieve it: use one as the main progress bar and the others as secondary progress bars which only displaying personalized messages.However, to implement the scrolling function, I need to continuously scroll the message of the secondary progress bar, so
clone
the message every time. Is this a good implementation approach?