68 - tkinter : Layout Manager (administrador de diseño)

Una de las herramientas fundamentales cuando armamos interfaces visuales es la metodología que utilizamos para disponer los controles dentro del formulario. Hasta ahora hemos utilizado el administrador de diseño Grid.

En la librería GUI tkinter disponemos de tres Layout Manager para disponer controles dentro de una ventana:

  1. Grid
  2. Pack
  3. Place

Solo se puede utilizar uno de estos Layout Manager dentro de un contenedor, recordemos que un contenedor puede ser la ventana propiamente dicha, un Frame o un LabelFrame.

El gestor de diseño más completo y que se adapta en la mayoría de las situaciones es el Grid, pero podemos en muchos casos crear Frame o LabelFrame y definir dentro de estos Layout Manager de tipo Pack o Place.

Layout Manager: Pack

Veamos con un ejemplo como se ubican los Widget utilizando Pack.

Problema:

Disponer una serie de botones utilizando el Layout Manager de tipo Pack.

La representación visual debe ser:

Pack

Programa: ejercicio246.py

Ver video

import tkinter as tk
from tkinter import ttk

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        self.boton1=ttk.Button(self.ventana1, text="Boton 1")
        self.boton1.pack(side=tk.TOP, fill=tk.BOTH)
        self.boton2=ttk.Button(self.ventana1, text="Boton 2")
        self.boton2.pack(side=tk.TOP, fill=tk.BOTH)
        self.boton3=ttk.Button(self.ventana1, text="Boton 3")
        self.boton3.pack(side=tk.TOP, fill=tk.BOTH)
        self.boton4=ttk.Button(self.ventana1, text="Boton 4")
        self.boton4.pack(side=tk.LEFT)
        self.boton5=ttk.Button(self.ventana1, text="Boton 5")
        self.boton5.pack(side=tk.RIGHT)
        self.boton6=ttk.Button(self.ventana1, text="Boton 6")
        self.boton6.pack(side=tk.RIGHT)
        self.boton7=ttk.Button(self.ventana1, text="Boton 7")
        self.boton7.pack(side=tk.RIGHT)
        self.ventana1.mainloop()

aplicacion1=Aplicacion()

Para ubicar los controles empleando el administrador de diseños Pack utilizamos el método 'pack' que cuenta con una serie de parámetros.

El boton1 se ubica en la parte superior de la ventana ya que en el parámetro side hemos pasado la contante tk.TOP:

        self.boton1=ttk.Button(self.ventana1, text="Boton 1")
        self.boton1.pack(side=tk.TOP, fill=tk.BOTH)

Si no especificamos el parámetro fill luego el botón ocupa solo el espacio necesario y no se expande:

        self.boton1=ttk.Button(self.ventana1, text="Boton 1")
        self.boton1.pack(side=tk.TOP)
Pack

El segundo y tercer botón se ubican debajo del primero porque también en el parámetro side hemos pasado el valor 'tk.TOP':

        self.boton2=ttk.Button(self.ventana1, text="Boton 2")
        self.boton2.pack(side=tk.TOP, fill=tk.BOTH)
        self.boton3=ttk.Button(self.ventana1, text="Boton 3")
        self.boton3.pack(side=tk.TOP, fill=tk.BOTH)

El cuarto botón hemos indicado en el parámetro side el valor 'tk.LEFT' y los siguientes botones el valor 'tk.RIGHT':

        self.boton4=ttk.Button(self.ventana1, text="Boton 4")
        self.boton4.pack(side=tk.LEFT)
        self.boton5=ttk.Button(self.ventana1, text="Boton 5")
        self.boton5.pack(side=tk.RIGHT)
        self.boton6=ttk.Button(self.ventana1, text="Boton 6")
        self.boton6.pack(side=tk.RIGHT)
        self.boton7=ttk.Button(self.ventana1, text="Boton 7")
        self.boton7.pack(side=tk.RIGHT)

Si agrandamos la ventana podemos ver que el botón fijado a izquierda siempre permanece en dicho lugar y los fijados a derecha se desplazan:

Pack

El parámetro side puede recibir alguno de estos cuatro valores:

  • tk.TOP
  • tk.LEFT
  • tk.RIGHT
  • tk.BOTTOM

Podemos pasar los parámetros padx y pady para dejar espacio entre los controles visuales y el borde del contenedor:

Pack

Para lograr ese resultado debemos hacer los siguientes cambios:

import tkinter as tk
from tkinter import ttk

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        self.boton1=ttk.Button(self.ventana1, text="Boton 1")
        self.boton1.pack(side=tk.TOP, fill=tk.BOTH, padx=5, pady=5)
        self.boton2=ttk.Button(self.ventana1, text="Boton 2")
        self.boton2.pack(side=tk.TOP, fill=tk.BOTH, padx=15, pady=15)
        self.boton3=ttk.Button(self.ventana1, text="Boton 3")
        self.boton3.pack(side=tk.TOP, fill=tk.BOTH, padx=25, pady=25)
        self.boton4=ttk.Button(self.ventana1, text="Boton 4")
        self.boton4.pack(side=tk.LEFT)
        self.boton5=ttk.Button(self.ventana1, text="Boton 5")
        self.boton5.pack(side=tk.RIGHT, padx=10)
        self.boton6=ttk.Button(self.ventana1, text="Boton 6")
        self.boton6.pack(side=tk.RIGHT)
        self.boton7=ttk.Button(self.ventana1, text="Boton 7")
        self.boton7.pack(side=tk.RIGHT)
        self.ventana1.mainloop()

