pythontkinterspinbox

Python Tkinter Spinboxes return value using for loops


I'm trying to use X many spin boxes as there are items in a list - so have put them on screen using a for loop.

However, I don't seem able to get them to return their value when I click a button.

aStarter = ["Starter 1", "Starter 2", "Starter 3"]

def getOrders():
            aStarterOrder = []
            aMainOrder = []
            aDessertOrder = []
            for i in aStarterQuantity:
                aStarterOrder.append(StarterQuantity.get())

            print(aStarterOrder)

aStarterQuantity = []
        StarterQuantity = IntVar()
        for i in range(len(aStarter)):
            lbl = Label(self, text = aStarter[i]).grid(column = 0, row = 2 + i)
            StarterQuantity = Spinbox(self, from_=0, to=20, width=5, command=callback, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 1, row = 2 + i)
            print(StarterQuantity.get())

I have tried appending the StarterQuantity to an array inside of the for loop:

aStarterQuantity = []
        StarterQuantity = IntVar()
        for i in range(len(aStarter)):
            lbl = Label(self, text = aStarter[i]).grid(column = 0, row = 2 + i)
            StarterQuantity = Spinbox(self, from_=0, to=20, width=5, command=callback, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 1, row = 2 + i)
            aStarterQuantity.append(StarterQuantity.get())

This returns a None Type error - I can't seem to get the value out of the spin box.

Any ideas?

Here is the full code (it's very much an early WIP!)

from tkinter import *
from tkinter.font import Font
import sqlite3
DatabaseFile = 'RMS.db'
connDatabase = sqlite3.connect(DatabaseFile)

#Creating a cursor to run SQL Commands
DatabaseSelect = connDatabase.cursor()

#Selecting all Menu Data
DatabaseSelect.execute("SELECT * FROM MENU")
MenuData = DatabaseSelect.fetchall()


class Main(Tk): #This sets up the initial Frame in a Window called Main
    def __init__(self): #This creates a controller to use the Frames
        Tk.__init__(self) #This creates a Window within the controller
        self._frame = None #This sets the frame to have a value of None - this makes sure the main Window doesn't get destroyed by the switch frame function
        self.switch_frame(Home)

    def switch_frame(self, frame_class): #This function is used to switch frames. Self is the master frame, frame_class is the name of the Class (page) being passed int
        new_frame = frame_class(self)
        if self._frame is not None:
            self._frame.destroy() #Destroy any frames except the master
        self._frame = new_frame #Make a new frame
        self._frame.grid(column=0, row=0) #Put the frame in the top left

class Home(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        Label(self, text="The White Horse").grid(column = 0, row = 0)
        btnPage1 = Button(self, text="Orders", command=lambda: master.switch_frame(Orders)).grid(column = 1, row = 0)


#Making a blank page with a return home button
class Orders(Frame): #This sets up a class called Page1 - Each new page will need a new class.
    def __init__(self, master): #This sets up a controller to put things into the Frame
        Frame.__init__(self, master) #Make the frame using the controller

        #Get list of Starters from Menu Data
        aStarter = ["Starter 1", "Starter 2", "Starter 3"]
        aMain = []
        aDessert = []
        for i in MenuData:
            if i[3] == "Starters":
                aStarter.append(i[1])
            if i[3] == "Main":
                aMain.append(i[1])
            if i[3] == "Dessert":
                aDessert.append(i[1])
        def getOrders():
            aStarterOrder = []
            aMainOrder = []
            aDessertOrder = []
            for i in aStarterQuantity:
                aStarterOrder.append(StarterQuantity.get())

            print(aStarterOrder)

        def callback():
            print(StarterQuantity.get())
            
                
        #Starter Section
        boxes = []
        aStarterQuantity = []
        StarterQuantity = IntVar()
        for i in range(len(aStarter)):
            lbl = Label(self, text = aStarter[i]).grid(column = 0, row = 2 + i)
            StarterQuantity = Spinbox(self, from_=0, to=20, width=5, command=callback, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 1, row = 2 + i)
            aStarterQuantity.append(StarterQuantity.get())
            
      
        #Mains Sections
        for i in range(len(aMain)):
            lbl = Label(self, text = aMain[i]).grid(column = 6, row = 2 + i)
            MainQuantity = Spinbox(self, from_=0, to=20, width=5, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 7, row = 2 + i)

        #Dessert Section
        for i in range(len(aDessert)):
            lbl = Label(self, text = aDessert[i]).grid(column = 12, row = 2 + i)
            DessertQuantity = Spinbox(self, from_=0, to=20, width=5, font=Font(family='Helvetica', size=20, weight='bold')).grid(column = 13, row = 2 + i)
        
        btnHome = Button(self, text="Home", command=lambda: master.switch_frame(Home)).grid(column = 0, row = 1) #Add a Button
        btnSubmitEntries = Button(self, text = "Submit", command = getOrders).grid(column = 0, row= 20)

  
    

Main = Main()
Main.geometry("1200x800")
Main.mainloop()

Solution

  • The problem seems to be that the grid method doesn't return anything, so StarterQuantity gets assigned None i.e. the default return value for a function.

    for i in range(len(aStarter)):
    
        lbl = Label(self, text=aStarter[i])
        lbl.grid(column=0, row=2 + i)
    
        sq_font = Font(family='Helvetica', 
                       size=20,
                       weight='bold')
          
        StarterQuantity = Spinbox(self,
                                  from_=0,
                                  to=20,
                                  width=5,                              command=callback, 
                                  font=sq_font)
        StarterQuantity.grid(column=1, row=2 + i)
    
        aStarterQuantity.append(StarterQuantity.get())
    

    This works in my case. (Pulled out the Font argument for Spinbox to increase readability on mobile).

    Unrelated note; I don't know what code style you prefer/have to maintain and it is up to you of course, but I thought it might be helpful to mention that the naming style convention in Python (PEP8) is to use CamelCase for class names, lower_case_w_underscores for variable names and functions/methods, and SCREAMING_SNAKE for constants.