How to disable a button in a rollout

How to disable a button in a rollout

10DSpace
Advisor Advisor
3,554 Views
12 Replies
Message 1 of 13

How to disable a button in a rollout

10DSpace
Advisor
Advisor

I have a rollout I have created with a variety of utilities that has been working perfectly for 6 months or so.  I am now trying to add a feature to disable one of the buttons if the user has selected more than 1 object in the scene. 

 

The line I am trying to use is:

 

if selection.count >1 do btn1.enabled = false

 

No matter where I place this line (I have tried either before the rollout declaration, after the createdialog NewMatRollout statement at the end or within the rollout itself.) I get the following error message:

 

 

-- Error occurred in anonymous codeblock; filename: C:\Users\Dave\AppData\Local\Autodesk\3dsmax\2020 - 64bit\ENU\usermacros\NewMaterialUtilities_V2.mcr; position: 28876; line: 675
-- Unknown property: "enabled" in undefined
-- MAXScript callstack:
-- thread data: threadID:2564
-- ------------------------------------------------------
-- [stack level: 0]
-- In top-level

 

The error seems to indicate that btn1 is not defined, but it is and has been working properly for 6 months and when I comment out the new line above, the script runs fine.   I have also tried declaring btn1 as a global variable before the rollout declaration, but with the same result. 

 

I have read that you cannot execute code from within a rollout (although frankly I am not very clear about the how and why or even what "execute code" means vs using conditional statements) but then how do you disable a button in a rollout based on the number of objects selected?   Note that I have been using the number of objects selected to trigger other elements of this script without any problem.   

 

Any help would be greatly appreciated.

0 Likes
Accepted solutions (2)
3,555 Views
12 Replies
Replies (12)
Message 2 of 13

ads_royje
Alumni
Alumni

Hi @10DSpace ,

 

that should work like this

rollout button_disabler "Disable button"
(
button but1 "Press me to disable/enable the other one"
button but2 "I can be disabled"

on but1 pressed do but2.enabled = false

)

theNewFloater = newRolloutFloater "ButtonDisabler" 300 220
addRollout button_disabler theNewFloater

If you try to access the button outside its context, outside the rollout, you need to call it with the rollout in its name

rollout button_disabler "Disable button"
(
button but1 "Press me to disable/enable the other one"
button but2 "I can be disabled"

on but1 pressed do disable_but()

)

fn disable_but = (
        button_disabler.but2.enabled = false
    )

theNewFloater = newRolloutFloater "ButtonDisabler" 300 220
addRollout button_disabler theNewFloater


Let me know if this helps or not,

Regards,

0 Likes
Message 3 of 13

10DSpace
Advisor
Advisor

Hi @ads_royje 

 

Thanks very much for your response.  The first script does disable btn2  and when I click on btn1.

 

The 2nd script did not work the first time I ran it but then the 2nd time I ran it, it did and it runs reliably now. ( I have no explanation for the first time I ran it and unfortunately, I cleared the Max listener so no going back.)   

 

But I am struggling to figure out how to integrate this into my existing script with an already created rollout with 1 button that I want to disable based not on clicking another button,  but based on the scene condition that more than 1 object has been selected.    Sorry, if I am being dense here but,

 

1.  Exactly how do I integrate the required condition of  selection.count >1 to trigger the disabling of my existing button in your script snippet and

2.  How can I do this without the need for another button on the rollout and making the user click it?

 

Thanks again for trying to help.    

0 Likes
Message 4 of 13

istan
Advisor
Advisor

search for callback & "#postNodeSelectOperation"..

 

0 Likes
Message 5 of 13

10DSpace
Advisor
Advisor

@istan 

 

Thanks for your reply, but most all of the links that result from that search give Page Not Found except for one:

 

https://trykle.gitee.io/3dsmax-2020.1-maxscript-help/index.html#!/url=./files/GUID-C1F6495F-5831-4FC...

 

and that one only says  the following:

 

#postNodeSelectOperation: undefined 

Sent after the system Node Selection Processor has finished selecting nodes.

 

 

I am not adverse to learning about callbacks and this #postnodeSelectOperation, but can you briefly describe how this system message would be used to modify a button on a scripted rollout?    Thanks for your time.

        

Message 6 of 13

istan
Advisor
Advisor

It could also be: "#selectionSetChanged"..

Sry. I'm more active in C++ development, there it is "NOTIFY_SELECTIONSET_CHANGED"

 
Message 7 of 13

ads_royje
Alumni
Alumni
Accepted solution

Hi again @10DSpace 

 

Thanks @istan , that is indeed the right direction! 🙂

with 1 little issue, is that the general callback #postNodeSelectOperation will get an event on node selection, but will not on deselection.

If we need an event for deselection too a "Node Event Callback" can be used in this case.

NodeEventCallback selectionChanged:fn()

 

As the callback is calling a function that toggles the button from outside the rollout context, the button must be called by its rollout

<rollout>.<button>.enabled


Let me know if this works for you:

