Página siguiente Página anterior Índice general

11. El widget GtkCList

El widget GtkCList ha reemplazado al widget GtkList (que sigue estando disponible).

El widget GtkCList es un widget de una lista multicolumna que es capaz de manejar, literalmente, miles de filas de información. Cada columna puede tener (opcionalmente) un título, que puede estar activado (opcionalmente), permitiéndonos enlazar una función con la selección.

11.1 Creando un widget GtkCList

Crear un GtkCList es algo bastante sencillo, una vez que sabe como crear un widget en general. Se proporcionan al menos dos formas estándar de crearlo, la forma fácil y la forma difícil. Pero antes de crear una GtkCList, hay una cosa que debemos saber: ¿Cuántas columnas va a tener?

No todas las columnas tienen que ser visibles y pueden utilizarse para almacenar datos que estén relacionados con una cierta celda de la lista.

GtkWidget *gtk_clist_new ( gint columns );

GtkWidget *gtk_clist_new_with_titles( gint   columns,
                                      gchar *titles[] );

Esta primera aproximación al problema es muy sencilla, pero la segunda requerirá alguna explicación adicional. Cada columna puede tener un título asociado. Si utilizamos la segunda forma, deberemos proporcionar punteros al texto del título, y el número de punteros debe ser igual al número de columnas especificadas. Por supuesto, siempre podemos utilizar la primera forma de creación y añadir más tarde los títulos de forma manual.

11.2 Modos de operación

Hay varios atributos que pueden utilizarse para alterar el aspecto de un GtkCList. Primero tenemos

void gtk_clist_set_selection_mode( GtkCList         *clist,
                                   GtkSelectionMode  mode );

que, como el propio nombre indica, establece el modo de selección de la lista GtkCList. El primer argumento es el widget GtkCList, y el segundo especifica el modo de selección de la celda (están definidos en gtkenums.h). En el momento de escribir esto, estaban disponibles los siguientes modos:

Puede que se añadan otros modos en versiones posteriores de GTK.

También tenemos

void gtk_clist_set_policy (GtkCList *clist,
                           GtkPolicyType vscrollbar_policy,
                           GtkPolicyType hscrollbar_policy);

que define que es lo que ocurre con las barras de desplazamiento. Los siguientes valores son los posibles para las barras de desplazamiento horizontal y vertical:

También podemos definir como debería ser el aspecto del borde del widget GtkCList. Esto lo podemos hacer mediante

void gtk_clist_set_border( GtkCList      *clist,
                           GtkShadowType  border );

Y los posibles valores para el segundo argumento son

11.3 Trabajando con los títulos

Cuando cree un widget GtkCList, también obtendrá automáticamente un conjunto de botones título. Vivirán en lo alto de una ventana CList, y pueden actuar como botones normales que responden cuando se pulsa sobre ellos, o bien pueden ser pasivos, en cuyo caso no serán nada más que un título. Hay cuatro llamadas diferentes que nos ayudarán a establecer el estado de los botones título.

void gtk_clist_column_title_active( GtkCList *clist,
                                     gint     column );

void gtk_clist_column_title_passive( GtkCList *clist,
                                     gint      column );

void gtk_clist_column_titles_active( GtkCList *clist );

void gtk_clist_column_titles_passive( GtkCList *clist );

Un título activo es aquel que actua como un botón normal, y uno pasivo es sólo una etiqueta. Las primeras dos llamadas de arriba activarán/desactivarán el botón título correspondiente a la columna column, mientras que las dos llamadas siguientes activarán/desactivarán todos los botones título que hayan en el widget clist que se le proporcione a la función.

Pero, por supuesto, habrá casos en el que no querremos utilizar los botones título, así que también tenemos la posibilidad de ocultarlos y de volverlos a mostrar utilizando las dos llamadas siguientes:

void gtk_clist_column_titles_show( GtkCList *clist );

void gtk_clist_column_titles_hide( GtkCList *clist );

Para que los títulos sean realmente útiles necesitamos un mecanismo que nos permita darles el valor que nosotros queramos y cambiar ese valor, y podremos hacerlo mediante

void gtk_clist_set_column_title( GtkCList *clist,
                                 gint      column,
                                 gchar    *title );

