#python #gtk #pygtk
Вопрос:
Я хочу, чтобы в моем приложении GTK было изображение, размер которого постоянно менялся в соответствии с его родительским контейнером.
Я добился этого, получив размер родительского контейнера внутри обратного вызова события выделения размера и изменив размер своего изображения в соответствии с этими размерами. Это прекрасно работает, когда я делаю окно меньше, но когда я хочу сделать его больше, оно отказывается изменять размер, потому что оно должно быть по крайней мере таким же большим, как содержимое (изображение).
Чтобы преодолеть этот аспект, я поместил изображение в прокрученное окно, чтобы я мог свободно изменять размер своего окна.
Проблема заключается в том, что, когда я переключаю показанное изображение на изображение с разными размерами, прокручиваемое окно, похоже, этого не осознает, и у меня остается прокручиваемое окно с неправильным размером содержимого и ненужными полосами прокрутки. Но, увы, я могу навести курсор на полосу прокрутки, и он поймет, что она слишком велика для своего содержимого, и удалит полосы прокрутки. Смотрите демонстрацию ниже.
Могу ли я каким-то образом добиться, чтобы это поведение «исправления» происходило сразу, а не при наведении курсора мыши на полосы прокрутки?
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()
Ответ №1:
size-allocate
на самом деле это не совсем подходящее место для изменения содержимого вашего виджета (например, для изменения пикселя виджета изображения), и обычно это работает неправильно, если вы пытаетесь использовать его таким образом. Он больше предназначен для пользовательских виджетов контейнеров для размещения их дочерних элементов, как только размер уже определен.
В GTK 3 я обычно решаю проблему заполнения изображениями доступного пространства, создавая очень простой пользовательский виджет, подобный этому:
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, чтобы получить тот же эффект без пользовательского виджета.
Комментарии:
1. Это делает это, просто нужно было добавить функцию change_image в класс scaleImage, которая изменила pixbuf и вызвала self.queue_draw. Спасибо!