Close dialog windows from maxscript


#1

Hey guys, I’m glad to finally find a site that will help me with this maxscript problem.

I created a script that goes through all the subdirectories of a directory of my choosing, and converts .max files to another format (which we have a plugin that handles).

This file is a batch script, it goes through them all and converts them by itself.

However, if a max file is corrupt, or for whatever reason wont open, a dialog box prompt appears, and I have to press OK to continue the process.

My question is: Is there a way for maxscript to signal an OK or RETRY or etc to the dialog box that pops up after the error appears on screen?

Ive been trying to find a method, but it completely alludes me!

Thanks in advance!

  • Doron

#2

Use the quiet mode when loading your max files to suppress errors or missing file dialogs.


   [left]loadmaxfile "apollo.max" quiet:true
   [/left]
    

Hope that’s what you’ve been looking for. Depends on the corruptness of the scenes xD


#3

Thanks for the reply :slight_smile:

I actually used Listeners and the UIAccessor and it did the trick.

But if this one line code works, it’s even better; ill give it a shot, thanks!


#4

if you still get a dialog despite using silent:true, have a look at DialogMonitorOps and UIAccessor ; combine the two and you can easily close any dialog that my pop up or perform a specific function depending on the dialog.


#5

Ok, well the one line trick didn’t work but my listeners are still good to go :stuck_out_tongue:

Although it does what it has to, after the script is run, it says (in the error box in bottom left)

MAXScript DialogMonitor Exception: --Unable to convert: undefined to type: boolean <<

Not sure how to fix that?

Also, is there someway to log the exact error of each popup? I don’t think there’s a method in UIAccessor for that.

Thanks again!


#6

You could try something like this…


  dialogMonitorOps.unRegisterNotification id:#test
  fn checkDialog = (
  	local hwnd = dialogMonitorOps.getWindowHandle()
  	if (hwnd == 0) then ( false )
  	else (
  		format "==================================================
"
  		format "%
" hwndText
  		local children = uiAccessor.getChildWindows hwnd
  		format "--------------------------------------------------
"
  		for child in children do (
  			format "%
" (uiAccessor.getWindowText child)
  		)
  		format "==================================================
"
  		uiAccessor.pressDefaultButton()
  		true
  	)
  )
  
  dialogMonitorOps.enabled = true
  dialogMonitorOps.interactive = false
  dialogMonitorOps.registerNotification checkDialog id:#test
  loadMaxFile "c:\	emp\\atextfile.txt" silent:true
  dialogMonitorOps.enabled = false
  

Edit: The error you got is probably due to the function you registered not returning a boolean value - it must always return true or false.


#7

Hm, I’ll post what I have so far, and maybe I can get a better grip on this by you guys understanding where I’m at (but thanks for the code, im testing it as we speak)


MacroScript Moves category:"My Toolz" buttonText:"Moves"
(
rollout BatchRollout "Batch Export" width:341 height:158
(
	button btn15 "Source" pos:[13,13] width:96 height:24
	button btn17 "Export" pos:[10,110] width:323 height:35
	edittext edt11 "" pos:[121,13] width:206 height:24

	on btn15 pressed do
	(
	source_dir = getSavePath()
	edt11.text = (source_dir as string)
	)
	on btn17 pressed do
	(	
				
		fn getFilesRecurse dir =
		(
		
			global batch_file = createFile (edt11.text + "\\batchLog.txt")
			
			direc = GetDirectories (edt11.text + "\\*")
			for d in direc do
			(
				join direc (GetDirectories (d+ "\\*"))
			)

			append direc (edt11.text + "\\") -- Need to include the original top level directory

			maxfiles = #()
			for de in direc do join maxfiles (getFiles (de + "*.max"))
			
			------------------
			DialogMonitorOPS.unRegisterNotification id:#hello
			fn handleBox = 
			(
				local windowHandle = DialogMonitorOPS.GetWindowHandle()
				if (windowHandle != 0) then 
				(
					local children = UIAccessor.getChildWindows windowHandle
					for child in children do
					(
						format "%
" (UIAccessor.getWindowText child) to:batch_file
					)
					--local title = UIAccessor.GetWindowText WindowHandle
					--local error = UIAccessor.GetWindowDllDescription WindowHandle
					UIAccessor.PressDefaultButton()
					true
				)
			)
			-----------------
			
			for f in maxfiles do
			(
				DialogMonitorOPS.RegisterNotification handleBox id:#hello
				DialogMonitorOPS.Enabled = true
				
				if (loadMaxFile f == true) then
				(
				fname = getFilenameFile(f) + ".bin"
				exportFile fname #noprompt
				continue
				)
				else
				(
				format "%
" f to:batch_file
				)
			)		
		)
		getFilesRecurse edt11.text
		DialogMonitorOPS.unRegisterNotification id:#hello
		DialogMonitorOPS.Enabled = false
	) 
)
createDialog BatchRollout 341 158
)

