viernes, 5 de diciembre de 2008

Código fuente lección No. 8

Aquí se encuentra el código fuente de la lección No. 8 así como los demás archivos:

http://www.mediafire.com/file/bqt01uyj2fi/L8.7z
http://www.mediafire.com/file/2entlj4tykz/Leccion No 8.odt
http://www.mediafire.com/file/qdnn1ndmym2/Leccion No 8.pdf
http://www.mediafire.com/file/nmmytmzfyr1/ReportLab.pdf

Lección No. 8

Nuevamente retomaremos la lección 6 con sus últimas modificaciones, o sea, la de la Lección No. 7, con el fin de aprender a hacer reportes de dos tipos: en html para visualizar en el navegador y en formato pdf.



Materiales para esta lección
El material de la lección número 6
El ReportLab que lo encontramos en.....
2 iconos de 16x16 para el menú reporte html y reporte pdf


Lección No. 8
Recuperamos la última modificación que reposa en la lección No. 7, creamos un menú denominado Reportes y debajo de éste se mostrarán las opciones: Formato html y Formato Pdf, agregamos los íconos para el menú.

Algo parecido a esto es lo que debemos tener:


Y sin más preámbulos vamos a programar el menú Formato html; creamos entonces una función (ya debes saber cómo crear una función para un menú, si no es así entonces te recomiendo que leas las lecciones anteriores) a la que denominaremos genera_html y este es el código:

En la parte superior del código debemos colocar las librerías que vamos a utilizar, así como configurar la conexión a MySQL:

import MySQLdb as con
db = con.connect(host='localhost',user='root',passwd='12345678',db='leccion6')
c = db.cursor()
import subprocess
import sys
import datetime
import os

Buscamos la función que acabamos de crear y digitamos nuestro código:

def genera_html(self,event): #init function
#add your code here
self.imprime()
f = open("C:/REPORTES/mascotas.html", "w")
f.write(self.cadena)
f.close()
if sys.platform == "win32":
subprocess.Popen('explorer C:\\REPORTES\\mascotas.html', shell=True)
else:
subprocess.Popen('firefox C:\\REPORTES\\mascotas.html', shell=True)
return #end function

El anterior código lo que hace es llamar a una función denominada imprime() [que debemos crear enseguida], abrir un archivo en modo de escritura (“w”) ubicándolo en la carpeta REPORTES que se encuentra en la raíz del C: (que no te pase, de una vez y antes de ejecutar el programa crea una carpeta en el disco duro donde se almacenarán los reportes), luego verifica si la plataforma sobre la que estamos trabajando es basada en win32 para abrir el internet explorer, sino abre el Firefox.

Ahora debemos crear la función imprime(), para ello solo buscate un espacio libre (puede ser debajo de la función anterior) y escribe como sigue:


La anterior función lo que hace es tomar una variable para concatenarla utilizando código html en la cual colocamos los encabezados de la tabla, la segunda parte de este código está determinada por una consulta sql y en el for lo que hacemos es reemplazar %s por el resultado de la consulta (bueno, estoy tratando de explicar de la forma más sencilla posible!!).

Guarda y ejecuta el programa. Al ejecutar el programa espera algo así como unos tres o cuatro segundos y se abrirá el navegador con el resultado de la consulta que aparece enmarcado dentro de una tabla.

Si todo va bien hasta ahora entonces podemos pasar a crear el reporte en pdf.

Lo primero es descargarnos la última versión del ReportLab que al día de escribir este tutorial anda por la 2.2, se puede descargar de http://www.reportlab.org/

ReportLab trae un binario denominado ReportLab-2.2.win32-py2.5.exe el cual una vez descargado no es sino dar doble clic y seguir las instrucciones de instalación (siguiente, siguiente,.... y listo).

Para verificar si quedó correctamente instalado el pdf puedes hacer la prueba desde la consola de windows como se muestra en la imagen:

Si no te aparece ningún error es porque todo marcha bien.

- Creamos una función para programar el pdf.
- Buscamos la función y allí escribimos

En la parte superior del código agregamos las librerías que vamos a utilizar, en este caso las del reportlab

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch

Luego vamos a la función que hemos creado y allí escribimos lo siguiente:

def genera_pdf(self,event): #init function
#add your code here
sql = c.execute('SELECT * FROM mascotas ORDER BY especie')
res = c.fetchall()

"""Determino la ubicación del reporte, el
tamaño y la orientación del papel a utilizar"""
ca = canvas.Canvas("c:/REPORTES/mascotas.pdf",pagesize=letter)
ca.setFont("Helvetica-Bold",14)
ca.drawString(30,750,"MASCOTAS") #Título
ca.drawString(30,735,u"Ejemplo de Reporte PDF") #Subtítulo

#Defino el tipo y tamaño de la fuente
ca.setFont("Times-Roman",10)
"""Ubico los campos factura y fecha
consultados de la base de datos"""
ca.drawString(30,655,u"Código")
ca.drawString(65,655,"Nombres")
ca.drawString(150,655,"Familia")
ca.drawString(230,655,"Especie")
ca.drawString(280,655,"Sexo")
ca.drawString(310,655,"F. Nacimiento")
ca.drawString(380,655,"F. Muerte")

y = 655

col1 = 30
col2 = 95
col3 = 315
col4 = 425

for i in res:
y = y - 10
y1 = y - 1

codigo = str(i[0])
nombres = str(i[1])
familia = str(i[2])
especie = str(i[3])
sexo = str(i[4])
nace = str(i[5])
muere = str(i[6])


ca.drawString(col1,y,codigo)
ca.line(30,y1,225,y1) #Dibuja la linea (x1,y1,x2,y2)

ca.drawString(65,y,nombres)
ca.line(65,y1,225,y1)

ca.drawString(150,y,familia)
ca.line(150,y1,225,y1)

ca.drawString(230,y,especie)
ca.line(230,y1,225,y1)

ca.drawString(280,y,sexo)
ca.line(280,y1,225,y1)

ca.drawString(310,y,nace)
ca.line(310,y1,225,y1)

ca.drawString(380,y,muere)
ca.line(430,y1,225,y1)

ca.showPage()
ca.save()

os.system('c:/REPORTES/mascotas.pdf') #Muestra el pdf

return #end function

Ejecuta el programa y prueba a generar el pdf, debes obtener algo como esto:

