A Powerful New Tool for UI Programming--User Interface Event Programming
Overview
Since the beginning, LabVIEW programmers have written user interfaces (UIs) in essentially the same way, using a technique commonly known as polling. A user interface VI would read all controls on its front panel in a loop, checking each one to see if it had changed since the last iteration. Over the years, clever users have developed advanced tricks and techniques to make UI programming easier, such as grouping together Boolean controls into invisible clusters so they can be tested in fewer operations, or using queued state machines to manage the overall state of the UI. However, the underlying mechanism has remained the same since LabVIEW's inception. Unfortunately, this mechanism has a few inherent problems. Polling is inefficient because every control must be tested repeatedly, regardless of whether it has changed or not. Poll too fast and you dominate CPU time. Poll too slowly and you might miss something, or be unable to determine the order in which multiple user actions occurred.
Fortunately, LabVIEW 6.1 introduces a new feature that makes UI programming both more efficient and significantly easier: the Event Structure.
Table of Contents
What is an Event Structure?
The Event Structure, which is the first new structure node since LabVIEW 1.0, makes it possible to do event-driven programming in LabVIEW. Event-driven programming is a popular paradigm for managing user interfaces in other language environments such as LabWindows/CVI and Visual Basic. With event-driven programming, your application can sleep until something "interesting" happens on the front panel, rather than having to repeatedly poll for such activity yourself. LabVIEW takes advantage of features in the operating system to be notified when user interface activity occurs, so the OS can give the CPU to other programs running on your computer while your application is idle. Of course, what qualifies as interesting is up to you, and you can configure the Event Structure to listen only for the events that are important for your application.An Event Structure is kind of like a cross between a Wait on Occurrence function and a Case Structure. Like a Case Structure, the Event Structure contains multiple subdiagrams, each of which is configured to handle one or more events, which are user actions such as Key Down or Mouse Move. You drop an Event Structure on your block diagram the same way you would any other G object, and it executes according to the normal data-flow rules. When LabVIEW executes the Event Structure, it puts the VI to sleep until one of the events it is configured to listen for occurs, just as a Wait on Occurrence sleeps until an occurrence is fired. When an event of interest happens, the Event Structure automatically wakes up and executes the appropriate subdiagram to handle that event. Each subdiagram has an Event Data node affixed to its inside left border that allows access to specific information about the event that occurred. The node looks and works like an Unbundle by Name function, so you can resize it and select only the data
fields you need.
See Also:
Learn more about LabVIEW 6.1
A Simple Example
Figures 1a and 1b show a simple VI that uses an Event Structure to monitor "OK" and "Cancel" buttons on the front panel, and brings up a confirmation dialog when the user attempts to close the panel.

