Add example for download with progress tracking
This commit is contained in:
parent
092e9fb4cc
commit
fff333f89b
@ -37,6 +37,7 @@ members = [
|
||||
"web",
|
||||
"wgpu",
|
||||
"winit",
|
||||
"examples/download_progress",
|
||||
"examples/bezier_tool",
|
||||
"examples/clock",
|
||||
"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