Intenta cambiar los valores de las variables del reportlab para que te des cuenta como funcionan, sino, igual adjunto un archivo en pdf que encontre hace ya muchos días en www.linux-magazine.es con un pequeño pero buen tutorial sobre el manejo de reportlab.

lunes, 10 de noviembre de 2008

Lección No. 7

Hoy retomaremos la lección 6, la cual complementaremos con nuevos elementos que estudiaremos como las grillas, los estilos/comportamientos y mejoraremos el aspecto del menú.

Materiales para esta lección
No podemos hacer esta lección si no tenemos los materiales de la lección 6 ya trabajados, además de esto necesitaremos:

– 3 iconos de 16x16


Abramos entonces nuevamente la carpeta donde se encuentra el proyecto anterior en mi caso denominada leccion6.

Empecemos por hacer algunas cosas que olvide en la lección pasada como es el darle funcionalidad a la opción Terminar del menú principal.

– Doble clic en el form MyFrame
– Clic en la pestaña Menu

– Clic en la opción Terminar
– Clic en la Events
– Clic en Component Event (botón E)

En el evento EVT_MENU asignamos un nombre a la función, yo le he puesto full_salida.


– Clic en la ficha Python (si estaba seleccionada esta ficha, debes entonces dar clic en la ficha Design y luego nuevamente en la ficha Python con el fin de que actualice el código sino, no podrás encontrar la función que estamos creando).
– Buscamos la función que acabamos de crear (full_salida)
– Y escribimos:

def full_salida(self,event): #init function

#add your code here
self.Close()

return #end function


Ahora ya podemos salir del programa al hacer clic en el menú Terminar.


Qué tal si le mejoramos la apariencia al menú colocando unos cuanto iconos.

En la ficha Menu, seleccionamos el primer ítem del menú denominado mascotas, luego en la ficha Properties buscamos la opción Bitmap; doble clic en esta opción.

Nos debe mostrar la siguiente ventana:


Hacemos clic en el botón RES, el cual nos muestra otra ventana:


En esta ventana debemos elegir el tipo de archivo que vamos a utilizar (Bmp, Jpg, Png, etc) y luego, debajo de la palabra Type existe un botón identificado con tres puntos seguidos … debes hacer clic ahí y localizar el archivo.

Una vez lo tengas localizado tendrás algo parecido a esto:


Haces clic en el botón OK, que te regresa a la ventana anterior y donde también debemos hacer clic en el botón OK. Guardas los cambios y ejecutas el programa para comprobar que hay un icono en nuestro menú de mascotas.

Repetimos los mismos pasos para el menú Terminar, obviamente seleccionando la imagen adecuada para este ítem.

Entonces debemos tener algo similar a esto:


Me imagino que repasaste algo de la lección si no es así es hora de que lo hagas ya que necesitamos crear un ítem de menú entre la opción Mascotas y Terminar denominada Lista Mascotas con su respectivo icono.

El resultado debe ser algo parecido a esto:


Ahora vamos a crear un form para Lista Mascotas al cual denominaremos frm_listar.


Nos volcamos a la ficha Project, hacemos clic con el botón derecho del mouse sobre la carpeta Design el cual nos mostrará un menú flotante en el cual debemos señalar la opción ADD wxMDIChildframe que se encuentra dentro de Add File|frame|panel; pues lo que vamos a crear es un form con la propiedad MDIChild (hija del frame principal MyFrame)
.



Al seleccionar esta opción se debe mostrar el form en la ventana de diseño, cambiemos su aspecto y fuente, y en la propiedad Name la cambiamos por frm_listar nombre con el que debe ser también guardado.

No olvidemos también asignar el título, cambiar el tamaño, etc.


Una vez “organizado” este form vamos a la ficha Components y ubicamos los siguientes componentes guiándonos en la gráfica:


Vamos a cambiar la propiedad de los static text a static text de sólo lectura.

Clic en el primer statictext, luego doble clic en Style de la ficha Properties, nos aparecerá la siguiente ventana:


En esta ventana se adicionan o se cambian algunos “estilos” (yo prefiero llamarlos comportamientos) de controles, formularios, entre otros. Si detallas bien, hacia abajo se encuentra entre otras la opción wxTE_READONLY, seleccionala, clic en aceptar y luego en Ok (observa que se tiene comportamientos interesantes como el ENTER, TAB, PASSWORD que utilizaremos más adelante en otros tutoriales).

Repite lo anterior con los otros dos controles (es posible que no se te pida confirmación de cambio y que solo baste con hacer clic en el botón Ok).

Lo que acabamos de hacer es darle a los controles que actualmente tenemos en el form el “estilo”/comportamiento/propiedad adicional -como lo quieras llamar- de sólo lectura, no podremos cambiar nada de lo que allí se muestre, ni tampoco escribir en estos controles.

Ahora haremos que nuestro form se pueda activar al hacer clic en el menú Lista Mascotas o haciendo clic con la combinación de teclas.

Si no recuerdas como se hace te recomiendo que leas nuevamente la lección anterior (que si que eres desobediente!!), no olvides importarlo en la sección de los includes, allá, arriba del código.


Aprovechamos también y hacemos el código del botón cerrar.


Ahora nos concentraremos en la grilla; vamos a agregar un wxGrid (grilla) que se llenará con algunos de los datos que contiene la tabla mascotas.


En la pestaña Components localizamos una carpeta denominada List que contiene entre otros componentes el que necesitamos, el Grid.

Hacemos doble clic en éste y verificamos que nos aparezca un pequeño recuadro café en la parte superior izquierda de nuestro form.


Para ubicarlo en el lugar que deseamos debemos hacer clic en el control identificado por gr seguido de un número de la pestaña Project del form frm_listar. Una vez seleccionado en esta sección podremos ubicar nuestro grid en el espacio que hemos dejado entre los controles y el botón cerrar.

Le cambiamos su ancho y alto y en la propiedad Name de la ficha Properties cambiamos su nombre a por ejemplo grilla.

Esto es lo que debemos tener hasta el momento.


Cambiamos la propiedad visible a False, agregamos un nuevo botón al que denominaremos bt_listar.

Debemos tener algo parecido a esto:

Creamos la función para el botón bt_listar (una función denominada por ejemplo llenar_grilla) y allí digitamos:
def llenar_grilla(self,event): #init function

#add your code here

