2

Me estoy iniciando en Tkinter y en concreto ahora estoy estudiando / revisando el widget Treeview.

Estudio y analizo distinto programas de este y otros foros y me ha surgido una duda, y es que unas veces lo instancian con self y otras con master, entendiendo por master padre del widget contenedor, y yo entiendo que esta es la correcta, pero me extraña que otros, con mucha mas experiencia, empleen la forma self. ¿Que se debe usar? ¿El contenedor o el padre?

He hecho la prueba cambiando self por master y en principio funciona correctamente, pero algo me dice que no es lo mismo. Uno de los dos modos debe ser el correcto

Pongo un ejemplo:

class Application(ttk.Frame):

    def __init__(self, main_window):
        super().__init__(main_window)
        main_window.title("Explorador de archivos y carpetas")

        #self.treeview = ttk.Treeview(self) ##### No deberia ser main_Window???? **ORIGINAL**
        self.treeview = ttk.Treeview(main_window)
        self.treeview.grid(row=0, column=0, sticky="nsew")

¿Alguien me puede aclarar esta duda?

FJSevilla
  • 55,603
  • 7
  • 35
  • 58
Chemag
  • 117
  • 7

1 Answers1

1

Lo que le debes pasar siempre a todo widget (incluidas StringVar y demás variables) es una referencia al widget padre, punto. Si no se pasa ninguna referencia el padre será la ventana principal y esto puede dar problemas inesperados:

El widget padre es el widget que contiene a nuestro widget a instanciar.

Bien, ahora vamos a ver tu ejemplo. Tiene una clase Application que deriva de Frame y dentro instancias un TreeView. self hace referencia a la instancia de esa clase, es decir es un ttk.Frame. La pregunta que debes hacerte es:

¿Dónde quiero ubicar mi TreeView?

  • Si usas main_window lo ubicas en la ventana principal, es decir, es un widget hermano del frame Application.

  • Si usas self lo ubicas dentro del Frame que a su vez está dentro de main_window.

La lógica nos dice que Application es el widget contenedor base de toda la app y que el Treeview y todo widget de la app debería estar dentro del mismo. Lo lógico es usar self como padre. De hecho es común usar Frames como base geométrica para estructurar los distintos nieveles de la app.

Ten en cuenta que los administrados de geometría, métodos grid, pack y place, actúan ubicando en el widget padre, no es lo mismo por tanto ubicar fuera o dentro del Frame.

Mejor un ejemplo:

import tkinter as tk
from tkinter import ttk


class Application(ttk.Frame):

    def __init__(self, main_window):
        super().__init__(main_window)
        main_window.title("Explorador de archivos y carpetas")
        s = ttk.Style()
        s.configure('My.TFrame', background='red')
        self["style"] = 'My.TFrame'

        self.btn1 = tk.Button(main_window, text="Botón 1")
        self.btn1.pack()
        self.btn2 = tk.Button(self, text="Botón 2")
        self.btn2.pack()


if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("600x400")
    root.configure(background='green')
    Application(root).pack(side="top", fill="both", expand=True)
    root.mainloop() 

He usado pack y dos botones por comodidad, pero esto es irrelevante. Le he asignado un fondo verde a la ventana principal y uno rojo al Frame (Application).

  • Si usamos main_window como padre del primer botón y self para el segundo:

     self.btn1 = tk.Button(main_window, text="Botón 1")
     self.btn2 = tk.Button(self, text="Botón 2")
    

    introducir la descripción de la imagen aquí

  • Si usamos self para ambos:

    self.btn1 = tk.Button(self, text="Botón 1")
    self.btn2 = tk.Button(self, text="Botón 2")
    

    introducir la descripción de la imagen aquí

  • Si usamos main_window para ambos:

    self.btn1 = tk.Button(main_window, text="Botón 1")
    self.btn2 = tk.Button(main_window, text="Botón 2")
    

    introducir la descripción de la imagen aquí ¿Para que queremos el Frame? Para eso no derivamos de ttk.Frame y creamos todo en la ventana principal...

FJSevilla
  • 55,603
  • 7
  • 35
  • 58
  • Muy clarificadora tu respuesta y acertados tus ejemplos. Solo a efectos conceptuales básicos, en mi código ejemplo
    ¿super().__init__(main_window) conceptualmente equivalente a self = ttk.Frame(main_window)?
    – Chemag Apr 15 '20 at 08:19
  • 1
    No, equivale a `ttk.Frame.__init__(self, main_window)`, ambos lo que hacen es llamar al inicializador de la clase padre pero `__init__` no es un constructor, no retorna nada. mejor dicho retorna `None`. La instacia (`self`) la genera el `__new__` de `Application`, esa instancia se pasa al `__init__` de `Application` y desde así se pasa al `__init__` del padre que iniciaiza la instancia con lo necesario para ser un Frame. Si haces `self = ttk.Frame.__init__(self, main_window)` `self ` será `None`. No se si me he medio explicado, es complicado en un comentario por espacio y por el formato. – FJSevilla Apr 15 '20 at 08:26
  • Puedes mirate [¿Qué es un cosntructor?](https://es.stackoverflow.com/a/63451/15089) y [¿Qué es y qué utilidad tiene super en POO?](https://es.stackoverflow.com/a/130252/15089) a ver si te aclaran algo al respecto. – FJSevilla Apr 15 '20 at 08:27
  • No me referia a equivalencia en cuanto a código, sino que en esta sentencia es donde se instancia el frame y se hace que main_window sea el padre. Estudiaré las referencia que me facilitas. – Chemag Apr 15 '20 at 08:29
  • 1
    Eso si, la llamada al inicializador del padre es lo que hace que sea un `ttk.Frame` y se comporte como el. Ya por herencia es un ttk.Frame, pero sin inicializar porque hemos sobrescrito el `__init__` del padre(tiene los métodos heredados de el). La inialización incluye realizar las interacciones necesarias con el intérprete TCL, llamada a otros incializadores o constructores si es necesario, cálculos de geometría, enlace de eventos, ... así como agregar todos los atributos definidos en el `__init__` paterno, lo que incluye hacer que `main_window` sea el padre (`master = `) – FJSevilla Apr 15 '20 at 08:36