Debe llevar cuidado, ya que sólo se puede especificar el título de una columna a la vez, por lo que si conoce todos los títulos desde el principio, le sugiero que utilice gtk_clist_new_with_titles (como se describe arriba) para establecerlos adecuadamente. Le ahorrará tiempo de programación, y hará su programa más pequeño. Hay algunos casos donde es mejor utilizar la forma manual, y uno de ellos es cuando no todos los títulos son texto. GtkCList nos proporciona botones título que pueden, de hecho, incorporar un widget entero, por ejemplo un pixmap. Todo esto se hace mediante

void gtk_clist_set_column_widget( GtkCList  *clist,
                                  gint       column,
                                  GtkWidget *widget );

que no debería necesitar de explicaciones adicionales.

11.4 Manipulando la lista en sí.

Es posible cambiar la justificación de una columna, y esto se hace mediante

void gtk_clist_set_column_justification( GtkCList         *clist,
                                         gint              column,
                                         GtkJustification  justification );

El tipo GtkJustification puede tomar los valores siguientes:

La siguiente función es muy importante, y debería ser un estándar para inicializar todos los widgets GtkCList. Cuando se crea la lista, los anchos de las distintas columnas se eligen para que coincidan con sus títulos, y éste es el ancho adecuado que tenemos que poner, utilizando

void gtk_clist_set_column_width( GtkCList *clist,
                                 gint      column,
                                 gint      width );

Observe que el ancho viene dado en pixeles y no en letras. Lo mismo vale para el alto de la celda en las columnas, pero como el valor por defecto es la altura del tipo de letra actual, no es algo tan crítico para la aplicación. De todas formas, la altura se cambia mediante

void gtk_clist_set_row_height( GtkCList *clist,
                               gint      height );

De nuevo, hay que advertir que el ancho viene dado en pixeles.

También podemos ir hacia un elemento sin la intervención del usuario, sin embargo hace falta que sepamos hacia donde queremos ir. O en otras palabras, necesitamos la fila y la columna del elemento al que queremos pasar.

void gtk_clist_moveto( GtkCList *clist,
                       gint      row,
                       gint      column,
                       gfloat    row_align,
                       gfloat    col_align );

Es importante comprender bien el significado de gfloat row_align. Tiene un valor entre 0.0 y 1.0, donde 0.0 significa que debemos hacer que la fila seleccionada aparezca en la alto de la lista, mientras que 1.0 significa que la fila aparecerá en la parte de abajo. El resto de valores entre 0.0 y 1.0 son válidos y harán que la fila aparezca entre la parte superior y la inferior. El último argumento, gfloat col_align funciona igual, siendo 0.0 la izquierda y 1.0 la derecha.

Dependiendo de las necesidades de la aplicación, puede que no tengamos que hacer un desplazamiento hacia un elemento que ya sea visible. Por tanto, ¿cómo podemos saber si ya es visible? Como siempre, hay una función que sirve para averiguarlo

GtkVisibility gtk_clist_row_is_visible( GtkCList *clist,
                                        gint      row );

El valor devuelto es uno de los siguientes:

Como puede ver, sólo nos dice si una fila es visible. Actualmente no hay ninguna forma de obtener el mismo dato para una columna. Sin embargo podemos obtener información parcial, porque si el valor devuelto es GTK_VISIBILITY_PARTIAL, entonces es que alguna parte está oculta, pero no sabemos si es la fila que está cortada por la parte de abajo de la lista, o si la fila tiene columnas que están fuera.

También podemos cambiar el color del primer y del segundo plano de una fila en particular. Esto es útil para marcar la fila seleccionada por el usuario, y las dos funciones que hay que utilizar son

void gtk_clist_set_foreground( GtkCList *clist,
                               gint      row,
                               GdkColor *color );

void gtk_clist_set_background( GtkCList *clist,
                               gint      row,
                               GdkColor *color );

Cuidado, ya que los colores deben estar asignados previamente en la memoria.

11.5 Añadiendo filas a la lista

Podemos añadir filas de dos formas. Se pueden añadir al final de la lista utilizando

gint gtk_clist_append( GtkCList *clist,
                       gchar    *text[] );

o podemos insertar una fila en un lugar determinado utilizando

void gtk_clist_insert( GtkCList *clist,
                       gint      row,
                       gchar    *text[] );

