API Reference¶

anim_attrs(obj, *, duration=1.0, step=0, transition='linear', **animated_properties)¶

Animates attibutes of any object.

import types

obj = types.SimpleNamespace(x=0, size=(200, 300))
await anim_attrs(obj, x=100, size=(400, 400))

Warning

Unlike kivy.animation.Animation, this one does not support dictionary-type and nested-sequence.

await anim_attrs(obj, pos_hint={'x': 1.})  # not supported
await anim_attrs(obj, nested_sequence=[[10, 20, ]])  # not supported

await anim_attrs(obj, color=(1, 0, 0, 1), pos=(100, 200))  # OK

Added in version 0.6.1.

Changed in version 0.9.0: The output_seq_type parameter was removed.

anim_attrs_abbr(obj, *, d=1.0, s=0, t='linear', **animated_properties)¶

anim_attrs() cannot animate attributes named step, duration and transition but this one can.

Added in version 0.6.1.

Changed in version 0.9.0: The output_seq_type parameter was removed.

async anim_with_dt(*, step=0)¶

An async form of kivy.clock.Clock.schedule_interval(). The following callback-style code:

def callback(dt):
    print(dt)
    if some_condition:
        return False

Clock.schedule_interval(callback, 0.1)

is equivalent to the following async-style code:

async for dt in anim_with_dt(step=0.1):
    print(dt)
    if some_condition:
        break

Added in version 0.6.1.

Deprecated since version 0.9.1: This will be removed in version 0.11.0. Use asynckivy.sleep_freq() instead.

async anim_with_dt_et(*, step=0)¶

anim_with_dt() and anim_with_et() combined.

async for dt, et in anim_with_dt_et(...):
    ...

Added in version 0.6.1.

Deprecated since version 0.9.1: This will be removed in version 0.11.0. Use asynckivy.sleep_freq() instead.

async anim_with_dt_et_ratio(*, base, step=0)¶

anim_with_dt(), anim_with_et() and anim_with_ratio() combined.

async for dt, et, p in anim_with_dt_et_ratio(...):
    ...

Added in version 0.6.1.

Changed in version 0.7.0: The duration parameter was replaced with base. The loop no longer ends on its own.

Deprecated since version 0.9.1: This will be removed in version 0.11.0. Use asynckivy.sleep_freq() instead.

async anim_with_et(*, step=0)¶

Returns an async iterator that yields the elapsed time since the start of the iteration.

async for et in anim_with_et(...):
    print(et)

The code above is equivalent to the following:

et = 0.
async for dt in anim_with_dt(...):
    et += dt
    print(et)

Added in version 0.6.1.

Deprecated since version 0.9.1: This will be removed in version 0.11.0. Use asynckivy.sleep_freq() instead.

async anim_with_ratio(*, base, step=0)¶

Returns an async iterator that yields the elapsed time since the start of the iteration, divided by base.

async for p in anim_with_ratio(base=3):
    print(p)

The code above is equivalent to the following:

base = 3
async for et in anim_with_et():
    p = et / base
    print(p)

Use kivy.animation.AnimationTransition for non-linear curves.

from kivy.animation import AnimationTransition

in_cubic = AnimationTransition.in_cubic

async for p in anim_with_ratio(base=...):
    p = in_cubic(p)
    print(p)

Added in version 0.6.1.

Changed in version 0.7.0: The duration parameter was replaced with base. The loop no longer ends on its own.

event(event_dispatcher, event_name, *, filter=None, stop_dispatching=False)¶

Returns an Awaitable that can be used to wait for:

  • a Kivy event to occur.

  • a Kivy property’s value to change.

# Wait for a button to be pressed.
await event(button, 'on_press')

# Wait for an 'on_touch_down' event to occur.
__, touch = await event(widget, 'on_touch_down')

# Wait for 'widget.x' to change.
__, x = await ak.event(widget, 'x')

The filter parameter:

# Wait for an 'on_touch_down' event to occur inside a widget.
__, touch = await event(widget, 'on_touch_down', filter=lambda w, t: w.collide_point(*t.opos))

# Wait for 'widget.x' to become greater than 100.
if widget.x <= 100:
    await event(widget, 'x', filter=lambda __, x: x > 100)

The stop_dispatching parameter:

It only works for events not for properties. See Kivy’s Event System for details.

class event_freq(event_dispatcher, event_name, *, filter=None, stop_dispatching=False, free_to_await=False)¶

When handling a frequently occurring event, such as on_touch_move, the following kind of code might cause performance issues:

