You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

循环生成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)) + " €"))

问题根源

  1. 共用状态变量:所有复选框绑定同一个self.ChoosingVariable,无法区分当前操作的是哪个复选框,状态判断完全混乱
  2. 单变量存储组件:用self.Material_Order_Text等单个变量存储Label实例,每次新创建都会覆盖旧数据,取消时只能操作最后一个创建的组件
  3. 无独立条目管理:没有为每个物料维护独立的订单状态(数量、组件实例),无法精准定位要删除的目标条目

修正方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 09:07:02