lunes, 6 de febrero de 2012

Aplicaciones con Interfaz grafica Python (GUI)

Prácticamente cualquier aplicación comercial que quiera hacerse debe contar con tres patas fundamentales.

Lógica programática.
Interfaz grafica
Base de datos.

Hablando desde mi experiencia personal, les cuento que yo me recibí en la facultad como Licenciado en Informática sin haber escrito nunca un programa que contuviera estas tres patas completas. Por lo cual cuando llego la hora de ponerme a laburar, no me sentía completamente capacitado para hacer sistemas.
Cuando salí de la facultad era un muy buen programador de scripts, pero un pésimo Programador de aplicaciones de escritorio.

Si bien la lógica de un programa suele ser la parte más compleja, es la primera que se aprende y esta bien que así sea. Desde nuestros comienzos cuando estudiamos cualquier lenguaje de programación y escribimos nuestros primeros algoritmos ya estamos desarrollando la lógica computacional que puede servir para resolver problemas de casi cualquier índole. El problema surge cuando queremos tomar datos o mostrar los resultados en una pantalla que sea amigable para el usuario final.
Ahí es cuando entra la Interfaz Grafica de Usuario también conocida como GUI. El tema es que cuando comencé a programar utilizaba Pascal o C, los cuales tienen un soporte para generar interfaces grafica muy muy pobres, luego “evolucione” a Visual Basic y a Java pero así y todo no terminaba de convencerme la manera en que se manejaban estas cuestiones. Por suerte apareció PyQt en mi vida…. J
En lo referente a las Bases de datos, en un primer momento lo hacia en archivos de Texto plano en donde tenia que ocuparme de TOOOOOODAAAA la lógica, para asegurar la integridad de los datos, trabajo tedioso si los hay. Luego comencé a trabajar con bases de datos relacionales pero haciendo todo a mano. Y por ultimo descubrí la “magia” de los ORM… Seba esta Feliz.. J  

Comencemos a ver algunas cositas pequeñas.

Como creo que ya dije en alguna oportunidad hoy en día (Febrero de 2012) me encuentro programando en Python 2.6, y para lo que son las interfaces graficas utilizo PyQt GPL v4.4.3 for Python v2.6. Como en el ejemplo que presentare a continuación usaremos Sqlite les recomiendo bajarse el Sqlite Manager del siguiente enlace:


PyQT tiene la particularidad de que genera archivos XML que luego pueden compilarse para generar interfaces graficas que se puedan utilizar en varios lenguajes, entre los que se encuentran C y Python entre otros.

El ejemplo que haremos será sencillo, crear un formulario que acepte 3 campos, Nombre, Apellido y dirección y que tenga tres botones, uno para guardar los datos en la Base de datos, otro para resetear el formulario y otro para cerrar.



Abrimos el Designer de PyQT y generamos la siguiente pantalla… Es muy intuitivo para utilizar, así que no me pondré a explicar paso a paso como hacerla (Si tenes dudas comenta, pregunta, googlea y seguro que obtenes respuesta)



Renombramos el formulario en el editor de propiedades que aparece a la derecha.




Modificamos el titulo de la ventaja tocando la propiedad siguiente:




La convención que utilizo para los nombres de los botones es btn<Accion>.


Agregamos tres labels con los textos:

Nombre
Apellido
Direccion

A los labels a menos que sea completamente necesarios le dejo el nombre por defecto que le pone PyQt.

Agregamos tres controles de los denominados lineEdit y los nombramos txtNombre, txtApellido y txtDireccion.

La pantalla final queda de la siguiente manera:


La guardamos con el nombre FormCargarPersona.ui.

Vamos a agregar un evento a un botón desde el PyQt Designer. Primero vamos a Edit Signals/Slots



Oprimimos clic derecho sobre el botón cerrar y luego arrastramos y soltamos sobre el fondo del Form. Se mostrara algo similar a esto:



Y se nos abrirá la siguiente pantalla. Sobre la parte izquiera se observan las señales habilitadas que puede enviar el boton:

clicked(), clicked(bool), pressed(). Etc..


Seleccionamos clicked() y checkeamos donde aparece la leyenda Show signals and slots inherited form QWidget. Nos apareceran mas opciones:



Seleccionamos clicked() y close(), y le damos click al boton Aceptar.

Volvemos a la edición de widget común clickeando en Edit Widgets



Y probamos que todo ande como nosotros queremos. Con CTRL+R corremos el formulario, ahora si pinchamos sobre el botón cerrar el mismo debería cerrarse.

Guardamos y listo por ahora… :)

Si editamos el archivo ui con un editor de texto, veremos que es código puramente XML.
Para compilarlo a lo que

Para compilar el código XML a código Python se utiliza un compilador llamado Pyuic4 que se encuentra por lo general en el Path: C:\Python25\Lib\site-packages\PyQt4 dependiendo la versión de Python que tengamos instalada.

Abrimos una consola y nos paramos en el Path en donde guardamos el archivo FormCargarPersona.ui. En ese lugar ejecutamos el siguiente comando:

>pyuic4 –x FormCargarPersona.ui –o FormCargarPersona.py



Esto generara en la carpeta C:\Python25\Lib\site-packages\PyQt4> el archivo FormCargarPersona.py. Cabe aclarar que no es recomendable modificar este archivo auto-generado. No es recomendable modificarlo, ya que ante cualquier modificación en la interfaz grafica, deberemos volver a compilar el archivo ui y esta compilación pisara cualquier cambio que se haya realizado en dicho archivo.

Por ahora pondremos todo en una misma carpeta, pero luego es aconsejable utilizar algún árbol de directorios como puede llegar a ser el siguiente:

Por gusto personal cuando debo crear una aplicación utilizo el sistema árbol de directorios, no es mi estilo decir cual es la forma correcta de trabajar.. J.. para mi la forma correcta es la que nos queda cómoda y da resultados…



En la carpeta archivos, guardo archivos que genere con mi aplicación, archivos de Texto, reportes en PDF y demás cuestiones.

En la carpeta Base de datos guardo todo lo referente a la conexión con la Base de datos, la creación de tablas, y las Clases de negocio que luego serán las que mapeo por medio del ORM con las tablas propiamente dichas.

En la carpeta diálogos guardo los códigos fuentes de lo que vendría a ser el controlador de las vistas… Al trabajar con Qt es casi obligatorio usar el patrón de diseño MVC, donde las vistas son los formularios creados con Qt y los controladores serian los diálogos en donde se pone toda la funcionalidad.

En la carpeta form_ui, guardo los formularios sin que hayan sido compilados, solo para tener el resguardo de la última versión.

En la carpeta forms, pongo los formularios ya compilados.

En images pongo las imágenes que usare dentro de mi aplicación, tanto iconos usados en los botones, como imágenes de fondo, etc.

En la carpeta impresión suelo poner los códigos fuentes de los programas dedicados a realizar reportes en PDF que luego se imprimen desde el sistema.

Por ultimo en la carpeta útiles, pongo código genérico que puedo utilizar en varios programas, para aumentar la reusabildiad de código.



Veamos un ejemplo funcional de cómo se realizaría la escritura en una Base de datos sencilla.

Ya tenemos nuestro FormCargarPersona.py… Ahora creemos el resto de los archivos necesarios para hacer funcional nuestro programa.

#conexion.py

import sqlalchemy as sql
from PyQt4.QtGui import *
from sqlalchemy.orm import sessionmaker

class conexion():
   
    def __init__(self):
        self.my_db = sql.create_engine('sqlite:///personas')
        self.metadata  = sql.MetaData(self.my_db)
   
   
    def crearSession(self):
        Session = sessionmaker(bind=self.my_db)
        session = Session()
        return session

#creacionTablas.py

from sqlalchemy import Table, Column, Integer, String, MetaData, Sequence
from conexion import conexion
from sqlalchemy.orm import mapper

from Personas import Personas

