Home Technical Talk

Maxscript Function Orders

polycounter lvl 6
Offline / Send Message
M-Cubed polycounter lvl 6
Hey guys. I'm a beginner when it comes to maxscript and scripting in general. I've been doing some small tool scripts as a learning exercise, but I'm still having trouble understanding the order in which things are initialized and called. For example, this is my current test script:
(
        objCollection = $*                                                                                                                            
        objArray = objCollection as array
        objList = #()
        txtNull = ""
        local nameSearchBox
    
        function updateList x= 
        (
            objCollection = $*                                                                                                                            
            objArray = objCollection as array
            objList = #()
            
            print(x)

            for i = 1 to objArray.count do
            (
                if findString objArray[i].name x == 1 do
                (
                    appendIfUnique objList objArray[i].name
                )
            )
            print(objList)
            nameSearchBox.objectList.items = objList
            nameSearchBox.objectSearch.text = x
        )

        function selUpdate = (
                print("Selection Changed")
            )

        rollout nameSearchBox "Name and Search"
        (
            edittext objectSearch 
            listbox objectList
            on objectSearch changed txt do updateList txt
                
            on nameSearchBox open do(
                    callbacks.addScript #selectionSetChanged "selUpdate()" id:#Selection_Update
            )
            
            on nameSearchBox close do(
                    callbacks.removeScripts id:#Selection_Update
            )
        )


        createdialog nameSearchBox 150 200

)
When I run the code I get the error "-- selUpdate: undefined
>> MAXScript Callback script Exception: -- Type error: Call needs function or class, got: undefined <<"

This I believe means that the function the callback is looking to run is not yet defined. However, what I've learned, or at least think I understand, is that if the function is placed before the rollout in the script, it should be defined right? So I thought this was written with the right order, but now I'm not so sure. Or is there something else I'm missing here? Thanks for the help!