En ambas llamadas podemos proporcionar un conjunto de punteros que serán los textos que queremos poner en las columnas. El número de punteros debe ser igual al número de columnas en la lista. Si el argumento text[] es NULL, entonces no habrá texto en las columnas de la fila. Esto sería útil, por ejemplo, si queremos añadir pixmaps en lugar de texto (en general para cualquier cosa que haya que hacer manualmente).

De nuevo, cuidado ya que el número de filas y de columnas comienza en 0.

Para eliminar una fila individual podemos utilizar

void gtk_clist_remove( GtkCList *clist,
                       gint      row );

Hay también una llamada que elimina todas las filas en la lista. Es mucho más rápido que llamar a gtk_clist_remove una vez por cada fila, que sería la única alternativa.

void gtk_clist_clear( GtkCList *clist );

También hay dos funciones que es conveniente utilizarlas cuando hay que hacerle muchos cambios a una lista. Son para evitar que la lista parpadee mientras es actualizada repetidamente, que puede ser muy molesto para el usuario. Por tanto es una buena idea congelar la lista, hacer los cambios, y descongelarla, que hará que la lista se actualice en la pantalla.

void gtk_clist_freeze( GtkCList * clist );

void gtk_clist_thaw( GtkCList * clist );

11.6 Poniendo texto y pixmaps en las celdas

Una celda puede contener un pixmap, texto o ambos. Para ponerlos en las celdas, podemos utilizar las siguientes funciones.

void gtk_clist_set_text( GtkCList *clist,
                         gint      row,
                         gint      column,
                         gchar    *text );

void gtk_clist_set_pixmap( GtkCList  *clist,
                           gint       row,
                           gint       column,
                           GdkPixmap *pixmap,
                           GdkBitmap *mask );

void gtk_clist_set_pixtext( GtkCList  *clist,
                            gint       row,
                            gint       column,
                            gchar     *text,
                            guint8     spacing,
                            GdkPixmap *pixmap,
                            GdkBitmap *mask );

Son bastante sencillas de entender. Todas las llamadas tienen la GtkCList como primer argumento, seguidas por la fila y la columna de la celda, y seguidas por el dato que debe ponerse en la celda. El argumento gint8 spacing en gtk_clist_set_pixtext es el número de pixels entre el pixmap y el principio del texto.

Para leer los datos que hay en una celda, podemos utilizar

gint gtk_clist_get_text( GtkCList  *clist,
                         gint       row,
                         gint       column,
                         gchar    **text );

gint gtk_clist_get_pixmap( GtkCList   *clist,
                           gint        row,
                           gint        column,
                           GdkPixmap **pixmap,
                           GdkBitmap **mask );

gint gtk_clist_get_pixtext( GtkCList   *clist,
                            gint        row,
                            gint        column,
                            gchar     **text,
                            guint8     *spacing,
                            GdkPixmap **pixmap,
                            GdkBitmap **mask );

No es necesario leer todos los datos en caso de que no estemos interesados. Cualquiera de los punteros que se supone contendrán los valores a devolver (cualquiera excepto el clist) pueden ser NULL. Por lo que si sólo queremos leer el texto de una celda que es de tipo pixtext, deberíamos hacer lo siguiente, suponiendo que clist, row y column ya existan:

gchar *mytext;

gtk_clist_get_pixtext(clist, row, column, &mytext, NULL, NULL, NULL);

Hay una rutina más que está relacionada con lo que está dentro de una celda de una clist, y es

GtkCellType gtk_clist_get_cell_type( GtkCList *clist,
                                     gint      row,
                                     gint      column );

que devuelve el tipo de datos que hay en la celda. El valor devuelto es uno de los siguientes

También hay una función que nos permite especificar la indentación de un celda (horizontal o vertical). El valor de la indentación es del tipo gint, viene dado en pixeles, y puede ser positivo o negativo.

void gtk_clist_set_shift( GtkCList *clist,
                          gint      row,
                          gint      column,
                          gint      vertical,
                          gint      horizontal );

11.7 Almacenando punteros a datos

Con una GtkCList es posible poner un puntero a datos en una fila. Este puntero no será visible al usuario, pero puede serle útil al programador.

De nuevo, las funciones son lo suficientemente autoexplicativas

void gtk_clist_set_row_data( GtkCList *clist,
                             gint      row,
                             gpointer  data );

