Open Daniel-Xu opened 11 months ago
Hi Daniel,
You should do the Select
prompt within a ProgressBar::suspend
. See: https://docs.rs/indicatif/latest/indicatif/struct.ProgressBar.html#method.suspend
Hi Daniel,
You should do the
Select
prompt within aProgressBar::suspend
. See: https://docs.rs/indicatif/latest/indicatif/struct.ProgressBar.html#method.suspend
Hi, Chris:
Thanks for the tips.
I tried suspend
, unfortunately it wil introduce happens-after relationship with the Select
.
If I don't choose an option, the PBar will never show with suspend
let ans = pb
.suspend(|| Select::new("choose a song again: ", songs_info.clone()).prompt());
Are you able to share the code so I can give it a try myself? Or at least the UI code to reproduce the problem.
Are you able to share the code so I can give it a try myself? Or at least the UI code to reproduce the problem. here is a runnable demo, what I want to do is displaying the pbar all the time, but the following code won't work.
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use inquire::Select;
fn main() {
let pb = Arc::new(ProgressBar::new_spinner());
pb.set_style(
ProgressStyle::with_template("{spinner:.blue} {msg}")
.unwrap()
// For more spinners check out the cli-spinners project:
// https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json
.tick_strings(&[
"▹▹▹▹▹",
"▸▹▹▹▹",
"▹▸▹▹▹",
"▹▹▸▹▹",
"▹▹▹▸▹",
"▹▹▹▹▸",
"▪▪▪▪▪",
]),
);
// pb.enable_steady_tick(Duration::from_millis(120));
pb.set_message("Playing Connecting...");
let pb_clone = pb.clone();
let h1 = thread::spawn(move || {
for i in 0..128 {
thread::sleep(Duration::from_millis(15));
pb.set_message(format!("item #{}", i + 1));
pb.inc(1);
}
pb.finish_with_message("done");
});
let songs_info = vec!["a", "b", "c"];
let ans = pb_clone.suspend(|| Select::new("choose a song: ", songs_info).prompt());
match ans {
Ok(choice) => {
pb_clone.suspend(|| {
println!("{choice}! Enjoy!");
});
}
Err(_) => {}
}
let _ = h1.join();
}
I guess I have to change my workflow:
The code as written seems to work OK for me - what different behavior are you expecting?
Also one thing that I notice is you don't need to put ProgressBar
in an Arc
. ProgressBar
is already an Arc
around its internal state.
The code as written seems to work OK for me - what different behavior are you expecting?
Also one thing that I notice is you don't need to put
ProgressBar
in anArc
.ProgressBar
is already anArc
around its internal state.
I want the progress bar and Select menu to show on the screen at the same time. Imagine you can select new songs while listening to the current one.
thanks for the Arc reminder.
@chris-laplante hi, another unrelated question: how to reset the front color's position to 0? I've tried bar.set_position(0), it doesn't work.
@chris-laplante hi, another unrelated question: how to reset the front color's position to 0? I've tried bar.set_position(0), it doesn't work.
reset or set_position won't work properly in multiple threads. If so I don't think the progressbar should be Sync.
I want the progress bar and Select menu to show on the screen at the same time. Imagine you can select new songs while listening to the current one.
Ah, gotcha. For the progress bar to be running at the same time as the user is making a selection, we'd need the ProgressBar
and the Select
to coordinate somehow. (Since to redraw the progress bar at each step, we'd first need to clear the screen of the Select
). That's certainly not implemented for a '3rd party' crate like inquire
, and I'm 99% sure it's also not implemented for dialoguer
which is a similar crate in our family: https://docs.rs/dialoguer/latest/dialoguer/.
It wouldn't be impossible to implement the coordination between indicatif and dialoguer, but IMHO it is a bit outside the scope of indicatif currently. @djc what do you think?
You might want to consider using a curses-based crate: https://crates.io/keywords/curses
@chris-laplante hi, another unrelated question: how to reset the front color's position to 0? I've tried bar.set_position(0), it doesn't work.
reset or set_position won't work properly in multiple threads. If so I don't think the progressbar should be Sync.
Can you show multi-threaded code you are trying? It should work fine from multiple threads.
It wouldn't be impossible to implement the coordination between indicatif and dialoguer, but IMHO it is a bit outside the scope of indicatif currently. @djc what do you think?
Yeah, I'm personally not motivated to spend time on that.
@chris-laplante hi, another unrelated question: how to reset the front color's position to 0? I've tried bar.set_position(0), it doesn't work.
reset or set_position won't work properly in multiple threads. If so I don't think the progressbar should be Sync.
Can you show multi-threaded code you are trying? It should work fine from multiple threads.
use async_trait::async_trait;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use inquire::{InquireError, Select};
use indicatif::{HumanDuration, ProgressBar};
async fn tick(p: ProgressBar) {
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop {
p.inc(50);
interval.tick().await;
}
}
use crossbeam::channel::{self, Receiver, Sender};
pub struct Player {
//send message to sink thread
sender: Sender<PlayerMessage>,
// receive message from sink
pub receiver: Receiver<SinkMessage>,
}
enum PlayerMessage {
Play { listen_url: String },
}
#[derive(Debug)]
pub enum SinkMessage {
Initialized,
Playing,
Done,
}
use anyhow::{anyhow, Context, Result};
impl Player {
/// Creating a `Player` might be time consuming. It might take several seconds on first run.
pub fn try_new() -> Result<Self> {
let (sender, receiver) = channel::unbounded();
let (sink_sender, sink_receiver) = channel::unbounded();
thread::spawn(move || loop {
while let Ok(message) = receiver.recv() {
match message {
PlayerMessage::Play { listen_url } => {
break;
}
}
}
sink_sender.send(SinkMessage::Initialized).unwrap();
});
Ok(Self {
sender,
receiver: sink_receiver,
})
}
pub fn play(&self, listen_url: &str) {
self.sender
.send(PlayerMessage::Play {
listen_url: listen_url.to_owned(),
})
.unwrap();
}
}
fn process_sink_message(player: Arc<Player>, pb: ProgressBar) {
while let Ok(msg) = player.receiver.recv() {
match msg {
SinkMessage::Initialized => {
pb.suspend(|| {
println!("initialized 1");
});
pb.reset();
}
SinkMessage::Playing => {}
SinkMessage::Done => pb.suspend(|| {
println!("done");
}),
}
}
}
#[tokio::main]
async fn main() {
let pb = ProgressBar::new(1024);
let player = Arc::new(Player::try_new().unwrap());
let pb_clone = pb.clone();
tokio::spawn(tick(pb_clone));
let playerd = player.clone();
let pb_cloned = pb.clone();
let notification_handle = thread::spawn(move || process_sink_message(playerd, pb_cloned));
let ans = pb.suspend(|| Select::new("choose a song: ", vec!["ab", "b", "c"]).prompt());
match ans {
Ok(choice) => {
pb.suspend(|| {
println!("hello world choose {}", choice);
});
pb.set_message(HumanDuration(Duration::from_secs(1000)).to_string());
}
Err(_) => {
std::process::exit(0);
}
}
player.play("abc");
notification_handle.join().unwrap();
}
this will reproduce the issue:
initialized
message is printed, so the pb.reset()
will be executed. However, the progress is not reset as expected. this will reproduce the issue:
- when you are at the Select menu, wait for a few seconds (let the tick run a few seconds)
- you will see that the
initialized
message is printed, so thepb.reset()
will be executed. However, the progress is not reset as expected.
I can reproduce that, and it's weird. I'll look into it later this week (can't do it right now unfortunately).
Hi, I have a simple program that the user can pick a song, play it and go back to pick song again.
The initial state: I'm using inquire's select:
When the user finishes picking the song, the loop goes back to the
Select
again.At the same time, there's another thread that will send back
playing
state via channel to the main thread.what I want to achieve is that I want to show the progressing bar below the select options. However, the bar kept clashing with the first line of Select UI. (
the original line is: "? choose a song:", and now is " >>>> Connecting..."
)I've tried to set the target to
stdout
, it doesn't work either.Any ideas?