I'm still a newbie to threading and asyncio in Python.
I am having a tkinter frame that is supposed to stop on a stop-button click. While the frame is open, two async functions are supposed to run in the background, where one is the producer loop and the other is the consumer loop (connected through an async queue).
Everything is working so far, though I'm getting an error when pressing the stop-button. I believe I am not quitting the coros properly.
Any help is much appreciated!
import random
import threading
import tkinter as tk
import asyncio
class ControlWindow(tk.Frame):
def __init__(self, window=None):
super().__init__()
self.window = window
tk.Button(window, text="Stop", command=self.stop).pack()
self.thread = None
self.tasks = None
self.flag = False
self.run()
# self.window.mainloop()
def schedule_check(self):
self.window.after(811, self.check)
def check(self):
if self.thread.is_alive():
self.schedule_check()
def run(self):
self.thread = threading.Thread(target=asyncio.run, args=[self.main_runner()])
self.thread.start()
self.schedule_check()
async def main_runner(self):
queue = asyncio.Queue()
self.tasks = [asyncio.create_task(self.code(queue)), asyncio.create_task(self.handle_code(queue))]
await asyncio.gather(*self.tasks)
async def code(self, queue):
while not self.flag:
ran = random.randint(0, 5)
queue.put_nowait(ran)
await asyncio.sleep(1)
async def handle_code(self, queue):
while True:
res = await queue.get()
print(res)
def stop(self):
self.flag = True
for task in self.tasks:
task.cancel()
self.thread.join()
self.window.quit()
if __name__ == "__main__":
root = tk.Tk()
ControlWindow(root)
root.mainloop()
This seems to work, i.e. using an Event
for the quit flag:
import random
import threading
import tkinter as tk
import asyncio
class AsyncThing:
def __init__(self):
self.quit_event = asyncio.Event()
self.tasks = []
def stop(self):
self.quit_event.set()
for task in self.tasks:
task.cancel()
async def main_runner(self):
queue = asyncio.Queue()
self.tasks = [
asyncio.create_task(self.code(queue)),
asyncio.create_task(self.handle_code(queue)),
]
try:
await asyncio.gather(*self.tasks)
except asyncio.CancelledError:
pass
async def code(self, queue):
while not self.quit_event.is_set():
ran = random.randint(0, 5)
queue.put_nowait(ran)
await asyncio.sleep(1)
async def handle_code(self, queue: asyncio.Queue):
while not self.quit_event.is_set():
res = await queue.get()
print(res)
class ControlWindow(tk.Frame):
def __init__(self, window=None):
super().__init__()
self.window = window
tk.Button(window, text="Stop", command=self.stop).pack()
self.thing = AsyncThing()
self.thread = None
self.run()
def run(self):
self.thread = threading.Thread(target=asyncio.run, args=[self.thing.main_runner()])
self.thread.start()
def stop(self):
self.thing.stop()
self.thread.join()
self.window.quit()
if __name__ == "__main__":
root = tk.Tk()
ControlWindow(root)
root.mainloop()