Este sitio web usa cookies de terceros para analizar el tráfico y personalizar los anuncios. Si no está de acuerdo, abandone el sitio y no siga navegando por él. ×


Curso de Pascal. Tema 17: El entorno Turbo Vision.

Turbo Vision es un entorno, el mismo que usa el IDE (entorno de desarrollo) de Turbo Pascal 6.0 y 7.0.  Este entorno permite crear menús desplegables, ventanas de diálogo, visualizadores de listas y de textos, botones de elección múltiple, etc.  Podemos manejarlo con el teclado o con el ratón.

(Para FreePascal, existe un "clon" de TurboVision, llamado FreeVision, de modo que estos fuentes deberían funcionar casi sin cambios. Hoy en día parece poco razonable usar FreePascal para crear "entornos avanzados en modo texto", existiendo Lazarus que permite crear entornos similares en modo gráfico, pero conservaremos este apartado por completitud y por si alguien quiere crear algo de este estilo para "resucitar" un ordenador basado en MsDos)

Cualquiera que haya manejado cualquiera de estas dos versiones de Turbo Pascal habrá visto que resulta bastante cómodo.  Además, se incluyen las unidades necesarias para crear entornos como este.  En eso nos vamos a centrar en esta lección.

No pretendo contarlo con toda la profundidad que sería deseable.  Como siempre, prefiero despertar la curiosidad en aquellos que no lo conozcais, y si os interesa teneis un montón de ejemplos con Turbo Pascal, con un nivel de dificultad creciente.  Estos ejemplos vienen además explicados en la "Guía de Referencia de Turbo Vision", un libro de ¡618 páginas! (en el caso de TP7.0 en inglés).  Y si no se os da bien el inglés, teneis muchos libros traducidos en los que podreis aprender muchísimo, o bien preguntar aquí las dudas que os vayan surgiendo.

Además, ya vereis que el tema en sí no es sencillo.  Si no entendeis algo, probad a seguir leyendo a ver si algún párrafo posterior lo deja claro, y si no, experimentad con el compilador o preguntad...
 

Vamos allá... El esqueleto de una aplicación básica se encuentra en la unidad "App".  En ella tenemos definida la clase (objeto) TApplication, que vamos a usar para nuestro primer ejemplo.  En éste sólo usaremos tres de los métodos: Init (el constructor), Done (el destructor) y Run (el cuerpo de la aplicación en sí).

{--------------------------} 
{  Ejemplo en Pascal:      } 
{                          } 
{    Turbo Vision - 1      } 
{    TV1.PAS               } 
{                          } 
{  Este fuente procede de  } 
{  CUPAS, curso de Pascal  } 
{  por Nacho Cabanes       } 
{                          } 
{  Comprobado con:         } 
{    - Turbo Pascal 7.0    } 
{    - Free Pascal 2.2.2w  } 
{--------------------------}

program TV1;
uses App;

var 
  Prog: TApplication;
  
begin 
  Prog.Init; 
  Prog.Run; 
  Prog.Done; 
end.

Con esta pequeñez ya nos aparece una pantalla parcialmente sombreada, por la que podemos pasear el ratón a nuestro gusto, y un letrero inferior que dice "Alt-X Exit".  Pues sí, funciona: si pulsamos Alt-X o pinchamos encima de ese letrero con el ratón, sale (exit) del programa.

Si usamos FreePascal, la apariencia de la pantalla será muy similar, con la diferencia de que, si no indicamos lo contrario, se tomará la reslución de pantalla más alta posible, de modo que tendremos 50 líneas de texto en pantalla:

Y estamos en OOP, así que no sólo podemos heredar esta aplicación base, sino que además podemos modificarla y ampliarla...

 

Vamos a empezar por modificar esa línea inferior para que al menos aparezca en español... ;-)