__, touch = await event(widget, 'on_touch_down')

# This loop registers and unregisters an event handler on every iteration.
while True:
    await event(widget, 'on_touch_move', filter=lambda w, t: t is touch)
    ...

If that happens, try the following code instead. It might resolve the issue:

__, touch = await event(widget, 'on_touch_down')
async with event_freq(widget, 'on_touch_move', filter=lambda w, t: t is touch) as on_touch_move:
    while True:
        await on_touch_move()
        ...

Added in version 0.7.1.

Changed in version 0.9.0: The free_to_await parameter was added.

The free_to_await parameter:

If set to False (the default), the only permitted async operation within the with-block is await xxx(), where xxx is the identifier specified in the as-clause. To lift this restriction, set free_to_await to True — at the cost of slightly reduced performance.

fade_transition(*widgets, duration=1.0, step=0)¶

Returns an async context manager that:

  • fades out the given widgets on __aenter__

  • fades them back in on __aexit__

async with fade_transition(widget1, widget2):
    ...

The widgets don’t have to be actual Kivy widgets. Anything with an attribute named opacity–such as kivy.core.window.Window–would work.

Deprecated since version 0.9.0: This will be removed in version 0.11.0. Use asynckivy.transition.fade_transition() instead.

async interpolate(start, end, *, duration=1.0, step=0, transition='linear') AsyncIterator¶

Interpolates between the values start and end in an async-manner. Inspired by wasabi2d’s interpolate.

async for v in interpolate(0, 100, duration=1.0, step=.3):
    print(int(v))

elapsed time

output

0 sec

0

0.3 sec

30

0.6 sec

60

0.9 sec

90

1.2 sec

100

async interpolate_seq(start, end, *, duration, step=0, transition='linear') AsyncIterator¶

Same as interpolate() except this one is for sequence types.

async for v in interpolate_seq([0, 50], [100, 100], duration=1, step=0.3):
    print(v)

elapsed time

output

0

[0, 50]

0.3

[30, 65]

0.6

[60, 80]

0.9

[90, 95]

1.2 sec

[100, 100]

Added in version 0.7.0.

Changed in version 0.9.0: The output_type parameter was removed. The iterator now always yields a list.

managed_start(aw: Awaitable | Task, /, *, daemon=False) Task¶

A task started with this function will be automatically cancelled when an App.on_stop event fires, if it is still running. This prevents the task from being cancelled by the garbage collector, ensuring more reliable cleanup. You should always use this function instead of calling asynckivy.start directly, except when writing unit tests.

task = managed_start(async_func(...))

Added in version 0.7.1.

move_on_after(seconds: float) AbstractAsyncContextManager[Task]¶

Returns an async context manager that applies a time limit to its code block, like trio.move_on_after() does.

async with move_on_after(seconds) as timeout_tracker:
    ...
if timeout_tracker.finished:
    print("The code block was interrupted due to a timeout")
else:
    print("The code block exited gracefully.")

Added in version 0.6.1.

n_frames(n: int)¶

Waits for a specified number of frames to elapse.

await n_frames(2)

If you want to wait for one frame, asynckivy.sleep() is preferable for a performance reason.

await sleep(0)
async rest_of_touch_events(widget, touch, *, stop_dispatching=False, grab=True) AsyncIterator[None]¶

Returns an async iterator that yields None on each on_touch_move event and stops when an on_touch_up event occurs.

async for __ in rest_of_touch_events(widget, touch):
    print('on_touch_move')
print('on_touch_up')
Parameters:
  • grab – If set to False, this API will not rely on touch.grab(), which means there is no guarantee that all events from the given touch will be delivered to the widget, as documented. If the on_touch_up event is not delivered, the iterator will wait indefinitely for it—an event that never comes. Do not set this to False unless you know what you are doing.

  • stop_dispatching – Whether to stop dispatching non-grabbed touch events. (Grabbed events are always stopped if the grab is True.) For details, see event-bubbling.

Warning

You should not use this when Kivy is running in async mode. Use rest_of_touch_events_cm() instead.

Changed in version 0.9.0: The timeout parameter was removed.

Changed in version 0.9.1: The grab parameter was added.

rest_of_touch_events_cm(widget, touch, *, stop_dispatching=False, free_to_await=False, grab=True)¶

A variant of rest_of_touch_events(). This version is more verbose, but remains safe even when Kivy is running in async mode.

async with rest_of_touch_events_cm(widget, touch) as on_touch_move:
    while True:
        await on_touch_move()
        print('on_touch_move')
print('on_touch_up')

Added in version 0.9.1.

