The pymxs Module

The pymxs module provides access to the MAXScript runtime, as well as some top-level functions and types. This topic covers those top-level members:

The MAXScript Runtime

pymxs.runtime

This member exposes the MAXScript runtime, and all the global symbols it contains, to Python scripts. The runtime is dynamic, so global objects created in MAXScript are subsequently available to pymxs.runtime.

The names of objects available in the pymxs.runtime module correspond to MAXScript globals. So, for example, to create a teapot you can use the MAXScript Teapot() constructor like this:

>>> import pymxs
>>> rt = pymxs.runtime
>>> t = rt.Teapot()
>>> rt.classOf(t)
<GeometryClass<Teapot>>

Context Expression Functions

Several MAXScript "context expressions" are exposed to pymxs via top-level functions. These members are:

Note:

the following MAXScript context expressions are not available in pymxs: coordsys, about, printAllElements, defaultAction, MXSCallstackCaptureEnabled, dontRepeatMessages, and macroRecorderEmitterEnabled. For coordsys, you can achieve the same effect by changing the current coordinate system using pymxs.runtime.setRefCoordSys(mode_name).

In MAXScript, a context expression is used in a scoped code block, for example:

t = teapot()
with animate on
(
  at time 0 t.position = [-100, 0, 0]
  at time 100 t.position = [100, 0, 0]
)

Here, the with animate context applies to the code block, while the at time context applies to each statement. Note that the with is optional in MAXScript, and you often do not see it used.

In pymxs we would do something similar by scoping using the Python with statement:

import pymxs
rt = pymxs.runtime
t = rt.Teapot()

with pymxs.animate(True):
    with pymxs.attime(0):
            t.pos = rt.Point3(-100, 0, 0)
    with pymxs.attime(100):
            t.pos = rt.Point3(100, 0, 0)

MAXScript Undo and Redo with pymxs.run_undo() and pymxs.run_redo()

Most 3ds Max Commands (those invoked by max ...) are not directly available in pymxs.runtime (see the pymxs Limitations and Workarounds topic). However, the two functions pymxs.run_undo() and pymxs.run_redo() are mapped to max undo and max redo respectively to provide direct access to these commands.

As with MAXScript, operations run through pymxs are not recorded by the undo system. Undo must be specifically enabled. Undoable pymxs operations appear as items labeled MAXScript in the 3ds Max Undo menu.

For example, to create a teapot, change its position, and then undo the change:

import pymxs
rt = pymxs.runtime
# this will not be undoable:
with pymxs.undo(False):
    t = rt.Teapot()

with pymxs.undo(True):
    t.pos = rt.Point3(20,20,20)

pymxs.run_undo() # undo the position assignment to [20,20,20]
pymxs.run_redo() # redo the position assignement to [20,20,20]

Undo / Redo and Exceptions

The way an undo block in pymxs handles exceptions is different than in MAXScript. When an exception is raised, code executed before the exception is undone. The exception is not raised beyond the scope of the undo block, as the undo operation is considered to be the exception handler. Here is an example to illustrate this behavior:

# undo with exception

import pymxs
from pymxs import runtime as rt

def make_box_and_raise():
    with pymxs.undo(True, 'Making box'):
        rt.Box()
        # this will undo the box, and the error will not show up
        # in the listener, because it is handled:
        raise RuntimeError('This is an error')

def make_sphere_and_raise():
    rt.Sphere()
    # this will show up in the lister as an unhandled exception
    raise RuntimeError('This is an error')

make_box_and_raise()
make_sphere_and_raise()

Printing to the Listener with pymxs.print_()

pymxs.print_(msg[, isErr[, [forceOnMainThread]])

This member gives you more control over error formatting and threading behavior.

This function exists because both MAXScript print() and assert() functions are disabled and not callable from pymxs.runtime. There are a couple of ways to work around this limitation to send messages to the Listener window. The simplest is to use the standard Python print() function and assert statement. However, you cannot control formatting or threading behavior with these functions, so there may be situations where pymxs.print_() is the better solution.

By-Reference Parameter Handling

The pymxs.byref() function allows you to use MAXScript functions that take "by reference" parameters. See the By-Reference Parameter Handling in pymxs topic for more information.

Note:

As of 3ds Max 2021, the pymxs.mxsreference() function is marked as deprecated, and will produce a warning in the Listener window when called the first time. Use pymxs.byref() instead.

Accessing the Runtime In Worker Threads with pymxs.mxstoken()

pymxs.mxstoken()

You cannot call pymxs.runtime commands directly on a worker thread, as this will cause a runtime exception. However, pymxs has a mxstoken() function to support multi-threading by providing a synchronization mechanism between worker threads.

For an example of this approach, see the example \<maxdirectory>\scripts\Python\demoMXSToken.py.