Control some option in viewport config through Script


#1

This is some option I want to control in viewport config

However, I can not find them in manual, there is no line in manual about these option. So how could I control them

Thanks for any help


#2

Shade Selected Faces is the shortcut key “F2”

Selection Brackets is “J”

Not sure about the last one… Why would you want to control these via scripting?


#3

enable your listener… all the options… and as you press the hot keys, you’ll see. stuff like this…

actionMan.executeAction 0 “370” – Tools: Show Selection Bracket Toggle

You can use this code to toggle the state… How to read the state is another issue…

Good luck


#4

Thanks :slight_smile:

But I dont want to mess with the tongle thing, I want to set it ON or OFF
like this for example command

viewport.showbracket = 0

But can not find this kind of type in manual, no info in viewport page


#5

Not every option is available in MaxScript. If you’ve looked for it and couldn’t find it, then you probably can’t set it through script. I’ve run into this before and it’s frustrating that not everything is scriptable, but there’s not much we can do about it beyond posting a message to the wishlist forum in The Area: http://area.autodesk.com/index.php/forums/viewforum/32/


#6

I see
Maybe we can not control it :|. No info in manual so I guess there is no script for this little check box


#7

naw, -everything- is scriptable if you try hard enough and are insane enough. Either via the crazy dialogMonitorOps + UIAccessor + Windows. + sendMessage, or via a third party utility (for example, to change as string; as I still haven’t figured out how one might go about creating a string buffer and get a pointer index for it back).

Anyway… using the first-mentioned:


BM_SETCHECK = 241 -- checkbutton toggle message ID
dialogMonitorOPS.UnRegisterNotification id:#test -- remove any old instance
dialogMonitorOps.enabled = true -- enable the monitor
fn test = (
	hwnd = DialogMonitorOPS.GetWindowHandle() -- check the dialog that popped up
	 hwnd_title = UIAccessor.GetWindowText hwnd
	if (hwnd_title == "Viewport Configuration") then ( -- the viewport config dialog
		controls = windows.getChildrenHWND hwnd -- get all controls for this dialog
		shadeSel = controls[335][1] -- get the appropriate controls
		selBrackets = controls[336][1]
		edgedFaces = controls[337][1]
		UIAccessor.SendMessage shadeSel BM_SETCHECK 1 0 -- 0 0 == unchecked
		UIAccessor.SendMessage selBrackets BM_SETCHECK 1 0
		UIAccessor.SendMessage edgedFaces BM_SETCHECK 1 0
		UIAccessor.PressButtonByName hwnd "OK"
	)
	true
)
dialogMonitorOPS.RegisterNotification test id:#test -- register the monitor
max vptconfig -- pop up the viewport config dialog (see above function for what happens next)
dialogMonitorOPS.UnRegisterNotification id:#test -- remove the monitor
dialogMonitorOps.enabled = false -- disable the monitor

You could abstract this into a function that essentially allows you to poke at any checkbox in that, or any other, dialog… 'll leave that as an excercise for the reader (it’s fairly trivial).

The above only works in 3ds Max 2008+ or 3ds Max r9+ with the AVGuard extension installed ( need it for “windows.getChildrenHWND” )


#8

I dont get much, maybe It is not a traditional script but hacking thing :arteest:

BUt I will try to look into, thanks :smiley:


#9

What a hack… :wink:

How do always you get the correct IDs for the buttons and stuff from a dialog?!
I.e. how do you know it is controls[335] that is the Shade Selected Faces toggle? And how do you know the ID of the message to send?

Can you also use dialogMonitorOps for UI elements in the Modify Tab rollout?

– MartinB


#10

It’s not nearly as hacky as some other methods %)

The command that gets all the controls in a dialog returns a large array of small arrays. These small arrays contains the hwnd of the control as the first element, then other stuff, and the label on the control as the last element.

Hasn’t changed in ages; but yes, if you want to be safe, see the previous; find the correct control directly.

msdn is your friend there :slight_smile:

I don’t think that opens as a dialog and as such, you can’t get the hwnd for that to play with directly. I guess you could get the main max hwnd and get its child windows and whatnot; I’ve certainly not tried, but as long as you can get at the dialog the control is in, and the control itself, you can do pretty much anything with standard windows messages.


#11

here you go… quicky code

Edit: forgot to mention; you’ll need some object with a Hemisphere checkbox in the modify panel, obviously


BN_CLICKED = 0
0
    BM_SETCHECK = 241
    241
    fn getModifyPanelControl str = (
    	max_hwnd = windows.getMAXHWND()
    	max_children = windows.getChildrenHWND max_hwnd
    	modifyPanelFound = false
    	for el in max_children do (
    		if (el[4] == "ModifyTask") then ( modifyPanelFound = true )
    		if (modifyPanelFound) then ( -- ignore if we haven't found the modify panel!
    			if (el[5] == str) then ( exit with el )
    			if (el[4] == "HierarchyTask") then ( exit with undefined ) -- hierarchy panel, no luck.
     		)
    	)
    )
    getModifyPanelControl()
 hemiSphere_ui = getModifyPanelControl "Hemisphere"
 #(123079254P, 30083334P, 30083334P, "Button", "Hemisphere")
 if (hemiSphere_ui != undefined) do (
 	control_hwnd = hemiSphere_ui[1]
 	controlParent = hemiSphere_ui[2]
 	controlID = UIAccessor.GetWindowResourceID hemiSphere_ui[1]
 	windows.sendMessage control_hwnd BM_SETCHECK 1 0 -- 0 0 to uncheck
 	windows.sendMessage controlParent WM_COMMAND ((bit.shift BN_CLICKED 16) + controlID) control_hwnd
 )
 

