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!

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

Open Source Applications Foundation "chandler-dev" mailing list
http://lists.osafoundation.org/mailman/listinfo/chandler-dev

Reply via email to