Re-think the easy ways you have used forever.
In Part I, I gave the rough outline of the task: how to manage over 12000 controls/indicators on one panel.
In Part II, we started whittling the task down to size, using sub panels and reentrancy.
Let’s think about reducing our memory usage, by some unusual techniques.
In LabVIEW, one of the easy ways to present a list of channels for the user to choose from, is to collect an array of strings (channel names) when the window comes up, and feed that array to the STRINGS[ ] property of a text ring or a menu ring and then just forget about it. It’s quick, it’s easy and it works. The user selects “Flatistrat Temperature” from a list, and may neither know nor care that he’s really specifying channel 13. From a user’s point of view, that is excellent.
However, recall from part I that a single block in our case has 51 channel selectors. Six of those on a page, and 12 pages makes 3672 copies of that list. Consider that we might have 300 channels, and each channel name is an average of 20 characters, and there is over 22 Mbytes of RAM, just holding copies of a single list.
There has to be a better way.
In my case, I was driven toward that better way by another requirement. When dealing with 300 channels, finding the one you want can be troublesome, since they are not necessarily added to the configuration in some sensible order. So we adopted the requirement that just clicking on the list would bring up the list in order of the channels’ hardware type (all SCXI channels, all ENET channels, all COUNTER channels, etc). SHIFT- clicking on the list brings up the list in order of the UNITS of the channels (Temp. channels, pressure channels, flow channels, concentration channels, etc.). CONTROL-clicking would bring them up in alphabetical order. Just reading this requirement ought to point the way to reducing memory usage: you simply cannot have 3672 copies of a single list and make it work. The list has to be dynamic.
The clue is in the fact that you don’t know what order the list should be in, until they actually click on the selector! Theoretically, you could watch the SHIFT and CTRL keys for changes and swap out ALL the lists on each change. That makes a lot of work, and doesn’t help the huge RAM burden.
So, we have to be smarter about it. We have to add more work to save all the RAM.
Recall that a TEXT ring and a MENU ring (your choice) has a property called STRINGS and VALUES[ ]. Normally, if you just assign an array of strings to the STRINGS[ ] property, the control internally assigns consecutive values to the array, so that “Flatistrat Temperature” produces the number 13, because that text is in position 13 in the array.
However, if you use the STRINGS and VALUES[ ] array, then you can assign your own values. If you want “A”, “B”, “C” to appear in that order, but produce values of 2, 1, 0, then use the STRINGS and VALUES[ ] property to do it. There’s nothing that says that “0” must be first. Carrying this even further – there’s nothing that says there has to be a zero at all – if you want “A”, “B”, “C” to mean 4000, 4001, and 4322, then this is how you do it – the VALUE can be completely disconnected from its position in the array.
Keep carrying out that line of thought and you realize that the selector only has to have one string and one value, WHEN IT IS SITTING AT REST. The only string it needs to hold is “Flatistrat Temperature”, and the only value it needs to hold is 13. So if it holds only one string and one value, then it’s not much of a menu is it?
Well, think about it. The only time you actually need a menu is when you pop up on it, right? The rest of the time the other (unselected) values don’t serve any purpose at all. That is the waste we are trying to reduce.
Now, connect that thought with the idea that you want to arrange the order of the menu based on whether the user holds down the SHIFT and/or CTRL keys at CLICK time, and you see that they mesh perfectly!
In summary, what we need to do is to:
- At INIT time, create a list of channel {Name|Value} pairs for every contingency: one list for “normal” , one for the SHIFT case, one for the CTRL case, one for the CTRL-SHIFT case. You could also use the OPTION (Alt) key for more choices, but be aware that Windows 7 uses ALT-click on a control for its own purposes and LabVIEW doesn’t respond the way you might expect. But still, there are four cases for you to use.
- At CLICK time, attach the correct menu to the control.
- At CHOOSE time, reduce the menu back to the chosen string | value pair (discard all the unselected ones).
The CLICK time handler is not difficult. You need to:
- Intercept the MOUSE DOWN? event on the control (this happens before the menu pops up). The event data contains a PLATFORM MODS cluster which tells you which modifier keys (SHIFT, CTRL, etc) are down at the time of the click.
- Use the MODS to determine which list should be showing.
- Read the current value of the control, you’ll see why in a moment.
- Set the STRINGS and VALUES property of the control to whichever list is specified by the MODS.
- Set the VALUE of the control to what you read before. Before, it showed “Flatistrat Temperature” with value 13, this is at index 0. Since the control is not really aware that you are shuffling things behind the scenes, it will pop up the menu showing the “current value” as whatever is in index 0, unless you do this.
- That’s it! The menu will show whichever order you chose, based on the MODS that are down.
If you stopped right here, it would work, but it would leave the menu fully populated with all 300+ channels. Pretty soon, all the menus are back up to full, and you haven’t gained anything.
So, you also want to attach another handler to the VALUE CHANGED event. In that handler:
- Extract the Current TEXT (“RingText.Text”) and the current VALUE.
- Combine those into a cluster and make an array of that single cluster.
- Set the control’s STRINGS and VALUES[ ] array with it. That erases the full list of channels and restores the control to only one value.
- Set the VALUE to the current value you just read. This is for the same reason as on the CLICK handler.
This means that when you change the value, the list is restored to a single name|value pair, and all the waste is trimmed.
There is still a flaw in this: If you pop up the menu and either choose the current item, or click off the menu then the menu pops down, but you do not get a VALUE CHANGED event, because the value didn’t change. That means the full list will be left in the control. It’s wasteful, but not harmful. There is not a good way of handling that (that I have found), so I just live with it. It’s not usual behavior, but it can and does happen.
Note that the INCREMENT/DECREMENT buttons can’t be used: the “next” and “previous” channels are undefined.
For a LV2013 project which demonstrates these ideas, click here.
For a LV2010 project which demonstrates these ideas, click here.
But that’s not all.
Stay tuned for Part IV
can you post a 2012 version of the monster panel
Thank you very much
I can’t post the whole panel – that is work done for a customer and would not be right. I did post the example, however, in LV2010 format.