Saltar al contenido
🐍 Curso de Python
/

Módulo 3 · Lección 10 de 50

El gestor place

Colocar por coordenadas exactas o proporcionales, por qué casi nunca conviene, y los tres casos en los que es la única respuesta.

⏱ 14 min de lectura

El tercer gestor es el que menos vas a usar, y esta lección es más corta por eso. Pero conviene conocerlo por dos motivos: resuelve un par de cosas que los otros dos no pueden hacer de ninguna manera, y es el que eligen por instinto quienes vienen de otras herramientas, así que verás código ajeno lleno de place mal usado.

place coloca cada widget en unas coordenadas que tú das, con un tamaño que tú das. Suena a lo más cómodo del mundo y es justo lo contrario: es el que más trabajo da en cuanto la ventana cambia de tamaño, cambia de fuente o cambia de sistema operativo.

Coordenadas absolutas#

La forma más directa: píxeles contados desde la esquina superior izquierda del contenedor.

python
etiqueta.place(x=10, y=20)

Sin más opciones, el widget se queda de su tamaño natural con su esquina superior izquierda en ese punto. Se puede comprobar:

consola
>>> etiqueta.place(x=10, y=20)
>>> etiqueta.place_info()
{'in': <tkinter.Tk object .>, 'x': '10', 'relx': '0', 'y': '20', 'rely': '0',
 'width': '', 'relwidth': '', 'height': '', 'relheight': '',
 'anchor': 'nw', 'bordermode': 'inside'}

Dos cosas de esa salida merecen comentario. Que el anclaje por defecto es nw, la esquina superior izquierda, a diferencia de pack y grid que centran. Y que los valores vienen como cadenas, no como números, que es una de esas asperezas que delatan que por debajo hay un intérprete de Tcl donde todo es texto. Si necesitas operar con ellos, hay que convertirlos.

Coordenadas relativas#

Aquí está lo que place aporta de verdad. Las opciones relx y rely reciben una proporción de 0.0 a 1.0 del tamaño del contenedor:

ValorSignifica
relx=0.0Borde izquierdo
relx=0.5Mitad del ancho
relx=1.0Borde derecho

Como son proporciones, sobreviven al redimensionado. Un widget con relx=0.5 se queda en la mitad de la ventana mida lo que mida.

Combinado con anchor, eso da el centrado exacto en una línea:

python
etiqueta.place(relx=0.5, rely=0.5, anchor="center")

Sin el anchor, lo que quedaría en la mitad sería la esquina superior izquierda del widget, no su centro, y el resultado se vería descolocado hacia abajo y a la derecha.

El tamaño#

place es el único gestor que también decide el tamaño del widget, y con la misma pareja de formas:

OpciónQué hace
width, heightTamaño en píxeles
relwidth, relheightProporción del tamaño del contenedor
consola
>>> etiqueta.place(relx=0.5, rely=0.5, anchor="center", relwidth=0.4)
>>> etiqueta.winfo_width(), ventana.winfo_width()
(200, 500)

Cuatro décimas de quinientos son doscientos, y así se mantiene al redimensionar. Como con las coordenadas, las dos formas se suman: relwidth=1.0, width=-24 es "todo el ancho menos veinticuatro píxeles", que es la forma de dejar un margen constante a los dos lados.

Fíjate en que aquí width sí son píxeles, aunque el widget sea un Label. Es una excepción a la tabla de unidades de la lección 7: la opción width del widget mide en caracteres, pero la opción width de place mide en píxeles, porque es del gestor y no del widget.

place no cuenta para el tamaño del contenedor#

Esta es la diferencia de fondo con los otros dos, y la razón por la que no se puede usar como gestor general.

pack y grid calculan cuánto necesita el contenedor a partir de lo que hay dentro, y por eso una ventana se ajusta sola al tamaño de su contenido. place no hace eso: coloca el widget donde le digas y no informa a nadie.

Las consecuencias son concretas:

  • Un contenedor cuyos hijos están todos con place calcula que no necesita ningún tamaño. Si no le fijas uno, se queda en un píxel.
  • Un widget colocado en unas coordenadas que caen fuera del contenedor no se ve, y no hay ningún aviso.
  • Si la ventana se encoge por debajo de lo que ocupan tus coordenadas, los widgets se salen y desaparecen por los bordes.

Por eso un programa entero hecho con place se ve perfecto en el equipo de quien lo escribió y se rompe en cualquier otro, en cuanto la fuente del sistema es un punto más grande o la pantalla tiene otra densidad.

Cuándo sí#

place tiene una capacidad que los otros dos no tienen en absoluto: superponer widgets. pack y grid reparten el espacio y nunca solapan nada. place no reparte, así que puede poner una cosa encima de otra.

De ahí salen sus usos legítimos.

Superponer algo sobre otro widget. Un contador encima de un icono, una etiqueta de "Nuevo" sobre una tarjeta, un botón de cerrar en la esquina de un panel, un mensaje de "cargando" tapando el contenido mientras llegan los datos.

python
insignia.place(in_=icono, relx=1.0, rely=0.0, anchor="ne", x=4, y=-4)

Ese in_ coloca el widget dentro de otro widget que no es su padre, y con place es donde de verdad se usa.

Fondos y marcas de agua. Una imagen que ocupa toda la ventana por detrás del resto, con relwidth=1.0, relheight=1.0, y los demás widgets encima.

Pantallas de presentación y diálogos centrados a mano, donde lo único que se quiere es una cosa en el centro exacto y no hay nada más que colocar.

superpuesto.py
"""El uso legítimo de place: una insignia encima de otro widget."""

import tkinter as tk

ventana = tk.Tk()
ventana.title("Superposición")
ventana.geometry("360x220")

# La estructura, con un gestor normal.
tarjeta = tk.Frame(ventana, bg="#F2F2F7", width=200, height=120)
tarjeta.pack(expand=True)
tarjeta.pack_propagate(False)

tk.Label(tarjeta, text="Mensajes", bg="#F2F2F7",
         font=("Helvetica", 14)).place(relx=0.5, rely=0.5, anchor="center")

# La insignia, superpuesta en la esquina. Esto solo lo puede hacer place.
insignia = tk.Label(tarjeta, text=" 7 ", bg="#D32F2F", fg="white",
                    font=("Helvetica", 10, "bold"))
insignia.place(relx=1.0, rely=0.0, anchor="ne", x=-6, y=6)

ventana.mainloop()

En pantalla hay una tarjeta gris centrada, con su texto en medio y un círculo rojo con el número siete pegado a su esquina superior derecha, seis píxeles hacia dentro por los dos lados. Esa insignia se queda en su sitio al redimensionar, porque su posición es relativa a la tarjeta y su margen es absoluto.

Ese pack_propagate(False) de la tercera línea es necesario y conviene entenderlo: por defecto, un contenedor encoge hasta el tamaño de su contenido, ignorando el width y el height que le hayas puesto. Desactivar la propagación es lo que hace que respete el tamaño pedido. Tiene su equivalente grid_propagate(False), y es un recurso que hace falta de vez en cuando con marcos que deben medir algo fijo.

Consultar y deshacer#

Los mismos tres métodos que los otros gestores:

MétodoQué hace
widget.place_info()Devuelve la colocación, con los valores como cadenas
contenedor.place_slaves()Lista los widgets colocados con place
widget.place_forget()Lo quita sin destruirlo

Y el mismo control de errores que los demás, con la lista de opciones válidas en el mensaje:

consola
>>> etiqueta.place(posx=3)
_tkinter.TclError: unknown option "-posx"