0

So I have written this basic kivy code. I want to read sentences from a file, and dynamically create buttons for each one of them. Then, I want these buttons to disappear when they are clicked. In a for loop, I create my buttons, and put my buttons with the index i in a list. Then with the on_press method, it should delete itself.

Button_List[i].bind(on_press= lambda x: self.remove_widget(Button_List[i]))

So there is a Button in Button_List[i] , and when it is clicked, it should run:

self.remove_widget(Button_List[i])

so it should delete itself

I have 5 buttons for example, the problem is, that it whichever button I click, it deletes the button with the highest index. And the other buttons dont get deleted. I feel like kivy is only executing the last index, but I am not sure.

Here is my code:

new.py:

import kivy.uix.button as kb
from kivy.app import App
from kivy.uix.widget import Widget
from Nils_Programm_verkürzt import lektionstextlesen
from kivy.uix.gridlayout import GridLayout

sentences = ['example_sentence1','example_sentence2','example_sentence3','example_sentence4','example_sentence5',]

class Button_Widget(Widget):
    def __init__(self, **kwargs):
        super(Button_Widget, self).__init__(**kwargs)
        global Button_List
        Button_List = []
        for i in range(len(sentences)):
         print(i)
         Button_List.append(kb.Button(text=sentences[i],pos=(self.width * i, self.height * i)))
         Button_List[i].size = 50, 50
         print('binding'+ str(i))
         Button_List[i].bind(on_press= lambda x: self.remove_widget(Button_List[i]))
         print(Button_List[i])
         self.add_widget(Button_List[i])




class ButtonApp(App):

    def build(self):
        return Button_Widget()


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

Your help is very much appreciated :).

  • This is kind of *late binding* in python. Have a look at this stack [post](https://stackoverflow.com/questions/3431676/creating-functions-in-a-loop). – ApuCoder Jul 03 '22 at 16:03

1 Answers1

1

That's a common problem when using lambda in a loop. They all get the last value of the loop. A fix is to create a new variable that holds the current loop variable value. So, try replacing:

     Button_List[i].bind(on_press= lambda x: self.remove_widget(Button_List[i]))

with:

        Button_List[i].bind(on_press= lambda x, j=i: self.remove_widget(Button_List[j]))
John Anderson
  • 35,991
  • 4
  • 13
  • 36
  • Thank you very much, just 2 minutes ago I did the exact thing you said, while trying to fix it! Just a question: as the loop loops, the value for j changes, why does lambda not look at the last value for j? Does it store the value for j somewhere? – Delicious_pie Jul 03 '22 at 13:30
  • Don't understand the inner workings myself. I have just seen this before. – John Anderson Jul 03 '22 at 13:31
  • This way (by introducing kwarg `j`) actually `lambda` forces the variable `i` available to its local scope. – ApuCoder Jul 03 '22 at 16:26