最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

分析Python編程時利用wxPython來支持多線程的方法

來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 14:39:51
文檔

分析Python編程時利用wxPython來支持多線程的方法

分析Python編程時利用wxPython來支持多線程的方法:如果你經(jīng)常使用python開發(fā)GUI程序的話,那么就知道,有時你需要很長時間來執(zhí)行一個任務(wù)。當(dāng)然,如果你使用命令行程序來做的話,你回非常驚訝。大部分情況下,這會堵塞GUI的事件循環(huán),用戶會看到程序卡死。如何才能避免這種情況呢?當(dāng)然是利用線程或進程了!本
推薦度:
導(dǎo)讀分析Python編程時利用wxPython來支持多線程的方法:如果你經(jīng)常使用python開發(fā)GUI程序的話,那么就知道,有時你需要很長時間來執(zhí)行一個任務(wù)。當(dāng)然,如果你使用命令行程序來做的話,你回非常驚訝。大部分情況下,這會堵塞GUI的事件循環(huán),用戶會看到程序卡死。如何才能避免這種情況呢?當(dāng)然是利用線程或進程了!本

如果你經(jīng)常使用python開發(fā)GUI程序的話,那么就知道,有時你需要很長時間來執(zhí)行一個任務(wù)。當(dāng)然,如果你使用命令行程序來做的話,你回非常驚訝。大部分情況下,這會堵塞GUI的事件循環(huán),用戶會看到程序卡死。如何才能避免這種情況呢?當(dāng)然是利用線程或進程了!本文,我們將探索如何使用wxPython和theading模塊來實現(xiàn)。

wxpython線程安全方法

wxPython中,有三個“線程安全”的函數(shù)。如果你在更新UI界面時,三個函數(shù)都不使用,那么你可能會遇到奇怪的問題。有時GUI也忙運行挺正常,有時卻會無緣無故的崩潰。因此就需要這三個線程安全的函數(shù):wx.PostEvent, wx.CallAfter和wx.CallLater。據(jù)Robin Dunn(wxPython作者)描述,wx.CallAfter使用了wx.PostEvent來給應(yīng)用程序?qū)ο蟀l(fā)生事件。應(yīng)用程序會有個事件處理程序綁定到事件上,并在收到事件后,執(zhí)行處理程序來做出反應(yīng)。我認為wx.CallLater是在特定時間后調(diào)用了wx.CallAfter函數(shù),已實現(xiàn)規(guī)定時間后發(fā)送事件。

Robin Dunn還指出Python全局解釋鎖 (GIL)也會避免多線程同時執(zhí)行python字節(jié)碼,這會限制程序使用CPU內(nèi)核的數(shù)量。另外,他還說,“wxPython發(fā)布GIL是為了在調(diào)用wx API時,其他線程也可以運行”。換句話說,在多核機器上使用多線程,可能效果會不同。

總之,大概的意思是桑wx函數(shù)中,wx.CallLater是最抽象的線程安全函數(shù), wx.CallAfter次之,wx.PostEvent是最低級的。下面的實例,演示了如何使用wx.CallAfter和wx.PostEvent函數(shù)來更新wxPython程序。

wxPython, Theading, wx.CallAfter and PubSub

wxPython郵件列表中,有些專家會告訴其他人使用wx.CallAfter,并利用PubSub實現(xiàn)wxPython應(yīng)用程序與其他線程進行通訊,我也贊成。如下代碼是具體實現(xiàn):

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()


我們會用time模塊來模擬耗時過程,請隨意將自己的代碼來代替,而在實際項目中,我用來打開Adobe Reader,并將其發(fā)送給打印機。這并沒什么特別的,但我不用線程的話,應(yīng)用程序中的打印按鈕就會在文檔發(fā)送過程中卡住,UI界面也會被掛起,直到文檔發(fā)送完畢。即使一秒,兩秒對用戶來說都有卡的感覺。

總之,讓我們來看看是如何工作的。在我們編寫的Thread類中,我們重寫了run方法。該線程在被實例化時即被啟動,因為我們在__init__方法中有“self.start”代碼。run方法中,我們循環(huán)6次,每次sheep10秒,然后使用wx.CallAfter和PubSub更新UI界面。循環(huán)結(jié)束后,我們發(fā)送結(jié)束消息給應(yīng)用程序,通知用戶。

