Search code examples
pythonbuttonevent-handlingkivybuttonclick

button on press being called before button press


I am trying to add a set of buttons to a grid layout scroll view using for loop. But the on press event is being triggered for all the buttons even before pressing the buttons. How can I fix this?

from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivy.app import runTouchApp
import webbrowser


def btnsclicked(id, url):
    print 'btn id '+id+' clicked'
    webbrowser.open(url)

layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
layout.bind(minimum_height=layout.setter('height'))

for i in range(5):
    url= 'https://www.google.com'
    btn = Button(text=str(i), size_hint_y=None, height=40, id='b'+str(i))
    btn.bind(on_press =btnsclicked('b'+str(i), url))
    layout.add_widget(btn)
root = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
root.add_widget(layout)

runTouchApp(root)

Solution

  • because you are actually calling your callback when passing args:

    btn.bind(on_press =btnsclicked('b'+str(i), url))
    

    Doc example says:

    def callback(instance):
        print('The button <%s> is being pressed' % instance.text)
    btn1 = Button(text='Hello world 1')
    btn1.bind(on_press=callback)
    

    You have to pass a function (not a function call) accepting only 1 argument: instance of the activated widget.

    do this using lambda expressions that define functions in-line:

    btn.bind(on_press = lambda x : btnsclicked('b'+str(i), url))
    

    When an event is triggered, the on_press callback is called with one argument: the widget id. That calls your lambda function which calls btnsclicked discarding this argument (that you don't need here), but passing your text id and the url you want to associate to.