sábado, 3 de diciembre de 2011

vipera 1.0.0

Hola. Acabo de subir vipera 1.0.0 en: http://code.google.com/p/vipera/.


vipera está bajo licencia GPL 3, por lo que se puede modificar, distribuir y hacer lo que se considere siempre bajo los términos de la General Public License, versión 3.

No he creado instaladores, puesto que no me ha dado tiempo, y como estoy en fase de pruebas y depuración de cosas, pues no me ha parecido conveniente.

Para hacer funcionar vipera únicamente hay que hacer:

python vipera.py

Si diera algún problema de fichero de configuración.... (perdón):

python vipera.py -r

Si encontráis alguna cosa rara, algún mal funcionamiento os agradecería eternamente que me lo comentaseis.

vipera es un proyecto, que ahora que está terminado, veo que puede mejorarse en muchas cosas, pero la idea era lo primero, así que a partir de dicha idea se puede ir a otros puertos, con otras tecnologías, como OpenGL en vez de OGL.

NOTA: Hay un vídeo demostrativo de su funcionamiento en:


https://www.facebook.com/photo.php?v=2040359947038
 
Un saludo.

viernes, 18 de noviembre de 2011

wxPython: cómo controlar los PyDeadObject

Hola. En este artículo vamos a ver una manera muy fácil de poder controlar los errores producidos en wxPython cuando se intenta acceder a widgets que han sido destruidos. Y vamos a verlo con el siguiente ejemplo:

# -*- coding: utf-8 -*-

import wx

class mi_frame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.SetTitle("Ejemplo de PyDeadObject")
        # 2 botones.
        self.boton1 = wx.Button(self, -1, u"Botón 1")
        self.boton2 = wx.Button(self, -1, u"Botón 2")
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.boton1, 0, wx.ALL, 1)
        sizer.Add(self.boton2, 0, wx.ALL, 1)
        self.SetSizer(sizer)
        # Binding.
        self.boton1.Bind(wx.EVT_BUTTON, self.OnBoton1)
        self.boton2.Bind(wx.EVT_BUTTON, self.OnBoton2)
       
    def OnBoton1(self, event):
        self.boton1.SetLabel("Hola Python")
       
    def OnBoton2(self, event):
        self.boton1.SetLabel("Hola wxPython")
       
app = wx.PySimpleApp()
f = mi_frame(None)
f.Show()
app.MainLoop()

Como se puede observar este script es una ventana que contiene dos botones. Al hacer click en cada uno de los botones se va cambiando el Label del primer botón. Esto es:


Hacemos click en el botón de la izquierda:


Hacemos click en el botón de la derecha:


Ahora vamos a hacer una pequeña modificación en el método OnBoton2: vamos a incluir una instrucción para que cuando se haga click en el botón 2 se elimine el botón 1:

    def OnBoton2(self, event):
        self.boton1.SetLabel("Hola wxPython")
        self.boton1.Destroy()


Si volvemos a hacer la misma operativa de arriba, cuando hagamos click en el boton 2 al intentar hacer el SetLabel nos dará un error PyDeadObject, ya que estamos intentando acceder a un widget que ya ha sido eliminado. Es decir:

Hacemos un primer click en el botón derecho:


Y al hacer un segundo click sobre el botón derecho nos da el siguiente error:

wx._core.PyDeadObjectError: The C++ part of the Button object has been deleted, attribute access no longer allowed.
File "c:\Users\Angel Luis\Desktop\ej_pydeadobject.py", line 30, in
  app.MainLoop()
File "c:\Python26\Lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 8010, in MainLoop
  wx.PyApp.MainLoop(self)
File "c:\Python26\Lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 7306, in MainLoop
  return _core_.PyApp_MainLoop(*args, **kwargs)
File "c:\Users\Angel Luis\Desktop\ej_pydeadobject.py", line 24, in OnBoton2
  self.boton1.SetLabel("Hola wxPython")

File "c:\Python26\Lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 14610, in __getattr__
  raise PyDeadObjectError(self.attrStr % self._name)


Que corresponde con la línea wx _core.py: 





¿Solución?
 
En ciertas áreas de nuestro código wxPython que sepamos que son susceptibles de ser eliminadas y después con posibilidad de ser referenciadas de nuevo (por un proceso, por un hilo o por cualquier otra cosa), podemos evaluar primero el widget para ver si realmente existe y luego realizar las operaciones pertinentes. En nuestro ejemplo vamos a cambiar de nuevo el método OnBoton2:


# -*- coding: utf-8 -*-

import wx

class mi_frame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.SetTitle("Ejemplo de PyDeadObject")
        # 2 botones.
        self.boton1 = wx.Button(self, -1, u"Botón 1")
        self.boton2 = wx.Button(self, -1, u"Botón 2")
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.boton1, 0, wx.ALL, 1)
        sizer.Add(self.boton2, 0, wx.ALL, 1)
        self.SetSizer(sizer)
        # Binding.
        self.boton1.Bind(wx.EVT_BUTTON, self.OnBoton1)
        self.boton2.Bind(wx.EVT_BUTTON, self.OnBoton2)
       
    def OnBoton1(self, event):
        self.boton1.SetLabel("Hola Python")
       
    def OnBoton2(self, event):
       if not self.boton1:
            wx.MessageBox(u"El botón se destruyó")

       else:
            self.boton1.SetLabel("Hola wxPython")
            self.boton1.Destroy()
       
app = wx.PySimpleApp()
f = mi_frame(None)
f.Show()
app.MainLoop()

¿Por qué?

Pues porque cuando un widget se elimina pasa a ser un _wxPyDeadObject el cual gana un método __nonzero__ que siempre devuelve False.

Si volvemos a ejecutar nuestro nuevo script y hacemos dos veces click en el botón de la derecha tendremos el siguiente resultado:


Espero que este artículo os sirva para escribir aplicaciones wxPython más robustas.

Un saludo.

viernes, 4 de noviembre de 2011

jueves, 27 de octubre de 2011

vipera tester 1.0.0


Hola. Estas son las grabaciones de vipera, un diseñador de aplicaciones para Python. Este programa será mi proyecto fin de carrera (o de grado como se llama ahora). En este primer vídeo hago una simulación muy sencilla de creación de clases de manera gráfica, y como se genera el código Python correspondiente.