void gtk_clist_set_row_data_full( GtkCList         *clist,
                                  gint              row,
                                  gpointer          data,
                                  GtkDestroyNotify  destroy );

gpointer gtk_clist_get_row_data( GtkCList *clist,
                                 gint      row );

gint gtk_clist_find_row_from_data( GtkCList *clist,
                                   gpointer  data );

11.8 Trabajando con la selección

También hay funciones que nos permiten forzar la (de)selección de una fila. Son

void gtk_clist_select_row( GtkCList *clist,
                           gint      row,
                           gint      column );

void gtk_clist_unselect_row( GtkCList *clist,
                             gint      row,
                             gint      column );

Y también una función que tomará las coordenadas x e y (por ejemplo, recibidas del ratón), mirará en la lista y devolverá la fila y la columna que les corresponden.

gint gtk_clist_get_selection_info( GtkCList *clist,
                                   gint      x,
                                   gint      y,
                                   gint     *row,
                                   gint     *column );

Cuando detectemos algo interesante, como por ejemplo el movimiento del ratón, o una pulsación en cualquier lugar de la lista, podemos leer las coordenadas del ratón y encontrar en que elemento de la lista se encuentra. ¿Engorroso? Afortunadamente, hay una forma más sencilla de hacer las cosas...

11.9 Las señales que lo hacen todo

Como con el resto de widgets, hay unas cuantas señales que podemos utilizar. El widget GtkCList está derivado del widget GtkContainer, y por tanto tiene las mismas señales que éste, pero además añade las siguientes:

Por tanto si queremos conectar una llamada a select_row, la llamada se deberá declarar como

void select_row_callback(GtkWidget *widget,
                         gint row,
                         gint column,
                         GdkEventButton *event,
                         gpointer data);

La llamada se conectará, como siempre, con

gtk_signal_connect(GTK_OBJECT( clist),
                   "select_row"
                   GTK_SIGNAL_FUNC(select_row_callback),
                   NULL);

11.10 Un ejemplo GtkCList

/* principio del ejemplo clist clist.c */

#include        <gtk/gtk.h>
#include        <glib.h>

/* Aquí tenemos algunos prototipos de las funciones de llamada */
void button_add_clicked( GtkWidget *button, gpointer data);
void button_clear_clicked( GtkWidget *button, gpointer data);
void button_hide_show_clicked( GtkWidget *button, gpointer data);
void selection_made( GtkWidget *clist, gint row, gint column,
                     GdkEventButton *event, gpointer data);

