I'm trying to create lambda function with dynamic argument function in dictionary, then assign it into each Checkbutton command argument. When I tick every checkbutton, the entrie Entrys are still greyed out except the last Entry can enable and disable via checkbutton
First, I'm assign checkbox command argument with lambda function with dynamic argument from another ditcionary into function directly, Then trying to store as separate command dictionary first and assign with specific key from this dictionary into the command argument. The results are still the same
I'm try to debug by getting each variable from the dictionary, every Checkbutton are linked and work correctly, but I'm curious why command isn't working except the last Entry. I'm noted that everytime I enable and disable the whole Checkbutton. I assume that the command argument from every Checkbuttons are linked with the last variable from the dictionaries.
But when I print the argument in function qCheckboxAction(checkboxVar,entryObj,entryVar) then I found out that entire items of each groups is replaced by the argument of the last item from the groups.
How can I assign each of command Checkbuttons correctly to prevent from previous loop is changed by key from current loop.
PS. I have solved the problem because lambda must scope the enclosure.
My JSON Data CafeData.json
{
"Coffee":
{
"Lattae":30,
"Espresso":35,
"Ice Lattae":40,
"Vale Coffee":45,
"Cappuccino":35,
"African Coffee":45,
"American Coffee":45,
"Ice Cappuccino":40
},
"Cake":
{
"Coffee Cake":40,
"Red Velvet Cake":45,
"Black Forest Cake":50,
"Boston Cream Cake":50,
"Lagos Chocolate":45,
"Kilburn Chocolate Cake":50,
"Carton Hill Cake":40,
"Queen Park Cake":50
},
"Service Charge":
{
"Type":"Flat",
"Value":1.59
},
"Paid Tax":
{
"Type":"Percent",
"Value":0.15
}
}
My Python Code
from tkinter import *
import json
import random
import time
from tkinter import messagebox
cafeData = {}
try:
with open("CafeData.json", encoding="utf-8") as f:
cafeData = json.loads(f.read())
except FileNotFoundError:
print("File not found. Please check CafeData.json file is in the same directory.")
exit()
# String variable
appTitle = "Engineering Sriracha Coffee Shop"
# Font Profiles
labelFontProfile = ("TH Sarabun New", 60, "bold")
cafeInfoFontProfile = ("TH Sarabun New", 18, "bold")
normalReceiptFontProfile = ("TH Sarabun New",12,"bold")
smallReceiptFontProfile = ("TH Sarabun New",11,"bold")
# Coffee Variable
coffeePriceDict = cafeData["Coffee"]
coffeeRefEntryObject = {}
coffeeRefEntry = {}
coffeeRefCheckbox = {}
coffeeCheckboxCommand = {}
# Cake Variable
cakePriceDict = cafeData["Cake"]
cakeRefEntryObject = {}
cakeRefEntry = {}
cakeRefCheckbox = {}
cakeCheckboxCommand = {}
# Cost Item Information
itemInfoNameList = ["Cost of Drinks","Cost of Cakes","Service Charge"]
itemInfoEntryRef = {}
# Payment Information
paymentInfoName = ["Paid Tax","Sub Total", "Total"]
paymentInfoEntryRef = {}
# Initialize GUI
app = Tk()
app.geometry("1200x900 0 0")
app.title(appTitle)
app.configure(background="cyan")
# Frame Properties
topFrame = Frame(app, width=1350, height=100, bd=14, relief="raise")
topFrame.pack(side=TOP)
mainLeftFrame = Frame(app, width=900, height=650, bd=8, relief="raise")
mainLeftFrame.pack(side=LEFT)
mainRightFrame = Frame(app, width=440, height=650, bd=8, relief="raise")
mainRightFrame.pack(side=RIGHT)
mainLeftTopFrame = Frame(mainLeftFrame, width=900, height=320, bd=8, relief="raise")
mainLeftTopFrame.pack(side=TOP)
mainLeftBottomFrame = Frame(mainLeftFrame, width=900, height=320, bd=8, relief="raise")
mainLeftBottomFrame.pack(side=BOTTOM)
receiptFrame = Frame(mainRightFrame, width=440, height=450, bd=12, relief="raise")
receiptFrame.pack(side=TOP)
buttonFrame = Frame(mainRightFrame, width=440, height=240, bd=12, relief="raise")
buttonFrame.pack(side=BOTTOM)
coffeeFrame = Frame(mainLeftTopFrame, width=400, height=330, bd=16, relief="raise")
coffeeFrame.pack(side=LEFT)
cakeFrame = Frame(mainLeftTopFrame, width=400, height=320, bd=16, relief="raise")
cakeFrame.pack(side=RIGHT)
cost1Frame = Frame(mainLeftBottomFrame, width=440, height=320, bd=14, relief="raise")
cost1Frame.pack(side=LEFT)
cost2Frame = Frame(mainLeftBottomFrame, width=440, height=320, bd=14, relief="raise")
cost2Frame.pack(side=RIGHT)
topFrame.configure(background="pink")
mainLeftFrame.configure(background="blue")
mainRightFrame.configure(background="blue")
# Function
def qCheckboxAction(checkboxVar,entryObj,entryVar):
if checkboxVar.get() == 1:
entryObj.configure(state=NORMAL)
elif checkboxVar.get() == 0:
entryObj.configure(state=DISABLED)
entryVar.set(0)
def qExit():
confirmed = messagebox.askyesno("Quit System!!!", "Do you want to quit?")
if confirmed > 0:
app.destroy()
return
def qReset():
qCoffeeReset()
qCakeReset()
qCostReset()
textReceipt.delete("1.0",END)
def qCoffeeReset():
for key in cakePriceDict.keys():
cakeRefCheckbox[key].set(0)
cakeRefEntry[key].set(0)
cakeRefEntryObject[key].configure(state=DISABLED)
def qCakeReset():
for key in coffeePriceDict.keys():
coffeeRefCheckbox[key].set(0)
coffeeRefEntry[key].set(0)
coffeeRefEntryObject[key].configure(state=DISABLED)
def qCostReset():
for item in itemInfoNameList:
itemInfoEntryRef[item].set("฿ 0.00")
for payment in paymentInfoName:
paymentInfoEntryRef[payment].set("฿ 0.00")
def qCostOfItem():
try:
totalCoffeePrice = 0
for name in coffeeRefEntry.keys():
totalCoffeePrice = coffeePriceDict[name] * float(coffeeRefEntry[name].get())
itemInfoEntryRef["Cost of Drinks"].set("฿ {:.2f}".format(totalCoffeePrice))
totalCakePrice = 0
for name in cakeRefEntry.keys():
totalCakePrice = cakePriceDict[name] * float(cakeRefEntry[name].get())
itemInfoEntryRef["Cost of Cakes"].set("฿ {:.2f}".format(totalCakePrice))
itemInfoEntryRef["Service Charge"].set("฿ {:.2f}".format(cafeData["Service Charge"]["Value"]))
paymentInfoEntryRef["Paid Tax"].set("฿ {:.2f}".format((totalCoffeePrice totalCakePrice cafeData["Service Charge"]["Value"]) * cafeData["Paid Tax"]["Value"]))
paymentInfoEntryRef["Sub Total"].set("฿ {:.2f}".format(totalCoffeePrice totalCakePrice cafeData["Service Charge"]["Value"]))
paymentInfoEntryRef["Total"].set("฿ {:.2f}".format((totalCoffeePrice totalCakePrice cafeData["Service Charge"]["Value"]) * (1 cafeData["Paid Tax"]["Value"])))
except ValueError:
qReset()
def qReceipt():
qCostOfItem()
textReceipt.delete("1.0",END)
billNumber = random.randint(10908,500876)
dateOfOrder = time.strftime("%d/%m/%Y")
textReceipt.insert(END,"Receipt Ref:\t\t\tBILL" str(billNumber) "\t\t" dateOfOrder "\n")
textReceipt.insert(END,"Item\t\t\t\t\t" "Cost of Items\n\n")
for coffee in coffeePriceDict.keys():
if float(coffeeRefEntry[coffee].get()) > 0:
textReceipt.insert(END,coffee "\t\t\t\t\t" coffeeRefEntry[coffee].get() "\n")
for cake in cakePriceDict.keys():
if float(cakeRefEntry[cake].get()) > 0:
textReceipt.insert(END,cake "\t\t\t\t\t" cakeRefEntry[cake].get() "\n")
textReceipt.insert(END,"\nCost of Drinks : \t\t" itemInfoEntryRef["Cost of Drinks"].get() "\n")
textReceipt.insert(END,"Cost of Cakes : \t\t" itemInfoEntryRef["Cost of Cakes"].get() "\n")
textReceipt.insert(END,"Service Charge : \t\t" itemInfoEntryRef["Service Charge"].get() "\n")
def costModifier(value,property):
if property["Type"] == "Flat":
return value property["Value"]
elif property["Type"] == "Percent":
return value * (1 property["Value"])
return value
# Label Properties
labelInfo = Label(topFrame, font=labelFontProfile, text=appTitle, bg="yellow", bd=18)
labelInfo.grid(row=0, column=0)
# Create GUI Coffee Quantity Fill
index=0
for coffee in coffeePriceDict.keys():
coffeeRefCheckbox[coffee] = IntVar()
coffeeRefCheckbox[coffee].set(0)
coffeeRefEntry[coffee] = StringVar()
coffeeRefEntry[coffee].set(0)
coffeeRefEntryObject[coffee] = Entry(coffeeFrame,font=cafeInfoFontProfile,bd=8,width=6,justify="left",textvariable=coffeeRefEntry[coffee],state=DISABLED)
coffeeRefEntryObject[coffee].grid(row=index,column=1)
coffeeCheckboxCommand[coffee] = lambda c=coffee:qCheckboxAction(coffeeRefCheckbox[c],coffeeRefEntryObject[c],coffeeRefEntry[c])
coffeeCheckbox = Checkbutton(coffeeFrame,text=coffee "\t",variable=coffeeRefCheckbox[coffee],onvalue=1,offvalue=0,font=cafeInfoFontProfile,command=coffeeCheckboxCommand[coffee])
coffeeCheckbox.grid(row=index, sticky=W)
index =1
# Create GUI Cake Quantity Fill
index=0
for cake in cakePriceDict.keys():
cakeRefCheckbox[cake] = IntVar()
cakeRefCheckbox[cake].set(0)
cakeRefEntry[cake] = StringVar()
cakeRefEntry[cake].set(0)
cakeRefEntryObject[cake] = Entry(cakeFrame,font=cafeInfoFontProfile,bd=8,width=6,justify="left",textvariable=cakeRefEntry[cake],state=DISABLED)
cakeRefEntryObject[cake].grid(row=index,column=1)
cakeCheckboxCommand[cake] = lambda c=cake:qCheckboxAction(cakeRefCheckbox[c],cakeRefEntryObject[c],cakeRefEntry[c])
cakeCheckbox = Checkbutton(cakeFrame,text=cake "\t",variable=cakeRefCheckbox[cake],onvalue=1,offvalue=0,font=cafeInfoFontProfile,command=cakeCheckboxCommand[cake])
cakeCheckbox.grid(row=index, sticky=W)
index =1
# Receipt Information
labelReceipt = Label(receiptFrame, font=normalReceiptFontProfile, text="Receipt",bd=2,anchor="w")
labelReceipt.grid(row=0,column=0,sticky=W)
textReceipt = Text(receiptFrame,font=smallReceiptFontProfile,bd=8,width=59,height=22,bg="white")
textReceipt.grid(row=1,column=0)
index=0
for name in itemInfoNameList:
itemInfoEntryRef[name] = StringVar()
itemInfoEntryRef[name].set("฿ 0.00")
labelRef = Label(cost1Frame,font=cafeInfoFontProfile,text=name,bd=8)
labelRef.grid(row=index,column=0,sticky=W)
entryRef = Entry(cost1Frame,font=cafeInfoFontProfile,bd=8,justify="left",textvariable=itemInfoEntryRef[name])
entryRef.grid(row=index,column=1,sticky=W)
index =1
index=0
for name in paymentInfoName:
paymentInfoEntryRef[name] = StringVar()
paymentInfoEntryRef[name].set("฿ 0.00")
labelRef = Label(cost2Frame,font=cafeInfoFontProfile,text=name,bd=8)
labelRef.grid(row=index,column=0,sticky=W)
entryRef = Entry(cost2Frame,font=cafeInfoFontProfile,bd=8,justify="left",textvariable=paymentInfoEntryRef[name])
entryRef.grid(row=index,column=1,sticky=W)
index =1
# Button Control Properties
btnCommandDict = {"Total":lambda:qCostOfItem(),"Receipt":lambda:qReceipt(),"Reset":lambda:qReset(),"Exit":lambda:qExit()}
index = 0
for btnName in btnCommandDict.keys():
btnRef = Button(buttonFrame,padx=16,fg="black",font=cafeInfoFontProfile,width=5,text=btnName,command=btnCommandDict[btnName]).grid(row=0,column=index)
index =1
app.mainloop()
CodePudding user response:
Change:
lambda:qCheckboxAction(coffeeRefCheckbox[coffee],coffeeRefEntryObject[coffee],coffeeRefEntry[coffee])
to:
lambda c=coffee:qCheckboxAction(coffeeRefCheckbox[c],coffeeRefEntryObject[c],coffeeRefEntry[c])
Also, change:
lambda:qCheckboxAction(cakeRefCheckbox[cake],cakeRefEntryObject[cake],cakeRefEntry[cake])
to:
lambda c=cake:qCheckboxAction(cakeRefCheckbox[c],cakeRefEntryObject[c],cakeRefEntry[c])
Look into Python closures, and how they relate to lambdas.