https://www.facebook.com/video/video.php?v=1889051804429

En los siguientes links se muestran como hacer prototipos de funciones:

https://www.facebook.com/video/video.php?v=1889100005634

https://www.facebook.com/video/video.php?v=1889147406819



Estos vídeos los he creado con recordMyDesktop. Perdón por no tener sonido el primero de ellos, estaba en fase de pruebas...

Por último decir que vipera puede crear módulos con clases, funciones, registros (emulados con funciones o clases), constantes y código de ejecución para pruebas de módulo.

Además incluye un diseñador de packages visual estilo árbol, un generador de documentación de proyecto y un sistema de generación de licencias. Además tiene un módulo de ingeniería inversa, en donde a partir de código Python se muestran las clases y sus relaciones.

Se puede imprimir el código generado, así como los diagramas diseñados. En próximos vídeos más.

Espero publicar este programa bajo las condiciones de GPL 3, a la espera que me de permiso la Universidad.

Un saludo.

domingo, 16 de octubre de 2011

Dennis Ritchie

Dennis Ritchie murió el pasado Miércoles 12 de Octubre.


Una de "sus pocas contribuciones" fue (junto con Ken Thompson) la creación del lenguaje de programación C, base de la mayoría de las tecnologías de las que ahora disfrutamos. Será por ello que no sale en televisión.

Descanse en paz.

lunes, 13 de junio de 2011

Taller tutorial de wxFormBuilder, en español

Hola. En este post os presento un material sobre wxFormBuilder, el diseñador de interfaces gráficas para wxPython, que se utilizará en el taller Caldum de esta tarde 13 de Junio.

El material lo podéis bajar, como siempre, del repositorio de este blog:

https://sites.google.com/site/elviajedelnavegante/documentacion

Es un zip, Taller_wxFB.zip, que contiene un PDF con la explicación/teoría/presentación del taller, así como carpetas con proyectos wxFB hechos. De la misma forma se incluyen ficheros Python que utilizan el código generado por wxFB para crear las interfaces.

Espero os sirva este material para vuestros intereses.

Un cordial saludo.

sábado, 4 de junio de 2011

Taller Caldum: wxFormBuilder (wxPython)

Hola. Escribo este post para comunicar que se ha retrasado el taller CALDUM de wxFormBuilder, y se impartirá el día 13 de Junio (Lunes), de 16:00 a 20:00 horas.

El taller se podrá seguir online por videoconferencia. Más información en la página de CALDUM.


Enlace

domingo, 22 de mayo de 2011

Taller de wxFormbuilder en la Universidad de Murcia

Hola. El día 10 de Junio 13 de Junio (Lunes), de 16:00 a 20:00 impartiré un taller CALDUM sobre wxFormBuilder en la Universidad de Murcia,

Es necesario saber Python así como tener clara la programación orientada a objetos, ya que veremos, no solamente el diseño de pantallas en wxFormBuilder, también el código que genera, para modificarlo  mediante herencia, en lo que se refiere a manejadores de eventos.

En este taller se verá fundamentalmente:

0) Generación de proyectos.
1) Contenedores de alto nivel: Frames, Paneles
2) Sistemas de menús.
3) Componentes:
  3.1) AUI_Notebook
  3.2) Controles de texto y etiquetas.
  3.3) Etc...
4) Posicionamiento de widgets mediante Sizers (BoxSizer y FlexGridSizer).
5) Herencia y override de métodos del código generado.
6) Aplicaciones FrontEnd en wxPython.
7) Bibliografía.

De lo que se trata en este taller es que el asistente tenga una idea clara de las posibilidades de wxFormBuilder como diseñador de interfaces gráficas basado en wxPython. El taller es eminentemente práctico, en donde se abarcarán la mayoría de conceptos de wxPython, a saber, widgets principales, binding, manejadores de eventos y herencia y override de métodos.

Saludos.

jueves, 19 de mayo de 2011

wxPython: Obtener datos de las hojas de un AUI_Notebook

Hola. En este post vamos a ver el componente de wxPython AUI Notebook, y en especial su instanciación, creación de hojas y forma de recorrerlas para obtener la información que pudiera haber sido introducida por el usuario.

Hay un ejemplo muy completo en la Demo de wxPython. Aquí se verá lo mismo, más simplificado y con el plus del recorrido de las hojas del AUI Notebook.

El código fuente de esta aplicación se puede encontrar en el repositorio de este blog, en:

https://sites.google.com/site/elviajedelnavegante/

Para poder entender bien como funciona un AUI_Notebook es necesario realizar un ejemplo, ya que es la mejor manera posible. De lo que se trata es de crear un módulo (fichero) que contendrá 2 clases. Una de ellas será el diseño de una hoja (que hereda de wx.Panel), y la segunda clase será un frame que heredará de wx.Frame. Cada hoja del AUI_Notebook será una instanciación de la clase hoja.

Comenzamos...

Lo primero de todo es importar los módulos necesarios:

import wx
import wx.aui

A continuación diseñamos la clase que representará cada hoja de nuestro AUI_Notebook:

class hoja_notebook(wx.Panel):
    def __init__(self, parent, nombre_hoja):
        # Constructor.
        wx.Panel.__init__(self, parent)
        # Sizer.
        sizer = wx.BoxSizer(wx.VERTICAL)
        # Etiqueta.
        self.c0 = wx.StaticText(self, -1, nombre_hoja)
        # Cajas de texto.
        self.c1 = wx.TextCtrl(self, -1)
        self.c2 = wx.TextCtrl(self, -1)
        # Posicionamos las cajas de texto y la etiqueta en el sizer.
        sizer.Add(self.c0, 0, wx.EXPAND|wx.ALL,5)       
        sizer.Add(self.c1, 0, wx.EXPAND|wx.ALL,5)
        sizer.Add(self.c2, 0, wx.EXPAND|wx.ALL,5)
        # Añadimos el sizer al frame.
        self.SetSizer(sizer)

Esta clase hereda de un wx.Panel, por lo que es un panel. Vemos que dentro tiene un sizer vertical que posiciona una etiqueta y dos cajas de texto. En la instanciación de la clase pasamos el texto que se presentará en la etiqueta.