Figure 1a: Simple Event Loop
![]() | ![]() |
Figure 1b: Nonvisible Event Structure Cases
Notice that the Event Structure is used in a While Loop because when the Event Structure executes, it will only wait for and handle exactly one event. Always remember to place your Event Structure in a loop to repeatedly process events. Failure to do so can cause your VI's front panel to hang if an event occurs a second time while the VI is running, but the Event Structure has completed once and is not called again. This is because when an event occurs that any Event Structure on a VI's block diagram is configured to handle, LabVIEW will lock the front panel until some Event Structure has handled the event. This is done to ensure that events are never lost and are always processed in the order they are received. You can avoid the hanging scenario by clearing the "Lock Panel" option, but this might produce other undesired results. The key is to place your Event Structure in a loop.
The user actions that generate events include mouse button presses and releases, key presses and releases, control value changes, menu selection, and a VI front panel window closing, among others. You select which events you want to monitor by using a configuration dialog that lets you choose one or more events to associate with each subdiagram. These events are divided into two classes: Notify Events and Filter Events. A Notify Event is a simple after-the-fact notification that some-thing happened on the front panel. For example, the Value Changed events for the OK and Cancel buttons in Figures 1a and 1b are Notify Events. Filter Events, on the other hand, allow you to programmatically affect the outcome of the action that triggered the event. You can either discard the event entirely or modify the data for the event before LabVIEW finishes processing it. For example, the Panel Closing event handled in Figure 1b is a Filter Event; you can recognize this by the presence of the Event Filter Node ("Discard?") on the inside right border of the subdiagram. If the user presses Cancel on the "Really close window?" dialog, the Two Button Dialog will return FALSE, and the "Discard?" terminal will be TRUE, so LabVIEW will discard the event without closing the panel window. All Filter Events will have a "Discard?" terminal; some will additionally have other fields you can modify as the event "passes through" the handling diagram. For example, because Key Down is a Filter Event, you could make a string control only accept upper case characters by translating keys from lower case to upper case as they are typed. If you do not wire a value to an Event Filter node terminal on the right, the value passes through unmodified.
There are a few more things to mention about our simple example before we move on. Notice that even though both the old and new values of the controls are available as event data in the OK and Cancel button event cases, we still read the values of these controls from their front panel terminals. There is a good reason for this. Because the OK and Cancel buttons are Latching Booleans, it is still necessary to read their front panel terminals in order to get them to reset. If you merely handle the Value Changed event and never read the terminal, the buttons will stay pressed and never "pop up." In general, it is always a good idea to place the front panel terminal of a control inside the event case for its Value Change event if it is not needed elsewhere. Pay special attention to this rule if you are using a "Stop" button to end an event-handling loop; if you read the value outside the event case, LabVIEW can (and will) read the terminal before the Event Structure executes and goes to sleep, so your loop might execute one more time than you might expect.
You may have also noticed the little white box displayed on the tunnels that exit from the Event Structure. This is a new kind of tunnel in LabVIEW 6.1; it automatically uses the default value for its data type when unwired. You can turn this behavior on and off by right-clicking the tunnel and toggling the "Use Default If Unwired" option. (Case Structure tunnels also have this feature, but it is off by default.)
Finally, what about the blue hourglass terminal in the upper left corner of the Event Structure? This is the Timeout terminal; if you wire a value in milliseconds, the Event Structure will wake up and execute a special Timeout case if no other events occur within the specified duration. If the terminal is unwired (or wired the value -1), the Event Structure will sleep forever if no "interesting" events ever occur. You might want to use a
Timeout case if you need to do periodic processing in the background.
That about covers what we can learn from the simple example in Figures 1a and 1b. Of course, real-world applications will be more complicated than this simple example. More advanced users might find it intuitive to use the Event Structure in conjunction with a state machine.
State Machines
If you have a legacy VI that already uses a state machine to manage its user interface, it is usually easy to retrofit that VI to use the new Event Structure. This is a matter of simply incorporating the Event Structure into the portion(s) of the state machine that poll for changes in the UI. You often can use the Event Structure to enhance and simplify your state machine. (In the most trivial cases, it can make the state machine architecture unnecessary.)
An example distributed with LabVIEW 6i (examples/general/ uievents.llb/User InterfaceEventExample.vi) demonstrates the use of a queued state machine to handle the user interface of a VI. Traditionally, in the Idle or No Event state, the VI polls for front panel activity, comparing the current value of controls to old values stored in shift registers. The results of these comparisons tell what buttons have been pressed or menu items selected, and thus what the next state should be. This approach has two unfortunate limitations:
- If multiple transitions occurred, there is no way to determine their order.
- It is possible that a control’s value was changed to a new value, then back to its original value before the "Idle" state executes, and actions that would have triggered a state transition go undetected.
the Event Structure has the potential to eliminate both
shortcomings.
Modifying an existing UI state machine to use an Event Structure is easy. Simply replace the code in the "Idle" state that is checking controls for value changes with an Event Structure configured to handle Value Changed events for those same controls. Have each event handling case then write the action to the queue.
As an example, Figure 2 illustrates the block diagram of a simplified version of the example VI. The "No Event" case checks for Menu selections, a button being pressed, and the value of a ring changing. With the Event Structure, you can detect selection of User and Application menu items, as well as Value Changes of controls, so you can replace all the code inside that case with a single Event Structure. The resulting diagram code is shown in Figures 3a and 3b.

