Você está na página 1de 6

061-066_PythonL11

11.10.2005

9:54

Uhr

Pgina

61

Python DESARROLLO

La librera curses en Python

CUADERNO DE BITCORA

Te acuerdas de cuando cambiaste la versin de Firefox por ltima vez? y de por qu instalaste ese programa tan raro que parece no servir para nada ? Yo tengo mala memoria, as que uso un cuaderno de bitcora. POR JOS MARA RUIZ Y PEDRO ORANTES

En todos los libros sobre administracin de sistemas se nos recomienda llevar un pequeo cuaderno de bitcora donde ir reflejando las acciones peligrosas que realicemos. De esta manera, se supone, podremos recrear paso a paso los eventos que nos llevaron a un desastre y por tanto ir deshacindolos en orden inverso.

uaderno de bitcora, fecha estelar 2123.

La cruda realidad es que no todo el mundo usa dichos cuadernos. Es pesado tener que dejar el teclado y coger el bolgrafo para escribir a mano! no estbamos en la era de los ordenadores? No bamos a desterrar el papel? Muchas personas usan un weblog en su propia mquina o en Internet para ir apuntando detalles o noticias que le resultan de inters. Mucha gente incluso publica sus ficheros de configuracin, de manera que siempre pueda acceder a ellos.

Y qu ocurre si slo lo queremos para nosotros? Y si la mquina a la que estamos accediendo no tiene un servidor web con el software adecuado configurado para tener un weblog? y si no queremos montar tanta parafernalia? Algunas aplicaciones, como KPIM, incorporan ya la opcin de llevar un diario personal, pero no funcionan de forma remota a no ser que tengamos una conexin de red con mucho ancho de banda.

WWW.LINUX- MAGAZINE.ES

Nmero 11

61

061-066_PythonL11

11.10.2005

9:54

Uhr

Pgina

62

DESARROLLO Python

Qu opciones nos quedan? Podemos volver nuestra mirada a la era antigua de los ordenadores, cuando los interfaces funcionaban exclusivamente desde una consola de texto. Dichos interfaces an se utilizan en numerosas aplicaciones, la razn es que son mucho ms simples de usar. Es ms fcil automatizar el pulsar tres veces TAB que mover el ratn y funcionan mejor remotamente, an con conexiones lentas. Vamos a disear y programar un cuaderno de bitcora en Python, que utilizar ncurses para el interfaz texto y dbm para almacenar las entradas por fecha.

una familia de libreras que nos permiten almacenar datos en un fichero y gestionarlos como si fuesen un diccionario o hash en Python. Cada entrada se compone de una clave y un valor asociado. Si no tenemos que realizar bsquedas complejas, dbm se convertir en nuestro mejor opcin. Bsicamente tenemos que mostrar un interfaz que divida la pantalla en dos partes. En una deber mostrar las fechas almacenadas, y debe permitir recorrerlas. En la otra debe mostrar el texto relacionado con la fecha indicada. Las acciones sern: Navegar entradas. Crear entrada. Editar entrada. Borrar entrada. Salir. Cada una de las acciones se corresponder con una combinacin de teclas. Comenzaremos creando los objetos que gestionen los datos y posteriormente el interfaz con el usuario.

Almacenamiento de datos
Debemos conservar el texto asociado a una fecha y hora en algn sitio. Con la fiebre actual por las bases de datos relacionales pocas veces se menciona la existencia de otras bases de datos que no cumplen el estndar relacional ni SQL. Realmente se necesita un motor relacional y SQL para cualquier cosa que necesitemos almacenar? Por supuesto que no. Desgraciadamente, cuando slo tienes un martillo, todo te parecen clavos. El problema est en la definicin de base de datos, dbm lo es pero sin mucha sofisticacin. Bsicamente nos permite almacenar llaves y valores asociados a las mismas, as como recuperar el valor o borrar las llaves, simplemente eso. La librera dbm necesita un fichero donde depositar los datos que se almacenan. As, tendremos que darle el nombre de un fichero e indicarle cmo queremos que lo trate. Puede abrir el fichero para introducir nuevos datos o crearlo de nuevo, aunque ya exista uno con el mismo nombre.