This loops through a directory, finds all .max files, exports it to .bin (our plugin), and should skip bad files (as it does) and it logs the name of the file that was skipped.

But now I need to log the error that was associated with each file as well.
That’s where I’m at!


#8

Haven’t run the code yet, but seems like that should be exactly what it’s doing in its current form?


#9

silly me…

As I said, the function used by DialogMonitorOps -must- return true/false. You changed the code around so that if the hwnd -isn’t- zero, it continues and returns true. However, you don’t specify what it should return if it -is- zero :slight_smile:

So make it…


if (windowHandle != 0) then
(
	local children = UIAccessor.getChildWindows windowHandle
	for child in children do
	(
		format "%
" (UIAccessor.getWindowText child) to:batch_file
	)
	--local title = UIAccessor.GetWindowText WindowHandle
	--local error = UIAccessor.GetWindowDllDescription WindowHandle
	UIAccessor.PressDefaultButton()
	true
)
else ( false )

Which is really the same code, but with its logic swapped.

That leaves the format to the batch_file, however. ‘batch_file’ doesn’t exist by the time that function is evaluated (which is -before- that section of code is run), so it will try to format to a void. You’ll have to make that external


#10

Here’s just a quick adjustment…
Things to keep in mind are…

  • you have to make sure you close that batch text file when done (otherwise a handle remains open)
  • you should only open that file handle once, at the beginning of the batch
  • because the dialog pops open -before- loadMaxFile gets its result, the error has to be cached so that it can be written out -after- the filename is written out. Easier would be if you always wrote the filename out, and then note that that file did load OK; that way you have a record of -all- files parsed while you’re at it :slight_smile: Then the cache of the error can be dropped as well.

 global batch_file
 global f_error
 
 fn handleBox = 
 (
 	local windowHandle = DialogMonitorOPS.GetWindowHandle()
 	if (windowHandle != 0) then 
 	(
 		local title = UIAccessor.GetWindowText WindowHandle
		format "	Window Title: %
" title to:f_error
 		format "	Window Body:
" to:f_error
 		local children = UIAccessor.getChildWindows windowHandle
 		for child in children do
 		(
 			format "		%
" (UIAccessor.getWindowText child) to:f_error
 		)
 		--local error = UIAccessor.GetWindowDllDescription WindowHandle
 		UIAccessor.PressDefaultButton()
 		true
 	)
 	else ( false )
 )
 
 rollout BatchRollout "Batch Export" width:341 height:158
 (
 	button btn15 "Source" pos:[13,13] width:96 height:24
 	button btn17 "Export" pos:[10,110] width:323 height:35
 	edittext edt11 "" pos:[121,13] width:206 height:24
 
 		fn getFilesRecurse dir =
 		(
 		
 			direc = GetDirectories (edt11.text + "\\*")
 			for d in direc do
 			(
 				join direc (GetDirectories (d+ "\\*"))
 			)
 
 			append direc (edt11.text + "\\") -- Need to include the original top level directory
 
 			maxfiles = #()
 			for de in direc do join maxfiles (getFiles (de + "*.max"))
 			
 			------------------
 			DialogMonitorOPS.unRegisterNotification id:#hello
 			-----------------
 			
 			for f in maxfiles do
 			(
 				DialogMonitorOPS.RegisterNotification handleBox id:#hello
 				DialogMonitorOPS.Enabled = true
 				
 				f_error = stringStream ""
 				if (loadMaxFile f == true) then
 				(
 				fname = getFilenameFile(f) + ".bin"
 				exportFile fname #noprompt
 				continue
 				)
 				else
 				(
 					format "%
" f to:batch_file
 					format "%
" (f_error as string) to:batch_file
 				)
 			)		
 		)
 
 	on btn15 pressed do
 	(
 	source_dir = getSavePath()
 	edt11.text = (source_dir as string)
 	)
 	on btn17 pressed do
 	(
 		batch_file = createFile (edt11.text + "\\batchLog.txt")
 		getFilesRecurse edt11.text
 		close batch_file
 		DialogMonitorOPS.unRegisterNotification id:#hello
 		DialogMonitorOPS.Enabled = false
 	) 
 )
 createDialog BatchRollout 341 158
 

