Search code examples
pythonkivykivy-language

custom widget triggers another custom widget's on_touch_down event


Whenever I press any area in MyWidget then it trigger the first MyButton 's on_touch_down event inside MyLayout class. I am new to kivy and don't understand why this is happening.

<MyButton@Button>:
  background_normal: ''
  background_color: [1,0,1,1]
  font_size: "44dp"
  color:[1,1,0,1]
  border:(1,1,1,1)
  text_size:self.size

<MyWidget@Widget>:
  id:customwidget
  canvas.before:
    Color:
      rgba:0.2,0.44,0.66,1
    Rectangle:
      pos:self.pos
      size:self.size

<MyLayout>:
  MyWidget:
    id:mywidget

  MyButton:
    id:button1
    text: "User Name"
    spacing: "10dp"
    on_touch_down: button1.text= self.text + "?"
    opacity: .8

  MyButton:
    text: "ikinci"
    on_touch_down: root.export_to_png("filename.png")
  MyButton:
    text: "ucuncu"

And this is python:

import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.widget import Widget

class MyLayout(BoxLayout):
    pass

class KivyApp(App):
    def build(self):
        return MyLayout()

if __name__ == "__main__":
    KivyApp().run()

Solution

  • Python code

    1. Create a class MyButton() and implement on_touch_down() method to check for collision of the touch with our widget.

    Snippet - Python code

    class MyButton(Button):
    
        def on_touch_down(self, touch):
            if self.collide_point(*touch.pos):
                print("\tMyButton.on_touch_down:")
                if self.text == 'User Name':
                    self.text= self.text + "?"
                elif self.text == 'ikinci':
                    App.get_running_app().root.export_to_png("filename.png")
                return True
            return super(MyButton, self).on_touch_down(touch)
    

    kv file

    1. Rename dynamic class <MyButton@Button>: to classs rule, <MyButton>:
    2. Remove all on_touch_down: from MyButton:

    Snippet - kv file

    <MyButton>:
        background_normal: ''
        background_color: [1,0,1,1]
        font_size: "44dp"
        color:[1,1,0,1]
        border:(1,1,1,1)
        text_size:self.size
    ...
        MyButton:
            id:button1
            text: "User Name"
            spacing: "10dp"
            opacity: .8
    
        MyButton:
            text: "ikinci"
    
        MyButton:
            text: "ucuncu"
    

    Touch event basics

    By default, touch events are dispatched to all currently displayed widgets. This means widgets receive the touch event whether it occurs within their physical area or not.

    In order to provide the maximum flexibility, Kivy dispatches the events to all the widgets and lets them decide how to react to them. If you only want to respond to touch events inside the widget, you simply check:

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            # The touch has occurred inside the widgets area. Do stuff!
            pass
    

    Output

    Img01 - Clicked Button-User Name Img02 - Clicked Button-ikinci