async library that works on top of tkinter's event loop
Project description
AsyncTkinter
asynctkinter
is an async library that saves you from ugly callback-based code,
just 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')
It's barely readable and not easy to understand.
If you use asynctkinter
, the code above will become like this:
import asynctkinter as at
async def what_you_want_to_do(label):
print('A')
await at.sleep(1000, after=label.after)
print('B')
await at.event(label, '<Button>')
print('C')
Installation
pip install asynctkinter
Pin the minor version
If you use this module, it's recommended to pin the minor version, because if it changed, it usually means some breaking changes occurred.
Usage
from tkinter import Tk, Label
import asynctkinter as at
at.patch_unbind()
root = Tk()
label = Label(root, text='Hello', font=('', 60))
label.pack()
async def some_task(label):
label['text'] = 'start heavy task'
# wait for a label to be pressed
event = await at.event(label, '<Button>')
print(f"pos: {event.x}, {event.y}")
# wait for 2sec
await at.sleep(2000, after=label.after)
at.start(some_task(label))
root.mainloop()
wait for the completion/cancellation of multiple tasks simultaneously
async def some_task(label):
from functools import partial
import asynctkinter as at
sleep = partial(at.sleep, after=label.after)
# wait until EITEHR a label is pressed OR 5sec passes
tasks = await at.or_(
at.event(label, '<Button>'),
sleep(5000),
)
print("The label was pressed" if tasks[0].done else "5sec passed")
# wait until BOTH a label is pressed AND 5sec passes"
tasks = await at.and_(
at.event(label, '<Button>'),
sleep(5000),
)
synchronization primitive
There is a Trio's Event equivalent.
import asynctkinter as at
async def task_A(e):
print('A1')
await e.wait()
print('A2')
async def task_B(e):
print('B1')
await e.wait()
print('B2')
e = at.Event()
at.start(task_A(e))
# A1
at.start(task_B(e))
# B1
e.set()
# A2
# B2
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 some_task(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 the completion
r = await at.run_in_executer(
thread_blocking_operation, executer, after=widget.after)
print("return value:", r)
Exceptions(not BaseExceptions) are propagated to the caller, so you can handle them like you do in synchronous code:
import requests
from requests.exceptions import Timeout
import asynctkinter as at
async def some_task(widget):
try:
r = await at.run_in_thread(
lambda: requests.get('htt...', timeout=10), after=widget.after)
except Timeout:
print("TIMEOUT!")
else:
print('GOT A RESPONSE')
Structured Concurrency
Both asynctkinter.and_()
and asynctkinter.or_()
follow the concept of "structured concurrency".
What does that mean?
They promise two things:
- The tasks passed into them never outlive them.
- Exceptions occured in the tasks are propagated to the caller.
Read this post if you are curious to the concept.
Misc
- Why is
patch_unbind()
necessary? Take a look at this.
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.2.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0cbe1fb73212b79a0b80d28c66d609d7b0407301b12d87371994b910676e8992 |
|
MD5 | 6715fb8ebcdbb101883094eeb014cd11 |
|
BLAKE2b-256 | 24aafd265b2923a792c26e9035f8f6ec79be71f39e2712a7a761a567c6ec113c |