apiundo
apiundo copied to clipboard
Undo/Redo support for Maya Python API 2.0
Undo/Redo support for Maya Python API 2.0
Leverage the API in your Python scripts, without losing out on the ability to undo or redo.
Features
- Undo and redo for arbitrary API calls
- Intermix
apiundo
with native undo from calls viacmds
orPyMEL
- Automatically aggregates calls to
cmds
withapiundo
- Distributed as a single Python module
- Vendorable, ship independent versions of apiundo with your code
Usage
Running the below snippet will cause the newly created transform to be deleted. Redoing will cause it to be re-created.
- Make API calls
- Commit an undo function to history
Example
from maya.api import OpenMaya as om
import apiundo
mod = om.MDagModifier()
mod.createNode("transform")
mod.doIt()
apiundo.commit(
undo=mod.undoIt,
redo=mod.doIt
)
Keep in mind that you are responsible for the undo to actually undo what you intend it to. apiundo
cannot know what you are asking it to call, anything could happen.
Install
apiundo is a single Python module, compatible with Maya 2015 and above on Windows, Linux and MacOS.
- Download
apiundo.py
- Save in your
$HOME/maya/scripts
directory
Or anywhere on your PYTHONPATH
.
$HOME
on..
- ..Windows is
c:\users\<username>
- ..Linux is
/home/<username>
- ..MacOS is
/Users/<username>
How it works
An MPxCommand
plug-in is registered and called whenever an undo operations is added to the queue. The call itself does nothing and is meant to represent API calls made before it. Calling it means Maya queues the subsequent undo and redo operations for when you next undo and redo.
Background
Maya implements undo/redo via subclasses of MPxCommand
. Each command implements doIt
, undoIt
and redoIt
which Maya manages for you such that when you ask it to undo, it calls the corresponding undoIt
method of your subclass.
class ExampleCmd(om.MPxCommand):
def doIt(self, args):
self.displayInfo("Doing..")
def undoIt(self):
self.displayInfo("Undoing..")
def redoIt(self):
self.displayInfo("Redoing..")
def isUndoable(self):
return True
However this doesn't account for when you require use of the API independently.
from maya.api import OpenMaya as om
node = om.MFnDagNode().create("transform")
This call for example is ~9 times faster than an equivalent call to pymel.core.createNode()
and ~3 times faster than maya.cmds.createNode()
, but cannot be undone.
Performance
As an aside and justification of the example in Background, this is how the 3x number was found.
# Tested on Maya 2015 and 2017
import timeit
from maya import OpenMaya as om1
from maya.api import OpenMaya as om2
from maya import cmds
from pymel import core as pm
def Test(func):
return min(timeit.repeat(
func,
setup=lambda: cmds.file(new=True, force=True),
number=1000,
repeat=3
))
fn1 = om1.MFnDagNode()
fn2 = om2.MFnDagNode()
timings = dict()
timings["mel"] = Test(lambda: mel.eval("createNode \"transform\""))
timings["cmds"] = Test(lambda: cmds.createNode("transform"))
timings["pm"] = Test(lambda: pm.createNode("transform"))
timings["om1"] = Test(lambda: fn1.create("transform"))
timings["om2"] = Test(lambda: fn2.create("transform"))
print("Timings")
for method, timing in timings.items():
print("%s: %.2f ms" % (method, timing * 1000))