python - 反复打开和销毁窗口时使用全局变量的问题
问题描述
我正在构建一个小型应用程序。
当一个人运行这个最小的程序时,他/她会看到开始屏幕上有一个登录按钮(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()
所以我想知道专家们关于这个话题的一些建议,关于使用全局变量是否是一种好的做法,我该如何规避这个问题?
解决方案
您收到此错误的主要原因是当您第一次注销保存文本小部件的容器时。即使小部件(框架)存在于全局命名空间中,您也已经销毁了分配给它的 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 进行编码。这是一个不错的选择,它将使我们能够避免全局的全部。在一个类中,我们可以使用一种称为类属性的东西,它可以从任何方法(类函数)访问,而无需定义全局。
这是您的代码的类示例。
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()
推荐阅读
- powerquery - 如何设置查询的源名称是另一个查询的值?
- apache-kafka - KTable 外键连接与 avro 模式注册表不兼容
- android - 伴随对象中的 val arrayListOf() 返回无效值
- python - 获取错误绑定参数 0 - 使用构造函数创建对象时可能不支持的类型
- python - 为什么这不能正确提取价格?每个价格都为0?它还说在定义之前就引用了“付款”?
- mysql - MySQL外键约束不兼容
- javascript - 在数组中添加新对象
- linux - 我使用 pip 在 linux 上安装了 pygame,但是当尝试在 VS Code 中导入它时,它说没有名为“pygame”的模块。我该如何解决这个问题?
- javascript - ANTLR4 javascript 访问者中的 ctx
- javascript - 获取当前正在播放 youtube 视频的频道名称