Bind a function to multiple dynamically created bu

2019-06-02 19:02发布

Problem

I want to create multiple buttons and bind them to a function. The problem is that whenever I click on one button, the function is called multiple times. It seems to be a problem with the event connection. When I look at the instance that called the function when I pressed a button, it seems that the function gets called from every button at once?!

KV Code:

...
# This is the button that I'am using
<ProjectSelectButton>:
height: 35
background_color: 0,0,1,1
on_touch_down: self.click_on_button(args[0], args[1])

...

# The buttons are added to this grid
ButtonsPlacedHere@GridLayout:
    id: active_projects
    cols: 1
    size_hint_y: None
    height: self.minimum_height
    spacing: 1
...

Python Code:

...
class ProjectSelectButton(Button):
    def click_on_button(self, instance, touch, *args):
        print(instance)
        if touch.button == 'right':
            print(self.id, "right mouse clicked")
        else touch.buttom == 'left':
            print(self.id, "left mouse clicked")

...

# The part of my programm that creates the buttons
# projects is a dictionary
for key, project in data_manager.resource_pool.projects.items():
    print(project.project_name)
    button= ProjectSelectButton(text=project.project_name, id=key, size_hint_y=None)
    # Bind without KV-File (same result)
    # label.bind(on_touch_up=self.click_on_button)
    ids.active_projects.add_widget(button)

Example Output:

What I get, when I click on a single button!

<guiMain.ProjectSelectButton object at 0x0BA34260>
ID01 right mouse clicked
<guiMain.ProjectSelectButton object at 0x0BA34260>
ID01 right mouse clicked
<guiMain.ProjectSelectButton object at 0x0BA28F10>
ID02 right mouse clicked
<guiMain.ProjectSelectButton object at 0x0BA28F10>
ID02 right mouse clicked
<guiMain.ProjectSelectButton object at 0x0BA22C00>
ID03 right mouse clicked
<guiMain.ProjectSelectButton object at 0x0BA22C00>
ID03 right mouse clicked

What I want when I press for example the button with ID 01:

<guiMain.ProjectSelectButton object at 0x0BA34260>
ID01 right mouse clicked

Question

How do I create multiple buttons which will call a single function when they are pressed?

1条回答
闹够了就滚
2楼-- · 2019-06-02 19:31

Programming Guide » Input management » Touch events

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.

Solution

Use self.collide_point method in click_on_button method. When it collides, you should get only one button. Please refer to my example for details.

Snippets

class ProjectSelectButton(Button):
    def click_on_button(self, instance, touch, *args):
        print(instance)
        if self.collide_point(*touch.pos):
            if touch.button == 'right':
                print(self.id, "right mouse clicked")
            elif touch.buttom == 'left':
                print(self.id, "left mouse clicked")
            return True
        return super(ProjectSelectButton, self).on_touch_down(touch)

Example

main.py

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button


class CreateButton(Button):

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            if touch.button == "right":
                print(self.id, "right mouse clicked")
            elif touch.button == "left":
                print(self.id, "left mouse clicked")
            else:
                print(self.id)
            return True
        return super(CreateButton, self).on_touch_down(touch)


class OnTouchDownDemo(GridLayout):

    def __init__(self, **kwargs):
        super(OnTouchDownDemo, self).__init__(**kwargs)
        self.build_board()

    def build_board(self):
        # make 9 buttons in a grid
        for i in range(0, 9):
            button = CreateButton(id=str(i))
            self.add_widget(button)


class OnTouchDownApp(App):

    def build(self):
        return OnTouchDownDemo()


if __name__ == '__main__':
    OnTouchDownApp().run()

ontouchdown.kv

#:kivy 1.10.0

<CreateButton>:
    font_size: 50
    on_touch_down: self.on_touch_down

<OnTouchDownDemo>:
    rows: 3
    cols: 3
    row_force_default: True
    row_default_height: 150
    col_force_default: True
    col_default_width: 150
    padding: [10]
    spacing: [10]
查看更多
登录 后发表回答