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: {}