My question feels simple, and maybe the solution is simple also but I am unsure. Basically I have a function that takes a long time to run, lets say around 10 seconds. For the example I will just do a long for loop to demonstrate this. I have a simple iced application window running with a button that when pressed, will run this long function. What I would like to have is also a progress_bar.
pub fn main() -> iced::Result {
iced::run("Test", update, view)
}
fn update(progress: &mut f32, message: Message) {
match message {
Message::Run => {
for i in 0..=1_000_000_000 {}
// no state updates here, in
// the real program this writes a file and then exits
},
Message::UpdateProgress(value) => progress = value,
}
}
fn view(progress: &f32) -> Element<Message> {
column![
progress_bar(0.0..=100.0, progress)
button("Run").on_press(Message::Run)
].into()
}
The setup is similar to above, the question is how can I run Message::UpdateProgress every 10% for example. I have tried
And have really gotten nowhere. I am not sure if I need to be using futures, if I need to spawn another thread (which I have also tried) and sending messages. Really not sure and the documentation on iced isn't very descriptive for someone who is newer (not completely new) to rust.
For anyone wondering, I came up with a basic solution.
We need a function with this signature fn() -> impl Stream<Item = Result<f32, ()>> that will stream us data (in this case 0 to 100) back and we can handle it with a closure.
fn run_loop() -> impl Stream<Item = Result<f32, ()>> {
// try_channel: Creates a new Stream that produces the items sent from a Future
// that can fail to the mpsc::Sender provided to the closure.
try_channel(1, move |mut o| async move {
let t: Vec<i32> = vec![0; 1000];
let mut q: std::slice::Iter<'_, i32> = t.iter();
let mut iter: i32 = 0;
while let Some(_) = q.next() {
// were looping 1000 times waiting 100ms between each to simulate load
iter += 1;
let _ = o.send((iter as f32 / 1000.0 ) * 100.0 as f32).await;
thread::sleep(Duration::from_millis(100));
}
let _ = o.send(100.0).await;
Ok(())
})
}
In the real version obviously this will be adapted to a real function that takes around 10s to run but I think this can help anyone trying to wrap their head around iced futures / streams
It is always jarring to see std::thread::sleep (assuming the reference is from std) used in an async block or fn. This will block the native async executor (tokio or whatever you're using). This should be replaced with the async equivalent, like:
Blocking for 100ms may not be noticeable in this case, especially if the download/progress is modal, preventing other user interactions. But it will get much worse when more sleeps are stacked up from multiple downloads or other background tasks.
Thanks for the answer, luckily the sleep is only there for examples sake. I will .await it from now on as this makes more sense when you are already inside a async block. Thanks again!