Interrupt GUI Triggered Functions in AutoIt

How to interrupt running functions and handle system events in AutoIt GUI scripting.

2636 views

Edited: 2017-02-10 22:20

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.

 Opt("GUIOnEventMode", 1)

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.

GUIRegisterMsg($WM_COMMAND, "_WM_COMMAND")

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

Tell us what you think:

  1. How to create a list of selectable items with AutoIt.
  2. Tutorial on how to make GUIs using the AutoIt scripting language.
  3. How to disable and enable AutoIt GUI elements using GUICtrlSetState.
  4. Tutorial on how to add images to AutoIt GUIs while maintaining aspect ratio.

More in: AutoIt GUIs