Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

User-defined Dynamic Hotkeys


  • Please log in to reply
34 replies to this topic
jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006
I spent some time searching the forums for a simple GUI allowing users to define their own hotkeys; but had a really hard time finding one. Since I couldn't find a good example relatively quickly, we can use one more. :)

There are four code examples in this post. The first is very simple, while each subsequent example adds features and becomes progressively more complex. All of these examples do at least two things.[*:2v4habpk]Prompt the user for a hotkey.
[*:2v4habpk]Activate the hotkey immediately for a predefined subroutine in the same script.Note this caveat from the AHK documentation.

The Hotkey control has limited capabilities. For example, it does not support mouse/joystick hotkeys...

It also does not support custom combinations using &. The purpose here is to provide a demonstration of the control, rather than AHK's full hotkey capabilities. Since the advice is often given in the help forum, "Use a Hotkey control," here is an example of exactly how a person might do that.

EXAMPLE #1: Single Hotkey Control
This is the simplest example.
#SingleInstance force
#NoEnv
SetBatchLines, -1

Gui, Add, Hotkey,   vHK gLabel     ;add a hotkey control
Gui, Add, CheckBox, vCB x+5, Win   ;add a checkbox to allow the Windows key (#) as a modifier.
Gui, Show,,Dynamic Hotkeys
return
GuiClose:
 ExitApp

Label:
 If HK in +,^,!,+^,+!,^!,+^!            ;If the hotkey contains only modifiers, return to wait for a key.
  return
 If (savedHK) {                         ;If a hotkey was already saved...
  Hotkey, %savedHK%, Label1, Off        ;     turn the old hotkey off
  savedHK .= " OFF"                     ;     add the word 'OFF' to display in a message.
 }
 If (HK = "") {                         ;If the new hotkey is blank...
  TrayTip, Label1, %savedHK%, 5         ;     show a message: the old hotkey is OFF
  savedHK =                             ;     save the hotkey (which is now blank) for future reference.
  return                                ;This allows an old hotkey to be disabled without enabling a new one.
 }
 Gui, Submit, NoHide
 If CB                                  ;If the 'Win' box is checked, then add its modifier (#).
  HK := "#" HK
 If StrLen(HK) = 1                      ;If the new hotkey is only 1 character, then add the (~) modifier.
  HK := "~" HK                          ;     This prevents any key from being blocked.
 Hotkey, %HK%, Label1, On               ;Turn on the new hotkey.
 TrayTip, Label1,% HK " ON`n" savedHK   ;Show a message: the new hotkey is ON.
 savedHK := HK                          ;Save the hotkey for future reference.
return

;This label may contain any commands for its hotkey to perform.
Label1:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

EXAMPLE #2: Multiple Hotkey Controls
This example makes extensive use of dynamic variables.
#SingleInstance force
#NoEnv
SetBatchLines, -1

Loop,5 {
 Gui, Add, Text, xm, Enter Hotkey #%A_Index%:
 Gui, Add, Hotkey, x+5 vHK%A_Index% gLabel     ;add some hotkey controls
 Gui, Add, CheckBox, x+5 vCB%A_Index%, Win     ;add checkboxes to allow the Windows key (#) as a modifier.
}
Gui, Show,,Dynamic Hotkeys
return
GuiClose:
 ExitApp

Label:
 If %A_GuiControl% in +,^,!,+^,+!,^!,+^!     ;If the hotkey contains only modifiers, then return to wait for a key.
  return
 num := SubStr(A_GuiControl,3)               ;Get the number of the hotkey control.  
 If (savedHK%num%) {                         ;If a hotkey was already saved in this control...
  Hotkey,% savedHK%num%, Label%num%, Off     ;     turn the old hotkey off
  savedHK%num% .= " OFF"                     ;     add the word 'OFF' to display in a message.
 }
 If (%A_GuiControl% = "") {                  ;If the new hotkey is blank...
  TrayTip, Label%num%,% savedHK%num%, 5      ;     show a message: the old hotkey is OFF
  savedHK%num% =                             ;     save the hotkey (which is now blank) for future reference.
  return                                     ;This allows an old hotkey to be disabled without enabling a new one.
 }
 Gui, Submit, NoHide
 If CB%num%                                  ;If the 'Win' box is checked, then add its modifier (#).
  %A_GuiControl% := "#" %A_GuiControl%
 If StrLen(%A_GuiControl%) = 1               ;If the new hotkey is only 1 character, then add the (~) modifier.
  %A_GuiControl% := "~" %A_GuiControl%       ;     This prevents any key from being blocked.
 Hotkey,% %A_GuiControl%, Label%num%, On     ;Turn on the new hotkey.
 TrayTip, Label%num%
  ,% %A_GuiControl% " ON`n" savedHK%num%, 5  ;Show a message: the new hotkey is ON.
 savedHK%num% := %A_GuiControl%              ;Save the hotkey for future reference.
return

;These labels may contain any commands for their respective hotkeys to perform.
Label1:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label2:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label3:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label4:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label5:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

EXAMPLE #3: Optional Features
This example adds the following features to example #2.
● Hotkeys are automatically saved to an INI file.
● Duplicate hotkeys are prevented.
#SingleInstance force
#NoEnv
SetBatchLines, -1

#ctrls = 5  ;How many Hotkey controls to add.
Loop,% #ctrls {
 Gui, Add, Text, xm, Enter Hotkey #%A_Index%:
 IniRead, savedHK%A_Index%, Hotkeys.ini, Hotkeys, %A_Index%, %A_Space%
 If savedHK%A_Index%                                       ;Check for saved hotkeys in INI file.
  Hotkey,% savedHK%A_Index%, Label%A_Index%                 ;Activate saved hotkeys if found.
 StringReplace, noMods, savedHK%A_Index%, ~                  ;Remove tilde (~) and Win (#) modifiers...
 StringReplace, noMods, noMods, #,,UseErrorLevel              ;They are incompatible with hotkey controls (cannot be shown).
 Gui, Add, Hotkey, x+5 vHK%A_Index% gLabel, %noMods%           ;Add hotkey controls and show saved hotkeys.
 Gui, Add, CheckBox, x+5 vCB%A_Index% Checked%ErrorLevel%, Win  ;Add checkboxes to allow the Windows key (#) as a modifier...
}                                                                ;Check the box if Win modifier is used.
Gui, Show,,Dynamic Hotkeys
return
GuiClose:
 ExitApp

Label:
 If %A_GuiControl% in +,^,!,+^,+!,^!,+^!    ;If the hotkey contains only modifiers, return to wait for a key.
  return
 num := SubStr(A_GuiControl,3)              ;Get the index number of the hotkey control.
 If (HK%num% != "") {                       ;If the hotkey is not blank...
  Gui, Submit, NoHide
  If CB%num%                                ;  If the 'Win' box is checked, then add its modifier (#).
   HK%num% := "#" HK%num%
  If !RegExMatch(HK%num%,"[#!\^\+]")        ;  If the new hotkey has no modifiers, add the (~) modifier.
   HK%num% := "~" HK%num%                   ;    This prevents any key from being blocked.
  Loop,% #ctrls
   If (HK%num% = savedHK%A_Index%) {        ;  Check for duplicate hotkey...
    dup := A_Index
    Loop,6 {
     GuiControl,% "Disable" b:=!b, HK%dup%  ;    Flash the original hotkey to alert the user.
     Sleep,200
    }
    GuiControl,,HK%num%,% HK%num% :=""      ;    Delete the hotkey and clear the control.
    break
   }
 }
 If (savedHK%num% || HK%num%)
  setHK(num, savedHK%num%, HK%num%)
return

setHK(num,INI,GUI) {
 If INI
  Hotkey, %INI%, Label%num%, Off
 If GUI
  Hotkey, %GUI%, Label%num%, On
 IniWrite,% GUI ? GUI:null, Hotkeys.ini, Hotkeys, %num%
 savedHK%num%  := HK%num%
 TrayTip, Label%num%,% !INI ? GUI " ON":!GUI ? INI " OFF":GUI " ON`n" INI " OFF"
}

;These labels may contain any commands for their respective hotkeys to perform.
Label1:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label2:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label3:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label4:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label5:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

EXAMPLE #4: Special Keys
This example adds the following feature to example #3.
● Special keys which are normally restricted are allowed as hotkeys. This example requires AHK_L 38+.
● The newly allowed hotkeys are: AppsKey, BackSpace, Delete, Enter, Escape, Pause, PrintScreen, Space, Tab.
#SingleInstance force
#NoEnv
SetBatchLines, -1

#ctrls = 5  ;How many Hotkey controls to add?
Loop,% #ctrls {
 Gui, Add, Text, xm, Enter Hotkey #%A_Index%:
 IniRead, savedHK%A_Index%, Hotkeys.ini, Hotkeys, %A_Index%, %A_Space%
 If savedHK%A_Index%                                       ;Check for saved hotkeys in INI file.
  Hotkey,% savedHK%A_Index%, Label%A_Index%                 ;Activate saved hotkeys if found.
 StringReplace, noMods, savedHK%A_Index%, ~                  ;Remove tilde (~) and Win (#) modifiers...
 StringReplace, noMods, noMods, #,,UseErrorLevel              ;They are incompatible with hotkey controls (cannot be shown).
 Gui, Add, Hotkey, x+5 vHK%A_Index% gGuiLabel, %noMods%        ;Add hotkey controls and show saved hotkeys.
 Gui, Add, CheckBox, x+5 vCB%A_Index% Checked%ErrorLevel%, Win  ;Add checkboxes to allow the Windows key (#) as a modifier...
}                                                                ;Check the box if Win modifier is used.
Gui, Show,,Dynamic Hotkeys
return
GuiClose:
 ExitApp

GuiLabel:
 If %A_GuiControl% in +,^,!,+^,+!,^!,+^!    ;If the hotkey contains only modifiers, return to wait for a key.
  return
 If InStr(%A_GuiControl%,"vk07")            ;vk07 = MenuMaskKey (see below)
  GuiControl,,%A_GuiControl%, % lastHK      ;Reshow the hotkey, because MenuMaskKey clears it.
 Else
  validateHK(A_GuiControl)
return

validateHK(GuiControl) {
 global lastHK
 Gui, Submit, NoHide
 lastHK := %GuiControl%                     ;Backup the hotkey, in case it needs to be reshown.
 num := SubStr(GuiControl,3)                ;Get the index number of the hotkey control.
 If (HK%num% != "") {                       ;If the hotkey is not blank...
  StringReplace, HK%num%, HK%num%, SC15D, AppsKey      ;Use friendlier names,
  StringReplace, HK%num%, HK%num%, SC154, PrintScreen  ;  instead of these scan codes.
  If CB%num%                                ;  If the 'Win' box is checked, then add its modifier (#).
   HK%num% := "#" HK%num%
  If !RegExMatch(HK%num%,"[#!\^\+]")        ;  If the new hotkey has no modifiers, add the (~) modifier.
   HK%num% := "~" HK%num%                   ;    This prevents any key from being blocked.
  checkDuplicateHK(num)
 }
 If (savedHK%num% || HK%num%)               ;Unless both are empty,
  setHK(num, savedHK%num%, HK%num%)         ;  update INI/GUI
}

checkDuplicateHK(num) {
 global #ctrls
 Loop,% #ctrls
  If (HK%num% = savedHK%A_Index%) {
   dup := A_Index
   Loop,6 {
    GuiControl,% "Disable" b:=!b, HK%dup%   ;Flash the original hotkey to alert the user.
    Sleep,200
   }
   GuiControl,,HK%num%,% HK%num% :=""       ;Delete the hotkey and clear the control.
   break
  }
}

setHK(num,INI,GUI) {
 If INI                           ;If previous hotkey exists,
  Hotkey, %INI%, Label%num%, Off  ;  disable it.
 If GUI                           ;If new hotkey exists,
  Hotkey, %GUI%, Label%num%, On   ;  enable it.
 IniWrite,% GUI ? GUI:null, Hotkeys.ini, Hotkeys, %num%
 savedHK%num%  := HK%num%
 TrayTip, Label%num%,% !INI ? GUI " ON":!GUI ? INI " OFF":GUI " ON`n" INI " OFF"
}

#MenuMaskKey vk07                 ;Requires AHK_L 38+
#If ctrl := HotkeyCtrlHasFocus()
 *AppsKey::                       ;Add support for these special keys,
 *BackSpace::                     ;  which the hotkey control does not normally allow.
 *Delete::
 *Enter::
 *Escape::
 *Pause::
 *PrintScreen::
 *Space::
 *Tab::
  modifier := ""
  If GetKeyState("Shift","P")
   modifier .= "+"
  If GetKeyState("Ctrl","P")
   modifier .= "^"
  If GetKeyState("Alt","P")
   modifier .= "!"
  Gui, Submit, NoHide             ;If BackSpace is the first key press, Gui has never been submitted.
  If (A_ThisHotkey == "*BackSpace" && %ctrl% && !modifier)   ;If the control has text but no modifiers held,
   GuiControl,,%ctrl%                                       ;  allow BackSpace to clear that text.
  Else                                                     ;Otherwise,
   GuiControl,,%ctrl%, % modifier SubStr(A_ThisHotkey,2)  ;  show the hotkey.
  validateHK(ctrl)
 return
#If

HotkeyCtrlHasFocus() {
 GuiControlGet, ctrl, Focus       ;ClassNN
 If InStr(ctrl,"hotkey") {
  GuiControlGet, ctrl, FocusV     ;Associated variable
  Return, ctrl
 }
}

;These labels may contain any commands for their respective hotkeys to perform.
Label1:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label2:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label3:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label4:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return

Label5:
 MsgBox,% A_ThisLabel "`n" A_ThisHotkey
return


genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Hey - was just using this, I like it and need it, however,
I am wondering why I can't get "pause" (the pause key on the keyboard) to show up in the hotkey box?
It works, by triggering the label, however, "pause" does not show up in the text box?
Thoughts?

Thanks

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006
It's a limitation of the hotkey control. Several keys don't work at all (e.g. Delete, Escape, Tab, Space, Backspace, Print Screen, AppsKey). Pause seems to be unique in that it is recognized but not shown. For me, the Pause key doesn't allow modifiers either.

For this reason, I mentioned at the beginning that this is not a robust program, just a demonstration of the control. On a related note, this morning I was looking at another topic: custom hotkey control, which is supposed to overcome some of these limitations. I haven't tested it, however.

(genmce)
  • Guests
  • Last active:
  • Joined: --
I can get win + pause to work here, but the pause does not show up.
Ah well..
I will check your link above, thanks for the reply.

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Hey - any way to prevent creating hotkeys from already existing hotkeys?
Maybe a ifwinactive from the title of the dynamic hotkey gui for choice of hotkey, that sets a var true, when the previously active hotkey is attempted to assign to something else, it sends an error message, preventing it from being double assigned.
Sounds laborious to put that code in for every hotkey tho... probably because I would not know how to handle that.


This looks interesting but I can't it to work properly, I remember trying it a while back and it working too...
<!-- m -->http://www.autohotke...topic16954.html<!-- m -->
It does not prevent previously assigned hotkeys from being assigned again, afaik.

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006

Hey - any way to prevent creating hotkeys from already existing hotkeys?

EDIT: See example #3 in the OP. I've moved the code there. Most of it is rewritten, so it needs testing again.

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Cool!
Seems to work very well.
Arrays are a mystery to me, I need to learn them.

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


jpboyrox
  • Members
  • 239 posts
  • Last active: Feb 15 2012 04:28 AM
  • Joined: 19 Dec 2010
I love you

jpboyrox
  • Members
  • 239 posts
  • Last active: Feb 15 2012 04:28 AM
  • Joined: 19 Dec 2010
Was that a bit weird of me? Ignore the last comment.

Chicken Pie 4 Tea
  • Members
  • 379 posts
  • Last active: Dec 12 2014 06:46 PM
  • Joined: 18 Aug 2009
@ jpboyrox
expressions of love are not weird, just part of the normal range of human emotions 8)
"Choose your parents wisely"

jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006

Several keys don't work at all...

I've added a fourth example which allows the hotkey control to accept previously restricted 'special' keys.

Aero98
  • Members
  • 112 posts
  • Last active: Mar 29 2013 03:42 PM
  • Joined: 08 Jan 2009
This script is awesome, a job well done. I wondere though, would it be possible to allow dynamically define not only the hotkey, but what the hot key does?

Say the customer wants Windows + Z to always minimize Internet explorer and wants alt + Shift + 3 (#) to paste a predfioned text into email.

Could this be update to allow this?
(\__/) This is Bunny.
(='.'=) Copy and paste Bunny onto your signature.
(")_(") Help Bunny gain World Domination.

jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006
If you want to compile this for distribution, then instead of putting your own code into the hotkey labels, you could put a Run command in each that calls another script or exe. For example...
;These labels may contain any commands for their respective hotkeys to perform. 
Label1: 
 Run, FirstHotkey.ahk 
return 

Label2: 
 Run, SecondHotkey.exe 
return

;etc.
Similarly, any 'predefined' text could come from an external file.

Newbie007
  • Members
  • 82 posts
  • Last active: Sep 30 2011 03:12 PM
  • Joined: 01 Dec 2010
can you make support for mouse wheelup, whelldown, Mbutton???

y, Ive added them in script, they are saved in config.ini but doesnt show on the gui =(

jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006
The Pause key behaves the same way. I'm not sure any more if it's an issue of the Hotkey control in general, or just the way AHK uses it. I will look into this, but perhaps not very quickly. :)