gint main (int argc, gchar *argv[])
{                                  
    GtkWidget       *window;
    GtkWidget       *vbox, *hbox;
    GtkWidget       *clist;
    GtkWidget       *button_add, *button_clear, *button_hide_show;    
    gchar           *titles[2] = {"Ingredients","Amount"};

    gtk_init(&argc, &argv);
    
    
    window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_usize(GTK_WIDGET(window), 300, 150);

    gtk_window_set_title(GTK_WINDOW(window), "GtkCList Example");
    gtk_signal_connect(GTK_OBJECT(window),
                       "destroy",
                       GTK_SIGNAL_FUNC(gtk_main_quit),
                       NULL);
    
    vbox=gtk_vbox_new(FALSE, 5);
    gtk_container_border_width(GTK_CONTAINER(vbox), 5);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show(vbox);
    
    /* Crear el GtkCList. Para este ejemplo utilizaremos 2 columnas */
    clist = gtk_clist_new_with_titles( 2, titles);

    /* Cuando se hace una selección, queremos saber algo acerca de
     * ella. La función de llamada utilizada es selection_made, y su
     * código lo podemos encontrar más abajo */
    gtk_signal_connect(GTK_OBJECT(clist), "select_row",
                       GTK_SIGNAL_FUNC(selection_made),
                       NULL);

    /* No es necesario ponerle sombra al borde, pero es bonito :) */
    gtk_clist_set_border(GTK_CLIST(clist), GTK_SHADOW_OUT);

    /* Lo que sí que es importante, es poner el ancho de las columnas
     * ya no tendrán el valor correcto en caso contrario. Recuerde que
     * las columnas se numeran desde el 0 en adelante (hasta el 1 en
     * este caso).
     */
    gtk_clist_set_column_width (GTK_CLIST(clist), 0, 150);

    /* Scollbars _only when needed_ */
    gtk_clist_set_policy(GTK_CLIST(clist), GTK_POLICY_AUTOMATIC,
                                           GTK_POLICY_AUTOMATIC);

    /* Añade el widget GtkCList a la caja vertical y lo muestra. */
    gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0);
    gtk_widget_show(clist);

    /* Crea los botones y los añade a la ventana. Ver la parte del
     * tutorial sobre botones para ver más ejemplos y comentarios
     * acerca de todo esto.
     */
    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
    gtk_widget_show(hbox);

    button_add = gtk_button_new_with_label("Add List");
    button_clear = gtk_button_new_with_label("Clear List");
    button_hide_show = gtk_button_new_with_label("Hide/Show titles");

    gtk_box_pack_start(GTK_BOX(hbox), button_add, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), button_clear, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), button_hide_show, TRUE, TRUE, 0);

    /* Conectar nuestras funciones de llamada a los tres botones */
    gtk_signal_connect_object(GTK_OBJECT(button_add), "clicked",
                              GTK_SIGNAL_FUNC(button_add_clicked),
                              (gpointer) clist);
    gtk_signal_connect_object(GTK_OBJECT(button_clear), "clicked",
                              GTK_SIGNAL_FUNC(button_clear_clicked),
                              (gpointer) clist);
    gtk_signal_connect_object(GTK_OBJECT(button_hide_show), "clicked",
                              GTK_SIGNAL_FUNC(button_hide_show_clicked),
                              (gpointer) clist);

    gtk_widget_show(button_add);
    gtk_widget_show(button_clear);
    gtk_widget_show(button_hide_show);

    /* Ahora hemos terminado el interface y sólo nos queda mostrar la
     * ventana y entrar en el bucle gtk_main.
     */
    gtk_widget_show(window);
    gtk_main();
    
    return 0;
}

/* El usuario pulsó el botón "Add List". */
void button_add_clicked( GtkWidget *button, gpointer data)
{
    int         indx;

    /* Algo tonto que añadir a la lista. 4 filas con 2 columnas cada
     * una
     */
    gchar      *drink[4][2] = {{"Milk", "3 Oz"},
                               {"Water", "6 l"},
                               {"Carrots", "2"},
                               {"Snakes", "55"}};

    /* Aquí hacemos la adición del texto. Se hace una vez por cada
     * fila.
     */
    for( indx=0; indx < 4; indx++)
        gtk_clist_append( (GtkCList*) data, drink[indx]);

    return;
}

/* El usuario pulsó el botón "Clear List" */
void button_clear_clicked( GtkWidget *button, gpointer data)
{
    /* Borrar la lista utilizando gtk_clist_clear. Esto es mucho más
     * rápido que llamar a gtk_clist_remove una vez por cada fila.
     */
    gtk_clist_clear((GtkCList*) data);

    return;
}

/* El usuario pulsó el botón "Hide/Show titles". */
void button_hide_show_clicked( GtkWidget *button, gpointer data)
{
    /* Una bandera para recordar el estado. 0 = actualmente visible */
    static short int flag = 0;

    if (flag == 0)
    {
        /* Oculta los títulos y pone la bandera a 1 */
        gtk_clist_column_titles_hide((GtkCList*) data);
        flag++;
    }
    else
    {
        /* Muestra los títulos y pone la bandera a 0 */
        gtk_clist_column_titles_show((GtkCList*) data);
        flag--;
    }

    return;
}

/* Se llegamos aquí, entonces el usuario ha seleccionado una fila de
 * la lista.
 */
void selection_made( GtkWidget *clist, gint row, gint column,
                     GdkEventButton *event, gpointer data)
{
    gchar       *text;

    /* Obtiene el texto que se ha almacenado en la fila y columna
     *  sobre las que se ha pulsado. Lo recibiremos como un puntero en
     * el argumento text.
     */
    gtk_clist_get_text(GTK_CLIST(clist), row, column, &text);

    /* Imprime alguna información sobre la fila seleccionada */
    g_print("You selected row %d. More specifically you clicked in column %d, and the text in this cell is %s\n\n", row, column, text);

    return;
}
/* final del ejemplo */


Página siguiente Página anterior Índice general