Respuesta rápida (controles del Etch A Sketch)
- ↑ ↓ ← → = mover y dibujar (también vale para diagonal si mantienes dos teclas)
- Shift (mantener) = “turbo” (más velocidad)
- Espacio = pluma arriba/abajo (dibujar sin pintar)
- C = limpiar pantalla (borrar)
- U = deshacer último trazo
- S = guardar dibujo (EPS)
- + / - = grosor del trazo
- 1…5 = color rápido · R = color aleatorio
- H = activar/desactivar modo difícil · L = subir nivel
- Esc = salir
Más:
Atajos de teclado ·
Ctrl + S (guardar) ·
Ctrl + Z (deshacer) ·
Teclas WASD ·
Teclas de edición
Qué vas a construir (y por qué mola)
Un Etch A Sketch tiene dos “mandos” (horizontal/vertical). Lo imitamos con el teclado: las flechas cambian la posición X/Y y la tortuga va dejando rastro. Lo divertido viene cuando le metes:
- Control fino con “KeyPress/KeyRelease” (no depende del autorepeat del teclado).
- Borrar / deshacer sin reiniciar el programa.
- Guardar el dibujo a archivo.
- Modo difícil: jitter, límites y niveles para convertirlo en mini-juego.
Requisitos
- Python 3 (Turtle viene en la librería estándar).
- Windows / macOS / Linux (en Mac, a veces necesitas hacer click en la ventana para darle foco al teclado).
Eventos vs binds: por qué usamos Tkinter bind
Turtle tiene onkey() y onkeypress(), que van genial para atajos “sueltos” (pulsar una vez). Pero si quieres movimiento suave, lo ideal es saber cuándo una tecla se mantiene y cuándo se suelta. Para eso, Turtle está montado sobre Tkinter, y podemos hacer:
screen.getcanvas().bind("<KeyPress>", ...)screen.getcanvas().bind("<KeyRelease>", ...)
Así creamos un “loop” con ontimer() que mueve la tortuga a 60 FPS aprox. Resultado: se siente como un juguete de verdad.
Código completo: Etch A Sketch con Python Turtle + teclado (binds)
import turtle
import random
import os
from datetime import datetime
WIDTH, HEIGHT = 900, 600
MARGIN = 18 # margen para no pegarse al borde
class EtchASketch:
def __init__(self):
# --- pantalla ---
self.screen = turtle.Screen()
self.screen.setup(WIDTH, HEIGHT)
self.screen.title("Etch A Sketch (Python Turtle) — Teclados Chulos")
self.screen.bgcolor("white")
self.screen.tracer(0) # render manual (suave)
# --- tortuga que dibuja ---
self.pen = turtle.Turtle(visible=False)
self.pen.speed(0)
self.pen.pensize(3)
self.pen.color("black")
self.pen.penup()
self.pen.goto(0, 0)
self.pen.pendown()
# --- HUD (mensajes) ---
self.hud = turtle.Turtle(visible=False)
self.hud.penup()
self.hud.color("#111")
self.hud.goto(-WIDTH // 2 + 12, HEIGHT // 2 - 36)
# --- estado ---
self.base_step = 6
self.step = self.base_step
self.fast_mult = 3
self.level = 1
self.hard_mode = False
self.jitter = 0 # “tembleque” en modo difícil
self.is_pendown = True
self.keys = {} # keysym -> bool
# --- binds (Tkinter) ---
canvas = self.screen.getcanvas()
canvas.focus_set()
canvas.bind("<KeyPress>", self._on_key_press)
canvas.bind("<KeyRelease>", self._on_key_release)
# --- atajos “de una pulsación” (Turtle) ---
self.screen.onkey(self.toggle_pen, "space")
self.screen.onkey(self.clear, "c")
self.screen.onkey(self.reset, "x")
self.screen.onkey(self.undo, "u")
self.screen.onkey(self.save, "s")
self.screen.onkey(self.toggle_hard_mode, "h")
self.screen.onkey(self.next_level, "l")
self.screen.onkey(self.quit, "Escape")
self.screen.listen()
self._msg("Listo: flechas para dibujar · C borrar · S guardar · H modo difícil")
self._tick()
turtle.done()
# ---------------------------
# Input
# ---------------------------
def _on_key_press(self, event):
k = event.keysym
self.keys[k] = True
# grosor (+ / -) en varios teclados
if k in ("plus", "equal", "KP_Add"):
self.bigger()
elif k in ("minus", "underscore", "KP_Subtract"):
self.smaller()
# colores rápidos
if k in ("1", "2", "3", "4", "5"):
self.set_color(int(k))
elif k in ("r", "R"):
self.random_color()
def _on_key_release(self, event):
self.keys[event.keysym] = False
# ---------------------------
# Loop de movimiento (60 FPS aprox.)
# ---------------------------
def _tick(self):
mult = self.fast_mult if (self.keys.get("Shift_L") or self.keys.get("Shift_R")) else 1
dx = 0
dy = 0
if self.keys.get("Up"):
dy += self.step * mult
if self.keys.get("Down"):
dy -= self.step * mult
if self.keys.get("Right"):
dx += self.step * mult
if self.keys.get("Left"):
dx -= self.step * mult
# modo difícil: jitter y “fricción rara”
if self.hard_mode and (dx or dy):
dx += random.randint(-self.jitter, self.jitter)
dy += random.randint(-self.jitter, self.jitter)
if dx or dy:
x, y = self.pen.position()
nx = x + dx
ny = y + dy
# límites (clamp)
half_w = WIDTH / 2 - MARGIN
half_h = HEIGHT / 2 - MARGIN
nx = max(-half_w, min(half_w, nx))
ny = max(-half_h, min(half_h, ny))
self.pen.goto(nx, ny)
self.screen.update()
self.screen.ontimer(self._tick, 16) # ~60 fps
# ---------------------------
# Acciones
# ---------------------------
def toggle_pen(self):
self.is_pendown = not self.is_pendown
if self.is_pendown:
self.pen.pendown()
self._msg("Pluma: ABAJO (dibujando)")
else:
self.pen.penup()
self._msg("Pluma: ARRIBA (mover sin dibujar)")
def clear(self):
# borra sin mover la tortuga
self.pen.clear()
self._msg("Borrado: pantalla limpia (C)")
def reset(self):
# reset tipo “sacudida” (borra y vuelve al centro)
self.pen.clear()
self.pen.penup()
self.pen.goto(0, 0)
self.pen.pendown() if self.is_pendown else self.pen.penup()
self._msg("Reset: centro + limpio (X)")
def undo(self):
# deshace “algo” del historial (depende de la complejidad del trazo)
try:
self.pen.undo()
self._msg("Undo: deshacer (U)")
except turtle.TurtleGraphicsError:
self._msg("Undo: nada que deshacer")
def bigger(self):
size = min(20, self.pen.pensize() + 1)
self.pen.pensize(size)
self._msg(f"Grosor: {size}")
def smaller(self):
size = max(1, self.pen.pensize() - 1)
self.pen.pensize(size)
self._msg(f"Grosor: {size}")
def set_color(self, n):
palette = {
1: "black",
2: "red",
3: "blue",
4: "green",
5: "purple",
}
self.pen.color(palette.get(n, "black"))
self._msg(f"Color: {palette.get(n, 'black')}")
def random_color(self):
self.pen.color(random.random(), random.random(), random.random())
self._msg("Color: aleatorio (R)")
def save(self):
# Guarda como .eps (vector). Luego puedes convertir a PNG si quieres.
name = self.screen.textinput("Guardar dibujo", "Nombre de archivo (sin extensión):")
if not name:
self._msg("Guardar: cancelado")
return
safe = "".join(ch for ch in name.strip().replace(" ", "_") if ch.isalnum() or ch in ("_", "-"))
if not safe:
safe = datetime.now().strftime("etch_%Y%m%d_%H%M%S")
filename = f"{safe}.eps"
canvas = self.screen.getcanvas()
canvas.postscript(file=filename, colormode="color")
self._msg(f"Guardado: {filename} (EPS)")
def toggle_hard_mode(self):
self.hard_mode = not self.hard_mode
if self.hard_mode:
self.jitter = 1 + self.level # temblor depende del nivel
self._msg(f"Modo difícil: ON (jitter {self.jitter})")
else:
self.jitter = 0
self._msg("Modo difícil: OFF")
def next_level(self):
self.level = 1 if self.level >= 5 else self.level + 1
self.step = self.base_step + (self.level - 1) * 2
if self.hard_mode:
self.jitter = 1 + self.level
self._msg(f"Nivel: {self.level} (velocidad base {self.step})")
def quit(self):
self._msg("Saliendo…")
try:
self.screen.bye()
except turtle.Terminator:
pass
# ---------------------------
# HUD
# ---------------------------
def _msg(self, text, ms=1400):
self.hud.clear()
self.hud.write(text, font=("Arial", 12, "normal"))
self.screen.ontimer(self.hud.clear, ms)
if __name__ == "__main__":
EtchASketch()
Cómo usarlo (paso a paso)
- Copia el código en un archivo:
etch_a_sketch.py - Ejecuta:
python etch_a_sketch.py - Haz click dentro de la ventana si no te coge el teclado (foco).
- Dibuja con flechas, borra con C, guarda con S, y prueba el H 😈
Borrar, deshacer y guardar: “lo básico” de cualquier maker-tool
- Borrar (C) limpia la pantalla pero no te mueve del sitio.
- Reset (X) limpia y vuelve al centro (como sacudir el Etch A Sketch).
- Deshacer (U) usa
undo(). Ojo: Turtle deshace acciones del historial; en trazos largos puede sentirse “por trozos”. - Guardar (S) exporta a .EPS (vector). Si quieres PNG/JPG, lo típico es convertirlo luego con una herramienta externa (o un script con Pillow, si te pica el gusanillo).
Sube la dificultad (modo “post maker”)
El modo difícil (H) mete “tembleque” y el nivel (L) sube la velocidad. Pero si quieres que esto sea un mini-juego de verdad, aquí van ideas evergreen:
- Ink limit: tienes “tinta” (por ejemplo 30 segundos). Si se acaba, solo puedes mover con pluma arriba.
- Zona prohibida: dibuja un rectángulo “lava” y si lo cruzas, pierde (reset).
- Objetivo: genera una forma fantasma (círculo/cuadrado) y reta a calcarla.
- Tiempo: “dibuja una casa en 20 segundos” y guarda el resultado.
- Modo espejo: cada movimiento se duplica simétrico (arte generativo instantáneo).
Trucos PRO (cuando te pica optimizar)
- WASD: si prefieres estilo gamer, puedes mapear WASD a lo mismo que las flechas (y dejar flechas para ajustar grosor/color).
- Snap: que el trazo vaya en “grid” (como píxeles gordos) para arte retro.
- Paletas: guarda 8 colores y cambia con 1…8.
Preguntas frecuentes
¿Por qué Turtle no detecta mis teclas?
Casi siempre es foco: haz click dentro de la ventana. En este script forzamos focus_set(), pero algunos sistemas igual requieren ese click inicial.
¿Qué diferencia hay entre onkey y los binds?
onkey/onkeypress van genial para “pulsar una vez”. Con bind(KeyPress/KeyRelease) controlas mantener/soltar, y eso hace que el movimiento sea suave (tipo joystick).
¿Se puede guardar en PNG en vez de EPS?
Sí, pero EPS es lo más directo con Tkinter. Para PNG suele hacerse conversión (por ejemplo con herramientas externas o librerías). Si quieres, puedes quedarte con EPS porque es vector (calidad top).
¿Cómo cambio el tamaño del lienzo?
Arriba tienes WIDTH y HEIGHT. Ajusta esos valores y listo.
¿Cómo hago que las flechas dibujen más “lento”?
Baja base_step (por ejemplo 4) o reduce el multiplicador de Shift (fast_mult).
¿Puedo usar esto para enseñar eventos en clase?
Sí: es perfecto para explicar “estado” (teclas pulsadas), bucles con ontimer(), y cómo un programa reacciona a eventos del usuario.
También te puede interesar