Una vez abierto el fichero, un objeto dbm se comporta como un contenedor cualquiera. Podremos hacer uso de la sintaxis [] a la que nos tienen acostumbrados la mayor parte de los lenguajes de programacin. Como podemos observar en el Listado 1, el uso de la librera dbm es realmente simple. Se comporta como una lista, con todas sus operaciones. El lector se habr preguntado al ver el cdigo dnde est el truco? si dbm representa una base de datos por qu puede hacer uso de la sintaxis []. La respuesta es que en Python la sintaxis [] es lo que en ingls se llama syntatic sugar. Por traducirlo de alguna manera, viene a decir que es una manera de hacer agradable visualmente (y a nuestros pobres dedos) la llamada a ciertas funciones del lenguaje. Podemos incorporar [] a uno de nuestro objetos y hacer que se comporte como una lista? La respuesta es si! y no tiene nada de complicado. Python reserva unas serie de mtodos debido a su uso especial, entre ellos estn: def __len__(self) def __setitem__(self, clave, valor) def __getitem__(self, clave) def __delitem__(self, clave)

Diseo del cuaderno


Comencemos nuestro diseo echando un vistazo a las libreras en que nos vamos a basar. ncurses fue desarrollada para abstraer, ocultar y simplificar la gestin de terminales texto. Cada fabricante dotaba a su terminal texto de caractersticas distintas a las del resto, forzadas la mayora de las veces por una feroz competencia. Esto converta en una tortura el simple hecho de cambiar un terminal por otro, requiriendo la mayora de las veces la modificacin del programa de turno. ncurses permita realizar programas sin tener en cuenta las diferencias entre los terminales. No slo eso, sino que adems simplific enormemente la gestin de interfaces de texto como veremos ms adelante. dbm es una base de datos. Lo pongo entre comillas porque en realidad slo nos permite almacenar datos, recuperarlos y realizar bsquedas, pero no usando SQL sino llamadas a libreras. dbm es

Listado 1: Ejemplo de uso de DBM


01 >>> import dbm 02 >>> datos = dbm.open('visitantes','c') # crea el fichero 03 >>> datos["Juan Jose"] = "vendra el martes" 04 >>> datos["Juan Jose"] 05 'vendra el martes' 06 >>> datos.close() 07 >>> 08 >>> datos = dbm.open('visitantes') 09 >>> datos["Juan Jose"] 10 'vendra el martes' 11 >>> datos.keys() 12 ['Juan Jose'] 13 >>> for llave in datos.keys(): 14 ... print "["+llave+"] -> " + datos[llave] 15 ... 16 [Juan Jos] -> vendra el martes 17 >>> datos.close()

62

Nmero 11

WWW.LINUX- MAGAZINE.ES

061-066_PythonL11

11.10.2005

9:55

Uhr

Pgina

63

Python DESARROLLO

Estos cuatro mtodos los enmascara python posteriormente de la manera mostrada en la Tabla 1. Por tanto podemos enmascarar las acciones de un objeto de manera que se use como si fuese

ser modal, tendr un modo de navegacin y uno de edicin, al igual que el editor Vi. Precisamente Vi fue uno de sus primeros usuarios.

Diseo principal
Figura 1: Hola Mundo en nuestro primer programa curses.

Listado 2: almacen.py
01 #!/usr/local/bin/python 02 03 import dbm 04 class Almacen: 05 def __init__ (self,nombre): 06 self.bd = dbm.open(nombre,'c') 07 08 def busca_palabra (self, palabra): 09 claves = self.entradas() 10 encontradas = [] 11 12 for clave in claves: 13 contenido = self.contenido(clave) 14 if palabra in contenido: 15 encontradas.push(clave) 16 17 return encontradas 18 19 def entradas (self): 20 a = self.bd.keys() 21 if not a: 22 a = [] 23 return a 24 25 def cierra (self): 26 self.bd.close() 27 28 def __len__(self): 29 return len(self.entradas()) 30 31 32 def __setitem__ (self, clave, valor): 33 self.bd[clave] = valor 34 35 def __getitem__(self,clave): 36 return self.bd[clave] 37 38 def __delitem__(self,clave): 39 del self.bd[clave]

