Search code examples
pythonvariablestkintersqlitescoping

How to keep a running total inside a function and allow it to be accessed outside of the function?


I want to keep a running total of how many calories a user has eaten and then insert this into a database whilst also giving feedback on the amount of calories the user has eaten. When I define TotalCalories outside the function and set it to a global variable (so that it can be used inside another function), I get the error UnboundLocalError: local variable 'TotalCalories' referenced before assignment

def FoodSelection(choice):
    global CurrentPupil
    CurrentPupil = ""
    global FoodSelectionWindow
    FoodSelectionWindow = Toplevel()
    FoodSelectionWindow.configure(bg="black")

    DateAdded = datetime.date.today()
    TimeTaken = 0
    NoFoodOption = 0

    #The validation timer starts here:

    while TimeTaken < 2:
        time.sleep(60)
        TimeTaken = TimeTaken +1

    #and ends here

    class food():
        def __init__(self, name = "no name", calories = 0, photo = ""):
            self.name = name
            self.calories = calories
            self.photo = photo

    BoiledEggs = food("Boiled Egg", 155, PhotoImage(file="BoiledEgg.gif"))
    ScrambledEggs = food("Scrambled Egg", 148, PhotoImage(file="ScrambledEgg.gif"))
    FriedEggs = food("Fried Egg", 196, PhotoImage(file="FriedEgg.gif"))
    PoachedEggs = food ("Poached Egg", 143, PhotoImage(file="PoachedEgg.gif"))
    Toast = food("Toast", 313, PhotoImage(file="Toast.gif"))
    Bacon = food("Bacon", 514, PhotoImage(file="Bacon.gif"))
    Cereal = food("Cereal", 379, PhotoImage(file="Cereal.gif"))
    Porridge = food("Porridge", 68, PhotoImage(file="Cereal.gif"))
    NoBreakfast = food("No Breakfast", 0, PhotoImage(file="NoBreakfast.gif"))

    def NoFood():
        FoodChoices("No Food")

    def BoiledEggsFunct():
        FoodChoices("Boiled Eggs")

    def FriedEggsFunct():
        FoodChoices("Fried Eggs")

    def ScrambledEggsFunct():
        FoodChoices("Scrambled Eggs")

    def PoachedEggsFunct():
        FoodChoices("Poached Eggs")

    def ToastFunct():
        FoodChoices("Toast")

    def BaconFunct():
        FoodChoices("Bacon")

    def CerealFunct():
        FoodChoices("Cereal")

    def PorridgeFunct():
        FoodChoices("Porridge")

    global TotalCalories
    TotalCalories = 0

    def FoodChoices(selection):

        if selection == "No Food":
            TotalCalories = NoBreakfast.calories

        elif selection == "Boiled Eggs":
            TotalCalories = BoiledEggs.calories + TotalCalories

        elif selection == "Fried Eggs":
            TotalCalories = FriedEggs.calories + TotalCalories

        elif selection == "Scrambled Eggs":
            TotalCalories = ScrambledEggs.calories + TotalCalories

        elif selection == "Poached Eggs":
            TotalCalories = PoachedEggs.calories + TotalCalories

        elif selection == "Toast":
            TotalCalories = Toast.calories + TotalCalories

        elif selection == "Bacon":
            TotalCalories = Bacon.calories + TotalCalories

        elif selection == "Cereal":
            TotalCalories = Cereal.calories + TotalCalories

        elif selection == "Porridge":
            TotalCalories = Porridge.calories + TotalCalories


        if choice == 'chris':
            CurrentPupil = "Chris"
            with db:
                cursor.execute(''' INSERT INTO 'Breakfast_History' (CaloriesTotal, DateAddded, PupilNames, PupilID) VALUES (?,?,?,?)''',
                               ([TotalCalories, DateAdded, CurrentPupil,"1"]))
        elif choice == 'josh':
            CurrentPupil = "Josh"
            with db:
                cursor.execute(''' INSERT INTO 'Breakfast_History' (CaloriesTotal, DateAddded, PupilNames, PupilID) VALUES (?,?,?,?)''',
                           ([TotalCalories, DateAdded, CurrentPupil,"2"]))
        elif choice == 'sam':
            CurrentPupil = "Sam"
            with db:
                cursor.execute(''' INSERT INTO 'Breakfast_History' (CaloriesTotal, DateAddded, PupilNames, PupilID) VALUES (?,?,?,?)''',
                           ([TotalCalories, DateAdded, CurrentPupil,"3"]))
        elif choice == 'daniel':
            CurrentPupl = "Daniel"
            with db:
                cursor.execute(''' INSERT INTO 'Breakfast_History' (CaloriesTotal, DateAddded, PupilNames, PupillID) VALUES (?,?,?,?)''',
                           ([TotalCalories, DateAdded, CurrentPupil, "4"]))
        elif choice == 'jim':
            CurrentPupil = "Jim"
            with db:
                cursor.execute(''' INSERT INTO 'Breakfast_History' (CaloriesTotal, DateAddded, PupilNames, PupilID) VALUES (?,?,?,?)''',
                           ([TotalCalories, DateAdded, CurrentPupil,"5"]))

        elif choice == 'sean':
            CurrentPupil = "Sean"
            with db:
                cursor.execute(''' INSERT INTO 'Breakfast_History' (CaloriesTotal, DateAddded, PupilNames, PupilID) VALUES (?,?,?,?)''',
                           ([TotalCalories, DateAdded, CurrentPupil,"6"]))

        db.commit()

    def FeedbackScreen():
        FinishWindow = Toplevel()

        if TotalCalories > 0 and TotalCalories < 1000:
            HealthyLabel = Label(FinishWindow, text = "Congratulations, you are healthy!", font=("Comic Sans MS", 25), fg = "light green",
                  bg = "black")
            HealthyLabel.grid(columnspan=10)

        elif TotalCalories > 1000:
            UnhealthyLabel = Label(FinishWindow, text = "Try to eat healthier tomorrow. See personalised advice", font=("Comic Sans MS", 25), fg = "yellow",
                  bg = "black")
            UnhealthyLabel.grid(columnspan=10)

        elif NoFoodOption == 1:
            NoFoodLabel = Label(FinishWindow, text = "Not eating can be harmful to your health. See personalised advice", font=("Comic Sans MS", 25), fg = "red",
                  bg = "black")
            NoFoodLabel.grid(columnspan=10)

        else:
            Error = Label(FinishWindow, text = "error", font=("Comic Sans MS", 25), fg = "red",
                  bg = "black")
            NoFoodLabel.grid(columnspan=10)    

As you can see, I need to use TotalCalories in a variety of functions throughout this code and so I thought that by making it a global variable, I would be able to do this.

Any suggestions?


Solution

  • Here is a MCVE of your problem:

    global TotalCalories
    TotalCalories = 0
    
    def FoodChoices(selection):
    
        if selection == "No Food":
            TotalCalories = 100
    
        elif selection == "Boiled Eggs":
            TotalCalories = 100 + TotalCalories
    
    FoodChoices("Boiled Eggs")
    

    Running this code produces this output:

    $ python test.py
    Traceback (most recent call last):
      File "test.py", line 13, in <module>
        FoodChoices("Boiled Eggs")
      File "test.py", line 11, in FoodChoices
        TotalCalories = 100 + TotalCalories
    UnboundLocalError: local variable 'TotalCalories' referenced before assignment
    

    And inserting global TotalCalories into the function makes it go away:

    def FoodChoices(selection):
        global TotalCalories
    
        if selection == "No Food":
            TotalCalories = 100
    
        elif selection == "Boiled Eggs":
            TotalCalories = 100 + TotalCalories
    

    Python requires that a global name be declared in the function where you are going to use it, if you intend to rebind the global. If you're just reading from it, or mutating it, then you can get away with no declaration in the function.