[pygtk] Expose event question - repeat

A.T.Hofkamp a.t.hofkamp at tue.nl
Tue Dec 11 21:03:50 WST 2007


Donn wrote:
> Hi,
>  I am stuck on this and thought it may have been lost in the noise, or didn't 
> reach the list. Can anyone give me a suggestion?

Let me try then. (Lack of response is usually a sign that you are asking 
something strange or impossible though, so not sure how far I get.)

>> What do you mean by "out of synch"?  Why is it so urgent that the
>> redraw happen quickly?
> I mean that *if* the expose event takes too long to run, the loop may have 
> begun again and the state of the app will have changed. 

afaik there is no 'loop beginning again'. the app simply gets 'timeout' events 
*in the queue* at some time in the future at least as far away as the time-out 
value.

Since events from the queue are handled sequentially, the time-out event will 
only get noticed after the expose event handler has finished.

>  I am building a 'stack' of objects that represent the state -- what gets 
> drawn where and in what order. It goes like this:
> 
> This is the loop in my _stack(gtk.DrawingArea) class:
>     def _tick(self): 
> 1.        self.MainTimeline._tick()
> 2.        _stack._draw()
> 3.       if _stack.quitApp:
> 4.            gtk.main_quit()
> 5.        return True # spawn timeout again.

and since you have to give this value each time, I think you will not get new 
timeout-events as long as you haven't handled the one in the queue.

(easy to check, stick a time.sleep() in there (every even time the function 
gets called, wait at least twice the time-out value), and print the precise 
time of the function being called (time.time()).)

> This function is initially called by:
>         gobject.timeout_add(self.speed, self._tick)
>         gtk.main()
> 
> _stack.draw() is:
>     def _draw(self):
>         if not self.quitApp:
>             self.alloc = self.get_allocation()
>             rect = gtk.gdk.Rectangle(self.alloc.x, self.alloc.y, 
> self.alloc.width, self.alloc.height)
>             self.window.invalidate_rect(rect, True)
> 
> So, it forces an expose event in the _stack class that goes through the stack 
> and draws all the graphics to the DrawingArea.

I don't really understand why you want to redraw everything every time, it 
seems very expensive. Couldn't you limit it in some way?
(the only difference with this improvement afaik is less cpu time / faster 
possible refresh rate, no change in behaviour).

>> No -- invalidate_rect will generally *not* trigger the expose event
>> until after you return to the main loop, i.e., after your _timer
>> callback finishes.  
> Actually, on my fairly fast machine, the expose event *is* running *and* 
> finishing before the return from _tick().

How do you know?

I don't see any form of multi-threading in your code, so how would this happen?

Did you print entry and exit of both event handlers?

>  I am worried that this is unplanned 
> and that on other machines it may take even longer to run.

The simplest way to try is to add a "time.sleep(5)" in your redraw code.
Another way to mess up timing and updating of the window, is to make an X11 
connection over the physical network, preferably encrypted :).

> So, I need to ensure (somehow) that line 5 (above) does *not* get reached 
> until the expose event (triggered by line 2) has finished.

In general, I'd say that is not possible. You don't even know when 'expose' is 
finished (you may get a number of expose events in response to one invalidate 
request).

Maybe you can do something with the idle event. (after a time-out, setup an 
idle event, and on an idle event, set up a timer?

>  I can use flags to prevent line 1 from running until expose has finished, but 
> I thought I'd try to get a better picture about the timing of these events.

Since you don't have threading, expose and the time-out event handlers will 
never be executed at the same time.
If the timer is much faster than the redraw, you'll get a 100% cpu time 
drawing to the screen.
Under the assumption that multiple update requests are merged in the queue 
(not sure about this!), you probably won't get a long queue of redraws though.

> Is there a better, faster, way to force an expose? I don't suppose I can call 
> the expose handler directly?

Qt allows it, so it may be possible. This is generally considered very, very 
bad though.

Rather than trying to squeeze the windowing system into your approach, it may 
be better to re-think your approach.

>> If you do a busy-loop like this, then not only 
>> will you be living in a state of sin (busy-loops are bad!), but you
>> will never receive the expose event at all.
> As I am actually getting the expose event, I must be doing something right :) 

Any problem disappears when you throw a big enough CPU at it.

However, 'it is working' is not the same as 'solved' imho.


>> What are you trying to do?
> I'm writing a retained-mode API to do vector animation using Cairo. 
>  So far it's working far better than I had hoped. The user will import my 
> module and use the simple API to setup tweens and parent-child relationships.  
>  Each object gets a draw() method where the user can use Cairo to draw stuff. 
> (I suppose they can use anything they want that will draw in a DrawingArea, 
> but I am using Cairo-specific matrixes for controlling everything.

Well, why aren't you/they using something intended for direct drawing, eg SDL?
(not entirely sure, I only briefly scanned its API)

Albert



More information about the pygtk mailing list