1

我正在尝试使用 Rust Gtk 创建类似Granite.AsyncImage的小部件,但我不知道如何使其工作,因为 Pixbuff 不是安全线程。

我有一个类似下一个的代码:

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!(),
            }
        });
4

1 回答 1

0

您可以安全地在线程之间发送 glib::Bytes。从 glib::Bytes 在主 gtk 线程中创建一个 Pixbuf。我这样做是这样的:

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--

应用程序.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--

在 gtk 主线程和其他线程之间发送数据的另一种方法是使用 MainContext::channel。但是你必须再次发送 glib::Bytes。alexislozano有一个很好的例子:gtk-rs-channels

顺便说一句,我正在使用 gtk4-rs 但它必须是相似的

于 2021-10-06T07:10:00.967 回答