jueves, 12 de diciembre de 2013

Ponte la Armadura, Java y las Clases Abstractas

Como mencionaba en un artículo anterior Una de las cosas más complejas de entender en el mundo de Java son las clases abstractas, en ésta entrada vamos a hablar de ellas.
¿Una armadura?, es extraño mencionar una armadura cuando de código se trata, pero veamos esto:
  • Una armadura es un conjunto de piezas  que vienen listas para permitir que hagamos algo.
  • Para poder portar la armadura es necesario tener (o implementar) ciertas aptitudes físicas para poder usarla.
Así mismo es una clase abstracta, en ésta existen un conjunto de elementos que ya están listos para ser usados y que fueron implementados como se implementan comúnmente métodos en Java, sin embargo hay otros que debemos hacer nosotros mismos antes de poder usar la clase y los hacemos declarando el método abstracto y removiendo el cuerpo del método.

Veamos un ejemplo:

public abstract class Ficha{
    private String nombre = "Ficha";
    public abstract void mover(String destino);
    public abstract void atacar(String destino);
    public String getNombre(){
        return nombre
    }
    public void setNombre(String nombre){
        this.nombre = nombre
    }
}

En el ejemplo anterior vemos la clase abstracta Ficha, que tiene dos métodos comunes (getNombre, SetNombre) y dos métodos abstractos (mover, atacar). Ahora bien, esta clase existe y tiene ciertas cosas que podemos usar (ya está la armadura), pero hay otras que debemos hacer nosotros (Trabajo físico para usar la armadura), para esto podríamos:
  • Crear una nueva clase que implemente la clase abstracta
  • Crear una instancia de la clase abstracta y definir los métodos ahí mismo (Se ve muy feo pero funciona)
Veamos un ejemplo de cada una:

Primera Forma
public class Peon implements Ficha {
    public Peon() {
        setNombre("Peon");
    }
    @Override
    public void mover(String destino){
        System.out.println(String.format("Me estoy moviendo a %s", destino));
    }
    @Override
    public void atacar(String destino){
        System.out.println(String.format("Estoy atacando %s", destino));
    }
}

Como vemos en el ejemplo anterior se ha creado una nueva clase, la cuál implementa la clase abstracta Ficha, es posible observar que podemos usar el método setNombre que había sido definido en la clase Ficha y que se han reescrito los métodos abstractos agregando lógica a  éstos. Es muy importante tener en cuenta que a los métodos reescritos se les ha puesto la anotación @Override, la cual especifica que lo que queremos hacer es reescribir el método.

Segunda Forma
public class Main {
    public static void main(String []argv) {
        Ficha f = new Ficha(){
            @Override
            public void mover(String destino){
                System.out.println(
                    String.format("Me estoy moviendo a %s", destino)
                );
            }
            @Override
            public void atacar(String destino){
                System.out.println(String.format("Estoy atacando %s", destino));
            }
        };
        f.setNombre("Peon");
    }
}

Como vemos en el ejemplo instanciamos la clase Ficha, pero debemos llenar de alguna manera los lugares faltantes.

¿Y para qué usar armadura?
Las clases abstractas son elementos que permitirían hacer código que luego sea extendido en situaciones específicas, por ejemplo, si se tiene un tablero de ajedrez y hay que implementar el movimiento de las fichas seria bueno usar clases abstractas, puesto que a pesar de que todas las fichas son iguales no se mueven igual, por lo tanto hay que reescribir el método de movimiento en cada clase.

Espero que sea útil.
Si te gusto el post
compartelo... :D


lunes, 25 de noviembre de 2013

Puesta en producción de una aplicación web con Django y Nginx


Cuando se usan frameworks para desarrollo web todo se hace más fácil, excepto la puesta en producción, una muestra clara de esto es django, un framework que permite que cosas como hacer un entorno de administración parezca juego de niños y que realizar consultas que parecen concebidas en la maligna mente del mismo Satanás sea una tarde de picnic. Desafortunadamente a la hora de poner en producción un proyecto empezamos a ver dificultades. Este post tiene como objetivo mostrar una sencilla manera de poner en producción nuestros proyectos con Django mediante el servidor Nginx.

Nota: Todo el proceso de configuración esta preparado para Linux Debian y derivados, si usas una distribución diferente es probable que cambien las rutas de los archivos de configuración.

Primero que todo, ¿Qué es Nginx?

Nginx es un servidor web y proxy inverso ligero de muy alto rendimiento, es software libre de código abierto y multiplataforma, tiene las siguientes características:
  • Servidor de archivos estáticos, índices y autoindexado.
  • Proxy inverso con opciones de caché.
  • Permite realizar balanceo de carga.
  • Tolerancia a fallos.
  • Soporte de HTTP sobre SSL.
  • Soporte para FastCGI con opciones de caché.
  • Servidores virtuales basados en nombre y/o en dirección IP.
  • Streaming de archivos FLV y MP4.
  • Soporte para autenticación.
  • Compatible con IPv6.
  • Soporte para protocolo SPDY.
  • Soporte para compresión gzip de paquetes.
  • Habilitado para soportar más de 10.000 conexiones simultáneas.
Muy bien, pero ¿Qué pasa con lo clásico?
Muchos se preguntaran ¿Por qué Nginx y no Apache2?, pienso que hay varias cosas al respecto:
  • La cantidad de recursos que demanda Apache2 son mucho mayores que los demandados por Nginx.
  • La velocidad alcanzada por Nginx es mucho mayor que la de Apache2 a la hora de responder peticiones.
  • Nginx es muchisimo más fácil de configurar que Apache2 puesto que Apache2 usa engorrosos XMLs, Nginx no.
La información mencionada la obtuve de aquí, aquí y aquí.

Muy bien, entonces, manos a la obra
En este momento me parece adecuado explicar cómo funciona la conexión entre django y Nginx:

Nginx es el encargado de atender las peticiones externas por el puerto 80 (protocolo HTTP), éste a su vez envía los datos de la petición a uWSGI mediante un puerto auxiliar, quien hace de intermediario en el proceso de comunicación y finalmente uWSGI entrega los datos de petición a nuestro proyecto django para que éste ejecute la acción correspondiente e inicie el proceso inverso para responder a la petición. Escogí uWSGI porque me pareció muy fácil de usar y en éste enlace hay una comparativa de rendimiento en la cual el tiempo de respuesta es mejor que el de Gunicorn.

Configuremos Nginx
Debemos crear el archivo de configuración para nuestro proyecto, recuerda que si el puerto 80 está siendo usado por Apache2, por alguna otra aplicación o por el mismo Nginx por otra configuración debes liberar primero el puerto, en caso de ser Apache2 debes editar el archivo /etc/apache2/ports.conf y cambiar el puerto 80 por uno diferente, luego reiniciar el servicio Apache2 así:
$ service apache2 restart
Después de esto debes crear el archivo de configuración en la carpeta /etc/nginx/sites-avaliable, el nombre del archivo lo eliges tú, yo elegí como nombre archivo.conf. El contenido del archivo debe ser el siguiente:
# archivo.conf
server {
    listen      80; # puerto por el que escucha la aplicación
    server_name midominio.co; # nombre o ip del servidor
    charset     utf-8;

    # tamaño maximo de subida
    client_max_body_size 75M;

    # Ruta de la carpeta media
    location /media  {
        alias /var/www/proyecto/media; # reemplace por su ruta a media
    }

    # Ruta de la carpeta static
    location /static {
        alias /var/www/proyecto/static; # reemplace por su ruta a media
    }

    # Configuración de las peticiones que no son resueltas con las
    # reglas anteriores. Estas son reenviadas al servidor django
    location / {
        uwsgi_pass 127.0.0.1:8001; # 127.0.0.1:PUERTO_AUXILIAR
        include     uwsgi_params;
    }
}
Cabe resaltar que el puerto auxiliar seleccionado es el 8001, este dato es importante puesto que debemos tenerlo en cuenta para iniciar la labor de uWSGI.
Después de esto es necesario habilitar la nueva configuración creando un enlace simbólico del archivo en la carpeta sites-avaliable de Nginx así:
$ cd /etc/nginx/sites-enabled
$ ln -s ../sites-avaliable/archivo.conf .
Finalmente reiniciamos el servidor Nginx
$ service nginx restart

