diff --git a/kivy/uix/behaviors.py b/kivy/uix/behaviors.py index ba801ddf7..a7682d90c 100644 --- a/kivy/uix/behaviors.py +++ b/kivy/uix/behaviors.py @@ -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()][:] + diff --git a/kivy/uix/togglebutton.py b/kivy/uix/togglebutton.py index 7de7154c1..c44b662f6 100644 --- a/kivy/uix/togglebutton.py +++ b/kivy/uix/togglebutton.py @@ -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