sábado, 26 de noviembre de 2016

Capítulo 85: Moviendo una figura en Python


Diario de un programador. Día 187

Moviendo una figura en Python.

Es el momento de ver cómo mover una figura en Python. Esto es algo que ya vimos en su oportunidad en Small Basic, así que lo implementaremos aquí. Tk no es muy potente para mover gráficos o realizar animaciones, por lo que aquí se verá será algo sencillo (Ya llegaremos a algo más complejo con otra herramienta) que servirá para tener una idea en cómo funciona esto de las animaciones.

En el primer ejemplo, se mostrará cómo crear una esfera que se desplaza por la ventana. Se empezará por crear la esfera y luego se procederá a moverla. El siguiente código sirve para mostrar una esfera en pantalla.

from tkinter import *
ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()

lienzo.create_oval(10,10,50,50, fill = "blue")


Ahora lo siguiente es agregar el código que permita que la figura se mueva. El siguiente código es el encargado de realizar esa tarea.

import time
from tkinter import *
ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

for x in range(0,60):
    lienzo.move(1,5,0)
    ventana.update()
    time.sleep(0.05)

Como se puede ver, se ha agregado en la primera línea una instrucción "import time", la cual permite utilizar la función sleep, con la cual logramos controlar la velocidad en que se ejecuta el programa. Para mover la figura, se utilizó una instrucción "for", la cual genera un ciclo que va desde 0 a 59. La siguiente línea utiliza la función "move". Esta función requiere de tres parámetros. El primero se refiere al identificador de la figura. Cuando se crea una figura, esta adquiere el identificador 1, si se crea otra adquiere el identificador 2 y así sucesivamente. El siguiente parámetro se refiere al desplazamiento "x" de la figura y el tercer parámetro se refiere al desplazamiento "y" de la figura. En este caso, la función lienzo.move(1,5,0) quiere decir "mover la figura con identificador 1, 5 pixeles a la derecha y 0 pixeles hacia abajo", como estamos dentro de un bucle "for", entonces la figura se estará desplazando constantemente 5 pixeles hacia la derecha hasta que finalice la instrucción.
La siguiente línea ventana.update(), obliga a actualizar la ventana en cada ciclo. De no utilizar esta instrucción, la ventana no se actualizaría sino hasta que finalice el bucle y no se vería el efecto del movimiento ya que la figura se desplazaría directamente hacia su posición final. La última línea time.sleep(0.05), permite que el programa se ejecute más lento, con esto se logra apreciar el movimiento. Debido a que el ciclo se ejecuta tan rápido que si no lo frenamos un poco entonces tampoco veríamos su desplazamiento. Esta función, acepta como parámetro un número que indica en cuantos milisegundos se debe retrasar  el programa.
La siguiente modificación al programa anterior hará que la figura avance y retroceda constantemente. Esto se logró mediante un ciclo while-True

import time
from tkinter import *
ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

while True:
    for x in range(0,60):
        lienzo.move(1,5,0)
        ventana.update()
        time.sleep(0.05)

    for x in range(0,60):
        lienzo.move(1,-5,0)
        ventana.update()
        time.sleep(0.05)

Lo siguiente a realizar es intentar mover la figura presionando una tecla. Para poder hacer esto, es necesario crear un evento, los cuales trabajan mediante el uso de funciones.
Entonces, vamos a modificar el primer programa, ese que hacía que la figura se moviera por sí sola hacia la derecha. Al borrarle algunas líneas el código quedaría de la siguiente manera:

from tkinter import *

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

Como se puede ver, se eliminó la parte que realizaba la animación, quedando solamente la parte correspondiente a la creación de la figura.
Como se dijo antes, para manejar el evento, es necesario crear una función. En este caso, la función se verá de la siguiente manera:

def moverEsfera(evento):
    lienzo.move(1,5,0)

La función a crear debe llevar un argumento (puede ser cualquier palabra), este argumento le sirve al módulo Tk para enviar información a la función. Para poder llamar a esta función, se utiliza la función bind_all, la cual recibe dos argumentos. El primer argumento es la tecla a presionar y el segundo parámetro corresponde a la función a ejecutar. Por ejemplo, si quisiera que al presionar la tecla "Enter" se ejecute la función "moverEsfera", la función bind_all debe quedar de la siguiente manera:

bind_all("<Keypress-Return">,moverEsfera)

Llevando lo anterior al programa, quedaría de la siguiente manera:

from tkinter import *

def moverEsfera(evento):
    lienzo.move(1,5,0)

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")
lienzo.bind_all("<KeyPress-Return>",moverEsfera)

