Search code examples
pythontkinterasynchronous

How to call an async function from a tkinter button


I want to call an asynchronous function from a tkinter button

import asyncio
import time
import tkinter as tk

async def gui():
    root = tk.Tk()
    timer = tk.Button(root, text='Timer', command=lambda: asyncio.run(wait()))
    # timer = tk.Button(root, text='Timer', command=wait())
    timer.pack()
    root.mainloop()

async def sleep():
    await asyncio.sleep(1)

async def wait():
    start = time.time()
    await sleep()
    print(f'Elapsed: {time.time() - start}')

async def main():
    await wait()

asyncio.run(main())
asyncio.run(gui())

If I use the line

    timer = tk.Button(root, text='Timer', command=wait())

I get the error

coroutine 'wait' was never awaited

but if I use the line

    timer = tk.Button(root, text='Timer', command=lambda: asyncio.run(wait()))

I get the error

asyncio.run() cannot be called from a running event loop

What's the resolution of this problem?


Solution

  • The essence of this is to understand which functions come under the async umbrella. This is simpler and resolves all the issues. Note that only the sleep function is asynchronous, and the wait function contains the async call

    asyncio.run(sleep())
    

    The code is now:

    import asyncio
    import time
    import tkinter as tk
    
    def gui():
        root = tk.Tk()
        timer = tk.Button(root, text="Timer", command=wait)
        timer.pack()
        root.mainloop()
    
    def wait():
        start = time.time()
        asyncio.run(sleep())
        print(f'Elapsed: {time.time() - start}')
    
    async def sleep():
        await asyncio.sleep(1)
    
    def main():
        wait()
    
    main()
    gui()