Replies

  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Looks like a scope issue. Try this:
    (                                                                                                                
    	objArray = selection as array
    	objList = #()
    	txtNull = ""
    	
    	global nameSearchBox
    	
    	rollout nameSearchBox "Name and Search"
    	(
    		edittext objectSearch 
    		listbox objectList
    		
    		function updateList x= 
    		(                                                                                                                       
    			objArray = selection as array
    			objList = #()
    			for i = 1 to objArray.count do
    			(
    				if findString objArray[i].name x == 1 do
    				(
    					appendIfUnique objList objArray[i].name
    				)
    			)
    			print(objList)
    			nameSearchBox.objectList.items = objList
    			nameSearchBox.objectSearch.text = x
    		)
    		
    		function selUpdate = 
    		(
    			print("Selection Changed")
    		)
    		
    		on objectSearch changed txt do 
    		(
    			updateList txt
    		)
    		
    		on nameSearchBox open do
    		(
    			callbacks.addScript #selectionSetChanged "nameSearchBox.selUpdate()" id:#Selection_Update
    		)
    		
    		on nameSearchBox close do
    		(
    			callbacks.removeScripts id:#Selection_Update
    		)
    	)
    	createdialog nameSearchBox 150 200
    )
    

    Weird to explain but basically its not in the global scope. So you can make the rollout global and then the function is accessible. (Callbacks are finicky or at least in my opinion. Would like to hear if there is a better way) Also I removed that crazyness $*. You can just use selection as Array where you can do whatever you want.
  • monster
    Options
    Offline / Send Message
    monster polycounter
    haiddasalami is right. Also I would move the code above the rollout into the Open block. That way if you open the rollout from another place in your code you don't have to repeat that code.

    But also you don't need to declare the rollout global. Rollouts are all global once created.
    (                                                                                                                
    	
    	rollout nameSearchBox "Name and Search"
    	(
    		edittext objectSearch 
    		listbox objectList
    		
    		function updateList x= 
    		(                                                                                                                       
    			objArray = selection as array
    			objList = #()
    			for i = 1 to objArray.count do
    			(
    				if findString objArray[i].name x == 1 do
    				(
    					appendIfUnique objList objArray[i].name
    				)
    			)
    			print(objList)
    			nameSearchBox.objectList.items = objList
    			nameSearchBox.objectSearch.text = x
    		)
    		
    		function selUpdate = 
    		(
    			print("Selection Changed")
    		)
    		
    		on objectSearch changed txt do 
    		(
    			updateList txt
    		)
    		
    		on nameSearchBox open do
    		(
    			objArray = selection as array
    			objList = #()
    			txtNull = ""
    			
    			callbacks.addScript #selectionSetChanged "nameSearchBox.selUpdate()" id:#Selection_Update
    		)
    		
    		on nameSearchBox close do
    		(
    			callbacks.removeScripts id:#Selection_Update
    		)
    	)
    	createdialog nameSearchBox 150 200
    )
    
  • M-Cubed
    Options
    Offline / Send Message
    M-Cubed polycounter lvl 6
    Hey thanks for the answers guys. I tried the updated code you provided and it does work as intended, but I'm still not quite sure I understand. Let me try and talk it out and you guys tell me if I'm wrong.

    So the way I had it written the functions were not in global scope? If so, why was that? I thought functions would always be global. Ugh, yeah I'm still having trouble wrapping my brain around this. Damn artist brain... If anyone could explain this to me, or point me to a tutorial that would be great.

    Thanks again!
  • Noors
    Options
    Offline / Send Message
    Noors greentooth
    I think the rollout creates a new global scope, thus making your functions unaccessible when recalled from callbacks.
  • Pathologist
    Options
    Offline / Send Message
    Pathologist polycounter lvl 4
    Since we are asking questions anyway, something I have been wondering for a while is:
    What is the use of using "(" at the start and ")" at the end of a maxscript file? Does it have a purpose? :icon_question:


    I would make the code like so:
    try(destroyDialog nameSearchBox)catch()
    
    objArray = selection as array
    objList = #()
    txtNull = ""
    
    function updateList x= 
    (                                                                                                                       
    	objArray = selection as array
    	objList = #()
    	for i = 1 to objArray.count do
    	(
    		if findString objArray[i].name x == 1 do
    		(
    			appendIfUnique objList objArray[i].name
    		)
    	)
    	print(objList)
    	nameSearchBox.objectList.items = objList
    	nameSearchBox.objectSearch.text = x
    )
    
    function selUpdate = 
    (
    	print("Selection Changed")
    )
    
    rollout nameSearchBox "Name and Search"
    (
    	edittext objectSearch 
    	listbox objectList
    	
    	on objectSearch changed txt do 
    	(
    		updateList txt
    	)
    	
    	on nameSearchBox open do
    	(
    		callbacks.addScript #selectionSetChanged "selUpdate()" id:#Selection_Update
    	)
    	
    	on nameSearchBox close do
    	(
    		callbacks.removeScripts id:#Selection_Update
    	)
    )
    
    createDialog nameSearchBox 150 200
    clearListener()
    
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    @monster: Hmm tried that code and get the same scope issue unless i make the rollout global. From my understanding on the scope, anything inside the macroscript definition is considered a local or separate scope from global.

    In regards to scope, heres some good reading from the documentation. Generally if you just google function and variable scope you should find bunch of articles. I can write some examples later if that will help you.
    http://docs.autodesk.com/3DSMAX/15/ENU/MAXScript-Help/index.html?url=files/GUID-382E7583-DD2A-4DF5-B568-71502DF95ED9.htm,topicNumber=d30e137332,hash=WS3ED54CBA79FF2E3D1E593B6612B78359E8F-7668

    @Pathologist: The () basically makes it into a macroscript that can be called on later or from my shitty understanding of maxscript. Not a fan of the language :P
  • monster
    Options
    Offline / Send Message
    monster polycounter
    @monster: Hmm tried that code and get the same scope issue unless i make the rollout global. From my understanding on the scope, anything inside the macroscript definition is considered a local or separate scope from global.



    My bad, I think I encased the code in () after testing. Normally I have a bunch of rollout definitions script in a plugin directory, and my macroscripts consist only of createDialogs calls.
  • Pathologist
    Options
    Offline / Send Message
    Pathologist polycounter lvl 4
    perna wrote: »
    Whatever you put inside the brackets is in local scope and doesn't survive end bracket.

    That's what I suspected, just wasn't certain about it, I recall having situations where it did survive. That could have been another script conflicting though, we use a major library with over 400 functions defined on launch and kept in memory like that, I am no fan of it myself the previous co-worker who maintained and made our tools was though, as it allowed for easy control over the functions and you could call them at any time.

    Thanks for answering :)
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    @Pathologist
    I don't know if you know about WHILE in FOR, but you can compact your FOR loop below to something like this:
    ==from==
    
        for i = 1 to objArray.count do
        (
            if findString objArray[i].name x == 1 do
            (
                appendIfUnique objList objArray[i].name
            )
        )
    
    ==to==
    
         for i in objArray where findString i.name x == 1 do appendIfUnique objList i.name
    
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    That's what I suspected, just wasn't certain about it, I recall having situations where it did survive. That could have been another script conflicting though, we use a major library with over 400 functions defined on launch and kept in memory like that, I am no fan of it myself the previous co-worker who maintained and made our tools was though, as it allowed for easy control over the functions and you could call them at any time.

    Thanks for answering :)

    This is one of the things that frustrate me well regarding the fact that you gotta launch the program with you're library unless you fileIn every script at the start to import a library. Though hopefully with the implementation of python this might be better :)

    @Monster: Ah awesome.
  • EMJAY1970
    Options
    Offline / Send Message
    EMJAY1970 polycounter lvl 3
    I am trying to add a custom attribute to an object in Max, but do not want it to show in the UI, just on a track. This is because I want to be able to locate an object by its custom attributes (which I also need to know), just in case (which is the case) the object gets renamed by someone else (even though they have been told not to change anything). I can script an attribute, but it shows in the UI

    code so far:

    nodeMasterID = attributes obj_Identifier
    (
    parameters mainP rollout:mainR

    (
    nodeID type:#node animatable:false
    )

    rollout mainR "Node Identifier:"

    (
    checkbox nodeIDcBx enabled: false
    )

    )
    custAttributes.add masterPH nodeMasterID

    The Maxscript help is very conveluted and difficult to navigate, having to jump between pages for info is awful. Maya help is so much more helpful
  • monster
    Options
    Offline / Send Message
    monster polycounter
    Hi EMJAY1970, feel free to create a new post when you ask a question not related to the original topic.

    To answer your question directly, remove the rollout from your Custom Attribute and it won't show in the UI.
    nodeMasterID = attributes obj_Identifier
    (
    	parameters mainP
    
    	(
    		nodeID type:#node animatable:false
    	)
    
    )
    
    custAttributes.add masterPH nodeMasterID
    

    However, a better solution is to remove then name dependency by using a Weak Reference instead of storing the node.
    (
    	--CREATE A BOX AND SPHERE
    	myBox = box()
    	mySphere = sphere()
    
    	--CREATE CUSTOM ATTRIBUTE, NOTE THE TYPE IS #MAXOBJECT
    	nodeMasterID = attributes obj_Identifier
    	(
    		parameters mainP
    
    		(
    			nodeID type:#maxObject
    		)
    
    	)
    
    	--ADD CUSTOM ATTRIBUTE TO BOX
    	custAttributes.add myBox nodeMasterID
    
    	--ADD WEAK REFERENCE TO THE BOX OF THE SPHERE
    	myBox.nodeID = nodeTransformMonitor node:mySphere forwardTransformChangeMsgs:false
    
    	--RENAME THE SPHERE
    	mySphere.name = "Globe"
    
    	--GET THE NEW NAME  OF THE SPHERE FROM THE BOX'S CUSTOM ATTRIBUTE
    	print myBox.nodeID.node.name
    )
    

    I almost never use the node type anymore. You can read more about Weak References here: http://paulneale.com/tutorials/Scripting/weakReferences/weakReferences.htm
Sign In or Register to comment.