酒吧收银Tkinter应用:如何跨函数删除Frame内创建的Label?
如何删除Tkinter Frame中由其他函数创建的Label?
我给协会酒吧做了个娱乐用的小型收银APP,现在实现错误输入行的删除功能时卡壳了——删除按钮能删掉数据库里最后一行数据,但界面上旧的行还看得见,只有新增产品时才会被覆盖。我怀疑是作用域的问题,但没搞明白具体怎么回事。我的需求是:通过另一个函数删除某个函数创建的Tkinter Frame里的Label。
附上的相关代码:
root = Tk() root.geometry("1024x600+0+0") #Full Screen Window #root.attributes('-fullscreen', True) root.title('Le Postillion') products = LabelFrame(root) products.place(x=0, y=0) lineHolder = LabelFrame(root) lineHolder.place(x=540, y=160) bill = LabelFrame(root) bill.place(x=540, y=0) def deleteline(): conn = sqlite3.connect('BarSales.db') connection = conn.cursor() connection.execute("DELETE FROM ticket WHERE ROWID = (SELECT MAX(ROWID) FROM ticket)") conn.commit() conn.close() #to clear the previously created bill display root.winfo_children()[2].destroy() conn = sqlite3.connect('BarSales.db') connection = conn.cursor() connection.execute("SELECT * FROM ticket") result1 = connection.fetchall() lineNumber = 2 lineHolder = LabelFrame(root) lineHolder.place(x=540, y=160) for item in result1: x = item[0] y = item[1] z = item[2] print(x, " ", y, " ", z) bill1 = Label(lineHolder, text=x,fg="black", width=10, height=1, cursor="hand2").grid(row=lineNumber, column=0) bill2 = Label(lineHolder, text=y,fg="black", width=10, height=1, cursor="hand2").grid(row=lineNumber, column=1) bill3 = Label(lineHolder, text=z,fg="black", width=10, height=1, cursor="hand2").grid(row=lineNumber, column=2) lineNumber += 1 conn.close()
问题根源
你的代码有两个核心问题:
- 作用域混淆:全局定义的
lineHolder和deleteline函数里重新定义的同名局部变量是两个独立对象,函数里的新Frame只是叠在旧的全局Frame上面,旧的内容自然还会显示。 - 不可靠的控件定位:
root.winfo_children()[2].destroy()靠索引找控件的方式极不稳定,控件顺序很容易因为新增/删除其他控件改变,根本没法保证销毁的是目标Frame。
解决方案
推荐两种可靠的修复方式:
方案1:直接清空现有Frame内的所有子控件
不用销毁重建整个Frame,直接遍历清空里面的Label即可:
def deleteline(): # 删除数据库最后一行数据 conn = sqlite3.connect('BarSales.db') connection = conn.cursor() connection.execute("DELETE FROM ticket WHERE ROWID = (SELECT MAX(ROWID) FROM ticket)") conn.commit() conn.close() # 清空lineHolder里的所有子控件 for widget in lineHolder.winfo_children(): widget.destroy() # 重新读取数据库数据生成账单 conn = sqlite3.connect('BarSales.db') connection = conn.cursor() connection.execute("SELECT * FROM ticket") result1 = connection.fetchall() lineNumber = 2 for item in result1: x = item[0] y = item[1] z = item[2] print(x, " ", y, " ", z) # 注意:Label和grid要分开写,否则变量存的是None(grid返回值) bill1 = Label(lineHolder, text=x, fg="black", width=10, height=1, cursor="hand2") bill1.grid(row=lineNumber, column=0) bill2 = Label(lineHolder, text=y, fg="black", width=10, height=1, cursor="hand2") bill2.grid(row=lineNumber, column=1) bill3 = Label(lineHolder, text=z, fg="black", width=10, height=1, cursor="hand2") bill3.grid(row=lineNumber, column=2) lineNumber += 1 conn.close()
方案2:销毁旧Frame并重建,同时更新全局变量
如果坚持要重建Frame,必须用global声明修改全局变量,确保新Frame覆盖旧的引用:
def deleteline(): # 删除数据库最后一行数据 conn = sqlite3.connect('BarSales.db') connection = conn.cursor() connection.execute("DELETE FROM ticket WHERE ROWID = (SELECT MAX(ROWID) FROM ticket)") conn.commit() conn.close() # 销毁旧的全局Frame,更新全局变量 global lineHolder lineHolder.destroy() lineHolder = LabelFrame(root) lineHolder.place(x=540, y=160) # 重新读取数据库数据生成账单 conn = sqlite3.connect('BarSales.db') connection = conn.cursor() connection.execute("SELECT * FROM ticket") result1 = connection.fetchall() lineNumber = 2 for item in result1: x = item[0] y = item[1] z = item[2] print(x, " ", y, " ", z) bill1 = Label(lineHolder, text=x, fg="black", width=10, height=1, cursor="hand2") bill1.grid(row=lineNumber, column=0) bill2 = Label(lineHolder, text=y, fg="black", width=10, height=1, cursor="hand2") bill2.grid(row=lineNumber, column=1) bill3 = Label(lineHolder, text=z, fg="black", width=10, height=1, cursor="hand2") bill3.grid(row=lineNumber, column=2) lineNumber += 1 conn.close()
额外注意事项
- 如果之后需要单独操作某个Label(比如修改文本、删除单行),必须把
Label()和grid()分开写,因为Label(...).grid(...)返回的是None,无法用来操作控件。 - 修改全局变量时,一定要在函数里用
global声明,否则会被当成局部变量处理,导致全局变量没被更新。
内容的提问来源于stack exchange,提问作者user3617217
相关产品推荐
相关产品推荐

