Search code examples
pythoneventswxpython

wxPython wx.CallAfter - how do I get it to execute immediately?


When I execute a function with wx.CallAfter, inside it a variable is set. I want to be able to get the value of that variable on the next line, however CallAfter seems to execute the function a LOT later on. I think it pushes it in some queue that gets processed later or something... Is there a way to get that queue to be processed immediately?

This is the code of wx.CallAfter:

def CallAfter(callableObj, *args, **kw):
    """
    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.

    :see: `wx.CallLater`
    """
    assert callable(callableObj), "callableObj is not callable"
    app = wx.GetApp()
    assert app is not None, 'No wx.App created yet'

    if not hasattr(app, "_CallAfterId"):
        app._CallAfterId = wx.NewEventType()
        app.Connect(-1, -1, app._CallAfterId,
                    lambda event: event.callable(*event.args, **event.kw) )
    evt = wx.PyEvent()
    evt.SetEventType(app._CallAfterId)
    evt.callable = callableObj
    evt.args = args
    evt.kw = kw
    wx.PostEvent(app, evt)

My assumption is that wx.PostEvent puts 'evt' in some internal container in 'app' and at some point that container is iterated and all elements have their 'callable' executed or something? So basically I need to cause this to happen immediately, but I can't find anything that looks like 'wx.ForceEventProcessing()' or 'wx.FlushEventsQueue'

I tried calling self.Update() in the panel where the methods are defined, but that just blocked the application and it stopped responding.


Solution

  • As you've guessed wx.CallAfter adds the call info to a queue (the pending event queue, which is processed in the GUI thread the next time the event queue is emptied) and then returns immediately. In other words, it is intended to be a "fire and forget" function call.

    If you need to get the result of that called function there are a few approaches that can be taken. One is the delayed result pattern, where basically you get a result object that will receive the result of the function call after it has been called, and the calling code can get a notification when that happens. There is an implementation of this in wx.lib.delayedresult and probably other implementations of this pattern can be found in various places. The nice thing about this approach is that the calling thread doesn't have to stop and wait for the result and can continue processing other things if desired.

    Another approach that is quite nice in my opinion is wxAnyThread, which can be found at https://pypi.python.org/pypi/wxAnyThread/. This module provides a decorator that allows methods to be called from any thread like a normal function call, but when called from something other than the GUI thread it will use wx.CallAfter-like processing to have the function called in the UI thread, and then wait for the result and will return it to the caller when it is received. So although the calling thread is blocked while the UI events are processed and the function is called, it is much simpler to use and reduces complexity on the calling side.