Search code examples
pythonkivy

Binding a function to labels in kivy is passing the wrong parameters to the function


I have a bunch of labels that have been created using a for loop, and each one has a function binded to it that passes a different paramater. However, when I click on any of the labels, it calls the function using the parameters of the last label, no matter which one is clicked.

here is my code:

from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.properties import ObjectProperty, StringProperty
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.app import App

def findVideos(name): #returns a 2d array

class findWindow(Screen):
    songName = ObjectProperty()
    videoInfo = []

    def submitBtn(self):
        findWindow.videoInfo = findVideos(self.songName.text)

        for video in findWindow.videoInfo:
            label = Label(text='[ref=world]world[/ref]', size_hint_y=None, height=12, text_size=(250, 20), markup=True)
            label.bind(on_ref_press=lambda x, y: findWindow.changeScreen(self, 'download', label)) #i believe this is the problem
            findWindow.videoInfo[findWindow.videoInfo.index(video)].append(label)
            self.ids.gridLay.add_widget(label)

    def changeScreen(self, screen, label):
        self.parent.current = screen
        for video in findWindow.videoInfo:
            if video[2] == label: self.parent.current_screen.ids.downloadWindowId.text = video[0] #always matches with the last item in the array

class MyApp(App):
    def build(self):
        sm = ScreenManager(transition=NoTransition())
        sm.add_widget(findWindow(name='find'))
        
        return sm

MyApp().run()


Here is my kivy:

<findWindow>:
    songName: songName

    FloatLayout:
        TextInput:
            id: songName
            hint_text: "Search"
            multiline: False
            font_size: 14
            size_hint: (0.6, 0.05)
            pos_hint: {"x":0.075, "y":0.925}

        Button:
            text: "Submit"
            on_release: root.submitBtn()
            font_size: 14
            size_hint: (0.2, 0.05)
            pos_hint: {"x":0.675, "y":0.925}

        ScrollView:
            do_scroll_x: False
            do_scroll_y: True
            size_hint: (0.90, 0.7)
            pos_hint: {"x":0.05, "y":0.11}

            GridLayout:
                id: gridLay
                cols: 1
                size_hint_y: None
                spacing: 14 
                height: self.minimum_height

I don't know what to try at this point. I don't understand why .bind wouldn't bind the correct label in the function? I've tried using findWindow.changeScreen(self, 'download', video[0])) aswell, however it still used the video[0] from the last label.


Solution

  • The lambda function is not evaluated until the reference is pressed, so the label parameter will always be the last value of the label. The fix is to create a temporary parameter that takes the current value of label and uses that in the lambda. Like this:

            label.bind(on_ref_press=lambda x, y, lab=label: findWindow.changeScreen(self, 'download',
                                                                         lab))  # i believe this is the problem
    

    The temporary parameter lab gets the current value of label.