jueves, 25 de junio de 2009

Crear una [pequeña] aplicación completa usando wxPython (Parte 3)

.
10 comentarios

En esta oportunidad crearemos el Controlador de la Vista Principal, y por primera vez podremos ejecutar nuestra aplicación. Sin mas preámbulo, empecemos!.

Codificando el Controlador de la Vista Principal

Para comenzar, voy a citar el punto 2 del artículo de la Wikipedia que les comentaba en la parte anterior del tutorial.

"… 1. El controlador recibe (por parte de los objetos de la interfaz-vista) la notificación de la acción solicitada por el usuario. El controlador gestiona el evento que llega, frecuentemente a través de un gestor de eventos (handler) o callback. ..."

Este punto nos esta diciendo:

1. El Controlador recibe desde la Vista, la notificación de la acción solicitada por el usuario (ej: "se pulso el botón Salir", "se pulso el botón Agregar Película", etc.).

2. El Controlador gestiona el evento, haciendo una llamada a quien corresponda para que atienda esa acción en particular (ej: "Si el usuario pulso el botón Agregar Película llamar al método onAgregarPelicula, para que se encargue de mostrar el Editor de Películas").

Teniendo estos dos puntos presentes, nuestro Controlador para la Vista Principal, podría (inicialmente) ser algo así:

controladorVistaPrincipal.py
import wx

from wx import xrc

from vistas.vistaPrincipal import VistaPrincipal

class ControladorVistaPrincipal:
def __init__(self, app):
self.app = app

self.vista = VistaPrincipal(app)

