''' Button Behavior =============== The :class:`~kivy.uix.behaviors.button.ButtonBehavior` `mixin `_ class provides :class:`~kivy.uix.button.Button` behavior. You can combine this class with other widgets, such as an :class:`~kivy.uix.image.Image`, to provide alternative buttons that preserve Kivy button behavior. For an overview of behaviors, please refer to the :mod:`~kivy.uix.behaviors` documentation. Example ------- The following example adds button behavior to an image to make a checkbox that behaves like a button:: from kivy.app import App from kivy.uix.image import Image from kivy.uix.behaviors import ButtonBehavior class MyButton(ButtonBehavior, Image): def __init__(self, **kwargs): super(MyButton, self).__init__(**kwargs) self.source = 'atlas://data/images/defaulttheme/checkbox_off' def on_press(self): self.source = 'atlas://data/images/defaulttheme/checkbox_on' def on_release(self): self.source = 'atlas://data/images/defaulttheme/checkbox_off' class SampleApp(App): def build(self): return MyButton() SampleApp().run() See :class:`~kivy.uix.behaviors.ButtonBehavior` for details. ''' __all__ = ('ButtonBehavior', ) from kivy.clock import Clock from kivy.config import Config from kivy.properties import OptionProperty, ObjectProperty, \ BooleanProperty, NumericProperty from time import time class ButtonBehavior(object): ''' This `mixin `_ class provides :class:`~kivy.uix.button.Button` behavior. Please see the :mod:`button behaviors module ` documentation for more information. :Events: `on_press` Fired when the button is pressed. `on_release` Fired when the button is released (i.e. the touch/click that pressed the button goes away). ''' state = OptionProperty('normal', options=('normal', 'down')) '''The state of the button, must be one of 'normal' or 'down'. The state is 'down' only when the button is currently touched/clicked, otherwise its 'normal'. :attr:`state` is an :class:`~kivy.properties.OptionProperty` and defaults to 'normal'. ''' last_touch = ObjectProperty(None) '''Contains the last relevant touch received by the Button. This can be used in `on_press` or `on_release` in order to know which touch dispatched the event. .. versionadded:: 1.8.0 :attr:`last_touch` is a :class:`~kivy.properties.ObjectProperty` and defaults to `None`. ''' min_state_time = NumericProperty(0) '''The minimum period of time which the widget must remain in the `'down'` state. .. versionadded:: 1.9.1 :attr:`min_state_time` is a float and defaults to 0.035. This value is taken from :class:`~kivy.config.Config`. ''' always_release = BooleanProperty(False) '''This determines whether or not the widget fires an `on_release` event if the touch_up is outside the widget. .. versionadded:: 1.9.0 .. versionchanged:: 1.10.0 The default value is now False. :attr:`always_release` is a :class:`~kivy.properties.BooleanProperty` and defaults to `False`. ''' def __init__(self, **kwargs): self.register_event_type('on_press') self.register_event_type('on_release') if 'min_state_time' not in kwargs: self.min_state_time = float(Config.get('graphics', 'min_state_time')) super(ButtonBehavior, self).__init__(**kwargs) self.__state_event = None self.__touch_time = None self.fbind('state', self.cancel_event) def _do_press(self): self.state = 'down' def _do_release(self, *args): self.state = 'normal' def cancel_event(self, *args): if self.__state_event: self.__state_event.cancel() self.__state_event = None def on_touch_down(self, touch): if super(ButtonBehavior, self).on_touch_down(touch): return True if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False touch.grab(self) touch.ud[self] = True self.last_touch = touch self.__touch_time = time() self._do_press() self.dispatch('on_press') return True def on_touch_move(self, touch): if touch.grab_current is self: return True if super(ButtonBehavior, self).on_touch_move(touch): return True return self in touch.ud def on_touch_up(self, touch): if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) assert(self in touch.ud) touch.ungrab(self) self.last_touch = touch if (not self.always_release and not self.collide_point(*touch.pos)): self._do_release() return touchtime = time() - self.__touch_time if touchtime < self.min_state_time: self.__state_event = Clock.schedule_once( self._do_release, self.min_state_time - touchtime) else: self._do_release() self.dispatch('on_release') return True def on_press(self): pass def on_release(self): pass def trigger_action(self, duration=0.1): '''Trigger whatever action(s) have been bound to the button by calling both the on_press and on_release callbacks. This is similar to a quick button press without using any touch events, but note that like most kivy code, this is not guaranteed to be safe to call from external threads. If needed use :class:`Clock ` to safely schedule this function and the resulting callbacks to be called from the main thread. Duration is the length of the press in seconds. Pass 0 if you want the action to happen instantly. .. versionadded:: 1.8.0 ''' self._do_press() self.dispatch('on_press') def trigger_release(dt): self._do_release() self.dispatch('on_release') if not duration: trigger_release(0) else: Clock.schedule_once(trigger_release, duration)