python curses

7
 La librería curses en Python CUADERNO DE BITÁCORA ¿T e acuerdas de cuando c ambiaste la versión de Firefox por última vez? ¿ y de por qué instalaste ese pro- grama tan raro que parece no servir para nada ? Yo tengo mala memoria, así que uso un cuaderno de bitá- cora. POR JOSÉ MAA RUIZ Y PEDR O ORANTES C uaderno de bitácora, fecha estelar 2123…. En todos los libros sobre administra- ción de sistemas se nos recomienda lle- var un pequeño cuaderno de bitácora donde ir reflejando las acciones peligro- sas que realicemos. De esta manera, se supone, podremos recrear paso a paso los eventos que nos llevaron a un desas- tre y por tanto ir deshaciéndolos en orden inverso. La cruda realidad es que no todo el mundo usa dichos cuadernos. Es pesado tener que dejar el teclado y coger el bolí- grafo para escribir… ¡a mano! ¿no está- bamos en la era de l os ordenadores? ¿No íbamos a desterrar el papel? Muchas personas usan un weblog en su propia máquina o en Internet para ir apuntando detalles o noticias que le resul- tan de interés. Mucha gente incluso publi- ca sus ficheros de configuración, de mane- ra que siempre pueda acceder a ellos. ¿Y qué ocurre si sólo lo queremos para nosotros? ¿Y si la máquina a la que esta- mos accediendo no tiene un servidor web con el software adecuado configura- do para tener un weblog? ¿y si no quere- mos montar tanta parafernalia? Algunas aplicaciones, como KPIM, incorporan ya la opción de llevar un diario personal, pero no funcionan de forma remota a no ser que tengamos una conexión de red con mucho ancho de banda. Python •  DESARROLLO 61 Número 11 WWW.LINUX- MAGAZINE.ES 61 061-066_PythonL11 11.10.2005 9:54 Uhr Página 61

Upload: jose-maria-alfonso-barroso

Post on 18-Jul-2015

217 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Python Curses

5/16/2018 Python Curses - slidepdf.com

http://slidepdf.com/reader/full/python-curses 1/6

 

La librería curses en Python

CUADERNO DEBITÁCORA

¿Te acuerdas de cuando cambiaste la versión de Firefox por última vez? ¿ y de por qué instalaste ese pro-

grama tan raro que parece no servir para nada ? Yo tengo mala memoria, así que uso un cuaderno de bitá-

cora. POR JOSÉ MARÍA RUIZ Y PEDRO ORANTES

Cuaderno de bitácora, fecha estelar 

2123….

En todos los libros sobre administra-

ción de sistemas se nos recomienda lle-

var un pequeño cuaderno de bitácora

donde ir reflejando las acciones peligro-

sas que realicemos. De esta manera, se

supone, podremos recrear paso a paso

los eventos que nos llevaron a un desas-

tre y por tanto ir deshaciéndolos en

orden inverso.

La cruda realidad es que no todo el

mundo usa dichos cuadernos. Es pesado

tener que dejar el teclado y coger el bolí-

grafo para escribir… ¡a mano! ¿no está-

bamos en la era de los ordenadores? ¿No

íbamos a desterrar el papel?

Muchas personas usan un weblog en su

propia máquina o en Internet para ir

apuntando detalles o noticias que le resul-

tan de interés. Mucha gente incluso publi-

ca sus ficheros de configuración, de mane-

ra que siempre pueda acceder a ellos.

¿Y qué ocurre si sólo lo queremos para

nosotros? ¿Y si la máquina a la que esta-

mos accediendo no tiene un servidor

web con el software adecuado configura-

do para tener un weblog? ¿y si no quere-

mos montar tanta parafernalia?

Algunas aplicaciones, como KPIM,

incorporan ya la opción de llevar un

diario personal, pero no funcionan de

forma remota a no ser que tengamos

una conexión de red con mucho ancho

de banda.

Python • DESARROLLO

61Número 11WWW.LINUX- M A G A Z I N E . E S 61

061-066_PythonL11 11.10.2005 9:54 Uhr Página 61

Page 2: Python Curses

5/16/2018 Python Curses - slidepdf.com

http://slidepdf.com/reader/full/python-curses 2/6

 

una familia de librerías que

nos permiten almacenar

datos en un fichero y ges-

tionarlos como si fuesen

un diccionario o hash enPython. Cada entrada se

