Search code examples
pythonimageuser-interfacegtkpygtk

pyGtk: Image prevents window resize


So I am attempting to make a GTK application using python, and I have run into this issue where after I place an image on a window, I can increase the size of the window, but not decrease it. Given that the purpose of this particular window is to display a resizable image, this is rather bothersome.

I have extracted the relevant code which demonstrates this behavior below

#!/usr/bin/env python
from gi.repository import Gtk, GdkPixbuf
import sys

class ImageWindow(Gtk.Window):
    def __init__(self, image_data):
        Gtk.Window.__init__(self, title="image test")
        if image_data and len(image_data) > 0:
            self.loader = GdkPixbuf.PixbufLoader()
            self.loader.write(image_data)
            self.pixbuf = self.loader.get_pixbuf()
            self.image = Gtk.Image.new_from_pixbuf(self.pixbuf)
        else:
            self.image = Gtk.Image.new()
        self.add(self.image)
        self.connect('delete-event', Gtk.main_quit)

win = ImageWindow(sys.stdin.read())
win.show_all()
Gtk.main()

If you pipe in nothing, the window resizes fine. Pipe in an image, and the form clicks to the size of the image, and can resize bigger, but cannot resize smaller.


Solution

  • So here is an example of a scaling image. The idea is that you put the image in a Gtk.ScrolledWindow() and resize the image as soon as the window is resized.:

    #!/usr/bin/env python
    from gi.repository import Gtk, GdkPixbuf, GLib
    import sys
    
    class ImageWindow(Gtk.Window):
        def __init__(self, image_data):
            Gtk.Window.__init__(self, title="image test")
            self.connect('delete-event', Gtk.main_quit)
    
            self.image = Gtk.Image()
            scrolled_window = Gtk.ScrolledWindow()
            scrolled_window.add(self.image)
            self.add(scrolled_window)
    
            if len(image_data) == 0:
                return
    
            self.loader = GdkPixbuf.PixbufLoader()
            self.loader.write(image_data)
            self.loader.close()
            self.pixbuf = self.loader.get_pixbuf()
            self.image.set_from_pixbuf(self.pixbuf)
    
            width = self.pixbuf.get_width()
            height = self.pixbuf.get_height()
            self.dimension = float(width) / height
            self.set_default_size(width, height)
            self.connect('check-resize', self.on_resize)
    
        def on_resize(self, window):
            width, height = self.get_size()
            if float(width) / height > self.dimension:
                self.pixbuf = self.pixbuf.scale_simple(
                    self.dimension * height,
                    height,
                    GdkPixbuf.InterpType.NEAREST
                )
            else:
                self.pixbuf = self.pixbuf.scale_simple(
                    width,
                    width / self.dimension,
                    GdkPixbuf.InterpType.NEAREST
                )
            GLib.idle_add(self.image.set_from_pixbuf, self.pixbuf)
    
    win = ImageWindow(sys.stdin.read())
    win.show_all()
    Gtk.main()
    

    As an alternative, you can load the pixbuf again from the loader and scale it afterwards. This looks better if you make your image smaller and then larger again but needs more processing:

    def on_resize(self, window):
        width, height = self.get_size()
        self.pixbuf = self.loader.get_pixbuf()
        if float(width) / height > self.dimension:
            self.pixbuf = self.pixbuf.scale_simple(
                self.dimension * height,
                height,
                GdkPixbuf.InterpType.BILINEAR
            )
        else:
            self.pixbuf = self.pixbuf.scale_simple(
                width,
                width / self.dimension,
                GdkPixbuf.InterpType.BILINEAR
            )
        GLib.idle_add(self.image.set_from_pixbuf, self.pixbuf)