MAXScript Values in pymxs

This topic covers how Python and MAXScript values and types interoperate with pymxs.

MAXScript Type Marshalling

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
gma = rt.array(*(1,2,3))
print(gma)    #output: #(1,2,3)
print(len(gma))    #output: 3
gma[1] = 999
print(gma[1])    #output: 999
l = list(gma) #[1,999,3]
l[2] = 888
print(l)    #output: [1, 888, 3]
print(gma)   # output: #(1, 999, 3)

For a demonstration of how to create, manipulate and cast complex MAXScript types in Python, see the demoPyMXSTypeInterop.py example.

Dictionaries

MAXScript introduced the Dictionary type in 3ds Max 2017.1, and by default uses 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}

MAXScript Collections in pymxs

MAXScript arrays and dictionaries can be created and manipulated in pymxs. In 3ds Max 2021 and later you can manipulate a MAXScript Array directly.

This example illustrates how to create and manipulate MAXScript arrays and dictionaries in pymxs:

# MAXScript Collections in pymxs

from pymxs import runtime as rt
import pymxs

py_array = rt.Array(1,2,3)
# 3ds Max 2020 and earlier requires rt.append
rt.append(py_array, 4)

try:
    # In 3ds Max 2021, we can do this directly:
    # adding an arbitrary element will created "undefined" intermediary elements:
    py_array[5] = 5
except:
    print('Direct access is available in 3ds Max 2021+')

py_dict = rt.Dictionary(("one",1), ("two",2))
py_dict["three"] = 3
# you could also use rt.SetDictValue(py_dict, 'three', 3)

print("py_array: ", py_array)
print("py_dict: ", py_dict)

Results:

py_array:  #(1, 2, 3, 4, undefined, 5)
py_dict:  Dictionary #string (DataPair "three" 3) (DataPair "one" 1) (DataPair "two" 2) 

Using pymxs objects as Python Dictionary keys

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:

from pymxs import runtime as rt

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.