Progress Bars

Progress bars are used to show the status of an operation. They are pretty easy to use, as you will see with the code below. But first lets start out with the calls to create a new progress bar.

There are two ways to create a progress bar, one simple that takes no arguments, and one that takes an Adjustment object as an argument. If the former is used, the progress bar creates its own adjustment object.

GtkWidget *gtk_progress_bar_new( void );

GtkWidget *gtk_progress_bar_new_with_adjustment( GtkAdjustment *adjustment );

The second method has the advantage that we can use the adjustment object to specify our own range parameters for the progress bar.

The adjustment of a progress object can be changed dynamically using:

void gtk_progress_set_adjustment( GtkProgress   *progress,
                                  GtkAdjustment *adjustment );

Now that the progress bar has been created we can use it.

void gtk_progress_bar_update( GtkProgressBar *pbar,
                              gfloat          percentage );

The first argument is the progress bar you wish to operate on, and the second argument is the amount "completed", meaning the amount the progress bar has been filled from 0-100%. This is passed to the function as a real number ranging from 0 to 1.

GTK v1.2 has added new functionality to the progress bar that enables it to display its value in different ways, and to inform the user of its current value and its range.

A progress bar may be set to one of a number of orientations using the function

void gtk_progress_bar_set_orientation( GtkProgressBar *pbar,
                                       GtkProgressBarOrientation orientation );

The orientation argument may take one of the following values to indicate the direction in which the progress bar moves:

  GTK_PROGRESS_LEFT_TO_RIGHT
  GTK_PROGRESS_RIGHT_TO_LEFT
  GTK_PROGRESS_BOTTOM_TO_TOP
  GTK_PROGRESS_TOP_TO_BOTTOM

When used as a measure of how far a process has progressed, the ProgressBar can be set to display its value in either a continuous or discrete mode. In continuous mode, the progress bar is updated for each value. In discrete mode, the progress bar is updated in a number of discrete blocks. The number of blocks is also configurable.

The style of a progress bar can be set using the following function.

void gtk_progress_bar_set_bar_style( GtkProgressBar      *pbar,
                                     GtkProgressBarStyle  style );

The style parameter can take one of two values:

  GTK_PROGRESS_CONTINUOUS
  GTK_PROGRESS_DISCRETE

The number of discrete blocks can be set by calling

void gtk_progress_bar_set_discrete_blocks( GtkProgressBar *pbar,
                                           guint           blocks );

As well as indicating the amount of progress that has occured, the progress bar may be set to just indicate that there is some activity. This can be useful in situations where progress cannot be measured against a value range. Activity mode is not effected by the bar style that is described above, and overrides it. This mode is either TRUE or FALSE, and is selected by the following function.

void gtk_progress_set_activity_mode( GtkProgress *progress,
                                     guint        activity_mode );

The step size of the activity indicator, and the number of blocks are set using the following functions.

void gtk_progress_bar_set_activity_step( GtkProgressBar *pbar,
                                         guint           step );

void gtk_progress_bar_set_activity_blocks( GtkProgressBar *pbar,
                                           guint           blocks );

When in continuous mode, the progress bar can also display a configurable text string within its trough, using the following function.

void gtk_progress_set_format_string( GtkProgress *progress,
                                     gchar       *format);

The format argument is similiar to one that would be used in a C printf statement. The following directives may be used within the format string:

The displaying of this text string can be toggled using:

void gtk_progress_set_show_text( GtkProgress *progress,
                                 gint         show_text );

The show_text argument is a boolean TRUE/FALSE value. The appearance of the text can be modified further using:

void gtk_progress_set_text_alignment( GtkProgress   *progress,
                                      gfloat         x_align,
                                      gfloat         y_align );

The x_align and y_align arguments take values between 0.0 and 1.0. Their values indicate the position of the text string within the trough. Values of 0.0 for both would place the string in the top left hand corner; values of 0.5 (the default) centres the text, and values of 1.0 places the text in the lower right hand corner.

