在我的应用中,我想分别处理背景触摸和小部件触摸。 Widget documentation忽略了如何防止来自.kv事件的冒泡。这是一个小测试案例:
from kivy.app import App
class TestApp(App):
def on_background_touch(self):
print("Background Touched")
return True
def on_button_touch(self):
print("Button Touched")
if __name__ == "__main__":
TestApp().run()
和.kv:
#:kivy 1.8.0
BoxLayout:
orientation: "vertical"
on_touch_down: app.on_background_touch()
padding: 50, 50
Button:
text: "Touch me!"
on_touch_down: app.on_button_touch()
结果:触摸背景或按钮会触发两个处理程序。我应该执行碰撞检测,还是有其他方式?
答案 0 :(得分:3)
您应该执行碰撞检测。例如,在类定义中:
class YourWidget(SomeWidget):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
do_stuff()
编辑:实际上,您的方法无论如何都无法正常工作,因为Button与BoxLayout重叠。我可能会改为创建一个BoxLayout子类并覆盖on_touch_down,先调用super,然后再调用False(表示尚未使用触摸)进行BoxLayout交互。
答案 1 :(得分:1)
我想要一个允许我绑定.kv
个文件的事件的解决方案。 @inclement解决方案不允许我这样做,因为一旦您从.kv
绑定事件,您就不能再返回True
来告诉您处理该事件的父级:
Button:
# you can't return True here, neither from the handler itself
on_touch_down: app.button_touched()
所以我所做的就是在父母身上执行碰撞检测,只有当它没有击中任何孩子时才会发出自定义on_really_touch_down
,并且再次对孩子进行碰撞检测,因为所有孩子都会收到无论如何都是触摸(这是一团糟,我知道)。这是完整的解决方案(需要 Kivy> = 1.9.0 ,因为使用walk方法):
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()
.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)
因此,这允许我绑定来自.kv
的触摸。
答案 2 :(得分:0)
通过.kv
文件/字符串语法绑定触摸事件的方法是可行的,这里是一个在检测到碰撞时修改调用者背景的示例。
<cLabel@Label>:
padding: 5, 10
default_background_color: 0, 0, 0, 0
selected_background_color: 0, 1, 0, 1
on_touch_down:
## First & second arguments passed when touches happen
caller = args[0]
touch = args[1]
## True or False for collisions & caller state
caller_touched = caller.collide_point(*touch.pos)
background_defaulted = caller.background_color == caller.default_background_color
## Modify caller state if touched
if caller_touched and background_defaulted: caller.background_color = self.selected_background_color
elif caller_touched and not background_defaulted: caller.background_color = caller.default_background_color
background_color: 0, 0, 0, 0
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
为了完整起见,以下是如果在触摸激活的布局中如何使用上述代码,只有在没有孩子(或孙子孙女等)也与同一事件发生冲突时才会触发。
<cGrid@GridLayout>:
on_touch_down:
caller = args[0]
touch = args[1]
caller_touched = caller.collide_point(*touch.pos)
spawn_touched = [x.collide_point(*touch.pos) for x in self.walk(restrict = True) if x is not self]
## Do stuff if touched and none of the spawn have been touched
if caller_touched and True not in spawn_touched: print('caller -> {0}\ntouch -> {1}'.format(caller, touch))
cols: 2
size_hint_y: None
height: sorted([x.height + x.padding[1] for x in self.children])[-1]
cLabel:
text: 'Foo'
size_hint_y: None
height: self.texture_size[1]
cLabel:
text: 'Bar'
size_hint_y: None
height: self.texture_size[1] * 2
我可能已经
texture_size
向后,或者可能没有,但是高度欺骗在大多数情况下可以被忽略,因为它的目的是帮助制作父布局更可点击。来电者的变色和打印触摸对象应该用
do_stuff()
或类似方法替换,因为它们会使示例自包含,并显示处理调用者保存状态的另一种方式。