wxPython使用delayedresult进行耗时处理

delayedresult使用背景介绍

在进行wxPython GUI画面编程时,如直接在画面主线程进行大量耗时计算处理,就会造成画面假死,不能响应用户输入。

使用wxPython的delayedresult模块,可轻松解决该问题,甚至都不需要了解相关线程处理机制,即可方便的把耗时处理放到单独的线程中,处理结束后把结果返回GUI画面主线程,并调用预先定义的相关处理,进行画面更新等。

为了演示delayedresult的使用情况,先新建一TestWindow框架,doSomeThing()是我们模拟进行大量耗时处理的函数。 

 1 import wx
 2 from wx.lib.delayedresult import startWorker
 3 import threading
 4 
 5 class TestWindow(wx.Frame):
 6     def __init__(self, title='Test Window'):
 7         self.app = wx.App(False)
 8         wx.Frame.__init__(self, None, -1, title)
 9         panel = wx.Panel(self)
10         self.btnBegin = wx.Button(panel, -1, label='Begin')
11         self.Bind(wx.EVT_BUTTON, self.handleButton, self.btnBegin)
12         self.txtCtrl = wx.TextCtrl(panel, style=wx.TE_READONLY, size=(300, -1))
13         vsizer = wx.BoxSizer(wx.VERTICAL)
14         vsizer.Add(self.btnBegin, 0, wx.ALL, 5)
15         vsizer.Add(self.txtCtrl, 0, wx.ALL, 5)
16         panel.SetSizer(vsizer)
17         vsizer.SetSizeHints(self)
18         self.Show()
19 
20     #处理Begin按钮事件
21     def handleButton(self, event):
22         self.workFunction()
23 
24     #开始执行耗时处理,有继承类实现
25     def workFunction(self, *args, **kwargs):
26         print'In workFunction(), Thread=', threading.currentThread().name
27         print '\t*args:', args
28         print '\t**kwargs:', kwargs
29         
30         self.btnBegin.Enable(False)
31 
32     #耗时处理处理完成后,调用该函数执行画面更新显示,由继承类实现
33     def consumer(self, delayedResult, *args, **kwargs):
34         print 'In consumer(), Thread=', threading.currentThread().name
35         print '\tdelayedResult:', delayedResult
36         print '\t*args:', args
37         print '\t**kwargs:', kwargs
38 
39         self.btnBegin.Enable(True)
40 
41     #模拟进行耗时处理并返回处理结果,给继承类使用
42     def doSomeThing(self, *args, **kwargs):
43         print'In doSomeThing(), Thread=', threading.currentThread().name
44         print '\t*args:', args
45         print '\t**kwargs:', kwargs
46         
47         count = 0
48         while count < 10**8:
49             count += 1
50             
51         return count

运行界面:

 

先看看如在GUI画面主线程进行doSomeThing()处理的

 1 class DoSomeThingInGUI(TestWindow):
 2     def __init__(self):
 3         TestWindow.__init__(self, 'Do Something In GUI Thread')
 4         self.app.MainLoop()
 5 
 6     #执行doSomeThing(),并在执行完成后,主动调用consumer()更新画面显示
 7     def workFunction(self, *args, **kwargs):
 8         TestWindow.workFunction(self, args, kwargs)
 9         var = self.doSomeThing()
10         self.consumer(var)
11 
12     def consumer(self, delayedResult, *args, **kwargs):
13         TestWindow.consumer(self, delayedResult, args, kwargs)
14         self.txtCtrl.SetValue(str(delayedResult))
15         
16 if __name__ == '__main__':
17     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后的处理情况:

 1 class DoSomeThingInSeperateThread(TestWindow):
 2     def __init__(self):
 3         TestWindow.__init__(self, 'Do Something In Seperate Thread')
 4         self.jobId = 100
 5         self.app.MainLoop()
 6 
 7     #调用wx.lib.delayedresult.startWorker,把函数处理放到单独的线程中去完成。
 8     #完成后会自动调用consumer进行画面更新处理
 9     #startWorker函数的各参数接下来分析
10     def workFunction(self, *args, **kwargs):
11         TestWindow.workFunction(self, args, kwargs)
12         startWorker(self.consumer, self.doSomeThing, jobID=self.jobId)
13 
14     #第一参数要为DelayedResult类型,即包含处理结果或异常信息,调用get接口取得。
15     def consumer(self, delayedResult, *args, **kwargs):
16         TestWindow.consumer(self, delayedResult, args, kwargs)
17         assert(self.jobId == delayedResult.getJobID())
18         try:
19             var = delayedResult.get()
20         except Exception, e:
21             print 'Result for job %s raised exception:%s' %(delayedResult.getJobID, e)
22                 
23         self.txtCtrl.SetValue(str(var))
24         
25 if __name__ == '__main__':
26     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: {}

 

startWorker函数介绍

该函数创建独立线程(Producer线程)执行workerFn(*wargs, **wkwargs),并将其结果发送给运行在Main线程的consumer(*cargs, ckwargs)。

jobID即为创建的Producer线程的名字,也可在consumer中判断是那个Producer线程。

详细可参考 help(wx.lib.delayedresult)

startWorker(consumer,
            workerFn,
            cargs=(), ckwargs={},
            wargs=(), wkwargs={},
            jobID=None,
            group=None,
            daemon=False,
            sendReturn=True,
            senderArg=None)

注意事项:

1)sendResurn为True的情况下,workFn执行结束后才执行consumer处理。

2)consumer第一参数为DelayedResult,可使用其结果get()取得workFn中返回的结果,getJobID()用于返回参数jobID或者None(jobID未指定时)

3)cargs, ckwargs传递给consumer;wargs, wkargs传递给workerFn

posted @ 2012-12-14 11:58  zhaoqfeng  阅读(531)  评论(0编辑  收藏  举报