Configuremos nuestro entorno
Primero es necesario crear un entorno para el proyecto con la orden mkvirtualenv vista en el post anterior y luego activamos el entorno creado con la orden workon:
$ mkvirtualenv entorno
$ workon entorno
después de ésto es necesario instalar las dependencias de nuestro proyecto django, personalmente pienso que es bueno trabajar con django 1.5 o superior, puesto que aasi tendremos muchas de las nuevas características, además django 1.6 fue liberado hace un par de semanas.
$ pip install django
$ pip install ....
$ pip install ....
Ahora necesitamos instalar uWSGI:
$ pip install uwsgi
Nota: Para instalar uWSGI es necesario tener instalados en el sistema el compilador gcc y los paquetes de desarrollo de python, para el caso de linux debian y derivados con python2.7 deben instalarse como superusuario así:
$ apt-get install gcc-4.5 python2.7-dev
Finalmente creamos el archivo para iniciar el proceso de conexión entre Nginx y django, este archivo es un script bash al que yo llame start.sh, este debe tener el siguiente contenido:
#!/bin/bash
uwsgi --socket :8001 --wsgi-file wsgi.py -d mensajes.log

Aquí viene la parte importante, cuando django genera un proyecto él crea una carpeta de proyecto, dentro de ésta hay una carpeta con los archivos de configuración, éstos son settings.py, urls.py, __init__.py y wsgi.py


debe hacer una copia del archivo wsgi.py a la raíz del proyecto y en éste mismo lugar poner el archivo start.sh así:

Después es necesario permitir la ejecución de este archivo mediante la orden:
$ chmod +x start.sh
Luego podemos iniciar el proceso así:
$ ./start.sh
Si tienes alguna duda o comentario puedes hacerla en la sección de comentarios, espero que sea útil.

Si te gusto el post
compartelo... :D

sábado, 23 de noviembre de 2013

Virtualenv II, yendo más allá con virtualenvwrapper


En una entrada anterior vimos algo de Virtualenv, ahora vamos a ver cómo usar una herramienta que facilita un tanto su uso, Virtualenvwraper.

¿Qué es Virtualenvwrapper?
Virtualenvwrapper es un conjunto de herramientas para extender Virtualenv. Éstas herramientas incluyen wrappers o contenedores para la creación y eliminación de entornos virtuales y la gestión del flujo de trabajo para desarrollo, facilitando trabajar en más de un proyecto a la vez sin generar conflictos entre dependencias.

Características de Virtualenvwrapper
  • Organiza todos sus entornos virtuales en un solo lugar.
  • Wrappers para la gestión de sus entornos virtuales (crear, borrar, copiar).
  • Facilita cambiar entre entornos con un solo comando.
  • Implementa el uso de la tecla de tabulación (tab) para los argumentos de las ordenes de los entornos virtuales (no válido para el CMD de Windows, para esto es necesario tener una shell Cygwin).
  • Posibilidad para que el usuario cree hooks (ganchos) en todas las operaciones para ejecutar acciones propias. 
  • Sistema de plug-ins para la creación de extensiones.
Instalación
Luego de tener instalado Virtualenv instalamos Virtualenvwrapper mediante la orden
easy_install virtualenvwrapper
o
pip install virtualenvwrapper
Luego de esto es necesario configurarlo, para esto debemos seleccionar la carpeta en la cual quedarán almacenados los entornos, para este caso la carpeta de entornos quedará en el directorio del usuaio y se llamará .entornos (Para los sistemas *nix el punto al principio oculta los entornos) .  Entonces debemos establecer una variable al sistema (para Windows también es necesario agregar una ruta al path del sistema), esto se hace así:

Para sistemas *nix (Linux, MacOSX)
Buscar un archivo en la carpeta de usuario llamado .bashrc o .bash_profile, ábrelo con tu editor preferido y pones las siguientes líneas:
export WORKON_HOME=$HOME/.entornos
source /usr/local/bin/virtualenvwrapper.sh
En estas dos líneas lo que hacemos es definir cuál es el directorio donde vamos a guardar los entornos y en la que sigue estamos ejecutando el script de inicialización de virtualenvwrapper. Recuerda que podrías poner cualquier ruta del sistema por ejemplo:
export WORKON_HOME=/tmp/entornos
o
export WORKON_HOME=/home/andres/proyectos/entornos
Después de esto es necesario recargar las variables de entorno, si editamos el archivo .bashrc lo hacemos así:
source ~/.bashrc
si editamos el archivo .bash_history lo hacemos así:
source ~/.bash_history
Nota: También podrías simplemente cerrar y reabrir la consola, así se recargaría automáticamente.

Para Sistemas Windows:
Después de tener instalado Virtualenv en Windows (recuerda el post anterior) debes instalar desde la linea de comandos (CMD) virtualenvwrapper mediante la orden:
pip install virtualenvwrapper-win
después debes crear la carpeta para los entornos así:
mkdir %USERPROFILE%\entornos
y luego debes establecer las variables en el entorno así:
  • Entra al menú de propiedades:
    • Accede al menú de Inicio, click derecho al botón Equipo, y click al botón de propiedades.
    • Click al botón de configuración avanzada del sistema.
    • Click a el botón variables de entorno.
    • En la sección de Variables de entorno agregamos una variable llamada WORKON_HOME con el valor %USERPROFILE%\entornos.
La próxima vez que abramos una consola ya podremos usar virtualenvwrapper.

Nota: Si estás en Windows debes instalar virtualenvwrapper-win y si quieres y tienes Powershell virtualenvwrapper-powershell porque el paquete virtualenvwrapper es exclusivo de sistemas *nix.

Nota: El paquete virtualenvwrapper-win tiene como ruta por defecto de los entornos la carpeta %USERPROFILE%\Envs, por tanto si quieres obviar la configuración de la variable de entorno podrías hacerlo.

Nota: En ocasiones la instalación de virtualenvwrapper-win mediante pip no establece los ejecutables necesarios en la carpeta de scripts de python, por lo cual deberá instalarse descargando el código fuente mediante Git así:
git clone git://github.com/davidmarble/virtualenvwrapper-win.git
cd virtualenvwrapper-win
python setup.py install

Nota: es probable que al usar la orden mkvirtualenv salga el mensaje:
"El sistema no puede encontrar la ruta especificada."
no te preocupes por eso, el entorno se crea de todas maneras.


Modo de empleo:
Para crear entornos nuevos usamos:
mkvirtualenv <entorno> # entorno estandar
mkvirtualenv <entorno1> --no-site-packages # entorno totalmente aislado
Para activar el entorno:
workon <entorno>
Para desactivarlo:
deactivate
Para listar los entornos existentes:
lsvirtualenv
Para eliminar un entorno (recuerde que no puede estar usándolo en el momento de eliminarlo)
rmvirtualenv <entorno>
Para copiar un entorno:
cpvirtualenv <entorno> <nombre-copia>
Para ejecutar un comando en todos los entornos:
allvirtualenv <comando>
# un ejemplo sería:
allvirtualenv pip install django
Este es un listado de comandos básicos para la gestión de entornos y en este enlace queda la documentación de los comando de virtualenvwrapper.

Espero que les sirva de ayuda.
Si te gusto el post
compartelo... :D

jueves, 21 de noviembre de 2013

Git, qué, por qué y cómo


Carpetas, carpetas y más carpetas, eso es lo que uno encuentra en su disco duro cuando aún se es joven en el mundo de la informática ( todos lo hemos sido y en lo personal yo aún me siento así), pero es algo que todos empezamos a mejorar, clasificando la información de mejor manera y usando herramientas para esto. Los desarrolladores de software no son la excepción a esta regla, pero los problemas son un tanto más complejos ahora, puesto que no es solo guardar las carpetas con cientos o miles de líneas, además deben poder usarse comentarios específicos de cada carpeta o archivo, sin contar el hecho de que en cualquier momento podría fallar el disco duro y todo el trabajo se iría al mismísimo infierno (y muchos desarrolladores también pues sus jefes los deportarían). Para solucionar esto se crearon sistemas de control de versiones, en esta ocasión vamos a hablar de uno algo reciente pero muy potente, Git.

