Search code examples
pythonuser-interfacekivykivy-language

Access python list in kivy file


I'm a physicist, and as everyone knows, we like our cocktails. I am therefore trying to build an automatic bartender. Unfortunately, the only experience I have with programming in python is to do physics simulations, and I am not that savvy with coding anyways.

My problem is now this: there is a python list in the class BartenderApp that I want to use in the kivy file, specifically in the LoadNewIngredients in the kivy file, for the spinners to take their options from. I have looked quite a while for a solution and none have worked so far. I know I should be able to put the spinners and labels in the python file using a for loop, but I would much rather have it a bit more clean and keep them in the kivy file.

So if anyone could help me how to pass the list to the kivy file, it'd be much appreciated!

Here is the .py file:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.clock import mainthread
from functools import partial
#Import drink list
from drinks import drink_list, drink_options

class drink:
    def __init__(self,name,ingredients,amount):
        self.name = name
        self.ingredients = ingredients
        self.amount = amount

#Define the different screens
class MainMenu(Screen):
    pass

class UseLastIngredients(Screen):
    pass

class DrinkMenu(Screen):

    #Mainthread will pause shortly to give script time, the rest adds the buttons
    @mainthread
    def on_enter(self):
        self.buttons = []
        self.ids.drinks.clear_widgets()
        for btn in range(len(drink_list)):
            self.buttons.append(Button(text=str(drink_list[btn]['name'])))
            self.buttons[btn].bind(on_press = self.pour_drink)
            self.ids.drinks.add_widget(self.buttons[btn])


    def pour_drink(self, button):
        print(button.text)


class LoadNewIngredients(Screen):
    def spinner_clicked(self, ident, value):
        if ident == 1:
            pass
        if ident == 2:
            pass
        if ident == 3:
            pass
        if ident == 4:
            pass
        if ident == 5:
            pass
        if ident == 6:
            pass
        if ident == 7:
            pass
        if ident == 8:
            pass


#Define the ScreenManager
class MenuManager(ScreenManager):
    pass

#Designate the .kv design file
kv = Builder.load_file('bartenderkv.kv')

class BartenderApp(App):

    #I want to use the ingredients list in the kivy file
    global ingredients
    ingredients = []
    for drink in range(len(drink_list)):
        ings = list(drink_list[drink]['ingredients'].keys())
        for ing in range(len(ings)):
            elem = ings[ing]
            if elem not in ingredients:
                ingredients.append(elem)

    def build(self):
        return kv

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

and here is the .kv file:

#:import Factory kivy.factory.Factory
#:import ScrollView kivy.uix.scrollview

MenuManager:
    MainMenu:
    LoadNewIngredients:
    DrinkMenu:

<MainMenu>:
    name: "MainMenu"
    GridLayout:
        rows: 3
        size: root.width, root.height
        padding: 10
        spacing: 10

        Label:
            text: "Main Menu"
            font_size: 32

        GridLayout:
            cols: 2
            size: root.width, root.height
            spacing: 10
            Button:
                text: "Use Last Ingredients"
                font_size: 32
                on_release: app.root.current = "DrinkMenu"
            Button:
                text: "Load New Ingredients"
                font_size: 32
                on_release: app.root.current = "LoadNewIngredients"
        Button:
            text: "See Permissable Ingredients"
            font_size: 32
            #on_press: print("It Works")
            on_release: Factory.PermissablePopup().open()

<LoadNewIngredients>:
    name: "LoadNewIngredients"
    GridLayout:
        cols: 2
        size: root.width, root.height
        padding: 10
        spacing: 10

        GridLayout:
            size: root.width, root.height
            size_hint_x: 0.4
            rows: 2
            Button:
                text: "Continue"
                font_size: 24
                on_release: app.root.current = "DrinkMenu"
            Button:
                text: "Main Menu"
                font_size: 24
                on_release: app.root.current = "MainMenu"
        GridLayout:
            
            #This is where I want the spinners to take in the ingredients list as options.

            id: choices
            rows: 4
            orientation: 'tb-lr'
            Label:
                id: pump_1
                text: "Pump 1"
                font_size: 24
            Label:
                id: pump_2
                text: "Pump 2"
                font_size: 24
            Label:
                id: pump_3
                text: "Pump 3"
                font_size: 24
            Label:
                id: pump_4
                text: "Pump 4"
                font_size: 24
            #Spinner is the easy drop down version in kivy, lets see how it looks.
            Spinner:
                id: spinner_id_1
                text: "Pump_1"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(1,spinner_id_1.text)

            Spinner:
                id: spinner_id_2
                text: "Pump_2"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(2,spinner_id_2.text)

            Spinner:
                id: spinner_id_3
                text: "Pump_3"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(3,spinner_id_3.text)

            Spinner:
                id: spinner_id_4
                text: "Pump_4"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(4,spinner_id_4.text)

            Label:
                id: pump_5
                text: "Pump 5"
                font_size: 24
            Label:
                id: pump_6
                text: "Pump 6"
                font_size: 24
            Label:
                id: pump_7
                text: "Pump 7"
                font_size: 24
            Label:
                id: pump_8
                text: "Pump 8"
                font_size: 24
            #Spinner is the drop down version, lets see how it looks.
            Spinner:
                id: spinner_id_5
                text: "Pump_5"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(5,spinner_id_5.text)

            Spinner:
                id: spinner_id_6
                text: "Pump_6"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(6,spinner_id_6.text)

            Spinner:
                id: spinner_id_7
                text: "Pump_7"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(7,spinner_id_7.text)

            Spinner:
                id: spinner_id_8
                text: "Pump_8"
                values: ["1", "2", "3"]
                on_text: root.spinner_clicked(8,spinner_id_8.text)