#Items del Menú
self.vista.frame.Bind(wx.EVT_MENU, self.onMostrarAgregarPelicula, id=xrc.XRCID('itemAgregarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onMostrarEditorDePelicula, id=xrc.XRCID('itemEditarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onEliminarPelicula, id=xrc.XRCID('itemEliminarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onSalir, id=xrc.XRCID('itemSalir'))

#Botones de la barra de herramientas
self.vista.frame.Bind(wx.EVT_TOOL, self.onMostrarAgregarPelicula, id=xrc.XRCID('toolAgregarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onMostrarEditorDePelicula, id=xrc.XRCID('toolEditarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onEliminarPelicula, id=xrc.XRCID('toolEliminarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onBuscarPelicula, id=xrc.XRCID('toolBuscarPelicula'))

def onMostrarAgregarPelicula(self, evt):
...

def onMostrarEditorDePelicula(self, evt):
...

def onEliminarPelicula(self, evt):
...

def onBuscarPelicula(self, evt):
...

def onSalir(self, evt):
self.app.Exit()

Lo primero que hacemos en esta clase es crear la Vista Principal de la aplicación (aunque aún no la mostramos).

self.vista = VistaPrincipal(app)

después "enlazamos" los eventos que nos interesan con su correspondiente manejador de evento. Primero lo hacemos para cuando sean activados los items del menú

#Items del Menú
self.vista.frame.Bind(wx.EVT_MENU, self.onMostrarAgregarPelicula, id=xrc.XRCID('itemAgregarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onMostrarEditorDePelicula, id=xrc.XRCID('itemEditarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onEliminarPelicula, id=xrc.XRCID('itemEliminarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onSalir, id=xrc.XRCID('itemSalir'))

y luego para cuando sean pulsados los botones de la barra de herramientas

#Botones de la barra de herramientas
self.vista.frame.Bind(wx.EVT_TOOL, self.onMostrarAgregarPelicula, id=xrc.XRCID('toolAgregarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onMostrarEditorDePelicula, id=xrc.XRCID('toolEditarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onEliminarPelicula, id=xrc.XRCID('toolEliminarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onBuscarPelicula, id=xrc.XRCID('toolBuscarPelicula'))

Pueden observar que algunos manejadores de eventos son llamados como respuesta a mas de un evento, por ej: tanto el ítem del menú "Eliminar" como el botón "Eliminar" de la barra de herramientas, están asociados al manejador de eventos onEliminarPelicula.

Como ultimo paso, creamos (a medias) los distintos manejadores de eventos que serán llamados según las acciones que solicite el usuario.

def onMostrarAgregarPelicula(self, evt):
...

def onMostrarEditorDePelicula(self, evt):
...

def onEliminarPelicula(self, evt):
...

def onBuscarPelicula(self, evt):
...

def onSalir(self, evt):
self.app.Exit()

Ahora haremos referencia al punto 2 del artículo de la Wikipedia, para seguir creando nuestro Controlador

"... 2. El controlador accede al modelo, actualizándolo, posiblemente modificándolo de forma adecuada a la acción solicitada por el usuario (por ejemplo, el controlador actualiza el carro de la compra del usuario). Los controladores complejos están a menudo estructurados usando un patrón de comando que encapsula las acciones y simplifica su extensión. ..."

Leyendo este punto, vemos que nuestro Controlador se encarga de acceder al Modelo y hacer las modificaciones necesarias. En nuestro caso la tarea de agregar y eliminar una película no estará a cargo de este Controlador sino a cargo del Controlador de la Vista del Editor de Películas, pero en el caso de que se quiera eliminar una película, esta acción si será llevada a cabo por el método onEliminarPelicula de este Controlador, y sería mas o menos así:

def onEliminarPelicula(self, evt):
id_pelicula = self.vista.getIDItemSeleccionado()

self.app.modelo.eliminarPelicula(id_pelicula)

así de simple es la tarea de eliminar la película que este seleccionada en el listado de la Vista Principal. Primero obtenemos el ID de la película seleccionada con el método getIDItemSeleccionado de la Vista Principal y luego llamamos al método onEliminarPelicula del Modelo pasándole el ID de la película que deseamos borrar, pero por si no lo recuerdan, el método getIDItemSeleccionado aún no lo hemos creado, así que debemos agregar el siguiente código a la clase VistaPrincipal:

def getIDItemSeleccionado(self):
itemIndex = self.listaDePeliculas.GetFirstSelected()
return self.listaDePeliculas.GetItemData(itemIndex)

en este método obtenemos un referencia al ítem seleccionado y luego retornamos el ID que esta asociado a éste.

Por ahora no nos estamos preocupando por las validaciones, pero mas adelante volveremos sobre esto. También deberíamos pedir una confirmación antes de borrar la película, e incluso podríamos dar la posibilidad de borrar de a mas de una película al mismo tiempo, pero por el momento quedará pendiente.

Los métodos onMostrarAgregarPelicula y onMostrarEditorDePelicula serán (justamente) los encargados de mostrar el Editor de Películas en modo Agregar o Modificar respectivamente. Como aún no tenemos ni la Vista ni el Controlador del Editor de Películas, por ahora haremos que sólo se muestre un mensaje diciendo qué debería suceder al ser llamados, es decir que quedarían así:

def onMostrarAgregarPelicula(self, evt):
wx.MessageBox("Mostrar el Editor de Películas para agregar una película")

def onMostrarEditorDePelicula(self, evt):
wx.MessageBox("Mostrar el Editor de Películas para modificar la película seleccionada")

También hacemos lo mismo con el método onBuscarPelicula:

def onBuscarPelicula(self, evt):
wx.MessageBox("Buscar una película")

En este punto nos referiremos a una parte del punto 4 del artículo que hemos tomado como referencia:

"... 4. … El controlador no pasa objetos de dominio (el modelo) a la vista aunque puede dar la orden a la vista para que se actualice. Nota: En algunas implementaciones la vista no tiene acceso directo al modelo, dejando que el controlador envíe los datos del modelo a la vista. ..."

De las dos implementaciones que nos habla este punto, nosotros usaremos la primera, es decir que el Controlador no pasará información del Modelo a la Vista, sino que el Controlador sólo avisará cuando haya alguna modificación en el Modelo, y será la Vista la encargada de actualizarse. Teniendo esto claro, necesitamos algún mecanismo para que el Controlador "se entere" de que el Modelo ha cambiado. Si hacemos un poco de memoria, recordaremos que el modelo "avisaba" cuando se agregaba, modificaba o eliminaba una película haciendo uso del modulo wx.lib.pubsub, a través del método sendMessage() del objeto Publisher, o sea que lo único que nos quedaría por hacer es suscribirnos a esos mensajes y así quedarnos a la escucha de estos. Para llevar a cabo esta tarea, simplemente utilizamos el método suscribe() del objeto Publisher de la siguiente manera:

Publisher.subscribe(self.seModificoElModelo, 'pelicula_agregada')
Publisher.subscribe(self.seModificoElModelo, 'pelicula_modificada')
Publisher.subscribe(self.seModificoElModelo, 'pelicula_eliminada')

Con estas tres lineas, lo que estamos haciendo es quedarnos a la escucha de los mensajes 'pelicula_agregada', 'pelicula_modificada' y 'pelicula_eliminada', y de ser recibidos, llamamos (por ahora) al método seModificoElModelo, que codificaremos así:

def seModificoElModelo(self, mensaje):
self.vista.cargarListadoDePeliculas()

Lo único que estamos haciendo aquí, es invocar el método cargarListadoDePeliculas() de la Vista Principal, con lo cual se recargarían las películas y así se vería reflejado el cambio que se ha producido en el Modelo (se agrego, modifico o elimino una película).

Por último, una vez que nos suscribimos a los mensajes que nos interesan, nos queda cargar el listado de películas y hacer visible la Vista:

self.vista.cargarListadoDePeliculas()
self.vista.Mostrar()

A continuación les muestro como queda la clase completa hasta ahora:

import wx

from wx import xrc
from wx.lib.pubsub import Publisher

from vistas.vistaPrincipal import VistaPrincipal

class ControladorVistaPrincipal:
def __init__(self, app):
self.app = app
self.vista = VistaPrincipal(app)

#Items del Menú
self.vista.frame.Bind(wx.EVT_MENU, self.onMostrarAgregarPelicula, id=xrc.XRCID('itemAgregarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onMostrarEditorDePelicula, id=xrc.XRCID('itemEditarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onEliminarPelicula, id=xrc.XRCID('itemEliminarPelicula'))
self.vista.frame.Bind(wx.EVT_MENU, self.onSalir, id=xrc.XRCID('itemSalir'))

#Botones de la barra de herramientas
self.vista.frame.Bind(wx.EVT_TOOL, self.onMostrarAgregarPelicula, id=xrc.XRCID('toolAgregarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onMostrarEditorDePelicula, id=xrc.XRCID('toolEditarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onEliminarPelicula, id=xrc.XRCID('toolEliminarPelicula'))
self.vista.frame.Bind(wx.EVT_TOOL, self.onBuscarPelicula, id=xrc.XRCID('toolBuscarPelicula'))

#Me suscribo a los mensajes que indican que se modifico el Modelo
Publisher.subscribe(self.seModificoElModelo, 'pelicula_agregada')
Publisher.subscribe(self.seModificoElModelo, 'pelicula_modificada')
Publisher.subscribe(self.seModificoElModelo, 'pelicula_eliminada')

#Cargo la lista de peliculas
self.vista.cargarListadoDePeliculas()

self.vista.Mostrar()

def onMostrarAgregarPelicula(self, evt):
wx.MessageBox("Mostrar el Editor de Peliculas para agregar una pelicula")

def onMostrarEditorDePelicula(self, evt):
wx.MessageBox("Mostrar el Editor de Peliculas para modificar la pelicula seleccionada")

def onEliminarPelicula(self, evt):
id_pelicula = self.vista.getIDItemSeleccionado()
self.app.modelo.eliminarPelicula(id_pelicula)

def onBuscarPelicula(self, evt):
wx.MessageBox("Buscar una pelicula")

def onSalir(self, evt):
self.app.Exit()
Con esto tenemos el Modelo, y la Vista y el Controlador de la Ventana Pricipal en condiciones de ser ejecutados, pero aún nos falta crear la clase que representa la aplicación, que será el punto de partida para que nuestro Catálogo puede iniciarse.

Codificando la clase de la Aplicación

Primero les mostrare el código de esta pequeña clase, y luego iremos paso a paso viendo que es lo que hace:

catalogo.py
import wx
from wx import xrc
from controladores.controladorVistaPrincipal import ControladorVistaPrincipal
from modelo import Model

class CatalogoDePeliculas(wx.App):
def OnInit(self):
self.modelo = Model()
self.res = xrc.XmlResource('gui.xrc')

self.controladorVistaPrincipal = ControladorVistaPrincipal(self)

return True

app = CatalogoDePeliculas()
app.MainLoop()

En primer lugar vemos que dentro de los modulos que importamos, se encuentran el Controlador de la Vista Principal (ControladorVistaPrincipal) y el Modelo (Model). También notamos que el nombre de la clase es CatalogoDePeliculas y que esta deriva de wx.App. Ya en el método OnInit, vemos que instanciamos self.modelo con el Modelo de la aplicación:

self.modelo = Model()

luego, cargamos el archivo de recursos

self.res = xrc.XmlResource('gui.xrc')

creamos el Controlador de la Vista Principal, al cual le pasamos la propia aplicación como parámetro

self.controladorVistaPrincipal = ControladorVistaPrincipal(self)

y ya fuera de la clase, creamos la aplicación e iniciamos el bucle principal.

De este modo, estamos listos para poder ver nuestra aplicación en marcha, ejecutando este archivo (catalogo.py). Si bien aún no podemos editar las películas del catálogo, podriamos agregar algunas películas en forma manual dentro de la base de datos, y al ejecutar el catálogo veríamos que estas se cargarían en el listado, e incluso las podríamos eliminar.

Hasta aquí hemos llegado en esta entrega. En la próxima trabajaremos en el Editor de Películas y así seguiremos agregando funcionalidad a nuestro pequeño Catálogo de películas.

Saludos!

Cómo usar Blogger con un dominio personalizado (.com.ar)? [Actualizado - Junio de 2009]

.
66 comentarios

Importante:

Dado que ha habido un cambio en la configuración de dominios personalizados en Blogger, he creado este nuevo post actualizado (Junio de 2009) para reflejar esta situación. Si quieres ver la versión anterior haz clic AQUÍ.

A la hora de crear este blog, y dado que se encuentra en Blogger, la dirección original era www.pablotilli.blogspot.com, pero mi idea desde un principio fue utilizar mi propio dominio (www.pablotilli.com.ar), por lo cual tuve que pasar por varias etapas para poder lograrlo y es por esto que me decidí a escribir como hacerlo, para que ustedes también puedan asociar el suyo.

En mi caso, el dominio pablotilli.com.ar ya lo tenía registrado, pero estaba asociado con el hosting de Xmundo (servicio que recomiendo, pues en mi caso en particular, nunca tuve un problema). Asumiendo que nuestro blog ya esta creado, la idea es hacer que nuestro dominio apunte a Blogger, es por esto que el primer paso seria ingresar a NIC.ar y entrar en la zona de "trámites vía web".


Dentro de los trámites que nos permiten realizar vía web, se encuentra el que nos va a interesar si el dominio ya lo tenemos registrado: "Modificaciones de delegaciones".


Una vez dentro de la página de "Modificación de datos de delegación de un dominio" ingresamos el e-mail del responsable, el domino que queremos modificar y pulsamos "continuar".


Aquí surge un inconveniente, ya que Blogger nos pide crear un registro CNAME apuntando a ellos (ghs.google.com), pero NIC.ar no permite la creación de este tipo de registros, sólo podemos especificar los DNS de nuestro servidor.


Cuando me encontré con esta situación me puse a investigar un poco y finalmente la solución fue usar el servicio de DNS estático de CDmon. CDmon nos permite (entre otros muchos servicios) crear registros CNAME a través de su servicio gratuito. Para lograr esto, ante todo debemos registrarnos en CDMon (lo cual no tiene ninguna complejidad).

Para poder usar el servicio de CDMon volvemos a donde nos habíamos quedado en NIC.ar y donde nos permite especificar los DNS de nuestro servidor, escribimos los de CDmon y pulsamos "continuar".

ns1.cdmon.net -> 212.36.74.129

ns2.cdmon.net -> 212.36.75.129

ns3.cdmon.net -> 213.186.58.203

Ahora debemos especificar el proveedor de servicios de Internet (ISP); para esto escribimos 10dencehispahard SL, y pulsamos "buscar".

elegimos el ISP de la lista de resultados (Al dia de hoy <08-01-2008> hay dos resultados y debemos elegir el primero, el cual no termina con punto) y pulsamos "continuar".


En este punto debemos especificar el contacto técnico de CDmon: Josep Colominas. Al igual que en el caso anterior, escribimos el nombre y pulsamos "buscar".


Elegimos Josep Colominas en la lista de resultados y pulsamos "continuar".


Ahora nos mostraran una página de confirmación con todos los datos que ingresamos. Revisamos que todo este bien, y pulsamos "confirmar". Por ultimo, nos aparecerá una página avisando que NIC.ar nos ha enviado un e-mail que deberemos reenviar tal cual como lo recibimos. Una vez reenviado el e-mail, deberemos esperar aprox. 48 horas hábiles para que finalice el tramite (Si el dominio lo estamos registrando y no modificando como en mi caso, te puede tardar incluso mas de 7 días ...paciencia!!!).

Una vez que NIC.ar nos envié el e-mail avisando que el tramite ha finalizado, entramos a nuestro panel de control de CDMon, e ingresamos en la opción "DNS estático".

Dentro de la página de "Panel de control del servidor DNS estático" debemos añadir nuestro dominio como se muestra en la siguiente imagen:


El siguiente paso es la primera diferencia que hay con el metodo anterior. Al añadir nuestro dominio en CDmon, en este punto creavamos un registro CNAME, pero ahora, de las varias opciones que hay, nos interesa la que se llama "Redirección URL".


Aquí debemos especificar que el dominio principal (tu-dominio.com.ar) sea redireccionado a www.tu-dominio.com.ar. Para esto sólo debemos escribir la URL destino (www.pablotilli.com.ar en mi caso) y pulsar "guardar".


Si todo fue bien, nuestro Panel de control debería quedar como en la siguiente imagen:


Como se puede ver en la parte superior de la captura de pantalla, CDmon nos indica el tiempo estimado para que el cambio sea efectivo (en este caso faltaban 24 minutos aprox.). Hasta aquí especificamos que la dirección: "http://www.tu-dominio.com.ar" apunte a Blogger, pero aún nos falta hacer lo mismo con la dirección "http://tu-dominio.com.ar" (sin www). Para esto pulsamos sobre el botón de "Crear un registro A (un subdominio)".


Ahora nos queda por escribir el nombre del subdomino, para lo cual solo debemos escribir www, y pulsamos "guardar":

Al pulsar "guardar" volveremos a nuestro Panel de control donde veremos que se ha listado el subdominio que acabamos de agregar, pero aun falta configurarlo, por lo que dice "Por determinar". Para configurarlo pulsamos el botón de "Editar la configuración del registro" como se ve en la imagen a continuación:


De la opciones que nos aparecen para configurar nuestro subdominio, elegimos "Registro CNAME".


En esta página debemos escribir como dominio: ghs.google.com, y pulsamos "guardar".


Tras pulsar "guardar", ya esta todo listo; sólo debemos habilitar el dominio personalizado en Blogger, y esperar el tiempo que CDmon nos informa que falta para que los cambios sean efectivos. El panel de control de CDmon, finalmente debería quedarte asi:



Para habilitar el dominio personalizado en Blogger, accedemos a nuestro Blog con nuestra cuenta, y una vez dentro, elegimos la opción "Configuración".

vamos a la opciones de "Publicación",

hacemos click en la opción de "cambiar por Dominio personalizado",

escribimos nuestro dominio .com.ar (con www, ya que este es el nuevo requerimiento de Blogger para los dominios personalizados, y es el motivo de la actualizacion de este Post),


y por ultimo simplemente escribimos el texto de verificación, pulsamos "guardar configuración" y tarea cumplida.


Si algo no te quedo claro, no dudes en preguntar. Si quieres hacer alguna critica o sugerencia, espero tu comentario también.