LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Tip: UpdateAllControls()

Maybe this is something which everybody does already, or maybe it isn't handy at all, comments welcome.

When I started out writing CVI programs I tried to only update the controls that were changed, right from the routine that changed them. However this sometimes yielded unexpected results, as some changed values updated other values, which in turn weren't updated on the display. It can be some work to keep track of all your values and to make sure that what you see on the screen is the actual value that the program uses internally.

I solved this problem by creating the UpdateAllControls(void) function.

The function basically is a long list of SetCtrlVal and SetCtrlAttribute commands. Below is a very trimmed version from one of my programs:

void UpdateAllControls(void)
{
SetCtrlVal(Leis.MainPanel, LEISPANEL_PHYDASCONNECTED, PhyDAS.Connected);
SetCtrlVal(Leis.MainPanel, LEISPANEL_NUMBEROFWINDOWS, Leis.NrOfWindows);

if ((Leis.Mode == LEIS1) || (Leis.Mode == QTOTAL)) {
SetCtrlVal(Leis.MainPanel, LEISPANEL_ENERGYLOW, Leis.Ebegin);
SetCtrlVal(Leis.MainPanel, LEISPANEL_ENERGYHIGH, Leis.Eend);
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYLOW, ATTR_LABEL_TEXT, "ION energy low (eV)");
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYLOW, ATTR_PRECISION, 0);
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYHIGH, ATTR_LABEL_TEXT, "ION energy high (eV)");
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYHIGH, ATTR_PRECISION, 0);
SetMenuBarAttribute (Leis.MenuBar, MENU_MANIP_TRACKWIDTH, ATTR_DIMMED, !(Manip.EnableAreaScanning));
}
if (Leis.Mode == XPS) {
SetCtrlVal(Leis.MainPanel, LEISPANEL_ENERGYHIGH, Leis.XPS_BE_End);
SetCtrlVal(Leis.MainPanel, LEISPANEL_ENERGYLOW, Leis.XPS_BE_Start);
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYLOW, ATTR_LABEL_TEXT, "Binding energy low (eV)");
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYLOW, ATTR_PRECISION, 1);
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYHIGH, ATTR_LABEL_TEXT, "Binding energy high (eV)");
SetCtrlAttribute (Leis.MainPanel, LEISPANEL_ENERGYHIGH, ATTR_PRECISION, 1);
}

if (Manip.Connected) SetCtrlAttribute (Leis.MainPanel, LEISPANEL_MANIP_X, ATTR_VISIBLE, 1);
else SetCtrlAttribute (Leis.MainPanel, LEISPANEL_MANIP_X, ATTR_VISIBLE, 0);

SetCtrlVal(Leis.MainPanel, LEISPANEL_EPRIM, Leis.Eprim);
SetCtrlVal(Leis.MainPanel, LEISPANEL_TIMEPERWINDOW, Leis.TimePerWindow);
...
}

After every function which causes variable values to change, I give the UpdateAllControls() command and I don't have to think about which controls should be updated and which ones not. In the UIR files I give start values like 1234, so I can always see, when my program is started, whether the initial values were updated by the program or not (because most initial values will not be 1234).

Disadvantages of this approach:
- The variables used must be global, at least to the file which uses UpdateAllControls()
- Updating the display may become slower because a lot of controls that don't need updating are updated anyway.

I deal with the global variables by putting them into a large structure, so I only have to "extern" the one structure.
I didn't yet see a panel that got updated noticeably slow, though it may become noticeable on slow computers.

Every panel can have its own UpdateAllControls(), like UpdateMainPanel(), UpdatePressurePanel(), etc etc...

Just a thought, any comments on this approach welcome.
0 Kudos
Message 1 of 5
(3,324 Views)
This might help or might not. Some testing would be nice from your end to verify...
Comment out your current (large update all function) and instead try subbing in this:

==========
ProcessDrawEvents()


When your program executes in a callback function or in code that does not call RunUserInterface or GetUserEvent, LabWindows/CVI does not update the user interface. Functions that are overly time-consuming can "lock out" user interface updates. To allow LabWindows/CVI to process these updates, call ProcessDrawEvents.

If you call this function in a multithreaded program, only the panels created in the current thread are guaranteed to be updated fully.
===========

This function should update all the controls on the user interface saving you some code...
0 Kudos
Message 2 of 5
(3,322 Views)
ProcessDrawEvents would be fine if all you need is updating the values, but I use it also to determine the state of menu items (checked/dimmed) or label text, depending on the measure mode the program is in. Instead of setting this in the routines where the mode change takes place, I just do the mode change and then call UpdateAllControls().

It forces me to think about how I want the UI to look dependent on the state the program is in, and then program it into one routine. After this, I never have to think again about which controls/menu items should be dimmed when, the one routine does it all.
0 Kudos
Message 3 of 5
(3,317 Views)
Hi,

An easy way to check which menu items should be dimmed is to assign a callback function to the menubar containing all the menu items.  Global variable flags can then be checked on the fly as the user navigates the menu to control the dimmed state and other aspects of the menu items (check marks etc.).
e.g.
void CVICALLBACK CallBackFunction (int menuBarHandle, int panelHandle)
{

 SetMenuBarAttribute (menuBarHandle, menu_item, ATTR_DIMMED, Global_associated_flag);
// etc.
}

John.

0 Kudos
Message 4 of 5
(3,146 Views)
When you have to show a large quantity of elements on the screen the strategy used to display them can influence program performance and let an application be well accepted to the user.
 
I usually tend to divide the elements on the screen in two groups: those that need to update frequently (two times per second or more) and those that vary more slowly. The latter group can be updated only when some element is actually modified, while the first one needs to be updated regularly with a function similar to Erwin one. I usually use SetCtrlAttribute (... ATTR_CTRL_VAL... ); instead of SetCtrlVal since it should be more efficient because the screen is updated only once when the callback concludes.
 
In my opinion another solution to improve program performances could be to organize elements on a table. The table can be updated more efficiently provided that:
1. It is hided before updating and showed after: no control flashing will be seen if this is done entirely in the same callback. The table must not be the active control, which is always true for indicator controls but must be checked for hot or normal ones
2. Data are organized so that the table can be updated in blocks (rows, columns or rectangular areas)
This can have a great impact on the application since it influences both the user interface and the data structure: using a table can be practical when there are large seriese of similar elements but can be confusing in other environments. On the other hand, data needs to be internally organized in arrays that can be rapidly moved to the table in one instruction.
 
 
As per menu update, I definitely second JAH assertion about InstallMenuDimmerCallback function (I suppose this is the function John refers to): after I discovered it I moved all menu update to this function since it is executed if and only when the user shows the menu: an appropriate set of global flag can de used to hold the desired status of menu items during program life, but they are actually updated only before the menu is shown.
 
 
Finally, I want to thank Erwin for placing this argument on the board: this resource should be used not only to discuss immediate problems but also to share personal solutions to common problems. It is always possible that some else's points of view show us new ways to approach application development.


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 5 of 5
(3,128 Views)