Undoability

Automatic Undo & Manual Undo

There are two types of undoability in omx:

1. Automatic Undo: When using omx for scripting, e.g. creating nodes using omx.create*Node, calling methods from AL.omx.XNode , AL.omx.XPlug or call AL.omx.XModifier method from AL.omx.currentModifier() instead of AL.omx.newModifier() for editing, one execution will be one undo item in Maya.

After execution, you simply use ctrl+Z and shift+Z to undo and redo.

2. Manual Undo: When using omx within your tools or framework, where you want to manage undo and redo yourself, you will need to ensure you are using non-immediate AL.omx.XModifier, and call AL.omx.XModifier.doIt(), AL.omx.XModifier.undoIt() manually in the proper places.

Mixing maya.cmds & AL.omx calls

Mixing om2 (maya.api.OpenMaya) and cmds in your code is a wrong choice generally. The rule of thumb is to try to use om2 as much as you can as it brings you the best performance in Python, and use om1 (maya.OpenMaya) if the feature is not available in om2, and use cmds only for queries. When you have to use cmds with om2 for editing, wrap it with om2.MDGModifier.pythonCommandToExecute().

The reason for these head-ups is the undo problems caused by mixed-use. When you undo in Maya, edit by cmds will be undone but edits by om2 will not, thus the broken states in the scene.

When it comes to mixing maya.cmds and omx calls, that depends. If you ensure omx calls all using immediate mode XModifier ( check XModifier Immediate Mode ), it is fine to mix it with maya.cmds:

from AL import omx
from maya.api import OpenMaya as om2

# reminder: create a new scene to ensure we are using the immediate mode of xmodifier.

# cmds calls:
transformName1, shapeName1 = cmds.polyCube()

# omx calls:
transform1 = omx.XNode(transformName1)
transform1.t.set((2,3,4))

# cmds calls:
transformName2, = cmds.spaceLocator()
# omx calls:
transform2 = omx.XNode(transformName2)
transform2.t.connectFrom(transform1.t)

# cmds calls:
cmds.polySphere()

# the undo/redo will work.

If you use omx in some production code where you manage undo & redo yourself, mixing cmds with omx calls is the same as mixing cmds with om2. You will have undo issues.

Mixing om2 Modifier and omx.XModifier

This is a bad idea. If you use omx.XModifier, it is better and safer to only use omx.XModifier, to ensure the undo is done in a sequential way, use AL.omx.newModifier() to create separate XModifier if you need it. Alternatively, use omx.XModifiers and om2.MDagModifier, but stick to native API modifier’s for the edits, do not use omx API for editing.

Undo with XPlug States Change

When you need to set XPlug state, isLocked, isKeyable, isChannelBox, etc, you have two options, take isLocked for example: omx.XPlug.setLocked(bool) and omx.XPlug.isLocked = bool. The difference between the two is setLocked() is undoable with omx.XModifier, but you pay more performance cost while the isLocked approach is not undoable and will be likely to ruin the surrounding undo states, but it is faster than setLocked(). As a rule of thumb, use omx.XPlug.isLocked = bool when you don’t need to undo the state change, use setLocked() if the undoability matters. The same rule applies to other state edits like isKeyable and isChannelBox.