lunes, 21 de mayo de 2012

Efectos de consola, parte 3

Una escena maritima

En nuestro afan por sacarle el jugo a la cónsola (dejaremos el tema de las interfaces gráficas para luego), en esta entrada ilustraremos cómo crear animaciones simples y agregar color al texto en la cónsola.  Por ejemplo, la escena marítima que ven a continuación fue hecha exclusivamente usando la cónsola de texto:


Por cierto, el script que realiza esta animación fué realizado por Leober Urbano y Daniel Vasquez, ambos estudiantes de mi curso de Python en la UNEFA este semestre (2012-1).  Pero antes de darles el código (con el permiso de ellos), quiero presentarles las secuencias de escape ANSI.

Secuencias de escape ANSI - Un poco de historia

Mucho antes de que existieran los entornos de interfáz gráfica (con ventanitas, ratones, iconos y todas esas cosas a las cuales ustedes se han (mal)acostumbrado), de hecho, antes de que existieran las primeras computadoras personales, la típica configuración computacional era una computadora mainframe con un sistema operativo que permitía el time-sharing y con el cual los usuarios en una sala de computación interactuaban mediante unos terminales de texto, como por ejemplo el famoso VT-100 que ven a continuación (pueden buscar el artículo en la Wikipedia):

Una cónsola VT-100.
Nótese el uso de la palabra cónsola,
aqui bajo su acepción original,
 que se refería a todo el dispositivo
de entrada-salida (pantalla y teclado).

Estos terminales nos parecerian increiblemente limitados hoy en día.  La información era visualizada como caracteres ASCII  y algo llamado "secuencias de escape ANSI" que permitía visualizar estos caracteres con otros atributos (por ejemplo, resaltados, subrayados, parpadeando, etc.).  Además, las secuencias de escape ANSI permitían (y permiten todavía como vamos a ver) controlar en cuál parte de la pantalla se visualizaba la información. Las secuencias de escape ANSI pronto se conviertieron en un estándar para todas las consolas, igual que los caracteres ASCII.