Esta línea se llama "línea de estado" (en inglés Status Line) y sus características se fijan con el método "InitStatusLine".  Ahora nuestro programa quedaría:

 {--------------------------} 
 {  Ejemplo en Pascal:      } 
 {                          } 
 {    Turbo Vision - 2      } 
 {    TV2.PAS               } 
 {                          } 
 {  Este fuente procede de  } 
 {  CUPAS, curso de Pascal  } 
 {  por Nacho Cabanes       } 
 {                          } 
 {  Comprobado con:         } 
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    } 
 {--------------------------}
 program TV2;
 uses App, Objects, Menus, Drivers, Views;
 const 
   cmAyuda = 100;   { Una orden que vamos a crear }
 type Programa = object (TApplication) 
     procedure InitStatusLine; virtual; 
   end;
 procedure Programa.InitStatusLine; 
 var 
   R: TRect;               { Rectángulo de pantalla } 
 begin 
   GetExtent(R);           { Miramos cuando ocupa } 
   R.A.Y := R.B.Y - 1;     { Nos quedamos la línea inferior } 
   New(StatusLine, Init(R, 
     NewStatusDef(0, $FFFF, 
       NewStatusKey('~Alt-S~ Salir', kbAltS, cmQuit, 
       NewStatusKey('~F1~ Ayuda', kbF1, cmAyuda, 
       nil)), 
     nil))); 
 end;
 var Prog: Programa; 
 begin 
   Prog.Init; 
   Prog.Run; 
   Prog.Done; 
 end. 


 

Esto es lo que obtendremos:

Comentarios generales:

InitStatusLine es un método virtual (espero que recordeis lo que era eso, de los temás de OOP), luego al heredarlo deberá seguir siéndolo.

TRect es el tipo "rectángulo" que usaremos para trabajar con partes de la pantalla.  Está definido en la unidad Objects.  Así, empezamos por ver el tamaño de la pantalla con GetExtent y lo guardamos en el rectángulo R.  Luego modificamos este tamaño para que pase a ser el que nos interesa: queremos que la línea de estado sea la inferior, lo que hace orden

    R.A.Y := R.B.Y - 1;

en la que A es la posición superior izquierda de la pantalla y B es la inferior derecha, cada una con sus coordenadas X e Y.  Por tanto, lo que hace esta orden es que la coordenada Y de la parte superior de nuestro rectángulo sea un unidad menor que la de la parte inferior.

Por tanto, si nuestra pantalla terminaba en la línea 25, ahora nuestra línea de estado termina en la línea 25 y empieza en la 24.  Esto no es del todo exacto, pero me niego a darle más vueltas porque esto es "lo fácil", así que quedaos simplemente con la idea de que estamos reservando una única línea para nuestra StatusLine y que es la inferior.

Después creamos la línea de estado, usando la sintaxis extendida de "new" que vimos el último día de OOP.  Daos cuenta de como vamos encadenando las opciones: cada una aparece como último parámetro de la anterior, y terminamos con "nil".

Al crear la StatusLine le indicamos el rectángulo en el que queremos que aparezca (R) y la definición en sí (StatusDef, en la unidad Menus), que son los rangos de ayuda (desde 0 hasta $FFFF) y cada una de las opciones.

¿Rangos de ayuda?  Sí, supongo que os habreis fijado que el IDE de TP 6 y 7 tiene una línea inferior de estado, y que se convierte en "ayuda" cuando os paseais por el menú superior.  Eso se hace con esos "rangos de ayuda". En nuestro caso, siempre (desde 0 hasta $FFFF=65535) van a aparecer las mismas opciones abajo.

Y en estas opciones indicamos: el texto con una parte realzada, que se marca entre dos caracteres ASCII 126 (la tilde que va sobre la "ñ"), la tecla a la que queremos que responda (constantes cuyo nombre, por convenio, empieza por "kb" y que están definidas en la unidad Drivers), y el comando que queremos que ejecute (constantes también, pero que ahora empiezan por "cm" y que hemos definido nosotros).

