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
- Context expression functions, such as
pymxs.animate
- The MAXScript undo system functions,
run_undo()
andrun_redo()
- The
pymxs.print_()
function - The
pymxs.byref()
function, to handle those situations where a MAXScript function takes a by-reference parameter - The
pymxs.mxstoken()
function, to handle access to pymxs.runtime in multi-threaded situations
- The MAXScript Runtime
- Context Expression Functions
- MAXScript Undo and Redo with
pymxs.run_undo()
andpymxs.run_redo()
- Printing to the Listener with pymxs.print_()
- By-Reference Parameter Handling
- Accessing the Runtime In Worker Threads with
pymxs.mxstoken()
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:
pymxs.animate(True|False)
pymxs.attime(True|False)
pymxs.atlevel(True|False)
pymxs.quiet(True|False)
pymxs.redraw(True|False)
pymxs.undo(True|False)
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()
pymxs.undo(True|False)
pymxs.run_undo()
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.
msg
- the string to print.isErr
- an optional boolean that indicates whether the message should be formatted as an error (in red, by default). The default is False.forceOnMainThread
- an optional boolean that indicates whether the print should happen on the main thread. In a threaded script, setting this to True ensures that output is sent to the Listener window even if called from a worker thread. If False, when called from a worker thread, output is sent to stdout/stderr (the 3dsmax_ouput.log file or console). The default is True.
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.
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.