compone de una clave y

un valor asociado. Si no

tenemos que realizar

búsquedas complejas,

dbm se convertirá en

nuestro mejor opción.

Básicamente tenemos

que mostrar un interfaz que

divida la pantalla en dos partes.

En una deberá mostrar las fechas

almacenadas, y debe permitir reco-

rrerlas. En la otra debe mostrar el texto

relacionado con la fecha indicada.

Las acciones serán:

• Navegar entradas.

• Crear entrada.

• Editar entrada.

• Borrar entrada.

• Salir.

Cada una de las acciones se correspon-

derá con una combinación de teclas.

Comenzaremos creando los objetos que

gestionen los datos y posteriormente el

interfaz con el usuario.

Almacenamiento de datosDebemos conservar el texto asociado a

una fecha y hora en algún sitio. Con la

fiebre actual por las bases de datos rela-

cionales pocas veces se menciona la

existencia de otras bases de datos que no

cumplen el estándar relacional ni SQL.

¿Realmente se necesita un motor rela-

cional y SQL para cualquier cosa que

necesitemos almacenar? Por supuesto

que no. Desgraciadamente, “cuando sólo

tienes un martillo, todo te parecen cla-

vos”.

El problema está en la definición de

“base de datos”, dbm lo es pero sin

mucha sofisticación. Básicamente nos

permite almacenar llaves y valores aso-

ciados a las mismas, así como recuperar

el valor o borrar las llaves, simplemente

eso.

La librería dbm necesita un fichero

donde depositar los datos que se almace-

nan. Así, tendremos que darle el nombre

de un fichero e indicarle cómo 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 acos-

tumbrados la mayor parte de los lengua-jes de programación.

Como podemos observar en el Listado 1,

el uso de la librería dbm es realmente sim-

ple. Se comporta como una lista, con todas

sus operaciones. El lector se habrá pregun-

tado al ver el código “¿dónde 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 sinta-

xis “[]” es lo que en inglés 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 cier-

tas 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 métodos

debido a su uso especial, entre ellos

están:

• def __len__(self)

• def __setitem__(self, clave,

valor)

• def __getitem__(self, clave)

• def __delitem__(self, clave)

¿Qué opcio-

nes nos quedan? Podemos volver nuestra

mirada a la era antigua de los ordenado-

res, cuando los interfaces funcionaban

exclusivamente desde una consola de

texto. Dichos interfaces aún se utilizan

en numerosas aplicaciones, la razón es

que son mucho más simples de usar. Es

más fácil automatizar el pulsar tres veces

TAB que mover el ratón y funcionan

mejor remotamente, aún con conexiones

lentas.

Vamos a diseñar y programar un cua-

derno de bitácora en Python, que utiliza-rá ncurses para el interfaz texto y dbm

para almacenar las entradas por fecha.

Diseño del cuadernoComencemos nuestro diseño echando un

vistazo a las librerías en que nos vamos

a basar. ncurses fue desarrollada para

abstraer, ocultar y simplificar la gestión

de terminales texto. Cada fabricante

dotaba a su terminal texto de caracterís-

ticas distintas a las del resto, forzadas la

mayoría de las veces por una feroz com-

petencia. Esto convertía en una tortura el

simple hecho de cambiar un terminal

por otro, requiriendo la mayoría de las

veces la modificación del programa de

turno. ncurses permitía realizar progra-

mas sin tener en cuenta las diferencias

entre los terminales. No sólo eso, sino

que además simplificó enormemente la

gestión de interfaces de texto como vere-

mos más adelante.

dbm es una “base de datos”. Lo pongo

entre comillas porque en realidad sólo

nos permite almacenar datos, recuperar-

los y realizar búsquedas, pero no usando

SQL sino llamadas a librerías. dbm es

DESARROLLO • Python

62 Número 11 WWW.LINUX - M A G A Z I N E . E S

01 >>> import dbm

