Search code examples
pythontkintershopping-cartnameerrorframes

How do you use .get() with a multiframe shopping cart in python tkinter?


In this code. I want display the information from the frame I hit the Buy button on. I'm sure it is a simple solution but it has eluded me. I've been going through google and stack overflow with no solution yet.

I tried making the NewFrame global but that just does the last one made.

from tkinter import *

root = Tk()


FrameNum = 1
ItemCount = 1

def combine_funcs(*funcs):

    def combined_func(*args, **kwargs):
        for f in funcs:
            f(*args, **kwargs)

    return combined_func


def place_order():

    global ItemCount

    Product = Product_Box.get()

    Label(root, text="Your Purchase of " + str(Product) + " Placed").grid(row=1, column=3, stick="w")
    ItemCount += 1


def create_frame():

    global FrameNum

    NewFrame = LabelFrame(root,name=("newframe"+str(FrameNum)), text=("Item "+str(FrameNum)),width=100, height=100)
    NewFrame.grid(row=FrameNum, column=0)

    Product_Lab = Label(NewFrame, text="Product").grid(row=0, column=0)

    Qty_Lab = Label(NewFrame, text="Qty").grid(row=0, column=1)

    Price_Lab = Label(NewFrame, text="Price").grid(row=0, column=2)


    # Entry Boxes

    Product_Box = Entry(NewFrame, width=10).grid(row=1, column=0)

    Qty_Box = Entry(NewFrame, width=12).grid(row=1, column=1)

    Price_box = Entry(NewFrame, width=6).grid(row=1, column=2)



    Remove_Bet_Button = Button(NewFrame, text="Del", command=NewFrame.destroy).grid(row=3, column=2)

    Buy_Button = Button(NewFrame, text="Buy", command=combine_funcs(place_order, NewFrame.destroy)).grid(row=3, column=0)

    FrameNum += 1


framebutton = Button(root, text="Frame", command=create_frame).grid(row=0, column = 3, stick ="n")


root.mainloop()

I get this error when hitting the Buy button. (with or without information entered into the boxes from that frame)

NameError: name 'Product_Box' is not defined


Solution

  • Product_Box is local variable created in create_frame() - to access it in other functions you have to assign to global variable - so you need add global Product_Box in create_frame()

    But there is other problem:

    variable = tk.Widget().grid() assigns None to variable because grid()/pack()/place() returns None. You have to do it in two steps variable = tk.Widget() and varaible.grid().

    Product_Lab = Label(NewFrame, text="Product")
    Product_Lab.grid(row=0, column=0)
    

    And now code works but later you will have the same problem with other variables.


    Full working code with other changes

    PEP 8 -- Style Guide for Python Code

    import tkinter as tk  # PEP8: `import *` is not preferred
    
    # --- functions ---
    
    def place_order(product_box, qty_box, price_box, new_frame):
        global item_count
        global label
        
        product = product_box.get()
        qty = int(qty_box.get())
        price = int(price_box.get())
        
        total = qty * price
    
        if not label:    
            label = tk.Label(root)
            label.grid(row=1, column=3, stick="w")
            
        # replace text
        #label['text'] = f"Your Purchase of {product} Placed. (Qty: {qty}, Price: {price}, Total: {total})"
        
        # or append in new line 
        if len(label['text']) > 0:
            label['text'] += "\n"
        label['text'] += f"Your Purchase of {product} Placed. (Qty: {qty}, Price: {price}, Total: {total})"
        
        item_count += 1
    
        new_frame.destroy()
    
    def create_frame():
        global frame_number
        
        new_frame = tk.LabelFrame(root, name=f"newframe {frame_number}", text=f"Item {frame_number}", width=100, height=100)
        new_frame.grid(row=frame_number, column=0)
    
        tk.Label(new_frame, text="Product").grid(row=0, column=0)
        tk.Label(new_frame, text="Qty").grid(row=0, column=1)
        tk.Label(new_frame, text="Price").grid(row=0, column=2)
    
        # Entry Boxes
    
        product_box = tk.Entry(new_frame, width=10)
        product_box.grid(row=1, column=0)
    
        qty_box = tk.Entry(new_frame, width=12)
        qty_box.grid(row=1, column=1)
    
        price_box = tk.Entry(new_frame, width=6)
        price_box.grid(row=1, column=2)
    
        tk.Button(new_frame, text="Del", command=new_frame.destroy).grid(row=3, column=2)
        tk.Button(new_frame, text="Buy", command=lambda:place_order(product_box, qty_box, price_box, new_frame)).grid(row=3, column=0)
    
        frame_number += 1
    
    # --- main ---
    
    frame_number = 1
    item_count = 1
    
    root = tk.Tk()
    
    tk.Button(root, text="Frame", command=create_frame).grid(row=0, column=3, stick="n")
    label = None  # it will add later but only once
    
    root.mainloop()