¿Qué es Git?
Git es un sistema de control de versiones distribuido, creado por Linus Torvalds con el fin de obtener la mayor eficiencia y la mejor confiabilidad posible. Se dice que es distribuido porque a diferencia de otros como SVN no hay un repositorio central contra el cual otros cargan y descargan actualizaciones,  por el contrario hay múltiples repositorios, unos se configuran como maestros y otros como esclavos y existe la posibilidad de cambiar la configuración en cualquier momento.

svn
Sistema de versiones Centralizado


git
Sistema de versiones distribuido
Como vemos en el gráfico 1 en sistemas centralizados de control de versiones como SVN todas las máquinas se sincronizan con una única máquina servidor, por el contrario en sistemas distribuidos como GIT cada una de las máquinas se pueden sincronizar con otras, y a su vez cada una de estas puede ser el repositorio maestro de otras.

Cuando existe un repositorio Git este tiene un repositorio local, el cual puede tener varias versiones de nuestro proyecto, a esto se le conocen como ramas (branches), de esta manera en un proyecto puede haber una rama estable, una de pruebas, una de desarrollo y demás.

¿Por qué?
Si quieres motivos para usar Git aquí los tienes:
  • Es distribuido. Gracias a esto obtienes:
    • Independencia. Tú eres un repositorio, no necesitas ninguna información de ningún otro lugar para ninguna operación. Es decir, no necesitas para nada red.
    • Mejor integración y comunicación en grupos de desarrollo grandes, de esta manera cada desarrollador puede compartir cambios con el resto sin depender de un servidor central.
  • Rapidez: Una de las principales características de Git es la velocidad que tiene para hacer todo lo que hace.
    • Manejo simple e intuitivo de las ramas de desarrollo:
    • Git permite mezclar varias ramas a la vez en una tercera (merge octopus).
    • Git permite mezclar eficientemente ramas que tenían como punto en común es un antiguo padre.
  • Git guarda contenido, más no ficheros, esto permite mayor eficiencia la hora de almacenar cambios pero obliga a que en el repositorio no hayan carpetas vacías (no tiene contenido alguno).
  • Permite trabajar en un punto intermedio entre nuestros ficheros reales y el repositorio, lo cual permite hacer modificaciones y más antes de que los cambios sean replicados.
  • Soporta múltiples protocolos de conexión a la hora de hacer publicación (HTTP, FTP, SSH, rsync).
  • Es compatible con otros sistemas de versiones como CVS y SVN.
  • Podrías pagar  por un servidor exclusivo para un proyecto y este te daría información relevante acerca del ritmo de trabajo, Github
¿Cómo?
A continuación pondré una lista de comando que pueden ser muy útiles.

Inicializar Repositorio
$ git init .
Agregar archivos
$ git add <file>
$ git add .
Actualizar archivos modificados
$ git add -u
Crear revisión inicial | Crear nueva revisión
$ git commit -m "mensaje de revisión"
Listar cambios realizados
$ git status
Listar los cambios del formato de parche
$ git diff
Ver historial de cambios
$ git log
$ git log -nX   #=> muestra las ultimas X revisiones
$ git log --stat --summary
Descartar todos los cambios hechos desde la última revisión
$ git reset --hard
Cargar archivo desde la última revisión
$ git checkout 
Corregir descripcion del último commit
$ git commit --amend
Incluir algo que faltó en la última revisión
$ git reset --soft HEAD^
$ git add arch1 arch2 ...
$ git commit
Crear un clon de un repositorio remoto
$ git clone <url del repositorio>
# por ejemplo
git clone ssh://usuario@mi.servidor.algo/ruta/interna/del/repositorio
Agregar origen remoto
$ git remote add origin <url del repositorio>
Sincronizar cambios con el repositorio remoto
$ git push origin master
Configurar usuario y correo para tener información de los commits
$ git config --global user.email "you@example.com"
$ git config --global user.name "Your Name"
Actualizar repositorio local
$ git pull origin master

Mostrar mensajes a color
$ git config --global color.ui auto
Evitar que nuestro servidor rachase commits externos
! [remote rejected] master -> master (branch is currently checked out)
$ git config receive.denyCurrentBranch ignore


Espero que les sirva de ayuda.

Si te gusto el post
compartelo... :D

martes, 19 de noviembre de 2013

Virtualenv, qué, por qué y cómo.


Cuando alguien empieza en el mundo de python lo que hace es crear sus programas e instalar librerías indiscriminadamente, sin tener en cuenta que puede que más adelante tenga conflictos a causa de las versiones de las librerías necesarias para sus proyecto. Es aquí donde Virtualenv salva el día.



¿Qué es Virtualenv?:
Virtualenv es una herramienta para crear entornos de python aislados, es decir entornos donde las librerías o las versiones de python no interfieren con las carpetas que python tiene por defecto en la maáquina. Haciendo una analogía con un edificio, un entorno vendría siendo como una planta, usa ciertos recursos como el agua o la energía eléctrica (para el caso de python usa la misma máquina) y a su vez cada planta tiene sus propios recursos, tales como los muebles, las habitaciones y demás (para el caso de python hablamos de librerías.).

¿Por qué?:
Cuando tienes python en tu máquina probablemente debas usar django 1.5 para un proyecto que surgio hace tiempo, y django1.6 para poder usar nuevas funciones, por esto es necesario usar Virtualenv, porque con ésta herramienta puedes crear espacios de trabajo separados con diferentes librerías o diferentes versiones de las mismas.

¿Cómo?:
Instalación:
primero debes tener python, luego instalas setuptools para poder usar easy_install, con easy_install instalas pip mediante la orden
easy install pip
y finalmente con pip instalas virtualenv
pip install virtualenv

nota: si usas Windows deberías agregar la carpeta de scripts de Python al path del sistema para poder usarlo desde la línea de comandos, mira este enlace.

Modo de empleo:

  • Crear un entorno 
    • virtualenv nombre_del_entorno 
  • Crear un entorno totalmente aislado virtualenv --no-site-packages nombre_del_entorno 
  • Usar un entorno especifico:
    • Hay que tener en cuenta que si es windows se hace de una manera un tanto diferente, puesto que en la carpeta del entono el genera una subcarpeta llamada Scripts y ahí está ubicado un archivo llamado activate.bat, mediante este se activa el entorno, entonces:
      • Para Windows (mediante la linea de comandos):
        • C:\.....\carpeta> ruta\a\la\carpeta\del\entorno\Scripts\activate.bat 
      • Para *nix [Mac, Linux] (desde la consola):
        • source /ruta/a/la/carpeta/del/entono/bin/activate 
    • nota: si el entorno quedo bien activado al principio de la linea de la consola debe aparecer entre parentesis el nombre del entorno, mas o menos así: 
      • (entorno)ogranada@maquina:~/python$ 
  • Para buscar paquetes 
    • pip search 
  • Instalar un paquete 
    • pip install paquete 
  • Eliminar paquete 
    • pip uninstall paquete 
  • Listar paquetes instalados (sirve para replicar el entono) 
    • solo listar 
      • pip freeze 
    • hacer archivo para replicar entorno 
      • pip freeze > requirements.txt 
  • Salir del entorno
    • deactivate



Espero que les sirva de ayuda.

Si te gusto el post
compartelo... :D

miércoles, 23 de octubre de 2013

Anotaciones en Java (Una notita para ayudarme)


Java es un lenguaje muy maduro y se ha dado a conocer por ofrecer posibilidades que otros lenguajes un poco más complejos no ofrecen, y una de las cosas que me han parecido más interesantes es la posibilidad de usar anotaciones para añadir meta-datos a un código fuente y que estos estén disponibles en tiempo de ejecución. Mediante esta herramienta es posible dejar en algunos casos de lado el uso de XML.


