Проблема с использованием глобальных переменных при многократном открытии и уничтожении окна - PullRequest
0 голосов
/ 08 ноября 2018

Я создаю небольшое приложение.

При запуске этой минимальной программы он / она увидит, что на стартовом экране есть кнопка входа в систему ( 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 голосов
/ 08 ноября 2018

Основная причина, по которой вы получаете эту ошибку, заключается в том, что вы выходите из системы при первом уничтожении контейнера, содержащего текстовый виджет. Даже если виджет (фрейм) существует в глобальном пространстве имен, вы уничтожили экземпляр 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()

Однако рано или поздно вы захотите начать кодирование в ООП. Это хороший вариант, и он позволит нам избежать глобального единства. В классе мы можем использовать нечто, называемое атрибутом класса, к которому можно получить доступ из любого метода (функции класса) без необходимости определять глобальный.

Вот пример класса вашего кода.

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...