kivy/kivy — pull request #2100 by matham (2014-04-21 17:41:22+00:00)
Title: Introduces rebind keyword for some properties to enable dynamic rebinding

Intro
------

Adds a `rebind` keyword to `ObjectProperty`, `AliasProperty`, and `DictProperty`, Which when set to True for that property, kv rules using this object as an intermediate attribute in a kv rule, will update the bound property when this object changes.

details
---------

The standard behavior is that if there's a kv rule ``text: self.a.b.c.d``, where ``a``, ``b``, and ``c`` are e.g. object properties and ``d`` is a `StringProperty`. Then when the rule is applied, ``text`` becomes bound only to ``d``. If ``a``, ``b``, or ``c`` change, ``text`` still remains bound to ``d``. Furthermore, if any of them were ``None`` when the rule was initially evaluated, e.g. ``b`` was ``None``; then ``text`` is bound to ``b`` and will not become bound to ``d`` even when ``b`` is changed to not be ``None``.

By using and setting ``rebind`` to ``True`` of these intermediate properties, however, the rule will now be re-evaluated and all the properties rebound when that intermediate property changes. E.g. in the xample above, whenever ``b`` changes or becomes not ``None`` if it was ``None`` before, ``text`` is evaluated again and becomes rebound to ``d``. The overall result is that ``text`` is now bound to all the properties among ``a``, ``b``, or ``c`` that have ``rebind`` set to ``True``.

Points:
--------

* I added nose tests for it.
* In terms of performance, it should if rebind is not used or set to `False`, it should have little effect. The only potential source of slowdown is that when applying the rule initially, we keep a list of all the intermediate properties. But I doubt that would make a difference. If you do use `rebind`, it only adds another binding, so beyond that it really should not create a performance hit.
* I only added it for `ObjectProperty`, `AliasProperty`, and `DictProperty`. I was gonna add it to `ListProperty`, but when parsing, kv ignores all the attributes of the list. I.e. if my rule says `text: self.list_prop[10].a.b.c.d`, the parser will only add `self` and `list_prop` to the watched keys. So unless the parser is expanded, there's no way do do this.

Eample
----------

Here's an example to show the difference, try changing rebind to False/True of the `ObjWidget`, `DictWidget`, `AliasWidget` to see the difference rebind makes:

from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.properties import ObjectProperty, DictProperty, AliasProperty

class ObjWidget(Label):
button = ObjectProperty(None, rebind=True)

class DictWidget(Label):
button = DictProperty({'button': None}, rebind=True)

class AliasWidget(Label):
_button = None

def setter(self, value):
self._button = value
return True

def getter(self):
return self._button
button = AliasProperty(getter, setter, rebind=True)

root = Builder.load_string('''
BoxLayout:
ToggleButton:
id: button_a
text: 'Button A'
ToggleButton:
id: button_b
text: 'Button B'
ToggleButton:
id: button
text: 'button {} tracked'.format('none' if obj_btn.button is None else\
('a' if self.state == 'down' else 'b'))
on_state:
obj_btn.button = button_a if self.state == 'down' else button_b
dict_btn.button.button = button_a if self.state == 'down' else\
button_b
alias_btn.button = button_a if self.state == 'down' else button_b
BoxLayout:
orientation: 'vertical'
ObjWidget:
id: obj_btn
text: 'ObjectProp state: {}'.format(self.button.state if\
self.button is not None else 'Un-set')
DictWidget:
id: dict_btn
text: 'DictProp state: {}'.format(self.button.button.state if\
self.button.button is not None else 'Un-set')
AliasWidget:
id: alias_btn
text: 'AliasProp state: {}'.format(self.button.state if\
self.button is not None else 'Un-set')
''')

runTouchApp(root)