6

我有一个简单的聊天客户端,我试图将其Tkinter用作界面。我的问题是,在为聊天输入/输出运行mainloopwith时.after,窗口会冻结并阻塞,直到收到另一条消息。

class Client(Frame):

    def __init__(self, **kwargs):
        Frame.__init__(self, Tk())
        self.pack()

        self.lb = Listbox(self, width=100, height=30)
        self.lb.pack()

        self.show_data = self.lb.after(1000, self.chat_handle)

        self.entry = Entry(self)
        self.entry.bind('<Return>', self.input_handle)
        self.entry.pack(side=BOTTOM, fill=X)

    def input_handle(self, event):
        msg = self.entry.get()
        self.entry.delete(0, 'end')
        new_msg = 'privmsg %s :' % self.channel + msg + '\r\n'
        self.client.sendall(new_msg)
        self.lb.insert(END, self.nick + ' | ' + msg)

    def chat_handle(self):
        try:
            self.data = self.client.recvfrom(1024)
        except socket.error:
            self.lb.insert(END, "Bad Connection!")
            return
        if self.data and len(self.data[0]) > 0:
            self.lb.insert(END, self.data[0])
        elif self.data and len(self.data[0]) == 0:
            self.lb.insert(END, "Connection Dropped!")
            return
        self.show_data = self.lb.after(1000, self.chat_handle)

此代码块已缩短,但显示了所涉及的相关部分。小Entry部件将在被调用时长时间无响应,.after并且在收到消息之前不会响应。

Entry小部件再次响应时,输入字段包含输入的所有数据,但是在“冻结”期间我不会看到更改。小部件也是如此Listbox

如果有人能阐明为什么会这样,或者指出我是否错过了在这里使用一种方法,将不胜感激。

编辑:经过更多研究,socket在这段时间里,每当它被调用并且窗口被冻结时,它看起来就像数据被阻塞。

4

2 回答 2

6

after在给定时间后执行回调函数;但是,此方法也在主线程中运行。因此,如果有一个操作比平时花费更多时间(在这种情况下,阻塞recvfrom),GUI 将无响应,直到执行完成回调。

为了解决这个问题,一个常见的方法是生成一个新线程并使用同步对象(如Queue. 因此,当您从套接字接收数据时,您将数据放入队列中,然后在after回调内部的主线程中定期检查。

这是一个可以调整其答案以使用相同方法的问题:Tkinter:如何使用线程来防止主事件循环“冻结”

于 2013-06-10T02:24:15.500 回答
0

我学习了使用selectto 进行系统调用来检查套接字文件是否准备好被读取。

如何在 python 的 socket recv 方法上设置超时?

class Client(Frame):

    def __init__(self, **kwargs):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.connect(("host", port))
        self.client.setblocking(0)
        Frame.__init__(self, Tk())
        self.pack()

        self.lb = Listbox(self, width=100, height=30)
        self.lb.pack()

        self.show_data = self.lb.after(1000, self.chat_handle)

        self.entry = Entry(self)
        self.entry.bind('<Return>', self.input_handle)
        self.entry.pack(side=BOTTOM, fill=X)

    def input_handle(self, event):
        msg = self.entry.get()
        self.entry.delete(0, 'end')
        new_msg = 'privmsg %s :' % self.channel + msg + '\r\n'
        self.client.sendall(new_msg)
        self.lb.insert(END, self.nick + ' | ' + msg)

    def chat_handle(self):
        socket_data = select.select([self.client], [], [], 0.3) # set timeout on last arg
        if socket_data[0]:
            try:
                self.data = self.client.recvfrom(1024)
            except socket.error:
                self.lb.insert(END, "Bad Connection!")
                return
            if self.data and len(self.data[0]) > 0:
                self.lb.insert(END, self.data[0])
            elif self.data and len(self.data[0]) == 0:
                self.lb.insert(END, "Connection Dropped!")
                return
            self.show_data = self.lb.after(1000, self.chat_hand
于 2013-06-11T15:28:35.670 回答