循环生成CTkCheckBox后无法删除指定订单条目Frame的问题求助
问题描述
通过for循环生成多个CTkCheckBox复选框,勾选时会在ordertotalframe中创建对应物料的名称、数量、价格Label(订单条目相关元素),并累加订单总价。但勾选多个选项后取消第一个选中项时,仅会移除最后添加的条目,无法正确删除指定订单条目。
相关代码
生成复选框的原代码
def MakeMaterialsButtons(self): self.ChoosingVariable = ctk.IntVar(value=0) CallDB = aoracandlestudiodb.cursor() query = 'SELECT idmaterials FROM materials WHERE mat_category = %s ' CallDB.execute(query, (StickersName,)) Stickers_Categories_ID3 = CallDB.fetchall() i = 1 j = 1 mat_distance = 10 x_mat_distance = 10 for stickers_item in Stickers_Categories_ID3: get_mat_ID_str = str(stickers_item) get_mat_ID = get_mat_ID_str[1:-2] int(get_mat_ID) if mat_distance >= 150: x_mat_distance = x_mat_distance + 200 mat_distance = 10 self.MakeOrderButtonsForStickers(get_mat_ID, mat_distance, x_mat_distance) j = j + 1 mat_distance = mat_distance + 60 def MakeOrderButtonsForStickers(self, get_mat_ID, mat_distance, x_mat_distance): get_mat_name = get_NameOfMaterial(get_mat_ID) ItembyCategoryCheckBox = ctk.CTkCheckBox(self.StickersFrame, text=get_mat_name, command=lambda: self.Add_To_Order_Materials(get_mat_ID, get_mat_name), fg_color="#a881af", variable=self.ChoosingVariable, onvalue="1", offvalue="0", width=150, height=40, text_color='black', font=("arial bold", 20), border_width=4, border_color='black') ItembyCategoryCheckBox.place(x=x_mat_distance, y=mat_distance)
原勾选处理函数
def Add_To_Order_Materials(self, Mat_ID, mat_name): print(self.ChoosingVariable.get()) Mat_Price = get_PriceOfMaterial(Mat_ID) if self.ChoosingVariable.get() == 1: if mat_name != self.Name_Order_Counter: self.Item_Order_Counter = 1 self.Item_Order_Distance = self.Item_Order_Distance + 30 self.Name_Order_Counter = mat_name self.Material_Order_Text = ctk.CTkLabel(self.ordertotalframe, text=mat_name, font=("arial bold", 20), text_color='black') self.Material_Order_Text.place(x=55, y=self.Item_Order_Distance) self.Material_quantity_text = ctk.CTkLabel(self.ordertotalframe, text=(str(self.Item_Order_Counter) + "x"), font=("arial bold", 20), text_color='black') self.Material_quantity_text.place(x=210, y=self.Item_Order_Distance) self.Material_price_text = ctk.CTkLabel(self.ordertotalframe, text=(str(Mat_Price) + " €"), font=("arial bold", 20), text_color='black') self.Material_price_text.place(x=300, y=self.Item_Order_Distance) self.Item_Order_Counter = self.Item_Order_Counter + 1 self.Order_Total_price = self.Order_Total_price + Mat_Price self.Order_Total_price_Label.configure(text=(str(self.Order_Total_price) + " €")) self.Material_Order_Text.place() else: self.Material_quantity_text.configure(text=(str(self.Item_Order_Counter) + "x")) self.Material_Order_Text.configure(text=mat_name) self.Item_Order_Counter = self.Item_Order_Counter + 1 self.Order_Total_price = self.Order_Total_price + Mat_Price self.Order_Total_price_Label.configure(text=(str(self.Order_Total_price) + " €")) else: self.Name_Order_Counter = "" self.Material_Order_Text.place_forget() self.Material_quantity_text.place_forget() self.Material_price_text.place_forget() self.Item_Order_Distance = self.Item_Order_Distance - 30 self.Order_Total_price = self.Order_Total_price - Mat_Price self.Order_Total_price_Label.configure(text=(str(round(self.Order_Total_price, 2)) + " €"))
问题根源
- 共用状态变量:所有复选框绑定同一个
self.ChoosingVariable,无法区分当前操作的是哪个复选框,状态判断完全混乱 - 单变量存储组件:用
self.Material_Order_Text等单个变量存储Label实例,每次新创建都会覆盖旧数据,取消时只能操作最后一个创建的组件 - 无独立条目管理:没有为每个物料维护独立的订单状态(数量、组件实例),无法精准定位要删除的目标条目
修正方案
1. 初始化订单管理结构
在类的__init__方法中添加以下初始化代码,用于存储订单状态:
def __init__(self, *args, **kwargs): # 保留原有初始化代码 self.order_items = {} # 存储每个物料的订单信息:key为物料ID,value是包含数量、组件的字典 self.Order_Total_price = 0.0 # 订单总价初始值 self.base_item_y = 30 # 订单条目的起始Y坐标
2. 修改复选框生成逻辑
为每个复选框创建独立的状态变量,避免共用变量导致的状态混乱:
def MakeMaterialsButtons(self): CallDB = aoracandlestudiodb.cursor() query = 'SELECT idmaterials FROM materials WHERE mat_category = %s ' CallDB.execute(query, (StickersName,)) Stickers_Categories_ID3 = CallDB.fetchall() mat_distance = 10 x_mat_distance = 10 # 存储每个复选框的状态变量 self.checkbox_vars = {} for stickers_item in Stickers_Categories_ID3: get_mat_ID_str = str(stickers_item) get_mat_ID = int(get_mat_ID_str[1:-2]) # 直接转换为整数 if mat_distance >= 150: x_mat_distance += 200 mat_distance = 10 self.MakeOrderButtonsForStickers(get_mat_ID, mat_distance, x_mat_distance) mat_distance += 60 def MakeOrderButtonsForStickers(self, get_mat_ID, mat_distance, x_mat_distance): get_mat_name = get_NameOfMaterial(get_mat_ID) # 为每个复选框创建独立的状态变量 var = ctk.IntVar(value=0) self.checkbox_vars[get_mat_ID] = var ItembyCategoryCheckBox = ctk.CTkCheckBox( self.StickersFrame, text=get_mat_name, # 绑定正确的物料ID和状态变量到回调函数 command=lambda mat_id=get_mat_ID, name=get_mat_name, v=var: self.Add_To_Order_Materials(mat_id, name, v), fg_color="#a881af", variable=var, onvalue=1, offvalue=0, width=150, height=40, text_color='black', font=("arial bold", 20), border_width=4, border_color='black' ) ItembyCategoryCheckBox.place(x=x_mat_distance, y=mat_distance)
3. 重构勾选处理函数
通过字典管理每个物料的订单条目,实现精准添加/删除:
def Add_To_Order_Materials(self, Mat_ID, mat_name, var): Mat_Price = get_PriceOfMaterial(Mat_ID) current_state = var.get() if current_state == 1: # 处理勾选逻辑:添加或更新条目 if Mat_ID not in self.order_items: # 计算当前条目的Y坐标 item_y = self.base_item_y + len(self.order_items) * 30 # 创建各Label组件 name_label = ctk.CTkLabel( self.ordertotalframe, text=mat_name, font=("arial bold", 20), text_color='black' ) name_label.place(x=55, y=item_y) quantity_label = ctk.CTkLabel( self.ordertotalframe, text="1x", font=("arial bold", 20), text_color='black' ) quantity_label.place(x=210, y=item_y) price_label = ctk.CTkLabel( self.ordertotalframe, text=f"{Mat_Price} €", font=("arial bold", 20), text_color='black' ) price_label.place(x=300, y=item_y) # 将条目信息存入字典 self.order_items[Mat_ID] = { "name": mat_name, "price": Mat_Price, "quantity": 1, "name_label": name_label, "quantity_label": quantity_label, "price_label": price_label, "y": item_y } self.Order_Total_price += Mat_Price else: # 已有条目,增加数量 item = self.order_items[Mat_ID] item["quantity"] += 1 item["quantity_label"].configure(text=f"{item['quantity']}x") self.Order_Total_price += Mat_Price # 更新总价标签 self.Order_Total_price_Label.configure(text=f"{round(self.Order_Total_price, 2)} €") else: # 处理取消勾选逻辑:移除条目 if Mat_ID in self.order_items: item = self.order_items.pop(Mat_ID) # 隐藏所有相关Label item["name_label"].place_forget() item["quantity_label"].place_forget() item["price_label"].place_forget() # 扣除对应总价 self.Order_Total_price -= item["price"] * item["quantity"] self.Order_Total_price_Label.configure(text=f"{round(self.Order_Total_price, 2)} €") # 重新排列剩余条目的位置 idx = 0 for item_id in self.order_items: current_item = self.order_items[item_id] new_y = self.base_item_y + idx * 30 current_item["y"] = new_y current_item["name_label"].place(y=new_y) current_item["quantity_label"].place(y=new_y) current_item["price_label"].place(y=new_y) idx += 1
关键改进点
- 每个复选框使用独立的
IntVar,精准区分操作的物料 - 用字典
self.order_items存储每个物料的完整订单信息,包括组件实例和数量 - 取消勾选时直接根据物料ID定位条目,移除后自动重新排列剩余条目的位置
- 总价计算基于每个条目的数量和单价,避免错误累加/扣除
内容的提问来源于stack exchange,提问作者Pavlos Katsioulis
相关产品推荐
相关产品推荐