Bien, ahora crearemos el diseño de la clase frame que contendrá el AUI_Notebook y que contendrá hojas a través de la instanciación de la clase anterior.

class frame_principal(wx.Frame):
    def __init__(self, parent):
        # Constructor.
        wx.Frame.__init__(self, parent, title = "Trabajando con el AUI NoteBook")
        # Sizer.
        sizer = wx.BoxSizer(wx.VERTICAL)
        # Botón para obtener datos.
        self.boton = wx.Button(self, -1, "Devolver datos")
        # Incluimos el botón en el sizer.
        sizer.Add(self.boton, 0, wx.EXPAND|wx.ALL, 5)
        # Panel de NoteBook.
        self.panel = wx.aui.AuiNotebook(self, -1)
        # Hojas.
        hoja1 = hoja_notebook(self.panel, "Primera hoja")
        hoja2 = hoja_notebook(self.panel, "Segunda hoja")
        hoja3 = hoja_notebook(self.panel, "Tercera hoja")
        # Añadimos las hojas al NoteBook.
        self.panel.AddPage(hoja1, "Hoja 0")
        self.panel.AddPage(hoja2, "Hoja 1")
        self.panel.AddPage(hoja3, "Hoja 2")
        # Incluimos el panel en el sizer.
        sizer.Add(self.panel, 1, wx.EXPAND|wx.ALL, 5)
        # Y añadimos el sizer al frame.
        self.SetSizer(sizer)
        # Binding.
        self.boton.Bind(wx.EVT_BUTTON, self.OnClickBoton)

    # Manejador de eventos.    
    def OnClickBoton(self, event):
        # Hoja actual.
        hoja_actual = self.panel.GetSelection()
        # Número total de hojas.
        total_hojas = self.panel.GetPageCount()
        # Algo de información antes de buscar datos.
        cadena = "Este AUI NoteBook tiene %s hojas. Estás en la %s" % \
        (total_hojas,hoja_actual)
        wx.MessageBox(cadena,"Info",wx.OK)
        # Recorremos las hojas, obteniendo la información requerida.
        texto = ""
        for i in range(0,total_hojas):
            # Texto en la etiqueta de la hoja.
            texto_etiqueta = self.panel.GetPageText(i)  
            # Datos de las cajas de texto de la hoja i.
            dato1 = self.panel.GetPage(i).c1.GetValue()
            dato2 = self.panel.GetPage(i).c2.GetValue()
            # Info.
            texto += u'''
            La hoja número %s, con etiqueta "%s"
            contiene los datos:
            Caja de texto: %s
            Caja de texto: %s
            ''' % (i, texto_etiqueta, dato1, dato2)
        # Mostramos los datos por pantalla.
        wx.MessageBox(texto, "Datos", wx.OK)

Puff!. Vaya, esta clase es algo más larga. Pero no hay problema, la comentamos poco a poco, que es muy fácil de entender. Se puede decir que esta clase implementa la creación de hojas por una parte y el recorrido de dichas hojas a partir de la generación de un evento por hacer click en un botón. Bien, veámoslo por partes.

Esta clase hereda de wx.Frame, por lo que es un frame (una ventana típica de Windows).  En dicho frame se ha utilizado un sizer vertical para posicionar un botón (wx.Button) y un componente AUI_Notebook. Si vemos el código, justo después de instanciar el AUI_Notebook, instanciamos tres hojas del modo:

hojai = hoja_notebook(self.panel, "XXX hoja")

Esto es, hemos utilizado la clase hoja_notebook para crear 3 hojas. Darse cuenta que el padre de las hojas es el propio AUI_Notebook. Mediante el método AddPage se añaden las hojas. Además, se ha creado un binding para cuando se genere el evento de hacer click en el botón.

Dicho evento  (wx.EVT_BUTTON) lanza el manejador de eventos OnClickBoton, que se encargará de realizar el recorrido entre todas las hojas, así como mostrar información sobre el posicionamiento de las mismas. Mediante los métodos GetSelection() y GetPageText(i) se obtiene la hoja actual, así como el texto de la etiqueta.

Lo que se hace en realidad es recorrer todas las hojas y buscar la información entre los objetos de cajas de texto, mediante:

self.panel.GetPage(i).c1.GetValue()

Como el lector puede observar con GetPage se obtiene el objeto hoja_notebook. Únicamente hay que acceder a sus atributos (c1, c2) y con el método GetValue() (de wx.TextCtrl) se obtiene el texto que recoge.

El código para hacer funcionar este programa podría ser tal que así:
app = wx.PySimpleApp()
frame = frame_principal(None)
frame.Show()
app.MainLoop()

Veamos capturas del ejemplo para ver que efectivamente funciona...

Ejecutamos.


Incluimos datos.




Al hacer click en el botón "Devolver datos", teniendo en cuenta que estamos en la última hoja, tenemos...



 CONCLUSIONES

En este artículo hemos visto de una manera muy sencilla el componente AUI_Notebook, que podemos ver en más profundidad en la Demo de wxPython. Se ha creado una aplicación muy sencilla, que se compone de una clase de tipo wx.Panel que define una hoja y una clase wx.Frame que incluye un componente AUI_Notebook. Las hojas del AUI_Notebook son instanciaciones de la clase hoja que hemos creado. Además se han visto algunos métodos para acceder a las hojas, así como la manera de obtener los datos que un posible usuario hubiera incluido en las hojas.

Saludos.

jueves, 12 de mayo de 2011

Lorca muy tocada...

Hola. Como el lector debe de saber ya soy de Lorca. Ayer sufrimos 2 terremotos que han dejado la ciudad con un 80% de daños en infraestructuras. Mi casa no ha sido una excepción, por lo que no se cuando volveré a escribir. Lo primero es lo primero.

Espero que sea pronto.

Un cordial saludo.

jueves, 14 de abril de 2011

Charla sobre Python y wxPython en Almería (IES Al-Andalus)

Hola. El Jueves 14 de Abril daré una charla de una hora (prometo no sobrepasarme en el tiempo) sobre Python y wxPython, en el IES Al-Ándalus, en Almería ciudad, en las Jornadas SLCENT 7.0.

