反复打开和销毁窗口时使用全局变量的问题

时间:2018-11-08 20:16:18

标签: python python-3.x tkinter

我正在构建一个小型应用程序。

运行此最小程序时,他/她将在开始屏幕上看到一个登录按钮( 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()

因此,我想从专家那里获得一些建议,涉及使用全局变量是否是一种好习惯以及如何规避此问题?

1 个答案:

答案 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()