async run_in_executor(executor: ThreadPoolExecutor, func)¶

Runs a function within a concurrent.futures.ThreadPoolExecutor, and waits for the completion of the function.

executor = ThreadPoolExecutor()
...
return_value = await run_in_executor(executor, func)

See I/O in AsyncKivy for details.

Warning

When the caller Task is cancelled, the func will be left running if it has already started, which violates “structured concurrency”.

async run_in_thread(func, *, daemon=None)¶

Creates a new thread, runs a function within it, then waits for the completion of that function.

return_value = await run_in_thread(func)

See I/O in AsyncKivy for details.

Warning

When the caller Task is cancelled, the func will be left running, which violates “structured concurrency”.

sleep(duration)¶

An async form of kivy.clock.Clock.schedule_once().

dt = await sleep(5)  # wait for 5 seconds
sleep_free(duration)¶

An async form of kivy.clock.Clock.schedule_once_free().

dt = await sleep_free(5)  # wait for 5 seconds
class sleep_freq(step=0, free_to_await=False)¶

An async form of kivy.clock.Clock.schedule_interval(). The following callback-style code:

def callback(dt):
    print(dt)
    if some_condition:
        return False

Clock.schedule_interval(callback, 0.1)

is equivalent to the following async-style code:

async with sleep_freq(0.1) as sleep:
    while True:
        dt = await sleep()
        print(dt)
        if some_condition:
            break

Changed in version 0.8.0: The API was made private.

Changed in version 0.9.0:

  • The API was made public again.

  • The API was renamed from repeat_sleeping to sleep_freq; the old name remains available as an alias.

  • The free_to_await parameter was added.

The free_to_await parameter:

If set to False (the default), the only permitted async operation within the with-block is await xxx(), where xxx is the identifier specified in the as-clause. To lift this restriction, set free_to_await to True — at the cost of slightly reduced performance.

class smooth_attr(target: tuple[kivy.event.EventDispatcher, str], follower: tuple[Any, str], *, speed=10.0, min_diff=kivy.metrics.dp)¶

Makes an attribute smoothly follow another.

import types

widget = Widget(x=0)
obj = types.SimpleNamespace(xx=100)

# 'obj.xx' will smoothly follow 'widget.x'.
smooth_attr(target=(widget, 'x'), follower=(obj, 'xx'))

To make its effect temporary, use it with a with-statement:

# The effect lasts only within the with-block.
with smooth_attr(...):
    ...

A key feature of this API is that if the target value changes while being followed, the follower automatically adjusts to the new value.

Parameters:
  • target –

    Must be a numeric or numeric sequence type property, that is, one of the following:

  • speed – The speed coefficient for following. A larger value results in faster following.

  • min_diff – If the difference between the target and the follower is less than this value, the follower will instantly jump to the target’s value. When the target is a ColorProperty, you most likely want to set this to a very small value, such as 0.01. Defaults to dp(2).

Added in version 0.8.0.

stencil_mask(widget, *, use_outer_canvas=False) Iterator[kivy.graphics.InstructionGroup]¶

Returns a context manager that allows restricting the drawing area of a specified widget to an arbitrary shape.

with stencil_mask(widget) as drawable_area:
    ...

The most common use case would be to confine drawing to the widget’s own area, which can be achieved as follows:

from kivy.graphics import Rectangle
import asynckivy as ak

rect = Rectangle()
with (
    ak.sync_attr(from_=(widget, 'pos'), to_=(rect, 'pos')),  # A
    ak.sync_attr(from_=(widget, 'size'), to_=(rect, 'size')),
    ak.stencil_mask(widget) as drawable_area,
):
    drawable_area.add(rect)
    ...

Note that if the widget is a relative-type widget and the use_outer_canvas parameter is False (the default), line A above must be removed.

Since this use case is so common, stencil_widget_mask() is provided as a shorthand.

Added in version 0.9.1.

stencil_widget_mask(widget, *, use_outer_canvas=False, relative=False) Iterator[kivy.graphics.InstructionGroup]¶

Returns a context manager that restricts the drawing area to the widget’s own area.

with stencil_widget_mask(widget):
    ...
Parameters:

relative – Must be set to True if the widget is a relative-type widget.

Added in version 0.9.1.

class suppress_event(event_dispatcher, event_name, *, filter=<function suppress_event.<lambda>>)¶

Returns a context manager that prevents the callback functions (including the default handler) bound to an event from being called.

from kivy.uix.button import Button

