關於wxpython多線程研究包括(import Publisher等錯誤研究),wxpythonpublisher

來源:互聯網
上載者:User

關於wxpython多線程研究包括(import Publisher等錯誤研究),wxpythonpublisher

作為一個自動化測試人員,開發基本的應用傳統型程式是必須的!最近在研究wxpython相關知識,目前看到多線程一塊,發現官方文檔介紹說:"線上程中不能修改修改視窗屬性!",但是實際情況是:最近在做一個FQ的簡單APP。我開了2個線程一個線程用於顯示設定進度(用的是第三方host,所以要下載host再覆蓋本地host) ,一個線程處理下載任務,發現第一個線程中動態設定self.gauge(value)可以生效,並沒用到wx.CallAfter!! 需要注意的是,wxpython一次只能處理一個事件,避免同時線上程中啟用用wx.CallAfter,這樣wxpython還是一個一個去執行CallAfter傳遞函數。即:啟用N個線程,N-1個用來幹事情(不要線上程種使用CallAfter),第N個線程調用CallAfter通知主線程更新介面。上面說過了本人嘗試了線上程中直接更改主視窗控制項(不知道是否出現問題)。是可行的!但是,還是建議按照官方說法利用wx.CallAfter,以免發生異常崩潰。

wxpython多線程應用的常景:對於一些複雜任務的處理(比如下載若干檔案),如果把這些代碼全部放在主線程中,等你觸發了這個事件後,應用程式會卡死,而且也觸發不了其他時間,幾乎處於一個假死狀態(雖然他還活著...),這樣的程式別說給別人用了,自己用都會崩潰....,所以就用到多線程,觸發了下載事件後,線上程中完成任務,主線程乾乾嘛幹嘛,但是要給一下提示給視窗程序,比如觸發下載按鈕時,主程式有"下載中..."等字樣,下載完成後,線程通知視窗程序更新狀態為"下載完成...",這樣的互動至少是友善的。

wxpython多線程的使用方法:wxpython開發人員建議的是使用wx.CallAfter+PubSub。CallAfter負者推送時間給主程式,PubSub實現wxPython應用程式與其他線程進行通訊。

其實在wx.CallAfter中直接傳一個主線程的方法,更簡單!但是既然官方這樣說,我們就這樣用!!好吧我們來一個官方的例子,來看看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()

上述代碼我們隨便搜尋下wxpython多線程的應用多會舉官方這個例子,可能是版本的問題(本人用的是wx-2.8),第一句就給來個錯,網上search一下提出這個Publisher這個moudle不存在的問題是普遍的,但是沒有相應的說法,現在就針對這個經典的wxpython程式剖析一下。這段代碼本身是簡單的,由於版本的問題可能出現如下問題。

1.ImportError: cannot import name Publisher

出現這個錯誤是正常的,我們進入wx.lib.pubsub這個模組發現並沒有Publisher這個類,但是我們在wx.lib.pubsub這個模組下面的pub模組發現了Publisher的影子,原來在2.8版本時已經將這個類私人化了,見126行,_publisher = _Publisher(),同時將Publisher的subscribe與sendMessage複製給了subscribe與sendMessage變數見(128,131)行。所以我們這樣引入頭:from wx.lib.pubsub import pub,同時將所有的Publisher()改為pub。
2.TypeError: sendMessage() takes exactly 2 arguments (3 given)

經過1的修改,以為大功告成,運行依然報錯崩潰。出現這個問題大多數人如果硬找問題原因很難找的,我們debug進出,發現他執行個體化的是"C:\Python27\Lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\core\kwargs\publisher.py"這個模組下的Publisher類,仔細一看sendMessage方法果然第二個參數應該傳個字典類型的所有我們將本程式24,32,71行改為wx.CallAfter(pub.sendMessage, "update", msg="Thread finished!"),pub.sendMessage("update", msg=amtOfTime),t = msg。運行一下沒問題~~注意sendMessage的索引值(這裡是msg)與subscribe監聽方法種的接收參數(這裡是msg)要相同,看源碼其實就是msgKwargs這個字典參數來傳遞值的。

其實,這段代碼沒有問題的。我們注意到”C:\Python27\Lib\site-packages\wx-2.8-msw-unicode\wx\lib\pubsub\core\__init__.py" 的43行,其實core這個模組對於Publisher這個類的載入是動態。我們進入policies.py這個模組第10行,發現msgDataProtocol = 'kwargs',原來如此...我們知道core模組下面有2個包一個是arg1,一個是kwargs,我們觀察這2個包publisher模組下Publisher類的sendMessage方法是不一樣的。原來我們這端代碼用的是arg1下的這個Publisher類,好吧,我們將policies.py的第10行改為msgDataProtocol ='arg1',還原代碼運行正確!!kwargs與arg1不同點是kwargs可以傳遞多個參數給主程式(**kwargs),而arg1隻能是傳遞一個參數。

經過以上簡單的論述,我們知道了wxpython是通過wx.CallAfter給主程式推入事件,通過PubSub與主程式傳遞資料。關於wxpython多線程的簡單理解就是這樣了,希望能提供協助。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.