Reputation: 11
I am new to wxPython and threading. I am trying to move some code into a thread so that it no longer blocks my GUI while executing, and I can push another button to kill the thread. Having done this, however, the code still blocks my GUI. How can I write this code so that my thread does not block the GUI?
import wx, sys
import threading
import time
class mywxframe(wx.Frame):
global sizer2, WorkerThread
def __init__(self):
wx.Frame.__init__(self, None)
pnl = wx.Panel(self)
szr = wx.BoxSizer(wx.VERTICAL)
pnl.SetSizer(szr)
szr2 = sizer2(self, pnl)
szr.Add(szr2, 1, wx.ALL | wx.EXPAND, 10)
log = wx.TextCtrl(pnl, -1,style=wx.TE_MULTILINE, size=(300,-1))
szr.Add(log, 1, wx.ALL, 10)
btn3 = wx.Button(pnl, -1, "Stop")
btn3.Bind(wx.EVT_BUTTON, self.OnStop)
szr.Add(btn3, 0, wx.ALL, 10)
redir = RedirectText(log)
sys.stdout=redir
szr.Fit(self)
self.Show()
def sizer2(self, panel):
sizer = wx.BoxSizer(wx.HORIZONTAL)
btn2 = wx.Button(panel, -1, "OK",)
self.Bind(wx.EVT_BUTTON, self.OnStart, btn2)
sizer.Add(btn2, 0, wx.ALL, 10)
return sizer
def WorkerThread(self):
self.dead = False
while (not self.dead):
for i in range(0,10):
print "printing", i
time.sleep(3)
def OnStart(self, event):
our_thread = threading.Thread(target=WorkerThread(self))
our_thread.start()
def OnStop(self, event):
self.dead = True
class RedirectText(object):
def __init__(self, aWxTextCtrl):
self.out=aWxTextCtrl
def write(self, string):
self.out.WriteText(string)
app = wx.PySimpleApp()
frm = mywxframe()
app.MainLoop()
Upvotes: 0
Views: 748
Reputation: 6306
You've got a couple problems there.
First of all, if you're going to define sizer2
and WorkerThread
as methods, you should use them as methods, not globals.
Next, when you were creating the thread you were calling WorkerThread
and passing its return value (None) to the thread. This is where you were blocking the GUI.
def OnStart(self, event):
our_thread = threading.Thread(target=WorkerThread(self))
our_thread.start()
Instead you should be passing a callable object (WorkerThread
without the ()
) to the thread so it will then be able to call it in the context of the new thread.
Finally, since self.out.WriteText
manipulates a UI object it must be called in the context of the GUI thread. Using wx.CallAfter
is an easy way to do that.
Here is your example updated with these changes:
import wx, sys
import threading
import time
print wx.version()
class mywxframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
pnl = wx.Panel(self)
szr = wx.BoxSizer(wx.VERTICAL)
pnl.SetSizer(szr)
szr2 = self.sizer2(pnl)
szr.Add(szr2, 1, wx.ALL | wx.EXPAND, 10)
log = wx.TextCtrl(pnl, -1,style=wx.TE_MULTILINE, size=(300,-1))
szr.Add(log, 1, wx.ALL, 10)
btn3 = wx.Button(pnl, -1, "Stop")
btn3.Bind(wx.EVT_BUTTON, self.OnStop)
szr.Add(btn3, 0, wx.ALL, 10)
redir = RedirectText(log)
sys.stdout=redir
szr.Fit(self)
self.Show()
def sizer2(self, panel):
sizer = wx.BoxSizer(wx.HORIZONTAL)
btn2 = wx.Button(panel, -1, "OK",)
self.Bind(wx.EVT_BUTTON, self.OnStart, btn2)
sizer.Add(btn2, 0, wx.ALL, 10)
return sizer
def WorkerThread(self):
self.dead = False
while (not self.dead):
for i in range(0,10):
print "printing", i
if self.dead:
break
time.sleep(3)
print 'thread exiting'
def OnStart(self, event):
our_thread = threading.Thread(target=self.WorkerThread)
our_thread.start()
def OnStop(self, event):
self.dead = True
class RedirectText(object):
def __init__(self, aWxTextCtrl):
self.out=aWxTextCtrl
def write(self, string):
wx.CallAfter(self.out.WriteText, string)
app = wx.App()
frm = mywxframe()
app.MainLoop()
Upvotes: 2