I wanted a solution that allows me to bind events from .kv
files. @inclement solution won't allow me to do that because once you bind the event from .kv
, you can't return True
anymore to tell the parent you handled the event:
Button:
# you can't return True here, neither from the handler itself
on_touch_down: app.button_touched()
So what I've done is to perform collision detection at the parent, emitting a custom on_really_touch_down
only if it doesn't hit any children, and performing collision detection yet again at the child, because all children receive the touch regardless of whatever (it's a mess, I know). Here's the complete solution (requires Kivy >= 1.9.0, because of the usage walk method):
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class CustomTouchMixin(object):
def __init__(self, *args, **kwargs):
super(CustomTouchMixin, self).__init__(*args, **kwargs)
self.register_event_type("on_really_touch_down")
def on_really_touch_down(self, touch):
pass
class CustomTouchWidgetMixin(CustomTouchMixin):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.dispatch("on_really_touch_down", touch)
return super(CustomTouchWidgetMixin, self).on_touch_down(touch)
class CustomTouchLayoutMixin(CustomTouchMixin):
def on_touch_down(self, touch):
for child in self.walk():
if child is self: continue
if child.collide_point(*touch.pos):
# let the touch propagate to children
return super(CustomTouchLayoutMixin, self).on_touch_down(touch)
else:
super(CustomTouchLayoutMixin, self).dispatch("on_really_touch_down", touch)
return True
class TouchHandlerBoxLayout(CustomTouchLayoutMixin, BoxLayout):
pass
class TouchAwareButton(CustomTouchWidgetMixin, Button):
pass
class TestApp(App):
def on_background_touch(self):
print("Background Touched")
def on_button_touch(self, button_text):
print("'{}' Touched".format(button_text))
if __name__ == "__main__":
TestApp().run()
The .kv
:
#:kivy 1.9.0
TouchHandlerBoxLayout:
padding: 50, 50
on_really_touch_down: app.on_background_touch()
TouchAwareButton:
text: "Button One"
on_really_touch_down: app.on_button_touch(self.text)
TouchAwareButton:
text: "Button Two"
on_really_touch_down: app.on_button_touch(self.text)
So this allows me to bind touches from .kv
.