Thursday, November 24, 2011

How to Use Callback Functions - Part 1

 
Written by Tal Alon - November 2011

In this post we explain how to create the link between UIR elements and your code and how to make stuff happen.


In CVI, every control (button, switch, textbox etc.) placed on a panel can be associated with a callback function placed inside one of the project's code files.

A callback function associated with a control will be called-upon whenever that control experiences an "event". An event can be a mouse click, double click, right click, hovering with the mouse over the control,  inputting a value into the control etc... Any triggered event will automatically call upon the associated callback-function, and the code you write inside that function will be executed. Later on we will learn explain how to react to specific events while ignoring others, but first we should understand how to link a control with a callback function.

Associating a Function with a Control

To associate a function with a control, first double click it (in edit mode) and fill in the 'Callback Function' text box found near the top of the properties screen (see step 1 in the figure below). The name you type in will be the name of the callback function that will soon be created. Make sure that the 'Control Mode' (step 2 in the below figure) is set to 'Hot' and not 'Indicator' as indicator controls do not trigger any events (you may, for the sake of simplicity, ignore the 'Normal' and 'Validate' modes).


Now exit the control-edit screen and right-click on the control, selecting 'Generate Control Callback' (see step 3 in the figure below). CVI will sometimes prompt you for the destination file, and in most simple programs you should choose your main code file. Note that generating the main code file (via 'Code'-->'Generate All Code' in the menu bar) will create, in addition to the 'main' function, every callback function associated with a control on the GUI, and then you do not need to follow this 3rd step.


Go to the code file and look for the newly created function - it should initially look something like the this:

int CVICALLBACK calculate (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
break;
}
return 0;
}


Reacting to Events with Our Written Code

Now we want to add real functionality to the callback function, and we have to understand events.

As previously mentioned, any triggered event will automatically call upon the associated callback-function, and it is important to realize that each such event has its own, unique, representing index (number) - for example, when the user hovers over the control with the mouse courser, event number 33 is triggered, this event is defined inside the included 'userint.h' file in the following way:

#define EVENT_MOUSE_POINTER_MOVE        33

If the user clicks on a control (a numeric field for example), event number 3 is triggered, defined as:

#define EVENT_LEFT_CLICK                3

And the most common event, which is the default in most callback functions, is triggered when the user commits on a change (pressing enter after entering a number, flicking a switch, click&unclick a button etc.). This event is cataloged as event number 1 and is defined as:

#define EVENT_COMMIT                1

Now you may have noticed that inside the generated callback function there is a switch statement:

        switch (event)
{
case EVENT_COMMIT:
break;
}

The switch uses the 'event' variable (which is one of the arguments of the callback function), enabling us to easily differentiated between events. If we like the program to react to an EVENT_COMMIT event (this is the most common event) then we would fill in the appropriate area in the switch statement, for example:

int CVICALLBACK calculate (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
        double mass;
switch (event)
{
case EVENT_COMMIT:  //When the user enters a number the energy will be calculated
                        GetCtrlVal(panelHandle, PANEL_MASS, &mass);
                        SetCtrlVal(panelHandle, PANEL_ENERGY, mass*light_speed*light_speed);
break;
}
return 0;
}

Notice that in the above example I have used the defined name EVENT_COMMIT instead of the number 1. You should always use the defined names, and you need not remember the numbers behind them.

If we want to react to different events, we may add cases to the switch statement, the next addition to the code, for example, zeroes the mass value if the user right click on the numeric control:

int CVICALLBACK calculate (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
        double mass;
switch (event)
{
case EVENT_COMMIT:  //When the user enters a number the energy will be calculated
                        GetCtrlVal(panelHandle, PANEL_MASS, &mass);
                        SetCtrlVal(panelHandle, PANEL_ENERGY, mass*light_speed*light_speed);
break;

                case EVENT_RIGHT_CLICK:  //When the user right clicks the mass will be initialized
                        SetCtrlVal(panelHandle, PANEL_ENERGY, 0.0);
break;
}
return 0;
}

Now, when we compile and run the program, our code will selectively run at the appropriate times.

Part 2 of the Callback Function Tutorial will be posted soon and will deal with the callback function's arguments 'panel', 'control' and 'event'.

Part 3 will deal with the rest of the arguments 'callbackData', 'eventData1' and 'eventData2'.

2 comments:

  1. Hi

    How can I make a counter (the numeric will display how many times i have clicked on a command button)?

    ReplyDelete