(
    -- UI rollout declaration
    rollout button_disabler "Disable button"
    (
        button but2 "I can be disabled by deselecting object"
        label labl "Select and unselect objects"

        -- what to do when the button is enabled and pressed, currently printing the node selection
        on but2 pressed do (print $)
    )

    -- function declaration, using the general dollar sign key $ as selection
    -- note the 2 arguments, see below note for details
    fn disable_but ev nd = (
            if $ != undefined then (button_disabler.but2.enabled = true) else (button_disabler.but2.enabled = false)
        )

    -- create the UI
    theNewFloater = newRolloutFloater "ButtonDisabler" 300 220
    addRollout button_disabler theNewFloater

    -- run function right after the UI creation to update the button state upon current selection, is there any selected objects on start up ?
    disable_but ev nd

    -- add node event callback on node selection & deselection on node selection changes the function is ran.
    /* note on Node Event Callback:
    the function declaration must contain 2 arguments as the callback mechanism sends them to the function.
    the 2 arguments can be used in the function but are not required, the 1st is the Event and the 2nd is the Node(s) selection
    see documentation for detailled information:
    http://help.autodesk.com/view/3DSMAX/2020/ENU/?guid=GUID-7C91D285-5683-4606-9F7C-B8D3A7CA508B
    */
    NodeEventCallback selectionChanged:disable_but()
)

 

0 Likes
Message 8 of 13

10DSpace
Advisor
Advisor

Thanks very much @ads_royje and @istan  for your help and replies.

 

@ads_royje 

 

I have got this to work and it is simpler than I thought it would be thanks to your patient explanations and link.   

To disable btn1  in my script based on the the currently selected number of objects (at the time of starting the script) all I have to do is add the following after the CreateDialogue line:

 

if selection.count >1 then (NewMatRollout.btn1.enabled = false) else (NewMatRollout.btn1.enabled = true)

 

The important bit I was missing was the correct syntax for accessing the btn1 after creation of the rollout and the bit you pasted from the documentation about needing to include the rollout name should have been obvious to me (in retrospect) but was not.    Wow. This is almost anticlimactic.  

 

If I want to take it to the next step and monitor any change in the number of selected objects in the scene and take further action based on the new selection,  then I can use the 

NodeEventCallback selectionChanged:My_Function

   callback.   You didn't mention it, but I guess it would be good practice to end the instance of the callback with something like the following which I found mentioned in the docs elsewhere:

 

callbackItem = undefined
gc light:true

 

Thanks again for your help.  

 

Message 9 of 13

10DSpace
Advisor
Advisor

@ads_royje 

 

To frame my last question a little better,

Do I need to end the instance of the callback like below before the script is closed or will the act of closing the rollout do this automatically?

 

callbackItem = NodeEventCallback selectionChanged:My_Function

 

callbackItem = undefined
gc light:true

Message 10 of 13

istan
Advisor
Advisor

plz refer to the help file, it's well documented there, imho.

Message 11 of 13

ads_royje
Alumni
Alumni
Accepted solution

Hi @10DSpace ,

 

yes, not mandatory but that would be the best practice to do so 🙂

That'd be called when the rollout closes

the rollout close event is within the rollout itself.

on <rollout> close do ()


Using my previous example, it would look like this:

(
    -- declaration for closing callback, creating variables for the callback and the function to clear it.
    -- as the function to clear the callback is called within the rollout, so before the callback is created:
    -- we create the variable first then we call in on the "On <rollout> close do ()", we then create the fn after the callback has been created.
    local mycallbackItem
    local mycallbackItemClose 

    rollout button_disabler "Disable button"
    (
        button but2 "I can be disabled by deselecting object"
        label labl "Select and unselect objects"

        on but2 pressed do (print $)

        -- on the rollout close do clear the callback. Note that we are using an empty variable at this point, but it must pre-exists
        on button_disabler close do (mycallbackItemClose())
    )

    fn disable_but ev nd = (
            if $ != undefined then (button_disabler.but2.enabled = true) else (button_disabler.but2.enabled = false)
        )

    theNewFloater = newRolloutFloater "ButtonDisabler" 300 220
    addRollout button_disabler theNewFloater

    disable_but ev nd

    -- associate the callback to the variable
    mycallbackItem = NodeEventCallback selectionChanged:disable_but()

    -- once the rollout is created and the node event associated to the variable, we can define the function to undefine it.
    fn mycallbackItemClose = (mycallbackItem = undefined; gc light:true) 
   
)


Let me know if this helps or not
Sorry if I am being too "explicit" 🙂

Message 12 of 13

10DSpace
Advisor
Advisor

Thanks @ads_royje  for the clear response.  Very helpful.  Also, explicit is a good thing in forum communications IMHO.  Thanks again for your series of helpful responses.  It seems like you are not even breaking a sweat.😀

Message 13 of 13

ads_royje
Alumni
Alumni

Thanks for your feedback! 😄

I did bang my head on walls, and still do!

0 Likes