Edit: durrr… and this doesn’t work; I keep forgetting this. This only checks the UI checkbox, but it doesn’t signal to windows that the checkbox state changed. So the (in my case) GeoSphere remains happily non-hemisphere. 'll have to dig up the proper message for this (it works in the viewport config dialog because it applies its values when you OK the dialog).

Edit2: Ohhhhh brimey, broody breeding herr. I forgot how annoying that was. Anyway, see the end of the new code up above.


#12

Thanks Richard,

very interesting, in cases where MAXScript is otherwise locked out, this seems to be a viable method to get things done anyway!

But what the heck is that last line doing?!
Also, BN_CLICKED is undefined. What value should I use?

– MartinB


#13

To an extent :slight_smile:

Whoops. BN_CLICKED should be 0 (zero). Adjusted the script in my post.

Haha… I wonder that myself sometimes.

The first sendMessage line changes the checked state of the UI control; this does not actually signal the UI that its state changed. The proper way to do that, next, is to simulate a user interaction; in our case, a click.

However, you can’t just ‘click’ the checkbox (control_hwnd).

Oh no, no, no…
You have to

  1. signal (WM_COMMAND | Windows Message)
  2. to the parent dialog (controlParent) that
  3. the UI element with the given ID (controlID)
  4. was clicked (BN_CLICKED | Button Notification. Yes, a checkbox is a type of button*. Don’t look at me, MSFT designed that. )
    And
  5. for good measure we pass it the handle (control_hwnd) as a parameter again so that if controlParent wanted to do something with that, it could.

3 and 4 have to be combined somehow as there’s only so many parameters you can pass (wParam, lParam) - but thankfully either can only be 16bit, by design, so we can stick one if the first 16bits and the other in the last 16bits of a 32bit value.

  • And with that realization finally snapping into my head… at least for checkbuttons, you could also use UIAccessor’s PressButton and/or PressButtonByName (get the parent hwnd first).

 BN_CLICKED = 0
 BM_SETCHECK = 241
 fn getModifyPanelControl str = (
 	max_hwnd = windows.getMAXHWND()
 	max_children = windows.getChildrenHWND max_hwnd
 	modifyPanelFound = false
 	for el in max_children do (
 		if (el[4] == "ModifyTask") then ( modifyPanelFound = true )
 		if (modifyPanelFound) then ( -- ignore if we haven't found the modify panel!
 			if (el[5] == str) then ( exit with el )
 			if (el[4] == "HierarchyTask") then ( exit with undefined ) -- hierarchy panel, no luck.
 		)
 	)
 )
 hemiSphere_ui = getModifyPanelControl "Hemisphere"
 if (hemiSphere_ui != undefined) do (
 	 control_hwnd = hemiSphere_ui[1]
 	 controlParent = hemiSphere_ui[2]
 	 controlID = UIAccessor.GetWindowResourceID hemiSphere_ui[1]
 	 windows.sendMessage control_hwnd BM_SETCHECK 1 0 -- 0 0 to uncheck
 	UIAccessor.pressButton control_hwnd -- that's much prettier ^.^'
 )
 

#14

Just to do the pressButtonByName bit and some further processing…


-- for each rollout in the modify panel
-- returns the hwnd to the actual rollout client dialog, plus the title of the rollout
fn getModifyPanelRollouts = (
	theRollouts = #()
	max_hwnd = windows.getMAXHWND()
	max_children = windows.getChildrenHWND max_hwnd
	modifyPanelFound = false
	for i = 1 to max_children.count do (
		el = max_children[i]
		if (el[4] == "ModifyTask") then ( modifyPanelFound = true )
		if (modifyPanelFound) then (
			if (el[4] == "RollupPanel") then (
				append theRollouts #(max_children[i+2][1],max_children[i+1][5])
			)
			if (el[4] == "HierarchyTask") then ( exit )
		)
	)
	theRollouts
)
modPanelRollouts = getModifyPanelRollouts()
#(#(29361592P, "Pick Boolean"), #(25561248P, "Parameters"), #(26609968P, "Display/Update"))
UIAccessor.pressButtonByName modPanelRollouts[1][1] "Pick Operand B"
true


#15

Thanks very much. That makes it a bit clearer. I guess it would help if one had experience in programming Windows?

Thanks again, I stored these examples away, in case I encounter non-scriptable UI elements again.

– MartinB


#16

Another useful example, thank you! I figured it runs when an old Boolean object is selected and the Modify panel is active. Interesting that I had to change modPanelRollouts[1][1] to modPanelRollouts[3][1] on my 3ds Max 2009 64bit. Maybe the order in which the children are reported is not reliable or changes with versions of 3ds Max?

– MartinB


#17

oh, quite possibly, Martin - but that’s why I included the title of the rollout in the result as well; just didn’t bother to actually utilize it in the one-liner :slight_smile:


#18

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.