Okay, pero ¿Qué es una anotación?
Una anotación es una linea de código que se pone atrás de la sentencia, el método, la clase, el campo, el parámetro o el paquete al que se le quieren agregar los meta-datos, esta inicia con un carácter arroba (@) seguida de una interfaz para anotación y finalmente los parámetros de la anotación, mediante los cuales se asignan los meta-datos. Esto se ve así:

package test;

@SuppressWarnings("deprecation")
public class MiClase{

    @InicializadorDeArreglo(longitud=20,valorInicial="Hola")
    String miArreglo;

    @Override
    public void toString(){
        return "MiClase...";
    }
}

Como podemos ver en el ejemplo anterior hay dos anotaciones, una para la clase y otra para método, la primera (Linea 3) remueve las alertas generadas por código obsoleto, la segunda es una anotación creada como ejemplo y la tercera (Linea 9) se usa para especificar que un método no es propio de la clase actual, y que esta siendo reescrito (sobrecargado).

¿Y como creó mis propias anotaciones?
Una anotación no es más que una interfaz, pero tiene algo extra que permite que sea usada como anotación. veamos un ejemplo:
package test;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface InicializadorDeArreglo{
    int longitud();
    Object valorInicial() default null;
}

Como vemos en el ejemplo una anotación se crea de la misma manera que una interfaz, pero se usa la palabra reservada @interfaz. Además se pueden usar valores por defecto para los atributos mediante la palabra reservada default. Una de las cosas que se deben tener en cuenta a la hora de hacer una anotación es para que tipo de campos se debe poder usar, en este caso la anotación solo sera usada para atributos de clase, por eso se pone como target ElementType.FIELD. La sentencia @Retention(RetentionPolicy.RUNTIME) determina cuanto tiempo deben estar disponibles los datos de la anotación, para el caso RetentionPolicy.RUNTIME la anotación estará disponible a lo largo de toda la ejecución de la aplicación.

Muy bien, pero ¿Cómo accedo a los meta-datos?
Para aceder a los meta-datos es necesario usar Java Reflection, a continuación veremos un metodo que usa Reflection para inicializar los atributos con la anotación @InicializadorDeArreglo:

package test;


public class Inicializador{

    public static void inicializar(Object obj){
        Field[] f = obj.getClass().getDeclaredFields();
        for (Field a : f) {
            InicializadorDeArreglo i = a.getAnnotation(
                             InicializadorDeArreglo.class);
            if (i != null) {
                a.setAccessible(true);
                try {
                    String []array = new String[i.longitud()];
                    for(int it=0; it<i.longitud(); it++)
                        array[it] = i.valorInicial();
                    a.set(obj, array);
                } catch (Exception e) {
                    System.err.println("Error: " + e.getMessage());
                }
            }
        }
    }

}

Como vemos en el ejemplo lo que se hace es tomar mediante Java Reflection los atributos de la clase y filtrar aquellos que tengan un tipo especifico de anotación (lineas 9, 10 y 11), para este caso la clase de la anotación es InicializadorDeArreglo. Luego creamos el valor que sera asignado al atributo (linea 14), después se asigna el valor por defecto a cada celda del arreglo (lineas 15 y 16 ) y finalmente se asigna el valor (17). El proceso de asignación se ejecuta adentro de un try-catch para que si el tipo del atributo de destino es diferente de el valor a asignar no se detenga toda la aplicación.

Espero que les sea útil, si les ha gustado compartan y comentes, hasta una próxima publicación.

lunes, 30 de septiembre de 2013

Aprendiendo Dart (I)


Es difícil evolucionar a la velocidad de la tecnología, pero hay que intentar hacerlo. Puede que Dart sea en algún tiempo una alternativa en el mundo de la web frente al famoso JavaScript, entonces vamos a aprender un poco de éste.

Palabras reservadas:
abstract continue factory import return try
as default false in set typedef
assert do final is static var
break dynamic finally library super void
case else for new switch while
catch export get null this with
class external if operator throw
const extends implements part true
Variables:

En Dart las variables se declaran así: 

var numero = 1;

O así:
int numero = 1;

Para el primer caso la variable es dinámica; para el segundo la variable es apta para números enteros, esto permite que herramientas como compiladores y editores le ayuden durante el desarrollado a evitar errores en su programa.

Finales y Constantes:

A diferencia de JavaScript, Dart permite variables finales y constantes así:
final String name_bob = 'Bob';

final name = 'Bob';

const PI = 3.141594;


Tipos:

  • dynamic: Tipo dinámico de datos
    • var x = 1;
  • int: Tipo numérico de datos, almacena números enteros. Hereda de la clase num.
    • var x = 1;
  • double: Tipo numérico de datos, almacena números con doble precisión (punto flotante) de 64 bits  . Hereda de la clase num.
    • var x = 1;
  • String: Tipo de datos para cadenas de texto, usa codificación UTF-16. Permite usar comillas dobles o simples, además de que cuando ponen tres comillas (dobles o sencillas) al principio y final de cada cadena permiten cadenas multilínea: 
    • String cadena1 = "Hola Mundo!!!";
      String cadena2 = 'Hola Nuevo Mundo!!!';
      String cadena3 = """Esta cadena
      permite multiples
      lineas en una cadena""";
      String cadena4 = '''Y esta cadena
      Tambien permite
      varias lineas''';
      
  • Booleans: Tipo de datos de valores de verdad, tiene dos literales, true y false, además  a diferencia de JavaScript cualquier valor diferente a true es falso: 
    • boolean b = true;
      if(b){
          print(b);
      }
      
  • Lists: Tipo para colección de datos
    • List lista = new List();
      List lista = [1,2,3,4];
  • Maps: Tipo de datos para arreglos asociativos (llave:valor), permite la asignación de nuevos valores en tiempo de ejecución de la misma manera que JavaScript
    • Map mapa = {"Hola":"Mundo"};
      mapa["Nuevo_Valor"]="No Toques mi Código";
      
Ésta fue una muy breve introducción al lenguaje, todo fue extraído de la documentación oficial del lenguaje. espero que les guste y continuaremos en una próxima ocasión.

sábado, 28 de septiembre de 2013

El molde de la taza, Java y las interfaces


Es fácil hacer  en Java un programa con un par de clases, sin embargo la potencia de un lenguaje como éste se ve a la hora de usar la artillería orientada a objetos. En general hay conceptos complejos dentro de la programación orientada a objetos, y dos que especialmente no son muy fáciles de entender, las interfaces y las clases abstractas, esta vez explicaré qué es una interfaz.


Hagamos un molde
¿Un molde?, ¿Qué tiene que ver un molde y una interfaz?. Es extraña la manera en que comparo esto, pero si nos detenemos a pensarlo un poco puede que sea claro. Una interfaz es una estructura de datos que contiene la definición (también se le llama contrato) de los métodos que debería tener una clase o su descendencia pero éstas no tienen lógica alguna, es decir, una interfaz es una guía de cómo debería lucir una clase, pero no es propiamente la implementación de la misma, es decir, es el molde de cómo debería ser una clase, a continuación veamos un ejemplo de esto: 

// Ejemplo de Interfaces
interface MoldeTaza{
    public void Servir(Object contenido);
    public void Vaciar();
}
// fin

Como vemos en el ejemplo se crea la interfaz y a su vez en esta se declaran los métodos, pero no se escribe el contenido de los mismos, únicamente se pone un punto y coma para reemplazar el cuerpo del método. Ahora bien, después de tener nuestro molde vamos a crear cosas con el.
// Ejemplo de Interfaces
class TazaDeCafe implements MoldeTaza{

    Object contenido;

    @Override
    public void Servir(Object contenido){
        this.contenido = contenido;
        System.out.println("El café esta servido...");
    }

    @Override
    public void Vaciar(){
        this.contenido = null;
        System.out.println("Se ha terminado el café...");
    }

}
// fin

