Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

Breaking an infinite loop with keypress


  • Please log in to reply
11 replies to this topic
anon
  • Guests
  • Last active:
  • Joined: --
Hello.

I would like to execute a loop infinitely, but I would like this loop to stop as soon as a key (any random key) is pressed. I found a similar code on the documentation, but that code executes a loop when a key is held down. Here is what I have so far:

WinWait, NetWinner.com - Windows Internet Explorer, 
IfWinNotActive, NetWinner.com - Windows Internet Explorer, , WinActivate, NetWinner.com - Windows Internet Explorer, 
WinWaitActive, NetWinner.com - Windows Internet Explorer, 
$F1::  ; Make the F1 key into a hotkey (the $ symbol facilitates the "P" mode of GetKeyState below).
Loop  ; Since no number is specified with it, this is an infinite loop unless "break" or "return" is encountered inside.
{
    if not GetKeyState("F1", "P")  ; If this statement is true, the user has physically released the F1 key.
        break  ; Break out of the loop.
    ; Otherwise (since the above didn't "break"), keep clicking the mouse.
MouseClick, left,  451,  527
Sleep, 17000
MouseClick, left,  607,  525
Sleep, 14000

}
return
[Moderator's note - Added code tags]

I have already tried taking out the "not" because I thought that would make the code in parenthesis a true statement. That didnt work. Anyone have any suggestions? Ive already tried searching.

Thanks.

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
This is an extremelly common question. What search terms did you use?

you need on ly a slight variation on the code from this recent thread: <!-- m -->http://www.autohotke... ... light=loop<!-- m -->


Loop, 
{
if (BreakLoop = 1)
  break 
;rest of loop code

}

elsewhere
Esc::
BreakLoop = 1
return
This will break on Escape key.


Also search for the word Suspend, it may help you.

ScottEdge
  • Members
  • 57 posts
  • Last active: May 16 2007 02:54 PM
  • Joined: 13 Aug 2005
For future reference I found I had to put a sleep in my loop for this to work.

LOOP
{
if (BreakLoop = 1) 
  break 
SEND,{END}
SEND,{SPACE}
SEND,{DOWN}
}

Esc would not break this loop.

LOOP
{
if (BreakLoop = 1) 
  break 
SEND,{END}
SEND,{SPACE}
SEND,{DOWN}
sleep,100
}

Escape would break this loop

tic
  • Members
  • 1925 posts
  • Last active: Sep 18 2014 08:41 PM
  • Joined: 22 Apr 2007
I dont think thats what he actually asked (even though he appears to have gone now).

I would like to execute a loop infinitely, but I would like this loop to stop as soon as a key (any random key) is pressed.


so he wants a loop to break on any key being pressed, not esc.

I think he meant something like this:

#Persistent
SetBatchLines, -1
SetKeyDelay, -1
OnExit, HandleExit

SetFormat, Integer, Hex

ThreadID := DllCall("GetCurrentThreadId")
VarSetCapacity(State, 256)
SetTimer, MonitorKeyboard, 30

Loop
{
   Sleep, 100
   If BreakLoop
   Break
   Tooltip, We are in the loop
}
Tooltip, The loop has been broken by a keypress
Return

;################################################################################

MonitorKeyboard:
TID := DllCall("GetWindowThreadProcessId", "UInt", WinActive("A"), "UInt", 0)

If (TID != TIDOld)
{
   DllCall("AttachThreadInput", "UInt", ThreadID, "UInt", TIDOld, "Int", 0)
   DllCall("AttachThreadInput", "UInt", ThreadID, "UInt", TID, "Int", 1)
   TIDOld := TID
}
DllCall("GetKeyboardState", "UInt", &State)

State := ""
Loop, 254
{
	If (*(&State+A_Index) & 0x80)
	State := A_Index
}

If State
{
   If State not in 0x1,0x2,0x4
   BreakLoop = 1
}
Return

;################################################################################

HandleExit:
DllCall("AttachThreadInput", "UInt", ThreadID, "UInt", TIDOld, "Int", 0)
ExitApp

so run that and the tooltip will inform you that youre in a loop, and when you press any key itll break. have only specified that left/right/middleclick on mouse dont break the loop. any other mouse buttons would need to be added

Edit: Maybe he didnt mean that :wink: i hate it when people ask something and then just leave, and dont even say thank you or "that worked" or "that didnt work" ! :)

kenn
  • Members
  • 407 posts
  • Last active: Feb 10 2014 11:02 AM
  • Joined: 11 Oct 2010
Thank you Tariq, your posts still help people.

pb3
  • Guests
  • Last active:
  • Joined: --
Yep. Helped me. Thanks for the posting :)

torp
  • Members
  • 1 posts
  • Last active: Aug 08 2012 12:51 AM
  • Joined: 08 Aug 2012

Loop, 
{
if (BreakLoop = 1)
  break 
;rest of loop code

}

elsewhere
Esc::
BreakLoop = 1
return
This will break on Escape key.


Hmmm... This is what I want to do but the code snippet does not seem to work. What would a working example look like? I've tried this (starting with your example):

