Hi all,
I’m looking to integrate undo into a series of commands made via the API.
from maya.api import OpenMaya as om
mod = om.MDagModifier()
mod.createNode("transform")
mod.doIt()
Maya is very kind to include the mod.undoIt() command, but it doesn’t enjoy calling it when being asked to Undo, such as via the Edit -> Undo menu item.
Google revealed that this topic has been discussed quite a few times in the past with little to no avail.. The suggested methods seem to boil down to either..
Let’s assume for a second that (1) is a no-go, then what about (2)? That would work in situations where you had a pre-defined command you needed to run multiple times in a pre-defined fashion; like the cmds.polySphere command that creates a few nodes, makes a few connections and sets a few attributes.
But what if you just wanted to run something like the above? What if the number of commands you had in mind are many, or depend on too many factors to embed into a plug-in? Is there no hope?
What I’m hoping for is something along these lines..
queue = om.MGlobalUndoQueue()
queue.append(mod.undoIt)
Such that when the user attempts to undo, this command is called, followed by whatever else was in the queue at the time of appending.
It doesn’t have to be this of course. So long as I tailor what is going to be undone when undo is on the march.
Any ideas?
Still haven’t found a reasonable solution to this..
At this time, it looks like I’ll need to stick with maya.cmds for anything that may need undoing, which is effectively anything that does more than just read from Maya.
Sure there aren’t any ideas out there?
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/1e61c343-5a80-4b87-977f-af4422128b15%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Consider a module that exposes members similarly to maya.cmds, such as createNode and connectAttr, but calls the Python API under the hood for performance. And consider having these members exposed not via flat functions like maya.cmds, but as objects like PyMEL. E.g. myNode["myAttr"] >> myOtherNode["yourAttr"]. Now consider expanding on what is made possible by both PyMEL and cmds, such as myNode["Distance", Kilometers] = myOtherNode["Age", Cached].
That is all fine and well, except none of it can be undone. :(
When dark things come up like that , maybe the issue is not maya or undos but the design of your workflow / tools , maybe it needs to be simplified to stay aight with Maya , but yeah besides that if you need it fur your life it seems like you are pretty much f*cked lol
Sent from Mail for Windows 10
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOBzCaKuv8hkKZYa1WxxdMfidbBXSzaYWaAVtR9iky2x_g%40mail.gmail.com.
I’ve put together my impression of what was mentioned in the cult of rig video posted earlier.
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
)
However I’m struggling to properly merge it with Maya’s native undo queue; the main contender is redo. Natively, once something has been redone, you can undo it again. I haven’t yet managed to find a generic method of achieving this. Other than that it remains largely untested. Could use some help on this one.
# modify your code with the following
import _ctypes
class apiUndo(om.MPxCommand):
def doIt(self, args):
# using asDouble, asInt fails here probably because id is too large
undo_id = long(args.asDouble(0))
redo_id = long(args.asDouble(1))
self.undo = _ctypes.PyObj_FromPtr(undo_id)
self.redo = _ctypes.PyObj_FromPtr(redo_id)
def undoIt(self):
self.displayInfo("Undoing..")
self.undo()
def redoIt(self):
self.displayInfo("Redoing..")
self.redo()
# in maya define undo/redo somehow
def undo():
print 'Undo Me'
def redo():
print 'Redo Me'
# call command directly
cmds.apiUndo(id(undo), id(redo))
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOAmLLdnKHFW%3DzQPuZMDu67XNhH8QYHmPwXme-zfhBsQGA%40mail.gmail.com.
That’s a good idea!
For starters you shouldn’t be popping undo/redo methods otherwise it immediately gets lost after you undo.
Indeed, was hoping to find a way around that, and it looks like what you’ve got here should work. Didn’t know the command class was instantiated per call, that is really helpful, as it means I can store stuff in the instance and let Maya keep track of the order of calls itself.
Here is one possible way of doing it:
Aw shucks. Doesn’t appear to work on Windows..
# Traceback (most recent call last):
# File "<maya console>", line 1, in <module>
# File "<string>", line 2, in apiUndo
# RuntimeError: Cannot convert argument 0 to float.
# # Traceback (most recent call last):
# # File "C:/.../apiundo/apiundo.py", line 73, in doIt
# # undo_id = long(args.asDouble(0))
# # TypeError: Cannot convert argument 0 to float. #
Was also a little weary of _ctypes.
However I managed to remove the need to pop at least, by storing undo/redo in the shared member, and passing them to the instance of the command class like in your example!
Thanks Serguei, making progress!
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOBazke0eVtNDNpWYNkj8tSOkhx2kkLpafcb2N%3DyYXGVaQ%40mail.gmail.com.
Ooo nice. :)
Out of these two options - one retrieving an object from memory via ctypes and the other via a shared object - which do you prefer? The shared object pollutes sys.modules whereas retrieving from memory requires some explanation, and is potentially a few cycles more expensive?
Ooo nice. :)
Out of these two options - one retrieving an object from memory via ctypes and the other via a shared object - which do you prefer? The shared object pollutes
sys.moduleswhereas retrieving from memory requires some explanation, and is potentially a few cycles more expensive?
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOCdurM2f0CJG7K3f-Se4wwe%3DBs7go8yXs0HrRODSb_NnQ%40mail.gmail.com.
if invoked by itself
Good point. It’d need to be prevented from being called explicitly somehow, and only be used via this “commit” function.
Will experiment with these approaches. Thanks!
I’ve ran into two issues with the ctypes approach so far.
long is deprecated in Python 3; but can be worked aroundI’ve put together a reproducible of the issue; TLDR; it (sometimes) crashes Maya.
import _ctypes
from maya.api import OpenMaya as om
ids = dict()
def createBox():
mod = om.MDagModifier()
mod.createNode("transform")
mod.doIt()
def undo():
mod.undoIt()
ids["undo"] = hex(id(undo))
createBox()
# At this point, `undo` has been garbage collected
undo = _ctypes.PyObj_FromPtr(long(ids["undo"], 0))
# WARNING: This *may* crash Maya
undo()
What I suspect happens is that _ctypes happily returns something that you might expect to be the local undo(), and lets you call it. Presumably because it is forcibly returned as a callable, regardless of what is actually at that memory location. In reality it probably couldn’t return that function, because it no longer exists due to garbage collection.
Any ideas?
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/ef35ba41-c0f1-402f-aaa0-6633bb85e15e%40googlegroups.com.
thanks a lot for keeping this thread alive
Welcome. :) Not having found the information elsewhere is reason enough to make the extra effort to see it solved once and for all, hoping it’ll pop up when the next person Google’s something like it.
On that note, @serguiei, would it be possible to link back to this thread from your blog? Direct link
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/ef35ba41-c0f1-402f-aaa0-6633bb85e15e%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAPCVZ5NDRtwdYz%3DHusA-BSd97bJm1cTw-Adb-xhAvnW5Gr7vJw%40mail.gmail.com.
Happy to report this has worked surprisingly well!
Using it primarily with the modifier, like in the example above. In fact, I subclassed the modifier to automatically add itself to the undo queue when used.
from maya.api import OpenMaya as om
import apiundo
class DagModifier(om.MDagModifier):
def __init__(self, *args, **kwargs):
super(DagModifier, self).__init__(*args, **kwargs)
apiundo.commit(self.undoIt, self.doIt)
mod = DagModifier()
mod.createNode("transform")
mod.doIt()
At first it seemed a little too simple to actually work in practice, but sometimes the simple things work best, and so it’s been!
There is however one caveat that I’ve encountered so far with apiundo in general, which is that undoing the creation of a node derived from a custom plug-in prevents that plug-in from being unloaded; even after calls to cmds.flushUndo() and cmds.file(new=True). I suspect the instance of the Maya command is being held in memory by Python along with the reference to the modifier responsible for creating the node, but have yet to thoroughly investigate. I did try storing references to functions with weakref.ref(), but even that didn’t make a difference.
The “workaround” is restarting Maya, which is somewhat of a pain so any help with this would be much appreciated.
Accepting pull-requests!
Ps. The above example works, but is somewhat simplified. Don’t forget the edge case of it being added to the undo-queue even withing calling
doIt, and if you do subclassdoItinstead, don’t forget to add the superclass-doIt to the undo queue.