Gtk-rs: Creating a button with an image downloaded from a URL

I'd like to create a button that shows an image downloaded from a URL but I'm at a loss.
A Playground link isn't possible because it lacks the required crates.

This is what I tried:

            let button = Button::new();
            let result = reqwest::get("https://picsum.photos/200").await.unwrap();
            let bytes = result.bytes().await.unwrap().to_vec();
            let pixbuf = Pixbuf::from_inline(&bytes, true).unwrap();
            let image = Image::from_pixbuf(Some(&pixbuf));
            button.set_image(Some(&image));

But that fails with thread 'main' panicked at 'called Result::unwrap()on anErr value: Error { domain: gdk-pixbuf-error-quark, code: 0, message: "Image header corrupt" }', src/main.rs:52:60

I also tried a variant like this

            let result = reqwest::get("https://picsum.photos/200").await.unwrap();
            let bytes = result.bytes().await.unwrap().to_vec();
            let bytes = glib::Bytes::from(&bytes.to_vec());
            let pixbuf = Pixbuf::from_bytes(&bytes, Colorspace::Rgb, false, 8, 200, 200, 3 * 200);
            let image = Image::from_pixbuf(Some(&pixbuf));

And that fails with
GdkPixbuf-CRITICAL **: 09:26:11.165: gdk_pixbuf_new_from_bytes: assertion 'g_bytes_get_size (data) >= width * height * (has_alpha ? 4 : 3)' failed thread 'main' panicked at 'assertion failed: ::glib::types::instance_of::<Self>(ptr as *const _)',

Unfortunately, there's almost zero documentation for any of these crates.
Does anyone have an idea on how to achieve my goal?
I'm not tied to reqwest

Thank you

Can you try writing the image to a file to see if you can open it with an image viewer?

Thanks for the suggestion.
I did, yes.

        let result = reqwest::get("https://picsum.photos/200").await.unwrap();
        let mut file = File::create("pic.jpg").unwrap();
        std::io::copy(&mut result.bytes().await.unwrap().as_ref(), &mut file);

This leads to a file I can open in a Browser and in Pinta which is an image editor also based on Gtk and Pixbuf.

What I tried now also works:

        let pixbuf = Pixbuf::from_file("pic.jpg").unwrap();
        let image = Image::from_pixbuf(Some(&pixbuf));

So the problem must be somewhere else. I must be using Gtk the wrong way somehow. I'd also love to use streams but I haven't found a way to convert a Stream to an InputStream....

Can you dump the headers? It might be a non-200 response, a redirect or similar that the browser handles automatically but reqwest doesn't. IIRC reqwest should handle redirects but dumping the response would clarify what's happening.

@qaopm That's what I also thought but when I write the response to a file it is all correct so that doesn't seem to be the problem. Unless I'm misunderstanding you.

If you use reqwest to save it to the file then it's fine. If you're using a browser then it'd be worth double-checking that reqwest is actually providing the data. Dumping headers, length of the binary vector, stuff like that.

In general it's also worth checking the HTTP status code, it can easily happen that you get occasional non-200 code because of e.g. throttling.

Unfortunately, there's almost zero documentation for any of these crates.

That's sadly true. But, if you click the src button near the function, you can see these functions are just wrappers over GDK-PixBuf C API, which is documented. Check out:

Looks like all these three methods expect different file formats – from_bytes expect to read raw array of values, from_inline expects some internal GDK-resource format, so only from_file is able to understand usual image formats like png or jpg.

If you want to avoid the need to create a temporary file, by best guess would be to try one of the from_stream functions, as gdk_pixbuf_new_from_stream also documents that The file format is detected automatically.

@krdln That is fantastic, thank you.

I have no idea why I didn't find that yesterday.
I'll se ewhat I can come up with.

With more help from the Gnome folks I got this to work:

            let button = Button::new();
            let result = reqwest::get("https://picsum.photos/200").await.unwrap();
            let bytes = result.bytes().await.unwrap().to_vec();
            let bytes = glib::Bytes::from(&bytes.to_vec());
            let stream = MemoryInputStream::from_bytes(&bytes);
            let pixbuf = Pixbuf::from_stream(&stream, NONE_CANCELLABLE).unwrap();
            let image = Image::from_pixbuf(Some(&pixbuf));
            button.set_image(Some(&image));

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.