un diccionario. Y precisamente eso es lo que hacemos con nuestro objeto Almacen que encubre un diccionario, aadiendo nuevas acciones. El lector puede comprobar el cdigo en el Listado 2 (disponible en [1]).

Curses
Curses son unas libreras de bajo nivel. Las abstracciones que crean son muy bsicas: preparar consola, crear ventanas (nada que ver con las grficas), escribir en esas ventanas, recoger caracteres y poco ms. Debido a ello son bastante complicadas de manejar. Hacer cosas vistosas suele llevar mucho cdigo. Por ello nos vamos a centrar en un interfaz sencillo. Nuestro programa

Comenzaremos por inicializar curses. Por desgracia, esto tambin nos hace perder el control de nuestra consola Python, puesto que anula su funcionamiento. Por ello se pide al lector que ejecute todas las acciones relacionadas con curses desde un programa Python ejecutable (recuerda hacer el chmod +x <programa>). Podemos ver un programa que inicializa la consola con curses en el Listado 3. Posteriormente escribimos un Hola mundo y se refresca la pantalla, podemos ver el resultado en la Figura 1. Si no refrescamos la pantalla curses no mostrar nada. En el Listado 3 stdscr representa toda la pantalla. Es posible crear subventanas y hacer actualizaciones selectivas como podremos comprobar en el cdigo del programa. Una vez realizadas las operaciones, pasamos a dejar la pantalla en una configuracin correcta, accin que realizan las cuatro ltimas llamadas a funciones. El objeto diario crear a su vez un objeto GUI, que gestiona el interfaz, y el objeto Almacen que se encarga de gestionar la base de datos. El objeto Almacen es pasado a GUI como

WWW.LINUX- MAGAZINE.ES

Nmero 11

63

061-066_PythonL11

11.10.2005

9:55

Uhr

Pgina

64

DESARROLLO Python

parmetro en su creacin. Y la misin de GUI no es otra que al de responder a los eventos que el usuario enve mediante un bucle infinito.

Dos ventanas
Listado 3: Hola mundo con curses
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 #!/usr/local/bin/python # -*- coding: ISO8859-1 -*import curses # Inicializamos la pantalla stdscr=curses.initscr() curses.noecho() curses.cbreak() stdscr.keypad(1) # Escribimos algo stdscr.addstr("Hola mundo",0) stdscr.refresh() # Limpiamos la pantalla stdscr.keypad(0) curses.echo() curses.nocbreak() curses.endwin()

das. Cmo podemos conseguir recrear este movimiento? La solucin es capturando las teclas de los cursores arriba y abajo. Cuando una de ellas se pulse incrementaremos o decrementaremos una variable que establece la posicin la entrada seleccionada en cada momento y volveremos a dibujar, o escribir, la pantalla de datos. Pero no lo haremos de cualquier forma. Queremos que el efecto sea vistoso, as que siempre intentaremos mostrar un nmero fijo de entradas encima y debajo de la nuestra. Como tenemos la posicin de la entrada seleccionada, o resaltada, con un sencillo clculo podemos seleccionar qu entradas mostraremos. Las listas en Python tienen una funcionalidad que nos ser muy til. Usando la sintaxis lista[comienzo:fin] podemos extraer los elementos entre comienzo y fin formando una nueva lista. Simplemente tenemos que seleccionar aquellos que estn a una distancia fija del seleccionado y usarlos como comienzo y fin. Podemos ver el cdigo que realiza esta accin en el mtodo dibuja_fechas(self) de GUI en el Listado 6 (disponible en [1]).

Nuestro programa va a disponer de dos ventanas. La mayor har las veces de tabln donde podemos ver las anotaciones realizadas por el momento. Podremos desplazarnos arriba y abajo por l. Para indicar qu fecha es la que tenemos seleccionada la distinguiremos iluminndola en negrita y subrayndola. La segunda ventana har las veces de barra de ayuda y estado. Cuando cambiemos el estado, por ejemplo al editar, se reflejar ah. Es el mismo modo de trabajo del que hace gala VIM. Las ventanas deben partir la pantalla de manera que no se solapen. La pantalla de un terminal tiene 80 columnas de ancho y 25 filas de alto. Dejaremos una fila abajo, que ser la que usemos para mostrar informacin. El resto de 24 filas se encargarn de mostrar las entradas almacenadas.

