2013-01-22 143 views
3

我對Python很新,所以我很抱歉如果我的問題是業餘的。線程化UDP偵聽器函數

我想做一個簡單的程序,允許用戶看到正在廣播的UDP消息。爲了讓程序不鎖定,我當然需要創建一個單獨的線程來監聽廣播。

在我試圖執行線程之前,程序運行良好。它只是等待UDP消息,並在接收到一個消息時將它們顯示在TextView中。當然,這鎖定了主線程。

這裏是我的代碼:(是的,我知道有我的代碼一些無關緊要的問題,但請忽略它們例如,我從來沒有關閉socket以後我會解決這些問題。)

import socket 
import select 
import sys 
import threading 

try: 
    import pygtk 
    pygtk.require("2.0") 
except: 
    pass 
try: 
    import gtk 
except: 
    print("GTK not available!") 
    sys.exit(1) 


class GUI: 
    def __init__(self): 
     self.builder = gtk.Builder() 
     self.builder.add_from_file("UDPListener.ui") 
     dic = {"on_mainWindow_destroy" : self.quit, 
      "on_listenButton_clicked" : self.startThread, 
     "on_stopListenButton_clicked" : self.stopListening} 
     self.builder.connect_signals(dic) 

     self.listenerThread = threading.Thread(target = self.listen) 

    def startThread(self, widget): 
     self.listenerThread.start() 

    def listen(self): 
     bufferSize = 1024               
     IPAddress = "0.0.0.0" 
     IPAddress: 0.0.0.0 
     portNumber = 50000 
     udpClient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

     textView = self.builder.get_object("messagesReceivedEntry").get_buffer() 
     textView.text = textView.get_text(textView.get_start_iter(),textView.get_end_iter()) 

     textView.set_text("Listening...") 

     try: 
      udpClient.bind((IPAddress, portNumber)) 
      udpClient.setblocking(0) 
     except: 
      print "Cannot connect." 

     try: 
      while True: 
       result = select.select([udpClient],[],[]) 
       message = result[0][0].recv(bufferSize) 
       print message 
     except: 
      print "Cannot receive message" 

    def quit(self, widget): 
     sys.exit(0) 

    def stopListening(self, widget): 
     pass 

gui = GUI() 
gtk.main() 

代碼將運行,一切運行正常,但它從不打印任何消息(我自己廣播它們)。


UPDATE:

由於J.F.塞巴斯蒂安的回答,我改變了我的代碼,包括以下內容:

import socket 
import select 
import sys 
import threading 
import gobject 

try: 
    import pygtk 
    pygtk.require("2.0") 
except: 
    pass 
try: 
    import gtk 
except: 
    print("GTK not available!") 
    sys.exit(1) 

gobject.threads_init() 

class GUI: 
    def __init__(self, UDPClient): 
     self.udpClient = UDPClient 

     self.builder = gtk.Builder() 
     self.builder.add_from_file("TestUDPListenerREORG.ui") 
     dic = {"on_mainWindow_destroy" : self.quit, 
      "on_listenButton_clicked" : self.startThread, 
     "on_stopListenButton_clicked" : self.stopListening} 
     self.builder.connect_signals(dic) 

     self.textView = self.builder.get_object("messagesReceivedEntry").get_buffer() 
     self.textViewText = self.textView.get_text(self.textView.get_start_iter(),self.textView.get_end_iter()) 

     self.listening = False 
     self.listenerThread = threading.Thread(target = self.listen) 

    def startThread(self, widget): 
     self.listenerThread.start() 
     pass 

    def listen(self): 
     try: 
      self.udpClient.connect() 
     except: 
      print "Cannot connect." 
     try: 
      while True: 
       result = select.select([self.udpClient.client],[],[]) 
       message = result[0][0].recv(1024) 
       print message 
       gobject.idle_add(self.updateGUI, message) 
     except: 
      print "Cannot receive message" 

    def updateGUI(self, message): 
     print "updating..." 
     self.textView.set_text(self.textViewText + "\n" + message) 
     print message 

    def quit(self, widget): 
     self.udpClient.close() 
     sys.exit(0) 

    def stopListening(self, widget): 
     pass 



class UDPClient: 
    def __init__(self, IPAddress, portNumber): 
     self.IPAddress = IPAddress 
     self.portNumber = portNumber 
     self.bufferSize = 1024 

     self.client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

    def connect(self): 
     try: 
      self.client.bind((self.IPAddress, self.portNumber)) 
      self.client.setblocking(0) 
     except: print "Cannot connect." 

    def close(self): 
     try: 
      self.client.close() 
     except: 
      pass 

udpClient = UDPClient("0.0.0.0", 50000) 
gui = GUI(udpClient) 
gtk.main() 

到目前爲止,一切都完美地運行。

回答

1

對於多線程gtk應用程序來說,我發現這種模式非常適用,它是在主線程中運行所有GUI代碼。 gtk在後臺線程中運行的唯一方法是gobject.idle_add(),用於調度在主線程中執行的gui回調。

gobject.threads_init()放在模塊頂部的某處,然後再運行其他gtk代碼。

Here's an example for Gtk 3但Gtk 2的原理相同。Another example

另一種方法是使用類似gobject.io_add_watch()的東西,並避免使用多個線程。

+0

太簡單了,謝謝。 –

+0

@ Jordan.CarrollCS:如果您在後臺線程中運行任意GUI代碼,則僅添加'gobject.threads_init()'是不夠的。看看我提供的例子*它足夠了。請閱讀[多線程GTK應用程序 - 第1部分:誤解](否則請閱讀http://blogs.operationaldynamics.com/andrew/software/gnome-desktop/gtk-thread-awareness)。 – jfs

+0

我實施了新的更改。你能解釋爲什麼它「不夠」,並驗證我是否正確實施了你的建議?我非常感謝你的幫助。 –