Accessing the gizmo of a modifier (for those modifiers that have a gizmo) can be tricky. Consider this code:
myMod = rt.UVWMap() rt.addModifier(myTeapot, myMod) myMod.gizmo.position = rt.Point3(20,20,20)
Although this works in MAXScript, in Python it produces the error: AttributeError: 'pymxs.MXSWrapperBase' object has no attribute 'gizmo'.
The reason is that the Python script does not force a scene graph evaluation the way that MAXScript does, so the gizmo is not created on the node by the time the script tries to access it. These situations can be worked around by forcing a viewport redraw, as in the following modified script:
from pymxs import runtime as rt myTeapot = rt.teapot() rt.convertTo(myTeapot, rt.editable_poly) myMod = rt.UVWMap() rt.addModifier(myTeapot, myMod) rt.redrawViews() # needed to make the gizmo available myMod.gizmo.position = rt.Point3(20,20,20) print myMod.gizmo.position
Although pymxs supports conversion between many Python and MAXScript types, and most "non-Pythonic" constructs (such as passing arguments by reference), you may find some MAXScript commands are simply impossible to replicate in Python. For example, using the "as" coercion syntax. In MAXScript you can do something like this:
selectedFaces = getFaceSelection selection[1] -- returns BitArray value myArray = selectedFaces as Array
In Python this is not possible. Instead, we can create a function in MAXScript that does this coercion internally and returns the result. Global functions will be visible to the MAXScript system for the lifetime of the 3ds Max instance. You can do this in a separate MAXScript script, or within your Python script using MaxPlus.Core.EvalMAXScript(), for example:
MaxPlus.Core.EvalMAXScript("fn b2a b = (return b as Array)") rt.b2a(selectedFaces)
MAXScript (and therefore pymxs) does not have an equivalent of MaxPlus.Core.GetRootNode(), but the functionality can be easily implemented. Here is an example that gets the root node and traverses its children:
from pymxs import runtime as rt def getRootNode(): rootScene = rt.rootScene worldSubAnim = rootScene[rt.Name('world')] return worldSubAnim.object def outputNode(n, indent = ''): print indent, n.Name for c in n.Children: outputNode(c, indent + '--') if __name__ == '__main__': outputNode(getRootNode())
Some MAXScript callback registration functions can take a Python function as the callback argument. These include registerTimeCallback(), registerRedrawViewsCallback(), and nodeEventCallback(). Others take a string rather than a function as an argument, and thus cannot take a Python function. These functions include DialogMonitorOPS.RegisterNofication() and callbacks.addScript().
One work-around is to define a variable in the MAXScript runtime using pymxs.runtime.Execute(), and then assign the Python function to the variable. Here is a simple example to illustrate the idea:
import pymxs rt = pymxs.runtime def myCallback(): print "Callback fired!" # connect to the MXS runtime rt.Execute ('pyCallback = undefined') rt.pyCallback = myCallback # register ourselves as a callback rt.callbacks.addScript(rt.Name('nodeCreated'), 'pyCallback()')