A friend of mine wrote a program that used curio and tkinter, and it ran tkinter's mainloop in the main thread and
curio.run 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 tkinter as tk
from _tkinter import DONT_WAIT
# _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 = root.tk.dooneevent
# probably running in pypy
dooneevent = _tkinter.dooneevent
# stop if the root window is destroyed
# simple example
async def counter():
# this is here just to prove that tk_mainloop() doesn't block
i = 0
i += 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())
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
curio.run(curio.spawn(stuff)) 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
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.