文章目录

autoHotkey — 连击/双击/重复 按键触发基本环境为什么要做这个事情代码

autoHotkey — 连击/双击/重复 按键触发

基本环境

autohotkeySciTE4AutoHotkey-Plus 编辑器, 非必须,打算长用ahk的可以考虑.autohotkey基础教程系列(一) ———— 怎么学,哪里学,有哪些现成的demo

为什么要做这个事情

是为了让少数的常用的快捷键发挥出更多的作用. 设计快捷键的思维应是增强现有的按键组合,而不是为了新功能去开发冷门的组合键. 而连续按键的检测,是为了引入上下文特征而用的. 并不是简单地,同一个组合按键固定连续按下两次就固定触发一个功能.

个人认为的快捷键设计原则:

键少功能多. 少量的人机接口方式,实现更多的功能.连击最多2次单击,且由双手食指触发. 因为食指灵敏, 且我们习惯了鼠标的双击动作.如果为双键结合,那么修饰键最好按整体功能进行分离,例如 shift 用来修饰编辑文本的. alt用来处理窗口操作的.尽可能保留已经习惯的触发键,例如s为保存,f为查找. c为复制不同软件的同概念性的快捷键应该保持一致. 例如 ctrl f 为一般的搜索功能,那么可以把百度激活搜索框的快捷键设置为ctrl f. 这样就不需要每次都用鼠标去单击搜索框,也不需要安装vim等插件. 因为是搜索引擎.没有必要保留原本的ctrl+F的功能.可将同概念性的功能按软件的先后层次进行顺序切换,例如,在百度里面, 如果没有在输入框中,则按一次ctrl+f先激活输入框,再按一次搜索打开的标签或者直接跳转到系统的全局搜索 ,例如everything这类工具,再按一次则切换回浏览器的输入框,完全没有必要设置那么多个快捷键。设计的时候先考虑新添加的功能是否与旧功能具有概念相似性。切记一上来就是一个新的快捷键。这是很多人一开始常犯的毛病。 举个具体的例子,在pycharm中,单独使用了一个ctrl +d 作为重复一行或者选中内容的快捷键. 其实完全没必要这么做. 只要在原始的复制ctrl+c加一个是否选取内容的判断就可以,当没有选择内容的时候,默认就是重复复制一行. 在选中多行,或者一行中局部内容的时候,直接重复填充反而会很乱. 还要额外去记一个快捷键.

代码

官方版本 在中文手册中,是这么来检测的. 他的原理是按下某个热键的时候,同时触发一个定时器,然后在这个计时器被调用之前去累计按下的累计的次数,在计时器到时的时候才触发相应的功能. 这种设计方式有多种局限,看后面.

这部分实际上当初参考了某个博客,但是找不到了,所以这边没有引用.

$1::

sendinput,+1 ;这边是自己修改的,为了弥补按一下也许等待计时器的问题

if winc_presses_1 > 0 ; SetTimer 已经启动, 所以我们记录键击.

{

winc_presses_1 += 1

return

}

winc_presses_1 = 1

SetTimer, KeyWin_1_大写, %tout% ; 在 100 毫秒内等待更多的键击.

return

KeyWin_1_大写:

SetTimer, KeyWin_1_大写,off

if winc_presses_1 = 2

{

sendinput,{backSpace}

sendinput,{backSpace}

sendinput,+1

}

winc_presses_1 = 0

return

局限性:

导致功能的触发点延迟,即无论你按多少次,都得等到计时器的时间到了 才能触发相应的功能.代码冗长, 当需要设置多个按键时,会发现代码量蹭蹭的就上去了.不灵活,每个热键都需要设置一个定时器,代码不能够复用.可读性差,看代码就知道了.没有检测其他按键的插入 . 当打字速度快的时候, 即便不是连续按下热键,而是穿插按下,也会被认为是连续按下. 例如 我们的理想状态是 AA ->触发 但是如果短时内输入的是ABA,同样也会被认为是AA,而触发功能。

他人版本 这个版本的功能比较强,他提供的 "RapidHotkey"函数,可以让我们传入多个待触发的命令,并指定这些命令是在按下多少次后触发的. 但是他也有很严重的局限性

同样仅有当约定的多键等待时间到了,才能触发功能.当有修饰键参与热键组合的时候,只能等到修饰键也一起弹起的才能够触发,这对于一些想要快速连发的功能而言是满足不了需求的.例如用shift+字母映射方向键的时候.不方便触发一些复杂的操作.同样,不检测其他按键的插队问题.

;http://www.autohotkey.com/board/topic/35566-rapidhotkey/

RapidHotkey(keystroke, times="2", delay=0.2, IsLabel=0)

