2013-01-22 82 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

我实施了新的更改。你能解释为什么它“不够”,并验证我是否正确实施了你的建议?我非常感谢你的帮助。 –