Dan Eloff wrote:
That will certainly eliminate most of the complexity. Now I need a way
to create a custom event that can be handled by any of my wx frames
and add that to the event loop.
As limodou wrote, CallAfter is used in cases where you want to avoid doing that. It simply sets up "deferred" calls to a routine, which the wx main loop processes after it's done with other stuff, so the call happens in the wx thread and not the thread which called CallAfter. Note that this doesn't entirely eliminate the possibility of race conditions (threading synchronization problems), but merely ensures that calls to methods in your widgets happen from the wx thread.
To do custom events is pretty simple (and can be simpler even than my code here):
EVT_MYEVENT = wx.PyEventBinder(wx.NewId(), 0)
class MyEvent(wx.PyEvent):
'''wrapper object to message into the wx event loop'''
ID_EVT_MYEVENT = EVT_MYEVENT.evtType[0]
def __init__(self, msg, *args, **kwargs):
MyEvent.__init__(self, *args, **kwargs)
self.SetEventType(self.ID_EVT_MYEVENT)
self.wrapped = msg
With that you can wrap an arbitrary Python object and have it sent in via the wx event processing like this:
First, make sure someone binds to that event in the usual manner:
self.Bind(EVT_MYEVENT, self.OnMyEvent) # no extra args needed
Then, from any other thread, create one of these objects and "post" it to your widget:
event = MyEvent(some_object_to_wrap)
widget.AddPendingEvent(event)
# or use something like wx.PostEvent(myapp, event)
The wx MainLoop will now process that in turn, just like other events, so in your OnMyEvent() handler you just do this:
def OnMyEvent(self, event):
wrapped_object = event.wrapped
# process wrapped_object in whatever way you see fit
I have no idea how to use wx.CallAfter to insert a custom event into
the event loop. I also have no idea about the arguments to Bind.
The only thing you were really missing is AddPendingEvent(), or wx.PostEvent(). Note that CallAfter is merely using something very similar to the above code to do its work... and of course you can read the source to see how (in _core.py). Fundamentally AddPendingEvent() is, I believe, *the* mechanism in wxWidgets for having outside threads trigger activity in the GUI, other than using a generic Queue and polling for changes in an OnIdle() handler.
Reading the C++ docs on wxEvtHandler::AddPendingEvent and ProcessEvent, and ::wxPostEvent (in the Miscellaneous functions section) gives good background material.
-Peter