The current text setting of a progress object can be retrieved using the current or a specified adjustment value using the following two functions. The character string returned by these functions should be freed by the application (using the g_free() function). These functions return the formatted string that would be displayed within the trough.

gchar *gtk_progress_get_current_text( GtkProgress   *progress );

gchar *gtk_progress_get_text_from_value( GtkProgress *progress,
                                         gfloat       value );

There is yet another way to change the range and value of a progress object using the following function:

void gtk_progress_configure( GtkProgress  *progress,
                             gfloat        value,
                             gfloat        min,
                             gfloat        max );

This function provides quite a simple interface to the range and value of a progress object.

The remaining functions can be used to get and set the current value of a progess object in various types and formats:

void gtk_progress_set_percentage( GtkProgress *progress,
                                  gfloat       percentage );

void gtk_progress_set_value( GtkProgress *progress,
                             gfloat       value );

gfloat gtk_progress_get_value( GtkProgress *progress );

gfloat gtk_progress_get_current_percentage( GtkProgress *progress );

gfloat gtk_progress_get_percentage_from_value( GtkProgress *progress,
                                               gfloat       value );

These functions are pretty self explanatory. The last function uses the the adjustment of the specified progess object to compute the percentage value of the given range value.

Progress Bars are usually used with timeouts or other such functions (see section on Timeouts, I/O and Idle Functions) to give the illusion of multitasking. All will employ the gtk_progress_bar_update function in the same manner.

Here is an example of the progress bar, updated using timeouts. This code also shows you how to reset the Progress Bar.

/* example-start progressbar progressbar.c */

#include <gtk/gtk.h>

typedef struct _ProgressData {
    GtkWidget *window;
    GtkWidget *pbar;
    int timer;
} ProgressData;

/* Update the value of the progress bar so that we get
 * some movement */
gint progress_timeout( gpointer data )
{
    gfloat new_val;
    GtkAdjustment *adj;

    /* Calculate the value of the progress bar using the
     * value range set in the adjustment object */

    new_val = gtk_progress_get_value( GTK_PROGRESS(data) ) + 1;

    adj = GTK_PROGRESS (data)->adjustment;
    if (new_val > adj->upper)
      new_val = adj->lower;

    /* Set the new value */
    gtk_progress_set_value (GTK_PROGRESS (data), new_val);

    /* As this is a timeout function, return TRUE so that it
     * continues to get called */
    return(TRUE);
} 

/* Callback that toggles the text display within the progress
 * bar trough */
void toggle_show_text( GtkWidget    *widget,
		       ProgressData *pdata )
{
    gtk_progress_set_show_text (GTK_PROGRESS (pdata->pbar),
                                GTK_TOGGLE_BUTTON (widget)->active);
}

/* Callback that toggles the activity mode of the progress
 * bar */
void toggle_activity_mode( GtkWidget    *widget,
			   ProgressData *pdata )
{
    gtk_progress_set_activity_mode (GTK_PROGRESS (pdata->pbar),
                                    GTK_TOGGLE_BUTTON (widget)->active);
}

/* Callback that toggles the continuous mode of the progress
 * bar */
void set_continuous_mode( GtkWidget    *widget,
			  ProgressData *pdata )
{
    gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
                                    GTK_PROGRESS_CONTINUOUS);
}

/* Callback that toggles the discrete mode of the progress
 * bar */
void set_discrete_mode( GtkWidget    *widget,
			ProgressData *pdata )
{
    gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
                                    GTK_PROGRESS_DISCRETE);
}
 
/* Clean up allocated memory and remove the timer */
void destroy_progress( GtkWidget     *widget,
		       ProgressData *pdata)
{
    gtk_timeout_remove (pdata->timer);
    pdata->timer = 0;
    pdata->window = NULL;
    g_free(pdata);
    gtk_main_quit();
}

