[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 Im 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
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.
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.
no it’s not working… (at least for me). we have to live the function to update the dialog…
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.
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
sorry, forget what I said, it should be like this:
User32Assembly = compilerResults.compiledAssembly
_user32 = User32Assembly.CreateInstance "User32"
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.
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.