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