Esc::
BreakLoop = 1 
return

Loop,
{
  if (BreakLoop = 1)
    break
  MsgBox Hello
  Sleep 1000
}

Exit

What I want is to see the MsgBox come up once per iteration of the loop, but, if I hit the hotkey (Esc), then the program will exit.

My observation is that the loop never runs...

It seems like defining the hotkey stops all execution of the script. Even this simpler script never shows the messsage box:

Esc::return
MsgBox Hello

Thanks for helping me understand this...

girlgamer
  • Moderators
  • 2808 posts
  • Last active: Today, 06:21 AM
  • Joined: 04 Jun 2010
The first hotkey or hotstring definition is the presumptive end of the autoexecute section of the script. assuming, of course, that a return is not encountered first. What that basically means is that once you define a hotkey in your script every command after that has to either be a) part of the autoexecute section, B) in a hotkey/hotstring definition, c) inside a function, d) in a named subroutine, or e) be an AutoHotkey system command (usually starting with a #) or it will not be executed. Anything outside those structures is considered "orphan" code.

MasterFocus
  • Moderators
  • 4322 posts
  • Last active:
  • Joined: 08 Apr 2009
Just like girlgamer said:
The Top of the Script (the Auto-execute Section) - <!-- m -->http://www.autohotke...cripts.htm#auto<!-- m -->

  • Guests
  • Last active:
  • Joined: --

Just like girlgamer said:
The Top of the Script (the Auto-execute Section) - <!-- m -->http://www.autohotke...cripts.htm#auto<!-- m -->


Thanks two both of you for your helpful replies!

With my enhanced understanding, I've got something that looks like what I want:


SetTimer, Loop, 1

Esc::BreakLoop=1

Loop:
Loop {
  if ( BreakLoop = 1 )
    exitApp
  MsgBox Hello
  Sleep 1000
}

This will display the MsgBox immediately, and keep doing so until I hit Esc...

I'm still not sure if this is "the AHK way" (preferred style) to do something
like this, but I think this is what I need for my purposes...

Additionally, I noticed that you can define more that one hotkey and then
implement a mini "state engine" based on hotkey "events" (my ultimate goal).
For example:

MyState = 0 ; initial state

SetTimer, Loop, 1

Esc::BreakLoop=1

F1::MyState=1
F2::MyState=2

Loop:
Loop {
  if ( BreakLoop = 1 )
    exitApp
  MsgBox % MyState
  Sleep 1000
}

This will display the MsgBox immediately and inform you of the current state (0,
1 or 2). State is changed between 1 and 2 by hitting F1 and F2 respectively.
Hit Esc to exit the program. The hotkeys work even if a MsgBox is currently
being displayed... Nifty! Thanks!

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
i would go one of a few ways with your script. You are technically calling the loop label every millisecond, and each loop label also has an infinite loop inside.

In the first version we remove settimer, since the loop will loop itself
MyState = 0 ; initial state

Gosub, Loop

Esc::BreakLoop=1

F1::MyState=1
F2::MyState=2

Loop:
Loop {
  if ( BreakLoop = 1 )
    exitApp
  MsgBox % MyState
  Sleep 1000
}
Return

or you can use settimer to set the action into motion every second. This is different in that the previous version has a 1 second delay between when you cliock ok in a box and the next one pops up. This version instead pops up a msgbox every second.

MyState = 0 ; initial state

SetTimer, Loop, 1000   ;Call loop every 1000 ms

Esc::BreakLoop=1

F1::MyState=1
F2::MyState=2

Loop:
  if ( BreakLoop = 1 )
    exitApp
  MsgBox % MyState
Return

If you start your loop before you define hotkeys, you don't need any acrobatics

MyState = 0 ; initial state

Loop {
  if ( BreakLoop = 1 )
    exitApp
  MsgBox % MyState
  Sleep 1000
}
Return   ;End of auto-execute section

Esc::BreakLoop=1

F1::MyState=1
F2::MyState=2

I would usually say that exitapp is not a nice way to break a loop, but if you want the script to close, that will certainly do it. I also would use Tooltip instead of Msgbox when called in a loop to reduce clicking OK buttons

MyState = 0 ; initial state

Loop {
  Tooltip % MyState
  Sleep 1000
}
Return

Esc::ExitApp

F1::MyState=1
F2::MyState=2

and this reduces down to this basic version with settimer
MyState = 0 ; initial state
SetTimer, ShowMyState, 1000
Return

ShowMyState:
  Tooltip % MyState
Return

Esc::ExitApp

F1::MyState=1
F2::MyState=2

Hope this illustrates some different ideas for you.

  • Guests
  • Last active:
  • Joined: --

i would go one of a few ways with your script...


Thanks engunneer for your excellent reply!

The way your code samples evolve is elucidating.

I was prone to acrobatics as I was hell-bent on defining my hotkeys at the top. I see now that to do this is to fight with the "natural" structure that an AHK script wants to have.

Your penultimate example strikes me as being the most elegant and I'll use that structure as the skeleton for my script (the Tooltip is neat too!).

Thanks again!

- Tor