Complete program - gtksimple.g

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

# 7.7. Complete program - `gtksimple.g`

    /*--------------------------------------------------------------------
     * File:        gtksimple.g
     *
     * Description: Cogent Tools Demo Tutorial One for GTK.
     *
     * Functions:
     *              start_qnserves
     *              program_startup
     *              reset_value
     *              write_data
     *              set_label
     *              table_labeler
     *              toggle_sym
     *              make_scale
     *              make_prog_bar
     *              make_spinner
     *              change_settings
     *              create_monitor
     *              main
     *------------------------------------------------------------------*/

    /********************************************************
     *                     STARTING UP                      *
     ********************************************************/

    /*--------------------------------------------------------------------
     * Function:    start_qnserves
     * Returns:     t on success, or nil
     * Description: Used to start qserve or nserve, as specified.  Called by
     *              the program_startup() function.
     *------------------------------------------------------------------*/
    function start_qnserves(command, name)
    {
      for(i=0; i<=50; i++)
        {
          if ((_os_ == "Linux") || (_os_ == "QNX4"))
            if ((qnx_name_locate(0, name, 0) == nil))
              {
                system(command);
                usleep(10000);
              }
            else
              {
                i = 50;
                nil;
              }
          else
            if (access(string("/dev/", command), 0) == -1)
              {
                system(command);
                usleep(10000);
              }
            else
              {
                i = 50;
                nil;
              }
        }
    }

    /*--------------------------------------------------------------------
     * Function:    program_startup
     * Returns:     t or nil
     * Description: Starts any necessary Cogent software and the main window.
     *              Called by the main() function.
     *------------------------------------------------------------------*/
    function program_startup (proc_name, queue_name, win_fn)
    {
      local window, tsk;

      /* See if qserve and nserve are running. If not, start them. */
      start_qnserves("qserve", "qserve");
      start_qnserves("nserve", "sc/nserve");

      /* Start interprocess communication. */
      if (init_ipc(proc_name, queue_name, "toolsdemo") == nil)
        error("Could not initialize IPC.");

      /* See if a DataHub is running in the toolsdemo domain.
         If not, start it and a DataHub Viewer. */
      if ((tsk = locate_task ("/dh/toolsdemo", t)) == nil)
          system("datahub -d toolsdemo");
      else
        close_task (tsk);

      if ((pid_dhview = fork()) == 0)
        {
          /* The child process is the DataHub Viewer. */
          exec ("dhview", "-d", "toolsdemo");
          /* In case the above exec commands fails, the following will be
           * called, and it's virtually guaranteed not to fail.  Thus in
           * any circumstance a child process gets created.*/
          exec("/bin/true");
        }
      princ("The process ID of the DataHub Viewer is: ", pid_dhview, "\n");

      /* Make sure the PID Emulator is not already running. */
      if ((tsk = locate_task ("emul", t)) != nil)
        {
          send(tsk, #exit_program(0));
          close_task (tsk);
        }

      /* Start the PID Emulator. */
      if ((pid_emul = fork()) == 0)
        {
          /* The child process is the PID Emulator. */
          exec ("gamma", "emul");
          exec("/bin/true");
        }
      princ("The process ID of the PID Emulator is: ", pid_emul, "\n");

      /* Kill child processes when this program exits. */
      atexit(`kill(pid_dhview, SIGINT));
      atexit(`kill(pid_emul, SIGINT));

      /* Create and register some points with the datahub. */
      SP_001 = register_point (#SP_001);
      PV_001 = register_point (#PV_001);
      MV_001 = register_point (#MV_001);
      AUTO_001 = register_point (#AUTO_001);
      FREQ_001 = register_point (#FREQ_001);
      PID1_Kp = register_point (#PID1_Kp);
      PID1_Ki = register_point (#PID1_Ki);
      PID1_Kd = register_point (#PID1_Kd);
      PROP_001 = register_point (#PROP_001);
      INT_001 = register_point (#INT_001);

      /* Create the main window, and put in a destructor function for GTK */
      window = eval(win_fn);
      window.signal ("destroy", #exit_program(0));
    }

    /********************************************************
     *                     THE MONITOR                      *
     ********************************************************/

    TRUE = 1;
    FALSE = 0;

    /*--------------------------------------------------------------------
     * Function:    reset_value
     * Returns:     t or nil
     * Description: Resets the value of a widget's adjustment.
     *------------------------------------------------------------------*/
    function reset_value(widget, newval, adj_id)
    {
      local newadj;
      newadj = widget.get_adjustment();
      if (adj_id)
        {
          gtk_signal_handler_block (newadj, adj_id);
          newadj.set_value(100 - newval);
          gtk_signal_handler_unblock (newadj, adj_id);
        }
      else
        newadj.set_value(newval);
    }

    /*--------------------------------------------------------------------
     * Function:    write_data
     * Returns:     t or nil
     * Description: Reads a widget that contains an adjustment value,
     *              and writes its value to the datahub.
     *------------------------------------------------------------------*/
    function write_data(widget, !ptname)
    {
      local ptval;
      ptval = widget.get_adjustment();
      write_point(string(ptname), ptval.value);
    }

    /*--------------------------------------------------------------------
     * Function:    set_label
     * Returns:     t or nil
     * Description: Sets the label of a button according to
     *              the value of a database point.
     *------------------------------------------------------------------*/
    function set_label(button, sym, onvalue, offvalue)
    {
      if (eval(sym) != 0)
        button.label = onvalue;
      else
        button.label = offvalue;
    }

    /*--------------------------------------------------------------------
     * Function:    table_labeler
     * Returns:     t or nil
     * Description: Creates a label and puts it into a table.
     *------------------------------------------------------------------*/
    function table_labeler(table, txt, left, right, top, bottom)
    {
      local label;

      label = new(GtkLabel);
      label.set_text(txt);
      table.attach_defaults(label, left, right, top, bottom);
    }

    /*--------------------------------------------------------------------
     * Function:    toggle_sym
     * Returns:     t or nil
     * Description: Toggles a database point between 0 and 1 when a button
     *              is pressed, and calls set_label() to change the label
     *              accordingly.
     *------------------------------------------------------------------*/
    function toggle_sym(button, sym, onvalue, offvalue)
    {
      set (sym, eval(sym) == 0 ? 1 : 0);
      write_point (sym, eval(sym));
      set_label(button, eval(sym), onvalue, offvalue);
    }

    /*--------------------------------------------------------------------
     * 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));

      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);

      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;
    }

    /*--------------------------------------------------------------------
     * 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);

      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);
    }

    /*--------------------------------------------------------------------
     * 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;
    }

    /*--------------------------------------------------------------------
     * Function:    change_settings
     * Returns:     t or nil
     * Description: Changes PID settings in the Monitor, and optionally
     *              unclicks the Auto button if it is on.
     * Note:        In the Demo, this function is a Library function, found
     *              in lib/linux.g.
     *------------------------------------------------------------------*/
    function change_settings(button, auto, sp1, v1, sp2, v2, sp3, v3, sp4,
                             v4, sp5, v5, sp6, v6, msgno, autobutton?)
    {
      if (button.get_active() == TRUE)
        {
          if (auto == nil)
            {
              if (AUTO_001 != 0)
                autobutton.clicked();
            }
          else
            sp1.set_value(v1);
          sp2.set_value(v2);
          sp3.set_value(v3);
          sp4.set_value(v4);
          sp5.set_value(v5);
          sp6.set_value(v6);
        }
    }

    /*--------------------------------------------------------------------
     * 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);

      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);

      /********************************************************
       *                   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);

      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);

      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);

      /********************************************************
       *                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);

      /********************************************************
       *        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
Label Functions - `set_label`, `table_labeler` | Up | Photon Functions (in `phsimple.g`)

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