sql1 = c.execute('SELECT COUNT(id) FROM mascotas')

res1 = c.fetchall()

rex1 = int(c.rowcount)

sql2 = c.execute('SELECT id,nombres,familia FROM mascotas')
res2 = c.fetchall()
rexx = int(c.rowcount)


#Limpiamos la grilla

self.grilla.Show(True)

self.grilla.ClearGrid()

""" Definimos (filas,columnas),
mediante este for
y la consulta sql1, res1
calculamos el número
de filas
que vamos a utilizar
"""

y = 0
for a in res1:
self.grilla.CreateGrid(a[0],3)
y+=1

#Se le da nombre a los encabezados de las columnas
#y luego se define el tamaño inicial de las columnas

#aunque más abajo se define AutoSizeColumns

self.grilla.SetColLabelValue(0,'CODIGO')

self.grilla.SetColLabelValue(1,'MASCOTA')

self.grilla.SetColLabelValue(2,'FAMILIA')
#Se muestran los resultados de la consulta

x = 0

for i in res2:

self.grilla.SetCellValue(x,0,str(i[0]))

self.grilla.SetCellValue(x,1,str(i[1]))

self.grilla.SetCellValue(x,2,str(i[2]))

self.grilla.AutoSizeColumns(True)

x+=1 self.bt_listar.Enable(False)
#Para evitar llamar nuevamente la grilla

return #end function

Si ejecutamos el programa podemos obtener algo como esto:



O un listado más completo, todo depende de cuántos registros tengas en la base de datos.

Ahora vamos a crear una función que al hacer clic en una de las celdas capture los datos y los ubique en los static_text.


Hacemos clic en la grilla, luego nos vamos a la ficha Events y buscamos el evento EVT_GRID_CELL_LEFT_DCLICK (el 3 de arriba hacia abajo), le damos el nombre a la función (por ejemplo obtiene_datos) y digitamos lo siguiente:

def obtiene_datos(self,event): #init function

#add your code here

"""Se obtienen los datos de la fila seleccionada en la grilla,
esto puedes servir
para eliminar datos, actualizar, etc"""
pos1 = self.grilla.GetCellValue(self.grilla.GetGridCursorRow(),0) #codigo

pos2 = self.grilla.GetCellValue(self.grilla.GetGridCursorRow(),1) #nombre
pos3 = self.grilla.GetCellValue(self.grilla.GetGridCursorRow(),2) #familia
self.txt_codigo.SetLabel(str(pos1))

self.txt_nombre.SetLabel(str(pos2))
self.txt_familia.SetLabel(str(pos3))
return #end function

Ahora cuando tengas datos listados en la grilla y hagas doble clic en una de las celdas, su contenido se visualizará también en los static_text.

Bueno, creo que eso es todo por hoy. Personalmente me gustaría que alguien que tenga buena experiencia en el trabajo con grillas escribiera un pequeño tutorial donde enseñara a editar, actualizar, eliminar, entre otras cosas; lo aquí visto con la grilla es algo realmente básico.

jueves, 25 de septiembre de 2008

Lección No. 6

En esta lección aprenderemos a crear un form MDI, repasaremos temas de conexión con MySQL, crearemos una barra de menú con sus respectivos ítems, shorcuts y funciones y tendremos un breve código de excepciones (aunque no se si me va a funcionar).


Material para esta lección

Debemos crear una base de datos denominada leccion6 con la siguiente estructura:

