Frage

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.

War es hilfreich?

Lösung

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top