<DrinkMenu>:
    name: "DrinkMenu"
    GridLayout:
        cols: 2
        width: root.width
        height: self.minimum_height
        padding: 10
        spacing: 10
        GridLayout:
            height: root.height
            size_hint_x: 0.4
            rows: 2
            Button:
                text: "Top Up"
                font_size: 24
                on_release:
            Button:
                text: "Main Menu"
                font_size: 24
                on_release: app.root.current = "MainMenu"
        ScrollView:
            size_hint_y: 0.73
            pos_hint: {'x':0, 'y': 0.11}
            do_scroll_x: False
            do_scroll_y: True

            GridLayout:
                id: drinks
                orientation: 'lr-tb'
                size_hint_y: None
                size_hint_x: 1.0
                cols: 3
                height: self.minimum_height
                row_default_height: 180
                row_force_default: True

#Create a rounded button, the @Button is what it inherits
<RoundedButton@Button>
    background_color: (0,0,0,0)
    background_normal: ''
    canvas.before:
        Color:
            rgba:
                (48/255,84/255,150/255,1)\
                if self.state == 'normal' else (0.6,0.6,1,1) # Color is red if button is not pressed, otherwise color is green
        RoundedRectangle:
            size: self.size
            pos: self.pos
            radius: [58]

<PermissablePopup@Popup>
    auto_dismiss: False
    #size_hint: 0.6,0.2
    #pos_hint: {"x":0.2, "top":0.9}
    title: "Permissable Ingredients"
    GridLayout:
        rows: 2
        size: root.width, root.height
        spacing: 10
        GridLayout:
            cols: 2
            Label:
                text: "Sodas"
                font_size: 32
                #Add list of sodas
            Label:
                text: "Alcohol"
                font_size: 32
                #Add list of alcohols

        Button:
            text: "Done"
            font_size: 24
            on_release: root.dismiss()


