PyGTK: Как мне сделать изображение автоматически масштабируемым, чтобы соответствовать родительскому виджету? - PullRequest
8 голосов
/ 21 февраля 2011

У меня есть приложение PyGTK, которое требует загрузки изображения неизвестного размера, однако у меня возникает проблема, заключающаяся в том, что если изображение очень большое или очень маленькое, компоновка окна становится искаженной и трудной для использования. Мне нужен какой-то способ сделать изображение автоматически масштабируемым, чтобы соответствовать его родительскому виджету. К сожалению, после некоторого исследования кажется, что нет кода, встроенного или нет, который делает то, что я ищу.

Как я могу написать что-нибудь для этого? Я бы подумал, что кто-то уже написал бы код для этого; я что-то пропустил?

Ответы [ 3 ]

9 голосов
/ 21 февраля 2011

Вы можете использовать widget.get_allocation (), чтобы узнать размер родительского виджета, и pixbuf.scale_simple для масштабирования изображения, например:

allocation = parent_widget.get_allocation()
desired_width = allocation.width
desired_height = allocation.height

pixbuf = gtk.gdk.pixbuf_new_from_file('your_image.png')
pixbuf = pixbuf.scale_simple(desired_width, desired_height, gtk.gdk.INTERP_BILINEAR)
image = gtk.image_new_from_pixbuf(pixbuf)

Если вы хотите, чтобы изображение масштабировалось при каждом изменении размера окна, вам придется поместить приведенный выше код (или что-то подобное, чтобы избежать загрузки изображения с диска каждый раз) в функции, связанной с сигналом size_allocate родительский виджет. Чтобы избежать бесконечных циклов, убедитесь, что изображение, которое вы помещаете в виджет, не изменяет его размер снова.

Ссылки:

0 голосов
/ 29 сентября 2015

Вот исключение, которое выполняет эту задачу в области рисования:

    self.spash_pixbuf = GdkPixbuf.Pixbuf.new_from_file('myfile.png')
    ...

    def on_draw(self, widget, cairo_ct):
        """
            draw
        """
        self._cairo_ct = cairo_ct
        self._width = widget.get_allocated_width()
        self._height = widget.get_allocated_height()

        self._draw_cover(self.spash_pixbuf)

    def _draw_cover(self, pixbuf):
        """
            Paint pixbuf to cover drawingarea.
        """
        img_width = float(pixbuf.get_width())
        img_height = float(pixbuf.get_height())
        # Scale
        width_ratio = self._width / img_width
        height_ratio = self._height / img_height
        scale_xy = max(height_ratio, width_ratio)
        # Center
        off_x = (self._width  - round(img_width*scale_xy)) //2
        off_y = (self._height - round(img_height*scale_xy)) //2

        # Paint
        self._cairo_ct.save()

        self._cairo_ct.translate(off_x, off_y)
        self._cairo_ct.scale(scale_xy, scale_xy)

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

        self._cairo_ct.restore()
0 голосов
/ 23 июля 2015

Вот небольшой фрагмент кода, который позволяет использовать автоматически масштабированное изображение.

import gtk


class ImageEx(gtk.Image):
    pixbuf = None

    def __init__(self, *args, **kwargs):
        super(ImageEx, self).__init__(*args, **kwargs)
        self.connect("size-allocate", self.on_size_allocate)

    def set_pixbuf(self, pixbuf):
        """
        use this function instead set_from_pixbuf
        it sets additional pixbuf, which allows to implement autoscaling
        """
        self.pixbuf = pixbuf
        self.set_from_pixbuf(pixbuf)

    def on_size_allocate(self, obj, rect):
        # skip if no pixbuf set
        if self.pixbuf is None:
            return

        # calculate proportions for image widget and for image
        k_pixbuf = float(self.pixbuf.props.height) / self.pixbuf.props.width
        k_rect = float(rect.height) / rect.width

        # recalculate new height and width
        if k_pixbuf < k_rect:
            newWidth = rect.width
            newHeight = int(newWidth * k_pixbuf)
        else:
            newHeight = rect.height
            newWidth = int(newHeight / k_pixbuf)

        # get internal image pixbuf and check that it not yet have new sizes
        # that's allow us to avoid endless size_allocate cycle
        base_pixbuf = self.get_pixbuf()
        if base_pixbuf.props.height == newHeight and base_pixbuf.props.width == newWidth:
            return

        # scale image
        base_pixbuf = self.pixbuf.scale_simple(
            newWidth,
            newHeight,
            gtk.gdk.INTERP_BILINEAR
        )

        # set internal image pixbuf to scaled image
        self.set_from_pixbuf(base_pixbuf)

И небольшой пример использования:

class MainWindow(object):
    def __init__(self):
        self.window = gtk.Window()
        self.window.connect("destroy", gtk.main_quit)

        # create new ImageEx
        self.image = ImageEx()
        # set size request, to limit image size
        self.image.set_size_request(width=400, height=400)
        # load image from file, change path with path of some of your image
        pixbuf = gtk.gdk.pixbuf_new_from_file("path/to/your/image.jpeg")

        # that's the key moment, instead `set_from_pixbuf` function
        # we use our newly created set_pixbuf, which do some additional assignments
        self.image.set_pixbuf(pixbuf)

        # add widget and show window
        self.window.add(self.image)
        self.window.show_all()

if __name__ == '__main__':
    MainWindow()
    gtk.main()
...