Package: asynckivy
AsyncKivyYoutube日本語docasynckivyis 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 1secprint('B')wait for a button to be pressedprint('C')in that order.
Your code would look like this:fromkivy.clockimportClockdefwhat_you_want_to_do(button):print('A')defone_sec_later(__):print('B')button.bind(on_press=on_button_press)Clock.schedule_once(one_sec_later,1)defon_button_press(button):button.unbind(on_press=on_button_press)print('C')what_you_want_to_do(...)It's not easy to understand.
If you useasynckivy, the above code will become:importasynckivyasakasyncdefwhat_you_want_to_do(button):print('A')awaitak.sleep(1)print('B')awaitak.event(button,'on_press')print('C')ak.start(what_you_want_to_do(...))InstallationIt's recommended to pin the minor version, because if it changed, it means someimportantbreaking changes occurred.poetry add asynckivy@~0.6
pip install "asynckivy>=0.6,<0.7"Usageimportasynckivyasakasyncdefsome_task(button):# waits for 1secdt=awaitak.sleep(1)print(f'{dt}seconds have passed')# waits until a button is pressedawaitak.event(button,'on_press')# waits until 'button.x' changes__,x=awaitak.event(button,'x')print(f'button.x is now{x}')# waits until 'button.x' becomes greater than 100ifbutton.x<=100:__,x=awaitak.event(button,'x',filter=lambda__,x:x>100)print(f'button.x is now{x}')# waits until EITHER a button is pressed OR 5sec passes.# i.e. wait at most 5 seconds for a button to be pressedtasks=awaitak.wait_any(ak.event(button,'on_press'),ak.sleep(5),)print("The button was pressed"iftasks[0].finishedelse"Timeout")# same as the aboveasyncwithak.move_on_after(5)asbg_task:awaitak.event(button,'on_press')print("Timeout"ifbg_task.finishedelse"The button was pressed")# waits until a button is pressed AND 5sec passes.tasks=awaitak.wait_all(ak.event(button,'on_press'),ak.sleep(5),)# nest as you want.# waits for a button to be pressed AND (5sec OR 'other_async_func' to complete)tasks=awaitak.wait_all(ak.event(button,'on_press'),ak.wait_any(ak.sleep(5),other_async_func(),),)child_tasks=tasks[1].resultprint("5sec passed"ifchild_tasks[0].finishedelse"other_async_func has completed")ak.start(some_task(some_button))Tested onCPython 3.8 + Kivy 2.3.0CPython 3.9 + Kivy 2.3.0CPython 3.10 + Kivy 2.3.0CPython 3.11 + Kivy 2.3.0CPython 3.12 + Kivy 2.3.0 (3.12.0 is not supported due tothis issue)Why this even existsKivy supports two legit async libraries,asyncioandTrio, from version 2.0.0 so developing another one seemsreinventing the wheel.
Actually, I started this one just for learning how async/await works so itwasinitially "reinventing the wheel".But after playing with Trio and Kivy for a while, I noticed that Trio is not suitable for the situation where fast reactions are required e.g. touch events.
The same is true of asyncio.
You can confirm it by runninginvestigation/why_xxx_is_not_suitable_for_handling_touch_events.py, and mashing a mouse button.
You'll see sometimesupis not paired withdown.
You'll see the coordinates aren't relative to theRelativeLayouteven though thetargetbelongs to it.The cause of those problems is thattrio.Event.set()andasyncio.Event.set()don'timmediatelyresume the tasks waiting for theEventto be set. They just schedule the tasks to resume.
Same thing can be said tonursery.start_soon()andasyncio.create_task().Trio and asyncio are asyncI/Olibraries after all.
They probably don't have to immediately resumes/starts tasks, which I think necessary for Kivy's touch handling.
(If a touch is not handled immediately, its state may change).
Their core design might not be suitable for GUI in the first place.
That's why I'm still developing thisasynckivylibrary to this day.