Python
Contenido
Instalación
Python tiene muchas versiones, pero una de las más completas se llama Python(x,y). Esta se puede descargar del siguiente enlace.
Ver Página Oficial de Python(x,y)
El archivo de instalación de Python ofrece la posibilidad de incluir gran cantidad de módulos y librerías, hay que poner especial atención al momento de la instalación, de manera que no estemos olvidando ningún paquete importante, como por ejemplo el PySerial.
Además de eso, es necesario instalar el módulo de Pygame, disponible en el siguiente enlace.
Este agregado facilita enormemente el desarrollo de interfaces gráficas pues es un paquete de Python especial para el desarrollo de videojuegos.
Inicializaciones
Módulos
Los módulos son archivos que contienen las definiciones y argumentos utilizados en Python, por lo que se debe comenzar el programa importando los que necesitemos, esto se realiza a través del comando import. Algunos módulos son standard, como sys, mientras que otros dependen de lo que se quiera realizar, como pygame o serial.
Además, se pueden importar archivos .py que contengan definiciones o funciones que deseemos utilizar en nuestro programa, y, de esta manera, un programa muy largo se puede fragmentar en varios archivos distintos.
Por ejemplo, un modelo de inicialización de módulos sería:
import sys, pygame #Importa modulo pygame
from pygame.locals import *
import serial #Importa modulo para comunicacion serial
El * colocado importa todas las definiciones menos aquellas que comiencen por guión bajo (_).
Si se desea utilizar pygame, se debe iniciar utilizando pygame.init() en la función principal.
Imágenes y audio
Para utilizar imágenes o audio en el programa (utilizando pygame), primero los archivos deben estar en la misma carpeta del proyecto, luego se deben cargar empleando:
#Para cargar imagenes
foto = pygame.image.load("foto.jpg")
#Para cargar y reproducir audio:
loops = -1 #Cantidad de veces que se repetira la cancion, -1 significa que siempre se repite
start = 0.0 #Determina el tiempo en el que empezara la pieza
pygame.mixer.music.load("cancion.mp3")
pygame.mixer.music.play(loops,start)
#Para dejar de reproducir audio:
pygame.mixer.music.stop()
Los formatos de imagen permitidos por pygame son:
- JPG
- PNG
- GIF (Inanimada)
- BMP
- PCX
- TGA (Sin comprimir)
- TIF
- LBM
- PBM, PGM, PPM
- XPM
Pantalla
Para configurar las características de la ventana, es decir, su tamaño, el nombre, y la imagen de fondo, se puede proceder como sigue:
#Esta es la funcion principal mencionada previamente
def main():
pygame.init() #Inicio Pygame
screen = pygame.display.set_mode((1024, 720)) #Crea una ventana de 1024 de ancho por 720 de alto de nombre screen
pygame.display.set_caption("Python-Wikitronica") #Nombre de la ventana (esquina superior izquierda)
fondo = pygame.image.load("Fondo.jpg") #Cargo la imagen de fondo
screen.blit(fondo, (0, 0)) #la dibujo en "screen", en la posicion (0,0)
pygame.display.flip() #Actualizo toda la superficie de la pantalla
Para mejores resultados, hacer la imagen de fondo del mismo tamaño de la pantalla. Y nótese, que se debe mantener el margen en todas las líneas de código porque sino se tendrá el error "IndentationError: unexpected indent"
Escritura y dibujo
Escritura
Para escribir en pantalla, se procede de una manera similar a mostrar una imagen. Primero, se debe determinar la fuente con la cual se escribirá, luego se escribe el mensaje y se muestra en pantalla. Esto se puede realizar de la siguiente forma:
#Opcion 1 para seleccionar la fuente
fuente = pygame.font.Font(None, 25) #None es el tipo de letra default. 25 es el tamaño de la letra
# Para esta opcion, si se quiere usar una fuente distinta, el archivo debe estar en la misma carpeta del programa y se coloca:
fuente2 = pygame.font.Font("Nombredefuente.ttf", 25)
#Opcion 2
print pygame.font.get_fonts() #Con este comando se ven todas las fuentes que se tienen instaladas en el sistema
fuente3 = pygame.font.SysFont('arial', 25) #Se coloca cualquiera de esas fuentes
#Se le asigna un mensaje a una variable
texto = "Programa de prueba. Python-Wikitronica"
#Font.render(text, antialias, color, background=None)
mensaje = fuente.render(texto, 1, (0, 0, 0)) #Se escriben en color negro, con el tipo de letra de la variable fuente.
#background=None (No se pone ningun color de fondo)
#Se muestra en pantalla
SCREEN.blit(mensaje, (400, 400)) #Se muestra en la posición (400,400)
pygame.display.flip() #Actualizo toda la superficie de la pantalla
Colores
Los colores se basan en el sistema RGB (Red, Green, Blue), y están determinados por un vector de tres coordenadas, donde la primera indica el grado de rojo, la segunda de verde y la tercera de azul. Así, si colocamos 0 en las tres coordenadas se tendrá negro, y si colocamos lo máximo, es decir, 255 ($FF), se tendrá blanco; el resto de los colores se obtiene por mezcla de estas tonalidades.
- Negro = (0,0,0)
- Blanco = (255,255,255)
- Rojo = (255,0,0)
- Verde = (0,255,0)
- Azul = (0,0,255)
- Amarillo = (255,255,0)
- Magenta =(255,0,255)
- Cian=(0,255,255)
Ver Tabla de colores para mayor información
Dibujo
Pygame permite dibujar diversas formas geométricas como rectángulos, círculos, elipses, polígonos, líneas, etc; de manera sencilla, utilizando la función draw. La sintaxis de cada una depende de la figura a dibujar, a continuación mostraremos los casos del rectángulo, círculo y arco:
#rect(Surface, color, Rect, width=0)
#El parametro Rect es un vector de 4 coordenadas en donde se coloca (PosX,PosY,Ancho,Largo)
#Creamos un rectangulo blanco en la posicion (20,20) de ancho 100 pixeles y largo 50 pix
rectangulo1=pygame.draw.rect(screen,(255,255,255),(20,20,100,50))
#circle(Surface, color, pos, radius, width=0)
#Creamos un circulo negro de centro (40,40) y radio 15pix
Circulo1=pygame.draw.circle(screen,(0,0,0),(40,40),15)
#arc(Surface, color, Rect, start_angle, stop_angle, width=1) Rect establece al area que llenara el arco
#Se crea un arco azul que empieza en pi y termina en 2pi, el angulo se mide en radianes
Arco1=pygame.draw.arc(screen,(0,0,255),(20,20,100,50),3.1416,3.1416*2)
Eventos de teclado
A partir de pygame se puede ver si determinada tecla está presionada de una manera sencilla utilizando los eventos de teclado. Éstos se colocan dentro de un for que "captura" el evento y luego se ve si la tecla (representadas por constantes en pygame) está presionada o no.
for event in pygame.event.get(): #capturo el evento
if event.type == pygame.KEYDOWN: #Caso para tecla presionada
if event.key== K_a: #Si la tecla es la letra a
#realizo cualquier accion
elif event.key == K_UP: #Si la tecla es la flecha hacia arriba
#realizo accion
elif event.type == pygame.KEYUP: #Caso para teclas levantadas
#Realizo accion
Nótese que el margen es el que establece qué está dentro de cada condicional. Ver el apartado Teclas de la sección de referencias para mayor información sobre las constantes.
Pygame también permite tomar en cuenta los modificadores de teclado como caps lock, numlock, ctrl, etc; esto se ve a través de los KMODS, por ejemplo:
for event in pygame.event.get(): #capturo el evento
if event.type == pygame.KEYDOWN: #Caso para tecla presionada
if pygame.key.get_mods() & KMOD_SHIFT: #Si la tecla shift esta presionada
#realizo cualquier accion
La siguiente figura corresponde a un ejemplo de los puntos 1, 2 y 3
Programación Orientada a Objetos
Python puede funcionar con programación orientada a objetos, de forma similar a como lo hace JAVA. En este tipo de programación se definen una clases, que son análogas a las structuras en C, salvo que dichas clases incluyen además ciertas operaciones (funciones) que puede ejecutar el objeto. Esto quiere, a muy groso modo, lo siguiente:
- Uno puede definir tipos de objetos llamados clases. Por ejemplo: las clases de los círculos, los perros, etc.
- Estas clases tienen atributos propios de sí. Por ejemplo: los círculos tienen radio, centro, color, etc.
- Las clases incluyen funciones, que son operaciones que pueden realizar en un determinado momento. Por ejemplo: un círculo puede aparecer en pantalla, moverse, etc.
- Las clases pueden generar varios objetos, que son especies de variables de un determinado tipo de dato, salvo por que incluyen las funciones ya mencionadas.
- Si se genera más de un objeto de una determinada clase, ambos objetos serán en esencia idénticos a menos que utilicemos un constructor para diferenciarlos.
- Un constructor es una función que inicializa los parámetros de nuestro objeto y que permite distinguirlo de los demás dándole un nombre y características distintas.
- Una determinada clase puede tener más de un constructor si es necesario.
Definición de una Clase
Ahora veamos más en detalle cada una de las partes de una definición de clase: Lo primero que debemos hacer es colocar la etiqueta "class" seguida del nombre que le queremos colocar a nuestra clase, lo segundo es declarar el inicializador (constructor) con los campos que tendrá. Este constructor es una función llamada "__init__" que recibe al propio objeto "__init__(self)" y a toda una serie de parámetros que le colocamos a nuestro objeto. En este caso, además del propio objeto, tenemos como elementos: la posición (x,y) el radio (size), el color, y el espesor. Para este constructor en particular, si se crea el objeto, puede no especificársele ni el color ni el espesor, ya que tienen valores por defecto, sin embargo, todos los demás datos deben ser indicados para cada objeto de la clase.
#Creacion de la clase circulo
class Circulo:
def __init__(self, x, y, size, color = (255,255,255), width=1):
self.x = x
self.y = y
self.size = size
self.color = color
self.width = width
Definida la clase del ejemplo, las siguientes son llamadas posibles para crear objetos
#Creacion de objetos de clase circulo
mi_circulo1 = Circulo(100, 100, 30, (255,0,0), 0) #Crea un circulo con centro en (100,100) radio 30, color rojo y espesor=0 (completamente relleno)
mi_circulo2 = Circulo(300, 200, 50) #Crea un circulo con centro en (300,200) radio 50, color blanco y espesor = 1
mi_circulo3 = Circulo(300, 500, 60, (0,0,255)) #Crea un circulo con centro en (300,500) radio 60, color azul y espesor = 1
Sin embargo, un objeto es algo vacío si no tiene asociadas las acciones que este puede hacer. La siguiente versión muestra la clase Círculo, esta vez con una acción que le permite aparecer en pantalla; la función display. Nótese que esta acción tiene una única instrucción que llama a pygame.draw.circle, pero la cantidad de instrucciones que puede tener cada acción y la cantidad de acciones por clase no es limitada.
#Creacion de la clase circulo con funciones
class Circulo:
def __init__(self, x, y, size, color = (255,255,255), width=1):
self.x = x
self.y = y
self.size = size
self.color = color
self.width = width
def display(self):
pygame.draw.circle(screen, self.color, (self.x, self.y), self.size, self.width)
Declarada esta variante de la clase, pueden crearse objetos circulos que, en cualquier parte del programa, pueden llamarse a aparecer en pantalla mediante la instrucción
#Llamada de la funcion display para un determinado circulo
mi_circulo1.display()
mi_circulo3.display()
Ejemplo completo de utilización de Clases
Este ejemplo utiliza la clase círculo ya explicada en el apartado anterior, crea objetos de esta clase y los muestra por pantalla. Preste atención a la manera adecuada de utilizar las clases dentro de un programa completo y estructurado.
import os, sys, math, pygame
from pygame.locals import *
#Definir Colores
negro = 0, 0, 0
blanco = 255, 255, 255
rojo = 255, 0, 0
verde = 0, 255, 0
azul = 0, 0, 255
#Preparar el Screen
screen = pygame.display.set_mode((800, 600))
#Definicion de la clase circulo
class Circulo:
def __init__(self, (x,y), size, color=(255,255,255), width=1):
self.x = x
self.y = y
self.size = size
self.color = color
self.width = width
def display(self):
pygame.draw.circle(screen, self.color, (self.x, self.y), self.size,self.width)
#Creacion de objetos
circulo1 = Circulo((100,100), 30, azul, 0)
circulo2 = Circulo((200,300), 40, verde, 8)
circulo3 = Circulo((600,500), 20)
circulo4 = Circulo((700,300), 50,rojo,4)
#Generar Reloj
clock = pygame.time.Clock()
#Ciclo continuo del programa
fps = 30
correr = True
while correr:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
correr = False
screen.fill(negro)
circulo1.display()
circulo2.display()
circulo3.display()
circulo4.display()
pygame.display.flip()
pygame.quit()
sys.exit()
Si se realiza la corrida de este programa, puede verse en pantalla el siguiente resultado:
Animaciones
Antes de leer este apartado se recomienda enormemente que lea primero el de programación orientada a objetos.
Python presenta muchas ventajas con respecto a otros lenguajes de programación, pero quizás uno de sus puntos más fuertes es la facilidad con la que permite realizar animaciones. Para esto se podría simplemente pedir al programa que dibuje una secuencia determinada de imágenes a una cierta velocidad, por ejemplo 30 FPS. Sin embargo, esto requiere una gran cantidad de imágenes y existen formas mucho más prácticas de animar objetos, como la que explicaremos a continuación.
Antes que nada se requiere una librería llama Pyeuclid que incluye ya prefabricadas una gran cantidad de funciones de gran utilidad, esta librería se puede obtener aquí: Acceso a Pyeuclid
En los vínculos externos seleccionamos euclid.py y se nos despliega una página con todo el contenido del archivo. Este contenido lo debemos guardar en un archivo de extensión ".py" usando por ejemplo notepad o notepad++ y lo colocamos en el mismo directorio del programa de animaciones en el que estamos trabajando.
Hecho esto podemos comenzar a trabajar importando el archivo y creando las variables y clases necesarias, pero en esta oportunidad queremos que nuestra clase tenga una función asociada que le permita desplazarse "Ciculo.move()". El siguiente ejemplo muestra un programa que genera 5 círculos de manera aleatoria y deja que estos se muevan por la pantalla también de manera aleatoria. Cómo se generan los números aleatorios para crear los circulos o para decidir su movimiento no es de tanta importancia como lo es el fenómeno de generación de animaciones en Python, así que cuando lo lea no preste demasiada atención a esos detalles:
Ejemplo de Animaciones en Python
Este es un pequeño extracto del ejemplo colocado en el link de arriba, que contiene las especificaciones sobre la creación de clases y las funciones agenas a la clase pero necesarias para el funcionamiento del programa. Básicamente, lo que se hace es modificar la velocidad al azar, utilizando "get_random_velocity()" y "change_velocity()". Más importante aún se utiliza la funcion move(), que reposiciona al circulo, sumándole a su posicion actual su velocidad multiplicada por un diferencial de tiempo.
#Creo la clase circulo, en este caso position es un vector que resume las coordenadas (x,y) y Vector2 es un vector que pertenece a pyeuclide
class Circulo:
def __init__(self, position, size, color = (255,255,255), velocity= eucly.Vector2(0,0), width=1):
self.position = position
self.size = size
self.color = color
self.width = width
self.velocity = velocity
#Esta es una variante de la funcion Display ya vista, que descompone primero las coordenadas de position en rx y ry
def display(self):
rx, ry = int(self.position.x), int(self.position.y)
pygame.draw.circle(screen, self.color, (rx, ry), self.size, self.width)
#Esta es una funcion de movimiento que reescribe la posicion final con la inicial mas la velocidad por tiempo
def move(self):
self.position = self.position + dtime * self.velocity
#Esta es una funcion que permite cambiar la velocidad de el objeto
def change_velocity(self,velocidad):
self.velocity = velocity
#Esta funcion no pertenece a la clase, pero genera un vector de velocidad al azar.
def get_random_velocity():
new_angle = random.uniform(0, math.pi*2)
new_x = math.sin(new_angle)
new_y = math.cos(new_angle)
new_vector = eucly.Vector2(new_x,new_y)
new_vector.normalize()
new_vector *= velocidad_inicial
return new_vector
Por último pueden observar los videotutoriales en el apartado de referencias, son de gran utilidad para entender y profundizar en esta parte.
Pyserial
Un módulo de interés al utilizar Python(x,y) combinado con nuestro microcontrolador es Pyserial.
Pyserial es muy amigable y fácil de utilizar pues los grandes conjuntos de instrucciones utilizados para la transmisión y recepción de información a los que estamos acostumbrados se resumen en funciones muy sencillas de manejar.
Al trabajar con puerto serial en Python lo primero que se debe hacer es importar la librería de serial, luego abrir el o los puertos y configurarlos a la velocidad de transmisión deseada. A continuación se muestra un ejemplo de como hacer esto:
import serial #Importa modulo para comunicacion serial
ser = serial.Serial('COM10',9600) #Abre el puerto COM1 y lo configura para transmitir a 9600baud
Es importante saber que puerto es el que deseamos abrir, y la manera de identificarlo es buscándo a través del administrador de dispositivos (Device Manager) ubicado en el panel de control. La siguiente imágen busca ilustrar un poco como conseguir esta información:
Una vez inicializado el puerto de manera correcta, las rutinas de transmisión y recepción son bien sencillas:
# Para Lectura a través de serial
x = ser.read() # Capturar un byte por serial y almacenarlo en X
s = ser.read(10) # Capturar hasta 10 bytes y almacenarlos en S
# Para Escritura a través de serial
ser.write("hello") # Envia por puerto serial la cadena de caracteres o el caracter escritos