This topic covers how Python and MAXScript values and types interoperate with pymxs.
For simple types such as integers, floats and strings, pymxs has a direct reference to the data, and these values can be obtained and set directly.
For complex types such as Array, pymxs wraps these objects with a MXSWrapper object, pymxs.MXSWrapperBase. For example:
import pymxs rt = pymxs.runtime # Create and visit a MaxScript Array rt.execute(" gma = #(1,2,3)") print rt.gma #output: #(1,2,3) len(rt.gma) #output: 3 rt.gma[1] = 999 rt.Print(rt.gma[1]) #output: 999 l = list(rt.gma) #[1,999,3] l[2] = 888 print l #output: [1, 888, 3] print rt.gma # output: #(1, 999, 3) # Create a MaxScript Array via python sequence ma = rt.Array(*(1, 2, 3)) print ma #output: #(1,2,3)
For a demonstration of how to create, manipulate and cast complex MAXScript types in Python, see the demoPyMXSTypeInterop.py example.
Name literals are commonly used in MAXScript to signify options passed to functions, and have no corresponding type in Python. They can be created with pymxs.runtime.Name(). For example, to use the equivalent of toolmode.coordsys #world:
pymxs.runtime.toolMode.coordsys(pymxs.runtime.Name("world"))
Or to get all the properties for a box, using the equivalent of getpropnames box:
boxprops = pymxs.runtime.getpropnames(pymxs.runtime.box)
This yields a list containing runtime.Name objects, however. It might be more useful to generate a list of Python strings:
boxprops = [str(x) for x in list(pymxs.runtime.getpropnames(pymxs.runtime.box))] # gives: ['typeinCreationMethod', 'typeInPos', 'typeInLength', 'typeInWidth', 'typeInHeight', 'length', 'width', 'height', 'widthsegs', 'lengthsegs', 'heightsegs', 'mapcoords']
Values passed back to Python from pymxs MAXScript functions that take a by-reference parameter, can be translated to Python values using the pymxs.mxsreference() function. Note that this is supported for types other than Python lists.
For example:
import MaxPlus rt = pymxs.runtime MaxPlus.Core.EvalMAXScript("mySphere = sphere radius:10") MaxPlus.Core.EvalMAXScript("myType = #reference") MaxPlus.Core.EvalMAXScript("myResultArray = Array()") offset1 = rt.Point3(50.0, 50.0, 10.0) myReturnedList = [] rt.maxOps.cloneNodes(rt.mySphere, offset=offset1, cloneType=rt.myType, newNodes=pymxs.mxsreference(myReturnedList)) print "python array: " print myReturnedList offset2 = rt.Point3(-50.0, -50.0, 10.0) rt.maxOps.cloneNodes(rt.mySphere, offset=offset2, cloneType=rt.myType, newNodes=pymxs.mxsreference(rt.myResultArray)) print "runtime array: " print rt.myResultArray
Note: for variables other than Python lists, the variable must be visible in the pymxs.runtime context. For example, to pass a variable of type Color:
import pymxs rt = pymxs.runtime MaxPlus.Core.EvalMAXScript("myColor = color 100 10 10") print rt.myColor rt.maxOps.colorById(2,pymxs.mxsreference(rt.myColor)) print rt.myColor
Whereas, this approach will not work:
import pymxs rt = pymxs.runtime myColor=rt.color(10,10,10) print myColor rt.maxOps.colorById(2,pymxs.mxsreference(myColor)) print myColor
MAXScript introduced the Dictionary type in 3ds Max 2017.1, and by default use name literals for key values.
Consider this MAXScript dictionary:
my_dict = Dictionary #(#one, 1) #(#two, 2)
In pymxs, this value is wrapped, and can be used like a MAXScript dictionary, for example getting the keys property:
>>> my_py_dict = pymxs.runtime.my_dict <Dictionary<Dictionary #name one:1 two:2 >> >>> my_py_dict.keys <Array<#(#one, #two)>>
In some cases, it may be more convenient to convert the MAXScript Dictionary to a native Python Dict:
my_py_dict = {str(key):myd[key] for key in pymxs.runtime.my_dict.keys} # yields: {'two': 2, 'one': 1}
As of 3ds Max 2019 Update 1, you can use some pymxs objects (such as scene nodes) as key values in native Python dictionaries (though not in MAXScript Dictionaries). For example:
import pymxs rt = pymxs.runtime t1 = rt.teapot() t2 = rt.teapot(pos=rt.point3(20,20,0)) t2.parent = t1 node_dict = {t1:"Teapot 1", t2:"Teapot2"} print (node_dict.keys()) print (node_dict.values())
To determine whether a MAXScript object can be used as a Python dictionary key, you can check whether it implements the python __hash__() function.