View Full Version : "when...deleted" Change Handler running BEFORE deletion....
JohnSwafford 04-12-2006, 05:26 AM In both Max 6 and Max 8, the script below shows the "when...deleted" change handler being called before the object is deleted (in direct contradiction to the docs). Run it in a new script window, then manually delete one of the objects and watch the output to the listener...
(
global CheckMyObjectsForDeleted
global MyObjects = #(Box(), Torus(), Teapot())
fn CheckMyObjectsForDeleted =
(
for obj=MyObjects.count to 1 by -1 do
(
if (isValidNode MyObjects[obj])
then (print ((MyObjects[obj].name)+" isValidNode: "+((isValidNode MyObjects[obj]) as string)))
else (deleteItem MyObjects obj)
)
)
for obj=1 to MyObjects.count do
(
when MyObjects[obj] deleted do (CheckMyObjectsForDeleted())
)
)
Here is the Listener output:
OK--script is run and the Torus
OK--is deleted by the user
"Teapot01 isValidNode: true"
"Torus01 isValidNode: true"--Torus still tests as valid
"Box01 isValidNode: true"
CheckMyObjectsForDeleted()--Function called from the Listener,
"Teapot01 isValidNode: true"--and now deletes the Torus reference
"Box01 isValidNode: true"--from the MyObjects array
OK
According to the docs, the change handler should be called after deletion:
Change Handlers and When Constructs
From 7th paragraph:
For object deletion events, the handler is called after the object is deleted, so you cannot perform any 3ds Max operations on it. 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.
Anyone else have this problem? This is a bug, isn't it?
|
|
ajohnson
04-12-2006, 11:12 AM
Yeah, I'm seeing that problem too :-/
Something like this could probably act as a workaround:
(
global obj
fn afterDeletion = (
unRegisterRedrawViewsCallback afterDeletion
print ("Actually Deleted [within redraw callback]? " + ((isDeleted obj) as string))
)
fn beforeDeletion = (
print ("Actually Deleted [within in deletion handler]? " + ((isDeleted obj) as string))
registerRedrawViewsCallback afterDeletion
)
obj = Sphere()
when obj deleted do (beforeDeletion())
)
I get the impression from the documentation that the handleAt: parameter to the when construct is supposed to do something similar to the code above, but I was unable to get that working. When I used handleAt:#redrawViews in combination with a "when..deleted", the handler expression never seemed to get called at all.
Here's the part in the documentation that deals with the handleAt: parameter:
The optional handleAt: parameter signals that the change handler expression should not be executed immediately upon notification, but delayed until the 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 ...
JohnSwafford
04-13-2006, 01:43 AM
Awesome workaround, Andrew....much better than what I'd just implemented. (I had the same trouble trying to use handleAt:#redrawViews, so I ended up trying to just delay the code execution by a set period of time....see below.)
Last night I created a rollout timer set to .interval=100ms and .active=false, then used the change handler to set .ticks=0 and .active=true. The "on timer tick do ()" event handler would run the code I needed after 100ms, and if its .ticks==1 it would set itself to .active=false. This effectively created a predictable 0.1 second delay, and it worked....but it might fail if the processing of the deletion ever took longer than the delay (e.g. a disk access, an auto gc() call, too many objects being deleted, etc...).
Your solution actually waits for the delete operation to finish.....excellent, flexible, and should be bulletproof. Just got it working this afternoon. Thanks!
JohnSwafford
04-13-2006, 03:21 AM
Andrew, I just came up with an even better solution using the "object_parameter" argument for the deletion Change Handler (I feel like an idiot for not noticing this option earlier). This argument is a direct reference to the object being deleted, and passing it by reference to a function allows that function to loop through my array and test it against each object value. Works great and is very simple....see below.
(
global MyObjects = #(Box pos:[-50,50,0], Torus pos:[50,50,0], Teapot pos:[50,-50,0])
global DeleteObjectFromArray
fn DeleteObjectFromArray &objRef =
(
for index=MyObjects.count to 1 by -1 do
(
if (objRef!=MyObjects[index])
then (print ("MyObjects["+(index as string)+"]: "+(MyObjects[index].name)))
else (deleteItem MyObjects index)
)
print ("MyObjects.count: "+(MyObjects.count as string))
)
when MyObjects deleted theObj do (DeleteObjectFromArray &theObj)
)
With this great revelation on my part (duhhh), it looks like there really isn't a bug fix needed for the Change Handler, just a docs fix. I'll post something here for Bobo in a bit....
ajohnson
04-13-2006, 03:43 AM
Sweet! That "object_parameter" slipped past me too, and it's definitely the way to go... unless, I suppose, you for some reason must have code run after the object is already actually gone. I really can't think of a reason for that, though :)
stuh505
04-13-2006, 03:57 AM
Who says this is a bug, and not just a typo in the documentation...
"For object deletion events, the handler is called when the object is deleted"
Anyway, I fail to see why a workaround for this inconsistency/bug would ever be necessary...in what kind of scenario would it be necessary to call the handler actual the after deletion occurs as opposed to right before it?
JohnSwafford
04-13-2006, 03:59 AM
Nope, that totally handles what I needed to do....thanks, Andrew.
Docs Fix Suggestion for Bobo:
On the "Change Handlers and When Constructs" page, replace this text...
The second form tracks object deletion by the user or other scripts. For object deletion events, the handler is called after the object is deleted, so you cannot perform any 3ds Max operations on it. 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.
....with this text.
The second form tracks object deletion by the user or other scripts. This is typically used if you have arrays or other structures containing MAXScript object values and you want to remove deleted objects from them as the user modifies the scene. For object deletion events, the handler is called before the object is deleted, so tests such as isDeleted or isValidNode will not show the object as deleted yet. In order to test which object values in the array should be deleted, the optional object_parameter can be compared against the values in the array.
JohnSwafford
04-13-2006, 04:13 AM
Who says this is a bug, and not just a typo in the documentation...
"For object deletion events, the handler is called when the object is deleted"
Anyway, I fail to see why a workaround for this inconsistency/bug would ever be necessary...in what kind of scenario would it be necessary to call the handler actual the after deletion occurs as opposed to right before it?
I said its a bug, and I spoke too soon. It is just a docs error, and I posted a suggestion for Bobo to consider.
As for your failure to see why it would matter........uhhhhh, read the post? I don't know how much clearer it could be, stuh. I coded according to what the docs stated, and my script failed because of the inaccuracy. I've now found the "correct" way of doing it, but if Maxscript actually functioned as stated in the docs, then the old way would probably be the only way to do this.
stuh505
04-13-2006, 01:49 PM
Well, whatever...I don't mean to be annoying, but I did read the thread before posting, and your toy example of removing a deleted element from an array does not need any kind of workaround simply because you forgot to pass the index or reference of the element being deleted! Maybe that's all or maybe I'm missing something
CGTalk Moderation
04-13-2006, 01:49 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.