Programación de maldiciones con
Python
Autor: Soy Kuchling, Eric S. Raymond
Lanzamiento
2.04
:
Resumen
Este documento describe cómo usar el módulo de extensión de curses para
controlar las pantallas de modo de texto.
¿Qué es maldiciones?
La biblioteca curses proporciona una herramienta de manejo de pantalla y
pintura de pantalla independiente de terminal para terminales basadas en
texto; tales terminales incluyen VT100s, la consola de Linux y el terminal
simulado proporcionado por varios programas. Los terminales de pantalla
admiten varios códigos de control para realizar operaciones comunes, como
mover el cursor, desplazar la pantalla y borrar áreas. Diferentes terminales
utilizan códigos muy diferentes y, a menudo, tienen sus propias peculiaridades
menores.
En un mundo de pantallas gráficas, uno podría preguntar "¿por qué
molestarse"? Es cierto que los terminales de pantalla de células de caracteres
son una tecnología obsoleta, pero hay nichos en los que ser capaz de hacer
cosas elegantes con ellos sigue siendo valioso. Un nicho está en Unixes
pequeños o integrados que no ejecutan un servidor X. Otra es herramientas
como los instaladores de SO y los configuradores de kernel que deben
ejecutarse antes de que esté disponible cualquier soporte gráfico.
La biblioteca de curses proporciona una funcionalidad bastante básica, que
proporciona al programador una abstracción de una pantalla que contiene
varias ventanas de texto que no se superponen. El contenido de una ventana
se puede cambiar de varias maneras (agregar texto, borrarlo, cambiar su
apariencia) y la biblioteca de curses determinará qué códigos de control deben
enviarse al terminal para producir la salida correcta. las cursas no proporcionan
muchos conceptos de interfaz de usuario, como botones, casillas de
verificación o cuadros de diálogo; Si necesita tales funciones, considere una
biblioteca de interfaz de usuario como Urwid .
La biblioteca de curses fue escrita originalmente para BSD Unix; las últimas
versiones System V de Unix de AT&T agregaron muchas mejoras y nuevas
funciones. BSD ya no se mantiene, ya que ha sido reemplazado por ncurses,
que es una implementación de código abierto de la interfaz de AT&T. Si está
utilizando un Unix de código abierto, como Linux o FreeBSD, es casi seguro
que su sistema utiliza ncurses. Dado que la mayoría de las versiones
comerciales actuales de Unix se basan en el código del Sistema V, todas las
funciones descritas aquí probablemente estarán disponibles. Sin embargo, las
versiones anteriores de curses llevadas por algunos Unixes propietarios
pueden no ser compatibles con todo.
La versión de Python para Windows no incluye el módulo de curses . Una
versión portada llamada UniCurses está disponible. También puede probar el
módulo de consola escrito por Fredrik Lundh, que no usa la misma API que los
curses pero proporciona salida de texto direccionable por el cursor y soporte
completo para la entrada del mouse y el teclado.
El módulo de curses de Python
El módulo Thy Python es un envoltorio bastante simple sobre las funciones C
proporcionadas por curses; Si ya está familiarizado con la programación de
curses en C, es realmente fácil transferir ese conocimiento a Python. La mayor
diferencia es que la interfaz de Python simplifica las cosas al combinar
diferentes funciones de C como addstr () , mvaddstr () y mvwaddstr
() en un solo método addstr () . Verás esto cubierto con más detalle más
adelante.
Este COMO es una introducción a la escritura de programas en modo texto con
curses y Python. No intenta ser una guía completa de la API de curses; para
eso, vea la sección de la guía de la biblioteca de Python en ncurses, y las
páginas del manual de C para ncurses. Sin embargo, te dará las ideas básicas.
Iniciar y finalizar una aplicación de curses
Antes de hacer cualquier cosa, las maldiciones deben ser inicializadas. Esto se
hace llamando a la función initscr () , que determinará el tipo de terminal,
enviará los códigos de configuración necesarios al terminal y creará varias
estructuras de datos internas. Si tiene éxito, initscr ()devuelve un objeto de
ventana que representa la pantalla completa; Esto generalmente se
llama stdscr después del nombre de la variable C correspondiente.
importar curses
stdscr = curses . initscr ()
Por lo general, las aplicaciones de cursas desactivan el eco automático de las
teclas en la pantalla, para poder leer las teclas y solo mostrarlas en ciertas
circunstancias. Esto requiere llamar a la función noecho () .
maldiciones . noecho ()
En general, las aplicaciones también deberán reaccionar a las teclas al
instante, sin necesidad de presionar la tecla Enter; esto se denomina modo
cbreak, a diferencia del modo de entrada con búfer habitual.
maldiciones . cbreak ()
Los terminales generalmente devuelven teclas especiales, como las teclas de
cursor o las teclas de navegación, como Página arriba y Inicio, como una
secuencia de escape multibyte. Si bien puede escribir su aplicación para
esperar tales secuencias y procesarlas en consecuencia, las cursas pueden
hacerlo por usted, devolviendo un valor especial
como curses.KEY_LEFT . Para obtener maldiciones para hacer el trabajo,
tendrás que habilitar el modo de teclado.
stdscr . teclado ( Verdadero )
Terminar una aplicación de curses es mucho más fácil que iniciar una. Tendrá
que llamar:
maldiciones . nocbreak ()
stdscr . Teclado ( falso )
curses . eco ()
para revertir la configuración de terminal amigable de curses. Luego llame a la
función endwin () para restaurar el terminal a su modo de operación original.
maldiciones . endwin ()
Un problema común cuando se depura una aplicación de curses es hacer que
su terminal se confunda cuando la aplicación muere sin restaurar el terminal a
su estado anterior. En Python, esto ocurre comúnmente cuando su código tiene
errores y genera una excepción no detectada. Las teclas ya no se reproducen
en la pantalla cuando las escribe, por ejemplo, lo que dificulta el uso del shell.
En Python puedes evitar estas complicaciones y hacer que la depuración sea
mucho más sencilla importando la función curses.wrapper () y usándola de
esta manera:
desde curses importar envoltorio
def main ( stdscr ):
# Borrar pantalla
stdscr . claro ()
# Esto aumenta ZeroDivisionError cuando i == 10.
para i en el rango ( 0 , 11 ):
v = i - 10
stdscr . addstr ( i , 0 , '10 dividido por {} es {}
' . formato ( v , 10 / v ))
stdscr . actualizar ()
stdscr . getkey ()
envoltura ( principal )
La función wrapper () toma un objeto que se puede llamar y realiza las
inicializaciones descritas anteriormente, también inicializando los colores si el
soporte de color está presente. la envoltura () luego ejecuta su callamable
proporcionado. Una vez que se devuelve la llamada, wrapper ()restaurará el
estado original del terminal. El invocable se llama dentro de
un intento ... excepto que captura excepciones, restaura el estado del
terminal y luego vuelve a elevar la excepción. Por lo tanto, su terminal no se
quedará en un estado divertido a excepción y podrá leer el mensaje de la
excepción y el rastreo.
Windows y Pads
Las ventanas son la abstracción básica en curses. Un objeto de ventana
representa un área rectangular de la pantalla y admite métodos para mostrar
texto, borrarlo, permitir que el usuario ingrese cadenas, etc.
El objeto stdscr devuelto por la función initscr () es un objeto de ventana
que cubre toda la pantalla. Es posible que muchos programas solo necesiten
esta única ventana, pero es posible que desee dividir la pantalla en ventanas
más pequeñas para volver a dibujarlas o borrarlas por separado. La
función newwin () crea una nueva ventana de un tamaño dado, devolviendo
el nuevo objeto de ventana.
begin_x = 20 ; begin_y = 7
altura = 5 ; ancho = 40
ganar = maldiciones . newwin ( alto , ancho , inicio_y ,
inicio_x )
Tenga en cuenta que el sistema de coordenadas utilizado en curses es
inusual. Las coordenadas siempre se pasan en el orden y, x , y la esquina
superior izquierda de una ventana es la coordenada (0,0). Esto rompe la
convención normal para manejar las coordenadas donde la coordenada xviene
primero. Esta es una diferencia desafortunada de la mayoría de las otras
aplicaciones informáticas, pero ha sido parte de las maldiciones desde que se
escribió por primera vez, y es demasiado tarde para cambiar las cosas ahora.
Su aplicación puede determinar el tamaño de la pantalla utilizando
las variables curses.LINES y curses.COLS para obtener
los tamaños y y x . Las coordenadas legales se extenderán
de (0,0) a (curses.LINES - 1, curses.COLS - 1) .
Cuando llama a un método para mostrar o borrar texto, el efecto no se muestra
inmediatamente en la pantalla. En su lugar, debe llamar al método refresh
() de los objetos de ventana para actualizar la pantalla.
Esto se debe a que las maldiciones se escribieron originalmente con
conexiones lentas de terminales de 300 baudios en mente; con estos
terminales, minimizar el tiempo requerido para volver a dibujar la pantalla fue
muy importante. En su lugar, las cursas acumulan cambios en la pantalla y los
muestran de la manera más eficiente cuando llama a refresh () . Por
ejemplo, si su programa muestra algo de texto en una ventana y luego borra la
ventana, no hay necesidad de enviar el texto original porque nunca están
visibles.
En la práctica, dictar maldiciones explícitamente para volver a dibujar una
ventana no complica mucho la programación con maldiciones. La mayoría de
los programas entran en una serie de actividades y luego se detienen para
esperar una pulsación de tecla o alguna otra acción por parte del usuario. Todo
lo que tiene que hacer es asegurarse de que la pantalla se haya redibujado
antes de pausar para esperar la entrada del usuario, llamando primero
a stdscr.refresh () o al método refresh () de alguna otra ventana
relevante.
Un pad es un caso especial de una ventana; puede ser más grande que la
pantalla de visualización real, y solo se muestra una parte del pad a la vez. La
creación de un pad requiere la altura y el ancho del pad, mientras que la
actualización de un pad requiere las coordenadas del área en pantalla donde
se mostrará una subsección del pad.
pad = curses . newpad ( 100 , 100 )
# Estos bucles llenan el teclado con letras; addch () se
# explica en la sección siguiente
para y en el rango ( 0 , 99 ):
para x en el rango ( 0 , 99 ):
pad . addch ( y , x , ord ( 'a' ) + ( x * x + y *
y ) % 26 )
# Muestra una sección del teclado en el centro de la
pantalla.
# (0,0): coordenada de la esquina superior izquierda del área
de relleno para mostrar.
# (5,5): coordenada de la esquina superior izquierda del área
de la ventana a rellenar
# con contenido de relleno.
# (20, 75): coordenada de la esquina inferior derecha del
área de la ventana para ser
#: rellenada con el contenido del teclado.
almohadilla . actualizar ( 0 , 0 , 5 , 5 , 20 , 75 )
La llamada refresh () muestra una sección del teclado en el rectángulo que
se extiende desde la coordenada (5,5) a la coordenada (20,75) en la
pantalla; la esquina superior izquierda de la sección mostrada es la coordenada
(0,0) en el pad. Más allá de esa diferencia, las almohadillas son exactamente
como las ventanas normales y admiten los mismos métodos.
Si tiene varias ventanas y almohadillas en la pantalla, existe una manera más
eficiente de actualizar la pantalla y evitar el parpadeo molesto de la pantalla a
medida que se actualiza cada parte de la pantalla. refresh () en realidad
hace dos cosas:
1. Llama al método noutrefresh () de cada ventana para actualizar una
estructura de datos subyacente que representa el estado deseado de la
pantalla.
2. Llama a la función doupdate () para cambiar la pantalla física para
que coincida con el estado deseado registrado en la estructura de datos.
En su lugar, puede llamar a noutrefresh () en una serie de ventanas para
actualizar la estructura de datos, y luego llamar a doupdate () para actualizar
la pantalla.
Visualización de texto
Desde el punto de vista de un programador en C, las maldiciones a veces
pueden parecer un laberinto de funciones, todas sutilmente diferentes. Por
ejemplo, addstr () muestra una cadena en la ubicación actual del cursor en
la ventana stdscr , mientras que mvaddstr () se mueve a una coordenada
y, x antes de mostrar la cadena. waddstr () es como addstr () , pero
permite especificar una ventana para usar en lugar de usar stdscr de forma
predeterminada. mvwaddstr () permite especificar una ventana y una
coordenada.
Afortunadamente, la interfaz de Python oculta todos estos detalles. stdscr es
un objeto de ventana como cualquier otro, y los métodos como addstr
() aceptan múltiples formas de argumento. Generalmente hay cuatro formas
diferentes.
Formar Descripción
str o ch Mostrar la cadena str o carácter ch en la posición actual
Mostrar la cadena str o carácter ch , utilizando el
str o ch , attr
atributo attr en la posición actual
Muévase a la posición y, x dentro de la ventana y
y , x , str o ch
visualice str o ch
y , x , str o ch , att Mueva a la posición y, x dentro de la ventana y
r visualice str o ch , usando el atributoattr
Los atributos permiten mostrar texto en formas resaltadas como negrita,
subrayado, código inverso o en color. Se explicarán con más detalle en la
siguiente subsección.
El método addstr () toma una cadena de Python o un bytestring como el
valor que se mostrará. El contenido de las circunvalaciones se envía al terminal
como está. Las cadenas se codifican en bytes utilizando el valor del atributo
de codificación de la ventana ; este es el valor predeterminado de la
codificación predeterminada del sistema que
devuelve locale.getpreferredencoding () .
Los métodos addch () toman un carácter, que puede ser una cadena de
longitud 1, un bytestring de longitud 1 o un entero.
Se proporcionan constantes para caracteres de extensión; estas constantes
son enteros mayores de 255. Por ejemplo, ACS_PLMINUS es un símbolo +/-,
y ACS_ULCORNER es la esquina superior izquierda de un cuadro (útil para
dibujar bordes). También puede utilizar el carácter Unicode apropiado.
Windows recuerda dónde se dejó el cursor después de la última operación, por
lo que si deja de lado las coordenadas y, x , la cadena o el carácter se
mostrarán allí donde se detuvo la última operación. También puede mover el
cursor con el método de mover (y, x) . Debido a que algunos terminales
siempre muestran un cursor parpadeante, es posible que desee asegurarse de
que el cursor esté colocado en algún lugar donde no distraiga; puede ser
confuso tener el cursor parpadeando en alguna ubicación aparentemente
aleatoria.
Si su aplicación no necesita un cursor parpadeante, puede llamar a curs_set
(Falso) para hacerlo invisible. Para la compatibilidad con versiones anteriores
de curses, hay una función leaveok (bool) que es un sinónimo
de curs_set () . Cuando bool es verdadero, la biblioteca de curses intentará
suprimir el cursor parpadeante y no tendrá que preocuparse por dejarlo en
ubicaciones impares.
Atributos y Color
Los personajes se pueden mostrar de diferentes maneras. Las líneas de estado
en una aplicación basada en texto se muestran comúnmente en video inverso,
o un visor de texto puede necesitar resaltar ciertas palabras. curses admite
esto al permitirle especificar un atributo para cada celda en la pantalla.
Un atributo es un entero, cada bit representa un atributo diferente. Puede
intentar mostrar texto con varios bits de atributo establecidos, pero las cursas
no garantizan que todas las combinaciones posibles estén disponibles, o que
todas sean visualmente distintas. Eso depende de la capacidad del terminal
que se use, por lo que es más seguro atenerse a los atributos más
comúnmente disponibles, que se enumeran aquí.
Atributo Descripción
A_BLINK Texto parpadeante
A_BOLD Texto extra brillante o en negrita
A_DIM Medio texto brillante
A_REVERSE Texto de video inverso
A_STANDOUT El mejor modo de resaltado disponible.
A_UNDERLINE Texto subrayado
Entonces, para mostrar una línea de estado de video inverso en la línea
superior de la pantalla, podría codificar:
stdscr . addstr ( 0 , 0 , "Modo actual: Modo de escritura "
,
curses . A_REVERSE )
stdscr . actualizar ()
La biblioteca de curses también admite el color en los terminales que lo
proporcionan. El terminal más común es probablemente la consola de Linux,
seguida de xterms de color.
Para usar el color, debe llamar a la función start_color () poco después de
llamar a initscr () , para inicializar el conjunto de colores predeterminado
(la función curses.wrapper () lo hace automáticamente). Una vez hecho
esto, la función has_colors () devuelve VERDADERO si el terminal en uso
realmente puede mostrar color. (Nota: curses usa el "color" de la ortografía
estadounidense, en lugar del "color" de la ortografía canadiense / británica. Si
está acostumbrado a la ortografía británica, tendrá que resignarse a escribirla
mal por el bien de estas funciones. )
La biblioteca de cursas mantiene un número finito de pares de colores, que
contienen un color de primer plano (o texto) y un color de fondo. Puede obtener
el valor del atributo correspondiente a un par de colores con la
función color_pair () ; esto puede ser O-bit con otros atributos
comoA_REVERSE , pero nuevamente, no se garantiza que tales combinaciones
funcionen en todos los terminales.
Un ejemplo, que muestra una línea de texto usando el par de colores 1:
stdscr . addstr ( "Pretty text" , curses . color_pair ( 1 ))
stdscr . actualizar ()
Como dije antes, un par de colores consiste en un color de primer plano y de
fondo. La función init_pair (n, f, b) cambia la definición del par de
colores n , al color de fondo f y al color de fondo b. El par de colores 0 está
cableado a blanco sobre negro y no se puede cambiar.
Los colores están numerados y start_color () inicializa 8 colores básicos
cuando activa el modo de color. Son: 0: negro, 1: rojo, 2: verde, 3: amarillo, 4:
azul, 5: magenta, 6: cian y 7: blanco. El módulo de curses define constantes
con nombre para cada uno de estos
colores:curses.COLOR_BLACK , curses.COLOR_RED , etc.
Vamos a poner todo esto juntos. Para cambiar el color 1 al texto rojo sobre un
fondo blanco, debe llamar a:
maldiciones . init_pair ( 1 , curses . COLOR_RED , curses .
COLOR_WHITE )
Cuando cambia un par de colores, cualquier texto que ya se muestre usando
ese par de colores cambiará a los nuevos colores. También puede mostrar
nuevo texto en este color con:
stdscr . addstr ( 0 , 0 , "¡ALERTA ROJA!" , curses .
color_pair ( 1 ))
Los terminales muy sofisticados pueden cambiar las definiciones de los colores
reales a un valor RGB dado. Esto le permite cambiar el color 1, que
generalmente es rojo, a púrpura o azul o cualquier otro color que
desee. Desafortunadamente, la consola de Linux no admite esto, por lo que no
puedo probarlo y no puedo proporcionar ningún ejemplo. Puede verificar si su
terminal puede hacer esto llamando a can_change_color () , que
devuelve True si la capacidad está allí. Si tiene la suerte de tener un terminal
tan talentoso, consulte las páginas de manual de su sistema para obtener más
información.
Entrada de usuario
La biblioteca C curses ofrece solo mecanismos de entrada muy
simples. El módulo curses de Python agrega un widget básico de entrada de
texto. (Otras bibliotecas como Urwid tienen colecciones más extensas de
widgets).
Hay dos métodos para obtener información desde una ventana:
getch () actualiza la pantalla y luego espera a que el usuario presione
una tecla, mostrando la tecla sise ha llamado a echo
() anteriormente. Opcionalmente, puede especificar una coordenada a
la que se debe mover el cursor antes de hacer una pausa.
getkey () hace lo mismo pero convierte el entero en una cadena. Los
caracteres individuales se devuelven como cadenas de 1 carácter, y las
teclas especiales como teclas de función devuelven cadenas más largas
que contienen un nombre clave, tales como KEY_UP o ^ G .
Es posible no esperar a que el usuario utilice el método de ventana nodelay
() . Después de nodelay (True) , getch () y getkey () para la ventana
se vuelven no bloqueantes. Para indicar que no hay entrada lista, getch
() devuelve curses.ERR (un valor de -1) y getkey () genera una
excepción. También hay una función halfdelay () , que se puede usar para
(en efecto) configurar un temporizador en cada getch () ; Si no hay entrada
disponible dentro de un retraso específico (medido en décimas de segundo),
las cursas generan una excepción.
El método getch () devuelve un entero; si está entre 0 y 255, representa el
código ASCII de la tecla presionada. Los valores superiores a 255 son teclas
especiales como Re Pág, Inicio o las teclas de cursor. Puede comparar el valor
devuelto a constantes
como curses.KEY_PPAGE ,curses.KEY_HOME o curses.KEY_LEFT . El bucle
principal de su programa puede verse algo como esto:
mientras True :
c = stdscr . getch ()
si c == ord ( 'p' ):
PrintDocument ()
elif c == ord ( 'q' ):
break # Salga del bucle while
elif c == curses . KEY_HOME :
x = y = 0
El módulo curses.ascii proporciona funciones de pertenencia a clase ASCII
que toman argumentos de cadena de 1 carácter o enteros; Estos pueden ser
útiles para escribir pruebas más legibles para tales bucles. También
proporciona funciones de conversión que toman argumentos de cadena de 1
carácter o entero y devuelven el mismo tipo. Por
ejemplo, curses.ascii.ctrl () devuelve el carácter de control
correspondiente a su argumento.
También hay un método para recuperar una cadena completa, getstr () . No
se usa muy a menudo, porque su funcionalidad es bastante limitada; las únicas
teclas de edición disponibles son la tecla de retroceso y la tecla Intro, que
termina la cadena. Opcionalmente, puede limitarse a un número fijo de
caracteres.
maldiciones . echo () # Habilita el eco de los
caracteres
# Obtenga una cadena de 15 caracteres, con el cursor en la
línea superior
s = stdscr . getstr ( 0 , 0 , 15 )
El módulo curses.textpad proporciona un cuadro de texto que admite un
conjunto de combinaciones de teclas similares a Emacs. Varios métodos de
la clase Textbox admiten la edición con validación de entrada y la recopilación
de los resultados de edición con o sin espacios finales. Aquí hay un ejemplo:
importar curses
desde curses.textpad importar Cuadro de texto ,
rectángulo
def principal ( stdscr ):
stdscr . addstr ( 0 , 0 , "Ingresar mensaje de IM:
(presiona Ctrl-G para enviar)" )
editwin = curses . newwin ( 5 , 30 , 2 , 1 )
rectángulo ( stdscr , 1 , 0 , 1 + 5 + 1 , 1 + 30 + 1 )
stdscr . actualizar ()
box = Textbox ( editwin )
# Deje que el usuario edite hasta que se presione Ctrl-G.
cuadro . editar ()
# Obtener
mensaje de contenido resultante = cuadro . reunir ()
Consulte la documentación de la biblioteca en curses.textpad para obtener
más detalles.
Para más información
Este CÓMO no cubre algunos temas avanzados, como leer el contenido de la
pantalla o capturar eventos del mouse desde una instancia de xterm, pero la
página de la biblioteca de Python para el módulo de curses ahora está
razonablemente completa. Deberías buscarlo a continuación.
Si tiene alguna duda sobre el comportamiento detallado de las funciones de
curses, consulte las páginas del manual para la implementación de sus curses,
ya sea si se trata de un cursillo o de un proveedor propietario de Unix. Las
páginas del manual documentarán cualquier peculiaridad y proporcionarán
listas completas de todas las funciones, atributos y caracteres ACS_
* disponibles para usted.
Debido a que la API de curses es tan grande, algunas funciones no son
compatibles con la interfaz de Python. A menudo esto no es porque sean
difíciles de implementar, sino porque nadie los ha necesitado todavía. Además,
Python aún no admite la biblioteca de menús asociada con ncurses. Los
parches que agreguen soporte para estos serían bienvenidos; consulte la Guía
del desarrollador de Python para obtener más información sobre cómo enviar
parches a Python.
Escribir programas con NCURSES : un largo tutorial para
programadores en C
La página de manual de ncurses
Las preguntas frecuentes de ncurses
"Use curses ... no jurar" : video de una charla de PyCon 2013 sobre el
control de terminales que usan curses o Urwid.
"Aplicaciones de consola con Urwid" : video de una charla de PyCon CA
2012 que muestra algunas aplicaciones escritas usando Urwid.