How to draw GdkPixbuf using GTK3 and PyGObject

I have a small application that uses DrawingArea to draw a simple map using PyGObject and GTK3 .

I load a Pixbuf using

 from gi.repository import Gtk, GdkPixbuf pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size("logo.png", 25, 25) 

and then try to draw it in the DrawingArea event DrawingArea

 def draw(self, widget, context): window = widget.get_window() ctx = window.cairo_create() ctx.set_source_pixbuf(pixbuf, 0, 0) 

but i get an error

 "AttributeError: 'cairo.Context' object has no attribute 'set_source_pixbuf'" 

If I read the Gtk2 to Gtk3 migration guide correctly , this should work. What am I doing wrong?

+6
source share
2 answers

The new rally signal uses a callback that already passes the Cairo context as a parameter, you do not need to do things like window = widget.get_window() , as you did in PyGtk, to get the cairo context while the exposure signal is present. In PYGObject, it's easier:

 import cairo class Foo(object): def __init__(self): (...) self.image = cairo.ImageSurface.create_from_png('logo.png') (...) def draw(self, widget, context): if self.image is not None: context.set_source_surface(self.image, 0.0, 0.0) context.paint() else: print('Invalid image') return False 

That is, if you do not need PixBuf, but if you need it for something else, you have several options:

  • To have both objects in memory. If both are downloaded from PNG, there should not be many problems besides memory loss.
  • Convert GdkPixbuf to PIL Image, then PIL Image to a data array, and then create a Cairo ImageSurface from this data array using create_for_data (). Yak: S I don't know better, sorry: S
  • Use the Gdk.cairo_set_source_pixbuf () suggested by hock. This is apparently the right way to draw Pixbuf in ImageSurface, but it's completely fearless (and so I hate this Introspection stuff, everything looks like C, like a bad C port).

If you choose the terrible second option, here's how:

 import Image import array from gi.repository import Gtk, GdkPixbuf width = 25 height = 25 pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size('logo.png', width, height) pil_image = Image.fromstring('RGBA', (width, height), pixbuf.get_pixels()) byte_array = array.array('B', pil_image.tostring()) cairo_surface = cairo.ImageSurface.create_for_data(byte_array, cairo.FORMAT_ARGB32, width, height, width * 4) 

Note: create_for_data () is not yet available for Python3 , only for Python2 .

Check also my answer on how to use a double buffer in PyGObject, if that is what you are trying to achieve: Drawing in PyGobject (python3)

respectfully

+9
source

The following seems to do the job:

 def draw(self, widget, context): Gdk.cairo_set_source_pixbuf(context, self.pixbuf, 0, 0) context.paint() 

One more question remains: is this the preferred way to do something?

+6
source

Source: https://habr.com/ru/post/913852/


All Articles