Merge pull request #1471 from matham/toggle_behavior

Add a ToggleButton type behavior
This commit is contained in:
Gabriel Pettier 2013-09-14 12:53:45 -07:00
commit 709f0574bc
2 changed files with 96 additions and 88 deletions

View File

@ -19,10 +19,11 @@ do::
'''
__all__ = ('ButtonBehavior', )
__all__ = ('ButtonBehavior', 'ToggleButtonBehavior')
from kivy.clock import Clock
from kivy.properties import OptionProperty
from kivy.properties import OptionProperty, ObjectProperty
from weakref import ref
class ButtonBehavior(object):
@ -44,7 +45,6 @@ class ButtonBehavior(object):
:data:`state` is an :class:`~kivy.properties.OptionProperty`.
'''
def __init__(self, **kwargs):
self.register_event_type('on_press')
self.register_event_type('on_release')
@ -106,6 +106,7 @@ class ButtonBehavior(object):
'''
self._do_press()
self.dispatch('on_press')
def trigger_release(dt):
self._do_release()
self.dispatch('on_release')
@ -114,3 +115,92 @@ class ButtonBehavior(object):
else:
Clock.schedule_once(trigger_release, duration)
class ToggleButtonBehavior(ButtonBehavior):
'''ToggleButton behavior, see ToggleButton module documentation for more
information.
.. versionadded:: 1.8.0
'''
__groups = {}
group = ObjectProperty(None, allownone=True)
'''Group of the button. If None, no group will be used (button is
independent). If specified, :data:`group` must be a hashable object, like
a string. Only one button in a group can be in 'down' state.
:data:`group` is a :class:`~kivy.properties.ObjectProperty`
'''
def __init__(self, **kwargs):
self._previous_group = None
super(ToggleButtonBehavior, self).__init__(**kwargs)
def on_group(self, *largs):
groups = ToggleButtonBehavior.__groups
if self._previous_group:
group = groups[self._previous_group]
for item in group[:]:
if item() is self:
group.remove(item)
break
group = self._previous_group = self.group
if group not in groups:
groups[group] = []
r = ref(self, ToggleButtonBehavior._clear_groups)
groups[group].append(r)
def _release_group(self, current):
if self.group is None:
return
group = self.__groups[self.group]
for item in group[:]:
widget = item()
if widget is None:
group.remove(item)
if widget is current:
continue
widget.state = 'normal'
def _do_press(self):
self._release_group(self)
self.state = 'normal' if self.state == 'down' else 'down'
def _do_release(self):
pass
@staticmethod
def _clear_groups(wk):
# auto flush the element when the weak reference have been deleted
groups = ToggleButtonBehavior.__groups
for group in list(groups.values()):
if wk in group:
group.remove(wk)
break
@staticmethod
def get_widgets(groupname):
'''Return the widgets contained in a specific group. If the group
doesn't exist, an empty list will be returned.
.. important::
Always release the result of this method! In doubt, do::
l = ToggleButtonBehavior.get_widgets('mygroup')
# do your job
del l
.. warning::
It's possible that some widgets that you have previously deleted are
still in the list. Garbage collector might need more elements before
flushing it. The return of this method is informative, you've been
warned!
'''
groups = ToggleButtonBehavior.__groups
if groupname not in groups:
return []
return [x() for x in groups[groupname] if x()][:]

View File

@ -23,95 +23,13 @@ for a Button class.
__all__ = ('ToggleButton', )
from weakref import ref
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.uix.behaviors import ToggleButtonBehavior
class ToggleButton(Button):
class ToggleButton(ToggleButtonBehavior, Button):
'''Toggle button class, see module documentation for more information.
'''
__groups = {}
group = ObjectProperty(None, allownone=True)
'''Group of the button. If None, no group will be used (button is
independent). If specified, :data:`group` must be a hashable object, like
a string. Only one button in a group can be in 'down' state.
:data:`group` is a :class:`~kivy.properties.ObjectProperty`
'''
def __init__(self, **kwargs):
self._previous_group = None
super(ToggleButton, self).__init__(**kwargs)
def on_group(self, *largs):
groups = ToggleButton.__groups
if self._previous_group:
group = groups[self._previous_group]
for item in group[:]:
if item() is self:
group.remove(item)
break
group = self._previous_group = self.group
if group not in groups:
groups[group] = []
r = ref(self, ToggleButton._clear_groups)
groups[group].append(r)
def _release_group(self, current):
if self.group is None:
return
group = self.__groups[self.group]
for item in group[:]:
widget = item()
if widget is None:
group.remove(item)
if widget is current:
continue
widget.state = 'normal'
def _do_press(self):
self._release_group(self)
self.state = 'normal' if self.state == 'down' else 'down'
def _do_release(self):
pass
@staticmethod
def _clear_groups(wk):
# auto flush the element when the weak reference have been deleted
groups = ToggleButton.__groups
for group in list(groups.values()):
if wk in group:
group.remove(wk)
break
@staticmethod
def get_widgets(groupname):
'''Return the widgets contained in a specific group. If the group
doesn't exist, an empty list will be returned.
.. important::
Always release the result of this method! In doubt, do::
l = ToggleButton.get_widgets('mygroup')
# do your job
del l
.. warning::
It's possible that some widgets that you have previously deleted are
still in the list. Garbage collector might need more elements before
flushing it. The return of this method is informative, you've been
warned!
.. versionadded:: 1.3.0
'''
groups = ToggleButton.__groups
if groupname not in groups:
return []
return [x() for x in groups[groupname] if x()][:]
pass