首页 > 解决方案 > tkinter TopLevel 销毁引发 AttrbuteError

问题描述

我正在 tkinter 中编写一个多窗口 GUI。单击主窗口中的“加载 CSV 数据”按钮会创建一个 LoadWindow 类的实例,该实例继承自tkinter.TopLevel,并且 MainGui 实例被传递给 LoadWindow,因为我想从 LoadWindow 操作它。但是,当我self.destroy在单击“加载 CSV”按钮时调用关闭“加载 CSV”窗口时,即使“加载 CSV 数据”窗口关闭,我也会收到以下错误。

if self._name in self.master.children:
AttributeError: 'MainGUI' object has no attribute 'children'

下面是代码:

import tkinter as tk
from tkinter import ttk
from tkinter import messagebox

import os
import pandas as pd


class MainGUI:
    def __init__(self, master):
        self.master = master
        master.title("Main Window")
        master.geometry("700x500")

        # create all elements in main window
        load_csv_button = ttk.Button(master,
                                     text='Load CSV Data',
                                     command=lambda: LoadWindow(master=self))
        load_db_button = ttk.Button(master, text='Load from Database')
        save_db_button = ttk.Button(master, text='Save Current File to Database')
        data_transform_button = ttk.Button(master, text='Data Transformation')
        data_analysis_button = ttk.Button(master, text='Data Analysis')
        #update later
        preview_df_button = ttk.Button(master, text='Preview Data Frame',
                                       command=lambda: print(self.main_df))
        self.text_box = tk.Text(master, bg='grey')

        # insert welcome message into text box and disable
        self.text_box.insert(tk.END, 'Welcome to the Data Analysis Hub')
        self.text_box.config(state='disabled')

        # snap all elements to grid
        load_csv_button.grid(row=0, column=1, sticky='NSEW')
        load_db_button.grid(row=1, column=1, sticky='NSEW')
        save_db_button.grid(row=2, column=1, sticky='NSEW')
        data_transform_button.grid(row=0, column=2, sticky='NSEW')
        data_analysis_button.grid(row=1, column=2, sticky='NSEW')
        preview_df_button.grid(row=2, column=2, sticky='NSEW')
        self.text_box.grid(row=4, column=1, columnspan=2)

        self.main_df = None

    def update_textbox(self, message):
        self.text_box.config(state='normal')
        self.text_box.delete('1.0', 'end')
        self.text_box.insert(tk.END, message)
        self.text_box.config(state='disabled')


class LoadWindow(tk.Toplevel):
    def __init__(self, master):
        tk.Toplevel.__init__(self)
        self.title("Load CSV")
        self.geometry("200x200")
        self.master = master

        # get csvs in current directory
        listbox = tk.Listbox(self, selectmode=tk.SINGLE)
        csv_files = self.find_csv_files(os.getcwd())
        for csv in csv_files:
            listbox.insert(tk.END, csv)
        listbox.grid(row=1, column=1, columnspan=3, sticky='NSEW')

        # assign selected csv to maindf
        active = listbox.get(tk.ACTIVE)
        load_csv_button = ttk.Button(self, text='Load CSV',
                                     command=lambda: self.load_selected(active))
        load_csv_button.grid(row=2, column=1)

    def find_csv_files(self, path):
        # Check for csvs in path
        filenames = os.listdir(path)
        csv_files = [x for x in filenames if x.endswith('.csv')]
        return csv_files

    def load_selected(self, active):
        try:
            csv_path = os.getcwd()+"/"+active
            main_df = pd.read_csv(csv_path)
            # update maingui variable
            self.master.main_df = main_df
            # update maingui status on df loaded
            self.master.update_textbox(f'{active} loaded as DataFrame')
            self.destroy()
        except pd.errors.ParserError:
            error = 'Looks like you either have no csvs in working directory ' \
                   'or loaded a file that is not a csv, please try another file'
            messagebox.showerror(title='Load error', message=error)


if __name__ == '__main__':
    window = tk.Tk()
    maingui = MainGUI(window)

    window.mainloop()

标签: pythonooptkinter

解决方案


从这一行开始:

LoadWindow(master=self)

self不是窗口。在__init__你做的里面self.master = master。因此,self.masterinside ofLoadWindow不是小部件,因此它没有children属性。

您需要将创建方式更改LoadWindow为如下所示:

LoadWindow(master=self.master)

推荐阅读