如果你經常使用python開發GUI程序的話,那么就知道,有時你需要很長時間來執行一個任務。當然,如果你使用命令行程序來做的話,你回非常驚訝。大部分情況下,這會堵塞GUI的事件循環,用戶會看到程序卡死。如何才能避免這種情況呢?當然是利用線程或進程了!本文,我們將探索如何使用wxPython和theading模塊來實現。
wxpython線程安全方法
wxPython中,有三個“線程安全”的函數。如果你在更新UI界面時,三個函數都不使用,那么你可能會遇到奇怪的問題。有時GUI也忙運行挺正常,有時卻會無緣無故的崩潰。因此就需要這三個線程安全的函數:wx.PostEvent, wx.CallAfter和wx.CallLater。據Robin Dunn(wxPython作者)描述,wx.CallAfter使用了wx.PostEvent來給應用程序對象發生事件。應用程序會有個事件處理程序綁定到事件上,并在收到事件后,執行處理程序來做出反應。我認為wx.CallLater是在特定時間后調用了wx.CallAfter函數,已實現規定時間后發送事件。
Robin Dunn還指出Python全局解釋鎖 (GIL)也會避免多線程同時執行python字節碼,這會限制程序使用CPU內核的數量。另外,他還說,“wxPython發布GIL是為了在調用wx API時,其他線程也可以運行”。換句話說,在多核機器上使用多線程,可能效果會不同。
總之,大概的意思是桑wx函數中,wx.CallLater是最抽象的線程安全函數, wx.CallAfter次之,wx.PostEvent是最低級的。下面的實例,演示了如何使用wx.CallAfter和wx.PostEvent函數來更新wxPython程序。
wxPython, Theading, wx.CallAfter and PubSub
wxPython郵件列表中,有些專家會告訴其他人使用wx.CallAfter,并利用PubSub實現wxPython應用程序與其他線程進行通訊,我也贊成。如下代碼是具體實現:
import time import wx from threading import Thread from wx.lib.pubsub import Publisher ######################################################################## class TestThread(Thread): """Test Worker Thread Class.""" #---------------------------------------------------------------------- def __init__(self): """Init Worker Thread Class.""" Thread.__init__(self) self.start() # start the thread #---------------------------------------------------------------------- def run(self): """Run Worker Thread.""" # This is the code executing in the new thread. for i in range(6): time.sleep(10) wx.CallAfter(self.postTime, i) time.sleep(5) wx.CallAfter(Publisher().sendMessage, "update", "Thread finished!") #---------------------------------------------------------------------- def postTime(self, amt): """ Send time to GUI """ amtOfTime = (amt + 1) * 10 Publisher().sendMessage("update", amtOfTime) ######################################################################## class MyForm(wx.Frame): #---------------------------------------------------------------------- def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial") # Add a panel so it looks the correct on all platforms panel = wx.Panel(self, wx.ID_ANY) self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here") self.btn = btn = wx.Button(panel, label="Start Thread") btn.Bind(wx.EVT_BUTTON, self.onButton) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.displayLbl, 0, wx.ALL|wx.CENTER, 5) sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5) panel.SetSizer(sizer) # create a pubsub receiver Publisher().subscribe(self.updateDisplay, "update") #---------------------------------------------------------------------- def onButton(self, event): """ Runs the thread """ TestThread() self.displayLbl.SetLabel("Thread started!") btn = event.GetEventObject() btn.Disable() #---------------------------------------------------------------------- def updateDisplay(self, msg): """ Receives data from thread and updates the display """ t = msg.data if isinstance(t, int): self.displayLbl.SetLabel("Time since thread started: %s seconds" % t) else: self.displayLbl.SetLabel("%s" % t) self.btn.Enable() #---------------------------------------------------------------------- # Run the program if __name__ == "__main__": app = wx.PySimpleApp() frame = MyForm().Show() app.MainLoop()
新聞熱點
疑難解答