Параметр tkinter зависает при нажатии, если after_idle вызывается в другом месте программы - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть графический интерфейс, который последовательно обрабатывает сообщения по мере их поступления и добавления в очередь несколькими веб-сокетами. Эта функция настраивает обратный вызов для себя, используя after_idle для увековечения процесса, например:

 def process_queue(self, flush=False):
        '''
        Check for new messages in the queue periodically.
        Sets up callback to itself to perpetuate the process.
        '''
        if flush:
            while not self.queue.empty():
                self.get_msg()
        else:
            self.get_msg()
            self.master.after_idle(self.process_queue)

У меня есть OptionMenu виджет в графическом интерфейсе, который зависает и вызывает сбой программы при нажатии:

self.plotind = tk.StringVar()
self.plotind.set('MACD')
options = ['MACD']
self.indopts = tk.OptionMenu(self.analysis_frame, self.plotind, *[option for option in options])
self.indopts.grid(row=1, column=1)

Если я изменю after_idle() на after(), он будет работать нормально.

Я предполагаю, что это потому, что нажатие на OptionMenu фактически устанавливает свой собственный вызов after_idle(), чтобы открыть меню, которое затем конкурирует с тем, что у меня есть в process_queue().

Я, конечно, могу использовать after() в своей функции, если это необходимо - она ​​может быть неоптимально быстрой при обработке очереди, но это не конец света. Но есть ли более изящный способ справиться с этим? Конечно, большинство графических интерфейсов должны быть в состоянии справиться с вызовом after_idle() где-нибудь, когда существует OptionMenu?

1 Ответ

0 голосов
/ 06 сентября 2018

Вообще говоря, вы не должны вызывать after_idle из функции, вызываемой через after_idle.

Вот почему:

Как только tkinter начнет обрабатывать очередь ожидания, он не остановится, пока очередь не станет пустой. Если в очереди есть один элемент, tkinter снимает этот элемент и вызывает функцию. Если эта функция добавляет что-то в очередь, очередь больше не пуста, поэтому tkinter обрабатывает новый элемент. Если этот новый элемент помещает что-то в очередь ожидания, то tkinter будет обрабатывать это и так далее. Очередь никогда не становится пустой, и tkinter никогда не предоставляется возможность делать что-либо еще, кроме обслуживания этой очереди.

Распространенным решением является использование after дважды, чтобы очередь могла опустеть, и, таким образом, tkinter мог обрабатывать другие неактивные события.

Например, вместо этого:

self.master.after_idle(self.process_queue)

... сделать это:

self.master.after_idle(self.master.after, 1, self.process_queue)

Это создает крошечное окно, в котором очередь становится пустой, что позволяет tkinter обрабатывать другие «неактивные» события, такие как запросы на перерисовку экрана перед повторным вызовом self.process_queue.

...