btn = Button()
btn.bind(on_press=lambda __: print("pressed"))
with suppress_event(btn, 'on_press'):
    btn.dispatch('on_press')

The above code prints nothing because the callback function won’t be called.

Strictly speaking, this context manager doesn’t prevent all callback functions from being called. It only prevents the callback functions that were bound to an event before the context manager enters. Thus, the following code prints pressed.

from kivy.uix.button import Button

btn = Button()
with suppress_event(btn, 'on_press'):
    btn.bind(on_press=lambda __: print("pressed"))
    btn.dispatch('on_press')

Warning

You need to be careful when you suppress an on_touch_xxx event. See Kivy’s Event System for details.

class sync_attr(from_: tuple[kivy.event.EventDispatcher, str], to_: tuple[Any, str])¶

Creates one-directional binding between attributes.

import types

widget = Widget(x=100)
obj = types.SimpleNamespace()

sync_attr(from_=(widget, 'x'), to_=(obj, 'xx'))
assert obj.xx == 100  # synchronized
widget.x = 10
assert obj.xx == 10  # synchronized
obj.xx = 20
assert widget.x == 10  # but not the other way around

To make its effect temporary, use it with a with-statement:

# The effect lasts only within the with-block.
with sync_attr(...):
    ...

This can be particularly useful when combined with transform().

from kivy.graphics import Rotate

async def rotate_widget(widget, *, angle=360.):
    rotate = Rotate()
    with (
        transform(widget) as ig,
        sync_attr(from_=(widget, 'center'), to_=(rotate, 'origin')),
    ):
        ig.add(rotate)
        await anim_attrs(rotate, angle=angle)

Added in version 0.6.1.

Changed in version 0.8.0: The context manager now applies its effect upon creation, rather than when its __enter__() method is called, and __enter__() no longer performs any action.

Additionally, the context manager now assigns the from_ value to the to_ upon creation:

with sync_attr((widget, 'x'), (obj, 'xx')):
    assert widget.x == obj.xx
class sync_attrs(from_: tuple[kivy.event.EventDispatcher, str], *to_)¶

When multiple sync_attr calls take the same from_ argument, they can be merged into a single sync_attrs call. For instance, the following code:

with sync_attr((widget, 'x'), (obj1, 'x')), sync_attr((widget, 'x'), (obj2, 'xx')):
    ...

can be replaced with the following one:

with sync_attrs((widget, 'x'), (obj1, 'x'), (obj2, 'xx')):
    ...

This can be particularly useful when combined with transform().

from kivy.graphics import Rotate, Scale

async def scale_and_rotate_widget(widget, *, scale=2.0, angle=360.):
    rotate = Rotate()
    scale = Scale()
    with (
        transform(widget) as ig,
        sync_attrs((widget, 'center'), (rotate, 'origin'), (scale, 'origin')),
    ):
        ig.add(rotate)
        ig.add(scale)
        await wait_all(
            anim_attrs(rotate, angle=angle),
            anim_attrs(scale, x=scale, y=scale),
        )

Added in version 0.6.1.

Changed in version 0.8.0: The context manager now applies its effect upon creation, rather than when its __enter__() method is called, and __enter__() no longer performs any action.

Additionally, the context manager now assigns the from_ value to the to_ upon creation:

with sync_attrs((widget, 'x'), (obj, 'xx')):
    assert widget.x is obj.xx
transform(widget, *, use_outer_canvas=False) Iterator[kivy.graphics.InstructionGroup]¶

Returns a context manager that sandwiches the widget’s existing canvas instructions between a kivy.graphics.PushMatrix and a kivy.graphics.PopMatrix, and inserts an kivy.graphics.InstructionGroup right next to the PushMatrix. Those three instructions will be removed when the context manager exits.

This may be useful when you want to animate a widget.

Usage

from kivy.graphics import Rotate

async def rotate_widget(widget, *, angle=360.):
    with transform(widget) as ig:  # <- InstructionGroup
        ig.add(rotate := Rotate(origin=widget.center))
        await anim_attrs(rotate, angle=angle)

If the position or size of the widget changes during the animation, you might need sync_attr.

The use_outer_canvas parameter

While the context manager is active, the content of the widget’s canvas would be:

# ... represents existing instructions

Widget:
    canvas.before:
        ...
    canvas:
        PushMatrix
        InstructionGroup
        ...
        PopMatrix
    canvas.after:
        ...

but if use_outer_canvas is True, it would be:

Widget:
    canvas.before:
        PushMatrix
        InstructionGroup
        ...
    canvas:
        ...
    canvas.after:
        ...
        PopMatrix