#11

Wow…thanks for the help!

The new script works great, but one thing:

If I keep the true in the UIAccessor function, it doesnt process all the bad files in the specified directory.
However, once the true, else (false) is omitted, it goes through all the bad files that it opens, but of course the evaluation to boolean error is shown.

Edit: is there someway to handle different buttons of different error windows?
I have one error that just shows me an OK, but the next shows me OPEN/CANCEL.
Is there anyway to always get it to push OK or CANCEL, whichever is appropriate?

EDIT 2: actually, I found out why it does that the problem I said at the start…because of edit 1 heh. The window is not getting the proper button press because of the just default option, and the handler is never being stopped. So, it leads to the output only showing 1 error instead of 2.

Thanks again :slight_smile:


#12

no prob :slight_smile: As for specific buttons, you can use ‘.pressButtonByName “OK”’, for example, to press the button labeled “OK”.


#13

Oh and also (sorry to keep bugging you, I only started maxscript-ing last friday :P) when I omit the true/false stuff, it shows me the last error but puts a copy of it with the first error in the batch_file text file.

Ill show you:

With the first true/false in place the text file gives this:
C: ry\ChF_All_Heads.max
Window Title: 3ds Max
Window Body:
OK

     File Open Failed: C:	ry\ChF_All_Heads.max

Which is good, but it doesnt list the second error.

WithOUT the true/false in place the text file gives this:
C: ry\ChF_All_Heads.max
Window Title: Error message
Window Body:
OK

     your texture has a bad width or a bad height 

L:\Projects\Idol\0Trunk\Art\04_3D\Textures\Characters\Female\ChF_Head_0003_Skin_color.png

C: ry\ChF_All_Heads_max9.max
Window Title: Error message
Window Body:
OK

     your texture has a bad width or a bad height 

L:\Projects\Idol\0Trunk\Art\04_3D\Textures\Characters\Female\ChF_Head_0003_Skin_color.png

Both errors are listed; but the second error overlaps the first one for some reason.


#14

oh… try moving the line…


				f_error = stringStream ""

From the handleBox function to just before the loadMaxFile line. The second error will ‘erase’ the first error otherwise (will adjust my post)


#15

Hmm…now it just lists the first error, and if I take out the true/false, it just lists the filenames…troubles troubles :stuck_out_tongue:


#16

hmm. hold on… what ‘two errors’ are you getting, exactly?
Presumably if the max file won’t load, then that’s the only error you should be getting for a single file.
If the max file does load, but throws up a missing files error or so, then that’s technically not an error as far as 3ds Max is concerned…

Just trying to figure out what, exactly, is failing here :slight_smile:


#17

Hm, I think you’re right.

The first file, doesn’t load completely, and gives the file cannot be open error.

The second file, is missing png’s.
BUT, the UIAccessor handles it still…

Is there anyway to handle everything that’s thrown at it? Like every type of window that pops up, to close it and record what happened?


#18