02 >>> datos = dbm.open('visitan-

tes','c') # crea el fichero

03 >>> datos["Juan Jose"] = "ven-

dra el martes"

04 >>> datos["Juan Jose"]

05 'vendra el martes'

06 >>> datos.close()

07 >>>

08 >>> datos = dbm.open('visitan-

tes')

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 mar-

tes

17 >>> datos.close()

Listado 1: Ejemplo de usode DBM

061-066_PythonL11 11.10.2005 9:54 Uhr Página 62

Page 3: Python Curses

5/16/2018 Python Curses - slidepdf.com

http://slidepdf.com/reader/full/python-curses 3/6

 

Python • DESARROLLO

63Número 11WWW.LINUX- M A G A Z I N E . E S

un diccionario. Y precisamente eso es lo

que hacemos con nuestro objeto

 Almacen que encubre un diccionario,

añadiendo nuevas acciones. El lector

puede comprobar el código en el Listado

2 (disponible en [1]).

CursesCurses son unas librerías de bajo nivel.

Las abstracciones que crean son muy

básicas: preparar consola, crear “venta-

nas” (nada que ver con las gráficas),

escribir en esas ventanas, recoger carac-

teres y poco más.

Debido a ello son bastante complica-

das de manejar. Hacer cosas vistosas

suele llevar mucho código. Por ello

nos vamos a centrar en un

interfaz sencillo.

Nuestro pro-

grama

será modal, tendrá un modo de “navega-

ción” y uno de “edición”, al igual que el

editor “Vi”. Precisamente “Vi” fue uno

de sus primeros usuarios.

Diseño principalComenzaremos por inicializar curses.

Por desgracia, esto también nos hace

perder el control de nuestra consola

Python, puesto que anula su funciona-

miento. Por ello se pide al lector que eje-

cute todas las acciones relacionadas con

curses desde un programa Python ejecu-

table (recuerda hacer el chmod +x

<programa>). Podemos ver un progra-

ma que inicializa la consola con curses

en el Listado 3.

Posteriormente escribimos un “Hola

mundo” y se refresca la pantalla, pode-

mos ver el resultado en la Figura 1. Si no

refrescamos la pantalla curses no mostra-

rá nada. En el Listado 3 stdscr representa

toda la pantalla. Es posible crear subven-

tanas y hacer actualizaciones selectivas

como podremos comprobar en el código

del programa.

Una vez realizadas las operaciones,

pasamos a dejar la pantalla en una con-

figuración correcta, acción que reali-

zan las cuatro últimas llamadas a

funciones.

El objeto diario creará a suvez un objeto GUI , que ges-

tiona el interfaz, y el obje-

to  Almacen que se

encarga de gestionar

la base de datos.

El objeto

 Almacen es

pasado a

GUI 

como

Estos cuatro métodos los enmascara

python posteriormente de la manera

mostrada en la Tabla 1. Por tanto pode-

mos enmascarar las acciones de un obje-

to de manera que se use como si fuese

Figura 1: Hola Mundo en nuestro primer pro-

grama curses.

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 encontradas18

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]

Listado 2: almacen.py

061-066_PythonL11 11.10.2005 9:55 Uhr Página 63

Page 4: Python Curses

5/16/2018 Python Curses - slidepdf.com

http://slidepdf.com/reader/full/python-curses 4/6

 

paráme-

tro en su crea-

ción. Y la misión de

GUI no es otra que al de res-

ponder a los eventos que el usuario

envíe mediante un bucle infinito.

Dos ventanasNuestro programa va a disponer de dos

ventanas. La mayor hará las veces de

“tablón” donde podemos ver las anota-

ciones realizadas por el momento.

Podremos desplazarnos arriba y abajo

por él. Para indicar qué fecha es la que

tenemos seleccionada la distinguiremos

iluminándola en negrita y subrayándola.

La segunda ventana hará las veces de

barra de ayuda y estado. Cuando cam-

biemos 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 panta-

lla de un terminal tiene 80 columnas de

ancho y 25 filas de alto. Dejaremos una

fila abajo, que será la que usemos para

mostrar información. El resto de 24 filas

se encargarán de mostrar las entradas

almacenadas.

Desplazamiento por lasentradasLa ventana de datos nos permitirá des-

plazarnos arriba y abajo por las entra-

das. ¿Cómo podemos conseguir recrear

este movimiento? La solución es captu-

rando las teclas de los cursores “arriba”

y “abajo”. Cuando una de ellas se pulse

incrementaremos o decrementaremosuna variable que establece la posición la

entrada seleccionada en cada momento y

volveremos a dibujar, o escribir, la panta-

lla de datos. Pero no lo haremos de cual-

quier forma.

Queremos que el efecto sea vistoso,

así que siempre intentaremos mostrar

un número fijo de entradas encima y

debajo de la nuestra. Como tenemos la

posición de la entrada seleccionada, o

resaltada, con un sencillo cálculo

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 comien-

 zo y fin formando una nueva lista.

Simplemente tenemos que seleccionar

aquellos que estén a una distancia fija

del seleccionado y usarlos como

comienzo y fin.

Podemos ver el código que realiza esta

acción en el método dibuja_fechas(self)

de GUI  en el Listado 6 (disponible en

[1]).

DESARROLLO • Python

64 Número 11 WWW.LINUX - M A G A Z I N E . E S

01 #!/usr/local/bin/python

02 # -*- coding: ISO8859-1 -*-

03

04 import curses

05 import curses.textpad

06

07 ncols, nlines = 9, 4

08 uly, ulx = 15, 20

09 stdscr.addstr(uly-2, ulx, "Use

Ctrl-G to end editing.")

10 win = curses.newwin(nlines,

ncols, uly, ulx)

11 rectangle(stdscr, uly-1,

ulx-1, uly + nlines, ulx +

ncols)

12 stdscr.refresh()

13 return Textbox(win).edit()

14

15 str =

curses.wrapper(test_editbox)

16 print 'Contents of text box:',

repr(str)

Listado 4: Ejemplo de usode Textbox

01 #!/usr/local/bin/python

02 # -*- coding: ISO8859-1 -*-

03

04 import curses

05

06 # Inicializamos la pantalla

07 stdscr=curses.initscr()

08 curses.noecho()

09 curses.cbreak()10 stdscr.keypad(1)

11

12 # Escribimos algo

13 stdscr.addstr("Hola mundo",0)

14 stdscr.refresh()

15

16 # Limpiamos la pantalla

17

18 stdscr.keypad(0)

19 curses.echo()

20 curses.nocbreak()

21 curses.endwin()

Listado 3: “Hola mundo”con curses 

061-066_PythonL11 11.10.2005 9:55 Uhr Página 64

Page 5: Python Curses

5/16/2018 Python Curses - slidepdf.com

http://slidepdf.com/reader/full/python-curses 5/6

 

Textbox

Python provee de una herramienta muy

útil para la edición de textos dentro de

curses. Desgraciadamente, a pesar de su

potencia posee algunos inconvenientes

de los que hablaremos más tarde.

Esta herramienta es el objeto Textbox

que se encuentra en la librería

curses.textpad. Textbox nos permite edi-

tar un texto dentro de una ventana y

poder utilizar muchas de las combina-

ciones 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 carácter

sobre el que nos encontremos.

Utilizaremos un Textbox para recoger

el texto que el usuario quiera introducir.

Desgraciadamente posee una limitacióndebido su diseño. Si cuando estamos edi-

tando el texto, pulsamos repetidas veces

el cursor izquierdo desplazándonos

hasta dar con el borde de la ventana, el

programa fallará.

El soporte de curses de Python se basa

en las librerías originales escritas en C , y

como ya hemos dicho son de muy bajo

nivel. La implementación de Textbox es

realmente básica y no controla todas las

circunstancias. Aún así nos hará un

buen servicio.

Un ejemplo de utilización de Textbox

aparece en su propio código fuente, ver

Listado 4 y Figura 2. Este código genera

un rectángulo con bordes (usando la

función rectangle de curses.textpad) y

nos solicita que escribamos algo en el

mismo. Para acabar debemos pulsar con-

trol+g , mostrándonos lo escrito más

abajo. Si lo probamos comprobaremos

que no podemos salir del rectángulo al

editar.

En la Figura 3 vemos cómo hemos

integrado el Textbox en nuestro progra-

ma. Hemos aumentado el número de

columnas para permitir introducir men-

sajes más largos.

El gestor de comandosExisten muchas maneras de hacer un

gestor de comandos. La más típica con-

Python • DESARROLLO

Figura 3: Inserción de una nueva entrada en

la bitácora.

Figura 2: Un cuadro de texto curses.

061-066_PythonL11 11.10.2005 9:55 Uhr Página 65

Page 6: Python Curses

5/16/2018 Python Curses - slidepdf.com

http://slidepdf.com/reader/full/python-curses 6/6

 

Podemos invocar el método habla usan-

do una cadena con su nombre mediante

el método getattr(), que precisa de la ins-

tancia del objeto y el método a invocar.

Devuelve, por así decirlo, una referenciaal método en cuestión que funciona de la

misma manera. Como dicen por ahí,

“una imagen vale más que mil pala-

bras”:

>>> pepe = Persona()

>>> pepe.habla()

hola mundo

>>> (getattr(pepe, "habla"))()

hola mundo

>>>

Lo que haremos será crear una lista de

listas, cada una de las cuales contendrá

dos elementos. El primero será un carác-

ter y el segundo el nombre del método a

invocar. De esta manera, nuestro gestor

de comandos se reduce a un código que

recibe un carácter, lo compara con el pri-

mer elemento de cada entrada en su lista

de comandos y, si encuentra una coinci-

dencia, ejecuta el comando asociado. Ver

Listado 5.

Como podemos ver en el código, se

comprueba si el carácter recibido es

“imprimible” y posteriormente se busca

en la lista de comandos. En caso de coin-cidencia se ejecuta usando como instan-

cia self . De esta manera, es posible mani-

pular el funcionamiento según qué

caracteres responde el programa sin

tener que modificar el código fuente.

Esto nos da mucha flexibilidad y es

menos propenso a errores.

Uso del programaEl uso del programa se ha hecho lo más

simple posible, el aspecto del mismo

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á introdu-

ciendo un texto, al pulsar “control+g”

se guarda. Para salir se pulsa “q” y con

los cursores “arriba” y “abajo” nos des-

plazamos 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.

ConclusiónAunque el uso de curses puede resultar

engorroso, Python nos provee de una

librería que las manipula dentro de su

instalación 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 demás alto nivel. Existen librerías que nos

proporcionan barras menús y widgets

más avanzados. Pero siempre es bueno

estar lo más cerca posible del estándar.

La próxima vez que tengas que hacer

un interfaz en modo texto puede que

fuese una buena idea darle una oportu-

nidad a curses. I

siste en hacer una sentencia switch o

gran cantidad de if s anidados, cada uno

de los cuales responde ante una tecla o

combinación distinta. El código genera-

do llega a convertirse en ilegible encuanto el número de comandos sobrepa-

sa los diez.

Hay una manera mucho más elegante

de atacar este problema, pero no es tan

fácil hacer uso de ella en todos los len-

guajes. Afortunadamente Python nos

permite una implementación muy senci-

lla. 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 método de nues-

tro objeto “GUI”. Hasta aquí todo es bas-

tante normal. Ahora viene la magia.

Python nos permite invocar métodos

de objetos usando su nombre. Si declara-

mos el objeto persona:

>>> class Persona:

... def habla(self):

... print "hola mundo"

...

>>>

DESARROLLO • Python

66 Número 11 WWW.LINUX - M A G A Z I N E . E S

[1] Descargas de los listados de este artí-

culo: http://www.linux-magazine.es/ 

Magazine/Downloads/11

RECURSOS

José María Ruiz actualmente está 

realizando el Proyecto Fin de 

Carrera de Ingeniería Técnica en

Informática de Sistemas mientras 

estudia Física. Lleva 8 años usan- 

do y desarrollando software libre 

y, desde hace dos, se está especia- 

lizando en FreeBSD. Pedro 

Orantes está cursando 3º de 

Ingeniería Técnica en Informática 

de Sistemas y en sus ratos libres 

toca en un grupo de música.

      L      O      S

      A      U      T      O      R      E      S

 __len__(self) devuelve la longitud de 

nuestro objeto. Se invoca cuando se eje- 

cuta len(miObjeto) 

 __setitem(self,clave,valor) se correspon- 

de con la asignación en un diccionario: 

miObjeto["Algo"] = "otra cosa" __geti- 

tem(self, clave) es el equivalente 

miObjeto["Algo"] y devuelve la informa- 

ción almacenada en “Algo”.

 __delitem(self, clave) es del 

miObjeto["Algo"] y se corresponde con

la eliminación de esa entrada.

TABLA 1: ALGUNOS MÉTODOSESPECIALES DE PYTHON

01 def ejecuta_commando(self,

ch):

02 "Procesa las teclas recibi-

das"

03 if curses.ascii.isprint(ch):

04 for comando in self.coman-

dos:

05 if comando[0] == chr(ch):

06 (getattr(self,coman-

do[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

Listado 5: Métodoejecuta_comando(self,ch) 

Figura 4: Vista de la bitácora con varias

entradas.

061-066_PythonL11 11.10.2005 9:55 Uhr Página 66