El programa está aquí.

La hora es a las 20:30 de la tarde. Lo que pretendo es dar una visión general de Python, sin entrar en ningún tipo de matiz, y ver por encima la plataforma de desarrollo de interfaces gráficas wxPython, con ejemplos.

Será una charla de ver por donde empezar para llegar a buen puerto.

El material que expondré lo dejo como siempre en el repositorio de este blog. Es muy parecido a la charla de la Lan Party 2010, pero he quitado muchas cosas (código) y añadido otras (capturas de pantalla de wxPython), con lo que creo la presentación gana en legibilidad y soltura.

Saludos.

martes, 29 de marzo de 2011

Algoritmos de clasificación y problema de cambio de monedas, en C

Hola. Revisando el disco duro de hace algunos años me he encontrado con unas prácticas de la carrera (del 2003-2004), el análisis y diseño de algoritmos de clasificación y la resolución del ejercicio (mediante ciertas técnicas) de cambio de monedas. Bien , pues ya es hora de compartir este trabajo, por si alguien lo necesita. Estos trabajos están hechos en C, en plataforma Linux.

Se pueden descargar del repositorio de este blog, en:

https://sites.google.com/site/elviajedelnavegante/documentacion

Los ficheros son ALGORITMOS DE ORDENACION.zip y PROBLEMA DEL CAMBIO DE MONEDAS.zip.

Algoritmos de ordenación

Este trabajo recoge el análisis, diseño e implementación, en C, de los algoritmos de clasificación más importantes, a saber:

- Algoritmo de selección directa.
- Algoritmo de burbuja.
- Algoritmo de inserción directa.
- Algoritmo de burbuja mejorado.
- Algoritmo de QuickSort.
- Algoritmo de MergeSort.
- Algoritmo de QuickSort no recursivo.
- Algoritmo de MergeSort no recursivo.
- Algoritmo de QuickSort con elección de pivote.

Aquí se incluyen estudios de coste promedio, estudio teórico del tiempo de ejecución, estudios experimentales de todos los algoritmos de clasificación, etc.

Cambio de monedas

Resolución del problema de cambio de monedas por backtracking (árbol de soluciones, tiempos de ejecución, reducción de número de nodos e implementación) y por programación dinámica (ecuación de recurrencia y solución óptima, estudio teórico del tiempo de ejecución e implementación).

Por último comentar que los ficheros .zip contienen tanto la documentación como los fuentes, incluidos los Makefile.

Saludos.

martes, 22 de marzo de 2011

Script para pasar código Python a HTML

Hola. En este artículo vamos a crear un script para pasar código fuente Python (.py) a formato HTML (y de esta manera mostrar dicho código en una página web). Además vamos a darle  coloración al código Python. Debe de haber formas más simples, sin duda, de resolver este tipo de problemáticas, ... a mí se me ha ocurrido esta.

Como siempre, el fichero .py que contiene el script se puede obtener del repositorio de este blog, en:

https://sites.google.com/site/elviajedelnavegante/

El fichero se llama mi_tokenize.zip, el cual contiene el script mi_tokenize.py.

En este problema no se ha utilizado programación orientada a objetos, esto es, no se implementa ninguna clase. Todo se resuelve con programación imperativa (mediante funciones).

Al grano...

El funcionamiento es sencillo, en términos generales. De lo que se trata es que, a partir de un fichero de código fuente Python, se genera otro (con extensión html) con código HTML, que representa el código Python formateado (con indentación y coloreado de código).

Un ejemplo sería el siguiente:


La captura de arriba es del Stani's Python Editor. Abajo nos encontramos el mismo código, formateado en HTML y coloreado, en Firefox.


¿Cómo enfocar el problema?

Pasar código Python a html es casi trivial. Únicamente hay que abrir un fichero .py, leerlo, crear otro fichero con una cabecera html, incluir dicho código Python y cerrar el html. Listo, ya tenemos el código en una página web.

El verdadero problema es el coloreado del código, ya que se tiene que identificar los diferentes bloques de código a los que se quieren dar color. En nuestro caso, daremos color a los comentarios, a las cadenas y a las palabras clave de la versión que utilicemos de Python.

¿Cómo hacerlo? Mediante los módulos cStringIO y tokenize, que se pueden encontrar en cualquier distribución de Python.

cStringIO sirve para crear, a partir de una cadena, un buffer de cadena. tokenize es el módulo que nos permite analizar código Python e identificar componentes del mismo (comentarios, cadenas, etc) a partir de un buffer de cadena. Mejor lo vemos con los pasos que debe de dar nuestro script:

1) Cargamos en memoria un fichero .py con el método read() de open

2) Mediante cStringIO creamos un fichero en memoria ó buffer de cadena, el cual es necesario para utilizar el tokenize. En realidad esto sirve para iterar, línea a línea sobre una cadena con varias líneas (con saltos de línea). Por ejemplo:

import cStringIO

cadena = '''
Este es un ejemplo
de la potencia del
módulo cStringIO
'''

texto = cStringIO.StringIO(cadena)
for i in texto: print i

Devuelve:

Este es un ejemplo
de la potencia del
módulo cStringIO

3) Mediante tokenize, y a partir del buffer de cadena creado en el paso anterior, podemos obtener los token (elementos de código), iterando sobre ellos, e identificando los elementos de código Python.

En la documentación de Python tenéis una explicación muy buena (en inglés) de estos dos módulos:



Puff!!! Una explicación algo extraña. Veámoslo con un ejemplo. Imaginemos que hemos cargado mediante el método read() de open un fichero con código Python con el siguiente contenido:

# -*- coding: utf-8 -*-
# Código Python.
for i in range(0,5):
  print "Número ", i

Mediante los pasos anteriores vamos a identificar los elementos del código, a saber, comentarios, palabras clave y cadenas. El código que haría esta operación podría ser el siguiente:

# Creamos buffer de cadena.
texto = cStringIO.StringIO(cadena)

# Creamos tokens.
tokens = tokenize.generate_tokens(texto.readline)