Listado 4: Ejemplo de uso de Textbox


01 02 03 04 05 06 07 08 09 10 11 #!/usr/local/bin/python # -*- coding: ISO8859-1 -*import curses import curses.textpad ncols, nlines = 9, 4 uly, ulx = 15, 20 stdscr.addstr(uly-2, ulx, "Use Ctrl-G to end editing.") win = curses.newwin(nlines, ncols, uly, ulx) rectangle(stdscr, uly-1, ulx-1, uly + nlines, ulx + ncols) stdscr.refresh() return Textbox(win).edit()

Desplazamiento por las entradas


La ventana de datos nos permitir desplazarnos arriba y abajo por las entra-

12 13 14 15 str = curses.wrapper(test_editbox) 16 print 'Contents of text box:', repr(str)

64

Nmero 11

WWW.LINUX- MAGAZINE.ES

061-066_PythonL11

11.10.2005

9:55

Uhr

Pgina

65

Python DESARROLLO

Figura 2: Un cuadro de texto curses.

Textbox
Python provee de una herramienta muy til para la edicin de textos dentro de curses. Desgraciadamente, a pesar de su potencia posee algunos inconvenientes de los que hablaremos ms tarde. Esta herramienta es el objeto Textbox que se encuentra en la librera curses.textpad. Textbox nos permite editar un texto dentro de una ventana y poder utilizar muchas de las combinaciones de teclas que soporta EMACS. As por ejemplo con control+e iremos al final de la linea que estemos editando y

con control+d borraremos el carcter sobre el que nos encontremos. Utilizaremos un Textbox para recoger el texto que el usuario quiera introducir. Desgraciadamente posee una limitacin debido su diseo. Si cuando estamos editando el texto, pulsamos repetidas veces el cursor izquierdo desplazndonos hasta dar con el borde de la ventana, el programa fallar. El soporte de curses de Python se basa en las libreras originales escritas en C, y como ya hemos dicho son de muy bajo nivel. La implementacin de Textbox es realmente bsica y no controla todas las circunstancias. An as nos har un buen servicio. Un ejemplo de utilizacin de Textbox aparece en su propio cdigo fuente, ver Listado 4 y Figura 2. Este cdigo genera un rectngulo con bordes (usando la funcin rectangle de curses.textpad) y nos solicita que escribamos algo en el mismo. Para acabar debemos pulsar con-

Figura 3: Insercin de una nueva entrada en la bitcora.

trol+g, mostrndonos lo escrito ms abajo. Si lo probamos comprobaremos que no podemos salir del rectngulo al editar. En la Figura 3 vemos cmo hemos integrado el Textbox en nuestro programa. Hemos aumentado el nmero de columnas para permitir introducir mensajes ms largos.

El gestor de comandos
Existen muchas maneras de hacer un gestor de comandos. La ms tpica con-

061-066_PythonL11

11.10.2005

9:55

Uhr

Pgina

66

DESARROLLO Python

siste en hacer una sentencia switch o gran cantidad de ifs anidados, cada uno de los cuales responde ante una tecla o combinacin distinta. El cdigo generado llega a convertirse en ilegible en cuanto el nmero de comandos sobrepasa los diez. Hay una manera mucho ms elegante de atacar este problema, pero no es tan fcil hacer uso de ella en todos los lenguajes. Afortunadamente Python nos permite una implementacin muy sencilla. La idea es la siguiente. Cada comando estar asociado a una serie de acciones a realizar. Englobaremos las acciones vinculadas con cada comando a un mtodo de nuestro objeto GUI. Hasta aqu todo es bastante normal. Ahora viene la magia. Python nos permite invocar mtodos de objetos usando su nombre. Si declaramos el objeto persona:
>>> class Persona: ... def habla(self): ... print "hola mundo" ... >>>

