Dynamically Create AutoHotkey Hotkey to Function/Subroutine
Asked Answered
N

5

8

The AutoHotkey command Hotkey allows for the creation of dynamic hotkeys at runtime, but its syntax and documentation seems to limit it to built-in or existing labels/subroutines, which makes it much less useful:

Hotkey, KeyName [, Label, Options]

Is there a way to get it to work like regular, hard-coded hotkeys? For example:

#z::MsgBox foobar        ; Typical, hard-coded hotkey pops up a message-box

Hotkey, z, MsgBox foobar ; Nope; complains about missing label “MsgBox foobar”

It looks like it might be possible due to the following line from the manual, however it is not clear how it would work:

Label - Both normal labels and hotkey/hotstring labels can be used.

Names answered 12/10, 2012 at 3:12 Comment(4)
Apparently, I'm not getting it. Could you give more detail about what you are looking for?Homoiousian
The example above is exactly what I am looking for; a way to do the following without having to manually create a labeled subroutine for each and every hotkey dynamically created: Hotkey, z, MsgBox foobarNames
It's not possible to do exactly how you describe. Can you give a realistic example of what you want to do?Devotional
@FakeRainBrigand, in the script that brought this issue up, I have ~200 hotkeys that call a function (which has optional arguments), passing it different parameters. I can’t create separate labels/subroutiens for each and every one, and even if I could, I don’t want to because it would make it difficult to add or remove hotkeys instead of just modifying a single line. I may be able to work around this with the current script by using arrays and A_ThisHotkey, but even if that works, it doesn’t really address the issue of dynamic hotkeys to functions (or what dynamic labels are).Names
D
8

Doing exactly what you want isn't possible in AutoHotkey. This is the closest way I can think of.

Call this file Hotkeys.ahk, and put it in My Documents/AutoHotkey/Lib. Alternatively make a folder called Lib, and put it in the same directory as your main script.

Hotkeys := {}

Hotkey(hk, fun, p*) {
    global hotkeys
    hotkeys[hk] := {}
    hotkeys[hk].fun := fun
    hotkeys[hk].p := p
    Hotkey, %hk%, HandleHotkey
}

HandleHotkey:
hotkeys[A_ThisHotkey].fun(hotkeys[A_ThisHotkey].p*)
return

Here's an example script that you could use it with.

Hotkey("e", "msgbox", "foobar")

MsgBox(msg) {
    msgbox % msg
}

#Include <Hotkeys>

The first parameter is the hotkey, the second is the function to call, and everything after that is passed to the function.