int main( int   argc,
          char *argv[])
{
    ProgressData *pdata;
    GtkWidget *align;
    GtkWidget *separator;
    GtkWidget *table;
    GtkAdjustment *adj;
    GtkWidget *button;
    GtkWidget *check;
    GtkWidget *vbox;

    gtk_init (&argc, &argv);

    /* Allocate memory for the data that is passwd to the callbacks */
    pdata = g_malloc( sizeof(ProgressData) );
  
    pdata->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_policy (GTK_WINDOW (pdata->window), FALSE, FALSE, TRUE);

    gtk_signal_connect (GTK_OBJECT (pdata->window), "destroy",
	                GTK_SIGNAL_FUNC (destroy_progress),
                        pdata);
    gtk_window_set_title (GTK_WINDOW (pdata->window), "GtkProgressBar");
    gtk_container_set_border_width (GTK_CONTAINER (pdata->window), 0);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
    gtk_container_add (GTK_CONTAINER (pdata->window), vbox);
    gtk_widget_show(vbox);
  
    /* Create a centering alignment object */
    align = gtk_alignment_new (0.5, 0.5, 0, 0);
    gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
    gtk_widget_show(align);

    /* Create a Adjusment object to hold the range of the
     * progress bar */
    adj = (GtkAdjustment *) gtk_adjustment_new (0, 1, 150, 0, 0, 0);

    /* Create the GtkProgressBar using the adjustment */
    pdata->pbar = gtk_progress_bar_new_with_adjustment (adj);

    /* Set the format of the string that can be displayed in the
     * trough of the progress bar:
     * %p - percentage
     * %v - value
     * %l - lower range value
     * %u - upper range value */
    gtk_progress_set_format_string (GTK_PROGRESS (pdata->pbar),
	                            "%v from [%l-%u] (=%p%%)");
    gtk_container_add (GTK_CONTAINER (align), pdata->pbar);
    gtk_widget_show(pdata->pbar);

    /* Add a timer callback to update the value of the progress bar */
    pdata->timer = gtk_timeout_add (100, progress_timeout, pdata->pbar);

    separator = gtk_hseparator_new ();
    gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
    gtk_widget_show(separator);

    /* rows, columns, homogeneous */
    table = gtk_table_new (2, 3, FALSE);
    gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
    gtk_widget_show(table);

    /* Add a check button to select displaying of the trough text */
    check = gtk_check_button_new_with_label ("Show text");
    gtk_table_attach (GTK_TABLE (table), check, 0, 1, 0, 1,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    gtk_signal_connect (GTK_OBJECT (check), "clicked",
                        GTK_SIGNAL_FUNC (toggle_show_text),
                        pdata);
    gtk_widget_show(check);

    /* Add a check button to toggle activity mode */
    check = gtk_check_button_new_with_label ("Activity mode");
    gtk_table_attach (GTK_TABLE (table), check, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    gtk_signal_connect (GTK_OBJECT (check), "clicked",
                        GTK_SIGNAL_FUNC (toggle_activity_mode),
                        pdata);
    gtk_widget_show(check);

    separator = gtk_vseparator_new ();
    gtk_table_attach (GTK_TABLE (table), separator, 1, 2, 0, 2,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    gtk_widget_show(separator);

    /* Add a radio button to select continuous display mode */
    button = gtk_radio_button_new_with_label (NULL, "Continuous");
    gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (set_continuous_mode),
                        pdata);
    gtk_widget_show (button);

    /* Add a radio button to select discrete display mode */
    button = gtk_radio_button_new_with_label(
               gtk_radio_button_group (GTK_RADIO_BUTTON (button)),
               "Discrete");
    gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
                      5, 5);
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (set_discrete_mode),
                        pdata);
    gtk_widget_show (button);

    separator = gtk_hseparator_new ();
    gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
    gtk_widget_show(separator);

    /* Add a button to exit the program */
    button = gtk_button_new_with_label ("close");
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               (GtkSignalFunc) gtk_widget_destroy,
                               GTK_OBJECT (pdata->window));
    gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

    /* This makes it so the button is the default. */
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

    /* This grabs this button to be the default button. Simply hitting
     * the "Enter" key will cause this button to activate. */
    gtk_widget_grab_default (button);
    gtk_widget_show(button);

    gtk_widget_show (pdata->window);

    gtk_main ();
    
    return(0);
}
/* example-end */