Making Scales and Spin Buttons - make_scale, make_prog_bar, make_spinner

Cogent Tools Demo and Tutorials: Version 4.0
Prev | Chapter 7. GTK Functions (in `gtksimple.g`) | Next

# 7.3. Making Scales and Spin Buttons - `make_scale`, `make_prog_bar`, `make_spinner`

The scale, progress bars and spin buttons are all created in a similar way. The `make_scale` function returns a scale, based on these parameters:

`title` | A title string that appears above the scale.
`color` | A hex number in the format `0x`rrggbb``. This determines the color inside the trough of the scale.
`container` | A `GtkContainer` widget that holds the scale.
`adjvar` | The variable whose value is shown and/or set by this scale.
`low` | The minimum value for the scale.
`high` | The maximum value for the scale.
`step` | The smallest incremental value change permitted by the scale.
`page` | The amount the scale changes when a user clicks in the trough.

       /*--------------------------------------------------------------------
        * Function:    make_scale
        * Returns:     A GtkVScale
        * Description: Makes a scale based on adjustment parameters.  Then makes
        *              a ruler and a label and then packs them together into a
        *              GtkVBox, and packs that box into a container.
        *------------------------------------------------------------------*/
       function make_scale(title, color, container, adjvar,
                           low, high, step, page)
       {
         local adj, adj2, newvalue, scale, label, scalebox;

         /* Two adjustments are necessary right now since the
          * GtkRange.set_inverted() doesn't exist yet, even in C code.
          * We use the first adjusment to handle the actual value, and
          * the second adjustment for the range and ruler, whose values
          * are inverted from the first adjustment.
          */
         adj1 = gtk_adjustment_new (0, 0, 100, step, page, 0.0);
         adj2 = gtk_adjustment_new (0, 0, 100, step, page, 0.0);
         newvalue = eval(adjvar);

         if (number_p(newvalue))
           {
             adj1.value = newvalue;
             adj2.value = 100 - newvalue;
           }

         scale = new(GtkVScale);
         scale.set_adjustment(adj2);
         scale.set_update_policy(GTK_UPDATE_CONTINUOUS);
         scale.height = 200;
         scale.set_draw_value(FALSE);
         scale.set_color(1, (GTK_STATE_NORMAL &&
                             GTK_STATE_ACTIVE), color);

         rule = new(GtkVRuler);
         rule.set_range (100, 0, -1, 200);
         rule.position = - 5;
         rule.set_sensitive(FALSE);

         label = new (GtkLabel);
         label.set_text(title);
         label_val = new(GtkLabel);
         label_val.set_text(substr(string((eval(adjvar) * 100) / 100), 0, 6));

         /* Implement the adjustments on the value label, ruler, and scale. */
         adj2.signal("value_changed", `(@adj1).set_value(100 - (@adj2).value));
         adj2.signal("value_changed",
                     `(@label_val).set_text(substr(string(((@adj1).value * 100) / 100),
                                                   0, 6)));
         adj2.signal("value_changed", `(@rule).position = (100 - (@adj2).value));
         adj2_id = adj2.signal("value_changed", `write_point(#SP_001, (@adj1).value));

| Notice the use of the quote operators ``` and `@` in the above function calls. In general, the ``` operator protects an expression from evaluation, except for sub-expressions that are preceded by the `@` operator. In this case, the ``` operator prevents the assignment from taking place when the code is being read, but the `@` operator allows the name of the variable representing an adjustment or widget to be evaluated. Note that the `@` operator applies to the whole expression it precedes. For example, the expression `@adj1.value` would evaluate to the value of the `.value` resource at the time that this code is read, leaving us with that static value. This is not what we want. The expression `(@adj1).value` evaluates to a variable `.value` resource for the adjustment, which can change each time a `value_changed` signal is received.

For data coming from the Cascade DataHub, we use the Gamma function `add_exception_function` to update the value of the widget.

         add_exception_function(#SP_001, `reset_value(@scale, SP_001, @adj2_id));

         /* Create a table to facilitate the width and spacing of the widgets. */
         table = gtk_table_new(1, 5, FALSE);
         table.border_width = 5;
         table.set_homogeneous(TRUE);
         table.attach_defaults(scale, 1, 2, 0, 1);
         table.attach_defaults(rule, 2, 4, 0, 1);

The `scalebox` gives us a way to pack the scale and its label into another `GtkContainer`. This is useful, because any change in spacing applied to the items in the `container` will be applied to the scale and its label as one unit, while their relative spacing remains constant.

         scalebox = new(GtkVBox);
         scalebox.pack_start(label, TRUE, TRUE, 0);
         scalebox.pack_start(table, TRUE, TRUE, 0);
         scalebox.pack_start(label_val, TRUE, TRUE, 0);

         container.pack_start(scalebox, TRUE, TRUE, 0);
         scale.show();
         scale;
       }

The `make_prog_bar` function is quite similar to the `make_scale function`.

       /*--------------------------------------------------------------------
        * Function:    make_prog_bar
        * Returns:     t or nil
        * Description: Makes a progress bar based on adjustment parameters.
        *              It puts the progress bar into a table to facilitate
        *              proper layout.  It also makes a label and packs the
        *              bar and label together into a container.
        *------------------------------------------------------------------*/
       function make_prog_bar (title, container, adjvar, color)
       {
         local label, table, adj, pbar;

         label = new(GtkLabel);
         label.set_text(title);
         container.pack_start(label, TRUE, TRUE, 0);

         table = gtk_table_new(1, 5, FALSE);
         table.border_width = 5;
         table.set_homogeneous(TRUE);
         container.pack_start(table, TRUE, TRUE, 0);

This is a work-around used to force the progress bar into a desirable size. This involves making a 5-column homogeneous table, and then packing the bar into the middle three columns.

         adj = gtk_adjustment_new (0, 0, 100, 0, 0, 0.0);
         adj.set_value(eval(adjvar));
         pbar = new(GtkProgressBar);
         pbar.adjustment = adj;
         pbar.set_format_string("%v");
         pbar.set_orientation(GTK_PROGRESS_BOTTOM_TO_TOP);
         pbar.height = 200;
         pbar.set_color (1, 2, color);
         pbar.set_show_text(1);
         add_exception_function(adjvar, `(@pbar).adjustment.set_value(@adjvar));
         table.attach_defaults(pbar, 1, 4, 0, 1);
       }

The `make_spinner` function returns a spin button, based on mostly the same parameters as `make_scale`. A new parameter is `digits`, which determines how many digits appear after the decimal point in the numeric display. We need this because we don't want to show fractions of a second for the FREQ spin button.

       /*--------------------------------------------------------------------
        * Function:    make_spinner
        * Returns:     A GtkSpinButton
        * Description: Makes a spin button based on adjustment parameters,
        *              and packs it into a table.
        *------------------------------------------------------------------*/
       function make_spinner(adjvar, digits, low, high, step, page,
                             table, left, right, top, bottom)

       {
         local adj, newvalue, spinner;

         adj = gtk_adjustment_new (0, low, high, step, page, 0.0);

         newvalue = eval(adjvar);
         if (number_p(newvalue))
           adj.value = newvalue;

         spinner = new(GtkSpinButton);
         spinner.set_adjustment(adj);
         spinner.set_update_policy(GTK_UPDATE_CONTINUOUS);
         spinner.set_digits(digits);
         spinner.width = 40;
         spinner.set_value(.05);
         adj.signal("value_changed", `write_data(@spinner, @adjvar));
         spinner.show();
         add_exception_function(adjvar, `reset_value(@spinner, @adjvar, nil));
         table.attach_defaults(spinner, left, right, top, bottom);
         spinner;
       }

Prev | Home | Next
The Monitor Window - `create_monitor` | Up | Update Functions - `reset_value`, `write_data`

Copyright 1995-2002 by Cogent Real-Time Systems, Inc.