Podemos invocar el mtodo habla usando una cadena con su nombre mediante el mtodo getattr(), que precisa de la instancia del objeto y el mtodo a invocar. Devuelve, por as decirlo, una referencia al mtodo en cuestin que funciona de la misma manera. Como dicen por ah, una imagen vale ms que mil palabras:
>>> pepe = Persona() >>> pepe.habla() hola mundo >>> (getattr(pepe, "habla"))() hola mundo >>>

Figura 4: Vista de la bitcora con varias entradas.

Listado 5: Mtodo ejecuta_comando(self,ch)


01 def ejecuta_commando(self, ch): 02 "Procesa las teclas recibidas" 03 if curses.ascii.isprint(ch): 04 for comando in self.comandos: 05 if comando[0] == chr(ch): 06 (getattr(self,comando[1]))() 07 break 08 else: 09 if ch in (curses.ascii.DLE, curses.KEY_UP): 10 self.incr_pos_fechas() 11 self.redibuja() 12 elif ch in (curses.ascii.SO, curses.KEY_DOWN): 13 self.decr_pos_fechas() 14 self.redibuja() 15 self.refresca() 16 return 1

Lo que haremos ser crear una lista de listas, cada una de las cuales contendr dos elementos. El primero ser un carcter y el segundo el nombre del mtodo a invocar. De esta manera, nuestro gestor de comandos se reduce a un cdigo que recibe un carcter, lo compara con el primer elemento de cada entrada en su lista de comandos y, si encuentra una coincidencia, ejecuta el comando asociado. Ver Listado 5. Como podemos ver en el cdigo, se comprueba si el carcter recibido es imprimible y posteriormente se busca en la lista de comandos. En caso de coincidencia se ejecuta usando como instancia self. De esta manera, es posible manipular el funcionamiento segn qu caracteres responde el programa sin tener que modificar el cdigo fuente. Esto nos da mucha flexibilidad y es menos propenso a errores.

ser el mostrado en la Figura 4. Cuando se pulsa n se crea una nueva entrada con la fecha y la hora, si existe ya una entrada con la fecha y la hora no se hace nada. Con d se elimina una entrada y con e se edita. Cuando se est introduciendo un texto, al pulsar control+g se guarda. Para salir se pulsa q y con los cursores arriba y abajo nos desplazamos por el programa. Al fichero de almacenado se le ha dado el nombre diario.db. Si no existe se crea, y si existe se emplea el existente.

Conclusin
Aunque el uso de curses puede resultar engorroso, Python nos provee de una librera que las manipula dentro de su instalacin base. Una vez realizado el programa sabremos que cualquiera que instale Python podr hacer uso de l. Siempre es posible realizar una serie de objetos que lleven a cabo tareas de ms alto nivel. Existen libreras que nos proporcionan barras mens y widgets ms avanzados. Pero siempre es bueno estar lo ms cerca posible del estndar. La prxima vez que tengas que hacer un interfaz en modo texto puede que fuese una buena idea darle una oportuI nidad a curses.

Uso del programa


El uso del programa se ha hecho lo ms simple posible, el aspecto del mismo

RECURSOS
[1] Descargas de los listados de este artculo: http://www.linux-magazine.es/ Magazine/Downloads/11

TABLA 1: ALGUNOS MTODOS ESPECIALES DE PYTHON


__len__(self) devuelve la longitud de nuestro objeto. Se invoca cuando se ejecuta len(miObjeto) __setitem(self,clave,valor) se corresponde con la asignacin en un diccionario: miObjeto["Algo"] = "otra cosa" __getitem(self, clave) es el equivalente miObjeto["Algo"] y devuelve la informacin almacenada en Algo. __delitem(self, clave) es del miObjeto["Algo"] y se corresponde con la eliminacin de esa entrada.

Jos Mara Ruiz actualmente est realizando el Proyecto Fin de Carrera de Ingeniera Tcnica en Informtica de Sistemas mientras estudia Fsica. Lleva 8 aos usando y desarrollando software libre y, desde hace dos, se est especializando en FreeBSD. Pedro Orantes est cursando 3 de Ingeniera Tcnica en Informtica de Sistemas y en sus ratos libres toca en un grupo de msica.

66

Nmero 11

WWW.LINUX- MAGAZINE.ES

LOS AUTORES

Você também pode gostar