Como vemos en el código anterior la clase TazaDeCafe implementa la interfaz MoldeTaza (Los métodos de las interfaces no se heredan, se implementan), es decir, los métodos que se describieron en la interfaz se reescriben con el cuerpo que corresponde de acuerdo a la clase, además se pone la anotación Override, que especifíca que el método no es propio de esta clase puesto que la primera vez que se escribió fue en una clase padre o en un interfaz que ha sido implementada. 

Y ¿Para qué usar moldes?
Tal vez una de las cosas más complejas de las interfaces no son en sí cómo hacerlas, lo que realmente las hace complejas es saber cuándo y por qué usarlas. Para entender esto debemos partir de un caso de prueba. Supongan que están haciendo un juego de ajedrez, todos sabes que hay un tablero en el cual hay varios tipos de fichas, todas estas son diferentes, se mueven diferente, se ven diferentes, pero al fin y al cabo no dejan de ser fichas. Este seria un caso ideal para usar interfaces, con una interfaz ficha definiremos todas las posibles acciones que tiene una ficha y cómo debe responder el tablero ante los objetos que implementan ésta interfaz, pero a la hora de hacer una ficha específica como el Caballo se especificarán la manera en que debe ejecutar las acciones este objeto.

Espero que les sea de utilidad.

jueves, 26 de septiembre de 2013

En el blanco. Dart, otra opción para la web


Hasta el momento la mejor y única opción para desarrollar aplicaciones web es JavaScript, y con la flamante puesta en marcha de Html5 las dudas al respecto son cada vez menos, sin embargo hace ya casi dos años Google trajo a la luz un lenguaje con el que esperaba entregar una nueva alternativa, Dart.

¿Qué es Dart?
Dart (inicialmente Dash) es un lenguaje programación que no tiene como objetivo reemplazar JavaScript, pero sí ofrecer una alternativa más moderna. Dart es un lenguaje orientado a objetos, muy similar a java en muchos aspectos pero con la posibilidad de ser tan dinámico como JavaScript, es tipado pero tiene un tipo especial llamado dynamic, el cual permite crear variables sin especificar su tipo, tal como en JavaScript.

Chromium incluye una máquina virtual para ejecutar Dart, Google anunció que proveerá máquina virtual a sus competidores para permitir que usen el lenguaje en otros navegadores y además de esto existe la posibilidad de compilar los programas de Dart a JavaScript.

A probar puntería
Muy bien, ahora probemos un poco esto, primero es necesario descargar el sdk de Dart, pueden hacerlo desde éste lugar, el cual trae consigo eclipse y una versión ligera de Chromium, después de esto crean un nuevo proyecto, puede ser web o para una aplicación de consola (línea de comandos), el siguiente código muestra un poco del lenguaje:
// Inicio del ejemplo
// tets.dart
import 'dart:html';


class Auto{
  var _name; // variable privada tipo dynamic
  
  void setName(var nombre){
    this._name = nombre;
  }
  
  String toString(){
    return _name;
  }
  
  void clicked(Object o){
    window.alert("Hola, Soy un Chevette...");
  }
  
}

void main() {
  Auto chevette = new Auto();
  chevette.setName("Chevette");
  query("#auto")..text = "Auto: ${chevette.toString()} "
  ..onClick.listen(chevette.clicked);
}
// Fin

A nivel de lenguaje no hay mucha diferencia entre Dart y otros lenguajes imperativos orientados a objetos como Java, para hacer comentarios se usa la secuencia "//", las llaves, los paréntesis y más, pero hay cosas que hacen de éste un lenguaje que simplifican el desarrollo de una aplicación, como la eliminación de las palabras reservadas public y private, características de los lenguajes orientados a objetos, en éste todo es público por defecto y se convierten en privados cuando el nombre inicia con "_".
Como vemos en el código anterior existe una clase llamada Auto, ésta tiene un atributo privado llamado _name, y hay un método para cambiar el valor de _name, y hay un método que retorna un objeto tipo cadena que retorna el contenido de la variable _name. Además de esto podemos modificar el DOM mediante la orden query, ésta fue importada de la librería html de Dart y de una manera muy similar a la de JQuery obtenemos elementos con un id específico mediante el carácter '#'.
Dart al igual que Java, C y C++ usa el método main para iniciar el programa.
Una de las cosas extra es que Dart permite hacer múltiples operaciones sobre el mismo elemento del dom mediante el operador '..' , un ejemplo claro es el contenido del método main.

A continuación el código HTML que acompaña el código anterior:

<!-- Inicio del ejemplo -->
<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Test</title>
    <link rel="stylesheet" href="test.css">
  </head>
  <body>
    <h1>Hello world from Dart!</h1>
    
    <div id="auto">      
    </div>
    
    <script type="application/dart" src="test.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>
<!-- Fin del ejemplo -->

Hacer esto no es complejo y gracias al IDE que provee la página web del proyecto,  éste IDE permite escribir un proyecto, depurarlo y finalmente generar todo el código JavaScript.

Interesante propuesta de Google, tendremos que esperar para ver qué ocurre con éste prometedor lenguaje.

miércoles, 28 de agosto de 2013

Mi propio navegador web en Android

En uno de los artículos anteriores hicimos un navegador web con Python y PySide, así que en esta entrada vamos a hacer algo parecido con Android.

Primero lo primero, un nuevo  proyecto
Creamos un nuevo proyecto de Android, éste debe tener una actividad principal sobre la cual trabajaremos, en este caso el nombre de la actividad es Inicio.

Ahora, el diseño
En la carpeta layout abrimos el layout asociado a la actividad principal, en este caso su nombre es activity_inicio.xml

El layout debe tener un EditText para la url de  destino, y un WebView así:

Además de esto es necesario editar el archivo XML del menú, para poner los botones atrás y adelante.

Y final mente el código en Java:
public class Inicio extends Activity {

    ProgressDialog progressBar;
    private int progressBarStatus = 0;
    private Handler progressBarHandler = new Handler();
    EditText txtUrl = null;

    public final String HOME = "http://notoquesmicodigo.blogspot.com/";

    private class MiWebViewClient extends WebViewClient {

        private void iniciaCarga(WebView v) {
            if(progressBar==null){
                progressBar = new ProgressDialog(v.getContext());
            }
            if (progressBar != null && !progressBar.isShowing()) {
                progressBar.setTitle("Espere...");
                progressBar.setMessage("Cargando página...");
                progressBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                progressBar.setCancelable(false);
                pbShow();
                progressBarStatus = 0;
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                    progressBarHandler.post(new Runnable() {
                    public void run() {
                        try {
                             progressBar
                              .setProgress(progressBarStatus++);
                        } catch (Exception e) {
                        }
                      }
                     });
                    }
                });
            }
        }

        private void pbCancel() {
            try {
                progressBar.cancel();
            } catch (Exception e) {
                Log.e("ERROR", e.getMessage());
            }
        }

        private void pbShow() {
            try {
                progressBar.show();
            } catch (Exception e) {
                Log.e("ERROR", e.getMessage());
            }
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            iniciaCarga(view);
            if (txtUrl != null) {
                txtUrl.setText(url);
            }
            super.onPageStarted(view, url, favicon);
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            pbCancel();
        }
    }

    private class MiChromeWebViewClient extends WebChromeClient {

    }

    WebView wv_pagina;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inicio);
        wv_pagina = (WebView) findViewById(R.id.wv_pagina);
        txtUrl = (EditText) findViewById(R.id.txtUrl);
        wv_pagina.clearHistory();
        wv_pagina.clearCache(true);
        wv_pagina.setWebViewClient(new MiWebViewClient());
        wv_pagina.setWebChromeClient(new MiChromeWebViewClient());
        wv_pagina.getSettings().setJavaScriptEnabled(true);
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                wv_pagina.loadUrl(HOME);
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_inicio, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (wv_pagina != null) {
            switch (item.getItemId()) {
            case R.id.menu_back:
                if (wv_pagina.canGoBack()) {
                    wv_pagina.goBack();
                }
                break;
            case R.id.menu_forward:
                if (wv_pagina.canGoForward()) {
                    wv_pagina.goForward();
                }
                break;
            }
            return true;
        } else {
            return super.onOptionsItemSelected(item);
        }
    }

}