aplicacion1=Aplicacion()

Layout Manager: Grid

Este tipo de Layout Manager lo hemos estado utilizando en muchos conceptos anteriores, veremos otras posibilidades que nos suministra.

Este tipo de Layout define una tabla con columnas y filas, cada vez que agregamos un Widget indicamos en que columna y fila se debe ubicar:

Pack

Un tema que no vimos es que podemos expandir celdas de la tabla para que se ocupen más de una columna o más de una fila.

Problema:

Disponer una serie de botones utilizando el Layout Manager de tipo Grid.

La representación visual debe ser:

Pack

Programa: ejercicio247.py

Ver video

import tkinter as tk
from tkinter import ttk

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        self.boton1=ttk.Button(self.ventana1, text="Boton 1")
        self.boton1.grid(column=0, row=0)
        self.boton2=ttk.Button(self.ventana1, text="Boton 2")
        self.boton2.grid(column=1, row=0)
        self.boton3=ttk.Button(self.ventana1, text="Boton 3")
        self.boton3.grid(column=2, row=0, rowspan=2, sticky="ns")
        self.boton4=ttk.Button(self.ventana1, text="Boton 4")
        self.boton4.grid(column=0, row=1)
        self.boton5=ttk.Button(self.ventana1, text="Boton 5")
        self.boton5.grid(column=1, row=1)
        self.boton6=ttk.Button(self.ventana1, text="Boton 6")
        self.boton6.grid(column=0, row=2, columnspan=3, sticky="we")
        self.ventana1.mainloop()

aplicacion1=Aplicacion()

Los dos primeros botones como ocupan una celda única no varía con lo que hemos visto hasta ahora:

        self.boton1=ttk.Button(self.ventana1, text="Boton 1")
        self.boton1.grid(column=0, row=0)
        self.boton2=ttk.Button(self.ventana1, text="Boton 2")
        self.boton2.grid(column=1, row=0)

El tercer botón si lo expandimos para que ocupe dos filas:

        self.boton3=ttk.Button(self.ventana1, text="Boton 3")
        self.boton3.grid(column=2, row=0, rowspan=2, sticky="ns")

En la propiedad sticky (se traduce como 'pegajoso') pedimos que el botón se expanda de north (norte) a south (sur), si no disponemos sticky luego el botón ocupa las dos celdas pero aparece centrado.

El cuarto y quinto botón ocupan celdas individuales:

        self.boton4=ttk.Button(self.ventana1, text="Boton 4")
        self.boton4.grid(column=0, row=1)
        self.boton5=ttk.Button(self.ventana1, text="Boton 5")
        self.boton5.grid(column=1, row=1)

Finalmente el último botón se expande a tres columnas:

        self.boton6=ttk.Button(self.ventana1, text="Boton 6")
        self.boton6.grid(column=0, row=2, columnspan=3, sticky="we")

En el parámetro sticky indicamos que se estire de west (oeste) a east (este)

Layout Manager: Place

Este tipo de Layout Manager nos permite disponer un Widget en una posición y con un tamaño con valor absoluto a nivel de píxeles. Hay que tener cuidado en que casos utilizar este tipo de administrador de diseños ya que si agrandamos o reducimos el tamaño de la ventana puede ser que los controles queden fuera de la ventana y el operador no pueda visualizarlos.

Problema:

Disponer dos botones en la parte inferior derecha de la ventana utilizando el Layout Manager de tipo Place. El ancho y alto de la ventana debe ser de 800 por 600 píxeles.

La representación visual debe ser:

Place

Programa: ejercicio248.py

Ver video

import tkinter as tk
from tkinter import ttk

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        self.ventana1.geometry("800x600")
        self.ventana1.resizable(0,0)
        self.boton1=ttk.Button(self.ventana1, text="Confirmar")
        self.boton1.place(x=680, y=550, width=90, height=30)
        self.boton2=ttk.Button(self.ventana1, text="Cancelar")
        self.boton2.place(x=580, y=550, width=90, height=30)
        self.ventana1.mainloop()

aplicacion1=Aplicacion()

Creamos una ventana de 800x600 píxeles:

        self.ventana1.geometry("800x600")

No permitimos que el operador la redimensione con el mouse (esto debido a que si reduce el tamaño los botones quedarán fuera de la ventana):

        self.ventana1.resizable(0,0)

Al botón que tiene la etiqueta "Confirmar" lo ubicamos en la columna 680 (indicado en píxeles) y la fila 550, también definimos el ancho y alto:

        self.boton1=ttk.Button(self.ventana1, text="Confirmar")
        self.boton1.place(x=680, y=550, width=90, height=30)

La ubicación del segundo botón queda con los siguientes valores:

        self.boton2=ttk.Button(self.ventana1, text="Cancelar")
        self.boton2.place(x=580, y=550, width=90, height=30)

Podemos utilizar el método 'place' para ubicar cualquiera de los controles visuales que hemos visto hasta ahora: Button, Label, Entry, etc.

Recomendación

Siempre que tenga que implementar una interfaz gráfica es conveniente que en un papel se haga un croquis y a partir de este agrupar controles relacionados dentro de Frame o LabelFrame.