for a,b,c,d,_ in tokens:
  if a == tokenize.STRING:
    print "Cadena: %s en posición [%s,%s]" %( b,c,d) 
  if a == tokenize.NAME and b in keyword.kwlist:
    print "Palabra clave: %s en posición [%s,%s]" % (b,c,d)
  if a == tokenize.COMMENT:
    print "Comentario: %s en posición [%s,%s]" % (b,c,d)

Dando como resultado:

Comentario: # -*- coding: utf-8 -*- en posición [(1, 0),(1, 23)]
Comentario: # Código Python. en posición [(2, 0),(2, 17)]
Palabra clave: for en posición [(3, 0),(3, 3)]
Palabra clave: in en posición [(3, 6),(3, 8)]
Palabra clave: print en posición [(4, 2),(4, 7)]
Cadena: "Número " en posición [(4, 8),(4, 18)]

Por tanto, mediante el módulo tokenize podemos identificar todos los elementos de los que se compone un código Python. La idea fundamental es esta. Aparte, mediante operaciones de transformación entre listas y cadenas, y demás operaciones se llega al resultado esperado.

¿Y cómo se colorea el texto? Pues mediante código HTML del tipo:


Así el otro hándicap es que a partir de las coordenadas de los elementos de código a colorear hay que insertar código HTML.

El script mi_tokenize.py

Tal como antes se ha comentado, el lector puede obtener el código de este script en el repositorio de El viaje del navegante. Aquí se presentan capturas de pantalla del código, debido principalmente a que se utiliza sintaxis html en el código Python, y no mezclar cosas.

Módulos que se cargan y estructuras que se definen:


Importamos los módulos necesarios y creamos un diccionario para colorear los elementos de código Python.

El código principal que ejecuta el script:


Podemos observar que el script pide dos argumentos para funcionar, el primero el fichero Python y el segundo el nombre del fichero html que se generará (se le da extensión .html si esta se omite) . A continuación llamamos a la función cargar_fichero que devuelve, mediante read() una cadena con el contenido del fichero. Finalmente, mediante la función tokenizar_codigo se crea una cadena con sintaxis html y Python, que se guarda en el fichero identificado por f_html_nombre.

Función para cargar el fichero .py:


Darse cuenta que reemplazamos ciertos caracteres especiales en html.

La función que analiza el código creando una cadena con html y código Python es la siguiente:


Esta función podría haberse escrito de otra forma, para ahorrar código, pero me ha parecido más pedagógico realizarlo de esta forma. Como se puede observar se utilizan cStringIO y tokenize. Devuelve una cadena con código html. Esta última función utiliza dos funciones auxiliares, a saber:

La función para insertar los elementos de html:


Y la función para incluir la cabecera y pie del documento de la página web:


Pero como no es lo mismo andar el camino que conocer el camino, es mejor probar el script. El lector puede pensar que sería mucho mejor utilizar CSS para estos menesteres, y en cierta manera, es verdad, pero no es objetivo de este post hacer un tratado sobre web y CSS. Lo verdaderamente importante es el parseo de código Python y el tratamiento de cadenas.

Se insta al lector a mejorar el código.

Saludos.

domingo, 20 de marzo de 2011

Mis antiguos juegos DOS en mi nuevo Linux Ubuntu

Hola. Este no es un post sobre Python, pero tenía que escribirlo, ya que llevo emocionado toda la mañana jugando a mis antiguos juegos de MS-DOS.

Mi primer PC fue allá por 1988 ú 89, ya no recuerdo. Un 8086 de 640 Kb, diskettera de 5 un cuarto y otra de 3 y medio. Monitor CGA monocromo (luego cambiaría a un TARGA VGA, todo un lujo). Por supuesto, un sistema operativo MS-DOS 3.22, que era lo máximo.

