This innocent code below looks okay, but it has one major bug here. The bug can cause the application to crash. This is because we try to perform some GUI related stuff in another thread (MyThread). Many GUI toolkits are single-threaded. In other words, the main loop (the loop that starts the GUI application) should only be responsible for handling any GUI related stuff. Any other long running tasks can be done in another thread.
#!/usr/bin/env python
import wx, threading
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "MyApp")
panel = wx.Panel(self)
self.btn = wx.Button(panel, label="Click Me!")
self.btn.Bind(wx.EVT_BUTTON, self._do_something)
def _do_something(self, evt):
MyThread(self).start()
class MyThread(threading.Thread):
def __init__(self, frame):
threading.Thread.__init__(self)
self.frame = frame
def run(self):
wx.MessageDialog(self.frame, message="Test", caption="Test",
style=wx.ICON_ERROR | wx.CENTRE).ShowModal()
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyFrame()
frame.Centre()
frame.Show(True)
app.MainLoop()
To fix this bug, wxPython provides some thread-safe methods, such as wx.CallAfter, wx.CallLater, and wx.PostEvent. A combination of Publisher/Subscribe and wx.CallAfter can eliminate the bug as shown below.
#!/usr/bin/env python
import wx, threading
from wx.lib.pubsub import Publisher
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "MyApp")
panel = wx.Panel(self)
self.btn = wx.Button(panel, label="Click Me!")
self.btn.Bind(wx.EVT_BUTTON, self._do_something)
Publisher().subscribe(self._update, "update")
def _do_something(self, evt):
MyThread().start()
def _update(self, msg):
# msg.data is the data that was sent in the CallAfter
wx.MessageDialog(self, message=msg.data, caption="Test",
style=wx.ICON_ERROR | wx.CENTRE).ShowModal()
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
msg = "Test Message"
wx.CallAfter(Publisher().sendMessage, "update", msg)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyFrame()
frame.Centre()
frame.Show(True)
app.MainLoop()