У меня есть приложение на 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 Я добавил MWE






При чтении 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 на основе ваших отзывов. Кажется, теперь все работает так, как задумано. Вы можете подтвердить / опровергнуть?
Для этого явно нужен минимальный воспроизводимый пример.