Add example for download with progress tracking
This commit is contained in:
parent
092e9fb4cc
commit
fff333f89b
@ -37,6 +37,7 @@ members = [
|
|||||||
"web",
|
"web",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
|
"examples/download_progress",
|
||||||
"examples/bezier_tool",
|
"examples/bezier_tool",
|
||||||
"examples/clock",
|
"examples/clock",
|
||||||
"examples/counter",
|
"examples/counter",
|
||||||
|
13
examples/download_progress/Cargo.toml
Normal file
13
examples/download_progress/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "download_progress"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Songtronix <contact@songtronix.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
iced = { path = "../.." }
|
||||||
|
iced_native = { path = "../../native" }
|
||||||
|
iced_futures = { path = "../../futures" }
|
||||||
|
async-std = { version = "1.0", features = ["unstable"] }
|
||||||
|
isahc = "0.9.1"
|
15
examples/download_progress/README.md
Normal file
15
examples/download_progress/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
## Download Progress
|
||||||
|
|
||||||
|
Downloading a file asynchronously with a `Subscription` while displaying the progress with a `ProgressBar`.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://gfycat.com/watchfulwelcomeladybird">
|
||||||
|
<img src="https://gfycat.com/watchfulwelcomeladybird">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
You can run it with `cargo run`:
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo run --package download_progress
|
||||||
|
```
|
99
examples/download_progress/src/downloader.rs
Normal file
99
examples/download_progress/src/downloader.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
use iced_futures::futures;
|
||||||
|
|
||||||
|
// Just a little utility function
|
||||||
|
pub fn file<T: ToString>(url: T) -> iced::Subscription<DownloadMessage> {
|
||||||
|
iced::Subscription::from_recipe(Downloader {
|
||||||
|
url: url.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Downloader {
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure iced can use our download stream
|
||||||
|
impl<H, I> iced_native::subscription::Recipe<H, I> for Downloader
|
||||||
|
where
|
||||||
|
H: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
type Output = DownloadMessage;
|
||||||
|
|
||||||
|
fn hash(&self, state: &mut H) {
|
||||||
|
use std::hash::Hash;
|
||||||
|
std::any::TypeId::of::<Self>().hash(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream(
|
||||||
|
self: Box<Self>,
|
||||||
|
_input: futures::stream::BoxStream<'static, I>,
|
||||||
|
) -> futures::stream::BoxStream<'static, Self::Output> {
|
||||||
|
use isahc::prelude::*;
|
||||||
|
|
||||||
|
Box::pin(futures::stream::unfold(
|
||||||
|
DownloadState::Ready(self.url),
|
||||||
|
|state| async move {
|
||||||
|
match state {
|
||||||
|
DownloadState::Ready(url) => {
|
||||||
|
let resp = Request::get(&url)
|
||||||
|
.metrics(true)
|
||||||
|
.body(())
|
||||||
|
.unwrap()
|
||||||
|
.send_async()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let metrics = resp.metrics().unwrap().clone();
|
||||||
|
// If you actually want to download:
|
||||||
|
/*let file = async_std::fs::File::create("download.bin")
|
||||||
|
.await
|
||||||
|
.unwrap();*/
|
||||||
|
|
||||||
|
async_std::task::spawn(async_std::io::copy(
|
||||||
|
resp.into_body(),
|
||||||
|
async_std::io::sink(), //file
|
||||||
|
));
|
||||||
|
|
||||||
|
Some((
|
||||||
|
DownloadMessage::DownloadStarted,
|
||||||
|
DownloadState::Downloading(metrics),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
DownloadState::Downloading(metrics) => {
|
||||||
|
async_std::task::sleep(
|
||||||
|
std::time::Duration::from_millis(100),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let percentage = metrics.download_progress().0 * 100
|
||||||
|
/ metrics.download_progress().1;
|
||||||
|
|
||||||
|
if percentage == 100 {
|
||||||
|
Some((
|
||||||
|
DownloadMessage::Done,
|
||||||
|
DownloadState::Finished,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Some((
|
||||||
|
DownloadMessage::Downloading(percentage),
|
||||||
|
DownloadState::Downloading(metrics),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DownloadState::Finished => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DownloadMessage {
|
||||||
|
DownloadStarted,
|
||||||
|
Downloading(u64),
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DownloadState {
|
||||||
|
Ready(String),
|
||||||
|
Downloading(isahc::Metrics),
|
||||||
|
Finished,
|
||||||
|
}
|
116
examples/download_progress/src/main.rs
Normal file
116
examples/download_progress/src/main.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
use iced::{
|
||||||
|
button, executor, Align, Application, Button, Column, Command, Container,
|
||||||
|
Element, Length, ProgressBar, Settings, Subscription, Text,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod downloader;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
Downloader::run(Settings::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Downloader {
|
||||||
|
// Whether to start the download or not.
|
||||||
|
enabled: bool,
|
||||||
|
// The current percentage of the download
|
||||||
|
current_progress: u64,
|
||||||
|
|
||||||
|
btn_state: button::State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Message {
|
||||||
|
DownloadUpdate(downloader::DownloadMessage),
|
||||||
|
Interaction(Interaction),
|
||||||
|
}
|
||||||
|
|
||||||
|
// For explanation of why we use an Interaction enum see here:
|
||||||
|
// https://github.com/hecrj/iced/pull/155#issuecomment-573523405
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Interaction {
|
||||||
|
// User pressed the button to start the download
|
||||||
|
StartDownload,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Application for Downloader {
|
||||||
|
type Executor = executor::Default;
|
||||||
|
type Message = Message;
|
||||||
|
|
||||||
|
fn new() -> (Downloader, Command<Message>) {
|
||||||
|
(Downloader::default(), Command::none())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(&self) -> String {
|
||||||
|
String::from("Download Progress - Iced")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
match message {
|
||||||
|
Message::Interaction(action) => match action {
|
||||||
|
Interaction::StartDownload => {
|
||||||
|
self.enabled = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Message::DownloadUpdate(update) => match update {
|
||||||
|
downloader::DownloadMessage::Downloading(percentage) => {
|
||||||
|
self.current_progress = percentage;
|
||||||
|
}
|
||||||
|
downloader::DownloadMessage::Done => {
|
||||||
|
self.current_progress = 100;
|
||||||
|
self.enabled = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
|
if self.enabled {
|
||||||
|
downloader::file("https://speed.hetzner.de/100MB.bin")
|
||||||
|
.map(Message::DownloadUpdate)
|
||||||
|
} else {
|
||||||
|
Subscription::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&mut self) -> Element<Message> {
|
||||||
|
// Construct widgets
|
||||||
|
|
||||||
|
let toggle_text = match self.enabled {
|
||||||
|
true => "Downloading...",
|
||||||
|
false => "Start the download!",
|
||||||
|
};
|
||||||
|
|
||||||
|
let toggle: Element<Interaction> =
|
||||||
|
Button::new(&mut self.btn_state, Text::new(toggle_text))
|
||||||
|
.on_press(Interaction::StartDownload)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let progress_bar =
|
||||||
|
ProgressBar::new(0.0..=100.0, self.current_progress as f32);
|
||||||
|
|
||||||
|
let progress_text = &match self.enabled {
|
||||||
|
true => format!("Downloading {}%", self.current_progress),
|
||||||
|
false => "Ready to rock!".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct layout
|
||||||
|
let content = Column::new()
|
||||||
|
.align_items(Align::Center)
|
||||||
|
.spacing(20)
|
||||||
|
.padding(20)
|
||||||
|
.push(Text::new(progress_text))
|
||||||
|
.push(progress_bar)
|
||||||
|
.push(toggle.map(Message::Interaction));
|
||||||
|
|
||||||
|
Container::new(content)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.center_x()
|
||||||
|
.center_y()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user