Hacía mis pinitos con Logo (AV, GD, GI, ...) y luego GW-BASIC (return, goto's, ...). Ahí es donde empezó mi curiosidad por la profesión, que ya viene de lejos.

Pero lo mejor, de verdad, lo mejor, eran los videojuegos. Unas auténticas joyas de programación, con los recursos hardware tan limitados que había por aquel entonces (comparado con ahora, claro) .

Yo no soy ningún entusiasta de los juegos de hoy día (de hecho, no juego), pero sí de los de mi época ochentera y noventera (el último el PC-FUTBOL 6.0, ¡qué maravilla!).

En mi nuevo Linux he descubierto que también está el DOSBox (que ya utilicé alguna vez en Windows), el cual es un emulador de entornos MS-DOS. Lo he instalado desde el Centro de software de Ubuntu. Hay muchas posibles configuraciones de esta aplicación. Aquí hay algunas guías:

http://www.arcades3d.com/textos/tutdosbox_uso.var

http://manualinux.my-place.us/dosbox.html

http://www.dosbox.com/DOSBoxManual.html

Lo único que he configurado para hacer funcionar DOSBox ha sido modificar la etiqueta [autoexec] del fichero de configuración dosbox-0.73.conf, que se encuentra en el directorio ./dosbox de la carpeta de usuario.



Como se puede observar he incluido la configuración del teclado al español:

keyb sp 437

 y he montado la unidad C: en el directorio donde tengo todos mis juegos DOS, mediante:

mount c /home/angel/juegos/dos

Arrancas el emulador DOSBox...


Increíble la sensación de jugar a juegos DOS desde un Linux!!!! Y lo mejor de todo, que funcionan a la velocidad de tu antiguo PC.

Xenon 2

Un clásico de arcades. Y menuda música que tiene (el altavoz echaba chispas)!!!



Digger 

¿Quién no ha jugado a este juego? ¿Y la música del Pop-corn? Bestial!!!


Y otros clásicos como Arkanoid, Volfied, Pacman, Striker, Rampage, Tetris (el auténtico de 1986), Sol Negro, .... En total tengo unos 118 juegos de los ahora denominados Abandonware.

Y no sé porqué, pero estos juegos, para mí, son mucho más adictivos e impresionantes que los de hoy día, ¡Y TODO EN MI LINUX!

Saludos.

martes, 15 de marzo de 2011

Me cambio a Linux

Hola. Después de algunos años en sistemas Windows XP (desde 2003, en donde dejé abandonada a su suerte a mi fabulosa RedHat 6), me vuelvo a Linux, a Linux Ubuntu definitivamente, con Python 2.6.6 y como IDE de desarrollo el Stani's Python Editor (SPE).

A partir de este post, todo lo que escriba se realizará en esta plataforma. La razón para el cambio es el intento de trabajar con Windows 7, en mi PC. La consecuencia: Un SO tiene que ayudar al desarrollador, y en mi caso, con mi máquina, eso es muy complicado (¡tarda más en ponerse en marcha que ejecutar NetBeans en un 486!).

No intento desprestigiar la familia de SO's Windows 7 ni nada parecido. Únicamente, que no es bueno para mí. Y como mi Linux Ubuntu 10.04 no me da problemas de rendimiento, pues me quedo aquí.

Por último decir que si por alguna excepción escribiera algo para Windows, sería con Windows XP SP3, y con el IDE PyScripter, uno de los entornos de desarrollo más potentes para Python en plataformas de Microsoft.

Saludos.

martes, 1 de marzo de 2011

Pasar parámetros entre frames en wxPython

Hola. En este artículo vamos a ver una forma muy sencilla de pasar parámetros entre 2 frames en wxPython, uno padre de otro. El paso de mensajes ó parámetros entre objetos se puede hacer de varias formas, siendo la que vamos a ver una de las más fáciles.

Aunque lo vamos a ver con un ejemplo (esto es, código), no está de más explicar el funcionamiento. La aplicación es un fichero .py donde tenemos una aplicación wxPython, que instancia una clase (frame_principal). Dicha clase, al hacer click en un botón instancia una segunda clase (frame_secundario). Cuando se introducen datos en el frame_secundario (objeto) y se hace click en un botón, se devuelve dicha información al frame_principal, que lo muestra. Es decir, se introducen datos en un frame y los devuelve al frame maestro desde que fue instanciado (llamado).

¿Cómo se ha hecho? La cuestión es que cuando se instancia el frame_secundario en el frame_principal, se le pasa como parámetro una referencia de él mismo. En frame_secundario se crea un atributo (self.padre), que contiene la referencia al frame padre en donde fue creado.

Este ejemplo se ha realizado en Linux Ubuntu y el editor joe. El código es el siguiente:


# -*- coding: utf-8 -*-
# El viaje del navegante.
# Ejemplo de paso de parámetros entre 2 frames en wxPython.

# Importamos las wx
import wx

# Creamos una clase frame que pide un dato.
class frame_secundario(wx.Frame):
  def __init__(self, parent):
    # Este es el constructor. Darse cuenta que se pasa como
    # parámetro parent, esto es, la referencia del frame que instancia
    # a esta clase. La guardamos.
    self.padre = parent
    # Llamamos al constructor de la clase de la que hereda.
    wx.Frame.__init__(self, None, -1, title = "Introduce un valor")
    # Creamos un sizer horizontal.
    sizer = wx.BoxSizer( wx.HORIZONTAL )
    # Creamos una caja de texto.
    self.caja_texto = wx.TextCtrl(self, -1)
    # Creamos un botón.
    self.boton = wx.Button(self, -1,"ACEPTAR")
    # Añadimos al sizer la caja y el botón.
    sizer.Add(self.caja_texto, 0, wx.ALL, 5)
    sizer.Add(self.boton, 0, wx.ALL, 5)
    # Incluimos el sizer en el frame.
    self.SetSizer(sizer)
    # Creamos el binding. Cuando se haga click en el
    # botón se lanzará el manejador de eventos correspondiente.
    self.boton.Bind(wx.EVT_BUTTON, self.OnClickBoton)

  # Manejador de eventos.   
  def OnClickBoton(self, event):
    # Obtenemos datos de la caja de texto.
    dato = self.caja_texto.GetValue()
    # Podríamos escribir directamente en el objeto
    # que se desease.
    self.padre.caja_texto.SetValue(dato)
    # Nos vamos.
    self.Destroy()
  
# Creamos la clase de la ventana principal.   
class frame_principal(wx.Frame):
  def __init__(self):
    # Constructor. Llamamos al constructor de la clase wx.Frame.
    wx.Frame.__init__(self, None, -1, title = 'Ventana Principal')
    # Creamos un sizer horizontal.
    sizer = wx.BoxSizer( wx.HORIZONTAL )
    # Creamos un botón.
    self.boton = wx.Button(self, -1, "Crear Frame Secundario")
    # Creamos una caja de texto de solo lectura.
    self.caja_texto = wx.TextCtrl(self, -1, style = wx.TE_READONLY)
    # Añadimos al sizer la caja y el botón.
    sizer.Add(self.boton, 0, wx.ALL, 5)
    sizer.Add(self.caja_texto, 0, wx.ALL, 5)
    # Asociamos el sizer al frame.
    self.SetSizer(sizer)
    # Creamos el binding. Cuando se haga click en el
    # botón se lanzará el manejador de eventos correspondiente.
    self.boton.Bind(wx.EVT_BUTTON, self.OnClickBoton)
     
  # Manejador de eventos.   
  def OnClickBoton(self, event):
    # Si se hace click se crea una instancia del frame_secundario.
    frame = frame_secundario(self)
    # Mostramos.
    frame.Show()
      
# Creamos una aplicación wxPython.
aplicacion = wx.PySimpleApp()
# Instanciamos el frame principal.
frame = frame_principal()
# Y por supuesto, no olvidar mostrarlo.
frame.Show()
# Esperamos a capturar eventos.
aplicacion.MainLoop()
   

El código está muy comentado, pero volvemos a analizarlo. Se crea una aplicación wxPython, que instancia la clase frame_principal.


Si hacemos click en el botón "Crear Frame Secundario" se instancia la clase frame_secundario, creando un segundo frame:



Incluimos un texto en la caja de texto, PYTHON!!!, y hacemos click en el botón Aceptar. 


Tal como se aprecia en el código, se envía el dato al frame padre y se destruye la instancia del frame_secundario.Y el resultado es:






En el código se ha resaltado en verde el paso de la referencia del frame padre. 


Saludos.

viernes, 25 de febrero de 2011

Manual de introducción a Python, en español

Hola. Con motivo del taller sobre Python en la Universidad de Murcia que se impartirá hoy, he creado un manual de introducción a Python. No tiene ningún tipo de licencia, así que se puede copiar y modificar libremente. No es necesario mencionarme si lo modificáis.

El documento se encuentra en:

https://sites.google.com/site/elviajedelnavegante/documentacion

Hay muchos manuales de referencia, y en español los hay muy buenos (aunque pocos). Este manual no es tan bueno como aquellos, pero deja la puerta abierta para que el lector saboree las capacidades básicas de Python.

Se han incluido 3 ejemplos (loterías, agenda, contar) en el documento, que se pueden descargar de:

https://sites.google.com/site/elviajedelnavegante/codigo

Al ser una introducción dejo muchas cosas en el camino, así que espero podáis entenderlo.

Saludos cordiales.

lunes, 14 de febrero de 2011

Curso de introducción a Python en la Universidad de Murcia

Hola. Os comunico que el día 25 de Febrero realizaremos un curso de introducción a Python, en la Universidad de Murcia. Toda la información la tenéis en el siguiente enlace:


http://um.sym.posium.com/event_detail/434/detail/taller-caldum:-introduccion-al-lenguaje-de-programacion-python.html


La charla durará unas 3 horas, y es una introducción al lenguaje, para empezar en el estupendo mundo de desarrollo de software. La impartirá Antonio Mario Molina Saorín y un servidor. Aquí os presento un descriptor de lo que queremos ver:

1) Lenguajes de programación. 