Aun así, a lo mejor os habeis dado cuenta de un "pequeño" problema: hemos "creado" la orden cmAyuda, pero no hemos dicho lo que queremos que haga cuando le demos esas órdenes.  Si ejecutais el programa, os dareis cuenta seguro  }:-)  porque no podreis hacer nada con ella: el teclado no hace nada si pulsais F1 (sí responde a Alt-S, porque el comando cmQuit está predefinido en la unidad Views), y si pulsais con el ratón sobre el letrero "F1 Ayuda" aparece un fondo verde que parece decir "si, ya te oigo, pero ¿qué quieres que haga?".

 



Pues vamos a corregir esto también.  Necesitamos una rutina que "maneje sucesos" (Handle Events, en inglés), a la que le diremos "cuando lo que ocurra sea que te llegue la orden _cmAyuda_, escribe un mensaje que yo te voy a decir..."

 {--------------------------} 
 {  Ejemplo en Pascal:      } 
 {                          } 
 {    Turbo Vision - 3      } 
 {    TV3.PAS               } 
 {                          } 
 {  Este fuente procede de  } 
 {  CUPAS, curso de Pascal  } 
 {  por Nacho Cabanes       } 
 {                          } 
 {  Comprobado con:         } 
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    } 
 {--------------------------}
 program TV3;
 uses App, Objects, Menus, Drivers, Views, MsgBox;
 const 
   cmAyuda = 100;   { Una orden que vamos a crear }
 type Programa = object (TApplication) 
     procedure InitStatusLine; virtual; 
     procedure HandleEvent(var Suceso: TEvent); virtual; 
   end;
 procedure Programa.InitStatusLine; 
 var 
   R: TRect;               { Rectángulo de pantalla } 
 begin 
   GetExtent(R);           { Miramos cuando ocupa } 
   R.A.Y := R.B.Y - 1;     { Nos quedamos la línea inferior } 
   New(StatusLine, Init(R, 
     NewStatusDef(0, $FFFF, 
       NewStatusKey('~Alt-S~ Salir', kbAltS, cmQuit, 
       NewStatusKey('~F1~ Ayuda', kbF1, cmAyuda, 
       nil)), 
     nil))); 
 end;
 procedure Programa.HandleEvent(var Suceso: TEvent); 
 var 
   R: TRect; 
 begin 
   inherited HandleEvent(Suceso);       { Primero que mire el "padre" } 
   if Suceso.What = evCommand then      { Si es una orden } 
     case Suceso.Command of             { Miramos qué orden es } 
       cmAyuda: 
          MessageBox('¡Aún no hay ayuda disponible!', 
            nil, mfWarning or mfOKButton); 
     end; 
 end;
 var Prog: Programa; 
 begin 
   Prog.Init; 
   Prog.Run; 
   Prog.Done; 
 end.

Pues el HandleEvent primero hace lo que haya heredado (y así no nos preocupamos nosotros de órdenes como cmQuit).  Luego mira si se trata de una orden (también podía ser una pulsación de tecla, un movimiento del ratón, etc), y si ese comando es el que nos interesa.

En caso de que sea la solicitud de ayuda escribe una ventana con un mensaje (Message Box), con un cierto texto, otros parámetros opcionales (en este caso ninguno: nil) y unos indicadores (flags: mf es abreviatura de Message Flags): mfWarning para que aparezca el título "Warning" y mfOKButton para que salga el boton OK.  Si queremos traducir alguno de estos mensajes no tenemos más que modificar la unidad MsgBox, o bien crear nuestra propia versión a partir de ella (heredándola).

(De hecho, crearemos nuestro propio MessageBox en el apartado 3 de esta lección).

 



