You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter登录时调用destroy()关闭主窗口报错排查

Tkinter登录功能中调用destroy()触发AttributeError的问题解答

错误信息

Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/tkinter/__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "/Users/tomassanchezgarcia/Desktop/python-test/iniciar_sesion.py", line 84, in validar
self.root.destroy()
^^^^^^^^^
AttributeError: 'Login' object has no attribute 'root'

相关代码

main.py

from tkinter import *
from tkinter import ttk
import tkinter as tk
import pymysql
from conexion import *
from tkinter import messagebox
from PIL import Image, ImageTk
from iniciar_sesion import Login

class AplicacionInventario:

    def __init__(self):
        self.root = Tk()
        self.root.title("StockMaster")
        self.root.geometry("400x500")
        # Bloquear agrandar ventana
        self.root.resizable(0,0)

        self.wtotal = self.root.winfo_screenwidth()
        self.htotal = self.root.winfo_screenheight()
        # Guardamos el largo y alto de la ventana
        self.wventana = 400
        self.hventana = 500
        # Aplicamos la siguiente formula para calcular donde debería posicionarse
        self.pwidth = round(self.wtotal/2-self.wventana/2)
        self.pheight = round(self.htotal/2-self.hventana/2)
        # Se lo aplicamos a la geometría de la ventana
        self.root.geometry(str(self.wventana)+"x"+str(self.hventana)+"+"+str(self.pwidth)+"+"+str(self.pheight))

        self.imagen = Image.open("src/logo.png")
        self.imagen = self.imagen.resize((350, 100))  # Opcional: Redimensionar la imagen
        self.imagen = ImageTk.PhotoImage(self.imagen)
        # Crear un widget Label con la imagen y colocarlo en la ventana
        self.label_imagen = tk.Label(self.root, image=self.imagen)
        self.label_imagen.pack(pady=20)

        self.login1 = Login(self)

        self.root.mainloop()

AplicacionInventario()

iniciar_sesion.py

from tkinter import *
from tkinter import ttk
import tkinter as tk
import pymysql
from conexion import *
from tkinter import messagebox
from PIL import Image, ImageTk
from test2 import Menus

class Login:
    def __init__(self, app):
        self.app = app

        self.t_Email = "Email:"
        self.label_email = tk.Label(text=self.t_Email)
        self.label_email.pack(pady=5, padx=20)
        self.label_email.place(x=150, y=150, width=100, height=20)
        
        self.entry_email = tk.Entry()
        self.entry_email.pack(pady=5)
        self.entry_email.place(x=75, y=170, width=250, height=50)
        
        self.t_Password = "Contraseña:"
        self.label_password = tk.Label(text=self.t_Password)
        self.label_password.pack(pady=5, padx=20)
        self.label_password.place(x=150, y=220, width=100, height=20)
        
        self.entry_password = tk.Entry(show="*")
        self.entry_password.pack(pady=5)
        self.entry_password.place(x=75, y=240, width=250, height=50)
        
        self.login_email = self.entry_email.get()
        self.login_password = self.entry_password.get()
        
        self.boton_login = tk.Button(text="Iniciar Sesión", command=self.validar)
        self.boton_login.pack(pady=5)
        self.boton_login.place(x=125, y=300, width=150, height=50)

    def validar(self):
        self.connection2 = connect_to_database()
        self.cursor2 = self.connection2.cursor()
        self.cursor2.execute("SELECT nombre,password FROM usuarios")
        self.data2 = self.cursor2.fetchall()
        
        for self.fila in self.data2:
            self.v_email = self.fila[0]
            self.v_password = self.fila[1]

        self.cursor2.close()
        self.connection2.close()
        if self.entry_email.get() == "" or self.entry_password.get() == "":
            self.dialogo = tk.Toplevel()
            self.dialogo.title("Diálogo Personalizado")
            self.dialogo.geometry("300x50")
            self.dialogo.resizable(0,0)

            self.wtotal2 = self.dialogo.winfo_screenwidth()
            self.htotal2 = self.dialogo.winfo_screenheight()
        
            self.wventana2 = 300
            self.hventana2 = 50
        
            self.pwidth2 = round(self.wtotal2/2-self.wventana2/2)
            self.pheight2 = round(self.htotal2/2-self.hventana2/2)
        
            self.dialogo.geometry(str(self.wventana2)+"x"+str(self.hventana2)+"+"+str(self.pwidth2)+"+"+str(self.pheight2))
            self.etiqueta = tk.Label(self.dialogo, text="Este es un cuadro de diálogo personalizado.", padx=10, pady=10)
            self.etiqueta.pack()
        elif self.entry_email.get() == self.v_email and self.entry_password.get() == self.v_password:
            self.ventana_menu = Menus(self)
        else:
            self.root.destroy()
            messagebox.showwarning("Advertencia", "Los datos introducidos son incorrectos.")

错误原因

Login类实例本身没有root属性,主窗口的root是属于AplicacionInventario类实例的。你在validar方法中直接调用self.root.destroy(),相当于在Login对象里寻找不存在的root属性,因此触发AttributeError。

操作失误点

  1. 没有通过传入的app参数(即AplicacionInventario实例)访问主窗口的root,错误地直接使用self.root。
  2. Login类中创建的Label、Entry等控件未指定父容器,会默认绑定到系统自动创建的根窗口,而非你定义的app.root,可能引发布局异常。
  3. 在Login的__init__方法中提前获取输入框内容,此时输入框刚创建,内容为空,后续验证逻辑会拿到无效值。

解决方法

1. 修正主窗口销毁的调用方式

在iniciar_sesion.py的validar方法最后,将self.root.destroy()改为通过self.app访问主窗口的root:

else:
    self.app.root.destroy()
    messagebox.showwarning("Advertencia", "Los datos introducidos son incorrectos.")

2. 为控件指定正确的父容器

修改Login类中创建控件的代码,将self.app.root作为父容器传入:

# 示例:Label控件修改
self.label_email = tk.Label(self.app.root, text=self.t_Email)
# Entry控件修改
self.entry_email = tk.Entry(self.app.root)
# 其余Label、Entry、Button控件均需添加self.app.root作为第一个参数

3. 修正输入内容的获取时机

将__init__中获取输入框内容的代码移到validar方法内,确保拿到用户输入的最新内容:

def validar(self):
    # 移到此处获取输入内容
    self.login_email = self.entry_email.get()
    self.login_password = self.entry_password.get()
    
    # 后续数据库验证逻辑...

内容的提问来源于stack exchange,提问作者Tomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 05:30:10