18.6 创建一个多线程的wxPython应用程序
在大多数的GUI应用程序中,在应用程序的后台中长期执行一个处理过程而不干涉用户与应用程序的其它部分的交互是有好处的。允许后台处理的机制通常是产生一个线程并在该线程中长期执行一个处理过程。对于wxPython的多线程,在这一节我们有两点需要特别说明。
最重要的一点是,GUI的操作必须发生在主线程或应用程序的主循环所处的地方中。在一个单独的线程中执行GUI操作对于无法预知的程序崩溃和调试来说是一个好的办法。基于技术方面的原因,如许多Unix的GUI库不是线程安全性的,以及在微软Windows下UI对象的创建问题,wxPython没有设计它自己的发生在多线程中的事件,所以我们建议你也不要尝试。
上面的禁令包括与屏幕交互的任何项目,尤其包括wx.Bitmap对象。
对于wxPython应用程序,关于所有UI的更新,后台线程只负责发送消息给UI线程,而不关心GUI的更新。幸运的是,wxPython没有强制限定你能够有的后台线程的数量。
在这一节,我们将关注几个wxPython中实现多线程的方法。最常用的技术是使用wx.CallAfter()函数,一会我们会讨论它。然后,我们将看一看如何使用Python的队列对象来设置一个并行事件队列。最后,我们将讨论如何为多线程开发一个定制的解决方案。
18.6.1 使用全局函数wx.CallAfter()
例18.5显示了一个使用线程的例子,它使用了wxPython的全局函数wx.CallAfter(),该函数是传递消息给你的主线程的最容易的方法。wx.CallAfter()使得主线程在当前的事件处理完成后,可以对一个不同的线程调用一个函数。传递给wx.CallAfter()的函数对象总是在主线程中被执行。
图18.4显示了多线程窗口的运行结果。
图18.4
http://www.pythontik.com/picture/blogpic/w18.4.gifin Action (多线程)" />
例18.5显示了产生图18.4的代码
例18.5 使用wx.CallAfter()来传递消息给主线程的一个线程例子
#-*- encoding:UTF-8 -*-
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,20)
self.messageDelay = 0.1 + 2.0 * random.random()
def stop(self):
self.timeToQuit.set()
def run(self):#运行一个线程
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)
for i in range(1, self.messageCount+1):
self.timeToQuit.wait(self.messageDelay)
if self.timeToQuit.isSet():
break
msg = "Message %d