2

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.

Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
ulak blade
  • 2,515
  • 5
  • 37
  • 81
  • In my opinion if you are comfortable with threading, wx.CallAfter is never needed. – user2963623 Jul 07 '14 at 16:34
  • @user2963623 any idea on what might cause the application to hang up completely after the wx call in the function that I pass to wx.CallAfter? It raises no exceptions and outputs no warnings, and no breakpoints are hit after it. It just hangs up the app. – ulak blade Jul 07 '14 at 16:43
  • Is this referring to your earlier problem about registering key presses using `msvcrt.getch()`? – user2963623 Jul 07 '14 at 16:46
  • `msvcrt.getch()` seemed to not be working with PyDev: https://bugs.eclipse.org/bugs/show_bug.cgi?id=375172 ,however what I'm currently doing used to work(before moving to another thread and using `wx.CallAfter()`) .But yes, it hangs up the same way `msvcrt.getch()` did and it leaves no warning or error message in console or exception. – ulak blade Jul 07 '14 at 16:56
  • hmm I've noticed that after I stop debugging, PyDev tells me the exit code of the thread where `wx.CallAfter()` was called, is `-1` – ulak blade Jul 07 '14 at 17:25
  • I tested a simple wxpython app using `msvcrt.getch()` and ran it from my command prompt. I realized that `msvcrt.getch()` only registers key presses when the command prompt was in focus. It doesn't register key presses for the app. So this may not be a possible way to register key_presses – user2963623 Jul 07 '14 at 17:29
  • PyDev in Eclipse can't really help me much in debugging python threads, is there any other way to analyze what's going on here? – ulak blade Jul 07 '14 at 17:50
  • None that i know of. Anyways I doubt using getch() will be helpful. – user2963623 Jul 07 '14 at 18:18
  • `wx.CallAfter` is used almost exclusively in threading scenarios. I don't know what user2963623 is talking about. It's pretty much the best way to pass data from background threads to the main thread. – FogleBird Jul 07 '14 at 20:09
  • @Bogomil: This isn't a typical pattern you see when using `CallAfter`. Are you calling this from a worker thread? What exactly are you trying to do here? You might need to use a Queue (https://docs.python.org/2/library/queue.html) to pass data between your threads. Events are also commonly useful (https://docs.python.org/2/library/threading.html#event-objects). – FogleBird Jul 07 '14 at 20:10

2 Answers2

4

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.

RobinDunn
  • 6,116
  • 1
  • 15
  • 14
  • I tried wxAnyThread, I decorated my method with `@anythread` and then created a thread: `worker_thread = Thread(target = self.WxCallsMethod, name = "Worker Thread") worker_thread.start()` And what happens is, WxCallsMethod never gets called, the breakpoint at its beginning isn't hit. If I remove @anythread, it hits the breakpoints, but of course it crashes when it does the wx calls. I tried putting the @anythread method inside a non-@anythread method and when I start the thread with the non-anythread one it hits breakpoint, but fails at the @anythread one again... – ulak blade Jul 08 '14 at 09:28
  • You need to provide a small, runnable example of your code. – Mike Driscoll Jul 08 '14 at 13:18
  • 1
    @Bogomil you definitely should not use `@anythread` on the thread's target function, but you can use it on any functions that it calls which need to interact with the GUI. – RobinDunn Jul 09 '14 at 17:38
-2

Did you try Google? I did and I got several ideas. See the following thread:

Try wx.WakeUpIdle() or wx.GetApp().ProcessIdle(). I also noticed in the docs that there is a ProcessPendingEvents method for wx.App, so you could also try wx.Getapp().ProcessPendingEvents() too.

Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • hmm, now after calling `wx.GetApp().ProcessPendingEvents()` any function that does a wx-call and that I pass to wx.CallAfter, causes the thread to die without leaving any error message or raising any exception. – ulak blade Jul 07 '14 at 16:33
  • Probably because you are calling `ProcessPendingEvents` from the wrong thread. This would have the same problems (and more) as calling your function from the wrong thread. – RobinDunn Jul 07 '14 at 22:31
  • `wx.PostEvent` already calls `WakeUpIdle`. It basically just adds a native NULL event to the event queue so it will be sure to "become empty" again in a timely manner if it was already empty. – RobinDunn Jul 07 '14 at 22:32
  • @RobinDunn - I didn't know that. That's really neat! – Mike Driscoll Jul 08 '14 at 13:16
  • 2
    Calling wx.GetApp().ProcessPendingEvents() after wx.CallAfter solved the similar problem for me – shiftyscales Jun 22 '16 at 21:02