如何修复内容更改后 GTK Scrolled Window 不更新滚动条?

How to fix GTK Scrolled Window not updating scroll bars after contents have changed?

我希望在我的 GTK 应用程序中有一个不断调整大小以适合其父容器的图像。

我通过在大小分配事件回调中获取父容器的大小并根据这些尺寸调整图像大小来完成此操作。当我将 window 变小时,这很好用,但是当我想把它变大时,它拒绝调整大小,因为它必须至少与内容(图像)一样大。

为了克服这个问题,我将图像放在 ScrolledWindow 中,这样我就可以自由地调整 window 更小的尺寸。

问题在于,当我将显示的图像切换为不同尺寸的图像时,ScrolledWindow 似乎没有意识到这一点,我留下了一个内容大小错误和不必要的滚动条的 ScrolledWindow。但是,唉,我可以将鼠标悬停在滚动条上,它意识到它对于它的内容来说太大了,并删除了滚动条。请看下面的演示。

我能否以某种方式让这种“更正”行为立即发生,而不是当我将鼠标悬停在滚动条上时发生?

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import GdkPixbuf


class Minimal(Gtk.Window):


    imageShown = 0
    img = Gtk.Image.new()
    pixbufRed = GdkPixbuf.Pixbuf.new_from_file("kirby_red.png")
    pixbufBlue = GdkPixbuf.Pixbuf.new_from_file("kirby_blue.png")
    pixbuf = None


    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_default_size(400,300)

        button = Gtk.Button.new_with_label("Swap Image")
        button.connect("clicked", self.on_button_click)

        self.pixbuf = self.pixbufRed
        self.img.set_from_pixbuf(self.pixbuf)

        scrolled = Gtk.ScrolledWindow()
        scrolled.connect("size-allocate", self.on_size_allocated);
        scrolled.add(self.img)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,spacing=0)
        box.pack_start(button, False, False, 0)
        box.pack_end(scrolled, True, True, 0)

        self.add(box)


    #swap image shown using imageShown flag to keep track
    def on_button_click(self, button):
        if(self.imageShown == 0):
            self.pixbuf = self.pixbufBlue
            self.imageShown = 1
        else:
            self.pixbuf = self.pixbufRed
            self.imageShown = 0
        self.img.set_from_pixbuf(self.pixbuf)


    def on_size_allocated(self, widget, allocation):
        scaledPixbuf = Minimal.scale_image_from_allocation_keep_aspect(self.pixbuf, allocation)
        self.img.set_from_pixbuf(scaledPixbuf)


    @staticmethod
    def scale_image_from_allocation_keep_aspect(pixbuf, allocation):
        imgWidth = pixbuf.get_width()
        imgHeight = pixbuf.get_height()

        parentWidth = allocation.width
        parentHeight = allocation.height

        aspectWidth = parentWidth/imgWidth
        aspectHeight= parentHeight/imgHeight

        aspect=0

        if(aspectWidth < aspectHeight):
            aspect = aspectWidth
        else:
            aspect = aspectHeight

        newWidth = imgWidth*aspect
        newHeight = imgHeight*aspect

        return pixbuf.scale_simple(newWidth, newHeight, GdkPixbuf.InterpType.BILINEAR)


win = Minimal()
win.show_all()
Gtk.main()

size-allocate 并不是更改小部件内容的正确位置(例如更改图像小部件的 pixbuf),如果您尝试那样使用它通常无法正常工作.它更多地用于自定义容器小部件,以便在确定大小后对其子项进行布局。

在 GTK 3 中,我通常通过创建一个非常简单的自定义小部件来解决使图像填充可用 space 的问题,如下所示:

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf, Gdk

class ScaleImage(Gtk.DrawingArea):
    def __init__(self, pixbuf):
        Gtk.DrawingArea.__init__(self)
        self.pixbuf = pixbuf

    def do_draw(self, cr):
        alloc, baseline = self.get_allocated_size()
        factor = min(alloc.width / self.pixbuf.get_width(), alloc.height / self.pixbuf.get_height())
        cr.scale(factor, factor)

        Gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)
        cr.paint()

win = Gtk.Window()
img = ScaleImage(GdkPixbuf.Pixbuf.new_from_file("file.png"))
win.add(img)
win.show_all()

Gtk.main()

我还没有尝试过,但在 GTK 4 中,您应该可以使用 Gtk.Picture 来获得相同的效果,而无需自定义小部件。