Doc: extend dispatching a property event in guide2

This commit is contained in:
qua-non 2013-01-08 22:17:20 +05:30
parent ee8b36de08
commit e322ed73d9
1 changed files with 175 additions and 3 deletions

View File

@ -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_<property_name> 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_<property_name> 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_<property_name> 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::