Notifications
Clear all

[Closed] Updating UI elements during operations

I have a button in a rollout labelled as “Copy Files” (via the button caption parameter)
When I click the button the script starts copying things and I would like the button to then read as “Copying Files…” during the operation.
There is also a cancel button that is normally greyed out (enabled:false) which I want to make active during the operation.

The code below does do what I’m asking of it except that the Buttons do not update until after the copy operation finishes so the copy button appears to stay as “Copy Files” and the cancel button remains greyed out.

button CopyIt "Copy Files" pos:[11,171.0] width:155.0 height:20.0
 button CancelCopy "Cancel Copy" pos:[166,171.0] width:95.0 height:20.0 enabled:false
 
 on CopyIt pressed do 
 	(
 		CopyIt.caption = "Copying Files......."
 		CancelCopy.enabled = true
 		--Copy code goes here (takes a while to complete)
 		CopyIt.caption = "Copy Files”
 		CancelCopy.enabled = false
 	)

I think I need some kind of completeRedraw() but for the UI instead of a viewport!

Thanks

18 Replies

the easy way in this situation is to use timer:


try(destroydialog sleepUI) catch()
rollout sleepUI "Sleep UI" width:200
(
	checkbutton action_bt "Execute" width:190 pos:[5,5]
	button cancel_bt "Cancel" width:190 pos:[5,30] enabled:off
	timer execute_tm interval:1
	
	fn action = 
	(
		sleep 2
		action_bt.text = "Execute"
		action_bt.enabled = not (cancel_bt.enabled = off)
		action_bt.state = off
	)
	on execute_tm tick do
	(
		execute_tm.active = off
		action()
	)
	on action_bt changed state do if state do
	(
		action_bt.text = "Executing..."
		action_bt.enabled = not (cancel_bt.enabled = on)
		execute_tm.active = on
	)
)
createdialog sleepUI

the right way is to force the dialog and its children redraw… using for example user32.dll RedrawWindow function.

3 Replies
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

This is great if all you want to do is make the cancel button appear active, but it is still unresponsive due to the synchronous nature of maxscript. You will have to either use a ProgressStart or a dotNet BackgroundWorker

doesn’t this work for that purpose?

windows.sendMessage <DIALOGHWND> 0x0112 0xF120 0

I think you’re the one who showed me this.

(@denist)
Joined: 11 months ago

Posts: 0

no it’s not working… (at least for me). we have to live the function to update the dialog…

(@denist)
Joined: 11 months ago

Posts: 0

here is it:


global _user32
fn CreateUser32Assembly =
(
source = "using System;
"
source += "using System.Runtime.InteropServices;
"
source += "class User32
"
source += "{
"
source += "	[DllImport(\"user32.dll\")]
"
source += "	static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
"
source += "	static uint FULL_REDRAW = 0x0185;
"
source += "	public bool RedrawAllWindow(Int32 hWnd) { return RedrawWindow((IntPtr)hWnd, IntPtr.Zero, IntPtr.Zero, FULL_REDRAW); }
"
source += "}
"

	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"

	compilerParams.ReferencedAssemblies.AddRange #("System.dll")

	compilerParams.GenerateInMemory = on
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)

	_user32 = User32Assembly.CreateInstance "User32"
)
CreateUser32Assembly()

try(destroydialog sleepUI) catch()
rollout sleepUI "Sleep UI" width:200
(
	checkbutton action_bt "Execute" width:190 pos:[5,5]
	button cancel_bt "Cancel" width:190 pos:[5,30] enabled:off
	
	on action_bt changed state do if state do
	(
		action_bt.text = "Executing..."
		action_bt.enabled = not (cancel_bt.enabled = on)
		_user32.RedrawAllWindow (windows.getchildhwnd 0 sleepUI.title)[1]
		sleep 2
 		action_bt.text = "Execute"
 		action_bt.enabled = not (cancel_bt.enabled = off)
 		action_bt.state = off
 	)
)
createdialog sleepUI

Thanks for your help guys!

Denis, the timer is working great!

Is there a good resource to read to allow one to understand the CreateUser32Assembly function you provided?

Rotern, you are very correct! I had overlooked that there would be no way to get the cancel button operational whilst my script was in the middle of another operation.

DenisT, your script triggers an error :

Unknown property: “createInstance” in undefined

_user32 = User32Assembly.CreateInstance "User32"

– Frame:
– User32Assembly: undefined
– csharpProvider: dotNetObject:Microsoft.CSharp.CSharpCodeProvider
– compilerParams: dotNetObject:System.CodeDom.Compiler.CompilerParameters
– source: “using System;

Max 2012 x64, hotfixes SP2.

 lo1

That line should probably read


_user32 = compilerResults.CreateInstance "User32"

There is progress… Now the error is :

Unknown property: "createInstance" in dotNetObject:System.CodeDom.Compiler.CompilerResults
 lo1

sorry, forget what I said, it should be like this:

User32Assembly = compilerResults.compiledAssembly
_user32 = User32Assembly.CreateInstance "User32"
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

thanks, LO. i missed one line coping the piece of code.

this criticism you can address to yourself. If you need to update only some controls why do you redraw whole window?

here is a snippet that updates only the button, and the rest of the dialog hasn’t to flicker:


 global _user32
 fn CreateUser32Assembly =
 (
 source = "using System;
"
 source += "using System.Runtime.InteropServices;
"
 source += "class User32
"
 source += "{
"
 source += "	[DllImport(\"user32.dll\")]
"
 source += "	static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
"
 source += "	static uint FULL_REDRAW = 0x0185;
"
 source += "	public bool RedrawAllWindow(Int32 hWnd) { return RedrawWindow((IntPtr)hWnd, IntPtr.Zero, IntPtr.Zero, FULL_REDRAW); }
"
 source += "}
"
 
 	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
 	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
 
 	compilerParams.ReferencedAssemblies.AddRange #("System.dll")
 	compilerParams.GenerateInMemory = on
 
 	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
 	_user32 = compilerResults.CompiledAssembly.CreateInstance "User32"
 )
 CreateUser32Assembly()
 
 try(destroydialog sleepUI) catch()
 rollout sleepUI "Sleep UI" width:200
 (
 	label info_lb "" width:190 pos:[5,-6] visible:on
 	checkbutton action_bt "Execute" width:190 pos:[5,9]
 	
 	on action_bt changed state do if state do
 	(
 		action_bt.text = "Executing... (Press ESC to cancel)"
 		action_bt.enabled = off
 		setfocus info_lb
 
 		dialog_hwnd = (windows.getchildhwnd 0 sleepUI.title)[1]
 		button_hwnd = (windows.getchildhwnd dialog_hwnd action_bt.text)[1]
 		_user32.RedrawAllWindow button_hwnd
 		
 		while not keyboard.escpressed do
 		(
 			sleep (random 0.01 0.2)
 			 if (getTextExtent info_lb.text).x < 190 then info_lb.text += "." else info_lb.text = "."
 		)
 		info_lb.text = ""
 		 action_bt.text = "Execute"
 		 action_bt.state = not (action_bt.enabled = on)
 	 )
 )
 createdialog sleepUI
 

also there are some tricks those help reduce a mxs dialog flickering during redraw. but it’s a different story.

Now it works thanks

I inserted this snippet in my script and it works perfectly, the window gets refreshed. The only criticism I could make is that the rollout window flickers after each refresh. But other than that it’s great.

Actually the whole window is filled with a single edittext.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

could you post your rollout? i don’t believe that the flickering can be so bad.

Page 1 / 2