Dibujando en la pantalla: el widget Canvas de Tkinter
¿Qué es Canvas y para qué sirve?
El Canvas es el widget gráfico más flexible de Tkinter. Proporciona una zona de dibujo basada en coordenadas donde puedes crear:
- Líneas y polígonos (ideal para juegos simples como "Snake" o "Tetris").
- Formas geométricas (rectángulos, óvalos, arcos) para diagramas o esquemas de flujo.
- Texto estilizado, useful en visualizaciones de datos o etiquetado dinámico.
- Imágenes raster (PNG, GIF, JPEG) para editores gráficos o presentaciones interactivas.
En resumen, Canvas es la herramienta de referencia cuando necesitas dibujar de forma programática dentro de una aplicación de escritorio Python.
Sistema de coordenadas
El origen (0,0) está en la esquina superior‑izquierda del widget. El eje x crece hacia la derecha y el eje y hacia abajo. Cada unidad corresponde a un píxel en la pantalla, aunque puedes aplicar scale y translate para cambiar la vista.
Ejemplo rápido:
canvas.create_line(0, 0, 100, 100) # Diagonal de la esquina superior‑izquierda a (100,100)
Dibujando los elementos básicos
Todos los métodos de dibujo devuelven un identificador (id) que permite modificar o eliminar el objeto posteriormente.
Líneas y polígonos
# Línea simple
line_id = canvas.create_line(10, 10, 200, 10, fill="#ff5733", width=3)
# Polígono (triángulo)
poly_id = canvas.create_polygon(50, 60, 150, 60, 100, 150,
outline="black", fill="#aaddff", width=2)
Rectángulos y óvalos
# Rectángulo
rect_id = canvas.create_rectangle(20, 20, 120, 80,
outline="green", fill="#ccffcc", width=2)
# Óvalo (círculo si ancho=alto)
oval_id = canvas.create_oval(150, 20, 250, 120,
outline="blue", fill="#ddeeff", width=2)
Texto
text_id = canvas.create_text(150, 150,
text="Hola Canvas!",
font=("Helvetica", 16, "bold"),
fill="#333333")
Imágenes
from tkinter import PhotoImage
img = PhotoImage(file="/ruta/a/mi_imagen.png")
image_id = canvas.create_image(200, 200, image=img, anchor="center")
# Mantén una referencia a "img" para que no sea recolectada por el GC.
Ejemplo práctico: Mini‑Paint
A continuación se muestra una aplicación mínima tipo Paint que permite al usuario dibujar libremente con el ratón.
import tkinter as tk
class MiniPaint(tk.Tk):
def __init__(self):
super().__init__()
self.title("Mini‑Paint con Tkinter Canvas")
self.geometry("800x600")
self.configure(bg="#f0f0f0")
# --- Canvas -------------------------------------------------
self.canvas = tk.Canvas(self, bg="white", cursor="cross")
self.canvas.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# --- Estado del dibujo --------------------------------------
self.last_x, self.last_y = None, None
self.color = "#000000"
self.width = 3
# --- Bindings ------------------------------------------------
self.canvas.bind("", self.on_press)
self.canvas.bind("", self.on_move)
self.canvas.bind("", self.on_release)
# --- Barra de herramientas ----------------------------------
self.create_toolbar()
def create_toolbar(self):
toolbar = tk.Frame(self, bg="#e0e0e0")
toolbar.pack(fill=tk.X, side=tk.TOP)
# Color picker
color_btn = tk.Button(toolbar, text="Color", command=self.choose_color)
color_btn.pack(side=tk.LEFT, padx=5, pady=5)
# Clear button
clear_btn = tk.Button(toolbar, text="Limpiar", command=self.clear_canvas)
clear_btn.pack(side=tk.LEFT, padx=5, pady=5)
# Brush size selector
size_label = tk.Label(toolbar, text="Grosor:")
size_label.pack(side=tk.LEFT, padx=(20, 5))
self.size_var = tk.IntVar(value=self.width)
size_spin = tk.Spinbox(toolbar, from_=1, to=20, textvariable=self.size_var,
command=self.update_width, width=5)
size_spin.pack(side=tk.LEFT)
def choose_color(self):
from tkinter import colorchooser
colour = colorchooser.askcolor(initialcolor=self.color)[1]
if colour:
self.color = colour
def clear_canvas(self):
self.canvas.delete("all")
def update_width(self):
self.width = self.size_var.get()
def on_press(self, event):
self.last_x, self.last_y = event.x, event.y
def on_move(self, event):
x, y = event.x, event.y
# Dibuja una línea desde la última posición a la actual
self.canvas.create_line(self.last_x, self.last_y, x, y,
fill=self.color, width=self.width,
capstyle=tk.ROUND, smooth=True)
self.last_x, self.last_y = x, y
def on_release(self, event):
self.last_x, self.last_y = None, None
if __name__ == "__main__":
app = MiniPaint()
app.mainloop()
Características destacadas:
- Soporte para cambiar color y grosor del pincel en tiempo real.
- Uso de
smooth=Truepara líneas más fluidas. - Botón de "Limpiar" que elimina todo el contenido del canvas.
Comparativa rápida: Canvas vs alternativas en Python
Ventajas de Tkinter Canvas
- Incluido en la biblioteca estándar – sin dependencias externas.
- Ligero y suficiente para aplicaciones de escritorio simples.
- Integración nativa con eventos de Tkinter (mouse, teclado).
Cuándo considerar otras opciones
- PyQt / PySide (QGraphicsView): Necesario para UI más complejas y renderizado OpenGL.
- Pillow (PIL) +
PhotoImage: Ideal para procesamiento de imágenes fuera de la pantalla. - Kivy: Mejor para aplicaciones multitáctiles y móviles.
Buenas prácticas y troubleshooting
Persistencia de objetos gráficos
Guarda siempre el id devuelto por los métodos de dibujo si necesitas modificar el elemento (cambiar color, mover, eliminar).
Gestión de imágenes
Las instancias PhotoImage deben mantenerse en una variable de alcance global o de instancia; de lo contrario, Tkinter las elimina y el canvas aparecerá vacío.
Rendimiento
- Para más de 10.000 objetos, el canvas puede volverse lento. Usa
canvas.delete('all')para limpiar o agrupa objetos entagsy manipúlalos en bloque. - Si necesitas animaciones a 60 fps, considera
canvas.after()en combinación conmove()ycoords()en lugar de crear nuevos objetos cada frame.
Seguridad
Al cargar imágenes externas, verifica la extensión y el tamaño para evitar consumo excesivo de memoria o ataques de descompresión ("zip bomb").