El Sat, 22 Apr 2023 15:52:25 -0300
Horacio <horacio9...@gmail.com> dijo:

> Buenas Tardes tengo una duda de porque sale este error,,
> 
> Exception in Tkinter callback Traceback (most recent call last): File
> "/usr/lib/python3.10/tkinter/__init__.py", line 1921, in call return
> self.func(*args) File
> "/home/hcaste/Escritorio/investigar/Proyecto_Florencia/Programas/Interface/imv02.py",
> line 24, in salir root.destroy() NameError: name 'root' is not defined


Hola.

Estas usando root dentro del método salir. Pero ahí root está fuera del alcance 
del método.

El método es un callback de tu botón salir. Se ejecutó cuando le hiciste clic 
con el botón izquierdo del ratón al botón que tienes definido en el widget.

Te recomiendo uses los widgets ttk en lugar de los clásicos y definas su 
apariencia mediante el atributo style. También te recomiendo que uses la 
geometría gird en lugar de la pack. A la larga te resultará más fácil.

Dale un vistazo a la documentación en linea que recomiendan en la doc de 
Python: https://docs.python.org/es/3/library/tkinter.html especialmente al 
primer enlace.

Aquí te dejo una versión ligeramente reformada de tú código.
La clase deriva ahora de ttk.Frame, con lo que estas heredando toda la 
maquinaria de tkinter en tu clase, por eso le puedes hacer app.mainloop a la 
instancia que creas.

Un detalle: self.winfo_toplevel() te devuelve la ventana más externa. Esta no 
es la ventana de tu app (es decir la ventana de la instancia que has creado al 
instanciar la clase cuando has hecho app = Img(tk.Tk()). Este 
self.winfo_toplevel() es el padre de tu instancia. Es un "toplevel" (se ha 
creado "mágicamente" cuando has instanciado la clase Tk) y le puedes aplicar 
los métodos de toplevel como .title (un Frame no tiene .title). Cuando 
instancias la clase, la instancia (self) es un Frame que es hijo de master 
(master y top en mi código son lo mismo). Pero es en top donde muestras tus 
widgets. El mainloop lo ejecutas sobre la instancia y a la hora de matarlo, lo 
haces con self.destroy()

Valora poner la creación de los widgets en un método separado del __init__ y es 
por eso por lo que uso top = self.winfo_toplevel() pues recupero en el método 
el "master" del __init__. El métódoo __init__ lo dejo para definir otros 
atributos de la instancia, 


import sys

import tkinter as tk
from tkinter import ttk


class Img(ttk.Frame):
    """
    La clase
    """

    def __init__(self, master):
        super().__init__(master)

        self._crear_widgets()

    def _crear_widgets(self):
        top = self.winfo_toplevel()  # Esto es equivalente a master
        top.resizable(height=tk.FALSE, width=tk.FALSE)
        top.title('Imagen en Tk')
        top.config(background='spring green', relief='sunken', bd=10)
        top.protocol('WM_DELETE_WINDOW', self._salir)
        top.option_add('*tearOff', tk.FALSE)

        marco = ttk.Frame(top, borderwidth=5, width=800, height=455)
        marco.grid(row=0, column=0)

        s = ttk.Style()
        s.configure('Miboton.TButton', background='red2', foreground='yellow', 
borderwidth=5)
        boton_salir = ttk.Button(top, text='Salir', style='Miboton.TButton', 
command=self._salir)
        boton_salir.grid(row=1, column=0, sticky='E')

    def _salir(self, envento=None):
        self.destroy()
        sys.exit()


# Prefiero llamar a la aplicación así:
if __name__ == '__main__':
    app = Img(tk.Tk())
    app.mainloop()

Derivar la clase principal de tu aplicación de ttk.Frame es útil cuando generas 
ventanas secundarias hijas de la principal. Si no quieres que tu clase esté 
derivada de ttk.Frame debes aplicar el mainloop a la instancia de tk, asignarla 
a un atributo en el __init__ para poder usarla en los métodos y destruirla 
cuando terminas con destroy. Podrías hacerlo así:


import sys

import tkinter as tk
from tkinter import ttk


class Img():
    """
    La clase
    """

    def __init__(self, raiz):
        self.root = raiz
        self._crear_widgets()

    def _crear_widgets(self):
        top = self.root.winfo_toplevel()
        top.resizable(height=tk.FALSE, width=tk.FALSE)
        top.title('Imagen en Tk')
        top.config(background='spring green', relief='sunken', bd=10)
        top.protocol('WM_DELETE_WINDOW', self._salir)
        top.option_add('*tearOff', tk.FALSE)

        marco = ttk.Frame(top, borderwidth=5, width=800, height=455)
        marco.grid(row=0, column=0)

        s = ttk.Style()
        s.configure('Miboton.TButton', background='red2', foreground='yellow', 
borderwidth=5)
        boton_salir = ttk.Button(top, text='Salir', style='Miboton.TButton', 
command=self._salir)
        boton_salir.grid(row=1, column=0, sticky='E')

    def _salir(self, envento=None):
        self.root.destroy()
        sys.exit()


# Prefiero llamar a la aplicación así:
if __name__ == '__main__':
    raiz = tk.Tk()
    app = Img(raiz)
    raiz.mainloop()


Saludos


Miguel Sánchez










_______________________________________________
Python-es mailing list
Python-es@python.org
https://mail.python.org/mailman/listinfo/python-es

Responder a