LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

SetTableCellRangeVals using for loading rows

I have read through previous posts on using SetTableCellRangeVals to load colums when data is non-homogeneous, but I need to do it in rows.
 
I have a structure that holds each row of data in respective data type, but I cannot seem to pass the required array pointer correctly to the function.
 
here is the structure.
 
struct ROW_MSG
{
 
 char DATE[10];
 char TIME[12];
 char SESSION[20];
 char CLASS[10];
 char CAR_NUMBER[10];
 char NAME[20];
 char LANE[10];
 double BUMP_TIME;
 double DIAL;
 double REACTION;
 double SIXTY;
 double THREE_THIRTY;
 double SIX_SIXTY;
 double SIX_SIXTY_MPH;
 double THOUSAND;
 double THOUSAND_MPH;
 double ET;
 double ET_MPH;
 double MOV;
 
 
};  
 

extern struct ROW_MSG row_msg, *row_msg_p;
 
and here is the call:
 
SetTableCellRangeVals (run_table_panel, RUNDATA_TABLE, MakeRect (row_count, 1, 1, 19), &((char *)row_msg_p),
          VAL_ROW_MAJOR);
 
here is the error:
 
 FATAL RUN-TIME ERROR:   "icard.c", line 536, col 96, thread id 0x0000015C:   Invalid argument type: found 'pointer to pointer to struct ROW_MSG', expected 'pointer to pointer to char'.
 
I have tried several variation of passing the pointer fo the structure to the fuction with no luck.
 
Thanks for you help
0 Kudos
Message 1 of 8
(9,996 Views)
Unfortunately, you cannot set a range of cells from a struct. You can only do so from an array, which means that you could only set a range of cells if the cells are all of the same type.

I mocked up some code that breaks it up into several calls (2 calls per row x 3 rows = 6 calls). The reason it is broken up into two calls per row is because your struct has two sections: a bunch of strings, followed by a bunch of doubles. If you changed this structure, you would have to change the code as well. Note that even this is not a very robust solution, since it assumes that there is no padding between each field in the structure. In your structure, that's a safe assumption, but again, if the structure changes you need to rethink this.

There are two additional caveats: 1. Because the CVI user protection expects an array, and not a structure, the code is disabling user protection for that function, otherwise you will keep hitting run-time errors when you run in debug mode. 2. The way you had your structure defined would not work at all, because you use static arrays for the string fields. The function expects an array of strings (consecutive 4-byte pointers), and not the actual arrays lined up in memory, back-to-back. In the sample code that I'm attaching, I changed them to char pointers, and assigned them to constant literals. But in your case, you will have to think about where the real string data is going to be allocated.

So there you have it. If you really want to do this, you probably can. But you might find that it's more trouble than it's worth. But go ahead and experiment with the attached code and decide what you want to do.

If you decide that you don't want to use this function and would rather use SetTableCellVal instead, I can recommend an alternative that should make your code go faster: the problem with SetTableCellVal is that is forces an immediate redraw of the table, which you probably don't want to do if you need to call it many times repeatedly. It will be slow, and will probably flash. But you can call SetTableCellAttribute (... , ATTR_CTRL_VAL,...) instead. It does the same thing but without the redraw. The only requirement is that the table has to be the active control at the time you call the function -- you can ensure this by calling SetActiveCtrl.

Hope this helps.

Luis
NI
Download All
Message 2 of 8
(9,986 Views)
I'm  sorry. I just realized that I attached a .uir that was saved in 8.0 format, which you might not be able to read with an older version of CVI. I'm reattaching the same .uir saved in 7.0 mode. I don't know which version of CVI you have. If it's older than 7.0, let me know and I can reattach a different one.

Luis
0 Kudos
Message 3 of 8
(9,983 Views)
Hi Luis, I saw this thread and your explanation has clarified some doubts I had on filling a table with strings. I just would like you to explain me one of your sentences.
 
You told that "The only requirement is that the table has to be the active control at the time you call the function -- you can ensure this by calling SetActiveCtrl." Does this mean that I cannot use this control on a hided panel? When I have to display some data, usually I load the panel, fill in the controls and then show the panel so that it is complete when the user sees the panel. Now it seems that I cannot do this if one of the controls to be filled is a table and I want ot use SetTableCellAttribute (... , ATTR_CTRL_VAL,...) for efficiency.

Can you give me a hint on this?
Thank you.


Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 4 of 8
(9,967 Views)
Sure, I'd be happy to clarify.