Figure 3b: Nonvisible Event Structure Cases
In addition to simplifying the diagram dramatically, the use of the Event Structure has two more benefits. Primarily, all events are queued, even if the Event Structure is not presently waiting on them. This means you do not miss any state changes. Also, event handling is synchronous. The events are guaranteed to be processed in the order they are received (unless two Event Structures are executing in parallel). Be aware that a couple of potential problems can arise when using the Event Structure with a more complicated state machine. First, the Event Structure by default sleeps indefinitely until an event fires. In the above example, the state machine will remain in the "No Event" case until a user interface event occurs. In many cases, this is desirable, as it minimizes CPU usage. However, if you also want to monitor a DAQ channel for a change in line state, this will not work for you. Placing that code in the "No Event" case will not work, as the loop will not iterate unless an event occurs and wakes up the Event Structure. To avoid this, you could, using a queued state machine, have a separate loop polling for the DAQ event, and write to the queue when it occurs. Better still, you could wire a timeout to the Event Structure, and execute your code to test for the change in line state in parallel with the Event Structure.
Another caveat when using Event Structures with state machines is that you must make sure there is always an Event Structure available to handle any user interface event that is generated. As mentioned earlier, if an event occurs and there is no Event Structure waiting to handle it, the user interface will be locked until the diagram code executes in such a way that an Event Structure configured to handle that event runs. In the best case, this will cause some latency in the feedback of your user interface. In the worst case, it will cause deadlock in the VI (and that is bad; very, very bad). Fortunately, there's always the Abort button, which is immune to event locks. You don’t have the Abort button hidden, do you?
See Also:
Learn more about LabVIEW 6.1
About LTR
LabVIEW Technical Resource (LTR) is the leading independent source of LabVIEW-specific information. Each LTR issue presents powerful tips and techniques and includes a Resource CD packed with VIs, Utilities, Source Code, and Documentation.
About the Authors
Craig Smith is a senior software engineer with National Instruments, specializing in improvements to the LabVIEW compiler and the G programming language definition. He can be reached at craig.smith@ni.com. Jason King is a Software Engineer at National Instruments. He can be reached at jason.king@ni.com.
Reader Comments | Submit a comment »
Very good article
Can this guy advise on the writing of the
LabVIEW manual - it all becomes clear in
the article but not in the 7.1 manual.
- A Vessey, Tunstall. - Sep 3, 2004
Well I'm new at this. I could understand the
wording but the diagram threw me for a loop.
It was very hard to understand. Needs a key
to chart functions or discribe what was in
the drawing. And activation arrows whould
help also to show where the signals were
going.
- Feb 1, 2003
About bloody time. I'll think I will switch back to LabVIEW.
- Hakan Hjalmers, Tellabs. hakan.hjalmers@tellabs.com - Aug 9, 2002
Nice Article about a feature I need
Nice Article about a feature I need.
- Jul 12, 2002
Very good article
Thank You very much. Very good and clear article.
- Mike Manzheliy, ACS. mike_acs@levsha.ru - May 15, 2002
The decussion was a good over view, however
it would be nice to point the
readers to an
example in LabVIEW 6.1 that uses this
feature or included a
downlaod with an
example. I like this new feature and will
use it in the
near future.
- gary noojin, Northrop Grumman IT. gary.noojin@brooks.af.mil - Apr 29, 2002
Legal
This tutorial (this "tutorial") was developed by National Instruments ("NI"). Although technical support of this tutorial may be made available by National Instruments, the content in this tutorial may not be completely tested and verified, and NI does not guarantee its quality in any way or that NI will continue to support this content with each new revision of related products and drivers. THIS TUTORIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND AND SUBJECT TO CERTAIN RESTRICTIONS AS MORE SPECIFICALLY SET FORTH IN NI.COM'S TERMS OF USE (http://ni.com/legal/termsofuse/unitedstates/us/).