hmmm sure… basically just another print if the file did load, but dialogs -were- detected.


  global batch_file
  global f_error
  
  fn handleBox = 
  (
  	local windowHandle = DialogMonitorOPS.GetWindowHandle()
  	if (windowHandle != 0) then 
  	(
  		local title = UIAccessor.GetWindowText WindowHandle
  		format "	Window Title: %
" title to:f_error
  		format "	Window Body:
" to:f_error
  		local children = UIAccessor.getChildWindows windowHandle
  		for child in children do
  		(
  			format "		%
" (UIAccessor.getWindowText child) to:f_error
  		)
  		--local error = UIAccessor.GetWindowDllDescription WindowHandle
  		UIAccessor.PressDefaultButton()
  		true
  	)
  	else ( false )
  )
  
  rollout BatchRollout "Batch Export" width:341 height:158
  (
  	button btn15 "Source" pos:[13,13] width:96 height:24
  	button btn17 "Export" pos:[10,110] width:323 height:35
  	edittext edt11 "" pos:[121,13] width:206 height:24
  
  		fn getFilesRecurse dir =
  		(
  		
  			direc = GetDirectories (edt11.text + "\\*")
  			for d in direc do
  			(
  				join direc (GetDirectories (d+ "\\*"))
  			)
  
  			append direc (edt11.text + "\\") -- Need to include the original top level directory
  
  			maxfiles = #()
  			for de in direc do join maxfiles (getFiles (de + "*.max"))
  			
  			------------------
  			DialogMonitorOPS.unRegisterNotification id:#hello
  			-----------------
  			
  			for f in maxfiles do
  			(
  				DialogMonitorOPS.RegisterNotification handleBox id:#hello
  				DialogMonitorOPS.Enabled = true
  				
  				f_error = stringStream ""
  				if (loadMaxFile f == true) then
  				(
  					if (filePos f_error != 0) then (
  						format "%
" f to:batch_file
  						format "	ERRORS/WARNINGS WHILE LOADING:
" to:batch_file
  						format "%
" (f_error as string) to:batch_file
  					)
  					fname = getFilenameFile(f) + ".bin"
  					exportFile fname #noprompt
  					continue
  				)
  				else
  				(
  					format "%
" f to:batch_file
  					format "	FAILED TO LOAD:
" to:batch_file
  					format "%
" (f_error as string) to:batch_file
  				)
  			)		
  		)
  
  	on btn15 pressed do
  	(
  	source_dir = getSavePath()
  	edt11.text = (source_dir as string)
  	)
  	on btn17 pressed do
  	(
  		batch_file = createFile (edt11.text + "\\batchLog.txt")
  		getFilesRecurse edt11.text
  		close batch_file
  		DialogMonitorOPS.unRegisterNotification id:#hello
  		DialogMonitorOPS.Enabled = false
  	) 
  )
  createDialog BatchRollout 341 158
  

Which gives output (example):


  c:\zort\\fail.max
  	FAILED TO LOAD:
  	Window Title: 3ds Max
  	Window Body:
  		OK
  		
  		File Open Failed: c:\zort\fail.max
  
  
  c:\zort\\grass11.max
  	ERRORS/WARNINGS WHILE LOADING:
  	Window Title: Missing External Files
  	Window Body:
  		Continue
  		Browse
  		Don't Display This Message at Render Time
  

Note that this doesn’t actually list the missing external files; that specific dialog could be detected (check for the title) and the missing external files be gathered and added to the error string using some of the other 3ds Max functionality, though…

Edit: in fact, with newish 3ds Max versions, just use the “missingExtFilesAction:”[font=Verdana] and similar keywords, passing a by-reference variable that will get an array of the missing files (there’s others for XRefs, etc.). Much easie :slight_smile:

Edit 2: Then again, that requires quiet mode - which supresses any of those other dialogs.
[/font]


#19

Cool thanks man!

I just tried it, works perfectly.

(I had it working last night when I replaced the UIAccessor.pressdefaultbutton() with .closedialog windowHandle, but it displayed doubles on the file name error (but still showed the right error underneath) But this has no doubles at all :slight_smile:

As for the listing of the dll’s that are missing, I dont think my version of 3DS Max (version 9) has the missingExtFilesAction command; it’s not in the reference. Unless its just not listed in there, too bad for me I guess :stuck_out_tongue:

Really, thanks for all your help! :slight_smile:


#20

You’re welcome :wink:

As for that - it should be in there… it was added for 3ds Max 9. Check the topics:

  • Quiet Mode : most extensive documentation
  • 3ds Max File Loading and Saving : just a small reference