Working with the global function wxCall After

Listing 18.5 displays a sample use of threads, using the wxPython global function wx.CallAfter(), which is the easiest way to pass messages to your main thread. Simply, wx.CallAfter() allows the thread to call a function on a different thread when the current event processing completes. The functional object passed to wx.CallAfter() will always be executed in the main thread.

Figure 18.4 displays the resulting multithreaded frame.

C Multi-threaded GUI

1-lnlxF

Start a thread;

Stop all threads

Worker Threads: 4

Message 1 from thread 1

Thread 3 iterating times with a delay of 0.5750 ¡Message 2 from thread 1 Message 3 from thread 1 ¡Message 1 from thread 3

Thread 4 iterating 19 times with a delay of 2.0030

¡Message 4 from thread 1

Message 2 from thread 3

Message 1 from thread 2

Message 5 from thread 1

Message 1 from thread 1

Thread 3 iterating times with a delay of 0.5750 ¡Message 2 from thread 1 Message 3 from thread 1 ¡Message 1 from thread 3

Thread 4 iterating 19 times with a delay of 2.0030

¡Message 4 from thread 1

Message 2 from thread 3

Message 1 from thread 2

Message 5 from thread 1

Figure 18.4 Multithreading in the background

Creating a multithreaded wxPython application Listing 18.5 displays the code to produce this threading example.

Listing 18.5 A threaded example using wx.CallAfter() to pass messages to the main thread import wx import threading import random class WorkerThread(threading.Thread):

This just simulates some long-running task that periodically sends a message to the GUI thread.

def _init_(self, threadNum, window):

threading.Thread.__init__(self) self.threadNum = threadNum self.window = window self.timeToQuit = threading.Event() self.timeToQuit.clear()

self.messageCount = random.randint(10,2 0) self.messageDelay = 0.1 + 2.0 * random.random()

def stop(self):

self.timeToQuit.set()

def run(self): <-1 Running the thread msg = "Thread %d iterating %d times with a delay of %1.4f\n" \ % (self.threadNum, self.messageCount, self.messageDelay) wx.CallAfter(self.window.LogMessage, msg) <— Posting a call to

LogMessage for i in range(1, self.messageCount+1):

self.timeToQuit.wait(self.messageDelay) if self.timeToQuit.isSet(): break msg = "Message %d from thread %d\n" % (i, self.threadNum) wx.CallAfter(self.window.LogMessage, msg) <■ else:

wx.CallAfter(self.window.ThreadFinished, self)

Posting a call to LogMessage

Posting a call to class MyFrame(wx>Frame): ThreadFinished def _init_(self):

wx.Frame._init_(self, None, title="Multi-threaded GUI")

panel = wx.Panel(self)

startBtn = wx.Button(panel, -1, "Start a thread") stopBtn = wx.Button(panel, -1, "Stop all threads") self.tc = wx.StaticText(panel, -1, "Worker Threads:

self.log = wx.TextCtrl(panel, -1, "", style=wx.TE_RICH|wx.TE_MULTILINE)

inner = wx.BoxSizer(wx.HORIZONTAL) inner.Add(startBtn, 0, wx.RIGHT, 15) inner.Add(stopBtn, 0, wx.RIGHT, 15) inner.Add(self.tc, 0, wx.ALIGN_CENTER_VERTICAL) main = wx.BoxSizer(wx.VERTICAL) main.Add(inner, 0, wx.ALL, 5) main.Add(self.log, 1, wx.EXPAND|wx.ALL, 5) panel.SetSizer(main)

self.Bind(wx.EVT_BUTTON, self.Bind(wx.EVT_BUTTON, self.Bind(wx.EVT_CLOSE, self.OnStartButton, startBtn) self.OnStopButton, stopBtn) self.OnCloseWindow)

self.UpdateCount()

def OnStartButton(self, evt): self.count += 1

thread = WorkerThread(self.count, self)

self.threads.append(thread)

self.UpdateCount()

thread.start() <— Starting the thread

Creating a thread def OnStopButton(self, evt): self.StopThreads() self.UpdateCount()

def OnCloseWindow(self, evt): self.StopThreads() self.Destroy()

def StopThreads(self): <— Removing threads from pool while self.threads:

thread = self.threads[0] thread.stop()

self.threads.remove(thread)

def UpdateCount(self):

self.tc.SetLabel("Worker Threads: %d" % len(self.threads))

def LogMessage(self, msg): <— Logging a message self.log.AppendText(msg)

def ThreadFinished(self, thread): <— Removing a thread self.threads.remove(thread) self.UpdateCount()

app = wx.PySimpleApp() frm = MyFrame() frm.Show() app.MainLoop()

This listing uses Python's threading module. While the C + + wxWidgets toolkit does offer threading tools, we recommend you stick with the Python native ones. They are much simpler.

This code passes methods to the main thread using wx.CallAfter(func, *args). Functionally, this posts an event to the main thread, after which the event is processed in the normal manner, and triggers the call func(*args). So in this case, the thread calls LogMessage() during its lifecycle, and ThreadFinished() before it is through.

Was this article helpful?

0 -1

Responses

  • Semrawit
    How to define function global in wxpthon?
    8 years ago

Post a comment