3

我对 Python 很陌生,所以如果我的问题是业余的,我深表歉意。

我正在尝试制作一个简单的程序,允许用户查看正在广播的 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() 

代码将运行并且一切正常,但它从不打印任何消息(我自己广播它们)。


更新:

感谢 JF Sebastian 的回答,我修改了代码以包含以下内容:

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

到目前为止,一切运行完美。

4

1 回答 1

1

我发现适用于多线程 gtk 应用程序的模型是在主线程中运行所有 GUI 代码。gtk 在后台线程中运行的唯一方法是gobject.idle_add()安排 gui 回调在主线程中执行。

在运行任何其他 gtk 代码之前,将gobject.threads_init()其放在模块顶部的某个位置。

这是 Gtk 3 的示例,但原理与 Gtk 2 相同。另一个示例

另一种方法是使用类似的东西gobject.io_add_watch()并避免使用多个线程。

于 2013-01-22T16:02:57.087 回答