Si todo salió bien, entonces al ejecutar el programa y presionar la tecla "Enter", la esfera debiera de desplazarse hacia la derecha.

Para hacer que la figura se desplace en otras direcciones, se pueden crear otras funciones para que cada función maneje una tecla distinta y realice una operación distinta. Por ejemplo, podemos crear cuatro funciones. Una función puede ser utilizada para mover la figura hacia arriba, otra hacia abajo, etc. y que cada función sea llamada mediante una tecla distinta. Esto podría quedar de la siguiente manera:

from tkinter import *

def derecha(evento):
    lienzo.move(1,5,0)

def izquierda(evento):
    lienzo.move(1,-5,0)

def abajo(evento):
    lienzo.move(1,0,5)

def arriba(evento):
    lienzo.move(1,0,-5)

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

lienzo.bind_all("<KeyPress-Right>",derecha)
lienzo.bind_all("<KeyPress-Left>",izquierda)
lienzo.bind_all("<KeyPress-Down>",abajo)
lienzo.bind_all("<KeyPress-Up>",arriba)   

Entonces, al ejecutar el programa, se puede apreciar como la figura puede ser controlada con las flechas direccionales del teclado.

Una alternativa a este programa lo podemos lograr mediante el uso de la propiedad "keysym", la cual permite manejar las teclas que se han pulsado.

Ejemplo:


from tkinter import *

def moverFigura(evento):
    if evento.keysym == "Right":
        lienzo.move(1,5,0)
    elif evento.keysym == "Left":
        lienzo.move(1,-5,0)
    elif evento.keysym == "Down":
        lienzo.move(1,0,5)
    elif evento.keysym == "Up":
        lienzo.move(1,0,-5)       

ventana = Tk()
lienzo = Canvas(ventana, width = 400, height = 400)
lienzo.pack()
lienzo.create_oval(10,10,50,50, fill = "blue")

lienzo.bind_all("<KeyPress-Right>",moverFigura)
lienzo.bind_all("<KeyPress-Left>",moverFigura)
lienzo.bind_all("<KeyPress-Down>",moverFigura)
lienzo.bind_all("<KeyPress-Up>",moverFigura)
   
Mediante el uso de keysym, solo se trabaja en una función, que en este caso es "moverFigura".

Antes de seguir avanzando, quisiera mencionar algunas funciones de uso común que nos harán la vida más fácil en más de una ocasión.

FUNCIONES INTERNAS

abs: La función abs retorna el valor absoluto de un numero
ejemplo:
>>> abs(-15)
15

chr: Devuelve el carácter correspondiente al argumento ascii entregado
>>> chr(65)
'A'

float: Convierte un número entero o cadena en un número flotante o decimal
ejemplo:
>>> float(12)
12.0
>>> float("15")
15.0


int: Convierte un número flotante o cadena a número entero.
ejemplo:
>>> int(14.5)
14
>>> int("10")
10

len: Cuenta la cantidad de elementos de una lista o cadena(incluyendo espacios).
ejemplo:
>>> len("Hola mundo")
10
>>> lista = [1,2,3,4,5]
>>> len(lista)
5

max: Devuelve el elemento mayor de una lista o cadena
>>> lista = [1,2,3,4,5]
>>> max(lista)
5
>>> cadena = "abcde"
>>> max(cadena)
'e'

min: Devuelve el elemento menor de una lista o cadena
>>> lista = [1,2,3,4,5]
>>> min(lista)
1
>>> cadena = "abcde"
>>> min(cadena)
'a'

ord: Regresa el equivalente ascii de un caracter
>>> ord("A")
65

pow: Potencia de un número. El primer argumento es la base mientras que el segundo es el exponente.
>>> pow(5,2)
25

round: Redondea un número decimal hacia abajo.
>>> round(14,1)
14
>>> round(14,9)
14

sorted: Ordena una lista de menor a mayor.
>>> numeros = [2,1,4,6,3,5]
>>> sorted(numeros)
[1, 2, 3, 4, 5, 6]

str: Convierte un número en una cadena.
>>> str(10)
'10'
>>> 

sum: Suma los elementos de una lista
>>> lista = [1,2,3,4,5]
>>> sum(lista)
15

Esto es todo por ahora. Saludos

Gustavo J. Cerda Nilo
Noviembre 2016



2 comentarios:

C++ El apuntador This

El apuntador This En C++, cada objeto tiene acceso a su propia dirección a través de un puntero o apuntador denominado This. Lo...