使用 AutoHotKey(或其他工具)创建自定义键盘布局
Create custom keyboard layout with AutoHotKey (or something else)
我想创建一个自定义键盘布局,主要用于键入 unicode 数学符号。我需要的符号集非常多,我想出的方案涉及多种布局和特殊组合。
我键入 `(反引号)一次,但我得到了一个特殊字符,我们将用 * 表示它。通过键入额外的键,我可以获得与特定主题相关的特定键盘布局。 (我想用一个特殊符号替换反勾,这样我就记得它是某种控制代码。通过输入两次,我得到一个正常的反勾)
下面是一些示例映射:
*s -> Set theory layout:
[ -> ∈ (element of)
o -> ∅ (empty set)
*r -> General math:
s -> ∫ (integral sign)
S -> ∬ (double integral sign)
*e -> Misc operators:
8 -> ∗ (convolution asterisk)
* -> ⋆ (star operator)
*g -> Greek alphabet
输入一个字符后,例如=
,我可以输入一些其他的特殊组合来修饰那个字符。例如:
*x -> Negates the previous character:
= -> ≠ (unequal)
≡ -> ≢ (negation of three line equality)
*w -> Expands the previous character:
∫ -> ∭ (triple integral)
∬ -> ⨌ (quad integral)
映射是助记的。我可以想象将所有我想要的符号塞进一个布局中,但它无法使用,所以我想尝试坚持这个方案或类似的方案。
键盘是针对Windows环境的,但我自己写键盘DLL是没有问题的。我调查了一下,它太复杂了。
现在,我正在寻找 AHK 的解决方案。可以在 AHK(或类似的东西)中完成吗?如果是这样,你能给我一些例子让我开始吗?
我也想知道是否有其他方法可以做到这一点
我知道 Microsoft Keyboard Layout Creator,过去也使用过它,但它的功能还不够强大。我也知道 Tavultesoft 的 Keyman,我知道它确实可以做我想做的事,但它贵得离谱,所以不是一个选择。
你所需要的,AutoHotkey 绝对可以做到。
棘手的部分是:您需要分配热键和 Hotstrings dynamically. The former is possible with the Hotkey-command,后者尚未实现,而且可能永远不会实现。
您可以将它们写入新的 .ahk 文件并单独执行该脚本,或者使用 polyethene's really awesome dynamical regEx hotstring creator. autohotkey.net's repository has been down for years, but I found the script here。
要获得以下示例脚本 运行,您需要将该脚本下载到 Hotstrings.ahk
并将其放在主 .ahk 文件所在的同一目录中。
但是,我无法将布局更改器热键分配给
`
,所以我设置为ä
。
我知道这个网站不是代码提供者网络,但我也对这个问题很感兴趣。
#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
#include Hotstrings.ahk
lastKey := ""
; KEYS OF THE NORMAL LAYOUT WHICH SHOULD BE NEGATABLE:
hotstrings("=", "_equals") ;
; CHOOSE LAYOUT OR NEGATE/EXPAND LAST CHARACTER:
:*?:ä::
; backSpacePressed: ; wtf is this? sorry just saw this now. Does not belong here, does not belong anywhere
sendRaw *
tooltip,
(
n normal layout
s set theory
r general math
x negate previous
w expand previous
{esc} cancel
), %A_CaretX%, %A_CaretY%
input, layout, L1, {Escape}, s,r,e,g,x,w
send {Backspace} ; remove the *
if layout in n,s,r,e,g,x,w
{
tooltip, %layout%, %A_CaretX%, %A_CaretY%
; RESET
if layout = n
{
reset_all_hotstrings()
; KEYS OF THE NORMAL LAYOUT WHICH SHOULD BE NEGATABLE:
hotstrings("=", "_equals")
}
; NEW LAYOUT
else if layout = s
{
reset_all_hotstrings()
; SET THEORY SHORTCUTS
hotstrings("o", "_emptySet")
hotstrings("\[", "_elementOf")
}
else if layout = r
{
reset_all_hotstrings()
; MATH SHORTCUTS
hotstrings("s", "_integral")
hotstrings("S", "_doubleIntegral")
hotstrings("=", "_identical")
}
; and so on
; ...
; EDIT PREVIOUS CHARACTER
else if layout = x
{
send {backSpace}
if lastKey = identical
sendUnicodeChar(0x2262)
else if lastKey = equals
sendUnicodeChar(0x2260)
}
; EXPAND PREVIOUS CHARACTER
else if layout = w
{
send {backSpace}
if lastKey = integral
sendUnicodeChar(0x222D)
else if lastKey = doubleIntegral
sendUnicodeChar(0x2A0C)
}
}
else
{
tooltip, cancelled, %A_CaretX%, %A_CaretY%
}
sleep, 500
tooltip
return
reset_all_hotstrings() {
hotstrings("=")
hotstrings("\[")
hotstrings("o")
hotstrings("s")
hotstrings("S")
; and so on
}
; NORMAL LAYOUT SHORTCUTS:
_equals:
sendUnicodeChar(0x003D)
lastKey = equals
return
; SPECIAL LAYOUT SHORTCUTS:
_emptySet:
;sendUnicodeChar(0x00D8)
altNumpad(0216)
; to find out numpad combination or unicode: press WIN+R, type in "charmap"
; or for unicode only, go to http://www.fileformat.info/info/unicode/category/index.htm
; (sendUnicodChar() needs 0x before the unicode string)
;altNumpad(0248)
;send Ø
;send ø
; choose whatever works best for you
lastKey = emptySet
return
_elementOf:
sendUnicodeChar(0x2208)
lastKey = elementOf
return
_integral:
sendUnicodeChar(0x222B)
lastKey = integral
return
_identical:
sendUnicodeChar(0x2261)
lastKey = identical
return
_doubleIntegral:
sendUnicodeChar(0x222C)
lastKey = doubleIntegral
return
; -------------------------------------------
altNumpad(numbers) {
stringSplit, n, numbers
setkeydelay, 100
send {alt down}
loop, %n0%
{
t := n%a_index%
send {numpad%t%}
}
send {alt up}
}
SendUnicodeChar(charCode)
{
VarSetCapacity(ki, 28 * 2, 0)
EncodeInteger(&ki + 0, 1)
EncodeInteger(&ki + 6, charCode)
EncodeInteger(&ki + 8, 4)
EncodeInteger(&ki +28, 1)
EncodeInteger(&ki +34, charCode)
EncodeInteger(&ki +36, 4|2)
DllCall("SendInput", "UInt", 2, "UInt", &ki, "Int", 28)
}
EncodeInteger(ref, val)
{
DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", 4, "Uint", val)
}
^e::reload
显然还有很大的改进空间
我想创建一个自定义键盘布局,主要用于键入 unicode 数学符号。我需要的符号集非常多,我想出的方案涉及多种布局和特殊组合。
我键入 `(反引号)一次,但我得到了一个特殊字符,我们将用 * 表示它。通过键入额外的键,我可以获得与特定主题相关的特定键盘布局。 (我想用一个特殊符号替换反勾,这样我就记得它是某种控制代码。通过输入两次,我得到一个正常的反勾)
下面是一些示例映射:
*s -> Set theory layout:
[ -> ∈ (element of)
o -> ∅ (empty set)
*r -> General math:
s -> ∫ (integral sign)
S -> ∬ (double integral sign)
*e -> Misc operators:
8 -> ∗ (convolution asterisk)
* -> ⋆ (star operator)
*g -> Greek alphabet
输入一个字符后,例如=
,我可以输入一些其他的特殊组合来修饰那个字符。例如:
*x -> Negates the previous character:
= -> ≠ (unequal)
≡ -> ≢ (negation of three line equality)
*w -> Expands the previous character:
∫ -> ∭ (triple integral)
∬ -> ⨌ (quad integral)
映射是助记的。我可以想象将所有我想要的符号塞进一个布局中,但它无法使用,所以我想尝试坚持这个方案或类似的方案。
键盘是针对Windows环境的,但我自己写键盘DLL是没有问题的。我调查了一下,它太复杂了。
现在,我正在寻找 AHK 的解决方案。可以在 AHK(或类似的东西)中完成吗?如果是这样,你能给我一些例子让我开始吗?
我也想知道是否有其他方法可以做到这一点
我知道 Microsoft Keyboard Layout Creator,过去也使用过它,但它的功能还不够强大。我也知道 Tavultesoft 的 Keyman,我知道它确实可以做我想做的事,但它贵得离谱,所以不是一个选择。
你所需要的,AutoHotkey 绝对可以做到。
棘手的部分是:您需要分配热键和 Hotstrings dynamically. The former is possible with the Hotkey-command,后者尚未实现,而且可能永远不会实现。
您可以将它们写入新的 .ahk 文件并单独执行该脚本,或者使用 polyethene's really awesome dynamical regEx hotstring creator. autohotkey.net's repository has been down for years, but I found the script here。
要获得以下示例脚本 运行,您需要将该脚本下载到 Hotstrings.ahk
并将其放在主 .ahk 文件所在的同一目录中。
但是,我无法将布局更改器热键分配给
`
,所以我设置为ä
。
我知道这个网站不是代码提供者网络,但我也对这个问题很感兴趣。
#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
#include Hotstrings.ahk
lastKey := ""
; KEYS OF THE NORMAL LAYOUT WHICH SHOULD BE NEGATABLE:
hotstrings("=", "_equals") ;
; CHOOSE LAYOUT OR NEGATE/EXPAND LAST CHARACTER:
:*?:ä::
; backSpacePressed: ; wtf is this? sorry just saw this now. Does not belong here, does not belong anywhere
sendRaw *
tooltip,
(
n normal layout
s set theory
r general math
x negate previous
w expand previous
{esc} cancel
), %A_CaretX%, %A_CaretY%
input, layout, L1, {Escape}, s,r,e,g,x,w
send {Backspace} ; remove the *
if layout in n,s,r,e,g,x,w
{
tooltip, %layout%, %A_CaretX%, %A_CaretY%
; RESET
if layout = n
{
reset_all_hotstrings()
; KEYS OF THE NORMAL LAYOUT WHICH SHOULD BE NEGATABLE:
hotstrings("=", "_equals")
}
; NEW LAYOUT
else if layout = s
{
reset_all_hotstrings()
; SET THEORY SHORTCUTS
hotstrings("o", "_emptySet")
hotstrings("\[", "_elementOf")
}
else if layout = r
{
reset_all_hotstrings()
; MATH SHORTCUTS
hotstrings("s", "_integral")
hotstrings("S", "_doubleIntegral")
hotstrings("=", "_identical")
}
; and so on
; ...
; EDIT PREVIOUS CHARACTER
else if layout = x
{
send {backSpace}
if lastKey = identical
sendUnicodeChar(0x2262)
else if lastKey = equals
sendUnicodeChar(0x2260)
}
; EXPAND PREVIOUS CHARACTER
else if layout = w
{
send {backSpace}
if lastKey = integral
sendUnicodeChar(0x222D)
else if lastKey = doubleIntegral
sendUnicodeChar(0x2A0C)
}
}
else
{
tooltip, cancelled, %A_CaretX%, %A_CaretY%
}
sleep, 500
tooltip
return
reset_all_hotstrings() {
hotstrings("=")
hotstrings("\[")
hotstrings("o")
hotstrings("s")
hotstrings("S")
; and so on
}
; NORMAL LAYOUT SHORTCUTS:
_equals:
sendUnicodeChar(0x003D)
lastKey = equals
return
; SPECIAL LAYOUT SHORTCUTS:
_emptySet:
;sendUnicodeChar(0x00D8)
altNumpad(0216)
; to find out numpad combination or unicode: press WIN+R, type in "charmap"
; or for unicode only, go to http://www.fileformat.info/info/unicode/category/index.htm
; (sendUnicodChar() needs 0x before the unicode string)
;altNumpad(0248)
;send Ø
;send ø
; choose whatever works best for you
lastKey = emptySet
return
_elementOf:
sendUnicodeChar(0x2208)
lastKey = elementOf
return
_integral:
sendUnicodeChar(0x222B)
lastKey = integral
return
_identical:
sendUnicodeChar(0x2261)
lastKey = identical
return
_doubleIntegral:
sendUnicodeChar(0x222C)
lastKey = doubleIntegral
return
; -------------------------------------------
altNumpad(numbers) {
stringSplit, n, numbers
setkeydelay, 100
send {alt down}
loop, %n0%
{
t := n%a_index%
send {numpad%t%}
}
send {alt up}
}
SendUnicodeChar(charCode)
{
VarSetCapacity(ki, 28 * 2, 0)
EncodeInteger(&ki + 0, 1)
EncodeInteger(&ki + 6, charCode)
EncodeInteger(&ki + 8, 4)
EncodeInteger(&ki +28, 1)
EncodeInteger(&ki +34, charCode)
EncodeInteger(&ki +36, 4|2)
DllCall("SendInput", "UInt", 2, "UInt", &ki, "Int", 28)
}
EncodeInteger(ref, val)
{
DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", 4, "Uint", val)
}
^e::reload
显然还有很大的改进空间