CREATE TABLE especie (
id int(4) unsigned NOT NULL auto_increment,
nombre varchar(12) NOT NULL default '0',
KEY id (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


CREATE TABLE mascotas (
id varchar(4) unsigned NOT NULL,
nombres varchar(30) NOT NULL,
familia varchar(15) NOT NULL,
especie varchar(15) NOT NULL,
sexo varchar(2) NOT NULL,
fnace date NOT NULL,
fmuere date default NULL,
KEY id (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


CREATE TABLE sexo (
id int(4) unsigned NOT NULL auto_increment,
nombre varchar(2) NOT NULL,
KEY id (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Llenamos la tabla especie con todas las especies de animales que se te ocurran: canino, felino, ave, reptil, equino, pez, etc....

Llenamos la tabla sexo con: F para femenino, M para masculino y NA para los indeterminados o casos donde no aplican.


Lo primero es lo primero, creamos una carpeta nueva para esta lección luego entramos al VisualWx, hacemos clic en el menú File y seleccionamos New Project cuidado es el New Project no el New Empty Project.

Nos aparecerá una ventana (Template) allí señalamos WX_MDI_Python, ubicamos la carpeta que hemos creado anteriormente y guardamos el proyecto, yo le he puesto leccion6

Ahora, ejecuta el programa (F5).

Observarás entonces una ventana dentro de otra, algo como esto:

Lo primero que vamos a organizar es que no nos aparezca esa ventana una vez se inicie nuestro programa, para ello debemos hacer doble clic en MyFrame que es el que actúa como formulario principal (el form madre por llamarlo de alguna manera) que contendrá a los demás formularios.

Teniendo este form en modo edición, hacemos clic en la ficha Python (si la del centro que nos lleva al código de python) y buscamos esta función:

def initAfter(self):
#add your code here
self.child=frmChild.frmChild(self,-1)
return

Podemos comentar la línea self.child=frmChild.frmChild(self,-1) o simplemente eliminarla, sin embargo fíjate muy bien como está escrita ya que la tomaremos como guía para activar nuestros forms desde el menú que más adelante crearemos.

Yo normalmente la elimino ya que casi casi me se el código de memoria, sin embargo al comienzo solo la dejaba comentada aunque no faltará quien sugiera que es mejor eliminarla ya que incrementa el número de líneas, memoria, etc, etc.

Si ejecutas nuevamente el programa notarás que ya no aparece el form de adentro (frmChild).

Si todo marcha bien entonces vamos a proceder a trabajar con el form frmChild. Lo cargamos para su edición, cambiamos su color, tamaño de fuente y ubicamos los siguientes controles:


Será necesario sacar la lista??? no creo.

Al cuadro de texto fecha de nacimiento y fecha de fallecimiento en la ficha propiedades en Text ponle 0000-00-00 para indicar que es el formato de fecha que utilizaremos año-mes-dia ó más técnicamente yyyy-mm-dd

Luego programamos los focos de los controles, empezando por el txt del código y terminar en el txt de fecha de fallecimiento.

Cómo lo hago??? Revisa la lección No. 5

Los botones Actualizar y Eliminar deben comenzar desactivados.

No olvides verificar los focos una vez programados.

Botón Guardar:

def guarda_datos(self,event): #init function
#add your code here
id = self.txt_codigo.GetValue()
nom = self.txt_nombre.GetValue()
flia = self.txt_familia.GetValue()
esp = self.cmb_especie.GetValue()
sex = self.cmb_sexo.GetValue()
fnace = self.txt_fnace.GetValue()
fmuere = self.txt_fmuere.GetValue()

"""Obligo a que el usuario ingrese los campos
que considero obligatorios"""
if id<>'' and nom<>'' and flia<>'' and esp<>'' and sex<>'':
"""Hay una condicón con la que deberíamos
tener un poco de cuidado y es el DatePicker fnace,
la condición sería que si la fecha es igual a la de
hoy le pregunte al usuario si es correcta o no,
pero ese tipo de situaciones la veremos en otros
tutoriales, hoy la obviaremos a propósito""
sql = c.execute('INSERT INTO mascotas(id,nombres,familia,especie,sexo,fnace,fmuere) \
VALUES(%s,%s,%s,%s,%s,%s,%s)' ,(id,nom,flia,esp,sex,fnace,fmuere))
res = int(c.rowcount)

"""Si la consulta arroja un número de filas
diferentes a 0, que muestre un mensaje"""
if res<>0:
w = wx.MessageDialog(self,u"Registro almacenado con éxito","REGISTRO ALMACENADO",wx.OK | wx.ICON_INFORMATION)
w.ShowModal()
w.Centre()
w.Destroy()

self.txt_codigo.SetLabel('')
self.txt_nombre.SetLabel('')
self.txt_familia.SetLabel('')
self.cmb_especie.SetLabel('')
self.cmb_sexo.SetLabel('')
self.txt_fnace.SetLabel('')
self.txt_fmuere.SetLabel('')

self.txt_codigo.SetFocus()
else:
w = wx.MessageDialog(self,u"Faltan datos por ingresar","ERROR DATOS",wx.OK | wx.ICON_EXCLAMATION)
w.ShowModal()
w.Centre()
w.Destroy()

self.txt_codigo.SetFocus()

return #end function

Aclaro que los nombres de los controles deben ser los que Ud. utilizo, o sea, reemplace todo lo que dice self.... por self.elnombredetucontrol

Antes de ejecutar para probar el código que acabamos de digitar debemos crear una barra de menú con su respectivo menú para abrir este form desde .... el menú claro. Lo hacemos de la siguiente manera:

Seleccionamos la ficha MyFrame:


Luego, clic en la ficha Menu:

Al hacer clic con el botón derecho del mouse en el espacio blanco debajo de la ficha Menu, nos aparecerá la opción para crear/adicionar una barra de menú o un menú. Para nuestro caso debemos seleccionar la opción Add Menu Bar.

Al seleccionar esta opción puedes observar como aparece inmediatamente en el form principal (MyFrame) una barra de menú.

Si quieres le cambias el nombre en la propiedad Name, yo normalmente la dejo tal cual.

Ya es hora de crear los diferente ítems de los menús que vamos a tener.

Para empezar hacemos clic con el botón derecho del mouse sobre menubar o como quieras que le hayas puesto. Nos aparecerán las opciones Add Menu y Delete (creo que no hay mucho que explicar aquí).

Clic en Add Menu:
Por supuesto a este si hay que cambiarle el nombre, entonces nos vamos a las propiedades y le cambiamos en la opción Title por Principal. Ejecutamos para ensayar y veremos nuestra ventana parecida a esta:

Ya tenemos Principal, el menú Window que se crea automáticamente. Si haces clic en principal no va a pasar ya que falta agregrar los items del menú que es lo vamos a hacer en este momento:

Hacemos clic con el botón derecho sobre “Principal”, te fijas bien en las opciones que se muestran ya que son bien didácticas; de estas opciones vamos a seleccionar Add Item que agrega un “item” al menú denominado “Principal”. Fíjate también en cómo se va formando un árbol con los diferentes menús, ítems y submenús.

Una vez agregado el ítem vamos a la ficha de propiedades y cambiamos en la opción Title por Mascotas, ahora tu menú debe lucir así:

Y si ejecutas el programa, algo como esto:

Cada vez que quieras agregar un ítem al menú Principal, o sea, que quieras que aparezca debajo así como Mascotas, deberás hacer clic derecho sobre la palabra “Principal” en la ficha Menú. También podemos agregar separadores y cambiar de posición los ítems del menú.

A ver si me han entendido. Como prueba, nuestro menú deberá lucir así:

Para crear otro menú digamos Auxiliar (por ejemplo), debemos hacer clic derecho en la opción Menu Bar y proceder como en el caso anterior.

Te estarás preguntado cómo hacemos un submenú, parecido al que podemos encontrar cuando hacemos clic en cualquier menú que tenga la palabra seguida de un >. Fácil, debajo del menú, por ejemplo de Auxiliar, clic derecho Add Menu, das el nombre en propiedades Title, luego agregas items a ese submenú que acabas de crear y ya está.

Bueno, si tienes claro hasta aquí, podemos proseguir, sino enviame un mail para tratar de aclarar tus dudas.

Hasta el momento nuestro menú no hace nada, hagas clic donde lo hagas no hace nada, pues entonces vamos a programar nuestro menú.

Selecciona la opción Mascotas de la ficha Menu.

Haz clic en la ficha Events botón E y en la opción EVT_MENU asigna un nombre a la función, yo le he puesto ver_mascotas; luego vas a la ficha Python, buscas la función y escribes lo siguiente:

def ver_mascotas(self,event): #init function
#add your code here
self.child=frmChild.frmChild(self,-1)
self.child.Centre()
return #end function

Otro código que hace lo mismo es:

def ver_mascotas(self,event): #init function
#add your code here
w = self.frmChild.frmChild(self,-1)
w.Centre()
return #end function

Y cuando se utilizan cuadros de diálogo que tiene la propiedad Modal se puede utilizar:

def ver_mascotas(self,event): #init function
#add your code here
#Si este form fuera del tipo dialogo
w = self.frmChild.frmChild(self,-1)
w.ShowModal()
w.Centre()
w.Destroy()
return #end function


Cómo quieras está bien, yo me quedo con la segunda opción. Ahora para que pueda ser ejecutado el formulario de mascotas, debemos digitar:

#[inc]add your include files here
import frmChild
#[inc]end your include

En la parte superior del código de MyFrame.

Ahora ejecuta el programa y haz clic en la opción Mascotas del menú Principal y obtendrás algo como esto:

Vamos a terminar de programar nuestro formulario de Mascotas, vamos con los combobox.

Antes de proceder con los combos vamos a la ficha Python de este formulario y en la parte superior digitamos:

#[inc]add your include files here
import MySQLdb as con
db = con.connect(host='localhost',user='root',passwd='tuclave',db='leccion6')
c = db.cursor()
#[inc]end your include

Recuerda que este código es el que nos permitirá interactuar desde Python con MySQL.

Ahora si nos vamos a los combos.

Seleccionamos FrmChild, nos cambiamos a modo diseño y hacemos clic en el combobox de especie.

Una vez seleccionado el combo, hacemos clic en la ficha Events botón B y buscamos el evento EVT_LEFT_DOWN asignamos un nombre a la función (yo le he puesto llena_especie), nos pasamos a la ficha Python y buscamos la función para escribir:

def llena_especie(self,event): #init function
#add your code here
sql = c.execute('SELECT nombre FROM especie ORDER BY nombre asc')
res = c.fetchall()
res1 = int(c.rowcount) #Devuelve el número (entero) de filas

"""Limpia el combo antes de ejecutar la
consulta y con esto evitamos que se dupliquen
los contenidos de los combos cada vez que se
haga clic en ellos"""
self.cmb_especie.Clear()

"""Verificamos que la consulta tenga al menos
una fila"""
if res1<>0:
for i in res:
#Agregamos el resultado al combo
self.cmb_especie.Append(str(i[0]))
event.Skip()

return #end function

Ejecuta el programa y verifica que al hacer clic en el combo de especie se despliegue las especies que antes le agregamos a la tabla.

Vamos a ingresar información en nuestro formulario de mascotas (por ahora metamos solo 1 registro):

(Cualquier parecido con la realidad es pura coincidencia)


Ahora vamos a crear una función para que al abandonar el campo código verifique si ya existe el código que se está asignado, si es así que cargue los datos de ese registro, de lo contrario que continúe con el proceso de registro.

Hacemos clic en el campo código, luego en la ficha Events buscamos EVT_KILL_FOCUS, damos un nombre a la función (yo le he puesto consulta_codigo) buscamos luego la función y allí escribimos:

def consulta_codigo(self,event): #init function
#add your code here
ind = self.txt_codigo.GetValue()

sql = c.execute('SELECT * FROM mascotas WHERE id=%s',(ind))
res = c.fetchall()
resX = int(c.rowcount)

if resX<>0:
for i in res:
self.txt_nombre.SetLabel(str(i[1]))
self.txt_familia.SetLabel(str(i[2]))
self.cmb_especie.SetLabel(str(i[3]))
self.cmb_sexo.SetLabel(str(i[4]))
self.txt_fnace.SetValue(str(i[5]))
self.txt_fmuere.SetLabel(str(i[6]))

self.bt_guardar.Enable(False)
self.bt_actualizar.Enable(True)
self.bt_eliminar.Enable(True)
event.Skip()

return #end function

Ejecutamos el programa, abrimos el formulario de mascotas y el campo código digitamos el número 1 o el código que le hayas dado a la mascota; si este código es localizado en la base de datos se cargarán los datos de ese registro, desactivará el botón guardar y activará actualizar y eliminar.

Vamos a suponer que necesitamos corregir la fecha de nacimiento, entonces el código del botón actualizar será el siguiente:

def actualiza_datos(self,event): #init function
#add your code here
id = self.txt_codigo.GetValue()
nom = self.txt_nombre.GetValue()
fam = self.txt_familia.GetValue()
esp = self.cmb_especie.GetValue()
sex = self.cmb_sexo.GetValue()
fna = self.txt_fnace.GetValue()
fmu = self.txt_fmuere.GetValue()

try:
#Acción potencialmente errónea
fmu='None'
except:
#Tratamiento del error
fmu='0000-00-00'

sql = c.execute('UPDATE mascotas SET nombres=%s,familia=%s,especie=%s,sexo=%s,fnace=%s,fmuere=%s WHERE id=%s',(nom,fam,esp,sex,fna,fmu,id))
res = c.fetchone()

w = wx.MessageDialog(self,u"Registro almacenado con éxito","ACTUALIZACION",wx.OK | wx.ICON_INFORMATION)
w.ShowModal()
w.Centre()
w.Destroy()

self.txt_codigo.SetLabel('')
self.txt_nombre.SetLabel('')
self.txt_familia.SetLabel('')
self.cmb_especie.SetLabel('')
self.cmb_sexo.SetLabel('')
self.txt_fnace.SetLabel('')
self.txt_fmuere.SetLabel('')

self.txt_codigo.SetFocus()

return #end function


Y si lo que quisieramos es eliminar el registro? Entonces el código en el botón eliminar sería:
def elimina_datos(self,event): #init function
#add your code here
id = self.txt_codigo.GetValue()

resp = wx.MessageBox("Realmente desea eliminar este registro?","CONFIRMAR ELIMINACION",wx.YES_NO)
if resp==wx.YES:
sql = c.execute('DELETE FROM mascotas WHERE id=%s',(id))

w = wx.MessageDialog(self,u"Registro eliminado con éxito","ELIMINACION",wx.OK | wx.ICON_EXCLAMATION)
w.ShowModal()
w.Centre()
w.Destroy()

self.txt_codigo.SetLabel('')
self.txt_nombre.SetLabel('')
self.txt_familia.SetLabel('')
self.cmb_especie.SetLabel('')
self.cmb_sexo.SetLabel('')
self.txt_fnace.SetLabel('')
self.txt_fmuere.SetLabel('')

self.txt_codigo.SetFocus()
else:
self.txt_codigo.SetFocus()


return #end function

Y por último, el código para el botón cerrar: self.Close()

Ahora lo que vamos a hacer es agregar teclas rápidas a nuestro menú, para ello debes tener seleccionado el MyFrame.

Abre la ficha Menu.
Clic en la opción “Mascotas”
Clic en la ficha propiedades
En la propiedad denominada Shorcut digitaremos Ctrl+M esto hará que cuando presionemos las teclas Control y M se abrirá el form de mascotas.

Hagamos lo mismo con la opción Terminar, sólo que a esta le escribiremos en la propiedad Shorcut F10.


Ejecutamos el programa y probamos primero presionando las teclas Crtl+M, se nos debe abrir el form. Cierralo y prueba presionando la tecla F10, se debe salir totalmente del programa.

Creo que es todo por ahora.


Para una próxima lección espero poder trabajar con el datepicker que en esta ocasión me pidio papeles y por más que pregunte en la lista no pude con este controlcito.

jueves, 11 de septiembre de 2008

Lección No. 5

Antes que nada... perdón por la demora.

Con la lección de hoy aprenderemos cómo conectarnos a MySQL desde python y cómo hacer algunas acciones básicas de las bases de datos: guardar, buscar, eliminar, actualizar. Todo ello utilizando Python MySQL y VisualWx.


Material de trabajo para esta lección
Para la lección de hoy vamos a necesitar instalar un servidor de MySQL, para ello puedes descargar el AppServ para windows que además te instala el Apache y el Php (claro que eso no los necesitamos pero.... pues matas varios pájaros de un solo tiro), lo puedes encontrar en http://www.appservnetwork.com/index.php?newlang=spanish actualmente yo utilizo la versión 2.5.8. porque la descargue hace mucho tiempo y me ha funcionado sin problemas, pero si ves la página notarás que van en la versión 2.6, ten cuidado con la vesión que descargues ya que tiene que ser compatible con la API que ofrece python para MySQL; Descargalo y cuando lo estés instalando asegurate de asignar una clave al MySQL.

También necesitamos el MySQL-python que lo puedes encontrar en http://sourceforge.net/projects/mysql-python/ descargas e instalas sin mayores inconvenientes (siguiente, siguiente...)

Si sabes pocon pocon de bases de datos, te recomendaría que te descargues también el HeidiSQL de http://www.heidisql.com/ que es un GUI gratis que te permitirá crear tablas, editarlas, etc. y hasta aprender algunas cosas de bdd. Actualmentemente va por la versión 3.2


Manos a la obra
Con estas herramientas ya instaladas procederemos con la lección de hoy, que inicialmente será algo sencillo pero que en las próximas lecciones iremos enriqueciendo.

Lo primero es lo primero, para quienes no sepan qué es MySQL (aunque si estás aquí me imagino que alguito debes saber) en resumidas cuentas podemos decir que es un sistema de gestión de bases de datos similar al SQLite, Postgress, Oracle, entre otros cada uno con sus pequeñas diferencias, sus pro y sus contra, puedes encontrar más documentación en san google.

El archivo MySQL-python-1.2.2.win32-py2.5 (o similar) sera el que nos permita hacer el “enlace” o conexión desde Python con la bdd (base de datos).

Vamos a crear nuestra base de datos y luego la utilizaremos desde VisualWx; si bajaste el HeidiSQL, lo abres, configuras la conexión con la base de datos MySQL (con la clave que le has puesto al instalar el AppServ).


Ya dentro del programa vamos a crear una base de datos denominada afiliados. Para crearla desde el HeidiSQL solo basta con hacer clic derecho en root@localhost seleccionar create database.... y seguir las instrucciones:


Ahora crearemos la tabla para la base de datos afiliados. Lo podemos hacer de varias formas, explicare solo dos, desde HeidiSQL y desde consola:

HeidiSQL
Teniendo seleccionada la base de datos afiliados da clic en el menú tools y de este menú selecciona Create Table, luego ingresa los diferentes campos con sus propiedades, algo como esto:

No olvides cambiar el tipo de dato y su longitud. Por último clic en el botón Create!

Si no te has descargado el Heidi, entonces desde consola se hace así:
Clic en Inicio – Ejecutar – cmd y Enter

Estando en el prompt de win2 digitas: mysql -u root -p y presionas enter.
Se te pedirá la clave que es la que digitaste cuando estabas instalando el AppServ.

Una vez ingreses digitas lo siguiente:

mysql>use afiliados;

Lo anterior es para usar la base de datos afiliados. Ahora a crear la tabla. Digita:

CREATE TABLE personas(
doc INT (11) UNSIGNED NOT NULL, nombre1 VARCHAR (12) NOT NULL,
nombre2 VARCHAR (12), apellido1 VARCHAR (12) NOT NULL,
apellido2 VARCHAR (12), direccion VARCHAR (35) NOT NULL,
telefono VARCHAR (11));

Ya tenemos nuestra base de datos creada (afiliados) con una tabla definida (personas).

Como este no es un tutorial de MySQL deberás buscar en san google si quiere aprender más sobre este sistema de bases de datos.

Ahora abrimos nuestro VisualWx y creamos una nueva aplicación WX_SDI_PYTHON, la guardas en una carpeta con el nombre que quieras (yo le he puesto afiliaciones -que imaginación la mía-).

Le cambiamos el color al form, el tamaño de la fuente; en la propiedad Title: Control de afiliados con MySQL o lo que quieras colocar.

NOTA
Debido a un lapsus mental se me ha ido el título de la ventana "Control de afiliados con SQLite", pero en realidad debería decir "Control de afiliados con MySQL".

Ahora vamos a ubicar los controles para obtener algo como esto:


Esta es la lista de los controles con sus nombres:

Antes de programar conectándonos a la bdd sería bueno programar los focos de los controles. Para iniciar podemos seleccionar el txt_doc y el ficha propiedades cambiar Focus a true, eso hará que cuando se active el form, el foco inicie en este control.

Ahora haremos que cuando se presione cualquiera de las teclas enter/intro o la tecla tab, se cambie el foco al siguiente control.

Mi primer control (documento) se denomina txt_doc y le sigue txt_nombre1, para programar esta acción estando ubicado el txt_doc hago clic en la ficha events y busco el EVT_KEY_DOWN, le doy el nombre a la función y luego en la ficha Python que se encuentra debajo del Form busco la función (yo le he puesto va_n1) y digito lo siguiente:

def va_n1(self,event): #init function
#add your code here
key = event.GetKeyCode()

if key==9 or key==13 or key==wx.WXK_NUMPAD_ENTER:
self.txt_nombre1.SetFocus()

event.Skip()

return #end function

Lo misma forma debes proceder con cada uno de los controles teniendo en cuenta a qué control debe llegar el foco.
Ejecuta el programa (F5) y verifica que todos los focus estén funcionando correctamente.

Si todo anda bien, entonces procedamos con la programación de la base de datos (bdd) y los botones.

Para conectarnos a la base de datos en la parte superior de nuestro código vamos a digitar lo siguiente:

import MySQLdb as con
db = con.connect(host='localhost', user='root', passwd='tuclave', db='afiliados')
c = db.cursor()

Esto importa el MySQL al que denominamos con y luego nos permite hacer la conexión con nuestra bdd denominada afiliados.



Ahora vamos a programar el botón guardar.

Clic en el botón guardar.
Clic en la ficha events.
Clic en el botón E (Component Events)
Clic en el EVT_BUTTON
Dale el nombre a la función, yo le he puesto guarda_datos (sigo impresionado con mi imaginación para asignar nombres!!!)

Vamos a la ficha Python (si la del centro) y buscamos la función que acabamos de crear; allí escribimos lo siguiente:

def guarda_datos(self,event): #init function
#add your code here
doc = self.txt_doc.GetValue()
n1 = self.txt_nombre1.GetValue()
n2 = self.txt_nombre2.GetValue()
ap1 = self.txt_apellido1.GetValue()
ap2 = self.txt_apellido2.GetValue()
di = self.txt_direccion.GetValue()
te = self.txt_telefono.GetValue()

"""Vamos a poner una pequeña restricción que obligue
al usuario a escribir por lo menos el doc, el nombre1
apellido1 y la direccion"""

if doc<>'' and n1<>'' and ap1<>'' and di<>'':
sql = c.execute('INSERT INTO personas VALUES(%s,%s,%s,%s,%s,%s,%s)',
(doc,n1,n2,ap1,ap2,di,te))
res = c.fetchall()

if res<>None:
w=wx.MessageDialog(self,u"Registro almacenado con éxito","REGISTRO",wx.OK | wx.ICON_INFORMATION)
w.ShowModal()
w.Centre()
w.Destroy

self.txt_nombre1.SetLabel('')
self.txt_nombre2.SetLabel('')
self.txt_apellido1.SetLabel('')
self.txt_apellido2.SetLabel('')
self.txt_direccion.SetLabel('')
self.txt_telefono.SetLabel('')

self.txt_doc.SetFocus()
else:
w = wx.MessageDialog(self,u"Faltan datos por ingresar","ERROR DE DATOS",wx.OK | wx.ICON_EXCLAMATION)
w.ShowModal()
w.Centre()
w.Destroy

self.txt_doc.SetFocus()

return #end function

No olvide los tabulados (identaciones) por favor....

Probamos ejecutando el programa e ingresando cualquier dato para guardarlo utilizando el botón, puede ser nuestro nombre:


El siguiente código puede ser utilizado para diferentes cosas, principalmente para evitar registros duplicados o para localizar rapidamente la información.

Selecciona el txt_doc y en la ficha events localiza el EVT_KILL_FOCUS asignale un nombre a la función, yo le he puesto consulta_datos. Esta función hace que una vez el control txt_doc pierda el foco se active el código de dicha función. Si lo prefieres puedes no utilizar este evento sino el de EVT_SET_FOCUS del control txt_nombre1 el cual recibe el foco después del txt_doc.

Si ya definiste el nombre de la función vamos a copiar el siguiente código:

def consulta_datos(self,event): #init function
#add your code here
doc = self.txt_doc.GetValue()

sql = c.execute('SELECT * FROM personas WHERE doc=%s',(doc))
res = c.fetchall()

if res<>None:
for i in res:
self.txt_nombre1.SetLabel(str(i[1]))
self.txt_nombre2.SetLabel(str(i[2]))
self.txt_apellido1.SetLabel(str(i[3]))
self.txt_apellido2.SetLabel(str(i[4]))
self.txt_direccion.SetLabel(str(i[5]))
self.txt_telefono.SetLabel(str(i[6]))

event.Skip()

return #end function

A grandes rasgos:

doc = self.txt_doc.GetValue() toma el valor del control y lo almacena en la variable doc.

sql = c.execute('SELECT * FROM personas WHERE doc=%s',(doc))
Genera la consulta sql reemplanzando %s por el contenido de la variable doc

res = c.fetchall()
El resultado de la consulta (número de filas devueltas)

if res<>None:
Si el resultado es diferente de vacío.

for i in res:
Se toma la variable i hasta res y se ....

self.txt_nombre1.SetLabel(str(i[1]))
... coloca el valor de cada i[X] en el respectivo control.

A veces ni yo mismo me entiendo per sí sé lo que digo!!! Si hay alguien más que pueda explicar de otra forma más clara o sencilla bienvenido sea.

Qué quiere decir todo esto... que si ejecutas el programa y vuelves a escribir un documento de identidad que YA EXISTE en la base de datos éste (la función) recupera/carga inmediatamente los datos en el form. Uff!!


El código para el botón buscar:
Haces clic en el botón buscar, luego vas a la ficha events, botón E, localizas el EVT_BUTTON, le das un nombre a la función, yo le he puesto.... buscar (qué imaginación) y escribes el siguiente código:

doc = self.txt_doc.GetValue()

sql = c.execute('SELECT * FROM personas WHERE doc=%s',(doc))
res = c.fetchall()

if res<>None:
for i in res:
self.txt_nombre1.SetLabel(str(i[1]))
self.txt_nombre2.SetLabel(str(i[2]))
self.txt_apellido1.SetLabel(str(i[3]))
self.txt_apellido2.SetLabel(str(i[4]))
self.txt_direccion.SetLabel(str(i[5]))
self.txt_telefono.SetLabel(str(i[6]))

event.Skip()

El código para el botón actualizar:
Hacemos el procedimiento como en el botón anterior; yo le he puesto actualiza_datos a la función y el código es el siguiente:

def actualiza_datos(self,event): #init function
#add your code here
doc = self.txt_doc.GetValue()
n1 = self.txt_nombre1.GetValue()
n2 = self.txt_nombre2.GetValue()
ap1 = self.txt_apellido1.GetValue()
ap2 = self.txt_apellido2.GetValue()
di = self.txt_direccion.GetValue()
te = self.txt_telefono.GetValue()

"""Se supone en este ejercicio que podemos actualizar
todos los datos menos el doc de identidad, para poder
cambiar el doc de identidad lo ideal sería haber creado
un indice o campo autonumeríco en la tabla personas"""
sql = c.execute('UPDATE personas SET nombre1=%s,nombre2=%s, \
apellido1=%s,apellido2=%s,direccion=%s,telefono=%s \
WHERE doc=%s',(doc,n1,n2,ap1,ap2,di,te,doc))
res = c.fetchall()

w = wx.MessageDialog(self,u"Registro actualizado con éxito",u"ACTUALIZACIÓN",wx.OK | wx.ICON_INFORMATION )
w.ShowModal()
w.Centre()
w.Destroy()

#Preparamos el form para más datos
self.txt_doc.SetLabel('')
self.txt_nombre1.SetLabel('')
self.txt_nombre2.SetLabel('')
self.txt_apellido1.SetLabel('')
self.txt_apellido2.SetLabel('')
self.txt_direccion.SetLabel('')
self.txt_telefono.SetLabel('')

self.txt_doc.SetFocus()

return #end function


El código para el botón eliminar:
def elimina_datos(self,event): #init function
#add your code here
doc = self.txt_doc.GetValue()

sql = c.execute('DELETE FROM personas WHERE doc=%s',(doc))
res = c.fetchall()

w = wx.MessageDialog(self,u"Registro eliminado con éxito","REGISTRO",wx.OK | wx.ICON_EXCLAMATION)
w.ShowModal()
w.Centre()
w.Destroy()

self.txt_nombre1.SetLabel('')
self.txt_nombre2.SetLabel('')
self.txt_apellido1.SetLabel('')
self.txt_apellido2.SetLabel('')
self.txt_direccion.SetLabel('')
self.txt_telefono.SetLabel('')

self.txt_doc.SetFocus()

return #end function

Y el código para el botón cerrar:
self.Close()

Obviamente estos códigos son algo “simples” ya que se debería programar teniendo en cuenta algunas restricciones o situaciones en ciertos momentos como lo es por ejemplo al momento de eliminar datos, sin embargo estos pequeños código servirán como ilustración para hacer otras cosas de mayor importancia.

Eso es todo por ahora.

martes, 26 de agosto de 2008

Lección 4

En esta lección crearemos un mini editor super básico que nos permitirá crear archivos y almacenarlos en cualquier lugar así como recuperarlos.

Utilizaremos los controles Edit text MultiLinea y ToolBar y ToolBar tool. Aprenderemos como cargar imágenes utilizando el editor GUI de VisualWx.

Para esta lección vamos a necesitar 4 iconos o imágenes de 16x16 que nos representen las siguientes acciones: nuevo, abrir, guardar y cerrar.

Crea una carpeta para esta lección y dentro de ella crea otra para almacenar los iconos.

Comenzaremos por crear un form al que pondremos por título “Mi Editor”. Si deseas le cambias el color, yo lo hice.

Vamos a ubicar la barra de herramientas. De la ficha Components seleccionamos la carpeta ToolBar allí se encuentra un control con el mismo nombre “ToolBar”, doble clic para que se ubique en el form.

Seleccionalo y cambiale el nombre a tb_herramientas.

Teniendo seleccionada la barra de herramientas agregamos el control denominado ToolBar Tool que mostrará un pequeño cuadro en dicha barra.

A este tipo de control sólo se le pueden hacer dos cosas desde el editor: cargar la imágen y asignarle el tooltip. Esto lo llevaremos a cabo desde la ficha Propertis.

Teniendo seleccionado este control, en la ficha propiedades (propertis) hacemos clic en Bitmap Tool, nos mostrará la siguiente imagen:

Teniendo esta ventana activa debemos hacer clic en el botón Res el cual nos arrojará esta ventana

desde la cual podemos elegir el tipo de archivo (imagen) que vamos a utilizar (png, gif, jpg, etc.), para cargar la imagen debemos hacer clic en el botón que aparece con los 3 puntos seguidos ... abrir la ubicación del archivo y seleccionarlo, por último clic en el botón Ok.

Teniendo aún seleccionado este control, en la ficha propiedades asignamos a Tool Tip la palabra Nuevo.

Repetimos las acciones anteriores (colocar el ToolBar Tool, asignar imagen, dar nombre) para los botones de abrir, guardar y cerrar con sus respectivos iconos representativos.

En edición nos debe aparecer algo así:


y ejecutado nos muestra algo parecido a esto:

Ahora asegurate de hacer clic en el form, fuera de la barra de herramientas y de la ficha Components elegimos la carpeta Control y de allí seleccionamos Edit Text MultiLinea la ubicamos de tal forma que nos ocupe todo el form y en la propiedad Name le asignamos el nombre txt_contenido.

Ahora vamos a programar los botones de la barra de herramientas, comenzaremos por el botón nuevo:

Selecciona de la ficha Project el control denominado Tool1c, luego ve a la ficha Events, clic en el botón E y en el evento EVT_TOOL asignamos el siguiente nombre de función crea_nuevo


Buscamos la función y digitamos:

def crea_nuevo(self,event): #init function
#add your code here
self.FileName = None
self.txt_contenido.Clear()
return #end function


Repetimos el procedimiento anterior asignando un nombre para cada función dependiendo el botón. Estos son los códigos de cada botón:

def abre_archivo(self,event): #init function
#add your code here
dlg = wx.FileDialog(self, "Abrir archivo", ".", "", "*.*", wx.OPEN)
try:
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetPath()
self.txt_contenido.LoadFile(filename)
self.FileName=filename
self.SetTitle(('Mi Editor - %s') % filename)
finally:
dlg.Destroy()


def guarda_archivo(self,event): #init function
#add your code here
dlg = wx.FileDialog(self, "Guardar como", ".", "", "*.*", wx.SAVE)
try:
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetPath()
self.txt_contenido.SaveFile(filename)
self.FileName=filename
self.SetTitle(('Mi Editor - %s') % filename)
finally:
dlg.Destroy()
return #end function


def salir_editor(self,event): #init function
#add your code here
self.Close()
return #end function



Ahora sólo queda probar el programa, ejecutalo y ensaya creando un texto pequeño y guardalo en algún lugar, luego trata de recuperarlo o abre otro archivo.

Y ya está.

Código fuente así como el tutorial lo encuentras aquí.