我正在构建一个小型应用程序。
运行此最小程序时,他/她将在开始屏幕上看到一个登录按钮( startS )。当单击它时,该窗口将被破坏并创建一个新窗口( homeP ),该窗口具有一个用于打开文本框的按钮和一个注销按钮。单击注销按钮时,销毁 homeP 并再次启动 startS 。
重现我的问题的步骤:
单击登录->单击“输入成绩”->单击注销->单击登录->单击“输入成绩”->错误:(can't invoke "grid" command: application has been destroyed
)
发生这种情况是因为我一直在使用globals
。正如人们所看到的,我想防止帧彼此堆叠,因此我检查是否已创建 eGrade 帧(即globals()
中是否存在)。如果是,则将现有的放置。但是,当页面被破坏时,它仍位于globals()
中,因此,如果我们尝试在窗口小部件被破坏时再次将其放置,它将给出一个错误。
from tkinter import *
def Start():
global startS
startS = Tk()
loginButton = Button(startS, text='Login', bg='blue', fg='white', command=Login)
loginButton.grid()
startS.mainloop()
def Home():
global homeP
homeP = Tk()
enterButton = Button(homeP, text='Enter Grades', bg='blue', fg='white', command=enterG)
enterButton.grid(row=0, column=0, sticky="w")
logoutButton = Button(homeP, text='LogOut', bg='brown', fg='white', command=Logout)
logoutButton.grid(row=0, column=1, sticky="e")
homeP.mainloop()
def enterG():
global homeP
global eGrade
if 'eGrade' not in globals(): #Prevent Frames from stacking up on one another
eGrade = Frame(homeP)
enterGrades = Text(eGrade, width=64, height=10)
enterGrades.grid(row=0, column=0, sticky="ewns")
eGrade.grid(row=1, column=0, columnspan=2, sticky="ns")
else:
eGrade.grid(row=1, column=0, columnspan=2, sticky="ns")
# for name, value in globals().copy().items():
# print(name, value)
def Logout():
global homeP
homeP.destroy()
Start()
def Login():
global startS
startS.destroy()
Home()
Start()
因此,我想从专家那里获得一些建议,涉及使用全局变量是否是一种好习惯以及如何规避此问题?
答案 0 :(得分:4)
出现此错误的主要原因是第一次注销时,销毁了保存文本小部件的容器。即使小部件(框架)存在于全局名称空间中,您也已经销毁了分配给它的tkinter实例。因此无法再应用于它。
这是直接创建Tk()
实例而不是仅使用一个实例并管理其中的数据的直接结果。
我的示例将您的代码压缩为更简单的内容,并且应该为您提供一个良好的起点。我们将在此处为您的文本框中的文本创建一个全局跟踪变量。这样,我们可以在注销时保存数据,然后在重新登录时重新应用数据。因此保留了旧文本。
import tkinter as tk
def Home():
clear_widgets()
tk.Button(root, text='Enter Grades', bg='blue', fg='white', command=enterG).grid(row=0, column=0, sticky="w")
tk.Button(root, text='LogOut', bg='brown', fg='white', command=logout).grid(row=0, column=1, sticky="e")
def enterG():
global txt
if txt == None:
txt = tk.Text(root, width=64, height=10)
txt.grid(row=1, column=0, columnspan=2, sticky="ns")
txt.insert(1.0, text_data)
def logout():
global txt, text_data
text_data = txt.get(1.0, "end-1c")
clear_widgets()
txt = None
tk.Button(root, text='Login', bg='blue', fg='white', command=login).grid(row=0, column=0)
def clear_widgets():
for widget in root.winfo_children():
widget.destroy()
def login():
# some method of checking login credentials.
Home()
root = tk.Tk()
text_data = ""
tk.Button(root, text='Login', bg='blue', fg='white', command=login).grid(row=0, column=0)
root.mainloop()
无论您迟早要开始在OOP中进行编码。这是一个很好的选择,它将使我们避免一起使用global。在类中,我们可以使用称为类属性的东西,该类属性可以从任何方法(类函数)访问,而无需定义全局。
这是您的代码的类示例。
import tkinter as tk
class Example(tk.Tk):
def __init__(self):
super().__init__()
self.text_data = ""
self.txt = None
tk.Button(self, text='Login', bg='blue', fg='white', command=self.login).grid(row=0, column=0)
def home(self):
self.clear_widgets()
tk.Button(self, text='Enter Grades', bg='blue', fg='white', command=self.enter_g).grid(row=0, column=0, sticky="w")
tk.Button(self, text='LogOut', bg='brown', fg='white', command=self.logout).grid(row=0, column=1, sticky="e")
def enter_g(self):
if self.txt == None:
self.txt = tk.Text(self, width=64, height=10)
self.txt.grid(row=1, column=0, columnspan=2, sticky="ns")
self.txt.insert(1.0, self.text_data)
def logout(self):
self.text_data = self.txt.get(1.0, "end-1c")
self.clear_widgets()
self.txt = None
tk.Button(self, text='Login', bg='blue', fg='white', command=self.login).grid(row=0, column=0)
def clear_widgets(self):
for widget in self.winfo_children():
widget.destroy()
def login(self):
# some method of checking login credentials.
self.home()
Example().mainloop()