AsyncTkinter
asynctkinter is an async library that saves you from ugly callback-style code,
like most of async libraries do.
Let's say you want to do:
print('A')- wait for 1sec
print('B')- wait for a label to be pressed
print('C')
in that order. Your code would look like this:
def what_you_want_to_do(label):
bind_id = None
print('A')
def one_sec_later(__):
nonlocal bind_id
print("B")
bind_id = label.bind("<ButtonPress>", on_press, "+")
label.after(1000, one_sec_later)
def on_press(event):
label.unbind("<ButtonPress>", bind_id)
print("C")
what_you_want_to_do(...)
It's not easy to understand.
If you use asynctkinter, the code above will become:
import asynctkinter as atk
async def what_you_want_to_do(clock, label):
print("A")
await clock.sleep(1)
print("B")
await atk.event(label, "<ButtonPress>")
print("C")
atk.start(what_you_want_to_do(...))
Installation
Pin the minor version.
pip install "asynctkinter>=0.5,<0.6"
Usage
import tkinter as tk
import asynctkinter as atk
async def main(*, clock: atk.Clock, root: tk.Tk):
label = tk.Label(root, text='Hello', font=('', 80))
label.pack()
# waits for 2 seconds to elapse
await clock.sleep(2)
# waits for a label to be pressed
event = await atk.event(label, "<ButtonPress>")
print(f"pos: {event.x}, {event.y}")
# waits for either 5 seconds to elapse or a label to be pressed.
# i.e. waits at most 5 seconds for a label to be pressed
tasks = await atk.wait_any(
clock.sleep(5),
atk.event(label, "<ButtonPress>"),
)
if tasks[0].finished:
print("Timeout")
else:
event = tasks[1].result
print(f"The label got pressed. (pos: {event.x}, {event.y})")
# same as the above
async with clock.move_on_after(5) as timeout_tracker:
event = await atk.event(label, "<ButtonPress>")
print(f"The label got pressed. (pos: {event.x}, {event.y})")
if timeout_tracker.finished:
print("Timeout")
# waits for both 5 seconds to elapse and a label to be pressed.
tasks = await atk.wait_all(
clock.sleep(5),
atk.event(label, "<ButtonPress>"),
)
# nests as you want.
tasks = await ak.wait_all(
atk.event(label, "<ButtonPress>"),
atk.wait_any(
clock.sleep(5),
...,
),
)
child_tasks = tasks[1].result
if __name__ == "__main__":
atk.run(main)
threading
Unlike trio and asyncio, asynckivy does not provide any I/O primitives.
Therefore, if you don’t want to implement your own, using threads may be the best way to perform I/O without blocking the main thread.
from concurrent.futures import ThreadPoolExecuter
import asynctkinter as atk
executer = ThreadPoolExecuter()
async def async_fn(clock: atk.Clock):
# create a new thread, run a function inside it, then
# wait for the completion of that thread
r = await atk.run_in_thread(clock, thread_blocking_operation)
print("return value:", r)
# run a function inside a ThreadPoolExecuter, and wait for its completion.
# (ProcessPoolExecuter is not supported)
r = await atk.run_in_executer(clock, executer, thread_blocking_operation)
print("return value:", r)
Unhandled exceptions (excluding BaseException that is not Exception) are propagated to the caller
so you can catch them like you do in synchronous code:
import requests
import asynctkinter as atk
async def async_fn(clock: atk.Clock):
try:
r = await atk.run_in_thread(clock, lambda: requests.get('htt...', timeout=10), ...)
except requests.Timeout:
print("TIMEOUT!")
else:
print('RECEIVED:', r)
Notes
- You may want to read the asyncgui's documentation as it is the foundation of this library.
- You may want to read the asyncgui_ext.clock's documentation as well.
- I, the author of this library, am not even a tkinter user so there may be plenty of weird code in the repository.
Release files for asynctkinter 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| asynctkinter-0.5.0.tar.gz | 4.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| asynctkinter-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 10.1 kB
Release files / asynctkinter-0.5.0.tar.gz
| Download URL | asynctkinter-0.5.0.tar.gz |
|---|---|
| Size | 4.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5d549b3129bbcf308598782f69fbd43a687eb58685c7a84241858ba658cec875
|
|
BLAKE2b-256 checksum How to use checksums |
827b3d5a17fc69a712c843a4b9a904e1290e719bea3377fa45b59143d73a82ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 1, 2026.
Transparency logRelease files / asynctkinter-0.5.0-py3-none-any.whl
| Download URL | asynctkinter-0.5.0-py3-none-any.whl |
|---|---|
| Size | 5.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
273e53257e784a575c7034219b9706650d447b2e7767ddf02f8b213b19183907
|
|
BLAKE2b-256 checksum How to use checksums |
ea34cedad76d1bc584112c6a521ce2653c2e7b5b4b12502b6a50f96186df9d34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 1, 2026.
Transparency log