ff-multiconverter, multimedia, audio, video, linux, ubuntu ubuntu kylin, china, releases, linux terminal, ubuntu, linux, comandos, shell conky, gadgets, ubuntu, linux SpeedTest-Cli, velocidad, red, consola, terminal tag foto 6 pinta, grafica, linux, ubuntu djl, juegos, yum, synaptic, paquetes ubuntu, releases, canonical psensor, ubuntu, linux, sistema, monitor

Ncurses, una librería para usar múltiples ventanas en Linux, segunda parte

ncurses Recuadros Alrededor de Ventanas
Puede crear recuadros alrededor de las ventanas para darle un aspecto más agradable a su programa. Hay una macro en la librería llamado box que lo hace por usted. Al contrario que otras funciones wbox no existe, box toma como argumento un puntero de tipo WINDOW.
Puede encontrar fácilmente más detalles de box en las páginas del man. Aunque hay algo más que debería ser mencionado. Ponerle un recuadro  a una ventana simplemente consiste en escribir los caracteres necesarios en la matriz de caracteres de la ventana que corresponden con las posiciones de los bordes. Si mas adelante por algún motivo escribe en estas posiciones, el recuadro puede corromperse. Para evitar esto, puede crear una ventana dentro de la ventana original con subwin, poner el [recuadro] en la ventana original y utilizar la ventana interior para escribir.
Teclas de Función
Para poder usar las teclas de función, el indicador _use_keypad tiene que ser activado en la ventana desde la que se va a leer la entrada de teclado. keypad es la función que establece el valor de _use_keypad. Una vez activado _use_keypad, puede tomar la entrada de teclado de la forma habitual con las funciones de entrada.
En este caso, si por ejemplo utiliza getch para obtener datos, debe tener cuidado en almacenar los datos en una variable de tipo entero en lugar de una de tipo char. Esto es porque los valores numéricos de las teclas de función son mayores que los valores que puede contener una variable de tipo char. No necesita conocer el valor numérico de las teclas de función ya que la librería proporciona nombres ya definidos para ellas. Estos nombres están listados en la página man de getch.
Un Ejemplo
Vamos a analizar ahora un sencillo y bonito programa. En él los menús van a ser creados usando ncurses y se indicará cuando se selecciones una opción del menú. Un aspecto interesante de este programa es el uso de ventanas ncurses para simular los menús cuando se abren. Lo puede ver en la captura de pantalla de más abajo.
El programa comienza con las cabeceras como es habitual. Luego definimos las constantes para los valores ASCII de las teclas de enter y escape.
#include <curses.h>

#include <stdlib.h>

#define ENTER 10
#define ESCAPE 27


La siguiente función es las primera que se llama al ejecutar el programa. Primero llama a initscr para inicializar curses y luego a start_color para poder utilizar colores. Los pares de colores que se van a utilizar en el programa se definen a continuación. La llamada a curs_set(0) hace que el cursor físico sea invisible. noecho hace que entrada del teclado no aparezca en pantalla. También se puede usar la función noecho para controlar la entrada del teclado y mostrar únicamente las partes que quiera que se muestren. Para quitar los efectos de la función noecho hay que llamar a la función echo. Finalmente se llama a la función keypad para habilitar las teclas de función cuando se lea la entrada de stdscr. Esto es necesario porque en nuestro programa vamos a utilizar las teclas F1, F2 y los cursores.


void init_curses()
{
    initscr();
    start_color();
    init_pair(1,COLOR_WHITE,COLOR_BLUE);
    init_pair(2,COLOR_BLUE,COLOR_WHITE);
    init_pair(3,COLOR_RED,COLOR_WHITE);
    curs_set(0);
    noecho();
    keypad(stdscr,TRUE);
}


La siguiente función crea una barra de menú que aparece en la parte de arriba de la pantalla. Si se fija en dicha función se dará cuenta de que la barra de menú que se ve como una línea sencilla en la parte superior en realidad está definida como una subventana de stdscr con una única línea de alto. Ésta función toma un puntero a esta ventana como parámetro, primero cambia el color de fondo y luego escribe los nombres de los menús. Usamos waddstr para escribir los nombres de los menús. Preste atención a las llamadas a wattron, se usan para escribir con un par de colores diferente (número 3) en lugar de usar el par de colores por defecto (número 2). Recuerde que el par número 2 fue puesto como par por defecto en la primera línea por wbkgd. wattroff es llamado cuando se quiere cambiar al par de color por defecto.


