[Chandler-dev] wx.Yield

Robin Dunn robin at alldunn.com
Tue Jun 12 14:23:07 PDT 2007


D John Anderson wrote:

> So, to summarize, most of the time we should probably be using 
> wx.SafeYield(None, True), followed by wx.GetApp().Yield(True) when you 
> want to let users issue commands during the Yield; wx.GetApp().Yield() 
> should almost never be called and absolutely never call wx.Yield().
> 

Some more info:

* In a nutshell yielding creates a temporary nested event loop, so any 
widget events that are currently pending will be sent before yield 
returns.  This is what can cause reentrance (whether they be problem 
reentrancies  or not.)  For example the user presses a button while some 
long running task (LRT) is still busy, but the event handler for that 
button shouldn't be executed until after the LRT is finished.  wx tries 
to prevent recursive calls to Yield, but it can't catch all reentrant 
situations such as the example I mentioned.

* Passing True to Yield causes it to simply return immediately if there 
is already a Yield active.  This helps avoid the recursive situations, 
but not all the potential reentrant problems.

* wx.SafeYield will disable all widgets in the app before it enters into 
the nested event loop, and then enables them again when it is done.  So 
it helps prevent reentrant problems because the user can't interact with 
the app while it is active, but it has a fair amount of overhead for 
doing the disabling and enabling.

IMO most of the time that people are using Yield something safer can be 
done instead.  Often it is used to get a window updated right away 
because some attributes have changed or Refresh has been called.  Most 
of the time simply calling the widget's Update() method will do the job, 
or even just waiting until the next normal paint event if you're not in 
the middle of some LRT.

There is an old overview of dealing with LRTs on the wxPython wiki at 
http://wiki.wxpython.org/LongRunningTasks.  Basically there are 3 options:

1. Use Yield inside the long running task's loop to allow events to be 
processed.  This has the problems with possible reentrance, etc.

2. Break up the LRT into chunks, (perhaps loop iterations) and run them 
from an EVT_IDLE handler one chunk at a time.  An EVT_IDLE event is sent 
whenever the system event queue becomes empty, and you can cause another 
to be sent immediately (if there are no new pending events) by calling 
event.RequestMore().  This approach is essentially the same as using 
Yield once per iteration of your LRT, but I think it is easier to make 
it safe because you don't have to deal with the magic black-box that is 
Yield, and you don't have to deal with potential recursions and 
reentrancies because the execution of each chunk is interleaved with 
normal event handling.  Looking at it from a little different 
perspective: In between each iteration of your LRT the program control 
is returning to the main event loop and any pending events are processed 
and then another EVT_IDLE happens and the next iteration of your LRT can 
run.  BTW, Python generators work well for this.

3. The final option is to move the LRT to a thread.  While this is a bit 
more work to do it has the potential of being the most efficient. 
However keep in mind that you can't call any widget methods that 
interact with or update the UI from the context of the worker thread. 
However there are all the various inter-thread communication techniques 
available, and in addition wx.CallAfter is safe to call from the worker 
thread.  It posts an event to the app which processes it in the main 
thread, so the function you give to wx.CallAfter will actually be called 
in the main thread, not the worker thread.

-- 
Robin Dunn
Software Craftsman
http://wxPython.org  Java give you jitters?  Relax with wxPython!



More information about the chandler-dev mailing list