wxPython fast and slow

David Bolen db3l.net at gmail.com
Thu Mar 12 03:57:00 EDT 2009


iu2 <israelu at elbit.co.il> writes:

> A question about CallAfter: As I understand, this function is intended
> to be used from within threads, where it queues the operation to be
> performed in the GUI queue.

I agree with the second half of the sentence but not the first.
CallAfter is intended to queue up a delayed call (via the GUI queue),
but it can be used anywhere you wish that behavior.  Yes, it's also
one of the very few functions that can be called from a thread other
than the GUI thread, but it works just as well from the GUI thread.

Or to quote its docstring:

    Call the specified function after the current and pending event
    handlers have been completed.  This is also good for making GUI
    method calls from non-GUI threads.  Any extra positional or
    keyword args are passed on to the callable when it is called.

> How does it work in this situation? Does it queue the opreation for
> some idle time or does it perform it right away?

You can actually see the source in _core.py in your wx installation.
It always executes via a wx.PostEvent call.

> And another question, if I may, I used to make tight loops in windows
> API, planting inside them a command that processes messages from the
> GUI queue and returns when no more messages exists. Something like
> this:
>
> loop {
>   operations
>   process_gui_messages
> }
>
> The loop ran quickly and the GUI remained responsive during the loop.
> I did it on window API using a function I defined similar to this one:

I don't think there's much difference in the above and doing your
operations during one of the events.  In both cases "operations" is
going to block any further event processing so cannot be lengthy or
the GUI will feel unresponsive.  "Lengthy" varies but I'd certainly
put it in the neighborhood of small fractions of a second.

Your original code took almost 2 seconds for the "operations" part
(before getting back to processing GUI messages through the main
loop), which certainly seems too long.

> void ProcessMessages()
> {
>   while (PeekMessage(....)) {
>     TranslateMessage(..);
>     DispatchMessage(..);
>   }
> }

Not quite positive, but if you're talking about implementing this as a
nested dispatch loop (e.g., called from within an existing event), you
can do that via wxYield.  Of course, as with any nested event loop
processing, you have to be aware of possible reentrancy issues.

> This technique is not good for long loops, where the user may activate
> other long GUI opreations during the tight loop and make a mess.
> But it carries out the job well where during the time of the loop the
> user may only access to certain features, such as pressing a button to
> cancel the operation, operating the menu to exit the program, etc.
> This scheme saves some state-machine code that is required when using
> event-based programming.

Maybe - for my own part, I'm not completely convinced and tend to far
prefer avoiding nested event loop dispatching.  There are some times
when it might be unavoidable, but I tend to find it indicative that I
might want to re-examine what I am doing.

It seems to me that as long as you have to keep the "operations" step
of your loop small enough, you have to be able to divide it up.  So
you'll need some state no matter what to be able to work through each
stage of the overall "operations" in between calls to process the GUI.

At that point, whether it's a local variable within the scope of the
looping code, or just some instance variables in the object handling
the event loop seems about the same amount of state management.

For example, in your original code you could probably consider the
generator and/or 'x' your local state.  But the current step in the
movement could just as easily be an instance variable.

> Does wxPython have something like ProcessMessages?

If you just mean a way to process pending messages wxYield may be
sufficient.

If you want to take over the primary dispatch loop for the application,
normally that has been handed off to wxWidgets via wxApp.MainLoop.  However,
I believe you can build your own main dispatch loop if you want, as there
are functions in wxApp like ProcessPendingEvents, Pending, Dispatch and
so on.  You may need to explicitly continue to support Idle events in
your own loop if desired.

If you need to get into more details, it's probably better dealt with
on the wxPython mailing list.

-- David



More information about the Python-list mailing list