What I meant was, that if you want to use ATTR_CTRL_VAL instead of SetCtrlVal (or SetTableCellVal, etc...) because you do not want the immediate draw effect, then the control should not be active. If it is active, the function will still work, but it might draw immediately anyway.

Let me give you some background...

The way ATTR_CTRL_VAL works internally is quite simple: it hides the control first, then calls SetCtrlVal, then shows the control again. This way, because the control is hidden when SetCtrlVal executes, no drawing needs to take place. This is how we fool SetCtrlVal into not drawing at all. (Because of how the code is set up, it would have been a lot more work to disable drawing, individually, for all the possible types of contols). And since no drawing takes place, you never get to see the panel without the control either -- there's no flashing.

Now, when the control is active, if we were to hide it and then reshow it, focus events would be generated (EVENT_GOT_FOCUS and EVENT_LOST_FOCUS) for both that control and for the next control in the tab order, and since we'd rather not startle users with events that they might not be expecting, we don't hide the control in that case. There are other reasons, but this is one off the top of my head.

(And you know what? Just as I'm typing this I realize that in my previous post I wrote exactly the opposite of what I intended to write. I intended to write that the table should not be the active control, and I wrote the opposite instead. Sheesh... now I'm giong to have to follow up that post with a correction.)

In any case, to address your last question, you don't have anything to worry about. If the control is already hidden when you change its value, then it doesn't really matter how you set the value, since it won't draw, regardless. But if the control is visible, and is not active, then it will not draw if you use ATTR_CTRL_VAL. In that case, the UI will update the next time CVI processes events.

I hope I didn't end up making this more confusing than it was... Smiley Tongue

Luis
0 Kudos
Message 5 of 8
(9,956 Views)
Uh-ho. Looks like I stepped into it again. I need to correct something I wrote in my first post. When I wrote:

"The only requirement is that the table has to be the active control at the time you call the function -- you can ensure this by calling SetActiveCtrl."

I really meant to write:

"The only requirement is that the table cannot be the active control at the time you call the function -- you can ensure this by calling SetActiveCtrl."

Ah, well.... it's not as if I wrote the opposite of what I meant or anything... Smiley Wink

Luis
0 Kudos
Message 6 of 8
(9,955 Views)
Luis,
 
I decided to try and load it as Columns instead of rows.
 
I am now getting an error "Argument to small".
 
 SetTableCellRangeVals (run_table_panel, RUNDATA_TABLE, MakeRect (1, 9, 3, 1), &ulog_icard_msg_p[0].DIAL, VAL_COLUMN_MAJOR);
 
I defined the pointer as follows:
ulog_icard_msg_p = (struct ULOG_ICARD_MSG *)malloc (sizeof (struct ULOG_ICARD_MSG) * 10);
 
 ulog_icard_msg_p[0].DIAL=1;
 ulog_icard_msg_p[1].DIAL=2.2;
 ulog_icard_msg_p[2].DIAL=3.3;
 
so  believe to load a column 3 rows high shouldn't be aproblem?

 

Fred


   
0 Kudos
Message 7 of 8
(9,902 Views)
Fred,

There are a couple of issues here:

1. I had written the following in my original reply: "Because the CVI user protection expects an array, and not a structure, the code is disabling user protection for that function, otherwise you will keep hitting run-time errors when you run in debug mode". In the new code that you now posted, you are still not passing an array. You are instead passing the address of a field in a structure, so you will still have to disable user protection around this code, in order to avoid the error that you are seeing.

2. For your new code to work, the three doubles that you are setting must be next to each other in memory (typically, an array). You don't include the definition of struct ULOG_ICARD_MSG, but the only scenario under which this will work is if there is only one field in the structure. Only then will the three fields be stored consecutively in memory.

I am attaching the modified version of my original code, that now also sets this column in the table. I made up a definition of the structure that only contains the one field.

Having said this, I want to repeat my warning that it seems to be a bad idea to be using structures instead of arrays. It would be much better to do something like this instead (in which case, it doesn't matter how many fields are in the original structure):

tmpArray = (double *)malloc (sizeof (double) * 3);
for (i = 0; i < 3; i++)
    tmpArray[i] = ulog_icard_msg_p[i].DIAL;
SetTableCellRangeVals (panel, PANEL_TABLE, MakeRect (1, 9, 3, 1), tmpArray,
    VAL_COLUMN_MAJOR);
free (tmpArray);

Luis



0 Kudos
Message 8 of 8
(9,861 Views)