Ткинтер. После того как метод заморозить окно?

У меня есть простой клиент чата, с которым я пытался работатьTkinter в качестве интерфейса. Моя проблема в том, что при запускеmainloop с.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('', 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 данные блокируются всякий раз, когда их вызывают, и окно замораживается в течение этого времени.

Ответы на вопрос(2)

Решение Вопроса

Я узнал об использованииselect сделать системные вызовы, чтобы проверить, готов ли файл сокета к чтению.

Как установить таймаут на питонеметод сокета 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('', 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

after выполняет функцию обратного вызова по истечении заданного времени; однако этот метод также выполняется в основном потоке. Так что если есть операция, которая занимает больше времени, чем обычно (в этом случае блокировкаrecvfrom), GUI будет не отвечать до тех пор, пока не будет выполнен полный обратный вызов.

Чтобы решить эту проблему, общий рецепт - создать новый поток и связать его с вашим кодом Tkinter с помощью синхронизированного объекта, такого какQueue, Таким образом, вы помещаете данные в очередь при получении их из сокета, а затем периодически проверяете основной поток внутриafter Перезвоните.

Это вопрос, ответ на который можно адаптировать для использования того же подхода:Tkinter: Как использовать потоки, чтобы предотвратить основной цикл событий от "замораживания»

 tijko11 июн. 2013 г., 17:30
я пошел сselect здесь, но яЯ собираюсь сделать версию, которая используетQueue модуль с потоками тоже.
 tijko10 июн. 2013 г., 04:31
спасибо за ответ и ссылку. Я'Я сейчас читаю на неблокирующих сокетах.
 JDM28 авг. 2018 г., 17:15
Чтобы быть совершенно ясно: этоне тоafter() методсам это замораживает вещи, этос обратным вызовом. Например. Вы можете выполнитьafter() с задержкой (скажем) 10 секунд, и в течение этих 10 секунд все будет продолжаться в обычном режиме. Но если обратный вызов вызывает блокировку или потребляет избыточные ресурсы,затем графический интерфейс будет зависать (Просто хотел отметить это, поскольку я был временно сбит с толку, думая, что если я поставлюafter() позвоните в моем коде, никаких строк после этогоafter() будет выполняться до истечения времени.)

Ваш ответ на вопрос