Question

I have an almost-working piece of code (I hope). In the update method of this class, random black points should be drawn at locations bounded by the width and height of the window - the problem is that the points are not drawn. A gtk window containing the background image that is loaded with the cairo ImageSurface.create_from_png(BG_IMG) is displayed and I've also verified that the update function is called (every 17ms with a gobject.timeout_add callback function). I've searched here and elsewhere, but I can't quite see what's wrong with this code..

class Screen(gtk.DrawingArea):

    __gsignals__ = {"expose-event": "override"}

    def do_expose_event(self, event):
        self.cr = self.window.cairo_create()

        self.cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
        self.cr.clip()
        self.draw(*self.window.get_size())

    def draw(self, width, height):
        x = y = 0
        self.bg = c.ImageSurface.create_from_png(BG_IMG)
        self.cr.set_source_surface(self.bg, x, y)
        self.cr.paint()

    def update(self):
        x = randint(0, DOCK_W)
        y = randint(0, DOCK_H)
        self.cr.rectangle(x, y, 1, 1)
        self.cr.set_source_rgba(0, 0, 0, 1)
        self.cr.fill()
        self.cr.paint()

Anybody have some insights into why this is code is failing? Big thanks in advance!

Solved

I was unaware that a new cairo context could be used at each draw operation. That turned out to be the main problem.

Était-ce utile?

La solution

Generally speaking, you should not draw directly to the window outside of an expose event. And do not keep the cairo context for later use: create one for each event run.

When you want to draw your points, just do: widget.queue_draw(), and a new expose event will be delivered to you ASAP. But note that in the expose event you will have to paint all the points, not just the new one.

There a useful optimization to your type of code: from the timer do not call queue_draw as it is fairly inefficient. Instead just draw the new point. However that doesn't excuse you to draw all the points in the do_expose_event, as an expose event can happen at any time and you do not want to lose the already painted points.

To do the one-point draw you have to create a new cairo context, but you do not need to save it:

def update(self):
    cr = self.window.cairo_create()
    x = randint(0, DOCK_W)
    y = randint(0, DOCK_H)
    self.points.append((x,y)) #for the expose event ;-)
    cr.rectangle(x, y, 1, 1)
    cr.set_source_rgba(0, 0, 0, 1)
    cr.fill()
    cr.paint()

Another common optimization, particularly if you have a lot of points is to keep the painted image in a bitmap, so when the expose event happens, you simply blit the bitmap, instead of iterating all along the list of points.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top