Tips and Tricks for using pymxs

Gizmos in pymxs

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

Non-callable MAXScript Constructs

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)