wxPython thread that does not block the GUI?
Question: 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()
    


Answer: 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()
    