1.1) Tipos. 
1.2) Python. 
1.2.1) Historia. 
1.2.2) Características principales.
2) Herramientas disponibles en Python 
2.1) Editores, GUI's, IDE's.
2.2) Frameworks.
2.3) Módulos.
4) Introducción al lenguaje.
4.1) Intérprete de Python. 
4.2) Hola mundo: print y raw_input.
4.3) Tipos de datos: números, booleanos, listas, tuplas, t.hashing, ficheros.
4.4) Estructuras de control de flujo (if, for, while).
4.5) Ficheros .py, pyc, pyw y pyo.
5) Scripts en Python.
6) Módulos más utilizados (os, datetime, sys) y namespaces en Python.
7) POO en Python. 
7.1) Clases y objetos.
7.2) Herencia, polimorfismo, encapsulación, abstracción.
8) cPickle: persistencia de objetos.
9) Recursos en Internet (sheets, cards).
10) Creación de una agenda personal, en Python.
11) Aplicaciones gráficas: wxPython/Tkinter.
12) Propuestas para nuevos talleres Python.
13) Preguntas.

En unas 3 horas te haces con el lenguaje básico, si te lo comentan mejor que si lo haces solo. Si no sabéis nada de Python y sí de programación, os sorprenderá este lenguaje, por lo sumamente sencillo de aprender y la sintaxis tan clara que tiene. Una vez que lo pruebas, de verdad que se hace muy difícil desengancharse. Se pueden hacer cosas muy rápidas y sencillas, ya que Python es un lenguaje de muy alto nivel (gran nivel de abstracción).


Aquíi tenéis el lugar en donde se impartirá:




Saludos.

jueves, 27 de enero de 2011

Persistencia de imágenes con Python: SQLServer y cPickle

Hola. En este artículo vamos a ver cómo guardar y recuperar imágenes de una base de datos Microsoft SQLServer 2005 Express Edition, con Python, así como guardar y recuperar también, imágenes, en un objeto que se serializa, guardando dicha información en disco, mediante cPickle.

Antes de nada comentar que en este blog se han tratado varios tipos de SGBD, tales como MySQL, Firebird,  ó SQLServer 2008. El elegir SQLServer 2005 Express Edition es simplemente para probar que Python funciona, mediante pyodbc, con la mayoría de los sistemas gestores de bases de datos actuales. Y para ver si funciona lo mejor es probarlo uno mismo, ya que no es lo mismo conocer el camino que andar el camino.

NOTA: Este post lo he escrito en la plataforma Microsoft Windows XP SP3, con Python 2.6.6.

Imágenes en SQLServer 2005 con Python

Lo primero de todo es descargar el SGBD Microsoft SQLServer 2005 Express Edition.


Se puede hacer desde aquí:

http://www.microsoft.com/downloads/details.aspx?FamilyID=220549b5-0b07-4448-8848-dcc397514b41&displayLang=es

También (recomendable) es posible descargarse el Microsoft SQL Server Management Studio Express, el cual es un entorno gráfico para gestionar las bases de datos que vayamos creando, de la siguiente URL:

http://www.microsoft.com/downloads/details.aspx?displaylang=es&FamilyID=c243a5ae-4bd1-4e3d-94b8-5a0f62bf7796#filelist

Instalamos los dos paquetes de software....


Una vez instalado, nos vamos al Management Studio y creamos una base de datos, llamada imagenes, con una tabla, llamada registro.


Como podemos observar, se crea una tabla con tres campos, un int, un nvarchar y un tipo image.

Una vez tenemos instalado SQLServer 2005, con nuestra base de datos y nuestra tabla, vamos a instalar pyodbc, el software de Python para crear conexiones ODBC a orígenes de datos, en este caso, el software de Microsoft. Para ello, descargamos el pyodbc, desde http://code.google.com/p/pyodbc/. Creamos a continuación, mediante las Herramientas Administrativas/Orígenes de datos (ODBC), nuestra conexión:


NOTA: El lector debe de tener en cuenta que existe un módulo específico para la conexión de SQLServer con Python, llamado pymssql, que se puede obtener de http://code.google.com/p/pymssql/.

NOTA 2: Pudiera ser que con SQLServer 2005 Express Edition aparezcan ciertos errores a la hora de crear la conexión ODBC referidos a la configuración del mismo, con el propósito de permitir conexiones remotas. Bien, la solución la tenéis aquí: http://support.microsoft.com/kb/914277/es.

Una vez creado el ODBC y la conexión correctamente realizada, ya tenemos todo listo para programar en Python.

El código para crear una conexión desde Python a SQLServer 2005, mediante pyodbc, podría ser tal que así:


import pyodbc


# Cadena de conexión.
cadenaConexion = 'DSN=imagenes;UID=ABUSIMBEL\familia'


