Search code examples
pythonpython-3.xkivy

'DropDown' object has no attribute 'button'


So, I've been building a recipe app in Kivy, and whenever I click on the remove button to remove an ingredient, the app crashes and I get the error above.

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.dropdown import DropDown


class IngredientPopup(Popup):
    def __init__(self, **kwargs):
        super(IngredientPopup, self).__init__(**kwargs)

        # Create a GridLayout for the pop-up window
        layout = GridLayout(cols=2)

        # Add a label to display current ingredients
        self.current_ingredients_label = Label(text='Current Ingredients:\n')
        layout.add_widget(self.current_ingredients_label)

        # Add a button to add ingredients
        add_button = Button(text='Add Ingredient')
        add_button.bind(on_press=self.add_ingredient_popup)
        layout.add_widget(add_button)

        # Add a button to remove ingredients
        remove_button = Button(text='Remove Ingredient')
        remove_button.bind(on_press=self.remove_ingredient_popup)
        layout.add_widget(remove_button)

        # Add the GridLayout to the pop-up window
        self.content = layout

        # Load the current ingredients list
        self.load_ingredients_list()

    def add_ingredient_popup(self, instance):
        # Create a new pop-up window for adding ingredients
        add_popup = AddIngredientPopup()
        add_popup.bind(on_dismiss=self.update_ingredient_list)
        add_popup.open()

    def remove_ingredient_popup(self, instance):
        # Create a new pop-up window for removing ingredients
        remove_popup = RemoveIngredientPopup()
        remove_popup.bind(on_dismiss=self.update_ingredient_list)
        remove_popup.open()

    def load_ingredients_list(self):
        # Load the current ingredients list from a database or file, etc.
        # For now, let's just use a hardcoded list of ingredients
        ingredients_list = ['salt', 'pepper', 'sugar', 'flour']

        # Update the label to display the current ingredients list
        self.current_ingredients_label.text = 'Current Ingredients:\n' + '\n'.join(ingredients_list)

    def update_ingredient_list(self, instance):
        # Update the list of ingredients displayed in the pop-up window
        self.load_ingredients_list()

class AddIngredientPopup(Popup):
    def __init__(self, **kwargs):
        super(AddIngredientPopup, self).__init__(**kwargs)

        # Create a GridLayout for the pop-up window
        layout = GridLayout(cols=2)

        # Add a label to ask for ingredient name
        ingredient_name_label = Label(text='Ingredient Name:')
        layout.add_widget(ingredient_name_label)

        # Add a TextInput widget to enter the ingredient name
        self.ingredient_name_input = TextInput()
        layout.add_widget(self.ingredient_name_input)

        # Add a button to add the new ingredient
        add_ingredient_button = Button(text='Add')
        add_ingredient_button.bind(on_press=self.add_ingredient)
        layout.add_widget(add_ingredient_button)

        # Add the GridLayout to the pop-up window
        self.content = layout

    def add_ingredient(self, instance):
        # Get the ingredient name entered by the user
        ingredient_name = self.ingredient_name_input.text

        # Save the new ingredient to a database or file, etc.
        # For now, let's just print the ingredient name to the console
        print(f"Added ingredient: {ingredient_name}")

        # Close the pop-up window
        self.dismiss()


class RemoveIngredientPopup(Popup):
    def __init__(self, **kwargs):
        super(RemoveIngredientPopup, self).__init__(**kwargs)

        # Create a GridLayout for the pop-up window
        layout = GridLayout(cols=2)

        # Add a label to select ingredient to remove
        ingredient_label = Label(text='Select Ingredient:')
        layout.add_widget(ingredient_label)

        # Add a dropdown menu to select the ingredient to remove
        self.ingredient_dropdown = DropDown()
        self.load_ingredients_list()
        layout.add_widget(self.ingredient_dropdown)

        # Add a button to remove the selected ingredient
        remove_ingredient_button = Button(text='Remove')
        remove_ingredient_button.bind(on_press=self.remove_ingredient)
        layout.add_widget(remove_ingredient_button)

        # Add the GridLayout to the pop-up window
        self.content = layout

    def load_ingredients_list(self):
        # Load the current ingredients list from a database or file, etc.
        # For now, let's just use a hardcoded list of ingredients
        ingredients_list = ['salt', 'pepper', 'sugar', 'flour']

        # Add the ingredients to the dropdown menu
        for ingredient in ingredients_list:
            self.ingredient_dropdown.add_widget(Button(text=ingredient))

    def remove_ingredient(self, instance):
        # Get the name of the selected ingredient
        selected_ingredient = self.ingredient_dropdown.button.text

        # Remove the ingredient from the database or file, etc.
        # For now, let's just print the ingredient name to the console
        print(f"Removed ingredient: {selected_ingredient}")

        # Close the pop-up window
        self.dismiss()