{

Pattern := Morse(delay*1000)

If (StrLen(Pattern) < 2 and Chr(Asc(times)) != "1")

Return

If (times = "" and InStr(keystroke, """"))

{

Loop, Parse, keystroke,""

If (StrLen(Pattern) = A_Index+1)

continue := A_Index, times := StrLen(Pattern)

}

Else if (RegExMatch(times, "^\d+$") and InStr(keystroke, """"))

{

Loop, Parse, keystroke,""

If (StrLen(Pattern) = A_Index+times-1)

times := StrLen(Pattern), continue := A_Index

}

Else if InStr(times, """")

{

Loop, Parse, times,""

If (StrLen(Pattern) = A_LoopField)

continue := A_Index, times := A_LoopField

}

Else if (times = "")

continue := 1, times := 2

Else if (times = StrLen(Pattern))

continue = 1

If !continue

Return

Loop, Parse, keystroke,""

If (continue = A_Index)

keystr := A_LoopField

Loop, Parse, IsLabel,""

If (continue = A_Index)

IsLabel := A_LoopField

hotkey := RegExReplace(A_ThisHotkey, "[\*\~\$\#\+\!\^]")

IfInString, hotkey, %A_Space%

StringTrimLeft, hotkey,hotkey,% InStr(hotkey,A_Space,1,0)

backspace := "{BS " times "}"

keywait = Ctrl|Alt|Shift|LWin|RWin

Loop, Parse, keywait, |

KeyWait, %A_LoopField%

If ((!IsLabel or (IsLabel and IsLabel(keystr))) and InStr(A_ThisHotkey, "~") and !RegExMatch(A_ThisHotkey

, "i)\^[^\!\d]|![^\d]|#|Control|Ctrl|LCtrl|RCtrl|Shift|RShift|LShift|RWin|LWin|Alt|LAlt|RAlt|Escape|BackSpace|F\d\d?|"

. "Insert|Esc|Escape|BS|Delete|Home|End|PgDn|PgUp|Up|Down|Left|Right|ScrollLock|CapsLock|NumLock|AppsKey|"

. "PrintScreen|CtrlDown|Pause|Break|Help|Sleep|Browser_Back|Browser_Forward|Browser_Refresh|Browser_Stop|"

. "Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|MButton|RButton|LButton|"

. "Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2"))

Send % backspace

If (WinExist("AHK_class #32768") and hotkey = "RButton")

WinClose, AHK_class #32768

If !IsLabel

Send % keystr

else if IsLabel(keystr)

Gosub, %keystr%

Return

}

Morse(timeout = 400) {

tout := timeout/1000

key := RegExReplace(A_ThisHotKey,"[\*\~\$\#\+\!\^]")

IfInString, key, %A_Space%

StringTrimLeft, key, key,% InStr(key,A_Space,1,0)

If Key in Shift,Win,Ctrl,Alt

key1:="{L" key "}{R" key "}"

Loop {

t := A_TickCount

KeyWait %key%, T%tout%

Pattern .= A_TickCount-t > timeout

If(ErrorLevel)

Return Pattern

If key in Capslock,LButton,RButton,MButton,ScrollLock,CapsLock,NumLock

KeyWait,%key%,T%tout% D

else if Asc(A_ThisHotkey)=36

KeyWait,%key%,T%tout% D

else

Input,pressed,T%tout% L1 V,{%key%}%key1%

If (ErrorLevel="Timeout" or ErrorLevel=1)

Return Pattern

else if (ErrorLevel="Max")

Return

}

}

个人版本

这边只实现了检测连续按下两次,因为不推荐检测3次以上的连发,不自然。 原理是通过判断上次热键和这次热键是否相同,且在规定的时间内. 这边用了ahk内置的三个变量 A_ThisHotkey, 当前触发的热键 A_PriorHotkey, 上次触发点热键 A_TimeSincePriorHotkey , 上次触发按键的时刻到目前的时长,单位是ms

代码

; 函数名是中文,因为我的编辑器支持,如果你的不支持的话,改成自己的就行.

; 我们手指的灵敏度不同,避免一股脑的给所有的连发设置同样的时间参数.

; 平时常用的手指可以设置快点.

不间断触发了两次(tout)

{

return (A_ThisHotkey = A_PriorHotkey) and (A_TimeSincePriorHotkey < tout)

}

优点:

触发受实际手速的影响,无须等待最大时长.

代码通用,在任何的热键中直接调用,用作判断条件即可

不影响单次触发的速度.

使用案例

; 注释

+e:: ; 用e作为注释键,是因为在word等文本编辑器中,我们常用他做居中的快捷键,因此比较熟练.

;而且都有一种代码右移的视觉感.

if 在这个程序中(ahkIDE) ; 这边是我自己判断在ahkIDE的情况下才生效

{

if (不间断触发了两次(300))

{

SendInput, ^q ;取消之前的注释代码

ToolTip,在300毫秒内连续按下了两次. ; 为了演示,这边就不控制这个提示自动隐藏了

SendInput,{End} ; 到代码段的末尾

SendInput,{space} `; ; 添加注释说明的符号

}

else

{

SendInput, ^q ;只按一次的话

}

}

if 在这个程序中(pythonIDE)

{

SendInput,!f

}

return

效果