Finalmente, vamos a crear también un menú desplegable en la parte superior de la pantalla (una barra de menú, en inglés Menu Bar):

 {--------------------------} 
 {  Ejemplo en Pascal:      } 
 {                          } 
 {    Turbo Vision - 4      } 
 {    TV4.PAS               } 
 {                          } 
 {  Este fuente procede de  } 
 {  CUPAS, curso de Pascal  } 
 {  por Nacho Cabanes       } 
 {                          } 
 {  Comprobado con:         } 
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    } 
 {--------------------------}
 program TV4;
 uses App, Objects, Menus, Drivers, Views, MsgBox;
 const 
   cmAyuda = 100;   { Una orden que vamos a crear } 
   cmAunNo = 101;   { Y otra }
 type Programa = object (TApplication) 
     procedure InitStatusLine; virtual; 
     procedure InitMenuBar; virtual; 
     procedure HandleEvent(var Suceso: TEvent); virtual; 
   end;
 procedure Programa.InitStatusLine; 
 var 
   R: TRect;               { Rectángulo de pantalla } 
 begin 
   GetExtent(R);           { Miramos cuando ocupa } 
   R.A.Y := R.B.Y - 1;     { Nos quedamos la línea inferior } 
   New(StatusLine, Init(R, 
     NewStatusDef(0, $FFFF, 
       NewStatusKey('~Alt-S~ Salir', kbAltS, cmQuit, 
       NewStatusKey('~F1~ Ayuda', kbF1, cmAyuda, 
       nil)), 
     nil))); 
 end;
 procedure Programa.InitMenuBar; 
 var 
   R: TRect; 
 begin 
   GetExtent(R); 
   R.B.Y := R.A.Y + 1; 
   MenuBar := New(PMenuBar, Init(R, NewMenu( 
     NewSubMenu('~A~rchivo', hcNoContext, NewMenu( 
       NewItem('~A~yuda', 'F1', kbF1, cmAyuda, hcNoContext, 
       NewItem('~S~alir', 'Alt-S', kbAltS, cmQuit, hcNoContext, 
       nil))), 
     NewSubMenu('~E~dición', hcNoContext, NewMenu( 
       NewItem('~P~rimera opción', '', kbnoKey, cmAunNo, hcNoContext, 
       NewItem('~S~egunda opción', '', kbNoKey, cmAunNo, hcNoContext, 
       NewLine( 
       NewItem('Tercera Opcion', 'Alt-T', kbAltT, cmAunNo, hcNoContext, 
       nil))))), 
     nil))))); 
 end;
 procedure Programa.HandleEvent(var Suceso: TEvent); 
 var 
   R: TRect; 
 begin 
   inherited HandleEvent(Suceso);       { Primero que mire el "padre" } 
   if Suceso.What = evCommand then      { Si es una orden } 
     case Suceso.Command of             { Miramos qué orden es } 
       cmAyuda: 
          MessageBox('¡Aún no hay ayuda disponible!', 
            nil, mfWarning or mfOKButton); 
       cmAunNo: 
          MessageBox(#3+'Esta opción tampoco funciona pero al menos' 
            +' está centrada en la ventana.  ;-)', 
            nil, mfInformation or mfOkCancel); 
     end; 
 end;
 var Prog: Programa; 
 begin 
   Prog.Init; 
   Prog.Run; 
   Prog.Done; 
 end.

Hay pocas cosas nuevas que comentar, porque el método InitMenuBar recuerda mucho al InitStatusLine, con la diferencia de que ahora hay SubMenús y elementos (Items), y que pueden tener un código de ayuda sensible al contexto cada uno de ellos (en este caso no se ha usado: todos son hcNoContext).

Un detalle que a lo mejor os ha extrañado es eso del "#3" que aparece delante del segundo mensaje.  Vayamos por partes: escribir #3 es lo mismo que escribir chr(3).  ¿Y por qué chr(3)?  Pues es una cosa curiosa ;-) ... El carácter 3 es el que se devuelve al pulsar Ctrl+C en el teclado.  Así que si un texto empieza por Ctrl+C, Turbo Vision considera que se trata del código de control de centrado, e intenta colocarlo en el centro de la ventana activa.  Este "truquito" sirve también en otras ventanas, no sólo en MessageBox.

Por cierto, estos programas están comprobados con Turbo Pascal 7.0. Con la versión 6.0 deberían funcionar, con la única diferencia de que no existe la palabra clave "inherited", luego habría que cambiar

  inherited HandleEvent;

por

  TApplication.HandleEvent;