delayedresult使用背景介绍
在进行wxPython GUI画面编程时,如直接在画面主线程进行大量耗时计算处理,就会造成画面假死,不能响应用户输入。
使用wxPython的delayedresult模块,可轻松解决该问题,甚至都不需要了解相关线程处理机制,即可方便的把耗时处理放到单独的线程中,处理结束后把结果返回GUI画面主线程,并调用预先定义的相关处理,进行画面更新等。
为了演示delayedresult的使用情况,先新建一TestWindow框架,doSomeThing()是我们模拟进行大量耗时处理的函数。
import wx
from wx.lib.delayedresult import startWorker
import threading
class TestWindow(wx.Frame):
def __init__(self, title="Test Window"):
self.app = wx.App(False)
wx.Frame.__init__(self, None, -1, title)
panel = wx.Panel(self)
self.btnBegin = wx.Button(panel, -1, label='Begin')
self.Bind(wx.EVT_BUTTON, self.handleButton, self.btnBegin)
self.txtCtrl = wx.TextCtrl(panel, style=wx.TE_READONLY, size=(300, -1))
vsizer = wx.BoxSizer(wx.VERTICAL)
vsizer.Add(self.btnBegin, 0, wx.ALL, 5)
vsizer.Add(self.txtCtrl, 0, wx.ALL, 5)
panel.SetSizer(vsizer)
vsizer.SetSizeHints(self)
self.Show()
#处理Begin按钮事件
def handleButton(self, event):
self.workFunction()
#开始执行耗时处理,有继承类实现
def workFunction(self, *args, **kwargs):
print'In workFunction(), Thread=', threading.currentThread().name
print '\t*args:', args
print '\t**kwargs:', kwargs
self.btnBegin.Enable(False)
#耗时处理处理完成后,调用该函数执行画面更新显示,由继承类实现
def consumer(self, delayedResult, *args, **kwargs):
print 'In consumer(), Thread=', threading.currentThread().name
print '\tdelayedResult:', delayedResult
print '\t*args:', args
print '\t**kwargs:', kwargs
self.btnBegin.Enable(True)
#模拟进行耗时处理并返回处理结果,给继承类使用
def doSomeThing(self, *args, **kwargs):
print'In doSomeThing(), Thread=', threading.currentThread().name
print '\t*args:', args
print '\t**kwargs:', kwargs
count = 0
while count < 10**8:
count += 1
return count先看看如在GUI画面主线程进行doSomeThing()处理的
class DoSomeThingInGUI(TestWindow):
def __init__(self):
TestWindow.__init__(self, 'Do Something In GUI Thread')
self.app.MainLoop()
#执行doSomeThing(),并在执行完成后,主动调用consumer()更新画面显示
def workFunction(self, *args, **kwargs):
TestWindow.workFunction(self, args, kwargs)
var = self.doSomeThing()
self.consumer(var)
def consumer(self, delayedResult, *args, **kwargs):
TestWindow.consumer(self, delayedResult, args, kwargs)
self.txtCtrl.SetValue(str(delayedResult))
if __name__ == '__main__':
win = DoSomeThingInGUI()执行后,点Begin开始后,直到处理完成画面窗口无法移动。
由以下log输出可以知道,处理都是在GUI画面主线程中完成的。
In workFunction(), Thread= MainThread
*args: ((), {})
**kwargs: {}
In doSomeThing(), Thread= MainThread
*args: ()
**kwargs: {}
In consumer(), Thread= MainThread
delayedResult: 100000000
*args: ((), {})
**kwargs: {}
使用wx.lib.delayedresult后的处理情况:
class DoSomeThingInSeperateThread(TestWindow):
def __init__(self):
TestWindow.__init__(self, 'Do Something In Seperate Thread')
self.jobId = 100
self.app.MainLoop()
#调用wx.lib.delayedresult.startWorker,把函数处理放到单独的线程中去完成。
#完成后会自动调用consumer进行画面更新处理
#startWorker函数的各参数接下来分析
def workFunction(self, *args, **kwargs):
TestWindow.workFunction(self, args, kwargs)
startWorker(self.consumer, self.doSomeThing, jobID=self.jobId)
#第一参数要为DelayedResult类型,即包含处理结果或异常信息,调用get接口取得。
def consumer(self, delayedResult, *args, **kwargs):
TestWindow.consumer(self, delayedResult, args, kwargs)
assert(self.jobId == delayedResult.getJobID())
try:
var = delayedResult.get()
except Exception, e:
print 'Result for job %s raised exception:%s' %(delayedResult.getJobID, e)
self.txtCtrl.SetValue(str(var))
if __name__ == '__main__':
win = DoSomeThingInSeperateThread()执行后,点Begin开始后,窗口移动不受影响,也就是说处理是异步的。
由以下log输出可以知道,耗时处理doSomeThing是在独立线程运行的。
处理完成后的画面更新处理consumer还是在GUI画面主线程完成的。
In workFunction(), Thread= MainThread
*args: ((), {})
**kwargs: {}
In doSomeThing(), Thread= 100
*args: ()
**kwargs: {}
In consumer(), Thread= MainThread
delayedResult: <wx.lib.delayedresult.DelayedResult instance at 0x02FFE828>
*args: ((), {})
**kwargs: {}