0

Im writing an app in Kivy which automaticaly adds Buttons and gives them a unique id using a for loop. This id is then used as a key in the dictionary for a link. So the dictionary works fine and after printing it, it outputs {'button0': 'somewebsite', 'button1': 'other website', 'button2': 'andanotherwebsite'} which is exactly what I want but the button callback function always prints out button2 instead of its own id. Am I assigning the ids wrong? The example below demonstrates my problem.

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivymd.utils import asynckivy
from kivy.clock import Clock


class TestButton(Button):
    def callback(self):
        print(self.id)


class RootWidget(BoxLayout):
    def __init__(self):
        super().__init__()
        
        self.links = ["somewebsite", "other website", "andanotherwebsite"]
        self.dic_btn_to_lnk = {}
        
        self.size_hint = (None, None)
        self.size = ("600dp", "50dp")
        Clock.schedule_once(self.add_widgets, 0)

    def add_widgets(self, *args):
        async def update():
            number = 0
            for link in self.links:
                button = TestButton()

                button.text = link
                button.size = ("200dp", "50dp")
                button.pos_hint = {"center_x": .5}

                btn_id = "button" + str(number)
                button.id = btn_id
                button.bind(on_release=lambda x: button.callback())
                number += 1

                self.dic_btn_to_lnk[btn_id] = link

                self.add_widget(button)

                print(self.dic_btn_to_lnk)
        asynckivy.start(update())


class TestApp(App):
    def build(self):
        return RootWidget()


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

1 Answer 1

2

The problem is that your on_release binding is calling button.callback(), and button will be the last Button added by the time the on_release is triggered. The solution is to use partial, which freezes its arguments to their values when partial is executed, so the on_release calls the correct button.callback. Like this:

button.bind(on_release=partial(button.callback))

And to simplify the above, the definition of callback is changed to:

class TestButton(Button):
    def callback(self, instance):
        print(self.id)
Sign up to request clarification or add additional context in comments.

1 Comment

I din't know, partial before and your post make me search about it, thanks. But I don't understand how partial is useful is this case? I just replaced his bind call by button.bind(on_release=button.callback) and change callback as you suggest and it work fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.