Interrupting a running function that was triggered by pushing a button, from within a GUI, can be done by creating a custom event handler, and then adding a "flag" which can be toggled. The flag is used to tell your script to stop running, and is just a variable that we set to 1, when we need to interrupt the function. In addition, we will also need to place an if statement Somewhere in the loop of the running function, which will check if the flag was set, and stop the function accordingly.
This tutorial will be using th OnEvent mode, which should be suitable for many different types of GUIs. We start by turning on the OnEvent mode.
In OnEvent mode, each button will be bound to a function, which is then executed when the button is clicked. I.e.:
$RunBtn = GUICtrlCreateButton("Run", 10, 10, 80, 30) GUICtrlSetOnEvent($RunBtn, "RunnerFunc")
The below is an example of an event handler, which is basically just another user-made function.
Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam) If BitAND($wParam, 0x0000FFFF) = $StopBtn Then $Interrupt = 1 Return $GUI_RUNDEFMSG EndFunc
This checks if the $StopBtn button was pressed, and sets the $Interrupt flag to 1 accordingly. The return $GUI_RUNDEFMSG part allows AutoIt to run its own handler.
For the event handler to work, we will need to tell AutoIt to pass events through the new custom function.
Problems handling $gui_event_close
If a system event is sent, such as when clicking the "X" in the corner of the GUI, and the script is in a loop, the event will either be queued until the loop has finished, or until the stop button is clicked – at which point it will immediately carry out the pending $gui_event_close, which can be confusing to the user.
A way to get around this problem, is to return to the main loop, and then use a flag to check if the RunnerFunc() needs to be started again. If the stop button is clicked, the interrupt function will simply reset the flag, to avoid auto-starting after clicking stop.
Setting a flag will also allow us to avoid GUI flickering, caused by repeatedly changing the stage of elements.
When $gui_event_close is not handled properly, it might appear like the close button is not working, or the script may feel unresponsive. Either way this is something most of us would likely want to avoid.
In OnEvent mode, full script
#include <GUIConstantsEx.au3> #include <WindowsConstants.au3> ; Declare the flags $Interrupt = 0 $EventCheck = 0 ; Todays actions ; Allows the script to carry on from where it stopped $Tc = 0 Opt("GUIOnEventMode", 1) $hGUI = GUICreate("Interrupt a running function", 500, 500) GUISetOnEvent($GUI_EVENT_CLOSE, "ThatExit") $RunBtn = GUICtrlCreateButton("Run", 10, 10, 80, 30) GUICtrlSetOnEvent($RunBtn, "RunnerFunc") $StopBtn = GUICtrlCreateButton("Stop", 10, 10, 80, 30) GUICtrlSetOnEvent($StopBtn, "StopFunc") $MaxActions = GUICtrlCreateInput("300", 220, 30, 70, 20) GUICtrlCreateLabel("Maximum actions to perform:", 120, 30, 85, 50) ; We want the stop button to be hidden when not needed, so we hide it for now. GUICtrlSetState($StopBtn, $GUI_HIDE) GUISetState() GUIRegisterMsg($WM_COMMAND, "_WM_COMMAND") While 1 Sleep(10) If $EventCheck = 1 Then ; This temporary return to the main loop, allows AutoIt to quickly handle system events ; such as GUI_EVENT_CLOSE. If we reached this far, then its safe to assume that ; there was no system events, and we can return to the RunnerFunc. RunnerFunc() EndIf WEnd Func RunnerFunc() ; This check avoids GUI flicker If $EventCheck = 0 Then GUICtrlSetState($RunBtn, $GUI_HIDE) GUICtrlSetState($StopBtn, $GUI_SHOW) EndIf $M = GUICtrlRead($MaxActions) $Interrupt = 0 $EventCheck = 0 For $i = $Tc To $M sleep(1000) ; Check for Interruption If $Interrupt <> 0 Then $EventCheck = 0 Return EndIf ConsoleWrite(">Action: " & $i & @CRLF) $Tc = $i+1 ; Return to allow checking for system events $EventCheck = 1 Return Next ConsoleWrite(">Waiting for next run" & @CRLF) ; Waiting loop here! EndFunc Func StopFunc() GUICtrlSetState($StopBtn, $GUI_HIDE) GUICtrlSetState($RunBtn, $GUI_SHOW) ConsoleWrite(">Stopped" & @CRLF) EndFunc Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam) If BitAND($wParam, 0x0000FFFF) = $StopBtn Then $Interrupt = 1 Return $GUI_RUNDEFMSG EndFunc Func ThatExit() Exit EndFunc