Очистка очереди в Tkinter с помощью after ()

У меня есть приложение на tkinter, которое одновременно принимает сообщения из нескольких веб-сайтов. Чтобы обрабатывать их последовательно и избегать состояний гонки, я добавляю каждое сообщение в Queue по мере их поступления и обрабатываю их последовательно, например:

def queue_msg(self, msg):
    self.queue.put(msg)

def process_queue(self):
    try:
        msg = self.queue.get(0)
    except Queue.Empty:
        pass
    else:
        ...do stuff...
    self.master.after(10, self.process_queue)

Функция self.process_queue() просто продолжает вызывать себя, пока приложение работает, поэтому все сообщения обрабатываются в фоновом режиме. Все идет нормально.

У меня есть несколько операций в коде, для которых мне нужно приостановить обработку новых сообщений. Когда это происходит, я бы хотел, чтобы приложение 1) прекратило добавление новых сообщений в очередь, 2) обработало все сообщения, которые уже находятся в очереди, 3) выполнило операцию и 4) снова начало добавлять сообщения в очередь. Отбрасывание сообщений, которые приходят в промежуточный период, нормально.

Моя первая попытка заключалась в том, чтобы определить переменную класса self.pause, установить для этих операций значение True и просто проверить это условие при получении новых сообщений:

def queue_msg(self, msg):
    if not self.pause:
        self.queue.put(msg)

def process_queue(self):
    try:
        msg = self.queue.get(0)
    except Queue.Empty:
        pass
    else:
        ...do stuff...
    self.master.after(10, self.process_queue)

def sensitive_operation(self):
    self.pause = True
    while self.queue.qsize() > 0:
        continue
    ....do stuff....
    self.pause = False

Но это не удается. Либо qsize () уже равен 0, и в этом случае по какой-то причине self.pause обычно остается False после выхода из sensitive_operation(), либо он застревает в бесконечном цикле на continue.

По-видимому, я что-то неправильно понимаю в методе after(), потому что явно изменения в self.pause не распространяются последовательно через различные обратные вызовы. Может ли кто-нибудь помочь мне понять, что здесь происходит за кулисами, и, возможно, предложить лучший способ приостановить и очистить очередь при необходимости?

Вот минимальный рабочий пример. Чтобы имитировать поступление сообщений из веб-сокета, у меня есть функция process_message (), которая случайным образом добавляет сообщения в очередь по мере их обработки. Нажатие кнопки pause будет работать большую часть времени, но если вы размять ее несколько раз, в конечном итоге она зависнет на continue:

import Tkinter as tk
import ttk
import Queue
import numpy as np

class App(tk.Frame):
    def __init__(self, parent):
        parent.deiconify()
        self.parent = parent
        self.queue = Queue.Queue()
        self.pause = False
        self.parent.after(1000, self.process_message)
        self.pause_button = tk.Button(parent, text='Pause', command = self.pause_and_empty)
        self.pause_button.pack()

    def queue_message(self, message):
        if not self.pause:
            self.queue.put(message)

    def process_message(self):
        try:
            message = self.queue.get(0)
        except Queue.Empty:
            pass
        else:
            print message
        rand = np.random.rand()
        if rand < 0.2:
            self.queue_message('test message: {0}'.format(rand))
        self.parent.after(10, self.process_message)

    def pause_and_empty(self):
        self.pause = True
        while self.queue.qsize() > 0:
            print 'stuck!'
            continue
        print 'made it!'
        self.pause = False

def main():
    root = tk.Tk()
    root.withdraw()
    app = App(root)
    root.mainloop()

if __name__= = "__main__":
    main()

Обновлено еще раз: исходя из первого ответа, следующая настройка функции pause_and_empty, похоже, помогает:

def pause_and_empty(self):
        self.pause = True
        if self.queue.qsize() > 0:
            self.parent.after(10, self.pause_and_empty)
        print 'made it!'

Для этого явно нужен минимальный воспроизводимый пример.

Nae 19.03.2018 20:12

Честно говоря, я над этим поработаю.

KBriggs 19.03.2018 20:15

@Nae Я добавил MWE

KBriggs 19.03.2018 20:44
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
203
1

Ответы 1

При чтении widget.after(milliseconds, callback, *args) он ставит в очередь событие таймера, которое вызывает callback с *argsпослеmilliseconds. После завершения метода Идет назад на линию mainloop.

Очередь в примере никогда не останавливается, поскольку вызов process_message никогда не прекращается, потому что зачем это нужно? self.pause установлен как False, но process_message никогда не проверяет, является ли он True или нет.

Понятно - наверное, я неправильно понял, как работает after. Я сделаю еще одну попытку. Я не хочу, чтобы process_message когда-либо останавливался, поскольку пауза в конце концов заканчивается. Во время паузы я просто хочу, чтобы queue_message перестал добавлять вещи в очередь. Я внес изменения в функцию pause_and_empty на основе ваших отзывов. Кажется, теперь все работает так, как задумано. Вы можете подтвердить / опровергнуть?

KBriggs 19.03.2018 21:12

Другие вопросы по теме