Aunque ustedes no me crean, yo llegué a usar estas perolas cuando estudiaba computación en la Universidad Central de Venezuela (a principio de los 90's).  Las consolas en cuestión estaban conectadas a una Burroughs B6700, a la cual nosotros en la Escuela de Computación nos referiamos, no tan cariñosamente, como la "Burro".

Secuencias de escape ANSI - ¿que son?

Todo comando para lograr los "efectos especiales" como los que se ven en la animación de arriba comienza por imprimir (mediante una sentencia print en Python) un iniciador de secuencia de control, o CSI por sus siglas en inglés.  Existen CSI's de un caractér, como por ejemplo los que emitimos para dar un salto de línea, una tabulación horizontal o vertical, mandar un beep al terminal.  Estos CSI's de un caracter fueron expuestos en la primera parte de esta serie de entradas sobre efectos de la cónsola.  Ahora, los que nos interesan son los comandos que podemos emitir con el CSI de dos caracteres: ESC+[ (un caracter de escape, código ASCII 27 en decimal, más el caracter de corchete cuadrado izquierdo, código ASCII 91 en decimal).  Por ser el primer caracter el ESCape, las secuencias de control que comienzan con este CSI se conocen como secuencias de escape ANSI.  Despues del CSI, los otros caracteres que se imprimen a continuación indican más información sobre la secuencia de control.  A continuación una lista no exhaustiva:

Comandos ANSI para posicionamiento de cursor, desplazamiento o borrado


Comando
(Secuencia de caracteres)
Descripción
CSI n A Mueve el cursor n caracteres (por defecto n=1) hacia arriba.
CSI n B Mueve el cursor n caracteres (por defecto n=1) hacia abajo.
CSI n C Mueve el cursor n caracteres (por defecto n=1) adelante (hacia la derecha).
CSI n D Mueve el cursor n caracteres (por defecto n=1) atrás (hacia la izquierda).
CSI n E Mueve el cursor al principio de la línea y n líneas (por defecto n=1) hacia abajo.
CSI n F Mueve el cursor al principio de la línea y n líneas (por defecto n=1) hacia arriba.
CSI n G Mueve el cursor a la columna n.
CSI n;m H Mueve el cursor a la posición indicada por la fila n y la columna m (por defecto n y m son iguales a 1). La posición (1,1) es la esquina superior izquierda de la ventana.
CSI n J Borra parte de la pantalla. Si n=0 (valor por defecto), limpia la pantalla desde la posición del cursor hasta el final. Si n=1, limpia la pantalla desde el cursor hasta el principio de la pantalla. Si n=2, limpia toda la pantalla.
CSI n K Borra parte de la línea. Si n=0 (valor por defecto), limpia desde la posición del cursor hasta el final de la línea. Si n=1, limpia desde el cursor hasta el principio de la línea. Si n=2, limpia toda la línea. La posición del cursor no varía.
CSI n S Desplaza toda la pantalla n líneas hacia arriba (por defecto n=1) insertando las n líneas desde abajo.
CSI n T Desplaza toda la pantalla n líneas hacia abajo (por defecto n=1) insertando las n líneas desde arriba.
CSI 6 n Captura la posición actual del cursor como si el usuario la hubiese ingresado por teclado. Se devuelve la siguiente secuencia de caracteres: ESC[n;mR , donde n y m son los números de fila y columna respectivamente. Notese que en esta secuencia de escape (CSI 6 n), la n representa una n minúscula y no un número pasado como parámetro.
CSI s Guarda la posición del cursor.
CSI u Restaura la posición del cursor a la posición guardada por CSI s.
CSI ?25l Esconde el cursor.
CSI ?25h Vuelve a visualizar el cursor.

En las secuencias de escape antecedentes, las letras n y m en cursiva representan parametros numericos que se pasan al interprete ANSI.  Así por ejemplo, la secuencia ESC[2;40H ubicaría la posición del cursor en la fila 2, columna 40.

Existe una secuencia de escape especial para modificar los atributos de los caracteres enviados a la pantalla.  Es la secuencia de escape especial de rendición gráfica (SGR por sus siglas en inglés), que se corresponde a

CSI n;[k] m

En lo anterior, n y k son parámetros númericos, k es un parámetro opcional.  Nótese que la secuencia de escape termina en m minuscula.  A continuación una descripción de los atributos modificables mediante el o los parámetros n y k:

Comandos de rendición gráfica


Parámetro Descripción
0 Restaura todos los atributos de rendición grafica a los valores por defecto (reset).
1 Coloca texto en negrillas o alta intensidad.
4 Coloca texto subrayado.
5 Coloca texto en parpadeo lento.
6 Coloca texto en parpadeo rápido.
7 Invierte (intercambia) los colores de fondo y de primer plano.
21 Desactiva las letras en negrilla y visualiza el texto en intensidad normal.
24 Desactiva el subrayado.
25 Desactiva el parpadeo.
30-37 Fija el color del texto (primer plano) en baja intensidad. El segundo dígito despues del 3 indica el color. Ver tabla anexa abajo.
40-47 Fija el color de fondo en baja intensidad.El segundo dígito despues del 4 indica el color.Ver tabla anexa abajo.
90-97 Fija el color del texto (primer plano) en baja intensidad.El segundo dígito despues del 9 indica el color. Ver tabla anexa abajo.
100-107 Fija el color de fondo en baja intensidad.El segundo dígito despues del 10 indica el color. Ver tabla anexa abajo.

Tabla de colores


Intensidad 0 1 2 3 4 5 6 7
Baja Negro Rojo Verde Amarillo Azul Magenta Cyan Gris claro
Alta Gris oscuro Rojo Verde Amarillo Azul Magenta Cyan Blanco

Haciendo uso  ingenioso de estas secuencias de escape y de algunos caracteres especiales unicode, Leober y Daniel hicieron su script para realizar una pequeña animación de un barco navegando:

import time
velas1= " "*5+u"\u2571"+u"\u23AA"+" "+u"\u23AA"+u"\u2572"
velas2= " "*4+u"\u2571"+" "+u"\u23AA"+" "+u"\u23AA"+" "+u"\u2572"
velas3= " "*3+u"\u2571"+"__"+u"\u23AA"+" "+u"\u23AA"+"__"+u"\u2572"
mastiles= " "*6+u"\u23AA"+" "+u"\u23AA"
timonel= " "*2+u"\u2599"+u"\u2588"*10+u"\u259f"
print "\n"*5
print "\033[11;0H"+"\033[0;34;44m"+u"\u2588"*80+"\033[0m"
for i in range(20):
 v=i+1
 print "\033[6;%dH"%v+velas1
 print "\033[7;%dH"%v+velas2
 print "\033[8;%dH"%v+velas3
 print "\033[9;%dH"%v+mastiles
 print "\033[10;%dH"%v+timonel
 time.sleep(0.5)
while i>0:
 v=i+1
 print "\033[6;%dH"%v+velas1+"\033[0K"
 print "\033[7;%dH"%v+velas2+"\033[0K"
 print "\033[8;%dH"%v+velas3+"\033[0K"
 print "\033[9;%dH"%v+mastiles+"\033[0K"
 print "\033[10;%dH"%v+timonel+"\033[0K"
 i=i-1
 time.sleep(0.5)

Referencias

  1. Wikipedia (2012). "ANSI escape code".  Artículo de Wikipedia disponible en: http://en.wikipedia.org/wiki/ANSI_escape_code. Nota: existe una versión en castellano de este artículo pero no es tan completa como la versión citada.
  2. Wikipedia (2012). "VT100". Artículo de Wikipedia disponible en: http://es.wikipedia.org/wiki/VT-100.
  3. s/a (2001). "Escuela de Computación|UCV|historia". Página web disponible en: http://www.ciens.ucv.ve/escueladecomputacion/inicio/historia
  4. s/a (2008). "Print in terminal using colors with python?". Pregunta en foro disponible en: http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python.

5 comentarios:

  1. Gracias Prof. Pero esto tambien fue realizado en colaboracion con el compañero Bachiller Daniel Vasquez. Asi que meritos para El tambien.

    ResponderEliminar
  2. Por cierto, si ustedes u otros compañeros del curso tienen más animaciones ANSI, me gustaría me contactaran para publicarlas en otra entrada del blog.

    ¿Quien se anima a hacer un video juego usando puro texto y efectos de cónsola?

    ResponderEliminar
    Respuestas
    1. ahi estan muchas:
      http://artscene.textfiles.com/vt100/

      Eliminar
  3. Si pero esos eran mainframes, y tienen su nicho de mercado.

    ResponderEliminar