Kivy provides built-in events, but you can also create your own events. This article explains how to create and register custom events and dispatch them.
Methods of the EventDispatcher Classes
Please refer to the following articles for more information on Kivy’s event mechanism, built-in events, and event propagation.
To create a custom event, the EventDispatcher class provides methods for registering events.
- register_event_type (self, event_type) : register a new custom event.
- unregister_event_type (self, event_type): unregister a custom event.
- bind (self, **kwargs) : bind an event to a specific callback.
- fbind (self, name, func, * args, **kwargs) : faster binding than bind().
- unbind (self, **kwargs) : Unbind events and callbacks when using bind().
- funbind (name, func, *args, **kwargs) : Unbind events and callbacks when using fbind().
- dispatch (self, event_type, *args, **kwargs) : trigger an event and call a listener.
- get_property_observers (self, name, args = False ) : retrieve the list of bound events.
- is_event_type (self, event_type): check if an event is registered.
Creating Custom Events
To create and execute a custom event, follow these steps
- Register a custom event. (use register_event_type() or __events__)
- Bind callback functions if necessary. ( use bind() or fbind())
- Trigger events. ( use dispatch())
Code Example
Kivy_events_custom_event1.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
#__events__ = ('on_custom_event',) # (1) Register custom events (Static).
def __init__(self, **kwargs):
self.register_event_type('on_custom_event') # (1) Register a custom event (Dynamic).
self.bind(on_custom_event=self.on_callback) # (2) Binding callback.
super().__init__(**kwargs)
def on_custom_event(self,*args):
self.ids.label.text += '\n on_custom_event called'
def on_callback(self,*args):
self.ids.label.text += '\n on_callback called'
class KivyEventsCustomEvent1(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
KivyEventsCustomEvent1().run()
Kivyeventscustomevent1.kv
<RootWidget>:
orientation: 'vertical'
Label:
id:label
text: "Event is dispatched."
font_size: 30
Button:
text: "Dispatch"
font_size: 30
on_press:
root.dispatch('on_custom_event') # (3) Triggering an event.
Cases Where It Is Not Necessary to Import EventDispatcher
Although register_event_type(), bind(), and dispatch() used in this example are methods of the EventDispatcher class, you do not need to import an EventDispatcher to use them.
This code uses a subclass that inherits from BoxLayout, which belongs to the Widget class. And since the Widget class inherits from the EventDispatcher class, these methods can be used without importing. In other words, if a subclass inherits from a UI widget, layout widget, or Widget class, there is no need to import it.
In the case of an App subclass as shown below, you need to define a subclass that inherits from EventDispatcher.
from kivy.event import EventDispatcher
from kivy.app import App
def CustomEvent(EventDispatcher):
pass
def CustomEventApp(App): pass
pass
Step 1: Registering a Custom Events
There are two ways to register a custom event.
- Using register_event_type()
- Special variable
How to use __events__
The event function to be registered in this example is shown below. The *args argument can be omitted.
def on_custom_event(self,*args):
self.ids.label.text = '\n on_custom_event called'
How to Use register_event_type
To register a custom event with register_event_type(), the following conditions must be met
Failure to adhere to these conditions will result in an error.
instance.register_event_type(event_type )
self.register_event_type('on_custom_event') # (1) Register a custom event (Dynamic).
The event_type argument specifies a custom event, but only one event can be registered. If you wish to register multiple events, write multiple lines.
self.register_event_type('on_custom_event1')
self.register_event_type('on_custom_event2')
self.register_event_type('on_custom_event3')
Special Variables How to Use __events__
The way to use special variables is to specify multiple events in a list.
The list must end with a comma.
__events__ = ('on_custom_event',) # (1) Register custom events (Static).
Difference Between the Two
Although it seems fine to use either, register_event_type() is suitable for both registering dynamic and static events. On the other hand
A static event is an event that is defined from the beginning and is immutable, meaning that the process does not change after an instance of the class is created. On the other hand, a dynamic event is an event that changes after an instance of the class is created, such as the addition of an event.
Step 2: Binding a Callback Function
To bind a callback function, use bind() or fbind(). Normally, bind() is sufficient, but if you use events frequently or for performance reasons, you may choose to use fbind(). fbind() is a binding method that is more sophisticated and faster than bind(). I will skip over the usage of fbind().
How to Write bind()
The callback function in this example is as follows
def on_callback(self,*args):
self.ids.label.text = '\n on_callback called'
Note that omitting args results in the following error: the error ‘positional argument takes 1 but was given 2 ‘ is a TypeError that occurs when the number of arguments defined does not match the number of arguments in the caller.
File "kivy\_event.pyx", line 727, in kivy._event.EventDispatcher.dispatch
File "kivy\\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
File "kivy\\_event.pyx", line 1231, in kivy._event.
TypeError: RootWidget.on_callback() takes 1 positional argument but 2 were given
The bind() is written as follows
instance_name.bind(event_name = callback_function)
self.bind(on_custom_event=self.on_callback)
When Using the KV Language
Usually, it can be bound in the kv language, but for custom events I get an error, it doesn’t seem to handle them the same way as Kivy’s built-in events.
# py file
# Register a custom event.
self.register_event_type('on_custom_event')
# kv file
# This will result in an error.
on_custom_event: root.on_callback()
Registering an overridden Kivy built-in event is not an error.
# py file
# Register overridden built-in event.
self.register_event_type('on_press')
# kv file
# This is not an error.
on_press: root.on_callback()
Step3: Triggering the Event
Use dispatch() to trigger an event.
on_press: root.dispatch('on_press', 'on_press', 'on_press')
root.dispatch('on_custom_event') # (3) Triggering an event.
In this example, a custom event is executed when a button is pressed. This time, the event is described in this location, but it can also be described in the constructor, for example, to execute the event when the application is launched.
Manipulating Custom Events
As mentioned at the beginning of this article, the EventDispatcher class provides methods for manipulating custom events. Here is a example code that uses the following methods.
- unregister_event_type (self, event_type): unregister a custom event.
- unbind (self, **kwargs) : unassociate an event with a callback using bund().
- get_property_observers (self, name, args = False ) : retrieve the list of bound events.
- is_event_type (self, event_type): check if an event is registered.
Code Example
Kivy_events_custom_event2.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
__events__ = ('on_dynamic_event', 'on_custom_event',)
def __init__(self, **kwargs):
self.bind(on_custom_event=self.on_callback)
super().__init__(**kwargs)
self.dispatch('on_dynamic_event')
# Custom event1
def on_custom_event(self,*args):
self.ids.label.text += '\n on_custom_event was called'
# Custom event2
def on_dynamic_event(self,*args):
self.ids.label.text += '\n on_dynamic_event was called'
# Callback
def on_callback(self, *args):
self.ids.label.text += '\n on_callback was called'
# Unregister the registered event.
def unregister_event(self):
if self.is_event_type('on_dynamic_event'):
self.unregister_event_type('on_dynamic_event')
self.ids.label.text = 'Unregistered on_dynamic_event'
self.ids.button1.text = 'Re-register Dynamic event'
else:
self.register_event_type('on_re_register')
self.dispatch('on_re_register')
# Unbind callback function.
def unbind_callback(self):
if self.is_callback() == None:
self.ids.label.text = 'Error: None returned in the list'
if self.is_callback() == True:
self.unbind(on_custom_event=self.on_callback)
self.ids.label.text = 'Unbound on_callback'
self.ids.button2.text = 'Re-bind Callback'
elif self.is_callback() == False:
self.register_event_type('on_re_bind')
self.dispatch('on_re_bind')
# Check if callback functions are bound.
def is_callback(self):
get_callback = self.get_property_observers('on_custom_event', args = False)
for observer in get_callback:
if observer.method_name == 'on_callback':
return True
elif observer.method_name == None:
return None
return False
# Re-register an unregistered custom event.
def on_re_register(self):
self.register_event_type('on_dynamic_event')
self.ids.label.text = 'Registered on_dynamic_event'
self.ids.button1.text = 'Unregister Dynamic event'
self.dispatch('on_dynamic_event')
# Rebind the unbind callback function.
def on_re_bind(self):
self.bind(on_custom_event=self.on_callback)
self.ids.label.text = 'Bound on_callback.'
self.ids.button2.text = 'Unbind Callback'
class KivyEventsCustomEvent2(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
KivyEventsCustomEvent2().run()
kivyeventscustomevent2.kv
<RootWidget>:
orientation: 'vertical'
Label:
id:label
text: ""
font_size: 30
BoxLayout:
Button:
id: button1
text: "Unregister Dynamic event"
font_size: 30
on_press: root.unregister_event()
Button:
id: button2
text: "Unbind Callback"
font_size: 30
on_press: root.unbind_callback()
Button:
text: "call Custom event"
font_size: 30
on_press: root.dispatch('on_custom_event')
Code Explanation
This code consists of the following methods
# Custom_event1
def on_custom_event(self,*args):
# Custom event2
def on_dynamic_event(self,*args):
# Callback
def on_callback(self,*args):
# Unregister the registered event.
def unregister_event(self):
# Unbind the callback function.
def unbind_callback(self):
# Check if the callback function is bound.
def is_callback(self):
# Re-register the unbind custom event.
def on_re_register(self):
# Re-bind the unregistered callback function.
def on_re_bind(self):
- on_dynamic_event() is triggered when the app is launched.
- Button1 repeatedly unregisters and re-registers the registered event.
- Button2 repeatedly binds and unbinds callback functions.
- Button3 triggers on_custom_even().
Unregistering a Registered Custom Event
To unregister a registered custom event, use unregister_event_type(). Also use is_event_type() to check if a custom event exists.
is_event_type()
The is_event_type() returns True if the parameter event_type is registered, False if not.
instance. is_event_type (self, event_type)
The event_type specifies the event name registered by either register_event_type() or __events__.
if self.is_event_type('on_dynamic_event'):
unregister_event_type()
The unregister_event_type() unregisters an event registered by register_event_type() or __events__.
instance. unregister_event_type (self, event_type)
The event_type specifies the event name registered by either register_event_type() or __events__.
self.unregister_event_type('on_dynamic_event')
Unbind a Callback Function
To unbind a callback, use unbind(). Also use get_property_observers() to check if it is bound.
get_property_observers()
get_property_observers() returns a list of callback functions bound to properties or events.
instance.get_property_observers (self, name, args = False)
name: Specify the property or event from which to bind.
args: bool value specifying whether to return the arguments of the bound callback function. Default is False; if False, only the callback function is returned and no arguments are returned; if True, the arguments are returned together. The argument list consists of five elements (callback, largs, kwargs, is_ref, uid ), where is_ref indicates whether the callback is a weakref (weak reference), uid is the uid if fbind() is used, and None if bind() is used. is used, and None if bind() is used.
# List returned if True.
[(<WeakMethod proxy=<__main__.RootWidget object at 0x00000164BC361240> method=None method_name=on_callback>, (), {}, 1, None)]
# List returned if Flase.
[<WeakMethod proxy=<__main__.RootWidget object at 0x00000164BC361240> method=None method_name=on_callback>]
Since the callback function name can be obtained by method_name, branch processing is performed and the flag is returned.
# Check if the callback function is bound.
def is_callback(self):
get_callback = self.get_property_observers('on_custom_event', args = False)
for observer in get_callback: if observer.method_name == False
if observer.method_name == 'on_callback': if observer.method_name = 'on_callback', args = False
return True
elif observer.method_name == None: return None
return None
return False
The get_callback is assigned a list of get_property_observers(), so it is expanded with a for statement and returns True if it matches ‘on_callback ‘.
In this example, method_name could be obtained because a callback function with no arguments was bound, but when a callback function with arguments was bound, there were cases where None was entered for method_name or method_name could not be obtained. The details of this are summarized in the following article.
unbind()
Unbinds a callback function that has been bound to an event with bind(). If a callback function is bound to an event multiple times, the first bound callback function is unbound.
self.bind(on_custom_event=self.on_callback_1) # The first bound callback function is unbound.
self.bind(on_custom_event=self.on_callback_2)
self.bind(on_custom_event=self.on_callback_3)
If it is a callback function with no arguments bound by fbind(), unbind() can be used. If it is a callback function with arguments, unbind fails, so use funbind().
instance. unbind (self, **kwargs)
self.unbind(on_custom_event=self.on_callback)
Propagation of Custom Events
The implementation of custom event propagation is described in the following article.
Comment