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

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

基本环境

为什么要做这个事情

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

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

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

代码

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

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

$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,而触发功能。
  1. 他人版本
    这个版本的功能比较强,他提供的 "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
   }
}
  1. 个人版本

这边只实现了检测连续按下两次,因为不推荐检测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       
  • 效果
    请添加图片描述
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值