03-16-2013 10:13 AM
Hi -
Still very new to CVI here.
I'm designing a small application that will accept data from various sources (file, socket) and plot the data on a graph. I read in the online help how the variable
void *callbackdata
can be used to avoid the need for global variables. But I don't fully understand this -- since the program isn't directly calling these callback routines, how does it specify/access the data location here?
Maybe if someone would be kind enough to provide a brief example, the light would come on for me. Thanks.
Solved! Go to Solution.
03-16-2013 10:38 AM
Hi,
since you are referring to the help you may have also seen the line
You can find examples of callback data in the toolslib control code. Browse through the code in toolslib\custctrl\fileBrowser.c. In the code, callback data is created in the FileBrowser_Create function.
Important to note is that callbackData is a pointer to some sort of memory - so your function needs to know in advance what kind of memory address is passed as a pointer, e.g. if it is of type double, char, etc. This is your responsibility...!
Note that you can also use the Search of this forum - callbackData is a frequent issue See for example here or here
03-16-2013 09:02 PM
Hi, Wolfgang -
The online help is pretty sparse when it comes to explaining terms like "file browser" and "tree control." Could I trouble you for a brief description of what these structures are?
I'm not sure that the file browser is right for me, because i'm really doing very little with files. Here's a little more detail on what I'm trying to do:
When a user pushes a button on the UI, I want the callback to open a file (selected by a popup menu), read in some data, and populate an array with this data. It might then send a signal to the main routine that new data is now available. The main routine will then plot the data on a graph.
The part I'm missing is how to tell the callback function where to store the information. I see that the callbackData pointer is used for this, but...how does this get initialized to point to something accessible by the main routine?
If you could help me with this, I think I can get this thing mostly going. Thanks for any assistance.
mz
03-17-2013 07:08 AM
Hi,
first of all, I cannot assist you with the file browser and the tree control, I have used none of them.
For loading a data file I use a regular string control which after successful file selection displays the selected file. The callback is
switch ( event )
{
case EVENT_LEFT_DOUBLE_CLICK:
ImportFile ();
I am not using the callbackData because I also have a menu entry that allows loading a data file, and in the menu callback I call the same function ImportFile. This function contains a call for FileSelectPopup:
selection_status = FileSelectPopupEx ( "", default_file_spec, file_type_list, title, VAL_SELECT_BUTTON, 0, 0, path_name );
and code to read the file, display the file name, analyse the data, display it on a graph etc. I am happy with it although I don't use callbackData 🙂
03-17-2013 08:57 AM
I can think to a scenario in which the main callback holds a double or other array with data, passes it to a callback that reads a file and fills in the array and then displays data on a graph. In this scenario, the pointer to the array could be installed as callbackData to the button casting it to (void *). The button callback could then allocate the array and fill it in with file data. As wolfgang said, it's up to you to guarantee that the called callback casts the pointer to the correct data type.
You have two problems in this scenario: first of all, you must store somewhere the lenght of data array for other routines to correctly manipulate it; second problem, you must create a way to infom the main routine that the called one has terminated so that it can display the data, since if it's a control calback it is not called directly from the main ().
The second item can be solutioned by having a separate routine to display data and call it both from the main and the button callback. The routine must receive at least the panel handle, the pointer to the array of data and its lenght.
03-17-2013 09:35 AM
Thank you both for the replies. Roberto, I have a couple of follow-up questions:
I can think to a scenario in which the main callback holds a double or other array with data, passes it to a callback
Why would one callback need to call another? Wouldn't it be adequate to simply call a plain function?
You have two problems in this scenario: first of all, you must store somewhere the lenght of data array for other routines to correctly manipulate it;
Yes, I think I'll define a structure containing the array, a length field and a bool to indicate whether the information is new or not.
The second item can be solutioned by having a separate routine to display data and call it both from the main and the button callback.
Ahh...this sounds promising. So, I'd have a display_data() that would contain a static array for the data? Is that what you're suggesting? This makes sense, and I think would get me where I need to go. Clever idea...thanks.
Now...does CVI offer mutexes or anything? I may need this as I might need to guard against two sources (a button push and a network operation) trying to load the buffer at the same time.
This is very encouraging...again, thanks for the help.
03-17-2013 05:22 PM
When designing your application you must decide whether your data array (or data structure, as you have supposed) is defined at a global or function level. If it is global to the whole app you have no need to struggle about how to pass data between functions, but if it is defined at function level (e.g. in the main function) every other function that must handle data must be informed of it. The button callback that is in charge of filing the data structure can receive its pointer in callbackData, the only parameter the programmer can freely use, while user defined functions (like your display_data function) can receive it directly as a parameter.
To protect your data structure from concurrent access to it you can for example define a 'busy' flag inside the structure itself: every time you want to modify data you must verify that busy flag is not set and set it as the fist thing if free. Alternatively you can use locks from the utility library calling CmtGetLock or CmtTryToGetLock to verify that data structure is free or locked.