void draw_menubar(WINDOW *menubar)
{
    wbkgd(menubar,COLOR_PAIR(2));
    waddstr(menubar,"Menu1");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F1)");
    wattroff(menubar,COLOR_PAIR(3));
    wmove(menubar,0,20);
    waddstr(menubar,"Menu2");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F2)");
    wattroff(menubar,COLOR_PAIR(3));
}


La siguiente función dibuja los menús cuando las teclas F1 o F2 son presionadas. Para crear el efecto de menú una ventana nueva con el mismo color blanco que la barra de menú es creada sobre la ventana azul que compone el fondo.No queremos que esta nueva ventana sobrescriba los caracteres que hubiera anteriormente en el fondo. Estos deben continuar ahí una vez que el menú sea cerrado. Esto es  por lo que la ventana menú no puede ser creada como una subventana de stdscr. Como verá más abajo, la ventana items[0] es creada con la función newwin mientras que las otras ocho ventanas items son creadas como subventanas de items[0]. Aquí items[0] es usada para dibujar una caja alrededor del menú y las otras ventanas items lo son para mostrar los items seleccionados en el menú y también para no sobreescribir los caracteres de la caja alrededor del menú. Para hacer que un item del menú parezca que está seleccionado es suficiente con hacer que el color de fondo sea diferente al del resto de los items. Esto es lo que hace la antepenúltima línea; el color de fondo del primer item es puesto de un color diferente al de los otros y así cuando el menú aparece, el primer item está seleccionado.






WINDOW **draw_menu(int start_col)
{
    int i;
    WINDOW **items;
    items=(WINDOW **)malloc(9*sizeof(WINDOW *));

    items[0]=newwin(10,19,1,start_col);
    wbkgd(items[0],COLOR_PAIR(2));
    box(items[0],ACS_VLINE,ACS_HLINE);
    items[1]=subwin(items[0],1,17,2,start_col+1);
    items[2]=subwin(items[0],1,17,3,start_col+1);
    items[3]=subwin(items[0],1,17,4,start_col+1);
    items[4]=subwin(items[0],1,17,5,start_col+1);
    items[5]=subwin(items[0],1,17,6,start_col+1);
    items[6]=subwin(items[0],1,17,7,start_col+1);
    items[7]=subwin(items[0],1,17,8,start_col+1);
    items[8]=subwin(items[0],1,17,9,start_col+1);
    for (i=1;i<9;i++)
        wprintw(items[i],"Item%d",i);
    wbkgd(items[1],COLOR_PAIR(1));
    wrefresh(items[0]);
    return items;
}


La función que tenemos a continuación simplemente borra la ventana menú creada por la función anterior. Primero borra las ventanas de los items con delwin y luego libera la memoria asignada para el puntero items.


void delete_menu(WINDOW **items,int count)
{
    int i;
    for (i=0;i<count;i++)
        delwin(items[i]);
    free(items);
}


La función scroll_menu nos permite movernos entre menús y dentro de ellos. Primero lee con getch las teclas que son pulsadas en el teclado. Si se presionan los cursores de arriba o abajo, entonces se selecciona el item superior o inferior. Esto se hace, como recordará, haciendo que el color de fondo sea diferente que el del resto de los items. Si se presionan los cursores de izquierda o derecha, el menú abierto se cierra y luego se abre el siguiente. Si se presiona la tecla enter, la función devuelve el item seleccionado. Si la tecla presionada es ESC, se cierran todos los menús sin seleccionar ningún item, La función ignora cualquier otra tecla que sea pulsada. En esta función, getch es capaz de leer las teclas de cursor del teclado. Permítame recordarle que esto es posible porque anteriormente, en la función init_curses, se hizo una llamada a keypad (stdscr, TRUE) y el valor devuelto por getch es de tipo int en lugar de ser de tipo cha ya que los valores de las teclas de función son mayores de lo que una variable de tipo char puede contener.


