2

I'm trying to create a similar widget like Granite.AsyncImage using Rust Gtk, but I can't figure how to make it work because Pixbuff is not safe thread.

I have a code like next:

let file = gio::File::for_uri("https://e00-elmundo.uecdn.es/assets/multimedia/imagenes/2020/12/14/16079475000780.jpg");

file.read_async(
        glib::Priority::default(),
        Some(&gio::Cancellable::new()),
        |s| {
            match s {
                Ok(stream) => {
                    Pixbuf::from_stream_at_scale_async(
                        &stream,
                        200,
                        200,
                        true,
                        Some(&gio::Cancellable::new()),
                        |r| {
                            match r {
                                Ok(pix) => {
                                    // How can I send this pixbuff to a Gtk Image on main thread?
                                }
                                Err(_) => {}
                            }
                        });
                }
                Err(_) => todo!(),
            }
        });
Carlos Lopez
  • 133
  • 4

1 Answers1

1

You can send glib::Bytes between threads safely. Create a Pixbuf in main gtk thread from glib::Bytes. I'm doing it like this:

api.rs

// --snip--
use gtk::glib::Bytes;
use reqwest::Error;
pub async fn get_manga_thumbnail(manga_id: u16) -> Result<Option<Bytes>, Error> {
    let response = reqwest::get(format!("{}/api/v1/manga/{}/thumbnail", SERVER, manga_id))
        .await?
        .bytes() // converting to bytes::Bytes
        .await?;
 
    Ok(Some(Bytes::from_owned(response))) // converting from bytes::Bytes to glib::Bytes 
// --snip--
}

main.rs

// --snip--
// https://tokio.rs/tokio/topics/bridging
use tokio::runtime::{Builder as RuntimeBuilder, Runtime}
pub static RUNTIME: SyncLazy<Runtime> = SyncLazy::new(|| {
    RuntimeBuilder::new_multi_thread()
        .worker_threads(1)
        .enable_all()
        .build()
        .unwrap()
});
// --snip--

application.rs

// --snip--
    let handle = RUNTIME.spawn(api::get_manga_thumbnail(manga.id))
    // block_on(handle) works like await.
    let bytes = RUNTIME.block_on(handle).unwrap().unwrap().unwrap(); 
    let stream = gio::MemoryInputStream::from_bytes(&bytes);
    let pixbuf = Pixbuf::from_stream(&stream, Some(&Cancellable::new())).unwrap();
    let image = Image::builder().build();
    image.set_from_pixbuf(Some(&pixbuf));
// --snip--

another way to send data between gtk main thread and other threads is using MainContext::channel. but again you have to send glib::Bytes. alexislozano has a greate example for it: gtk-rs-channels

btw I'm using gtk4-rs but it must be similar

Mahor1221
  • 11
  • 2