views:

603

answers:

2

How can I create PyGTK pixmaps with one pixel value set to transparent? I know it has something to do with creating a pixmap of depth 1 and setting it as a mask, but all I find is that it either does nothing or totally erases my pixmap when drawn. At the moment, I make a pixmap with

r = self.get_allocation()
p1 = gtk.gdk.Pixmap(self.window,r.width,r.height)
p1_c = p1.cairo_create()

then draw black lines all over it using Cairo. What I'd like to be able to do is to have all of the area not covered by lines transparent (making white the transparent colour, say), so that when I draw it to the window with draw_drawable, it leaves everything 'underneath' intact.

The FAQs and mailing list postings regarding this issue are most unhelpful as they are so outdated. Someone must know here!

+1  A: 

It looks like you want to use a Pixbuf and not a Pixmap. The Pixbuf includes an alpha setting, which will give you transparency, whereas the Pixmap does not.

plinth
Thanks. I've looked through the PyGTK Pixbuf documentation, but there doesn't seem to be any way of using them as a surface for Cairo to draw on. Do I need to convert back and forth between Pixbuf and ImageSurface in order to use them?
Ignis Umbrae
+2  A: 

I don't think you can do what you want with a Pixmap or Pixbuf, but here are two strategies for implementing scribbling on top of an existing Widget. The most obvious one is just to catch the draw event and draw straight onto the Widget's Drawable, with no retained image in the middle:

from gtk import Window, Button, main
from math import pi
import cairo

w = Window()
b = Button("Draw on\ntop of me!")

def scribble_on(cr):
    cr.set_source_rgb(0, 0, 0)
    cr.rectangle(10, 10, 30, 30)
    cr.fill()
    cr.arc(50, 50, 10, 0, pi)
    cr.stroke()

def expose_handler(widget, event):
    cr = widget.window.cairo_create()
    cr.rectangle(event.area.x, event.area.y,
                 event.area.width, event.area.height)
    cr.clip()
    scribble_on(cr)
    return False

b.connect_after("expose_event", expose_handler)
w.add(b)
w.set_size_request(100, 100)
w.show_all()
main()

A second option, if you want to have an intermediary ARGB image that you don't have to update each time a redraw is requested, would be to pre-render the image to an ImageSurface. Here's a replacement for expose_handler, above, that only draws the image once:

import cairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
scribble_on(cairo.Context(surface))

def expose_image_handler(widget, event):
    cr = widget.window.cairo_create()
    cr.rectangle(event.area.x, event.area.y,
                 event.area.width, event.area.height)
    cr.clip()
    cr.set_source_surface(surface)
    cr.paint()

If this is the sort of thing you're looking for, I would recommend updating the title of the question to reflect your real need :).

Glyph