Trabajando con imágenes en Tkinter: logos, fondos y botones
Aprende a cargar imágenes en tus interfaces gráficas con Tkinter, descubre las limitaciones de PhotoImage, cómo superarlas con Pillow (PIL) y evita el temido error de que la imagen desaparezca.
1. ¿Qué formatos soporta PhotoImage por defecto?
El widget PhotoImage incorporado en Tkinter está limitado a los siguientes formatos sin dependencias externas:
- GIF (animado y estático)
- PPM/PGM (formato de mapa de bits propio de Tk)
- PNG (desde Python 3.4, pero solo versiones sin transparencia avanzada)
Si necesitas trabajar con JPG, BMP, TIFF o PNG con canales alfa complejos, deberás recurrir a Pillow.
2. Pillow (PIL) como puente para formatos modernos
Pillow es una bifurcación mantenida de la antigua biblioteca PIL. Con ella puedes cargar prácticamente cualquier formato de imagen y convertirlo a un objeto PhotoImage que Tkinter entienda.
# Instalación
pip install pillow
# Conversión de una imagen JPG a PhotoImage
from PIL import Image, ImageTk
pil_img = Image.open('foto.jpg') # Carga JPG, PNG, BMP, etc.
tk_img = ImageTk.PhotoImage(pil_img) # Convierte a PhotoImage usable por Tkinter
Ventajas de usar Pillow:
- Soporte nativo para JPG, PNG (con alfa), BMP, ICO, WebP, TIFF.
- Herramientas de resize, crop, rotate y filter integradas.
- Compatibilidad con Python 3.8‑3.12 y todas las plataformas principales.
3. Añadir imágenes a los widgets de Tkinter
A continuación, ejemplos funcionales para Label, Button y Canvas. Cada bloque muestra dos versiones: usando solo PhotoImage (para GIF/PNG) y usando Pillow (para JPG/PNG avanzado).
3.1. Label con PhotoImage
import tkinter as tk
root = tk.Tk()
logo = tk.PhotoImage(file='logo.gif') # GIF o PNG simple
lbl = tk.Label(root, image=logo)
lbl.pack()
root.mainloop()
Nota: Mantén la referencia logo fuera del ámbito del widget.
3.2. Label con Pillow (JPG/PNG con alfa)
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
pil_img = Image.open('foto.jpg')
photo = ImageTk.PhotoImage(pil_img)
lbl = tk.Label(root, image=photo)
lbl.pack()
root.mainloop()
3.3. Button con PhotoImage
import tkinter as tk
root = tk.Tk()
icon = tk.PhotoImage(file='icon.png')
btn = tk.Button(root, image=icon, command=lambda: print('¡clic!'))
btn.pack(pady=10)
root.mainloop()
3.4. Button con Pillow (PNG con transparencia)
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
pil_icon = Image.open('icon_transparent.png')
icon = ImageTk.PhotoImage(pil_icon)
btn = tk.Button(root, image=icon, command=lambda: print('¡clic!'))
btn.pack(pady=10)
root.mainloop()
3.5. Canvas con PhotoImage (fondo y sprites)
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300)
canvas.pack()
bg = tk.PhotoImage(file='fondo.png')
sprite = tk.PhotoImage(file='sprite.gif')
canvas.create_image(0, 0, anchor='nw', image=bg) # Fondo
canvas.create_image(200, 150, image=sprite) # Sprite central
root.mainloop()
Si el fondo es JPG, usa Pillow para cargarlo y conviértelo a PhotoImage antes de pasar a create_image.
4. El temido problema de la desaparición de la imagen
Tkinter gestiona la vida de los objetos PhotoImage mediante referencia de Python. Si la única referencia está dentro del widget y no se guarda en una variable, el recolector de basura los elimina y la GUI queda sin la imagen.
# Código erróneo (la imagen desaparece al actualizar la ventana)
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, image=tk.PhotoImage(file='icon.png'))
btn.pack()
root.mainloop()
Solución: asigna la imagen a una variable de alcance global o como atributo del widget.
# Solución correcta
import tkinter as tk
root = tk.Tk()
icon = tk.PhotoImage(file='icon.png')
btn = tk.Button(root, image=icon)
btn.image = icon # atributo para mantener referencia
btn.pack()
root.mainloop()
En aplicaciones más grandes, es una buena práctica almacenar todas las imágenes en un diccionario centralizado (self.images = {}) dentro de la clase principal.
5. Buenas prácticas y trucos avanzados
- Escalado inteligente: usa
Image.thumbnail()oImage.resize()antes de crear elPhotoImagepara reducir consumo de memoria. - Gestión de recursos: cierra los archivos con
pil_img.close()cuando ya no los necesites. - Cache de imágenes: en interfaces con muchos iconos, crea una caché (p.ej.,
self.icon_cache = {}) para evitar cargar el mismo archivo repetidamente. - Compatibilidad multiplataforma: verifica que los caminos de archivo usen
os.path.joinyPathlibpara evitar problemas en Windows vs Linux. - Rendimiento: en animaciones, pre‑carga todos los fotogramas como objetos
PhotoImagey actualiza el widget conconfig(image=frame)en unafter()loop.
15 Trabajando con imágenes en Tkinter: logos, fondos y botones