我如何(有效地)更新程序生成的 tkinter 标签的文本?
How do I (efficiently) update the text of a tkinter label that was procedurally generated?
我正在制作一个餐厅菜单程序,它从 .json 文件中提取项目及其属性,然后允许用户将它们添加到购物车并导出订单(不用于实际使用,只是一个项目)
我为这个项目设定的目标是通过简单地更改 .json 文件中的项目来自定义菜单,然后程序自动显示它们(我希望它尽可能模块化)
我已经解决了按钮事件和创建按钮等问题,但购物车按钮有问题。每个项目旁边都有“+”和“-”按钮,标签显示所选数量。 Here is an image
我需要在按下 +/- 按钮时更改中间的标签,但由于它是使用 for 循环创建的,所以我不知道该怎么做。我的一个想法是每次按下按钮时重新打印整个屏幕,这对于这么简单的事情应该可以正常工作,但我认为这将是一个坏主意,因为对于更大的事情来说这不是一个可接受的解决方案。我想知道是否有更好的方法,以及如何实现它。
下面是我的一段代码(删除了所有花哨的颜色和图像),它看起来不太好,但它可以很好地重现问题。我还将 menu.json 中的几行复制到代码中,以便它可以 运行 自己。
import tkinter as tk
menu={ # this is a sample of some items from the .json with the full menu
"Entrees":{
"Huitres":{
"price":24,
"calories":350
},
"Poireaux vinaigrette":{
"price":18,
"calories":430
}
},
"Appetizers":{
"Croissant Basket":{
"price":4,
"calories":600,
"size":"3 Servings (150g)",
"description":"3 fresh croissants to share"
},
"Toasted Baguette":{
"price":4,
"calories":680,
"size":"Several Servings (250g)",
"description":"a warm parisian baguette for the table"
}
}
}
#----------#
window=tk.Tk()
window.geometry("600x720+450+50")
window.minsize(width=600, height=720)
window.maxsize(width=600, height=720)
window.columnconfigure(0, minsize=600)
#----------#
color0="#fff0d4"
color1="#ffe6c4"
color2="#ffb65c"
color3="#ffce8f"
font0="Brush Script MT",
font1="Freestyle Script"
#----------#
class Cart:
def __init__(self):
self.items={} # create empty dict
for catName in menu:
self.items[catName]=menu[catName].copy() # copy category names to new dict
for itemName in menu[catName]:
self.items[catName][itemName]=0 # copy items from categories, set quantities to 0
cart=Cart()
#----------#
def clear(): # destroys all widgets on the screen
for widget in window.winfo_children():
widget.destroy()
def draw_menu_small(category): # this is usually called from a main menu, but this code has been trimmed down
count=0
for itemName in menu[category]:
def changeItemQuantity(category,item,amount):
if amount==1:
cart.items[category][item]+=1
elif amount==-1:
if cart.items[category][item]>0:
cart.items[category][item]-=1
# updateLabel() << This is where I need the Label to be updated
frm_item=tk.Frame( # the frame that holds all of the widgets associated with a given item
window,
relief=tk.FLAT,
width=600,
bg=color0,
)
frm_item.grid(
row=count,
column=0,
sticky="ew",
pady=8
)
frm_item.columnconfigure(0,minsize=450)
frm_item.columnconfigure(1,minsize=150)
tk.Label(
frm_item,
text=itemName,
font=(font1, 26),
bg=color0,
).grid(
row=0,
column=0,
sticky="w",
padx=30,
)
frm_buttons=tk.Frame(
frm_item,
relief=tk.FLAT,
bg=color0,
)
frm_buttons.grid(
row=0,
column=1,
sticky="e",
padx=12
)
tk.Button(
frm_buttons,
text="-",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName:changeItemQuantity(category,itemName,-1),
).grid(
row=0,
column=0,
)
tk.Label(
frm_buttons,
text=cart.items[category][itemName],
font=("Arial",16),
bg=color0,
width=2,
).grid(
row=0,
column=1,
)
tk.Button(
frm_buttons,
text="+",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName:changeItemQuantity(category,itemName,1),
).grid(
row=0,
column=2,
)
price=menu[category][itemName]["price"]
tk.Label(
frm_item,
text=f"${price}",
font=(font1, 16),
bg=color0,
).grid(
row=1,
column=0,
sticky="w",
padx=30,
)
count+=1
#----------#
draw_menu_small("Entrees") # the final product would have a draw_homescreen() here, but the menu_small is the one I have a problem with
window.mainloop()
print(cart.items) # this is for debugging, shows what the buttons are doing
我建议您不要创建新标签来在单击 + 或 - 时更改值,而是更新同一标签所持有的值。
示例代码:
import tkinter as tk
root = tk.Tk()
root.geometry('300x300')
items=['Food a','Food b','food c','food d','food e','food f'] #food items
#a tkinter label object shouldn't be stored as a string, as it loses it's value
#if you print tkinter label object, it looks like this: .!label1 or .!label2 same for button objects: .!button1 then .!button2 etc
#stores the food name as key and tkinter label object(here the label shows the food name) as value like {'food a':<tkinter label object>,'food b':<tkinter label object>}
itemslbl_dict={}
itemlblnum=0
#stores the quantity of each food that the user has added to cart
quantityval_dict={}
for i in items:
quantityval_dict[i]=0 #initially it is 0
#stores the food name as key and tkinter label object as value. Here the label shows the quantity of each food and is placed between the + and - buttons
quantitylbl_dict={}
quantlblnum=0
#stores food name as key and tkinter button object associated with the food name as value.
#i.e. one plus button is associated with increasing the quantity of one food item and not any other food item
plusbtn_dict={}
plusbtnnum=0
#same with the minus button dict
minusbtn_dict={}
minusbtnnum=0
#loop to generate the labels for the items/food names
for i in items:
lbl=tk.Label(root, text=i)
lbl.grid(row=itemlblnum,column=0)
itemlblnum+=1
itemslbl_dict[i] = [lbl]
#this function increase the label showing qauntity by 1 & update the dictionary containing the quantity of each food
#executes whenever a plus button is clicked
def plusbtnclick(item_name):
global quantitylbl_dict, quantityval_dict
lbl = quantitylbl_dict[item_name]
val = int(lbl[0].cget("text"))
lbl[0].config(text=str(val+1)) # increases the value of the variable by 1
quantityval_dict[item_name]=val+1 #updating the dictionary so that it stores the right quantity of each food item
#same fucntion as the above one except that it decreses quantity by 1 and
#executes whenever a minus button is clicked
def minusbtnclick(item_name):
global quantitylbl_dict, quantityval_dict
lbl=quantitylbl_dict[item_name]
val = int(lbl[0].cget("text"))
lbl[0].config(text=str(val-1)) # decreaseses the value of the variable by 1
quantityval_dict[item_name]=val-1
for i in items:
#creating the quantity label, placing it between the + and - buttons and adding the label's object id to the lbldictionary
lbl=tk.Label(root,text=0)
lbl.grid(row=quantlblnum,column=2)
quantitylbl_dict[i]=[lbl]
#creating the + button , placing it before the quantity label and adding the button's object id to the plusbtndictionary
plusbtn = tk.Button(root, text="+")
plusbtn.grid(row=plusbtnnum, column=1)
plusbtn_dict[i]=[plusbtn]
#creating the - button , placing it after the quantity label and adding the button's object id to the minusbtndictionary
minusbtn = tk.Button(root, text="-")
minusbtn.grid(row=minusbtnnum, column=3)
minusbtn_dict[i]=[minusbtn]
#updating the value by one so that the buttons and labels can be placed at the next row
quantlblnum+=1
plusbtnnum+=1
minusbtnnum+=1
#assigning the plusbtnclick fucntion to each of the + buttons that we created
for plusbtnobj in plusbtn_dict:
plusbtn_dict[plusbtnobj][0].configure(command= lambda x=plusbtnobj: plusbtnclick(x))
#assigning the minusbtnclick fucntion to each of the - buttons that we created
for minusbtnobj in minusbtn_dict:
minusbtn_dict[minusbtnobj][0].configure(command=lambda x=minusbtnobj: minusbtnclick(x))
tk.mainloop()
#this loop shows the final quantities of the food that was oredered once the tkinter window is closed
for i in quantityval_dict:
print(i,' : ',quantityval_dict[i])
您可以在新文件中尝试这段代码,检查这是否是您想要的,并从这段代码中提取您需要的部分。这是一个很长的代码,如果您想详细说明任何事情,请将我添加到聊天室,因为使用评论部分可能会很麻烦。
如果你想更新标签,你需要使用变量存储它并传递给changeItemQuantity()
。您还需要在两个按钮(-
和 +
)之前创建标签,以便将其传递给函数:
# added lbl argument
def changeItemQuantity(category,item,amount,lbl):
if amount==1:
cart.items[category][item]+=1
elif amount==-1:
if cart.items[category][item]>0:
cart.items[category][item]-=1
# update label
lbl.config(text=cart.items[category][item])
...
# create the label and store the instance to a variable
lbl = tk.Label(
frm_buttons,
text=cart.items[category][itemName],
font=("Arial",16),
bg=color0,
width=2,
)
lbl.grid(
row=0,
column=1,
)
# pass "lbl" to changeItemQuantity()
tk.Button(
frm_buttons,
text="-",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName,lbl=lbl:changeItemQuantity(category,itemName,-1,lbl),
).grid(
row=0,
column=0,
)
# pass "lbl" to changeItemQuantity()
tk.Button(
frm_buttons,
text="+",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName,lbl=lbl:changeItemQuantity(category,itemName,1,lbl),
).grid(
row=0,
column=2,
)
...
我正在制作一个餐厅菜单程序,它从 .json 文件中提取项目及其属性,然后允许用户将它们添加到购物车并导出订单(不用于实际使用,只是一个项目)
我为这个项目设定的目标是通过简单地更改 .json 文件中的项目来自定义菜单,然后程序自动显示它们(我希望它尽可能模块化)
我已经解决了按钮事件和创建按钮等问题,但购物车按钮有问题。每个项目旁边都有“+”和“-”按钮,标签显示所选数量。 Here is an image
我需要在按下 +/- 按钮时更改中间的标签,但由于它是使用 for 循环创建的,所以我不知道该怎么做。我的一个想法是每次按下按钮时重新打印整个屏幕,这对于这么简单的事情应该可以正常工作,但我认为这将是一个坏主意,因为对于更大的事情来说这不是一个可接受的解决方案。我想知道是否有更好的方法,以及如何实现它。
下面是我的一段代码(删除了所有花哨的颜色和图像),它看起来不太好,但它可以很好地重现问题。我还将 menu.json 中的几行复制到代码中,以便它可以 运行 自己。
import tkinter as tk
menu={ # this is a sample of some items from the .json with the full menu
"Entrees":{
"Huitres":{
"price":24,
"calories":350
},
"Poireaux vinaigrette":{
"price":18,
"calories":430
}
},
"Appetizers":{
"Croissant Basket":{
"price":4,
"calories":600,
"size":"3 Servings (150g)",
"description":"3 fresh croissants to share"
},
"Toasted Baguette":{
"price":4,
"calories":680,
"size":"Several Servings (250g)",
"description":"a warm parisian baguette for the table"
}
}
}
#----------#
window=tk.Tk()
window.geometry("600x720+450+50")
window.minsize(width=600, height=720)
window.maxsize(width=600, height=720)
window.columnconfigure(0, minsize=600)
#----------#
color0="#fff0d4"
color1="#ffe6c4"
color2="#ffb65c"
color3="#ffce8f"
font0="Brush Script MT",
font1="Freestyle Script"
#----------#
class Cart:
def __init__(self):
self.items={} # create empty dict
for catName in menu:
self.items[catName]=menu[catName].copy() # copy category names to new dict
for itemName in menu[catName]:
self.items[catName][itemName]=0 # copy items from categories, set quantities to 0
cart=Cart()
#----------#
def clear(): # destroys all widgets on the screen
for widget in window.winfo_children():
widget.destroy()
def draw_menu_small(category): # this is usually called from a main menu, but this code has been trimmed down
count=0
for itemName in menu[category]:
def changeItemQuantity(category,item,amount):
if amount==1:
cart.items[category][item]+=1
elif amount==-1:
if cart.items[category][item]>0:
cart.items[category][item]-=1
# updateLabel() << This is where I need the Label to be updated
frm_item=tk.Frame( # the frame that holds all of the widgets associated with a given item
window,
relief=tk.FLAT,
width=600,
bg=color0,
)
frm_item.grid(
row=count,
column=0,
sticky="ew",
pady=8
)
frm_item.columnconfigure(0,minsize=450)
frm_item.columnconfigure(1,minsize=150)
tk.Label(
frm_item,
text=itemName,
font=(font1, 26),
bg=color0,
).grid(
row=0,
column=0,
sticky="w",
padx=30,
)
frm_buttons=tk.Frame(
frm_item,
relief=tk.FLAT,
bg=color0,
)
frm_buttons.grid(
row=0,
column=1,
sticky="e",
padx=12
)
tk.Button(
frm_buttons,
text="-",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName:changeItemQuantity(category,itemName,-1),
).grid(
row=0,
column=0,
)
tk.Label(
frm_buttons,
text=cart.items[category][itemName],
font=("Arial",16),
bg=color0,
width=2,
).grid(
row=0,
column=1,
)
tk.Button(
frm_buttons,
text="+",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName:changeItemQuantity(category,itemName,1),
).grid(
row=0,
column=2,
)
price=menu[category][itemName]["price"]
tk.Label(
frm_item,
text=f"${price}",
font=(font1, 16),
bg=color0,
).grid(
row=1,
column=0,
sticky="w",
padx=30,
)
count+=1
#----------#
draw_menu_small("Entrees") # the final product would have a draw_homescreen() here, but the menu_small is the one I have a problem with
window.mainloop()
print(cart.items) # this is for debugging, shows what the buttons are doing
我建议您不要创建新标签来在单击 + 或 - 时更改值,而是更新同一标签所持有的值。
示例代码:
import tkinter as tk
root = tk.Tk()
root.geometry('300x300')
items=['Food a','Food b','food c','food d','food e','food f'] #food items
#a tkinter label object shouldn't be stored as a string, as it loses it's value
#if you print tkinter label object, it looks like this: .!label1 or .!label2 same for button objects: .!button1 then .!button2 etc
#stores the food name as key and tkinter label object(here the label shows the food name) as value like {'food a':<tkinter label object>,'food b':<tkinter label object>}
itemslbl_dict={}
itemlblnum=0
#stores the quantity of each food that the user has added to cart
quantityval_dict={}
for i in items:
quantityval_dict[i]=0 #initially it is 0
#stores the food name as key and tkinter label object as value. Here the label shows the quantity of each food and is placed between the + and - buttons
quantitylbl_dict={}
quantlblnum=0
#stores food name as key and tkinter button object associated with the food name as value.
#i.e. one plus button is associated with increasing the quantity of one food item and not any other food item
plusbtn_dict={}
plusbtnnum=0
#same with the minus button dict
minusbtn_dict={}
minusbtnnum=0
#loop to generate the labels for the items/food names
for i in items:
lbl=tk.Label(root, text=i)
lbl.grid(row=itemlblnum,column=0)
itemlblnum+=1
itemslbl_dict[i] = [lbl]
#this function increase the label showing qauntity by 1 & update the dictionary containing the quantity of each food
#executes whenever a plus button is clicked
def plusbtnclick(item_name):
global quantitylbl_dict, quantityval_dict
lbl = quantitylbl_dict[item_name]
val = int(lbl[0].cget("text"))
lbl[0].config(text=str(val+1)) # increases the value of the variable by 1
quantityval_dict[item_name]=val+1 #updating the dictionary so that it stores the right quantity of each food item
#same fucntion as the above one except that it decreses quantity by 1 and
#executes whenever a minus button is clicked
def minusbtnclick(item_name):
global quantitylbl_dict, quantityval_dict
lbl=quantitylbl_dict[item_name]
val = int(lbl[0].cget("text"))
lbl[0].config(text=str(val-1)) # decreaseses the value of the variable by 1
quantityval_dict[item_name]=val-1
for i in items:
#creating the quantity label, placing it between the + and - buttons and adding the label's object id to the lbldictionary
lbl=tk.Label(root,text=0)
lbl.grid(row=quantlblnum,column=2)
quantitylbl_dict[i]=[lbl]
#creating the + button , placing it before the quantity label and adding the button's object id to the plusbtndictionary
plusbtn = tk.Button(root, text="+")
plusbtn.grid(row=plusbtnnum, column=1)
plusbtn_dict[i]=[plusbtn]
#creating the - button , placing it after the quantity label and adding the button's object id to the minusbtndictionary
minusbtn = tk.Button(root, text="-")
minusbtn.grid(row=minusbtnnum, column=3)
minusbtn_dict[i]=[minusbtn]
#updating the value by one so that the buttons and labels can be placed at the next row
quantlblnum+=1
plusbtnnum+=1
minusbtnnum+=1
#assigning the plusbtnclick fucntion to each of the + buttons that we created
for plusbtnobj in plusbtn_dict:
plusbtn_dict[plusbtnobj][0].configure(command= lambda x=plusbtnobj: plusbtnclick(x))
#assigning the minusbtnclick fucntion to each of the - buttons that we created
for minusbtnobj in minusbtn_dict:
minusbtn_dict[minusbtnobj][0].configure(command=lambda x=minusbtnobj: minusbtnclick(x))
tk.mainloop()
#this loop shows the final quantities of the food that was oredered once the tkinter window is closed
for i in quantityval_dict:
print(i,' : ',quantityval_dict[i])
您可以在新文件中尝试这段代码,检查这是否是您想要的,并从这段代码中提取您需要的部分。这是一个很长的代码,如果您想详细说明任何事情,请将我添加到聊天室,因为使用评论部分可能会很麻烦。
如果你想更新标签,你需要使用变量存储它并传递给changeItemQuantity()
。您还需要在两个按钮(-
和 +
)之前创建标签,以便将其传递给函数:
# added lbl argument
def changeItemQuantity(category,item,amount,lbl):
if amount==1:
cart.items[category][item]+=1
elif amount==-1:
if cart.items[category][item]>0:
cart.items[category][item]-=1
# update label
lbl.config(text=cart.items[category][item])
...
# create the label and store the instance to a variable
lbl = tk.Label(
frm_buttons,
text=cart.items[category][itemName],
font=("Arial",16),
bg=color0,
width=2,
)
lbl.grid(
row=0,
column=1,
)
# pass "lbl" to changeItemQuantity()
tk.Button(
frm_buttons,
text="-",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName,lbl=lbl:changeItemQuantity(category,itemName,-1,lbl),
).grid(
row=0,
column=0,
)
# pass "lbl" to changeItemQuantity()
tk.Button(
frm_buttons,
text="+",
font=("Arial",16),
bg=color0,
activebackground=color0,
relief=tk.FLAT,
width=2,
command=lambda itemName=itemName,lbl=lbl:changeItemQuantity(category,itemName,1,lbl),
).grid(
row=0,
column=2,
)
...