Ahora bien, debemos tener en cuenta varias cosas:

  • Para que nuestro navegador no lance otro de los navegadores del celular (como Chrome por ejemplo) debemos crear clases propias que hereden de WebViewClient y WebChromeClient.
  • La mayor parte del código esta en la clase MiWebViewClient para el ProgressDialog que usamos.
  • En la clase MiWebViewClient sobrecargamos los métodos onPageStarted y onPageFinished para poder lanzar el ProgressDialog y cambiar la URL en la caja de texto de la dirección.
  • En el evento onOptionsItemSelected ponemos los eventos de atrás y adelante filtrando los elementos menu_back y menu_forward.
El resultado es éste:


El código esta disponible en GitHub, si tiene alguna duda pueden preguntar en los comentarios.

martes, 27 de agosto de 2013

Consoleando (III)

En artículos pasados he intentado mostrar un poco de las potencia de la consola, pero en realidad la potencia de la consola de los sistemas basados en Unix está en la posibilidad de tener muchísimos pequeños programas que se enlazan de manera mutua para hacer una labor compleja, por éste motivo existen las tuberías o Pipes.

Entre un Tubo...
El uso de tuberías o Pipes permite que el usuario ejecute un proceso y con la información de salida de éste alimente un nuevo proceso, de esta manera se pueden encadenar de manera sucesiva la cantidad de tareas que se desee. El operador encargado de las tuberías es la barra vertical ( | ), ésta es usada en otros lenguajes como el operador de bits OR, para el caso de bash ésta se usa como operador de tubería.

Y, ¿Cómo se usa?
Su uso es sencillo: Basta con separar los procesos con la barra vertical ( | ), lo cual hará que la salida del primer proceso ejecutado sea la entrada del segundo proceso, así:
ps -ef | grep root
En éste ejemplo podemos ver dos instrucciones:
La primera es ps con el argumento -ef, de ésta manera se hace una lista de todos los procesos existentes y muestra muchos de los datos relevantes del proceso, entre estos el comando con el que fue lanzado.
La segunda instrucción es grep con el argumento root, ésta instrucción filtra todas las líneas que contengan la palabra root, de manera que al unir ambas tareas mediante una tubería se mostrarán todos los procesos que contengan la palabra root, es decir, se filtrarán los procesos del usuario root y aquellos que en la línea de comandos tengan ésta palabra.

Un ejemplo más
¿Cómo cerrar un proceso de acuerdo al nombre?, esa es una de las tareas que podrían ser automatizadas. Por ejemplo, si hacemos una aplicación cliente-servidor que internamente usa la instrucción fork (ésta instrucción crea copias del proceso original) podríamos eliminar todas las instancias de la aplicación mediante el uso de las siguientes instrucciones:

  • ps : Lista todos los procesos de la máquina.
  • grep : Filtra las aplicaciones de acuerdo a un patrón especificado.
  • awk : Permite hacer operaciones sobre cadenas, como separarlas por palabras (tokenizarlas) y más.
  • xargs : Pasa los parametros que recibe mediante la tubería a una instrucción especificada.
  • kill : Termina una aplicación.
y las instrucciones se encadenarían así (en una sola línea):

ps -ef | grep NombreProceso | grep -v grep | awk '{print $2}' | xargs kill

La anterior secuencia de comandos y tuberías funciona así:

  1. ps -ef : Lista todos los procesos de la máquina.
  2. grep NombreProceso : Filtra todos los procesos que contengan la subcadena NombreProceso.
  3. grep -v grep : Quita las líneas que contengan la cadena grep (para que no se incluya el proceso del filtro anterior).
  4. awk '{print $2}' : Entrega únicamente el segundo token de cada línea, para éste caso es el número de identificación de cada proceso.
  5. xargs kill : Ejecuta el comando kill sobre cada uno de los números de proceso entregados.
Una de las grandes ventajas de la consola es que mediante el uso de tuberías se pueden automatizar procesos complejos de administración de máquinas y demás.

Espero que les sea de utilidad.

viernes, 2 de agosto de 2013

Muy rápido, muy fácil, reproductor de audio con Python (Ahora con video)

En el articulo pasado hicimos un reproductor de audio, este únicamente reproducía pistas de audio, en esta entrega vamos a hacer que mediante un modulo nuevo y unas cuantas lineas extra sea posible reproducir vídeo.

Qt es un framework que ofrece herramientas muy poderosas, una de estas es el modulo Phonon, el cual nos da la posibilidad de reproducir audio y vídeo, para este fin usaremos el widget VideoWidget, el cual viene incluido dentro del grupo de widgets de la librería Phonon. el VideoWidget será enlazado a un AudioObject  y a un MediaObject, este ultimo sera el encargado de cambiar el archivo fuente e iniciar la reproducción.
Todo lo anterior sera agrupado en una clase para intentar hacer esto un poco más modular y poder reusarlo de ser necesario.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
from PySide.QtGui import QApplication
from PySide.phonon import Phonon
from PySide import QtCore, QtGui


class player(Phonon.VideoWidget):

    def __init__(self, file_path):
        super(player, self).__init__(parent=None)
        self.setWindowTitle("Video")
        QtGui.QApplication.addLibraryPath(
            os.path.join(os.path.dirname(__file__), 'plugins'))
        media_src = Phonon.MediaSource(file_path)
        self.file_path = file_path
        media_obj = Phonon.MediaObject()
        media_obj.setCurrentSource(media_src)
        Phonon.createPath(media_obj, self)
        self.media_obj = media_obj
        audio_out = Phonon.AudioOutput(Phonon.VideoCategory)
        Phonon.createPath(media_obj, audio_out)
        self.audio_out = audio_out
        self.media_obj.setTickInterval(1000)
        self.media_obj.tick.connect(self.alAvanzar)
        self.media_obj.stateChanged.connect(self.alCambiarEstado)

    def alAvanzar(self, tiempo):
        pass

    def alCambiarEstado(self, new, old):
        pass

    def play(self):
        self.media_obj.play()

    def pause(self):
        self.media_obj.pause()

    def stop(self):
        self.media_obj.stop()

    def state(self):
        return self.media_obj.state()

En el código anterior vemos como una la clase player hereda de la clase Phonon.VideoWidget y tiene como atributos un MediaObject y un AudioOutput, los cuales sirven como control de reproducción y salida de audio respectivamente, y los tres son enlazados en el constructor de la clase en la linea 21 y 24.

Ahora veremos la nueva versión del reproductor, con las mejoras respectivas para implementar nuestro player de vídeo:
from PySide import QtCore, QtGui, phonon, QtUiTools
from PySide.phonon import Phonon

from videopanel import player