Solution

  • I finally managed to solve it, maybe not the best solution, but it works.

    In the class LoadNewIngredients I wrote a function that checked what ingredients are available and returns a list with the ingredients. This function is then referenced in the kivy app using root.function_name().

    The .py file:

    #Everything needed for kivy
    from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.core.window import Window
    from kivy.uix.button import Button
    from kivy.clock import mainthread
    from functools import partial
    #Import drink list
    from drinks import drink_list, drink_options
    
    class drink:
        def __init__(self,name,ingredients,amount):
            self.name = name
            self.ingredients = ingredients
            self.amount = amount
    
    #Define the different screens
    class MainMenu(Screen):
        pass
    
    class UseLastIngredients(Screen):
        pass
    
    class DrinkMenu(Screen):
    
        #Mainthread will pause shortly to give script time, the rest adds the buttons
        @mainthread
        def on_enter(self):
            self.buttons = []
            self.ids.drinks.clear_widgets()
            for btn in range(len(drink_list)):
                self.buttons.append(Button(text=str(drink_list[btn]['name'])))
                self.buttons[btn].bind(on_press = self.pour_drink)
                self.ids.drinks.add_widget(self.buttons[btn])
    
        def pour_drink(self, button):
            print(button.text)
    
    class LoadNewIngredients(Screen):
        def spinner_clicked(self, ident, value):
            if ident == 1:
                pass
            if ident == 2:
                pass
            if ident == 3:
                pass
            if ident == 4:
                pass
            if ident == 5:
                pass
            if ident == 6:
                pass
            if ident == 7:
                pass
            if ident == 8:
                pass
        #I want to use the ingredients list in the kivy file
        def get_ingredients(self,*args,**kwargs):
            global ingredients
            ingredients = []
            for drink in range(len(drink_list)):
                ings = list(drink_list[drink]['ingredients'].keys())
                for ing in range(len(ings)):
                    elem = ings[ing]
                    if elem not in ingredients:
                        ingredients.append(elem)
            return ingredients
    
    
    #Define the ScreenManager
    class MenuManager(ScreenManager):
        pass
    
    #Designate the .kv design file
    kv = Builder.load_file('bartenderkv.kv')
    
    class BartenderApp(App):
    
        def build(self):
            return kv
    
    if __name__ == '__main__':
        BartenderApp().run()
    
    

    and here is the .kv file:

    #Need to define everything, the ScreenManager is the entity that keeps tabs
    #on all the different menu windows
    
    #This is for the popup, lets you instansiate a class from anywhere
    #:import Factory kivy.factory.Factory
    #:import ScrollView kivy.uix.scrollview
    
    MenuManager:
        MainMenu:
        LoadNewIngredients:
        DrinkMenu:
    
    <MainMenu>:
        name: "MainMenu"
        GridLayout:
            rows: 3
            size: root.width, root.height
            padding: 10
            spacing: 10
    
            Label:
                text: "Main Menu"
                font_size: 32
    
            GridLayout:
                cols: 2
                size: root.width, root.height
                spacing: 10
                Button:
                    text: "Use Last Ingredients"
                    font_size: 32
                    on_release: app.root.current = "DrinkMenu"
                Button:
                    text: "Load New Ingredients"
                    font_size: 32
                    on_release: app.root.current = "LoadNewIngredients"
            Button:
                text: "See Permissable Ingredients"
                font_size: 32
                on_release: Factory.PermissablePopup().open()
    
    <LoadNewIngredients>:
        name: "LoadNewIngredients"
        GridLayout:
            cols: 2
            size: root.width, root.height
            padding: 10
            spacing: 10
            #size hint sets relative sized, x-dir, y-dir
            GridLayout:
                size: root.width, root.height
                size_hint_x: 0.4
                rows: 2
                Button:
                    text: "Continue"
                    font_size: 24
                    on_release: app.root.current = "DrinkMenu"
                Button:
                    text: "Main Menu"
                    font_size: 24
                    on_release: app.root.current = "MainMenu"
            GridLayout:
                id: choices
                rows: 4
                orientation: 'tb-lr'
                Label:
                    id: pump_1
                    text: "Pump 1"
                    font_size: 24
                Label:
                    id: pump_2
                    text: "Pump 2"
                    font_size: 24
                Label:
                    id: pump_3
                    text: "Pump 3"
                    font_size: 24
                Label:
                    id: pump_4
                    text: "Pump 4"
                    font_size: 24
                Spinner:
                    id: spinner_id_1
                    text: "Pump_1"
                    #This references the get_ingredients function in the main py file
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(1,spinner_id_1.text)
    
                Spinner:
                    id: spinner_id_2
                    text: "Pump_2"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(2,spinner_id_2.text)
    
                Spinner:
                    id: spinner_id_3
                    text: "Pump_3"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(3,spinner_id_3.text)
    
                Spinner:
                    id: spinner_id_4
                    text: "Pump_4"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(4,spinner_id_4.text)
    
                Label:
                    id: pump_5
                    text: "Pump 5"
                    font_size: 24
                Label:
                    id: pump_6
                    text: "Pump 6"
                    font_size: 24
                Label:
                    id: pump_7
                    text: "Pump 7"
                    font_size: 24
                Label:
                    id: pump_8
                    text: "Pump 8"
                    font_size: 24
                Spinner:
                    id: spinner_id_5
                    text: "Pump_5"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(5,spinner_id_5.text)
    
                Spinner:
                    id: spinner_id_6
                    text: "Pump_6"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(6,spinner_id_6.text)
    
                Spinner:
                    id: spinner_id_7
                    text: "Pump_7"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(7,spinner_id_7.text)
    
                Spinner:
                    id: spinner_id_8
                    text: "Pump_8"
                    values: root.get_ingredients()
                    on_text: root.spinner_clicked(8,spinner_id_8.text)
    
    <DrinkMenu>:
        name: "DrinkMenu"
        GridLayout:
            cols: 2
            width: root.width
            height: self.minimum_height
            padding: 10
            spacing: 10
            GridLayout:
                height: root.height
                size_hint_x: 0.4
                rows: 2
                Button:
                    text: "Top Up"
                    font_size: 24
                    on_release:
                Button:
                    text: "Main Menu"
                    font_size: 24
                    on_release: app.root.current = "MainMenu"
            ScrollView:
                size_hint_y: 0.1
                pos_hint: {'x':0, 'y': 0.11}
                do_scroll_x: False
                do_scroll_y: True
    
                GridLayout:
                    id: drinks
                    orientation: 'lr-tb'
                    size_hint_y: None
                    size_hint_x: 1.0
                    cols: 3
                    height: self.minimum_height
                    row_default_height: 100
                    row_force_default: True
    
    <RoundedButton@Button>
        background_color: (0,0,0,0)
        background_normal: ''
        canvas.before:
            Color:
                rgba:
                    (48/255,84/255,150/255,1)\
                    if self.state == 'normal' else (0.6,0.6,1,1)
            RoundedRectangle:
                size: self.size
                pos: self.pos
                radius: [58]
    
    <PermissablePopup@Popup>
        auto_dismiss: False
        #size_hint: 0.6,0.2
        #pos_hint: {"x":0.2, "top":0.9}
        title: "Permissable Ingredients"
        GridLayout:
            rows: 2
            size: root.width, root.height
            spacing: 10
            GridLayout:
                cols: 2
                Label:
                    text: "Sodas"
                    font_size: 32
                    #Add list of sodas
                Label:
                    text: "Alcohol"
                    font_size: 32
                    #Add list of alcohols
    
            Button:
                text: "Done"
                font_size: 24
                on_release: root.dismiss()
    
    

    Hopefully this helps if someone else has the same problem. But if anyone has any other solutions (or suggestions about the code in general), please let me know!