I have a script wich uses several mousemoves and, when needed, I press F3 to stop the moves forcing a reload or ESC to stop the moves exiting the app. It works fine with no issues in the regular speed.
The problem is: when I decrease the mousespeed and increase the mousedelay, the script becomes unstoppable: I press the F2, F3 and ESC keys several times (trying to make it stops) but the script ignores it and continues running the mousemoves.
Here's a minimal example of my problem:
SendMode Input
$F1::
SetDefaultMouseSpeed, 50
SetMouseDelay, 30
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
Mousemove, 200, 200
Mousemove, 600, 600
SoundPlay, *48
return
$F2:: Pause
$F3:: Reload
$ESC:: ExitApp
Why does it happens and how can I fix it (how can I stop the script even with low speed+high delay)?
ps: F1 key starts the mousemoves sequence.
MouseMove with non-zero speed cannot be interrupted by another hotkey inside the same AHK script because Autohotkey is single-threaded internally (it just emulates thread-like behavior) and performs a blocking sleep for each mouse micromovement until it reaches the overall distance you specified.
Solutions:
Check whether a hotkey was pressed after each MouseMove, but it's non-instantaneous;
Implement your own MouseMove that checks GetKeyState after each 1px movement;
Use a master script with hotkeys that run/pause/stop the child:
Stopping: trivial, use Process Close
Running: the child should hide tray icon otherwise it'll stay after process close
Pausing: nontrivial but there are solutions (see also the underlying mechanics).
Related
For starters, I'm really new to AutoHotKey. I was making a script that looks for a color (with a little variance) at a specific coordinate, & if it finds that color, type the specified number.
It should continuously sift through the five coordinate spots in the loop and send the number that matches the color, but it finds the number every single time. Just a constant stream of "123451234512345" I've tried completely removing the color from the screen, but it still manages to find it.
I'm sure that I probably have something wrong with my code. Getting a headache trying to figure out what. Would love some help.
Loop
{
CoordMode, Pixel, Screen
PixelGetColor, color1x, color1y, 200, 200, 200, 200, 0xFF0000, 3, FastRGB
if ErrorLevel = 0
Send, 1
else
CoordMode, Pixel, Screen
PixelGetColor, color2x, color2y, 400, 400, 400, 400, 0xFF0000, 3, FastRGB
if ErrorLevel = 0
Send, 2
else
CoordMode, Pixel, Screen
PixelGetColor, color3x, color3y, 600, 600, 600, 600, 0xFF0000, 3, FastRGB
if ErrorLevel = 0
Send, 3
else
CoordMode, Pixel, Screen
PixelGetColor, color4x, color4y, 800, 800, 800, 800, 0xFF0000, 3, FastRGB
if ErrorLevel = 0
Send, 4
else
CoordMode, Pixel, Screen
PixelGetColor, color5x, color5y, 1000, 1000, 1000, 1000, 0xFF0000, 3, FastRGB
if ErrorLevel = 0
Send, 5
else
return
}
Numpad9::pause
esc::ExitApp
There are a couple of things that are wrong with your code:
You're using PixelGetColor incorrectly. It stores the color of the pixel at the desired coordinates in OutputVar, not compare the color of the pixel at that coordinate with a specified color.
You're checking the ErrorLevel. It's set to 1 if there's a problem or 0 otherwise.
If you need to scan the five locations for each loop, there's no need for the else statement (you're also using it incorrectly in this case).
As what 0x464e said, you need to state CoordMode only once at the start of the script.
In your case, PixelGetColor always succeeds in getting the color of the pixel, which means ErrorLevel is always set to zero. This gives you the impression that it's always "returning true".
You should be checking the OutputVar instead. Try this:
DESIRED_COLOR := "0xFF0000"
CoordMode, Pixel, Screen
Loop
{
PixelGetColor, color1, 200, 200
if (color1 = DESIRED_COLOR)
Send, 1
PixelGetColor, color2, 400, 400
if (color2 = DESIRED_COLOR)
Send, 2
PixelGetColor, color3, 600, 600
if (color3 = DESIRED_COLOR)
Send, 3
PixelGetColor, color4, 800, 800
if (color4 = DESIRED_COLOR)
Send, 4
PixelGetColor, color5, 1000, 1000
if (color5 = DESIRED_COLOR)
Send, 5
}
Numpad9::pause
esc::ExitApp
This is a script that is supposed to use the windows snipping tool to sequentially screencap pictures from an online gallery. If anyone can spot the problem that would be much appreciated.
#SingleInstance, Force
a := 112
name :=1
x:: Pause, Toggle
y:: ExitApp
Loop, a
{
MouseClickDrag, Left, 1300, 210, 645, 140
Sleep, 100
MouseClick, Left, 1277, 1038, 0, 5
sleep, 100
MouseClick, Left, 838, 64, 0, 5
sleep, 100
SendInput, %name%
name ++
sleep, 100
SendInput, {Enter}
Sleep, 100
MouseClickDrag, Left, 670, 13, 1393, 153
Sleep, 100
MouseClick, Left, 500, 490, 0, 5
Sleep, 300
MouseClick, Left, 500, 490, 0, 5
SendInput, {Right}
}
Two problems in it.
Firstly, your loop is unreachable code.
Code execution stops when your first hotkey label is encountered. This is called the Auto-execute Section.
Secondly, loop doesn't take an expression to the first parameter. It takes a legacy text parameter. So you'd either want to use the legacy way of referring to a variable, which would be %a%, but personally I'd push you towards using the modern expression syntax and forcing an expression to that parameter by starting the parameter off with a % followed up by a space. So Loop, % a.
To read more about legacy syntax vs expression syntax, see this page of the documentation.
Here's your fixed script:
#SingleInstance, Force
a := 112
name := 1
Sleep, 3000
Loop, % a
{
MouseClickDrag, Left, 1300, 210, 645, 140
Sleep, 100
MouseClick, Left, 1277, 1038, 0, 5
Sleep, 100
MouseClick, Left, 838, 64, 0, 5
Sleep, 100
SendInput, % name
name++
Sleep, 100
SendInput, {Enter}
Sleep, 100
MouseClickDrag, Left, 670, 13, 1393, 153
Sleep, 100
MouseClick, Left, 500, 490, 0, 5
Sleep, 300
MouseClick, Left, 500, 490, 0, 5
SendInput, {Right}
}
;this return here ends the auto-execute section
;but of course, in this specific case it's totally
;useless since the next line is a hotkey label
;which would also stop the auto-execute section
return
;even though the code execution gets stuck inside the loop,
;hotkeys can be specified down here
;they're created even before the auto-execute section starts
x::Pause, Toggle
y::ExitApp
I have 2 scripts I want to merge in one file, however when I put them together, only the first one gets executed:
Script 1:
#if WinActive("ahk_exe program.EXE")
#Persistent
Loop
{
WinWaitActive, Wizard
Send, !{F4}
}
Return
Script 2:
#if WinActive("ahk_exe program2.EXE")
#Persistent
Loop
{
WinWait, ahk_class bosa_sdm_Mso96
; IfWinNotActive, ahk_class bosa_sdm_Mso96, ,WinActivate, ahk_class bosa_sdm_Mso96
; WinWaitActive, ahk_class bosa_sdm_Mso96
; Sleep, 0
ControlMove, RichEdit20W6, 20, 850, 750, 25 ;Adress box
ControlMove, SysTreeView321, , , 800, 700
ControlMove, TreeViewCFParent1, , , 1000, 700
ControlMove, SysTreeView322, , , 800, 700
ControlMove, TreeViewParent1, , , 760, 940
WinMove, ahk_class bosa_sdm_Mso96, , 600, 50, 1000, 900 ; 900 width
}
Return
I tried removing or changing the location of "#Persistent", "Loop", "Return".. or adding #if at the end of each script.. still only the first one gets executed.. Even when trying to #Include the first script in the second, the first only gets executed. Maybe it needs "else" or something.. not sure..
The #If directive is only used for creating context-sensitive hotkeys and hotstrings.
If you use WinWait or WinWaitActive the script waits until the (first) window exists or becomes active and doesn't go any further to move the second window.
Without WinWait the CPU usage of the script is high.
The better solution in this case is SetTimer:
#Persistent
SetTimer, Close_Move_Windows, 500
return
Close_Move_Windows:
IfWinActive, Wizard
WinClose
IfWinExist, ahk_class bosa_sdm_Mso96
{
WinGetPos, X, Y, Width, Height, ahk_class bosa_sdm_Mso96
If (X != 600 || Y != 50 || Width != 900 || Height != 1000) ; "!" means "NOT" and "||" means "OR"
{
ControlMove, RichEdit20W6, 20, 850, 750, 25 ;Adress box
ControlMove, SysTreeView321, , , 800, 700
ControlMove, TreeViewCFParent1, , , 1000, 700
ControlMove, SysTreeView322, , , 800, 700
ControlMove, TreeViewParent1, , , 760, 940
WinMove, ahk_class bosa_sdm_Mso96,, 600, 50, 900, 1000 ; 900 width
}
}
Return
I need to pause a spacebar spamming macro with a key like f10, here is my code
c::
Loop
{
if not GetKeyState("c", "P")
break
Sleep 25 ; ms
Send {space}
}
return
I tried to add a pause similar to the getkeystate in and out of the loop but to no avail.
I always do something to the extent of this:
c::
Toggle := !Toggle
While Toggle {
; do whatever you need to do here
}
Return
An additional advantage here is that there's only one hotkey to remember. Press once to begin the endless loop. Press again to stop.
q::
Loop
{
Click, right,
Mousemove, 0, 110, 5, Rel
click, left
Mousemove, 350, -473, 5, rel
click, left
Mousemove, -350, 363, 5, rel
}
return
#p::Pause,Toggle
https://autohotkey.com/board/topic/95308-endless-loop-with-hotkey-pause/
I would like my code to show 'paused' upon script being paused and show 'running' for only a short interval after the script is unpaused.
However, it did not work as expected.
My code below (commented the problematic issue):
NumpadEnter:: ; script is paused on said key and reactivated on said key
Suspend
ToolTip, PAUSED, 200, 250
Pause ,, 1
; ; ToolTip, RUNNING, 200, 250 ; Code segment when un-commented does not work as it should
; ; Sleep 500 ;
SetTimer, RemoveToolTip, 1
return
RemoveToolTip:
SetTimer, RemoveToolTip, Off
ToolTip
return
Thank you so much!!
Try this:
NumpadEnter::
Suspend
Pause ,,1
if A_IsPaused {
ToolTip, PAUSED, 200, 250
} else {
ToolTip, RUNNING, 200, 250
SetTimer RemoveToolTip, 1000
}
return
RemoveToolTip:
ToolTip
SetTimer, RemoveToolTip, Off
return