try:
    conexionSQLServer = pyodbc.connect(cadenaConexion)
    cursorSQLServer = conexionSQLServer.cursor()
    print "Conectado con SQLServer 2005 Express!"
except:
    print "No he podido conectar con base de datos SQLServer!"

# Cerramos cursor y conexión.
print "Cerrando conexiones!"
cursorSQLServer.close()
conexionSQLServer.close()

Si todo ha ido bien, debe de aparecer algo como esto:

Conectado con SQLServer 2005 Express!
Cerrando conexiones!

A continuación vamos a insertar una imagen en la tabla registro de la base de datos imagenes. El código podría ser tal que así:

import os

fichero_imagen = os.path.realpath('alvaro.jpg')
imagen = open(fichero_imagen,'rb').read()

Cargamos un fichero jpg, y lo referenciamos mediante imagen. Se supone que el fichero ha de existir. El código para insertar una imagen en la base de datos es el siguiente:

# Cadena SQL a ejecutar.
cadenaSQL = "insert into registro values (1,'alvaro',?)"
# Lanzamos Sentencia.
cursorSQLServer.execute(cadenaSQL, (pyodbc.Binary(imagen),))
# Confirmamos escritura.
conexionSQLServer.commit()

Cabe destacar que transformamos en binario la imagen cargada del fichero. Además hacemos commit, no olvidarlo. Si nos vamos al Management Studio y hacemos una consulta a la tabla tenemos la solución:


Bien, ya sabemos como insertar imágenes en la base de datos. Ahora toca como recuperar la información, mediante el select correspondiente. El código para hacer esto podría parecerse a lo siguiente:

# Creamos el nombre de un fichero destino.
fichero_destino = os.path.realpath('destino.jpg')
# Lanzamos la consulta de selección SQL.
imagen = cursorSQLServer.execute('SELECT * FROM registro WHERE id = 1')
# Creamos un fichero de destino (temporal).
fichero_final = open(fichero_destino,'wb')
# Escribimos en el fichero y lo cerramos.
fichero_final.write(imagen.next()[2])
fichero_final.close()

Cabe destacar que se crea un fichero de salida temporal (destino.jpg) para obtener en disco el fichero seleccionado de la base de datos. También decir que utilizamos next() para iterar sobre el cursor y obtener la imagen, que se encuentra en la posición 3 de la tupla (es decir, imagen.next()[2]), ya que la tabla tiene 3 campos y el campo imagen es el tercero.

Decir que si se quiere que se vea automáticamente la imagen recuperada, se puede utilizar:

fichero_destino = os.path.realpath('destino.jpg')
os.startfile(fichero_destino)

En mi sistema Windows, automáticamente se abre el programa asociado a ficheros de imágenes, Visor de imágenes y faz de Windows, dando como resultado:


Imágenes serializadas en objetos con Python

Bien. Estamos en la segunda parte del post. Ahora lo que vamos a hacer es guardar una imagen dentro de un objeto y dicho objeto guardarlo en disco. Sería así como un wrapper de la imagen que se guardará en disco. Lo bueno que tiene esto es que se pueden guardar múltiples imágenes en cualquier tipo de contenedor (lista, tupla, diccionario) y guardarla directamente en disco, a capón. Para ello vamos a crear primero una clase, llamada persistencia, que tendrá la funcionalidad de cargar y guardar un objeto.

El código que define la clase persistencia es el siguiente:


import cPickle


class persistencia(object):
    def nombre_clase(self):
        return str(self).split(' ')[0].split('.')[1]


    def cargar(self, nombre_fichero = None):
        if nombre_fichero is None:
            nombre_fichero = self.nombre_clase()
        manejador_fichero = open(nombre_fichero,'r')
        objeto = cPickle.load(manejador_fichero)
        manejador_fichero.close()
        return objeto


    def salvar(self, objeto, nombre_fichero = None):
        if nombre_fichero is None:
            nombre_fichero = self.nombre_clase()
        # Fichero que contendrá el objeto.
        manejador_fichero = open(nombre_fichero,'w')
        # Volcamos el objeto de memoria al fichero.
        cPickle.dump(self, manejador_fichero)
        # Cerramos fichero.
        manejador_fichero.close()


Ahora, únicamente hay que crear una clase que herede de persistencia, para obtener su funcionalidad, e implementar las estructuras necesarias para guardar imágenes. Para ello implementamos la clase registro, tal que así:


class registro(persistencia):
    def __init__(self):
        self._registro = []


    def insertar_persona(self, nombre, foto):
        self._registro.append([nombre, foto])


    def devolver_persona(self, nombre = None):
        if nombre is None:
            return self._registro
        else:
            for i in self._registro:
                if i[0] == nombre:
                    return i
        return None

Ahora falta, por fin, utilizar las clases que hemos diseñado, para poder ver que efectivamente funciona la idea. El código para serializar una imagen en un objeto podría tener el siguiente:

import os

# Cargamos imagen.
fichero_imagen = os.path.realpath('alvaro.jpg')
imagen = open(fichero_imagen,'rb').read()

# Instanciamos la clase que contendrá el registro de personas.
personas = registro()
personas.insertar_persona('alvaro',imagen)
personas.salvar(personas)

Como podemos observar la funcionalidad heredada nos permite guardar la imagen en un fichero, mediante la serialización del objeto. Decir que hemos incluido una imagen únicamente. Podríamos incluir tantas imágenes como quisiéramos. Evidentemente se crea un fichero, llamado registro, el cual tiene el siguiente contenido:


El formato del fichero nos da igual. Lo importante es que podamos recuperar la información guardada en las estructuras de datos de alto nivel que dispone Python. Vamos por último a ver cómo recuperar la información guardada en el objeto serializado. Podríamos utilizar un código como este: 

import os

# Obtenemos datos.
personas = registro()
personas = personas.cargar()
individuo = personas.devolver_persona('alvaro')

# Creamos fichero.
fichero_destino = os.path.realpath('destino.jpg')
imagen = open(fichero_destino,'wb')
imagen.write(individuo[1])
imagen.close()

# Y lo mostramos por pantalla, para ver que efectivamente funciona la idea.
os.startfile(fichero_destino)

Esto es todo por el momento. Saludos.