How to draw a GdkPixbuf using GTK3 and PyGObject
Asked Answered
H

2

10

I have a small application that uses a 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's draw event signal

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

but I get the error message

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

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

Holo answered 22/4, 2012 at 17:5 Comment(3)
Are you sure you're getting something drawable back from get_window?Driskell
Yes, I get a <gtk.gdk.X11Window object at 0xb71aab44 (GdkX11Window at 0x9db5aa0)> object back, so the DrawingArea widget is realized. I seem to be at a loss of how drawing pixbufs in Gtk3 is supposed to work.Holo
Since the accepted answer is not to use a pixbuf, and the real problem was not on how to draw a pixbuf can you change the question so that people (like me) coming here to find an answer on how to really draw a GdkPixbuf using GTK3 and PyGObject do not step into this question?Linguistician
I
10

The new draw signal uses a callback that already passes the cairo context as a parameter, you don't need to do stuff like window = widget.get_window() like you did in PyGtk to get the cairo context while attending the expose-event signal. In PYGObject is simpler:

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 don't need the PixBuf, but if you need it for something else you have several options:

  1. To have both objects in memory. If both are loaded from a PNG there should not be much problems other than the waste of memory.
  2. Convert GdkPixbuf to PIL Image, then PIL Image to data array and then create a Cairo ImageSurface from that data array using create_for_data(). Yak :S I don't know better, sorry :S
  3. Use Gdk.cairo_set_source_pixbuf() proposed by hock. This seems to be the correct way to draw a Pixbuf in a ImageSurface, but it is totally unpythonic (and that's why I hate this Introspection stuff, all looks like C, like a bad C port).

If you choose the awful second option here is 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 that 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 this is what you're trying to achieve: Drawing in PyGobject (python3)

Kind regards

Iago answered 11/5, 2012 at 7:16 Comment(3)
Thanks. This seems to be a lot saner way of handling graphics in GTK3 than my various efforts ;)Holo
+1 "but it is totally unpythonic (and that's why I hate this Introspection stuff, all looks like C, like a bad C port)."Marymarya
Update create_for_data is now available, the current docs are here pycairo.readthedocs.io/en/latest/reference/…Vasya
H
8

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 question still remains: Is this the preferred way of doing things?

Holo answered 23/4, 2012 at 19:48 Comment(5)
my program generates "segmentation faults" after executing cairo_set_source_pixbuf any help ?Poison
In Gtk doc they say „be careful, this function is not from cairo ...”. I should -1 the answer above this one, but seem's more constructive to +1 this one. Gtk3 is a diferent way of things, it use cairo and give back the handler to surface, so don't act like it is an external tool: there are where the drawings happends.Rosie
@AkashShende - I am also getting a segmentation fault using .cairo_get_source_pixbuf, did you figure out how to fix this?Iolite
@Iolite I gave up on Pixbuf and used Cairo_Surface and used method cairo_set_source_surface to set image. Check this github.com/akash0x53/i3lock/commit/…Poison
@AkashShende - thx, it turned out not to be a problen with .cairo_set_source_pixbuf for me but the call to GdkPixbuf.Pixbuf.new_from_data call i was doing. Changing to GdkPixbuf.Pixbuf.new_from_bytes fixed it for me.Iolite

© 2022 - 2024 — McMap. All rights reserved.