Search code examples
pythonkivykivy-languagekivymd

How to send an object to a function written in on_press?


They have a simple model

class Card_:
    def __init__(self, numb, adr, text, date, st):
        self.number = numb
        self.adress = adr
        self.text = text
        self.date = date
        self.status = st

have a code to create MDlist lines

for i, card in enumerate(_card_list):
                    print(card)
                    tabs_list[-1].ids.main_list.add_widget(ThreeLineAvatarIconListItem(text=f'Задача № {card.number}, {card.text}',
                                                                            secondary_text=str(card.data),
                                                                            tertiary_text=card.status,
                                                                            on_press=lambda t: self.card_open(card)))

But on_press i receive the last in 'for' card-object

<Card.Card_ object at 0x000001CA350406C8>

<Card.Card_ object at 0x000001CA3F402048>

<Card.Card_ object at 0x000001CA3F999F48>

<Card.Card_ object at 0x000001CA3F99A2C8>

<Card.Card_ object at 0x000001CA3F99AD08>

<Card.Card_ object at 0x000001CA3F402088>

<Card.Card_ object at 0x000001CA3F999FC8>

<Card.Card_ object at 0x000001CA3F402188>

<Card.Card_ object at 0x000001CA3F99AA88>

<Card.Card_ object at 0x000001CA3F99A188>

[INFO ] [Base ] Start application main loop

/Print (card) in card_open/

<Card.Card_ object at 0x000001CA3F99A188>

<Card.Card_ object at 0x000001CA3F99A188>

card_open function

def card_open(self, card):
    print(card)

    self.root.transition.direction = 'left'
    self.root.current = 'card'
    self.root.ids.content.title = f"Задача №{card.number}"

Whyyyy i receive the last in 'for' card-object?

I want each line to have its own card


Solution

  • If I understood you right, your problem is in Python's lambda late binding. When you define the closure, your arguments of the closure are resolved at the moment of the call, not at the moment of the defining. So, you need another way to store your card associated with the ThreeLineAvatarIconListItem.

    The first possible solution is a bit hacky but usable - you can provide a "default" argument to the lambda as the same object and it will work:

    ThreeLineAvatarIconListItem(..., on_press=lambda t, card=card: self.card_open(card))
    

    By this, you provide default argument that is resolved during lambda defining.

    The second (generically more valid) approach is to save your card locally in the object and use this card in the card_open function:

    ...
    for i, card in enumerate(_card_list):
        print(card)
        new_widg = ThreeLineAvatarIconListItem(...)
        new_widg.card_inside = card
        new_widg.on_press = lambda t: self.card_open(t)
        tabs_list[-1].ids.main_list.add_widget(new_widg)
    ...
    
    def card_open(self, widg):
        card = widg.card_inside
        ...
    

    It's all assuming on_press send object itself during call.