The Monitor Window - create_monitor

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

# 7.2. The Monitor Window - `create_monitor`

The Monitor window is divided into three framed panels. The first panel contains a scale, while the other two contain progress bars, and each panel contains a few buttons and label. Below the panels is a row of radio buttons.

| This part of the tutorial assumes a basic working knowledge of Gamma/GTK. For a more detailed tutorial on topics covered here, please refer to the Building Applications tutorial and the Example Code appendix of the Gamma/GTK manual.

Each of the three colored bars in the panels correspond to one of the three PID Emulator values in the datahub. The scale on the left, SP, is used in two ways--to display values and to set values. The two progress bars (MV and PV) are for display only. The Kp, Ki, Kd, INT and PROP spin buttons and labels to their left are arranged using `GtkTable` widgets; and three levels of `GtkBox` widgets are used to achieve the correct layout.

The first part of the function builds the main window and title area, and creates the top-level boxes.

       /*--------------------------------------------------------------------
        * Function:    create_monitor
        * Returns:     A GtkWindow
        * Description: Creates the Monitor window.
        *------------------------------------------------------------------*/
       function create_monitor ()
       {
         local win_monitor, main_title, scale, button, label, table;
         local ambutton, frame, box1, box2, box3, onstring, offstring;
         local s1, s2, s3, s4, s5, s6, but1, but2, but3, but4;

         win_monitor = new (GtkWindow);
         win_monitor.signal ("destroy", #win_monitor = nil);
         win_monitor.set_color(1, GTK_STATE_NORMAL, 0xdddddd);
         win_monitor.title = "Cogent Tools Demo: Monitor";
         win_monitor.border_width = 5;

         main_title = new (GtkLabel);
         main_title.set_text("Cogent Tools Demo - Monitor");
         main_title.height = 25;

         frame = new(GtkFrame);
         frame.set_color(1, GTK_STATE_NORMAL, 0xff0000);
         frame.border_width = 5;
         frame.add(main_title);

We put in an extra `GtkHBox` here for future use, so that a trend graph can be added on the right.

         box1 = new (GtkHBox);
         win_monitor.add (box1);

         box2 = new (GtkVBox);
         box2.spacing = 5;
         box2.border_width = 5;
         box2.pack_start(frame, TRUE, TRUE, 5);
         box1.pack_start (box2, TRUE, TRUE, 0);
         box2.show();

         box3 = new(GtkHBox);
         box2.pack_start(box3, TRUE, TRUE, 0);

The next three main parts of the function build the three panels that contain the spin buttons and scale or progress bar. Each panel is different, but they are created in a similar way. This first one will serve as an example.

         /********************************************************
          *                   Set point Panel                    *
          ********************************************************/
         box4 = new(GtkVBox);
         box4.set_border_width(5);

         evaluation = eval(SP_001);
         scale = make_scale("SP:\nSet point", 0x9da3eb,
                            box4, #SP_001, 0.0, 100.0, 0.1, 5.0);

         table = gtk_table_new(2, 2, FALSE);
         table.set_row_spacings(5);

The scale is built using the `make_scale` function. Its functionality is added by calling the Gamma function `add_exception_function`, which in turn calls the program's `reset_value` function whenever a datahub point changes its value.

To get consistent spacing, sizing, and vertical alignment among all the elements at the bottom of the window, we use a table. Each row in the table is similar, containing a label made using our `table_labeler` function, and a button.

         table_labeler(table, "Auto mode ", 0, 1, 0, 1);
         ambutton = new(GtkButton);
         set_label(ambutton, AUTO_001, "ON", "OFF");

         ambutton.signal("clicked",
                         `toggle_sym(@ambutton, #AUTO_001, "ON", "OFF"));
         add_exception_function(#AUTO_001,
                                `set_label(@ambutton, #AUTO_001,"ON", "OFF"));
         table.attach_defaults(ambutton, 1, 2, 0, 1);

The Automatic mode button (`ambutton`) has a toggling label: On and Off. We use the `set_label` function to set the label according to the value of `AUTO_001` in the datahub. Then there are two ways that the button label should be able to change. Clicking on the button itself calls the `toggle_sym` function, which changes the value of `AUTO_001` in the datahub, and then changes the label with the `set_label` function. But the label should also change when the value of `AUTO_001` gets changed in the datahub by some other program. For that case, we use Gamma's `add_exception_function` to activate the `set_label` function directly.

The next button (`s1`) is a spin button, and we create it using our `make_spinner` function. We give it a callback with the `add_exception_function`, add it to the table, and pack the table in the box. All the other parameter spin buttons are created in the same way.

         table_labeler(table, "Delay", 0, 1, 1, 2);

         s1 = make_spinner(#FREQ_001, 0, 1.0, 20.0, 1.0, 1.0,
                           table, 1, 2, 1, 2);
         box4.pack_start(table, TRUE, TRUE, 0);

         frame = new(GtkFrame);
         frame.set_color(1, GTK_STATE_NORMAL, 0xaaaaff);
         frame.set_shadow_type(GTK_SHADOW_IN);
         frame.add(box4);

         box3.pack_start(frame, TRUE, TRUE, 5);

All this gets put inside a frame, to create a "control panel" appearance. The Controller Panel and Plant Panel are created in essentially the same way.

         /********************************************************
          *                Control output Panel                  *
          ********************************************************/
         box4 = new(GtkVBox);
         box4.set_border_width(5);

         make_prog_bar("MV:\nControl Output", box4, #MV_001, 0x41be41);

         table = gtk_table_new(2, 3, FALSE);
         table.set_row_spacings(5);

         table_labeler(table, "Kp: Prop", 0, 1, 0, 1);
         PID1_Kp = register_point (#PID1_Kp);
         s2 = make_spinner(#PID1_Kp, 2, 0.0, 1.0, 0.01, 0.1,
                           table, 1, 2, 0, 1);

         table_labeler(table, "  Ki: Int", 0, 1, 1, 2);
         PID1_Ki = register_point (#PID1_Ki);
         s3 = make_spinner(#PID1_Ki, 2, 0.0, 5.0, 0.01, 0.1,
                           table, 1, 2, 1, 2);

         table_labeler(table, "Kd: Deriv", 0, 1, 2, 3);
         PID1_Kd = register_point (#PID1_Kd);
         s4 = make_spinner(#PID1_Kd, 2, 0.0, 1.0, 0.01, 0.1,
                           table, 1, 2, 2, 3);

         box4.pack_start(table, TRUE, TRUE, 0);

         frame = new(GtkFrame);
         frame.set_color(1, GTK_STATE_NORMAL, 0xaaffaa);
         frame.set_shadow_type(GTK_SHADOW_IN);
         frame.add(box4);

         box3.pack_start (frame, TRUE, TRUE, 5);

         /********************************************************
          *                     Plant Panel                      *
          ********************************************************/
         box4 = new(GtkVBox);
         box4.set_border_width(5);

         make_prog_bar("PV:\nProcess Variable", box4, #PV_001, 0xeb0000);

         table = gtk_table_new(2, 3, FALSE);
         table.set_row_spacings(5);

         table_labeler(table, "PROP", 0, 1, 0, 1);
         PROP_001 = register_point (#PROP_001);
         s5 = make_spinner(#PROP_001, 2, 0.1, 5.0, 0.01, 0.1,
                           table, 1, 2, 0, 1);

         table_labeler(table, "  INT", 0, 1, 1, 2);
         INT_001 = register_point (#INT_001);
         s6 = make_spinner(#INT_001, 2, 0.0, 10.0, 0.01, 0.1,
                           table, 1, 2, 1, 2);

         table_labeler(table, " ", 0, 1, 2, 3);
         table.set_row_spacing(1, 10);

         box4.pack_start(table, TRUE, TRUE, 0);

         frame = new(GtkFrame);
         frame.set_color(1, GTK_STATE_NORMAL, 0xff9999);
         frame.set_shadow_type(GTK_SHADOW_IN);
         frame.add(box4);

         box3.pack_start (frame, TRUE, TRUE, 5);

The bottom part of the window has four radio buttons and an exit button packed into a `GtkHBox`. We use a `GtkHBox` instead of a `GtkHButtonBox` because it gives more consistent spacing in this case. The code for each radio button is similar, each one calling the `change_settings` function when clicked. We set the Poor button active, to start out with the most visible PID response.

         /********************************************************
          *        Radio buttons for setting PID Loop            *
          ********************************************************/
         box3 = new (GtkHBox);
         box3.spacing = 0;
         box3.border_width = 5;

         label = new(GtkLabel);
         label.set_text("PID Loop: ");
         box3.pack_start (label, TRUE, TRUE, 0);

         but1 = gtk_radio_button_new_with_label(nil, "Good");
         box3.pack_start (but1, TRUE, TRUE, 0);
         but1.signal("clicked",
                     `change_settings(@but1, t, @s1, 4, @s2, .05, @s3, .45,
                                      @s4, .05, @s5, .70, @s6, 1.0, "3.8", nil));

         but2 = gtk_radio_button_new_with_label(list(but1), "Poor");
         box3.pack_start (but2, TRUE, TRUE, 0);
         but2.signal("clicked",
                     `change_settings(@but2, t, @s1, 10, @s2, .05, @s3, .70,
                                      @s4, .05, @s5, .70, @s6, 5.00, "3.9", nil));

         but3 = gtk_radio_button_new_with_label(list(but1,but2), "Oscillating");
         box3.pack_start (but3, TRUE, TRUE, 0);
         but3.signal("clicked",
                     `change_settings(@but3, nil, @s1, 10, @s2, .05,
                                      @s3, 1.37, @s4, .05, @s5, .70,
                                      @s6, .55, "3.10", @ambutton));

         but4 = gtk_radio_button_new_with_label(list(but1,but2,but3),
                                                "Out-of-control");
         box3.pack_start (but4, TRUE, TRUE, 0);
         but4.signal("clicked",
                     `change_settings(@but4, nil, @s1, 10, @s2, 1.0,
                                      @s3, 3.0, @s4, 1.0, @s5, .70,
                                      @s6, .55, "3.11", @ambutton));

         button = new(GtkButton);
         button.label = "Exit";
         button.signal("clicked", `(@win_monitor).destroy());
         button.width = 40;
         box3.pack_start(button, TRUE, TRUE, 0);

         box2.pack_start (box3, TRUE, TRUE, 0);

         but2.set_active(TRUE);
         win_monitor.show_all();
         win_monitor.reposition(20, 250);
         win_monitor;
       }

       /*--------------------------------------------------------------------
        * Function:    main
        * Returns:     doesn't return
        * Description: Calls the program_startup() function and loops.
        *------------------------------------------------------------------*/
       function main()
       {
         program_startup("monitor", "monq", #create_monitor());

         /* Loop forever handling events. */
         gtk_main();
       }

Prev | Home | Next
GTK Functions (in `gtksimple.g`) | Up | Making Scales and Spin Buttons - `make_scale`, `make_prog_bar`, `make_spinner`

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