Curio and tkinter's mainloop


A friend of mine wrote a program that used curio and tkinter, and it ran tkinter’s mainloop in the main thread and in another thread. I needed to run tkinter with curio at some point too, and I figured out a way to run tkinter’s mainloop in curio. It works like this:

import curio
import functools
import tkinter as tk

    from _tkinter import DONT_WAIT
except ImportError:
    # _tkinter.DONT_WAIT is there on cpython and pypy, but providing a
    # fallback doesn't hurt
    DONT_WAIT = 2

async def tk_mainloop(root):
        dooneevent =
    except AttributeError:
        # probably running in pypy
        dooneevent = _tkinter.dooneevent

    while True:
        await curio.sleep(0)

        # stop if the root window is destroyed
        except tk.TclError:

# simple example

async def counter():
    # this is here just to prove that tk_mainloop() doesn't block
    i = 0
    while True:
        print("counter:", i)
        i += 1
        await curio.sleep(1)

async def main():
    root = tk.Tk()
    button = tk.Button(root, text="Click me",
                       command=functools.partial(print, "you clicked me"))
    countertask = await curio.spawn(counter())
    await tk_mainloop(root)
    await countertask.cancel()

dooneevent() calls Tcl_DoOneEvent(3tcl), and it runs Tk’s mainloop by one iteration.

My code works, but there are some problems with it. Most of these are probably not too hard to fix and there might already be a workaround that I haven’t found yet.

  • The code is heavy on the CPU because curio’s main loop runs as fast as possible, and nothing slows it down. I have no idea how to fix this. Urwid’s mainloops seem to just call time.sleep(something_small).
  • Doing to spawn an async function from a tkinter callback doesn’t work.

I think it would be nice to see something like a curio.tkinter submodule for writing tkinter code with curio. Using and curio.sleep(0) feels like a hack, and it would be really nice to just do button['command'] = some_async_func and have it run correctly in curio’s mainloop.