async library for tkinter
Project description
AsyncTkinter
asynctkinter
is an async library that saves you from ugly callback-based 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('<Button>', on_press, '+')
label.after(1000, one_sec_later)
def on_press(event):
label.unbind('<Button>', bind_id)
print('C')
what_you_want_to_do(...)
It's barely readable and not easy to understand.
If you use asynctkinter
, the above code will be:
import asynctkinter as at
async def what_you_want_to_do(label):
print('A')
await at.sleep(label.after, 1000)
print('B')
await at.event(label, '<Button>')
print('C')
at.start(what_you_want_to_do(...))
Installation
It's recommended to pin the minor version, because if it changed, it means some important breaking changes occurred.
poetry add asynctkinter@~0.3
pip install "asynctkinter>=0.3,<0.4"
Usage
from functools import partial
from tkinter import Tk, Label
import asynctkinter as at
at.install()
root = Tk()
label = Label(root, text='Hello', font=('', 60))
label.pack()
async def async_fn(label):
sleep = partial(at.sleep, label.after)
# wait for 2sec
await sleep(2000)
# wait for a label to be pressed
event = await at.event(label, '<Button>')
print(f"pos: {event.x}, {event.y}")
# wait until EITHER a label is pressed OR 5sec passes.
# i.e. wait at most 5 seconds for a label to be pressed.
tasks = await at.wait_any(
at.event(label, '<Button>'),
sleep(5000),
)
if tasks[0].finished:
event = tasks[0].result
print(f"The label was pressed. (pos: {event.x}, {event.y})")
else:
print("5sec passed")
# wait until a label is pressed AND 5sec passes.
tasks = await at.wait_all(
at.event(label, '<Button>'),
sleep(5000),
)
at.start(async_fn(label))
root.mainloop()
threading
asynctkinter
doesn't have any I/O primitives like Trio and asyncio do,
thus threads are the only way to perform them without blocking the main-thread:
from concurrent.futures import ThreadPoolExecuter
import asynctkinter as at
executer = ThreadPoolExecuter()
async def async_fn(widget):
# create a new thread, run a function inside it, then
# wait for the completion of that thread
r = await at.run_in_thread(thread_blocking_operation, after=widget.after)
print("return value:", r)
# run a function inside a ThreadPoolExecuter, and wait for its completion.
# (ProcessPoolExecuter is not supported)
r = await at.run_in_executer(executer, thread_blocking_operation, after=widget.after)
print("return value:", r)
Exceptions(not BaseExceptions) are propagated to the caller, so you can catch them like you do in synchronous code:
import requests
import asynctkinter as at
async def async_fn(widget):
try:
r = await at.run_in_thread(lambda: requests.get('htt...', timeout=10), after=widget.after)
except requests.Timeout:
print("TIMEOUT!")
else:
print('RECEIVED:', r)
Note
- You may want to read the asyncgui's documentation as it is the foundation of this library.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for asynctkinter-0.3.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 64ed38a45536771b72c59c508aa6af3d46e83c6c629878c7dd2d2d86dfe30124 |
|
MD5 | b7099b912ff730add55f3c169634276b |
|
BLAKE2b-256 | beecfeb8149a506b97e7c9d041fbb7ed6a3fca05d051a18b796a1473f6157509 |