2

I'd like to create dropdown-lists in kivy based on a dictionary property of a class. But something about creating these in a loop is either confusing Kivy or I am just confused. Here's what I have:

for main, ingrediants in self.ingrediants.items():
    print main, ingrediants
    dropdown = DropDown()

    for ingrediant in ingrediants:
        btn = Button(text=ingrediant, size_hint_y=None, height=44)
        btn.bind(on_release=lambda btn: dropdown.select(btn.text))
        dropdown.add_widget(btn)

    trigger = Button(text=main, size_hint=(None, None))
    trigger.bind(on_release=dropdown.open)
    dropdown.bind(on_select=lambda instance, x: setattr(trigger, 'text', x))

    self.toolbar.dropdown_bar.add_widget(trigger)

Here's what my property looks like:

ingrediants = DictProperty(
    {
        'Milk': ['Whole Milk', 'Soy', 'Creamer'],
        'Coffee': ['Drip Coffee', 'Espresso', 'Pour Over'],
        'Sugar': ['Sugar', 'Simple Syrup', 'Raw Sugar'],
    }
)

When this renders, the dropdown bar looks correct, three buttons, BUT, the milk one does not trigger a dropdown, the coffee one triggers it's dropdown, but when selected, changes the sugar button's text, and the third button works normally, triggering a dropdown and changing the buttons text on selection.

I feel like I'm just doing something wrong with my loop. But maybe you can't declare dropdowns like this? Thanks.

EDIT: Here's what I had to do to get it working.

dropdowns = {}
for main, ingrediants in self.ingrediants.iteritems():
    dropdowns[main] = DropDown()

    for ingrediant in ingrediants:
        btn = Button(text=ingrediant, size_hint_y=None, height=44)
        btn.bind(on_release=lambda btn=btn, dropdown=dropdowns[main]: dropdown.select(btn.text))
        dropdowns[main].add_widget(btn)

    trigger = Button(text=main, size_hint=(None, 1))

    trigger.bind(on_release=dropdowns[main].open)
    dropdowns[main].bind(on_select=lambda instance, x, trigger=trigger: setattr(trigger, 'text', x))
    self.toolbar.dropdown_bar.add_widget(trigger)
aeikenberry
  • 378
  • 3
  • 8

1 Answers1

3

I'm pretty sure your problems are mostly to do with the way lambda functions behave in for loops. You can see for instance this previous question for information on why - short answer, every lambda receives the same variable, so only that variable (the last dropdown) does anything.

I didn't have time to create a working example (it's fiddly, and you didn't provide an initial working example), but I'll try to make one later if this isn't enough for you to fix the issue.

I also had a problem with dropdowns not working, but I think that's because you don't store references to them so they get garbage collected. I added dropdowns = ListProperty([]) and self.dropdowns.append(dropdown) to keep references around, which solved the problem of them not appearing.

Community
  • 1
  • 1
inclement
  • 29,124
  • 4
  • 48
  • 60