使用(Py)GTK在调整大小时自动缩放图像

6 投票
3 回答
9087 浏览
提问于 2025-04-16 11:29

我在一个可以调整大小的窗口里有一个 GtkImage 控件,还有一个 GdkPixBuf 的引用,用来存储我想要填充到 GtkImage 里的图片。

我可以用以下方法来调整 GdkPixBuf 的大小,以填满 GtkImage 控件:

def update_image(self, widget=None, data=None):
    # Get the size of the source pixmap
    src_width, src_height = self.current_image.get_width(), self.current_image.get_height()

    # Get the size of the widget area
    widget = self.builder.get_object('image')
    allocation = widget.get_allocation()
    dst_width, dst_height = allocation.width, allocation.height

    # Scale preserving ratio
    scale = min(float(dst_width)/src_width, float(dst_height)/src_height)
    new_width = int(scale*src_width)
    new_height = int(scale*src_height)
    pixbuf = self.current_image.scale_simple(new_width, new_height, gtk.gdk.INTERP_BILINEAR)

    # Put the generated pixbuf in the GtkImage widget
    widget.set_from_pixbuf(pixbuf)

当我手动调用 update_image 时,它的效果是我预期的。现在我想让这个缩放在 GtkImage 控件大小变化时自动发生。我想到的最好办法是把 update_image 方法绑定到窗口的 configure-event GTK 事件上。每次窗口大小改变后,图片确实会被正确缩放。然而,我对这个解决方案有两个问题:

  • 我只能把窗口变大。一旦图片被放大,GTK 就不允许我把窗口缩小,因为窗口不能比它的控件小。我明白为什么会这样,但我不知道怎么改变这种行为。
  • 也许这不是大问题,但我更希望能有来自 GtkImage 控件的事件,而不是来自最外层的窗口。

对于这样一个简单的问题,我解释得有点长,抱歉,希望你能帮我。

3 个回答

2

如果你不想使用 GtkScrolledWindow,可以把你的 GtkImage 换成 GtkDrawingArea,然后用 Cairo 来绘制你的图片。这样做可以让图片缩小,因为 GtkDrawingArea 不会强制设置大小。

我对 Python 不太了解,不过这里有一个用 C 语言和 GTK3 的例子:

gboolean drawing_area_draw (GtkWidget *widget, cairo_t *cr, GdkPixbuf *current_image)
{

    .....   //Calculate size

    pixbuf = gdk_pixbuf_scale_simple (current_image,
                                    new_width,
                                    new_height,
                                    GDK_INTERP_BILINEAR);

    gdk_cairo_set_source_pixbuf (cr, 
                                pixbuf,
                                allocation.width/2 - new_width/2, 
                                allocation.height/2 - new_height/2);
    cairo_paint (cr);

    return FALSE;
}

int main (int argc, char *argv[])
{
    .....

    drawing_area = gtk_drawing_area_new ();
    g_signal_connect (G_OBJECT (drawing_area), "draw",
            G_CALLBACK (drawing_area_draw), current_image);

    .....
}

如果绘图区域的背景看起来不透明,可以把 gtk_widget_set_has_window () 设置为 FALSE。不过,最好是从 GtkDrawingArea 派生出你自己的小部件,并在它的 init 函数中设置这个属性。

如果你在使用 GTK2,代码差不多,只是你需要在 widget->window 上调用 gdk_cairo_create (),完成后要调用 cairo_destroy ()

另外,在 GTK2 中,如果 GtkDrawingArea 没有自己的 GdkWindow,那么绘图的坐标是相对于父级的 GdkWindow 而不是这个小部件。

2

所有的控件都有一个叫做 size-allocate 的信号(可以参考 PyGObject Gtk3)。

当控件被分配到一个新的空间时,就会发出“size-allocate”信号。

也许你可以用这个信号。

10

我觉得你可以使用这个小部件的 expose-event 信号来处理图片缩放的问题。另外,把图片放进一个可以滚动的容器里,应该也能解决窗口调整大小时的问题。请看看下面的例子是否对你有用。

import gtk

class ScaleImage:
    def __init__(self):
        self.temp_height = 0
        self.temp_width = 0

        window = gtk.Window(gtk.WINDOW_TOPLEVEL)

        image = gtk.Image()
        image.set_from_file('/home/my_test_image.jpg')
        self.pixbuf = image.get_pixbuf()
        image.connect('expose-event', self.on_image_resize, window)    

        box = gtk.ScrolledWindow()
        box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        box.add(image)

        window.add(box)
        window.set_size_request(300, 300)
        window.show_all()        

    def on_image_resize(self, widget, event, window):
        allocation = widget.get_allocation()
        if self.temp_height != allocation.height or self.temp_width != allocation.width:
            self.temp_height = allocation.height
            self.temp_width = allocation.width
            pixbuf = self.pixbuf.scale_simple(allocation.width, allocation.height, gtk.gdk.INTERP_BILINEAR)
            widget.set_from_pixbuf(pixbuf)

    def close_application(self, widget, event, data=None):
        gtk.main_quit()
        return False

if __name__ == "__main__":
    ScaleImage()
    gtk.main()

希望这能帮到你,祝好!

撰写回答