class TopStorage(Button):
    def __init__(self, **kwargs):
        super(TopStorage, self).__init__(**kwargs)
        self.opacity = 0
        self.size_hint = (None, None)
        self.size = (0, 0)
        
    def on_press(self):
        # create popup window
        popup = IngredientPopup(title='Top Storage', size_hint=(None, None), size=(400, 400))
    
        # open popup window
        popup.open()

class TopFridge(Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.opacity = 0
        self.size_hint = (None, None)
        self.size = (0, 0)
        
    def on_press(self):
        # create popup window
        popup = Popup(title='Top Fridge', size_hint=(None, None), size=(400, 400))
        
        # add content to popup window
        label = Label(text='top fridge')
        popup.content = label
        
        # open popup window
        popup.open()

class BottomFridge(Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.opacity = 0
        self.size_hint = (None, None)
        self.size = (0, 0)
        
    def on_press(self):
        # create popup window
        popup = Popup(title='Bottom Fridge', size_hint=(None, None), size=(400, 400))
        
        # add content to popup window
        label = Label(text='bottom fridge')
        popup.content = label
        
        # open popup window
        popup.open()

class Pot(Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.opacity = 0
        self.size_hint = (None, None)
        self.size = (0, 0)
        
    def on_press(self):
        # create popup window
        popup = Popup(title='Pot', size_hint=(None, None), size=(400, 400))
        
        # add content to popup window
        label = Label(text='pot')
        popup.content = label
        
        # open popup window
        popup.open()


class MyApp(App):
    def build(self):
        # Create the main layout
        layout = FloatLayout()

        # Create the background image
        background = Image(source='C:/Users/private/Desktop/KuhinjaV2.jpg')

        # Create the buttons
        button1 = TopStorage(text='storage gore')
        button2 = TopFridge(text='frizider gore')
        button3 = BottomFridge(text='frizider dole')
        button4 = Pot(text='pot')

        # Position the buttons on top of the background image
        button1.pos_hint = {'x': 0.08, 'y': 0.75}
        button1.size_hint = (0.85, 0.2)

        button2.pos_hint = {'x': 0.8, 'y': 0.4}
        button2.size_hint = (0.15, 0.2)

        button3.pos_hint = {'x': 0.8, 'y': 0.035}
        button3.size_hint = (0.15, 0.35)

        button4.pos_hint = {'x': 0.094, 'y': 0.38}
        button4.size_hint = (0.07, 0.07)

        # Add the background and buttons to the layout
        layout.add_widget(background)
        layout.add_widget(button1)
        layout.add_widget(button2)
        layout.add_widget(button3)
        layout.add_widget(button4)
    
        return layout
        return TopStorageButton(text='Top Storage')

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


Since I'm pretty new to this, some explanation in an answer would be appreciated.

I tried many solutions, but it either says "self.dismiss() not defined" or the error I stated before.


Solution

  • Your Remove button triggers the remove_ingredient() method, but that method would need inspect the DropDown in order to find which ingredient was selected. A simpler approach would be to just bind each ingredient button to the remove_ingredient() method. So, in your load_ingredients_list() method just add the binding to remove_ingredient, like this:

    def load_ingredients_list(self):
        # Load the current ingredients list from a database or file, etc.
        # For now, let's just use a hardcoded list of ingredients
        ingredients_list = ['salt', 'pepper', 'sugar', 'flour']
    
        # Add the ingredients to the dropdown menu
        for ingredient in ingredients_list:
            self.ingredient_dropdown.add_widget(Button(text=ingredient, on_release=self.remove_ingredient, size_hint_y=None, height=44))
    

    Then the remove_ingredient() method can just be:

    def remove_ingredient(self, instance):
        # Get the name of the selected ingredient
        selected_ingredient = instance.text
    
        # Remove the ingredient from the database or file, etc.
        # For now, let's just print the ingredient name to the console
        print(f"Removed ingredient: {selected_ingredient}")
    
        # Close the pop-up window
        self.dismiss()