Using async/await keywords with Tk.after() method of tkinter Using async/await keywords with Tk.after() method of tkinter tkinter tkinter

Using async/await keywords with Tk.after() method of tkinter


tk.after accepts a normal function, not a coroutine. To run the coroutine to completion, you can use run_until_complete, just as you did the first time:

loop = asyncio.get_event_loop()root.after(10000, lambda: loop.run_until_complete(updateLoans()))

Also, don't call loop.close(), since you'll need the loop again.


The above quick fix will work fine for many use cases. The fact is, however, that it will render the GUI completely unresponsive if updateLoans() takes a long time due to slow network or a problem with the remote service. A good GUI app will want to avoid this.

While Tkinter and asyncio cannot share an event loop yet, it is perfectly possible to run the asyncio event loop in a separate thread. The main thread then runs the GUI, while a dedicated asyncio thread runs all asyncio coroutines. When the event loop needs to notify the GUI to refresh something, it can use a queue as shown here. On the other hand, if the GUI needs to tell the event loop to do something, it can call call_soon_threadsafe or run_coroutine_threadsafe.

Example code (untested):

gui_queue = queue.Queue()async def updateLoans():    while True:        offers = await dd.loanOffers()        demands = await dd.loanDemands()        print('loans obtained')        gui_queue.put(lambda: updateLoansGui(offers, demands))        await asyncio.sleep(10)def updateLoansGui(offers, demands):    w.LoanOfferView.delete(1.0, END)    w.LoanDemandView.delete(1.0, END)    w.LoanOfferView.insert(END, offers)    w.LoanDemandView.insert(END, demands)    print('loans GUI refreshed')# http://effbot.org/zone/tkinter-threads.htmdef periodicGuiUpdate():    while True:        try:            fn = gui_queue.get_nowait()        except queue.Empty:            break        fn()    root.after(100, periodicGuiUpdate)# Run the asyncio event loop in a worker thread.def start_loop():    loop = asyncio.new_event_loop()    asyncio.set_event_loop(loop)    loop.create_task(updateLoans())    loop.run_forever()threading.Thread(target=start_loop).start()# Run the GUI main loop in the main thread.periodicGuiUpdate()root.mainloop()# To stop the event loop, call loop.call_soon_threadsafe(loop.stop).# To start a coroutine from the GUI, call asyncio.run_coroutine_threadsafe.