mirror of https://github.com/kivy/kivy.git
200 lines
6.5 KiB
ReStructuredText
200 lines
6.5 KiB
ReStructuredText
Events
|
|
======
|
|
|
|
There are two types of events in Kivy:
|
|
|
|
- Clock events: if you want to call a function X times per second, or if you
|
|
want to call a function later.
|
|
- Widget events: if you want to call a function when something changes in the
|
|
widget, or attach a function to a widget specific event.
|
|
|
|
|
|
Clock events
|
|
------------
|
|
|
|
Before we discuss events, you need to know that Kivy has a main loop, and that
|
|
it's important that you avoid breaking it. The main loop is responsible for
|
|
reading from inputs, loading images asynchronously, drawing to the frame, etc.
|
|
If you are looping or sleeping, you'll break the main loop. As an example, the
|
|
following code does both::
|
|
|
|
while True:
|
|
animate_something()
|
|
time.sleep(.10)
|
|
|
|
When you run this, the program will never exit your loop, preventing Kivy from
|
|
doing all of the other things that need doing. As a result, all you'll see is a
|
|
black window which you won't be able to interact with. You need to "schedule"
|
|
your ``animate_something()`` function call over time. You can do this in 2 ways:
|
|
a repetitive call or one-time call.
|
|
|
|
Scheduling a repetitive event
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
You can call a function or a method every X times per second using
|
|
:meth:`~kivy.clock.Clock.schedule_interval`. Here is an example of calling a
|
|
function named my_callback 30 times per second::
|
|
|
|
def my_callback(dt):
|
|
print 'My callback is called', dt
|
|
Clock.schedule_interval(my_callback, 1 / 30.)
|
|
|
|
You have two ways of unscheduling a previously scheduled event. The first would be
|
|
to use :meth:`~kivy.clock.Clock.unschedule`::
|
|
|
|
Clock.unschedule(my_callback)
|
|
|
|
Or, you can return False in your callback, and your event will be automatically
|
|
unscheduled::
|
|
|
|
count = 0
|
|
def my_callback(dt):
|
|
global count
|
|
count += 1
|
|
if count == 10:
|
|
print 'Last call of my callback, bye bye !'
|
|
return False
|
|
print 'My callback is called'
|
|
Clock.schedule_interval(my_callback, 1 / 30.)
|
|
|
|
|
|
Scheduling a one-time event
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Using :meth:`~kivy.clock.Clock.schedule_once`, you can call a function "later",
|
|
like in the next frame, or in X seconds::
|
|
|
|
def my_callback(dt):
|
|
print 'My callback is called !'
|
|
Clock.schedule_once(my_callback, 1)
|
|
|
|
This will call ``my_calback`` in one second. The second argument is the amount
|
|
of time to wait before calling the function, in seconds. However, you can
|
|
achieve some other results with special values for the second argument:
|
|
|
|
- If X is greater than 0, the callback will be called in X seconds
|
|
- If X is 0, the callback will be called after the next frame
|
|
- If X is -1, the callback will be called before the next frame
|
|
|
|
The -1 is mostly used when you are already in a scheduled event, and if you
|
|
want to schedule a call BEFORE the next frame is happening.
|
|
|
|
|
|
Trigger events
|
|
~~~~~~~~~~~~~~
|
|
|
|
If you want to schedule a function to be called only once for the next frame,
|
|
like a trigger, you can achieve that like so::
|
|
|
|
Clock.unschedule(my_callback)
|
|
Clock.schedule_once(my_callback, 0)
|
|
|
|
This way of programming a trigger is expensive, since you'll always call
|
|
unschedule, whether or not you've even scheduled it. In addition, unschedule
|
|
needs to iterate the weakref list of the Clock in order to find your callback
|
|
and remove it. Use a trigger instead::
|
|
|
|
trigger = Clock.create_trigger(my_callback)
|
|
# later
|
|
trigger()
|
|
|
|
Each time you call trigger, it will schedule a single call of your callback. If
|
|
it was already scheduled, it will not be rescheduled.
|
|
|
|
|
|
|
|
|
|
Widget events
|
|
-------------
|
|
|
|
A widget has 2 types of events:
|
|
|
|
- Property event: if your widget changes its position or size, an event is fired.
|
|
- Widget-defined event: an event will be fired for a Button when it's pressed or
|
|
released.
|
|
|
|
|
|
Property event
|
|
~~~~~~~~~~~~~~
|
|
|
|
A widget has many properties. You'll find in the documentation that every property has a
|
|
type like :class:`~kivy.properties.NumericProperty`,
|
|
:class:`~kivy.properties.StringProperty`,
|
|
:class:`~kivy.properties.ListProperty`.
|
|
|
|
Usually, when you want to create a Python class with properties, you do something like this::
|
|
|
|
class MyClass(object):
|
|
def __init__(self):
|
|
super(MyClass, self).__init__()
|
|
self.prop1 = 'bleh'
|
|
|
|
Using this code though, you do not have a good way to know when ``prop1`` is
|
|
changed, except by rewriting the class and adding a hook in
|
|
``__getattribute__``. The Kivy way to do this is::
|
|
|
|
class MyClass(Widget):
|
|
prop1 = StringProperty('bleh')
|
|
|
|
You can connect a function to this property if you want to be called when the
|
|
value of the property changes::
|
|
|
|
def my_callback(instance, value):
|
|
print 'the widget', instance, 'prop1 changed to', value
|
|
|
|
# create an instance of MyClass
|
|
obj = MyClass()
|
|
# and connect my_callback to prop1
|
|
obj.bind(prop1=my_callback)
|
|
# now change prop1 => it will call your callback !
|
|
obj.prop1 = 'hello world'
|
|
|
|
If you want to stop receiving events from the ``prop1`` property, call unbind::
|
|
|
|
obj.unbind(prop1=my_callback)
|
|
|
|
|
|
Widget-defined event
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Sometimes the property event is not enough to hook onto. For example, a Button
|
|
could have a state property that indicates whether the Button is currently
|
|
pressed or not. We made the choice to add additional events for this: the
|
|
:meth:`~kivy.uix.button.Button.on_press` and
|
|
:meth:`~kivy.uix.button.Button.on_release` events::
|
|
|
|
def my_callback_press(instance):
|
|
print 'The button', instance, 'is pressed'
|
|
button = Button(text='Hello world')
|
|
button.bind(on_press=my_callback_press)
|
|
|
|
Every event defined by a widget is in the documentation, at the start of the
|
|
class. You can find a list of widget-defined events that the widget supports.
|
|
|
|
If you are designing your own widget, you can create a widget event by using
|
|
:meth:`~kivy.event.register_event_type`::
|
|
|
|
class MyClass(Widget):
|
|
|
|
def __init__(self, **kwargs):
|
|
self.register_event_type('on_custom_event')
|
|
super(MyClass, self).__init__(**kwargs)
|
|
|
|
def on_custom_event(self):
|
|
# empty handler needed
|
|
pass
|
|
|
|
Then, the user can hook to it, the same as to the Button.on_press event. In this
|
|
example, the event is never dispatched. Let's just add a function demonstrating
|
|
how to dispatch a widget-defined event::
|
|
|
|
class MyClass(Widget):
|
|
|
|
# ... __init__ + on_custom_event
|
|
|
|
def do_something(self):
|
|
self.dispatch('on_custom_event')
|
|
|
|
Now, every time you call the ``do_something()`` method, it will dispatch
|
|
``on_custom_event``, and call every function attached to this event.
|