diff --git a/doc/sources/guide2/events.rst b/doc/sources/guide2/events.rst index 79d56157e..3350fff1b 100644 --- a/doc/sources/guide2/events.rst +++ b/doc/sources/guide2/events.rst @@ -76,10 +76,15 @@ describe. - :class:`~kivy.properties.StringProperty` - :class:`~kivy.properties.NumericProperty` +- :class:`~kivy.properties.BoundedNumericProperty` - :class:`~kivy.properties.ObjectProperty` +- :class:`~kivy.properties.DictProperty` - :class:`~kivy.properties.ListProperty` -- :class:`~kivy.properties.ObjectProperty` +- :class:`~kivy.properties.OptionProperty` - :class:`~kivy.properties.AliasProperty` +- :class:`~kivy.properties.BooleanProperty` +- :class:`~kivy.properties.ReferenceListProperty` + Declaration of a Property ------------------------- @@ -90,11 +95,12 @@ properties is not the attribute, it's a mechanism to create events for your attributes:: class MyWidget(Widget): + text = StringProperty('') -If you override `__init__`, *always* accept `**kwargs` and use super() to call -parent's `__init__` with it:: +When overriding `__init__`, *always* accept `**kwargs` and use super() to call +the parents `__init__` with it:: def __init__(self, **kwargs): super(MyWidget, self).__init__(**kwargs) @@ -102,3 +108,169 @@ parent's `__init__` with it:: Dispatching a Property event ---------------------------- + +Kivy properties by default; provide a on_ event. This event is +called when the value of the property is changed. + +.. note:: + if the new value for the property is equal to the older value then the + on_ event will not be called. + +For example consider the following code. + +.. code-block:: python + :linenos: + + class CustomBtn(Widget): + + pressed = ListProperty([0, 0]) + + def on_touch_down(self, touch): + if self.collide_point(*touch.pos): + self.pressed = touch.pos + return True + + def on_pressed(self, instance, pos): + print ('pressed at {pos}'.format(pos=pos)) + +In the code above at line: 3 :: + + pressed = ListProperty([0, 0]) + +We define `pressed` Property of type :class:`~kivy.properties.ListProperty`, +giving it a default value of `[0, 0]`. from this point on-wards the `on_pressed` +event will be called when ever the value of this property is changed. + +At Line 5:: + + def on_touch_down(self, touch): + if self.collide_point(*touch.pos): + self.pressed = touch.pos + return True + +We override the :meth:`on_touch_down` method of the Widget class. Here we check +for collision of the `touch` with our widget and change the value of `pressed` +to touch.pos if the touch is inside our Widget. Then finally we return True +indicating that we consumed the touch and don't want it propagating any further. + +Finally on line 10:: + + def on_pressed(self, instance, pos): + print ('pressed at {pos}'.format(pos=pos)) + +We define `on_pressed` function that will be called by the property whenever the +property value is changed. + + +**Binding to the property** + +How do you dispatch a property event without having to override the base class +that property is defined in? You Bind to the property:: + + your_widget_instance.bind(property_name=function_name) + +For example consider the following code. + +.. code-block:: python + :linenos: + + class RootWidget(BoxLayout): + + def __init__(self, **kwargs): + super(RootWidget, self).__init__(**kwargs) + self.add_widget(Button()) + cb = CustomBtn() + cb.bind(pressed=self.btn_pressed) + self.add_widget(cb) + self.add_widget(Button()) + + def btn_pressed(self, instance, pos): + print ('pos: printed from root widget: {pos}'.format(pos=.pos)) + +If you run the code as is you will notice two print statements in the console. +One from the on_pressed event that is called inside the `CustomBtn` class and +one from the `btn_pressed` function that we bind to the property change. + +The reason that both the functions are called is simple. Binding doesn't mean +over-riding. You should also take note about the parameters that are passed to +the on_ or the function bound to the property. + +.. code-block:: python + + def btn_pressed(self, instance, pos): + +The first parameter is self which is the instance of the class this function is +defined in. If you use a in-line function like so. + +.. code-block:: python + :linenos: + + cb = CustomBtn() + + def _local_func(instance, pos): + print ('pos: printed from root widget: {pos}'.format(pos=.pos)) + + cb.bind(pressed=_local_func) + self.add_widget(cb) + +Then the first parameter would be `instance` of the class the property is +defined in. + +The last parameter is the `value` which is the new value of the property. + +This is the complete runnable code derived from the snippets above that you can +use to copy and paste into a editor to experiment with. + +.. code-block:: python + :linenos: + + from kivy.app import App + from kivy.uix.widget import Widget + from kivy.uix.button import Button + from kivy.uix.boxlayout import BoxLayout + from kivy.properties import ListProperty + + class RootWidget(BoxLayout): + + def __init__(self, **kwargs): + super(RootWidget, self).__init__(**kwargs) + self.add_widget(Button(text='btn 1')) + cb = CustomBtn() + cb.bind(pressed=self.btn_pressed) + self.add_widget(cb) + self.add_widget(Button(text='btn 2')) + + def btn_pressed(self, instance, pos): + print ('pos: printed from root widget: {pos}'.format(pos=pos)) + + class CustomBtn(Widget): + + pressed = ListProperty([0, 0]) + + def on_touch_down(self, touch): + if self.collide_point(*touch.pos): + self.pressed = touch.pos + + class TestApp(App): + + def build(self): + #thread.start_new_thread( self.rsh(), ()) + return RootWidget() + + + if __name__ == '__main__': + TestApp().run() + + +Some Gotchas in AliasProperty and ReferenceListProperty. + +While defining a AliasProperty you normally define a getter and a setter +function yourself. Here It falls on to you to define when the getter and the +setter functions are called using the `bind` argument. + +Consider the following code. + +.. code-block:: python + :linenos: + + class TODO::