user3403418
user3403418

Reputation: 11

wxPython thread that does not block the GUI?

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

Answers (1)

RobinDunn
RobinDunn

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

Related Questions