class creacionTablas():
    def __init__(self):
        bd = conexion()
        metadata = MetaData()
        print 'Creacion de tablas'


        tabla_personas = Table('personas', metadata,
            Column('idPersona', Integer, Sequence('user_id_seq'), primary_key=True),
            Column('nombre', String(30)),
            Column('apellido', String(30)),
            Column('direccion', String(30))
        )

        metadata.create_all(bd.my_db)

        mapper(Personas,tabla_personas)





#Personas.py

class Personas(object):

    def __init__(self, nombre, apellido, direccion):
        self.nombre = nombre
        self.apellido = apellido
        self.direccion = direccion


#PrincipalPersona.py

import sys
import PyQt4
sys.path.append("..")

import creacionTablas
from PyQt4.QtGui import *
from PyQt4 import QtGui
from PyQt4 import QtCore

from dlgCargarPersonas import dlgCargarPersonas

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    creacionTablas.creacionTablas()
    d = dlgCargarPersonas()
    d.exec_()


#dlgCargarPersonas.py

from PyQt4 import QtCore, QtGui
from FormCargarPersona import Ui_FormCargarPersona
from Personas import Personas
import conexion
import PyQt4
import sys

class dlgCargarPersonas(QtGui.QDialog):

    def __init__(self):
        QtGui.QDialog.__init__(self)
        self.ui = Ui_FormCargarPersona()
        self.ui.setupUi(self)
        conect = conexion.conexion()
        self.session = conect.crearSession()
        self.connect(self.ui.btnAceptar, QtCore.SIGNAL("clicked()"), self.prueba)
       
    def cargarPersona(self):
        if(self.ui.txtApellido.text() <> "" and
           self.ui.txtNombre.text() <> ""):
            nombre = str(self.ui.txtNombre.text())
            apellido = str(self.ui.txtApellido.text())
            direccion = str(self.ui.txtDireccion.text())
            persona = Personas(nombre, apellido, direccion)
            self.session.add(persona)
            self.session.commit()
   
Poniendo estos 5 archivos en una misma carpeta y ejecutando PrincipalPersona.py, debería poderse guardar personar en la base de datos. Para ver si efectivamente se guardaron los datos en la base de datos podemos usar el sqliteman, que ya descargamos previamente, abrimos la base de datos que se llama personas, que se encontrara en la misma carpeta en donde tenemos todos los fuentes, y hacemos un Select * from personas. 

ORM – Sqlalchemy (Bases de datos Python)

Desde ya hace un buen tiempo que trabajar con Bases de Datos dejo de ser algo tedioso y complicado, mucha de esta simplificación se la debemos a los ORM.

Los ORM básicamente sirven para realizar una conversión directa de objetos software a registros o tablas de una Base de datos relacional.

Supongamos que tenemos la clase Persona con los atributos, nombre, apellido y dirección.
Por otro lado deberíamos tener la tabla tPersona en la Base de datos, con los campos idPersona, apellido, nombre y dirección.

Antiguamente y a grandes razgos para guardar una persona en la Base de datos se tenia que escribir dentro del código fuente de nuestro programa, la sentencia sql que ejecutaba el insert. Así como también hacer el mapeo uno a uno de los atributos de la clase con los campos de la tabla. También se debía crear la conexión manualmente y los programas quedaban fuertemente acoplados con la Base de Datos utilizada. Es decir migrar un programa que usara MySql a uno que usara PostgreSql no era algo trivial.
Por esta razón el código quedaba “sucio” y su escalabilidad y mantenimiento se volvía complicado.

Los ORM sirven para subsanar estos inconvenientes, y hacer que el hecho de trabajar con Bases de datos relacionales sea algo mucho más llevadero.

Por ejemplo si quisiéramos escribir en la Base de datos una persona sin utilizar la potencia de un ORM, seria más o menos así...

(NO ESTA DE MAS ACLARAR QUE ESTE ES UN EJEMPLO ILUSTRATIVO Y NO FUNCIONAL…)

class Persona(object):

    def __init__(self, nombre, apellido, direccion):
        self.nombre = nombre
        self.apellido = apellido
        self.direccion = direccion

