Last active
September 7, 2021 03:10
-
-
Save jsbueno/0297113b60b783d5bf2db8360db06eaf to your computer and use it in GitHub Desktop.
asyncio tkinter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Sample file to enable tkinter to run with co-routine and asyncio code | |
with no need for extra threads or blocking the UI | |
(this will likely be upgraded to a full package at some point in time) | |
""" | |
import asyncio, sys, tkinter | |
from functools import wraps | |
import inspect | |
_tkasync_tasks = set() | |
def tkasync(func): | |
"""Decorator to enable asyncio callbacks for tkinter | |
""" | |
if not inspect.iscoroutinefunction(func): | |
return func # nothing to be done | |
@wraps(func) | |
def wrapper(*args, **kwargs): | |
coro = func(*args, **kwargs) | |
try: | |
loop = asyncio.get_running_loop() | |
except RuntimeError: | |
# no running event loop: we are running in sync context: | |
# just create a loop, await for the wrapped function and be done with it | |
asyncio.run(coro) | |
return | |
task = loop.create_task(coro) | |
_tkasync_tasks.add(task) | |
return wrapper | |
async def main_async(root): | |
"""Tkinter async driver, meant to replace tkinter.mainloop | |
""" | |
global _tkasync_tasks | |
while True: | |
try: | |
root.update() | |
except Exception as error: | |
# FIXME: suppress error message when tkinter app is destroyed. | |
print(error, file=sys.stderr) | |
raise | |
if _tkasync_tasks: | |
complete, pending = await asyncio.wait(_tkasync_tasks, timeout=0.005) | |
del complete | |
_tkasync_tasks = pending | |
else: | |
await asyncio.sleep(0.005) | |
############################################################ | |
# Example code just to show something running in parallel | |
index = 0 | |
interval=250 | |
area = button = root = None | |
def build(): | |
global root, area, button | |
root = tkinter.Tk() | |
button = b = tkinter.Button(root, text="ok", command=click) | |
area =l = tkinter.Label(root, text=" " * 80, background="yellow") | |
b.pack() | |
area.pack() | |
root.after(interval, update_color) | |
return root | |
def update_color(): | |
global index | |
colors = "yellow", "red", "blue", "black", "green" | |
index += 1 | |
index %= len(colors) | |
area["background"] = colors[index] | |
root.after(interval, update_color) | |
##################################################### | |
# Example coroutine used as a tkinter callback: | |
@tkasync | |
async def click(): | |
state = button["state"] | |
button["state"] = "disabled" | |
print("click enter") | |
await asyncio.sleep(2) | |
print("click exit") | |
button["state"] = state if state != "disabled" else "normal" | |
########################################## | |
# sample main function | |
def main(): | |
root = build() | |
# Call this instead of tkinter.mainloop: | |
loop = asyncio.run(main_async(root)) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
At line 64 there is an unpack error on
area, button, root = None
, shouldn't it be something likearea = button = root = None
?