一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - 分析Python編程時利用wxPython來支持多線程的方法

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

2020-05-31 10:39腳本之家 Python

這篇文章主要介紹了Python編程時利用wxPython來支持多線程的方法,本文主要以開發GUI程序時做線程通訊作為一個示例來講解,需要的朋友可以參考下

如果你經常使用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應用程序與其他線程進行通訊,我也贊成。如下代碼是具體實現:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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,并將其發送給打印機。這并沒什么特別的,但我不用線程的話,應用程序中的打印按鈕就會在文檔發送過程中卡住,UI界面也會被掛起,直到文檔發送完畢。即使一秒,兩秒對用戶來說都有卡的感覺。

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

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

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

?
1
2
3
4
5
6
7
8
9
10
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()


看我們如何從線程中提取消息,并用來更新界面?我們還使用接受到數據的類型來告訴我們什么顯示給了用戶。很酷吧?現在,我們玩點相對低級一點點,看wx.PostEvent是如何辦的。

wx.PostEvent與線程

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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()


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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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”函數關聯起來,在wxPython代碼中,我們將事件處理函數與EVT_RESULT進行捆綁,這就可以在線程中使用wx.PostEvent來將事件發送給自定義的ResultEvent了。

結束語

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 冰雪奇缘1完整版免费观看 变形金刚第一部 | 国产成人精品一区二三区2022 | 视频免费观看在线播放高清 | 成人天堂入口网站 | 精品一区二区三区免费毛片 | 久久五月综合婷婷中文云霸高清 | 456亚洲人成高清在线 | 精品国产乱码久久久久久软件 | 成人精品第一区二区三区 | ysl蜜桃色成人麻豆 youwu在线影院 | 欧美bbxx| 日本视频免费在线观看 | 日日摸日日碰夜夜爽97纠 | 国产农村一一级特黄毛片 | 国产精品免费综合一区视频 | 欧美一区二区三区高清不卡tv | free chinese麻豆 | 香蕉久久夜色精品国产尤物 | 亚洲精品国产精品麻豆99 | 四影虎库最新2021 | 四虎成人www国产精品 | 9re视频这里只有精品 | fc2免费人成在线 | 亚洲v日韩v欧美在线观看 | 亚洲啊v天堂 | 给我免费的视频在线观看 | 国产福利不卡一区二区三区 | 好大好硬好深好爽想要之黄蓉 | 91天堂在线| 日本在线视频免费观看 | 国产精品午夜性视频网站 | 日本国产一区二区三区 | 日本不卡高清免费v日本 | 爱情岛论坛亚洲品质自拍视频 | 精品欧美日韩一区二区三区 | 80日本xxxxxxxxx96| 午夜秀场在线观看 | 性色香蕉AV久久久天天网 | 高清视频免费 | 色8| 国产成人精品一区二区阿娇陈冠希 |