int scroll_menu(WINDOW **items,int count,int menu_start_col)
{
    int key;
    int selected=0;
    while (1) {
        key=getch();
        if (key==KEY_DOWN || key==KEY_UP) {
            wbkgd(items[selected+1],COLOR_PAIR(2));
            wnoutrefresh(items[selected+1]);
            if (key==KEY_DOWN) {
                selected=(selected+1) % count;
            } else {
                selected=(selected+count-1) % count;
            }
            wbkgd(items[selected+1],COLOR_PAIR(1));
            wnoutrefresh(items[selected+1]);
            doupdate();
        } else if (key==KEY_LEFT || key==KEY_RIGHT) {
            delete_menu(items,count+1);
            touchwin(stdscr);
            refresh();
            items=draw_menu(20-menu_start_col);
            return scroll_menu(items,8,20-menu_start_col);
        } else if (key==ESCAPE) {
            return -1;
        } else if (key==ENTER) {
            return selected;
        }
    }
}


Finalmente tenemos la función principal. En ella se usan todas las funciones que hemos escrito anteriormente para hacer que el programa funcione adecuadamente. También lee las teclas pulsadas con getch y si F1 o F2 son pulsadas, dibuja la ventana de menú correspondiente con draw_menu. Después llama a scroll_menu y deja que el usuario haga su selección desde los menús. Una vez tenga el valor devuelto por scroll_menu, borra las ventanas de menú e imprime el item seleccionado en la barra de mensajes.


Habría que mencionar ahora la función touchwin. Si se llama directamente a refresh sin llamar antes a touchwin después de cerrar los menús, el último menú abierto puede permanecer en pantalla. Esto es poque las funciones de menú no afectan para nada a stdscr y así, cuando se llama a refresh, éste no reescribe ningún caracter de stdscr ya que asume que dicha ventana no ha sido cambiada. touchwin pone todos los indicadores en la estructura WINDOW para decirle a refresh que todas las líneas de la ventana han cambiado y así en el próximo refresco de pantalla se reescribe la ventana completa incluso aunque sus contenidos no hayan cambiado. La información escrita en stdscr permanece  ahí una vez que los menús se han cerrado porque los menús no escriben sobre stdscr, sino que lo hacen en las ventanas nuevas que han creado.


int main()
{
    int key;
    WINDOW *menubar,*messagebar;

    init_curses();

    bkgd(COLOR_PAIR(1));
    menubar=subwin(stdscr,1,80,0,0);
    messagebar=subwin(stdscr,1,79,23,1);
    draw_menubar(menubar);
    move(2,1);
    printw("Press F1 or F2 to open the menus. ");
    printw("ESC quits.");
    refresh();

   do {
        int selected_item;
        WINDOW **menu_items;
        key=getch();
        werase(messagebar);
        wrefresh(messagebar);
        if (key==KEY_F(1)) {
            menu_items=draw_menu(0);
            selected_item=scroll_menu(menu_items,8,0);
            delete_menu(menu_items,9);
            if (selected_item<0)
                wprintw(messagebar,"You haven't selected any item.");
            else
                wprintw(messagebar,
                  "You have selected menu item %d.",selected_item+1);
            touchwin(stdscr);
            refresh();
        } else if (key==KEY_F(2)) {
            menu_items=draw_menu(20);
            selected_item=scroll_menu(menu_items,8,20);
            delete_menu(menu_items,9);
            if (selected_item<0)
                wprintw(messagebar,"You haven't selected any item.");
            else
                wprintw(messagebar,
                  "You have selected menu item %d.",selected_item+1);
            touchwin(stdscr);
            refresh();
        }
    } while (key!=ESCAPE);

    delwin(menubar);
    delwin(messagebar);
    endwin();
    return 0;
}


Si copia el código en un fichero llamada example.c y elimina mis explicaciones puede compilarlo con


gcc -Wall example.c -o example -lcurses


y probar el programa. También puede descargar el código en el enlace que viene más abajo en la sección de referencias.


Si te ha gustado el artículo inscribete al feed clicando en la imagen más abajo para tenerte siempre actualizado sobre los nuevos contenidos del blog:


Espero que esta publicación te haya gustado. Si tienes alguna duda, consulta o quieras complementar este post, no dudes en escribir en la zona de comentarios. También puedes visitar Facebook, Twitter, Linkedin, Instagram, Pinterest y Feedly donde encontrarás información complementaria a este blog. COMPARTE EN!

0 comments:

No insertes enlaces clicables, de lo contrario se eliminará el comentario. Si quieres ser advertido via email de los nuevos comentarios marca la casilla "Notificarme". Si te ayudé con la publicación o con las respuestas a los comentarios, compártelo en Facebook, Twitter, Tumblr, Google +, Pinterest o Instagram. Gracias.