Change Handlers are used to detect when certain types of user events are performed on objects in a scene, allowing you to write scripts that respond to user operations like object moves, vertex edits, object deletions, name changes, and so on. Change Handlers are applied to individual MAXWrapper objects and specify an attribute to monitor for that object and an expression to evaluate when that attribute changes. Example attributes that can be monitored are geometry, name, transform, and parameters. Change Handlers are not stored in a 3ds Max scene.
The ChangeHandler : Value class instances represent change handler setups. ChangeHandler values are created using the when construct. Each time a when construct is executed, it creates and returns a new ChangeHandler instance. You must store this ChangeHandler value in a variable or array so that you can dismiss the handler when it is appropriate to do so.
The when construct defines a change handler function for a certain type of event on one or more objects. The system then automatically calls this function whenever the event occurs.
The syntax of the when construct has two forms as follows:
when <attribute> <objects> change[s] [ id:<name> ] \ [handleAt: #redrawViews|#timeChange] \ [ <object_parameter> ] do <expr>
when <objects> deleted [ id:<name> ] \ [handleAt:#redrawViews|#timeChange] \ [ <object_parameter> ] do <expr>
In the first form, the <attribute> can be one of:
These specify the attribute of the given object(s) to be tracked for change (see below for more details).
The second form tracks object deletion by the user or other scripts. This is typically used if you have tables or other structures containing MAXScript object values and you want to remove deleted objects from them as the user modifies the scene.
NOTES: |
The #selectedNodesPreDelete general callback is the far superior method to use to catch node deletion by the user. It is called before anything happens to the nodes and will report the deletion of any node. In contrast, the when construct operates only on nodes explicitly registered with the callback. |
mypot1 = teapot() --create a teapot fn whendeleted = --define a callback function ( local deletedobjects = callbacks.notificationParam() format "Callback: %\n" deletedobjects -- This will printevery object that was deleted. -- By this point, the object isstillattached to it's parents -- and is still in groups etc... -- Nothing has been done to the nodeyet. ) when mypot1 deleted id:#foo obj do --define a when construct ( format "When Construct:%\n" obj ) --unregisterany preDeletecallbacks callbacks.removeScripts #selectedNodesPreDelete --register the function as general callback callbacks.addscript #selectedNodesPreDelete "whendeleted()" |
A regression in 3ds Max 9 caused a change in the way the when construct was called. In releases prior to 3ds Max 9, the when construct was called before the actual deletion of the object. In 3ds Max 9, the when callback in the above code would return <deleted scene node> instead. This behavior has been fixed in 3ds Max 2008. |
In both forms, the <objects> argument can be one of the following:
If more than one object is specified, the handler is called each time the given attribute of any of the objects is signaled as changed by the 3ds Max core.
The optional id: parameter specifies an ID for one or more handlers as a name literal. The ID name can later be used to delete the handler and if the same name is given to several handlers, they can be deleted as a group.
The optional handleAt: parameter signals that the change handler expression must not be executed immediately upon notification, but delayed until either the 3ds Max viewports are redrawn ( #redrawViews ) or current 3ds Max animation time is changed ( #timeChange ). Delaying the processing of the change handler expression is highly recommended, as described in the Caution section below. An example usage of the handleAt: parameter is:when select $ changes id:#foo handleAt:#redrawViews do ...
The optional <object_parameter> lets you specify the name of a parameter variable that will be accessible in the do <expr> and will hold the actual object that has changed. You typically specify this parameter name if the handler will respond to changes in many objects, so you can determine which one has changed. The <expr> can be a single expression or block expression.
The change attributes are interpreted as follows:
Signaled when the topology of an object changes in the Modify panel such as, using a mesh smooth, optimize, or vertex delete.
Signaled when the geometry of an object changes such as, by moving a vertex or using an animated modifier.
Signaled when the name of an object is changed if this occurs because a user edits the name in one of the 3ds Max command panels. The handler is called repeatedly with each character that is changed.
Signaled when the transform of an object is changed such as, by a move, rotate, or scale.
Signaled when a scene node moves into or out of the current selection set. You should interrogate the <node>.isSelected property to determine the new state.
Signaled when any parameters are changed in the object. This is something of a catch all because the core signals this event in many situations.
Signaled when the dynamic subAnim structure changes such as, when a new vertex becomes animated in an editable mesh, or when a new controller is added to a list controller. Also called when subAnims are reassigned, for example, when a material is changed in an object.
Signaled when a new controller is assigned to one of the object's tracks.
Signaled when an object has immediate children added or removed.
You can use the following two methods for deleting change handlers:
deletes specified change handler . change_handler is the value returned by a when construct.
If optional id: parameter is not specified, deletes all change handlers. If optional id: parameter is specified, deletes all change handlers with the specified id.
For efficiency reasons, you do not want irrelevant change handlers running in the background, so it is essential that you delete those that you no longer need.
If a runtime error occurs in the body of a change handler while it is being executed, an error message is displayed and that handler is permanently disabled.
If all of the objects being tracked by a change handler are deleted, the change handler is also deleted.
The do <expr> change handler code is executed in a special context and not in the context of the code that created it. This means that the <expr> code cannot contain references to local variables in outer code nestings surrounding the when because those variables are on an execution stack that does not exist at the time the when handler is called. The compiler detects any illegal references to outer locals and generates a compiler message. An important exception to this is utility and rollout panel locals such as, local functions, rollout variables, and nested rollouts. You can refer to them in change handler code inside rollout code as they are associated directly with their rollout or utility object.
Change handlers are called only for user initiated events, and not for changes resulting from a change in controller values. For example, a change handler on the transform attribute of a node would be called when the user moves the node. If the position of the node is animated, and you play back the animation, the transform attribute change handler is not called.
If you assign a change handler to an attribute on multiple objects, the change handler is called once per object if that object’s attribute changes.
Change handlers are only applied to object already present in the scene. Change handlers are not automatically applied to newly created objects.
If you have multiple change handlers defined, the order in which the change handlers are called is somewhat arbitrary.
Due to the way that 3ds Max internally processes notification signals, the $ form of accessing selected objects is not recommended in a select change handler. To access the selected objects, you must use the selection objectset. This is because $ relies on information that has not yet been set in the selection processing whereas selection uses a different method of accessing selections and the information it uses has been set up.