你會注意到,在我們的代碼中,我們是在按鈕的事件處理程序中啟動的線程。我們還禁用按鈕,這樣就不能開啟多余的線程來。如果我們讓一堆線程跑的話,UI界面就會隨機的顯示“已完成”,而實際卻沒有完成,這就會產(chǎn)生混亂。對用戶來說是一個考驗,你可以顯示線程PID,來區(qū)分線程,你可能要在可以滾動的文本控件中輸出信息,這樣你就能看到各線程的動向。

最后可能就是PubSub接收器和事件的處理程序了:

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()


看我們?nèi)绾螐木€程中提取消息,并用來更新界面?我們還使用接受到數(shù)據(jù)的類型來告訴我們什么顯示給了用戶。很酷吧?現(xiàn)在,我們玩點相對低級一點點,看wx.PostEvent是如何辦的。

wx.PostEvent與線程

下面的代碼是基于wxPython wiki編寫的,這看起來比wx.CallAfter稍微復(fù)雜一下,但我相信我們能理解。

import time 
import wx 
 
from threading import Thread 
 
# Define notification event for thread completion 
EVT_RESULT_ID = wx.NewId() 
 
def EVT_RESULT(win, func): 
 """Define Result Event."""
 win.Connect(-1, -1, EVT_RESULT_ID, func) 
 
class ResultEvent(wx.PyEvent): 
 """Simple event to carry arbitrary result data."""
 def __init__(self, data): 
 """Init Result Event."""
 wx.PyEvent.__init__(self) 
 self.SetEventType(EVT_RESULT_ID) 
 self.data = data 
 
######################################################################## 
class TestThread(Thread): 
 """Test Worker Thread Class."""
 
 #---------------------------------------------------------------------- 
 def __init__(self, wxObject): 
 """Init Worker Thread Class."""
 Thread.__init__(self) 
 self.wxObject = wxObject 
 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) 
 amtOfTime = (i + 1) * 10
 wx.PostEvent(self.wxObject, ResultEvent(amtOfTime)) 
 time.sleep(5) 
 wx.PostEvent(self.wxObject, ResultEvent("Thread finished!")) 
 
######################################################################## 
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) 
 
 # Set up event handler for any worker thread results 
 EVT_RESULT(self, self.updateDisplay) 
 
 #---------------------------------------------------------------------- 
 def onButton(self, event): 
 """
 Runs the thread
 """
 TestThread(self) 
 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()


讓我們先稍微放一放,對我來說,最困擾的事情是第一塊:

# Define notification event for thread completion 
EVT_RESULT_ID = wx.NewId() 
 
def EVT_RESULT(win, func): 
 """Define Result Event."""
 win.Connect(-1, -1, EVT_RESULT_ID, func) 
 
class ResultEvent(wx.PyEvent): 
 """Simple event to carry arbitrary result data."""
 def __init__(self, data): 
 """Init Result Event."""
 wx.PyEvent.__init__(self) 
 self.SetEventType(EVT_RESULT_ID) 
 self.data = data


EVT_RESULT_ID只是一個標識,它將線程與wx.PyEvent和“EVT_RESULT”函數(shù)關(guān)聯(lián)起來,在wxPython代碼中,我們將事件處理函數(shù)與EVT_RESULT進行捆綁,這就可以在線程中使用wx.PostEvent來將事件發(fā)送給自定義的ResultEvent了。

結(jié)束語

希望你已經(jīng)明白在wxPython中基本的多線程技巧。還有其他多種多線程方法這里就不在涉及,如wx.Yield和Queues。幸好有wxPython wiki,它涵蓋了這些話題,因此如果你有興趣可以訪問wiki的主頁,查看這些方法的使用。

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

本文如未解决您的问题请添加抖音号:51dongshi(抖音搜索懂视),直接咨询即可。

文檔

分析Python編程時利用wxPython來支持多線程的方法

分析Python編程時利用wxPython來支持多線程的方法:如果你經(jīng)常使用python開發(fā)GUI程序的話,那么就知道,有時你需要很長時間來執(zhí)行一個任務(wù)。當(dāng)然,如果你使用命令行程序來做的話,你回非常驚訝。大部分情況下,這會堵塞GUI的事件循環(huán),用戶會看到程序卡死。如何才能避免這種情況呢?當(dāng)然是利用線程或進程了!本
推薦度:
  • 熱門焦點
專題
Top

抖音扫码关注

手机端二维码

每天分享百科知识!