From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

Example Code

Undo (Ctrl+Z) that works in stand alone application built in LabVIEW (exe)

Code and Documents

Attachment

The Undo (Ctrl-z) menu option is not available in stand alone applications built in LabVIEW.

The LabVIEW help confirms this: http://zone.ni.com/reference/en-XX/help/371361G-01/lvdialog/application_item_tags/

Look where it says: "Items followed by two asterisks (**) are available in stand-alone applications you build in LabVIEW."

Then look for Undo and Redo in the Edit menu and you will see that they are not followed by the (**).

The attached zip file has a project with SIX versions of a simple program that implements Undo.

Folder 1UndoUsingLVmenu:

Has the "Application Item" version of the Undo, Redo menu options. If you execute this VI in the development environment it works fine, but when it is built into a stand alone application (exe) the Edit menu is grayed out.

RTM.png

Folder 2UndoUsingUserMenu:

The main VI here is called "Undo-usingUserMenu.vi" and it calls "UndoFGV.vi" which is a Functional Global Variable (a.k.a. Action Engine) that implements the Undo/Redo functionality. This VI works well both in the development mode and as an exe. It uses the customized "UndoMenu-UserMenu.rtm" menu.

FGV.png

Folder 3Undo-LVOOP

An example of the same code where the type def cluster used in the other two folders is replaced now by a class.

OOP.png

Folder 4UndoBratVI

An example of a "Brat VI" solution. UndoBrat.vi can be placed in the block diagram of any VI that has a run time menu with "User Item" type menu tags for "Undo" and "Redo" and it would implement this functionality. The only other thing that needs to be added is firing the global stop event.

This VI uses "Tool-GetParent.vi" and "VIG-GlobalStop.vi" presented by Norm Kirchner in his SMoReS presentation at NI Week 2010: http://decibel.ni.com/content/docs/DOC-11996

4UndoBratVI.PNG

The UndoBrat.vi will only undo/redo controls, indicators won't be modified by it. Also, booleans with latch mechanical action will be ignored, because LabVIEW can not read nor write its value using property nodes.

The final two are each in their own project to avoid cross linking. These were added by Stephen Mercer, working from Fabiola's original post.

5UndoBratVIFewerDataCopies -- open  5UndoUsingBratVIFewerDataCopies.lvproj to view this version.

This example uses the same approach as #4, but instead of copying all of the control states every time, this copies only the control that changed, recording both the old and new values into the undo stack. This significantly decreases the data copies required to store the undo stack.

6UndoBratVIAndClasses -- open 6UndoUsingBratVIAndClasses.lvproj to view this version.

This example takes the approach used in #5 and changes the transaction record from a cluster to a class, so now we can have transactions for things other than control edits -- for example, menu items or button actions can now be recorded in the undo stack, so long as you have some way to record the before and after state of the action. For each type of transacted action you want to record, create a child class of Transaction.lvclass to record that type of action, and use the "Add To Undo Buffer.vi" to add it to the undo stack. You can call that subVI from anywhere so long as the brat VI has already been started.

For an opportunity to learn from experienced developers / entrepeneurs (Steve, Joerg, and Brian amongst them):
Check out DSH Pragmatic Software Development Workshop!

DQMH Lead Architect * DQMH Trusted Advisor * Certified LabVIEW Architect * Certified LabVIEW Embedded Developer * Certified Professional Instructor * LabVIEW Champion * Code Janitor

Have you been nice to future you?

Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.

Comments
G-Money
NI Employee (retired)
on

Both the FGV and the LVOOP applications are functional. I can follow the logic for why both are valid techniques and scalable. Great post!

G-Money
NI Employee (retired)
on

Which do you feel is the most efficient approach to this? I love the Brat VIs and their drop in functionality. Thoughts?

AristosQueue (NI)
NI Employee (retired)
on

Using Brat or not shouldn't (crosses fingers) have any meaningful impact on runtime performance... that just makes it easier to set up in the first place. The performance at runtime is impacted by how much data has to be copied for each individual edit, and that's definitely lower after my refactoring.

There's actually another modification that I found later that isn't in the posted VIs that would help performance. The circular buffer is implemented by using a Rotate 1D Array primitive. Instead of rotating the array, rotate the indicies themselves. Currently, the oldest operation in the stack is always in index zero. Modify it so that you store "oldest transaction index", and allow that to move through the circular buffer whenever a new value is added to the already full buffer. This results in a ciruclar buffer implementation with significantly less data movement.

Achuthaperumal
Member
Member
on

Hi FabiolaDelaCueva,

Great work! But I need a clarification...

 

I've added the following to the "UndoBrat.vi":  A Path control, an Image Indicator and a boolean control labeled as "Threshold". Whenever I select an image file path, The Image should be shown in the image indicator. When I press the Threshold button, the image should be converted into a binary image. Now I added this in the above mentioned VI and selected a valid image path then I tried the Undo command. Only the path value became empty and the image was still there.

 

I thought that this Undo Command will wipe out the entire operation that I performed!! But It just cleared the control value. Can you please explain why?

 

 

Oh! one more thing... I'm a huge fan of DQMH Smiley Happy Great work! Kudos to the entire team 🙂


@FabiolaDelaCueva
 
GatorMech89
Member
Member
on

Just wanted to say I LOVE this approach and am already finding it immensely useful. The Brat approach in particular is very convenient. Word of caution - when I implemented it in an Application it was initially not functional but only in the EXE. After a bit of troubleshooting I found the run time engine did not like the mechanical action property used to filter out latching booleans within the FGV initialize case. After implementing Paul Faulkenstein's workaround (https://forums.ni.com/t5/LabVIEW/How-to-get-mechanical-action-Latch-or-switch-of-a-boolean/td-p/1599...) works like a charm! 

Contributors