Documentos de Académico
Documentos de Profesional
Documentos de Cultura
PensandoEnPythonII PDF
PensandoEnPythonII PDF
LISTADO 1 Hola Solop en wxPython cada control se detallan los eventos que ste
puede generar.
#!/usr/bin/env python
import wx
Gestores de posicin: sizers
class MiMarco(wx.Frame): Los gestores de posicin, representados por la
Clase frame que visualiza una imagen. clase wx.Sizer y sus descendientes en la jerar-
def __init__(self, imagen, padre=None, id=-1,
pos=wx.DefaultPosition, titulo=Hola solo programadores): qua de clases de wxPython, se han converti-
Crea una instancia de Frame y visualiza una imagen. do en el mtodo elegido para definir el posi-
temp = imagen.ConvertToBitmap() cionamiento de controles en dilogos, dada su
tamano = temp.GetWidth(), temp.GetHeight()
wx.Frame.__init__(self, padre, id, titulo, pos, tamano) habilidad para crear dilogos atractivos inde-
self.bmp = wx.StaticBitmap(self, -1, temp) pendientes de la plataforma, que tengan en
consideracin las diferencias en tamao y
class MiAplic(wx.App): estilo de los controles individuales. El cuadro
La clase aplicacion.
def OnInit(self): Gestores de posicin ms comnmente utili-
wx.InitAllImageHandlers() zados muestra algunos de los sizers disponi-
imagen = wx.Image(solop.jpg, wx.BITMAP_TYPE_JPEG) bles en wxPython.
self.miMarco = MiMarco(imagen)
self.miMarco.Show()
self.SetTopWindow(self.miMarco) Gestores de posicin ms comnmente
return True utilizados
if __name__ == __main__: Sizer Clase base abstracta
miAplic = MiAplic() GridSizer Un gestor de posicin que dispone
miAplic.MainLoop()
los controles en una matriz donde
las celdas tienen el mismo tamao.
correspondiente. wxGlade (http://wxglade.sour- BoxSizer Gestor para disponer controles en
ceforge.net/) es un buen ejemplo. wxGlade es un una fila o columna.
diseador de interfaces que asiste en la creacin
de interfaces grficas con wxPython y Dibujando grficos
wxWidgets. Genera cdigo fuente tanto en En wxPython no se puede dibujar directamen-
Python como en C++. te en una ventana. Es preciso utilizar un con-
texto de dispositivo (device context o DC). DC
Manejo de eventos es la clase base para ClientDC, PaintDC,
Toda ventana, y, por extensin, todo control MemoryDC, PostScriptDC, MemoryDC,
(dado que los controles derivan de la clase MetafileDC and PrinterDC. La virtud de los DC
wx.Window), definen una serie de eventos que es que se puede utilizar exactamente el mismo
se lanzan cuando el usuario interacta sobre cdigo para dibujar encima de cualquiera de
ellos. wxPython define por cada control una estos dispositivos. Algunos ejemplos de las
tabla de eventos que asocia un tipo de evento funciones disponibles para dibujar son
con un gestor del mismo. Por ejemplo, como DC.DrawLine, DC.DrawCircle o DC.DrawText.
se ilustra debajo, para el evento EVT_BUTTON Para optimizar el pintado de figuras geomtri-
que ocurre cuando se pulsa un botn cas, es recomendable incluir las primitivas de
(botonNuevoUsuario), se podra asociar el dibujo entre las llamadas DC.BeginDrawing y
manejador OnNuevoUsuario. Obsrvese que el DC.EndDrawing. El color del fondo de cada
segundo parmetro de este mtodo evt es de figura dibujada se puede controlar con el con-
tipo wx.Event. Este objeto contiene informa- cepto de brocha (Brush) y el grosor y color de
cin sobre el evento: su tipo, identificador del los bordes de cada figura con el concepto de
control que lo gener, cundo ocurri el bolgrafo (Pen).
evento, una referencia al objeto que lo gene-
r, etc.: Aadiendo interfaz grfica a la aplicacin
... de tres en raya
wx.EVT_BUTTON(self, self.botonNuevoUsuario. Recordemos que en la anterior entrega definimos
GetId(), self.OnNuevoUsuario) una aplicacin de tres en raya en modo texto que
... requera el login de cada jugador y mantena en
Figura 2. Ventana de login a memoria las estadsticas sobre los resultados de
la aplicacin tres en raya. def OnNuevoUsuario(self, evt): sus partidas. Usando wxPython, aadiremos
# cdigo para crear nuevo usuario ahora las siguientes ventanas grficas a la aplica-
cin tres en raya:
Para poder conocer qu eventos puede gene- 1- Ventana de login (vase la figura 2) a travs de
rar cada control en wxPython remitimos al la cual el usuario puede entrar en la aplicacin,
lector a la documentacin de wxWidgets, que crear nuevos detalles de login de un usuario o
acompaa a la distribucin de wxPython. Por simplemente salir de la aplicacin.
LISTADO 3 Manejadores de eventos de la clase LoginGUI El constructor (lneas finales del listado 2) usa
gestores de posicionamiento de tipo BoxSizer
def OnSalir(self, evt):
self.__cerrarAplicacion()
para colocar tanto en vertical como en horizon-
tal los controles antes mencionados. Cuando
def OnLogin(self, evt): creamos un BoxSizer indicamos la direccin en
try: la que colocaremos los controles, y luego sim-
self.registro.login(self.nombreUsuarioTxtCtrl.GetValue(),
self.passwordTxtCtrl.GetValue()) plemente usamos el mtodo Add del BoxSizer
except: para aadir controles al mismo. Como ltima
mostrarDialogo(self, No hay ningun usuario registrado con el nombre lnea del constructor definimos un manejador
de usuario y password especificados, Login incorrecto)
return para el evento cerrar ventana.
self.bitmaps.append(wx.Bitmap
(vase la figura 3) donde se podrn introducir (images/blank.png, wx.BITMAP_TYPE_PNG))
los detalles de un nuevo usuario y esconder self.celdaTablero.append(wx.BitmapButton
la ventana de login. El manejador OnLogin, (self, i, self.bitmaps[i]))
comprobar que el usuario ha sido registrado
Figura 4. Ventana principal de wx.EVT_BUTTON(self, i , self.OnClick)
y si es as, esconder la ventana de login y
la aplicacin tres en raya, con
ventana de partida y dilogo crear una ventana principal de la aplicacin self.gridsizer.Add(self.celdaTableroa[i])
de resultado. (vase la figura 4).
self.gridsizer.Fit(self)
self.SetAutoLayout(True)
self.SetSizer(self.gridsizer)
Dilogo de resultado de partida (figura 4)
Todos los resultados, o mensajes que hay que dar
al usuario se visualizan a travs de una funcin de
ayuda, que simplemente instancia un
wx.MessageDialog:
def mostrarDialogo(ventanaPadre, mensaje,
titulo, modo=wx.OK):
dialog = wx.MessageDialog(ventanaPadre,
mensaje, titulo, modo);
dialog.ShowModal()
partidasJugadas = self.resultadoEstadisticas[0] +
wx.Frame. En el constructor se le asigna un self.resultadoEstadisticas[1] + self.resultadoEstadisticas[2] + 0.0
tamao fijo de 640x480 pixels y se define un if partidasJugadas > 0:
manejador OnPaint para el redibujado de la alturaBarraGanadas =
int(250*(self.resultadoEstadisticas[0]/partidasJugadas))
ventana. Este evento ser invocado cada vez alturaBarraEmpatadas =
que se mueva, maximice o cambie el tamao int(250*(self.resultadoEstadisticas[1]/partidasJugadas))
de la ventana de estadsticas. En OnPaint, se alturaBarraPerdidas =
crea una instancia de DC, se limpia el rea de int(250*(self.resultadoEstadisticas[2]/partidasJugadas))
if alturaBarraGanadas < 1:
dibujado del marco y se inicia el redibujado alturaBarraGanadas = 1
con la sentencia dc.BeginDrawing(). A conti- if alturaBarraEmpatadas < 1:
nuacin, se inicializa el formato de los trazos a alturaBarraEmpatadas = 1
if alturaBarraPerdidas < 1:
dibujar con un objeto Pen de color negro: alturaBarraPerdidas = 1
dc.SetPen(wx.Pen(BLACK,1)). Similarmente, else:
se inicializa una brocha de color rojo para el alturaBarraGanadas = 1
relleno de la primera barra de resultados a alturaBarraEmpatadas = 1
alturaBarraPerdidas = 1
dibujar: dc.SetBrush(wx.Brush(RED)). Luego
se dibujan las barras que muestran el porcen- cabecera = Total partidas jugadas: + str(int(partidasJugadas))
taje de victorias, empates y partidas perdidas (w, h) = dc.GetTextExtent(cabecera)
dc.DrawText(cabecera, 320-(w/2), 70-h) # dibujar cabecera grficos
por un jugador. Se calculan las alturas de cada
una de las barras y a continuacin se procede (w, h) = dc.GetTextExtent(Ganadas) # cabecera barra Ganadas
a su dibujado, por medio de las primitivas dc.DrawText(Ganadas, 160-(w/2), 390-h)
dc.DrawRectangle(100, 350, 120, -alturaBarraGanadas) # dibujar barra
dc.DrawRectangle. Es importante indicar que de ganadas
el origen de coordenadas de dibujado se (w, h) = dc.GetTextExtent(`self.resultadoEstadisticas[0]`)
encuentra en la parte superior izquierda de la dc.DrawText(`self.resultadoEstadisticas[0]`, 160-(w/2), 350-
ventana. Esa es la razn por la que se utilizan alturaBarraGanadas-20)
valores negativos para indicar tamaos en dc.SetBrush( wx.Brush(GREEN) )
vertical. Por ejemplo: # cdigo similar a barra Ganadas para barra verde de empates
...
dc.DrawRectangle(100, 350, 120,
-alturaBarraGanadas) dc.SetBrush( wx.Brush(BLUE) )
# cdigo similar a barra Ganadas para barra azul de empates
dibuja un rectngulo que empieza en las coorde- ...
nadas (100, 350), tiene 120 pixels de anchura y se dc.EndDrawing()
extiende hasta la coordenada 350-alturaBarraGa-
nadas de altura. Esta funcin devuelve un objeto de tipo fiche-
ro que define entre otros los mtodos read,
Serializacin de datos en Python para leer uno o un buffer de caracteres, write,
para escribir uno o un grupo de caracteres,
La mayora de los datos en los sistemas de readlines para leer todas las lneas de un
informacin se guardan en ficheros. Python fichero y close para cerrar un fichero. A conti-
ofrece unas interfaces muy sencillas para nuacin se muestra el cdigo para leer un
guardar y recuperar datos de ficheros. Para fichero de texto lnea a lnea. Como siempre,
leer o escribir un fichero en Python usamos la remitimos al lector a la Python Library
funcin predefinida open, que tiene la Referente (http://docs.python.org/lib/lib.html)
siguiente firma: para ms detalles:
open(<nombre-fichero>, <modo: r (read), # leerfichero.py
w(write), a(append), b(binario)>
LISTADO 5 Haciendo persistente el registro de jugadores d[key] = data # guarda un valor bajo
key
class RegistroJugadoresPersistente(RegistroJugadores):
def __init__(self): data = d[key] # lo recupera
RegistroJugadores.__init__(self)
self._RegistroJugadores__jugadores = shelve.open(jugadores) del d[key] # lo borra
if not self._RegistroJugadores__jugadores.has_key(solop):
self._RegistroJugadores__jugadores[solop] = solop d.close() # cierra el diccionario
self._RegistroJugadores__estadisticas = shelve.open(estadisticas) persistente
if not self._RegistroJugadores__estadisticas.has_key(solop):
# jugador -> [ganados, empatados, perdidos] Aadiendo persistencia de datos
self._RegistroJugadores__estadisticas[solop] = [0, 0, 0]
a la aplicacin tres en raya
def __del__(self):
self._RegistroJugadores__jugadores.close() Una vez aadida una interfaz grfica a nuestra
self._RegistroJugadores__estadisticas.close() aplicacin de tres en raya, quizs el nico
aspecto que le resta para darle un carcter ms
fh = open(holamundo.py) # open crea un profesional, es aadirle la capacidad de recor-
objeto de tipo fichero dar los usuarios que se registran con la misma,
for line in fh.readlines() : # lee todas as como las estadsticas de los resultados
las lneas en un fichero obtenidos por esos usuarios. En la primera
print line,
entrega de esta serie definimos una clase lla-
mada RegistroJugadores que mantena en dos
fh.close() diccionarios, en memoria, informacin sobre
$ python leerfichero.py
los jugadores registrados (__jugadores) y sobre
los resultados obtenidos por los mismos
#!/usr/bin/python (__estadisticas). Para hacer que estos datos se
guarden de manera persistente, slo debemos
print Hola mundo
definir una nueva clase que herede de
El siguiente cdigo muestra cmo escribir datos a RegistroJugadores a la que llamaremos
un fichero de texto: RegistroJugadoresPersistente. El listado 5
# escribirfichero.py muestra el cdigo de esta clase. En el cons-
tructor de esta nueva clase nos aseguramos
fh = open(out.txt, w) que los miembros de RegistroJugadores
fh.write (estamos escribiendo ...\n) __jugadores y __estadisticas pasen de ser sim-
ples mapas a shelves, es decir, mapas de datos
fh.close() persistentes. Es preciso recordar que en Python
$ python escribirfichero.py se usa la tcnica de name mangling, para hacer
que todos los campos y funciones privadas o
$ cat out.txt protected, no sean visibles desde fuera. Por esa
estamos escribiendo ... razn, tenemos que usar la notacin
_nombreClase__nombreCampoPrivado, por
Serializacin de objetos: Pickle y shelves ejemplo, self._RegistroJugadores__estadisti-
Al igual que otros lenguajes modernos como cas, para tener acceso a estos campos.
Java y C#, Python ofrece mecanismos para Finalmente, para asegurarnos que el mapa per-
serializar y deserializar objetos desde disco o a sistente es correctamente cerrado, aadimos
travs de la red. El mdulo pickle implementa en el destructor de la clase (__del__) llamadas
un algoritmo para la serializacin y deseriali- a los metodos close() de las baldas de datos
zacin de objetos en Python. Para serializar (shelves).
una jerarqua de objetos, deberemos crear un
Pickler, y luego llamar al mtodo dump(), Conclusiones
pasndole como argumento el objeto a seriali-
zar. Para deserializar crearemos un Unpickler e En esta entrega hemos incrementado nuestros
invocaremos su mtodo load(). El mdulo conocimientos del lenguaje de programacin
shelve, por su parte, define diccionarios per- Python, aprendiendo cmo se pueden crear ven-
sistentes. Las claves tienen que ser cadenas de tanas grficas con la toolkit wxPython y cmo se
caracteres mientras que los valores pueden ser pueden serializar datos a travs de ficheros, y los
cualquier objeto que se puede serializar con mdulos pickle y shelve. Con sto, hemos conse-
pickle. A continuacin mostramos un ejemplo guido darle una apariencia y un funcionamiento
de cmo implementar un diccionario o mapa mucho ms profesional a la aplicacin de tres en
persistente: raya que comenzamos en la anterior entrega. En
import shelve el prximo artculo aprenderemos a crear una
interfaz web al juego de tres en raya y a guardar
d = shelve.open(filename) # abre un los datos de jugadores y estadsticas en una base
fichero de datos.