tablaPersona = Table('tPersona', self.metadata, autoload=True)

persona = Persona(“Seba”, “Bataglia”, “Dire seba”)

sentenciaSql = “Insert into tPersona (nombre, apellido, direccion) values(‘“ + persona.nombre + “’,’” + persona.apellido + “’,’”+persona.direccion+”’)”

ejecutarSql(sentenciaSql)

Ahora bien, si quisiéramos hacer lo mismo con un ORM bastaría con hacer algo así.

persona = Persona(“Seba”, “Bataglia”, “Dire seba”)
persona.Save()

Como puede observarse el código queda muchísimo más limpio. Y nos desligamos completamente de escribir Sql dentro de nuestro código fuente.

Esta es una muy muy breve introducción a lo que son los ORM. En la próxima entrega veremos como crear una aplicación con interfaz grafica por medio de PyQT y luego haremos una aplicación que sirva para persistir datos, bien basica. 

DICCIONARIOS PYTHON! [:-)]

**************************

Un diccionario es una estructura de datos que se caracteriza por realizar la indexación a sus datos por medio de una palabra o clave. En lugar de que el índice de la estructura sea un entero, en el diccionario la clave puede ser cualquier valor, un entero, un carácter o una palabra.

El formato típico de los diccionarios es el siguiente:

Diccionario = <clave, valor>

En donde la clave es el índice por el cual se accede al valor que le corresponde.

En Python se mantiene dicha nomenclatura para lo que son los diccionarios. En el siguiente ejemplo se puede observar como crear un diccionario en Python.

>>> dictPersona = {}
>>> dictPersona["nombre"] = "Pedro"
>>> dictPersona["apellido"] = "Andrade"
>>> dictPersona["dni"] = 31914936
>>> print dictPersona
{'nombre': 'Pedro', 'dni': 31914936, 'apellido': 'Andrade'}
>>> print dictPersona["nombre"]
Pedro

Los diccionarios son una estructura de datos fundamental cuando se quiere trabajar con Bases de Datos, ya que la mayoría de los ORM (Modelo Objeto-Relacional), utilizan listas de diccionarios para devolver el contenido de las consultas. Esto se debe a que la lista de diccionarios debe ser la forma mas cómoda y natural de representar una tabla, ya que los distintos elementos de la lista representarían cada uno de los reglones/registros y cada una de las tuplas Clave:Valor, reprensaría las columnas.

>>> tablaPersona = [{"nombre": "Jose", "apellido": "Thomas"},
...                 {"nombre": "Marcos", "apellido": "Aguirre"},
...                 {"nombre": "Rodolfo", "apellido": "Ricardi"}]


>>> for dict in tablaPersona:
...     print "-----------------------------"
...     for clave in dict.keys():
...             print str(clave) + "  =  " + str(dict[clave])
...     print "-----------------------------"
...


-----------------------------
nombre  =  Jose
apellido  =  Thomas
-----------------------------
-----------------------------
nombre  =  Marcos
apellido  =  Aguirre
-----------------------------
-----------------------------
nombre  =  Rodolfo
apellido  =  Ricardi
-----------------------------

El utilizar diccionarios nos da independencia de la posición física del dato dentro de la estructura.

Por ejemplo si tenemos el vector Persona y lo definimos de la siguiente manera:

>>> persona = ["Sebastian", "Schanz"]
>>> def mostrarPersonaVector(persona):
...     print "Nombre: " + persona[0]
...     print "Apellido: " + persona[1]
...
>>> mostrarPersonaVector(persona)
Nombre: Sebastian
Apellido: Schanz
>>> persona.insert(0,"31914934")
>>> mostrarPersonaVector(persona)
Nombre: 31914934
Apellido: Sebastian

Si tienes alguna consulta no dudes en dejar tu comentario que tratare de darte una mano a la mayor brevedad posible!...

Recorda que esto es una simple muestra de la potencia que tienen los diccionarios de Python y de cualquier lenguaje.