Dado para Android hecho con Python
Creación de un Dado para Android utilizando Apache Cordova y Brython
Vamos a crear una aplicación sencilla para Android utilizando Python. Crearemos un Dado:
Antes de empezar pongo a tu disposición los archivos que crearemos en el proyecto y el apk final:
CREAR EL PROYECTO DADO
Lo primero que tenemos que hacer es crear un Proyecto Cordova. Si no tienes instalado el Framework Cordova o no sabes como crear un proyecto puedes visitar la página principal de esta sección.
A continuación crearé el proyecto Cordova, agregaré la plataforma Android al proyecto, eliminaré los archivos innecesarios generados por defecto e instalaré Brython:
cordova create dado com.tecnobillo.dado Dado
cd dado
cordova platform add android
cd www
rm index.html css/index.css js/index.js
rm -r img
cd js
python3 -m brython --install
rm *.txt *.html
Ya tenemos preparado el proyecto dado. Crearemos la aplicación mediante 4 archivos en el proyecto calculadora:
- config.xml - Archivo de configuración en el que especificamos nombre de aplicación, icono, etc.
- www/index.html - Archivo en el que definimos la estructura de la interfaz gráfica de la aplicación.
- www/css/styles.css - Archivo en el que definimos el estilo de la interfaz gráfica de la aplicación.
- www/app.py - Archivo en el que definimos la lógica de la aplicación (Python).
Además en la carpeta www debe haber un directorio assets que contiene las imágenes que representan las caras del dado.
Cuando tengamos todo listo compilaremos nuestro archivo APK mediante:
cordova build android
CONFIG.XML
Archivo dado/config.xml
Lo primero que debemos hacer es editar el archivo config.xml para especificar el nombre de la aplicación, icono, etc. Yo lo dejaré del siguiente modo:
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.tecnobillo.dado" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Dado</name>
<description>
Dado creado con Apache Cordova y Brython
</description>
<author email="[email protected]" href="https://tecnobillo.com">
José María Sánchez Ruiz (tecnobillo)
</author>
<content src="index.html" />
<icon src="dado.png" />
<preference name="Orientation" value="portrait" />
</widget>
Si utilizas este config.xml debes copiar un archivo dado.png en el directorio raíz del proyecto (el mismo donde está config.xml), ya que lo he especificado en el atributo src de la etiqueta icon.
He utilizado el siguiente icono:
Quiero que la aplicación tenga orientación portrait, es decir, que siempre se vea en vertical aunque el dispositivo se gire, para ello he añadido:
<preference name="Orientation" value="portrait" />
He configurado la aplicación con orientación portrait porque el archivo styles.css que crearemos mas adelante solamente está pensado para esta orientación, pero si quieres puedes modificarlo para que la aplicación también se vea bien en landscape.
Cambia mis datos por los tuyos (autor, email, sitio web, etc), y fíjate en el atributo src de la etiqueta content: Indica que el contenido que debe cargarse al ejecutarse la aplicación es el del archivo index.html.
INDEX.HTML
Archivo dado/www/index.html
Una vez editado el archivo config.xml, el siguiente paso es crear un archivo index.html en el directorio www. Copia lo siguiente en dicho archivo:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval'">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
<script src="js/brython.js"></script>
<script src="js/brython_stdlib.js"></script>
<script type="text/javascript">
function startBrython(){
document.addEventListener('deviceready', brython, false);
}
</script>
<link rel="stylesheet" href="css/styles.css"/>
</head>
<body onload='startBrython();'>
<script type="text/javascript" src="cordova.js"></script>
<img id="vista-dado" src="assets/1.png"/>
<button id="lanzar-dado">LANZAR<br/>DADO</button>
<script type="text/python">
from browser import ajax, run_script
ajax.get('app.py', oncomplete=lambda req:run_script(req.text, '__main__'))
</script>
</body>
</html>
Si quieres conocer los detalles de este archivo puedes ver una descripción completa en la página principal de esta sección.
Fíjate en los siguientes puntos:
Hemos enlazado Brython en el documento (que instalamos anteriormente):
<script src="js/brython.js"></script> <script src="js/brython_stdlib.js"></script>
Hemos enlazado una hoja de estilos CSS (que crearemos posteriormente):
<link rel='stylesheet' href='css/styles.css'/>
Hemos definido una función
startBrython
que espera a que el dispositivo esté ready para ejecutarbrython
:function startBrython(){ document.addEventListener('deviceready', brython, false); }
Ejecutamos
startBrython
una vez que body se haya cargado:<body onload='startBrython();'>
Hemos definido los siguientes elementos html en body:
<img id="vista-dado" src="assets/1.png"/> <button id="lanzar-dado">LANZAR<br/>DADO</button>
Los utilizaremos posteriormente en app.py y les daremos estilo en styles.css, por eso les asignamos un atributo id. La etiqueta img es la imágen del dado, y el button es el botón para lanzar el dado.
Hemos enlazado el documento con un script Python (que crearemos posteriormente):
<script type="text/python3"> from browser import ajax, run_script ajax.get('app.py', oncomplete=lambda req:run_script(req.text, '__main__')) </script>
STYLES.CSS
Archivo dado/www/css/styles.css
Crea un archivo styles.css en el directorio www/css y copia lo siguiente:
*{
margin:0px;
padding:0px;
box-sizing:border-box;
}
body {
width:100vw;
height:100vh;
background:gray;
}
#vista-dado{
width:95vw;
max-width:95vw;
max-height:50vh;
position:fixed;
top:0px;
left:0px;
margin:2vw;
border:0.5em solid black;
border-radius:100px;
}
#lanzar-dado{
width:100vw;
max-width:100vw;
height:40vh;
max-height:40vh;
font-size:3em;
position:fixed;
bottom:0px;
left:0px;
}
No estamos aquí para aprender CSS así que esta parte no voy a explicarla, simplemene copia el código o adáptalo a tu gusto. Este código css está pensado para una aplicación portrait.
Sinceramente no me gusta demasiado CSS y se me da fatal. Por favor no me juzges si mi css te parece ridículo
APP.PY
Archivo dado/www/app.py
Crea un archivo app.py en el directiorio www.
¡MANOS A LA OBRA!
Lo primero que debes hacer es importar los módulos que necesitaremos en nuestro programa:
from browser import document, window
from browser.timer import set_interval, clear_interval
import random
A continuación crea una clase DadoApp:
class DadoApp:
def __init__(self, vista, boton):
self.vista = vista
self.boton = boton
Continuaremos definiendo la clase DadoApp, pero antes añade lo siguiente al script:
if __name__ == '__main__':
imgVista = document['vista-dado']
btnLanzar = document['lanzar-dado']
DadoApp(imgVista, btnLanzar)
Aquí lo que hemos hecho es obtener la img y el button que habíamos definido en index.html. Guardamos la imagen en la variable imgVista
y el botón en la variable btnLanzar
.
A continuación hemos instanciado una DadoApp pasándole como argumentos la imágen y el botón:
DadoApp(imgVista, btnLanzar)
Volvamos ahora a la clase DadoApp:
class DadoApp:
def __init__(self, vista, boton):
self.vista = vista
self.boton = boton
self.vista
es la imagen del dado, y self.boton
es el botón para lanzar el dado.
Vamos a almacenar las direcciones de las imágenes del dado en un diccionario al que identificamos como self.states
. Las imágenes del dado se encuentran en la carpeta assets, y se llaman 1.png, 2.png, 3.png, 4.png, 5.png y 6.png (6 imágenes para representar las 6 caras del dado). Añade lo siguiente al método __init__
de DadoApp:
for i in range(1, 7):
self.states[i] = f'assets/{i}.png'
De este modo hemos creado un diccionario en el que las claves son los números enteros del 1 al 6, y su valor es el path de la imagen correspondiente formateada con el valor de la clase.
Ahora vamos a definir un método show_random
para mostrar una cara aleatoria del dado:
def show_random(self):
n = random.randrange(1, 7)
self.vista.src = self.states[n]
show_random
obtiene un número aleatorio comprendido entre 1 y 6, y lo almacena en la variable local n
, que es utilizada para obtener el path de una de las imágenes del dado a través de self.states[n]
, cuyo valor se asigna a self.vista.src
, es decir, al atributo src de la imágen en el documento html.
Una vez definido el método show_random
, lo invocamos dentro del método __init__
para mostrar una imágen aleatoria del dado al abrir la aplicación. La clase DadoApp de momento se ve así:
class DadoApp:
def __init__(self, vista, boton):
self.vista = vista
self.boton = boton
for i in range(1, 7):
self.states[i] = f'assets/{i}.png'
self.show_random()
def show_random(self):
n = random.randrange(1, 7)
self.vista.src = self.states[n]
En este punto podrías finalizar la aplicación añadiendo al final de __init__
lo siguiente:
self.boton.bind('click', lambda e:self.show_random())
Así, cada vez que se pulse self.boton
se ejecutará el método self.show_random
y se mostrará una cara aleatoria del dado.
Pero vamos a complicar un poco la cosa para crear la sensacióm de que el dado ha sido realmente tirado: Define un método lanzar
así:
def lanzar(self, e):
pass
Al final de __init__
enlaza este método con el evento click sobre el botón:
self.boton.bind('click', self.lanzar)
Ya sólo nos queda definir el método lanzar
, pero antes hay que añadir una serie de atributos de clase que utilizaremos en este método:
states = {}
interval = None
times = 0
La clase DadoApp ahora mismo se ve así:
class DadoApp:
states = {}
interval = None
times = 0
def __init__(self, vista, boton):
self.vista = vista
self.boton = boton
for i in range(1, 7):
self.states[i] = f'assets/{i}.png'
self.show_random()
self.boton.bind('click', self.lanzar)
def show_random(self):
n = random.randrange(1, 7)
self.vista.src = self.states[n]
def lanzar(self, e):
pass
Tenemos que ejecutar self.show_random
varias veces seguidas para dar la sensación de que el dado está rodando. Define una función anidada dentro de self.lanzar
y ejecútala a intervalos de 100ms:
def lanzar(self, e):
def show_timer():
self.show_random()
self.boton.disabled = True
self.interval = set_interval(show_timer, 100)
Observa que mediante self.boton.disabled = True
desactivo el botón cuando se hace click en él. Solamente al finalizar la tarea de show_timer
volveremos a activar el botón.
Hay que añadirle un poco mas de funcionalidad a show_timer
:
def show_timer():
self.show_random()
if self.times < 20:
self.times += 1
else:
clear_interval(self.interval)
window.navigator.vibrate(100)
self.boton.disabled = False
self.times = 0
Lo que hemos hecho en show_timer
es ejecutar self.show_random()
y a continuación comprobar self.times < 20
. En ese caso incrementamos en 1 el valor de self.times
.
Si self.times >= 20
, se reinicia la variable mediante self.times = 0
y se detiene el self.interval
(que hace referencia al intervalo que está ejecutando periódicamente a show_timer
).
Además, cuando detenemos a self.interval
reactivamos el botón de lanzar el dado: self.boton.disabled = False
.
He añadido window.navigator.vibrate(100)
para que el dispositivo vibre cuando el dado se haya detenido. Esta sentencia solamente funcionará si previamente has instalado el plugin vibrate en el proyecto cordova. Puedes hacerlo mediante:
cordova plugin add cordova-plugin-vibration
EL archivo app.py finalmente debe verse así:
from browser import document, window
from browser.timer import set_interval, clear_interval
import random
class DadoApp:
states = {}
interval = None
times = 0
def __init__(self, vista, boton):
self.vista = vista
self.boton = boton
for i in range(1, 7):
self.states[i] = f'assets/{i}.png'
self.show_random()
self.boton.bind('click', self.lanzar)
def show_random(self):
n = random.randrange(1, 7)
self.vista.src = self.states[n]
def lanzar(self, e):
def show_timer():
self.show_random()
if self.times < 20:
self.times += 1
else:
clear_interval(self.interval)
window.navigator.vibrate(100)
self.boton.disabled = False
self.times = 0
self.boton.disabled = True
self.interval = set_interval(show_timer, 100)
if __name__ == '__main__':
imgVista = document['vista-dado']
btnLanzar = document['lanzar-dado']
DadoApp(imgVista, btnLanzar)
__Ya puedes crear el *archivo APK* e instalarlo en tu dispositivo:__
cordova build android
El APK se genera en el directorio dado/platforms/android/app/build/outputs/apk/debug y se llama app-debug.apk.
Si quieres crear una versión de lanzamiento de la aplicación (app-release.apk) puedes ver como hacerlo en Cordova-Brython.