Devotional answered 13/10, 2012 at 17:13 Comment(7)
Yup, this is what I was thinking of; creating a generic handler. It doesn’t fix the Hotkey command’s limitation, but it’s a satisfactory solution. I like that your example provides the ability to supply different arguments so that other functions can be called. One note though, the Include has to be moved to the end of the script because it terminates the auto-execute section, so the calls to the Hotkey function are never done if it’s at the start.Names
A couple of notes. (1) I had to remove the asterisk from the function definition in order to get it to work (Hotkey(hk, fun, p*)Hotkey(hk, fun, p) (2) For some reason using E as the key to the associative array does not work (A, BZ, ^E, +E, etc. all work). It looks like a bug with AHK’s array handling (or maybe it’s a special key, but not as far as I can find; only base seems to be special).Names
The asterisk lets you pass any number of parameters, if you remove it, you can only pass one (not a big deal if that's all you need). You could still pass an array if you like, though.Devotional
Also, try using lowercase hotkeys. e means the 'e' key, E means Shift-e. e works for me.Devotional
e means the 'e' key, E Nope; +e and +E mean Shift+E, both e and E mean E. e doesn’t work either; for some reason it seems to be invalid as a key. (I’m using _L 1.1.7.1 and _L 1.0.48.5 doesn’t support {} and replacing it with Object() did nothing at all.)Names
An old answer, but I just started with AHK and this just saved me so many ugly lines of code. Thank you! :)Ripley
Because the hotkeys is like a Map; \ if you have duplicate hotkey in different #IfWinActive (& use them as the key of the Map); \ the latter hotkey will overwrite the prev hotkey.Swoop
W
10

This is a refinement of FakeRainBrigand's answer. It is used exactly the same:

Hotkey("x", "Foo", "Bar") ; this defines:  x:: Foo("Bar")

Changes from the original:

  1. Prevent accidental auto-execute of the handler subroutine by tucking it into the function.

  2. Allowing me to reduce namespace pollution by narrowing the scope of the hotkeys variable from global to static.

  3. Optimizations: fun is looked up only once (using Func()) at hotkey definition time; At invocation time, object lookups reduced four to two by splitting hotkeys into two objects funs and args;

This still relies of course on the _L version of AutoHotKey because of Object notation and variadic arg* syntax.

Hotkey(hk, fun, arg*) {
    Static funs := {}, args := {}
    funs[hk] := Func(fun), args[hk] := arg
    Hotkey, %hk%, Hotkey_Handle
    Return
Hotkey_Handle:
    funs[A_ThisHotkey].(args[A_ThisHotkey]*)
    Return
}
Wearing answered 29/7, 2013 at 19:16 Comment(0)
D
8

Doing exactly what you want isn't possible in AutoHotkey. This is the closest way I can think of.

Call this file Hotkeys.ahk, and put it in My Documents/AutoHotkey/Lib. Alternatively make a folder called Lib, and put it in the same directory as your main script.

Hotkeys := {}

Hotkey(hk, fun, p*) {
    global hotkeys
    hotkeys[hk] := {}
    hotkeys[hk].fun := fun
    hotkeys[hk].p := p
    Hotkey, %hk%, HandleHotkey
}

HandleHotkey:
hotkeys[A_ThisHotkey].fun(hotkeys[A_ThisHotkey].p*)
return

Here's an example script that you could use it with.

Hotkey("e", "msgbox", "foobar")

MsgBox(msg) {
    msgbox % msg
}

#Include <Hotkeys>

The first parameter is the hotkey, the second is the function to call, and everything after that is passed to the function.

Devotional answered 13/10, 2012 at 17:13 Comment(7)
Yup, this is what I was thinking of; creating a generic handler. It doesn’t fix the Hotkey command’s limitation, but it’s a satisfactory solution. I like that your example provides the ability to supply different arguments so that other functions can be called. One note though, the Include has to be moved to the end of the script because it terminates the auto-execute section, so the calls to the Hotkey function are never done if it’s at the start.Names
A couple of notes. (1) I had to remove the asterisk from the function definition in order to get it to work (Hotkey(hk, fun, p*)Hotkey(hk, fun, p) (2) For some reason using E as the key to the associative array does not work (A, BZ, ^E, +E, etc. all work). It looks like a bug with AHK’s array handling (or maybe it’s a special key, but not as far as I can find; only base seems to be special).Names
The asterisk lets you pass any number of parameters, if you remove it, you can only pass one (not a big deal if that's all you need). You could still pass an array if you like, though.Devotional
Also, try using lowercase hotkeys. e means the 'e' key, E means Shift-e. e works for me.Devotional
e means the 'e' key, E Nope; +e and +E mean Shift+E, both e and E mean E. e doesn’t work either; for some reason it seems to be invalid as a key. (I’m using _L 1.1.7.1 and _L 1.0.48.5 doesn’t support {} and replacing it with Object() did nothing at all.)Names
An old answer, but I just started with AHK and this just saved me so many ugly lines of code. Thank you! :)Ripley
Because the hotkeys is like a Map; \ if you have duplicate hotkey in different #IfWinActive (& use them as the key of the Map); \ the latter hotkey will overwrite the prev hotkey.Swoop
H
0

Is this what you are looking for?

#Persistent
#SingleInstance Force
#installKeybdHook

Hotkey, #z, MyLabel

MyLabel:
    MsgBox,OK
Return
Homoiousian answered 12/10, 2012 at 8:25 Comment(0)
M
0

With newer ahk version you can now use functions as label argument. See https://www.autohotkey.com/docs/commands/Hotkey.htm

Maggiemaggio answered 4/4, 2021 at 18:1 Comment(0)
S
0

In Hotkey, KeyName, Label, Options; You can use a Function Object instead of a label.

ex

#NoEnv
#SingleInstance Force 
#Persistent

func_R := Func("print").bind("RRRRRRR")
func_P := Func("print").bind("PPPPPPP")

Hotkey, % "^r", % func_R ; <<<
Hotkey, % "^p", % func_P

print(output) {
  ToolTip, % output
}


; ;@note; Dont know why you cannot do the following::
; ;@note;
; ;@note; Hotkey, % "^r", % Func("print").bind("RRRRRRR")
; ;@note; Error:  Nonexistent hotkey. -- ^r

Press ^r, the corresponding function will be executed.

ref

~// []
~// Hotkey - Syntax & Usage | AutoHotkey
~// Creates, modifies, enables, or disables a hotkey while the script is running.

[]
[v1.1.20+]: If not a valid label name, this parameter can be the name of a function, or a single variable reference containing a function object. For example, Hotkey #z, %FuncObj%, On or Hotkey #z, % FuncObj, On.

https://www.autohotkey.com/docs/v1/lib/Hotkey.htm

~// []
~// Function Hotkeys [v1.1.20+]
~// One or more hotkeys can be assigned a function by simply defining it immediately after the hotkey label as in this example:
~//
~// https://www.autohotkey.com/docs/v1/Hotkeys.htm#Function \

Swoop answered 20/3, 2023 at 16:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.