class Reproductor(QtGui.QWidget):

    def __init__(self):
        super(Reproductor, self).__init__(parent=None)
        self.setWindowTitle("Python Simple Player")
        self.gridLayout = QtGui.QGridLayout(self)
        loader = QtUiTools.QUiLoader()
        file = QtCore.QFile("repro.ui")
        file.open(QtCore.QFile.ReadOnly)
        self.ui = loader.load(file, self)
        file.close()
        self.gridLayout.addWidget(self.ui, 0, 0, 1, 1)
        # seccion multimedia
        self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
        self.mediaObject = Phonon.MediaObject(self)
        Phonon.createPath(self.mediaObject, self.audioOutput)
        self.mediaObject.setTickInterval(1000)
        #
        self.ui.lcdAvance.display("00:00")
        QtCore.QObject.connect(
            self.ui.btnArchivo,
            QtCore.SIGNAL("clicked()"),
            self.openFile)
        QtCore.QObject.connect(
            self.ui.btnPlay,
            QtCore.SIGNAL("clicked()"),
            self.play)
        self.mediaObject.tick.connect(self.alAvanzar)
        self.mediaObject.stateChanged.connect(self.alCambiarEstado)
        #----------------------
        self.player = None

    def alAvanzar(self, tiempo):
        displayTime = QtCore.QTime(
            0, (tiempo / 60000) %
            60, (tiempo / 1000) %
            60)
        self.ui.lcdAvance.display(displayTime.toString('mm:ss'))

    def alCambiarEstado(self, new, old):
        if self.mediaObject.state() == Phonon.State.StoppedState:
            self.ui.btnPlay.setEnabled(True)
        elif self.mediaObject.state() == Phonon.State.PlayingState:
            self.ui.btnPlay.setText("||")  # .setEnabled(False)

    def play(self):
        if self.player:
            if self.player.state() == Phonon.State.PlayingState:
                self.player.pause()
                self.player.show()
                self.ui.btnPlay.setText(">")
            else:
                self.player.show()
                self.player.play()
                self.ui.lblNombre.setText(self.player.file_path)
                self.ui.btnPlay.setText("||")
        else:
            if self.mediaObject.state() == Phonon.State.PlayingState:
                self.mediaObject.pause()
                self.ui.btnPlay.setText(">")
            else:
                self.mediaObject.play()
                #///////////////////////////////////
                meta = self.mediaObject.metaData()
                print(meta)
                meta["TITLE"] = meta[
                    "TITLE"] if "TITLE" in meta else "Indefinido"
                meta["ARTIST"] = meta[
                    "ARTIST"] if "ARTIST" in meta else "SinNombre"
                label = "{TITLE} by {ARTIST}".format(**meta)
                self.ui.lblNombre.setText(label)
                #///////////////////////////////////
                self.ui.btnPlay.setText("||")

    def openFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(
            self,
            u"Abrir Archivo",
            u".",
            u"Archivos de Audio (*.mp3 *.wav *.ogg);;Archivos de Video (*.mp4 *.mpg *.avi)")
        if fileName[1]:
            self.path = fileName[0]
            self.mediaObject.setCurrentSource(
                Phonon.MediaSource(self.path))
            self.ui.btnPlay.setText(">")
            self.ui.lblNombre.setText("...")
            self.ui.lcdAvance.display("00:00")
            if fileName[0][-3:] in "mp4 mpg avi".split():
                self.player = player(fileName[0])
                self.player.alAvanzar = self.alAvanzar
                self.player.alCambiarEstado = self.alCambiarEstado
            else:
                self.player = None

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    rep = Reproductor()
    rep.show()
    sys.exit(app.exec_())

Ahora lo primero que vemos es una modificación en el constructor, en el cual creamos una variable de clase llamada player que es nula (linea 38). Luego vemos en el método play una condición en la cual si el player es diferente de nulo llama al objeto player para que este inicie la reproducción o la detenga. Finalmente en el método openFile vemos que se verifica la extensión del archivo a reproducir, si es un archivo de vídeo la variable de clase player deja de ser nula y se convierte en una instancia de la clase player que ya habíamos hecho antes, de lo contrario sigue siendo nula.

Espero que les guste y que les sirva, dejo el código en GitHub, si tienen sugerencias o demás comenten.

jueves, 1 de agosto de 2013

Entendiendo Javascript (VI)


Hasta el momento todo lo abarcado en los artículos ha permitido entender un poco mejor muchos de los pormenores de este interesante pero complejo lenguaje, pero muy poco se ha hecho por entender cómo modificar lo que el usuario utiliza, qué es en esencia lo que más necesita un usuario JavaScript, por esto en esta entrega vamos a empezar con la exploración de el objeto donde se almacena el contenido de la página, el objeto document.

Peleando con el documento
Ya habíamos hablado acerca de que en el momento en el que el navegador carga una página web toma todo el contenido HTML y lo convierte en objetos, estos objetos en realidad son nodos "manipulables", que a su vez contienen otros nodos. A esto se le llama document. Es muy importante resaltar que todo lo que hay en el document es un nodo, a continuación vamos a ver un ejemplo de esto.
<html>
    <head>
        <title>El Documento...</title>
    </head>
    <body>
        <h1>El Documento</h1>
        <div>Este es un ejemplo...</div>
    </body>
</html>

Si hiciéramos un esquema a manera de árbol de cómo se ve este HTML tendríamos:
De esta manera vemos como el nodo html es el padre inmediato de head y body, head es el padre de title y title tiene una propiedad llamada text, cuyo valor es "El documento...". body es el padre de h1 y div, los cuales a su vez tienen cada uno como propiedad text "El Documento" y "Ejemplo de documento..." respectivamente.

Muy bonito pero...
¿Para qué nos sirve entender esto?, la idea de entender esto es poder desplazarnos por el árbol sin depender únicamente de los identificadores, las etiquetas o los nombres.

Esta bien, y ¿Cómo me desplazo?
Esa es la parte divertida, porque para desplazarnos el objeto document nos ofrece unas herramientas, a continuación vamos a ver algunas de estas:
Orden Descripción
document.getElementById(valor) Retorna el elemento que en el atributo id tenga el valor especificado.
document.getElementsByName(nombre) Retorna una lista de todos los elementos que tengan el nombre especificado.
document.getElementsByTagName(tag) Retorna una lista de todos los elementos hechos con con el tag especificado

Y Para Terminar...
Vamos a hacer un ejemplo de una función que busca el título de nuestra página y lo cambia, otra que cambia el contenido de un div y su color de fondo y cada una va a ser asociada a un botón. Después del código esta el ejemplo.

<html>
    <head>
        <title>El Documento...</title>
        <script>
             function cambiarTituloPor(nuevoTitulo){
                document.getElementsByTagName("title")[0].innerHTML=nuevoTitulo;
             }
             function cambiarContenidoPor(nuevoContenido){
                var elemento = document.getElementById("modificable");
                elemento.innerHTML=nuevoContenido;
                var estilo = document.createAttribute("style");
                estilo.value = "background-color:#1662A8;";
                elemento.setAttributeNode(estilo);
             }
        </script>
    </head>
    <body>
        <h1>El Documento</h1>
        <div id="modificable">Este es un ejemplo...</div>
        <button onclick="cambiarTituloPor(prompt('Nuevo Título','Hola Mundo!!!'))">Cambiar Título</button>
        <button onclick="cambiarContenidoPor(prompt('Nuevo Contenido','Estoy aprendiendo JavaScript...'))">Cambiar Div</button>
    </body>
</html>

Este es un ejemplo...

martes, 30 de julio de 2013

Muy rápido, muy fácil, reproductor de audio con Python


En la entrada anterior pudimos ver cómo con muy poco código fue posible obtener un simple pero poderoso navegador web con el lenguaje de programación Python, PySide (Los bindings de Qt, uno de los  frameworks gráficos del lenguaje C++) y poco más de 70 líneas de código. En esta entrega veremos cómo usando el diseñador de interfaces y algo de código podemos hacer un muy simple reproductor de audio, el cual será extendido en próximas entregas.
Primero que todo es necesario decir que Qt ofrece una herramienta para diseñar interfaces de usuario. Esta se llama QtDesigner y viene incluida en el instalador de la libreria PySide de windows (para los entornos linux hay que instalarlo a mano). Esta herramienta es muy útil, puesto que con ella podemos hacer la interfaz y luego exportarla al lenguaje que vayamos a usar para realizar la aplicación (en la actualidad Qt tiene bindings para varios lenguajes, entre ellos Java, Ruby, Python y Php, entre otros), además de proveer un sistema de enlace entre objetos y métodos mediante eventos (en Qt se habla de slots y signals), lo cual facilita en cierta manera la tarea del programador. Esta herramienta es una herramienta WYSIWYG, la cual nos permite arrastrar, soltar y acomodar hasta obtener la interfaz como se desea. A continuación la imagen de la interfaz gráfica del reproductor:


La interfaz gráfica contiene unos cuantos QLabel para mostrar información, un QLCDNumber para mostrar cuánto tiempo ha transcurrido, un par de botones y unos espaciadores para conservar las proporciones al cambiar el tamaño de la ventana. A pesar de que sería posible "compilar" la interfaz a el lenguaje nativo con herramientas como el pyside-uic no lo vamos a hacer.

Ahora vamos a ver el código Python:

#!/usr/bin/env python

from PySide import QtCore, QtGui, phonon, QtUiTools
from PySide.phonon import Phonon

class Reproductor(QtGui.QWidget):

    def __init__(self):
        super(Reproductor, self).__init__(parent=None)
        self.setWindowTitle("Python Simple Player")
        self.gridLayout = QtGui.QGridLayout(self)
        loader = QtUiTools.QUiLoader()
        file = QtCore.QFile("repro.ui")
        file.open(QtCore.QFile.ReadOnly)
        self.ui = loader.load(file, self)
        file.close()
        self.gridLayout.addWidget(self.ui, 0, 0, 1, 1)
        # seccion multimedia
        self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
        self.mediaObject = Phonon.MediaObject(self)
        Phonon.createPath(self.mediaObject, self.audioOutput)
        self.mediaObject.setTickInterval(1000)
        #
        self.ui.lcdAvance.display("00:00")
        QtCore.QObject.connect(
            self.ui.btnArchivo,
            QtCore.SIGNAL("clicked()"),
            self.openFile)
        QtCore.QObject.connect(
            self.ui.btnPlay,
            QtCore.SIGNAL("clicked()"),
            self.play)
        self.mediaObject.tick.connect(self.alAvanzar)
        self.mediaObject.stateChanged.connect(self.alCambiarEstado)

    def alAvanzar(self, tiempo):
        displayTime = QtCore.QTime(
            0, (tiempo / 60000) %
            60, (tiempo / 1000) %
            60)
        self.ui.lcdAvance.display(displayTime.toString('mm:ss'))

    def alCambiarEstado(self, new, old):
        if self.mediaObject.state() == Phonon.State.StoppedState:
            self.ui.btnPlay.setEnabled(True)
        elif self.mediaObject.state() == Phonon.State.PlayingState:
            self.ui.btnPlay.setText("||")  # .setEnabled(False)

    def play(self):
        if self.mediaObject.state() == Phonon.State.PlayingState:
            self.mediaObject.pause()
            self.ui.btnPlay.setText(">")
        else:
            self.mediaObject.play()
            #///////////////////////////////////
            meta = self.mediaObject.metaData()
            meta["TITLE"] = meta["TITLE"] if "TITLE" in meta else "Indefinido"
            meta["ARTIST"] = meta[
                "ARTIST"] if "ARTIST" in meta else "SinNombre"
            label = "{TITLE} by {ARTIST}".format(**meta)
            self.ui.lblNombre.setText(label)
            #///////////////////////////////////
            self.ui.btnPlay.setText("||")

    def openFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(
            self,
            u"Abrir Archivo",
            u"/",
            u"Archivos de Audio (*.mp3 *.wav *.ogg)")
        if fileName[1]:
            self.path = fileName[0]
            self.mediaObject.setCurrentSource(
                Phonon.MediaSource(self.path))
            self.ui.btnPlay.setText(">")
            self.ui.lblNombre.setText("...")
            self.ui.lcdAvance.display("00:00")

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    rep = Reproductor()
    rep.show()
    sys.exit(app.exec_())

En el código anterior podemos ver cómo mediante el uso del modulo QtUiTools es posible usar la clase QUiLoader para cargar en tiempo de ejecución el archivo de interfaz gráfica (repro.ui), y obtenemos un objeto QWidget, el cual contiene toda la jerarquía de objetos que habíamos hecho en el diseñador de interfaces, los cuales quedan listos para ser usados a gusto. Después de esto acoplamos el QWidget obtenido mediante un gridLayout. Seguidamente creamos las instancias de la salida de audio (audioOutput) y del reproductor (mediaObject), los cuales son enlazados mediante la instrucción:
Phonon.createPath(self.mediaObject, self.audioOutput)
Esta instrucción enlaza el reproductor con la salida de audio. Después de esto se establece cada cuánto se va a lanzar una señal de actualización (es para que pueda medirse el tiempo transcurrido y hacer cosas de acuerdo a este) mediante el tickInterval, el cual se establece cada 1000 milisegundos (un segundo) y se conectan objetos como botones a los eventos correspondientes para que antes de finalizar el constructor se enlacen las señales del reproductor a los métodos encargados de modificar el avance y cambiar la etiqueta del botón de reproducción.
Los métodos siguientes se encargan de cambiar el valor de las etiquetas, de iniciar o detener la reproducción o cambiar el título de la pista.

 El código correspondiente esta en GitHub para quienes estén interesados.

lunes, 29 de julio de 2013

Entendiendo Javascript (V)


En el post anterior se intentó dar un vistazo general a el entorno de trabajo en JavaScript dentro de un navegador, mostrando cuáles eran sus propiedades principales, teniendo en cuenta que al iniciar la aplicación el entorno de trabajo está compuesto por todo lo que compone un objeto window, pero no sabemos mucho acerca de qué métodos o funciones podemos usar. A continuación veremos un poco de esto.
Recordemos, los procedimientos mostrados a continuación son métodos del objeto ventana y pueden ser invocados de manera directa puesto que el contexto de inicio de los scripts que se ejecutan en el navegador es la ventana misma.

  • alert(mensaje): Es uno de los procedimientos más usados, éste despliega un diálogo con un mensaje específico
  • close(): Cierra la ventana
  • confirm(mensaje): Lanza un diálogo de confirmación y dependiendo de la respuesta retorna true o false
  • open(url, nombre, parámetros, reemplazar):  Crea una nueva ventana en la cual carga la url, con el nombre y los parámetros especificados, y dependiendo del último valor la url especificada reemplazará la url de la ventana actual.
  • print(): Imprime el contenido de la página actual.
  • prompt(mensaje, valor): Crea un diálogo con una caja de texto, el diálogo tiene un mensaje especificado en el primer parámetro y como valor inicial el valor (valga la redundancia) especificado en el segundo parámetro.
  • setInterval(funcion_o_expr, milisecs): Llama a una función o evalúa una expresión cada cierto intervalo de milisegundos. se detiene cuando la función clearInterval(id) es llamada. Ésta retorna un identificador.
  • clearInterval(id): Detiene la ejecución periódica de la acción creada con setInterval a la que está asociada el id entregado.
  • setTimeout(): Llama a una función o evalúa una expresión después de un intervalo de tiempo especificado en milisegundos. Ésta retorna un identificador y es detenida por la función clearTimeout(id).
  • clearTimeout(id): Detiene la ejecución de la acción lanzada por setTimeout asociada al id entregado.
Muy bien, todo esto es muy bonito, pero, ¿Para qué podría usar todo esto?. A continuación vamos a hacer un pequeño ejemplo en el cual vamos a usar solo un par de los procedimientos que fueron descritos anteriormente. Vamos a hacer un cronómetro.

A continuación está el código HTML:

<html>
  <head>
    <title>Mi Cronometro</title>
    <style>
      #reloj {
        font-size:4em;
      }
    </style>
    <script src="crono.js"></script>
  </head> 
  <body>
    <script>
        var cronometro = new Cronometro();
    </script>
    <button onclick="cronometro.iniciar()">Inicio</button>
    <button onclick="cronometro.detener()">Detener</button>
    <button onclick="cronometro.reiniciar()">Reiniciar</button>
    

    <div id="reloj">
      0:0
    </div>
</body>
</html>

Y el código JavaScript:
function Cronometro(lugarCarga){
  var self = this;
  self.interval = 0;
  this.upd = function(){
    var secs = self.interval/100;
    var milis = self.interval%100;
    self.lugarCarga.innerHTML = Math.floor(secs)+":"+milis;
  }
  this.paso = function(){
    self.interval = self.interval + 1;
    self.upd();
  }
  this.iniciar = function(){
    self.lugarCarga = document.getElementById('reloj');
    self.id_intervalo = setInterval(self.paso,10);
  };
  this.detener = function(){
    clearInterval(self.id_intervalo);
  };
  this.reiniciar = function(){
    self.interval = 0;
    self.lugarCarga.innerHTML = "0:0";
  };
}


Pueden encontrar los códigos de ejemplo en GitHub.