Wouter
Wouter

Reputation: 2653

How to set the size of a GtkImage

I'm downloading an image from a web page, the image is too big (600px largest edge normally), and I want to scale it down to fit in a 220x220px box.

The code as I have it works - except for the final size. The image is downloaded, and then put in the GtkImage (it's from a Glade layout). I'm downloading it into a temporary file as I can't seem to directly transfer the data from the web site into the image. Now the problem is this image is way too big when displayed in the application.

f = tempfile.NamedTemporaryFile()
try:
    res = urllib2.urlopen('http://hikingtours.hk/images/meetingpoint_%s.jpg'% (self.tours[tourid]['id'], ))
    f.write(res.read())

    # This f.read() call is necassary, without it, the image
    # can not be set properly.
    f.read()
    self.edit_tour_meetingpoint_image.set_from_file(f.name)
    self.edit_tour_meetingpoint_image.show()

except:
    raise

f.close()

On a side note, I'd love to get rid of that temporary file construction :)

Please note, I'm using GTK3.

Upvotes: 2

Views: 4855

Answers (2)

Simon Feltman
Simon Feltman

Reputation: 1116

Use Gdk.Pixbuf.new_from_file_at_scale() along with Gtk.Image.set_from_pixbuf():

pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(f.name, width=220, height=220,
                                                 preserve_aspect_ratio=False)
self.edit_tour_meetingpoint_image.set_from_pixbuf(pixbuf)

If you want to preserve the aspect, just set that argument to True or use: GdkPixbuf.Pixbuf.new_from_file_at_size(f.name, width=220, height=220)

Side note: The reason you need to call read() before using the file is because it is buffered and hasn't been written to disk. The read call is causing the buffer to flush, a clearer technique (from a readability standpoint) would be to call flush() instead of read().

If you want to get rid of the temporary file, use the Gio module along with a streaming pixbuf:

from gi.repository import Gtk, GdkPixbuf, Gio

file = Gio.File.new_for_uri('http://www.gnome.org/wp-content/themes/gnome-grass/images/gnome-logo.png')
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(file.read(cancellable=None),
                                                   width=220, height=220,
                                                   preserve_aspect_ratio=False,
                                                   cancellable=None)
self.edit_tour_meetingpoint_image.set_from_pixbuf(pixbuf)

You can take things further by using an async image stream which then injects the completed results into the app when the pixbuf is ready, maintaining interactivity in the UI during the file transfer:

from gi.repository import Gtk, GdkPixbuf, Gio

# standin image until our remote image is loaded, this can also be set in Glade
image = Gtk.Image.new_from_icon_name('image-missing', Gtk.IconSize.DIALOG)

def on_image_loaded(source, async_res, user_data):
    pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(async_res)
    image.set_from_pixbuf(pixbuf)

file = Gio.File.new_for_uri('http://www.gnome.org/wp-content/themes/gnome-grass/images/gnome-logo.png')
GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(file.read(cancellable=None),
                                                220, 220, # width and height
                                                False,    # preserve_aspect_ratio
                                                None,     # cancellable
                                                on_image_loaded, # callback,
                                                None)     # user_data

Note we cannot use the nice keyword arguments in the async version because of the user_data arg. This goes away in pygobject 3.12 where the user_data can actually be left off if not used (or used as a keyword arg too).

Upvotes: 4

Rachel Gallen
Rachel Gallen

Reputation: 28553

Use pixbuf (part of gtk)

def scale(dest, dest_x, dest_y, dest_width, dest_height, offset_x, offset_y, scale_x, scale_y, interp_type)

or for simple scale

gtk.gdk.Pixbuf.scale_simple

def scale_simple(dest_width, dest_height, interp_type)

You can still use Pixbuf in gtk3 by importing the gdk but you need to import cairo

import cairo
import Image
import array
from gi.repository import Gtk, GdkPixbuf

width = 25
height = 25
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size('logo.png', width, height)
pil_image = Image.fromstring('RGBA', (width, height), pixbuf.get_pixels())
byte_array = array.array('B', pil_image.tostring())
cairo_surface = cairo.ImageSurface.create_for_data(byte_array, cairo.FORMAT_ARGB32, width, height, width * 4)

Code Sample Reference

Upvotes: 0

Related Questions