Transcending Tab Controls
When building LabVIEW user interfaces there comes a point where a single VI's user interface won't hold all of the content your program needs to display. Separating the content into different windows is an option, but the tool many of us turn to is the reliable tab control.
Tab controls can be great, but they have limitations, including:
- They keep all of the controls and indicators in a single user interface, cluttering the block diagram
- They tangle source code control because a change to any tab alters the single file
- They prevent you from distributing development work to multiple developers
- They make reuse challenging, because you need a separate copy of each instance of a tab if you need the same interface for multiple things
- They have inflexible tab label positioning and formatting options
- You can't add splitters inside a tab control, so resizing the UI is a challenge
When you're ready to move past the tab control, we have found an easy next step to have a similar interface. This interface splits "tabs" into separate VIs, addressing all of the above challenges.
How to Build a Radio Button Controlled Subpanel UI:
Start with a radio button
A radio button handles all of the logic for setting the previous selection false and returns a data type like an Enum, so it self-documents well.
- Right click on the front panel » Modern palette » Click Boolean palette » Click Radio Button » Left click on the front panel
- Ctrl-space to launch quick drop » type "ra" » left click on the front panel
Pick better buttons
We want to replace the boolean controls in the radio button with something that highlights when the mouse hovers over it. The System Button is an example of something that highlights. I'm going to use a semi-transparent control that I have included in the .zip file example. Because of the way a radio button returns the label of the selected control, the buttons inside it will all need different label text for the radio button to work. Replacing the buttons is just like replacing controls within a cluster.
- Right click on the front panel » Click "Select a Control" » Browse to Transparent Background.ctl and double click it » Left click inside the radio button frame
- Ctrl-space to launch quick drop » Type "sac" and hit enter » Browse to Transparent Background and double click it » Left click inside the radio button frame
- Delete the Radio Selection buttons in the frame
- Ctrl-left drag the new button to create copies of it. Place them in the radio button frame.
- Arrange the buttons vertically by right clicking the frame of the radio button and selecting AutoSize » Arrange Vertically
- Change the labels of each copy so they're all unique
Apply some style
Let's clean these buttons up by hiding labels and making the radio button frame and background transparent. Labels and boolean text are different because if the label is over the button, clicking on the label doesn't change the button state. Clicking on boolean text lets the click change the button's state.
- Shift-right click on the front panel to bring up the tool picker » Click on the paintbrush
- Right click on the radio button frame » Hit space to change to the secondary color » Select the T in the top right of the color picker for transparent
- Right click on the radio button background » Select the T in the color picker
- Right click on the front panel background » Select some pretty color
- Hit tab to bring back auto-tool
- Left drag across all of the buttons. As long as you don't select the whole radio button frame, you'll select just the buttons.
- Right click on one of the selected buttons » Select Visible Items » Deselect Label
Add the Subpanel
To provide some division between the menu and the panels that are displayed, we'll add a vertical splitter to the right of the radio button. I like to make the subpanel frame transparent and hide the label.
- Right click on the front panel » Classic palette » Layout palette » Click on Vertical Splitter Bar (Classic) » Left click in the middle of the front panel
- Ctrl-space » Type "cl v sp" » Left click in the middle of the front panel
- Let's color the splitter and right pane by selecting the paint brush tool like we did in the previous section and right clicking on each. I picked a gray.
- Drop a subpanel from the modern palette or Ctrl-space » "sub" » Left click in the right pane
- Right click on the subpanel frame » Select Fit Control to Pane
- Use the paintbrush tool to make the subpanel's frame transparent the same way we did for the radio button
Create a VI for each menu selection
Here's where the benefits of subpanels kick in. We need to build some VIs that each contain a separate user interface. For this practice, you can just drop some random controls on each panel. We'll put each of these VIs on the block diagram of the Menu.vi so they're running when we place them into the subpanel. I've included some samples in the .zip file. In each of these front panels I'll set their scroll bars to Off While Running so there aren't scroll bars showing when they're loaded into the subpanel.
- Right click on either scroll bar » Select Horizontal and Vertical Scroll Bar » Off while running
Add some execution logic
Because each of these VIs will be running the whole time, we'll need a way to exit them when the main user interface closes. We'll use a queue reference for this, destroying the queue in the host panel to signal the nested panels to close.
- In the Menu.vi's block diagram hit Ctrl-Space » Type "ob" » Left-click on the block diagram
- Ctrl-Space » Type "fa" » Left click to the left of the Obtain Queue
- Wire the False Constant to the Element Data Type terminal of the Obtain Queue
- Ctrl-Space » Type "rele" » Left click on the block diagram
- Wire the queue reference from the Obtain Queue to the Release Queue
- Create a constant on the Release Queue's Force Destroy terminal and set its value to True
- Create a constant from the queue reference wire and Ctrl-x to cut it off this diagram and Ctrl-v in the nested panel's diagram to paste it
- Ctrl-space » Type "ws" » Left drag to make a while loop on this diagram
- Inside the while loop Ctrl-space » Type "deq" » Left click to place Dequeue Element
- Next to that place an Or with Ctrl-Space » Type "or" » Left click
- Wire the dequeued element and error into the Or and wire that to the conditional terminal of the while loop
- Add the queue reference to the connector pane by clicking a connector pane terminal on the front panel and then clicking the queue reference control
Wire it all up
Now we just need to build an array of references to these nested panels that we can place in the subpanel when we want them to be displayed. Static VI references let us do this.
- Place the four nested VIs on the block diagram
- Wire the queue reference to the subvis
- Add a while loop
- Add an event structure with Quick Drop » Type "evstr" » Left drag
- Change the Application: Timeout case in the case structure to handle the Front Panel Window: Close? event. Place a true constant there and wire it to the Discard? terminal as well as the conditional terminal for the while loop.
- Make sure that the queue reference is wired through the while loop so the nested VIs don't close until the program is done executing.
- Drop four static VI references with Quick Drop » Type "stat" » Left click
- Drag each of the nested VIs into a static VI reference and wire the references into a build array.
- Add a case to the event structure to handle the Radio Button value change.
- In the new case place an Index Array with Quick Drop » Type "nd" » Left click
- Drag the subpanel's Insert VI invoke node into the case and wire the array and radio button to the index and the indexed element to the VI Ref terminal on the invoke node.
Now you can run the menu VI and see how the menu selections change which panel is displayed.
This is a slick way to move beyond tab controls, but stay tuned for how to enhance it by:
- Using something better than a queue reference to exit the nested loops
- Having the user interfaces only run when shown
- Make resizing and turning scroll bars off automatic