Тема: AHK: Замена gui контролу "Hotkey"
Смысл в устранении таких недостатков:
- Буквенные клавиши сохраняются по имени, и потом могут не работать при другой языковой раскладке (есть обходной путь, на данный момент эта проблема решена в AutoHotkey).
- Определяются не все клавиши клавиатуры
- Не определяются кнопки мыши и джойстиков
- Не определяется клавиша модификатор "Win"
- Не определяются левые и правые модификаторы
- Не определяются двойные хоткеи вида "RControl & RShift"
Также в предложенном варианте возможно создать группы контролов, между которыми не будет повторов.
При повторе, поле ввода сбрасывается, те в которых назначен аналогичный хоткей, обозначаются миганием.
Можно получать сообщение в своей функции, о том в каких контролах было совпадение, см. спойлер "Обработка повторов в группах".
----------------------------------------------
Тема для обсуждения
----------------------------------------------
Библиотека Hotkey.ahk
(правка: 20:58 26.07.2018)
----------------------------------------------
Описание в комментариях примера:
#NoEnv
#SingleInstance Force
Gui, +AlwaysOnTop
; Сохраняем в переменную путь к ини файлу.
PathIni = %A_ScriptDir%\Hotkey.ini
; Создаём Edit, указываем переменную после "hwnd" для сохранения хэндла контрола.
; После "g" указываем функцию которая будет вызыватся при нажатиях клавиш.
; r1 - чтобы не было переноса строки, это обязательно.
; Hotkey_Read(p1[, p2, p3]) - p1 ключ ини файла, p2 секция, p3 путь к ини файлу.
; Читает из ини файла ранее записанный хоткей, и возвращает в понятном виде для отображения в Edit.
; Также cохраняет значение внутри библиотеки в виде хоткея, для дальнейшего извлечения по ID.
; Если надо отобразить хоткей не из файла, читайте ниже про Hotkey_Set.
Gui, Add, Edit, Center w300 r1 hwndhMyHotkey1 gWriteIni, % Hotkey_Read("MyHotkey1", "Section", PathIni)
Gui, Add, Edit, Center wp y+10 r1 hwndhMyHotkey2 gWriteIni, % Hotkey_Read("MyHotkey2", "Section", PathIni)
; Регистрируем Edit`ы, передавая в массивах:
; Первое - связанное уникальное имя (не может быть числом!), которое понадобится например для записи в ини файл, или чтобы по имени узнать текущее значение хоткея.
; Второе - hwnd контрола.
; Третье - опции (необязательно):
; Указываем строку из возможных букв "KMLRJDSWNG1", в любой последовательности:
; если не указано то - "K".
; K - Определять клавиши клавиатуры
; M - Кнопки мыши без левой и правой.
; L - Левая кнопка мыши, для определения необходимо: зажать правую кнопку - зажать левую кнопку - отпустить правую кнопку.
; R - Правая кнопка мыши, определяется при отпускании кнопки.
; J - Кнопки джойстика, определяются без модификаторов, и без опции W.
; D - Определять модификаторы как левые и правые.
; S - Определять только одну нажатую клавишу, то есть без комбинации с модификаторами. С этим параметром опция "D" не имеет смысла.
; W - Определять как двойной хоткей вида "RControl & RShift". С этим параметром опции "D" и "S" не имеют смысла.
; N - 11 Numpad клавиш зависящих от состояния Numlock сохраняются по виртуальному коду, иначе как скан код,
; желательно её наличие или отсутствие во всех контролах внутри группы, так как при разнных опциях есть конфликт их сравнения при повторах.
; Gn - Указываем "G" и сразу за ней номер группы (целое положительное число больше нуля). В контролах из одной группы не могут быть назначены одинаковые ГК.
; Для страховки, при закрытии и\или показе окна с уже созданными контролами, можно запускать Hotkey_Group("CheckAll") для дополнительной проверки повторов.
; Двойной клик по контролу сбрасывает определённые в нём клавиши.
Hotkey_Register(["MyHotkey1",hMyHotkey1,"KD"], ["MyHotkey2",hMyHotkey2,"KMLRJ"])
; Демонстрационный контрол.
Gui, Add, Edit, Center vText wp
Gui, Show
Return
; Функция указанная при создании контролов, в первом параметре указана переменная для передачи Hwnd контрола.
WriteIni(CtrlHwnd) {
; Делаем переменную с путём к ини файлу, доступной внутри функции.
Global PathIni
; Получаем из Hwnd связанное с ним имя который вызвал функцию.
; Имя используем как одноимённый ключ в ини файле.
Name := Hotkey_ID(CtrlHwnd)
; Получаем из Hwnd текущее значение ввиде строки хоткея.
; Строка пригодна для записи в ини файл, или установки хоткея.
Value := Hotkey_Value(CtrlHwnd)
; Записываем в ини файл.
IniWrite, % Value, % PathIni, Section, % Name
; Просто для демонстрации.
GuiControl, , Text, % Name " = " Value
}
GuiClose() {
ExitApp
}
#NoEnv
#SingleInstance Force
Gui, +AlwaysOnTop
PathIni = %A_ScriptDir%\Hotkey.ini
Gui, Add, Edit, Center w300 r1 hwndhMyHotkey1 gWriteIni, % Hotkey_Read("MyHotkey1", "Section", PathIni)
Gui, Add, Edit, Center wp y+10 r1 hwndhMyHotkey2 gWriteIni, % Hotkey_Read("MyHotkey2", "Section", PathIni)
Hotkey_Register(["MyHotkey1",hMyHotkey1,"KD"], ["MyHotkey2",hMyHotkey2,"KMLRJ"])
Gui, Add, Edit, Center vText wp
Gui, Show
Return
WriteIni(CtrlHwnd) {
Global PathIni
Name := Hotkey_ID(CtrlHwnd)
Value := Hotkey_Value(CtrlHwnd)
IniWrite, % Value, % PathIni, Section, % Name
GuiControl, , Text, % Name " = " Value
}
GuiClose() {
ExitApp
}
Другие возможности:
Если требуется выводить имена символьных клавиш только как при английской раскладке.
Hotkey_Arr("OnlyEngSym", True) ; Запускать перед созданием контролов.
Если нужно изменить текст (по умолчанию - "Нет") который выводится при отсутствии нажатых клавиш, указываем новый текст во втором параметре.
Hotkey_Arr("Empty", Text) ; Запускать перед созданием контролов.
Для удобства можно сохранить путь к ини файлу:
Hotkey_IniPath(Path)
В этом случае при вызове Hotkey_Read или Hotkey_IniWrite можно не указывать третий параметр.
Аналогично можно сохранить секцию ини файла, и также далее её не указывать.
Hotkey_IniSection(Section)
Указав ID, можно записать в ини файл.
ID - это Hwnd или связанное имя. Напомню, что имя не может быть числом.
Hotkey_IniWrite(ID[, Section, FilePath])
Секцию и путь к инифайлу можно не указывать, если вы их сохранили в библиотеке.
Прочитать из ини файла как есть.
Hotkey_IniRead(Name[, Section, FilePath])
Преобразует строку хоткея для использования с командой Send.
Hotkey_HKToSend(p1[, p2, p3])
MsgBox % Hotkey_HKToSend("~*$>^<+vk53") ; {RCtrl Down}{LShift Down}{vk53}{RCtrl Up}{LShift Up}
MsgBox % Hotkey_HKToSend("^vk53") ; {LCtrl Down}{vk53}{LCtrl Up}
MsgBox % Hotkey_HKToSend("vk53") ; {vk53}
MsgBox % Hotkey_HKToSend("Enter") ; {Enter}
MsgBox % Hotkey_HKToSend("sc19 & Enter") ; {sc19 Down}{Enter Down}{sc19 Up}{Enter Up}
Hotkey_HKToSendShort вернёт краткую версию
MsgBox % Hotkey_HKToSendShort("~*$>^<+vk53") ; ^+{vk53}
MsgBox % Hotkey_HKToSendShort("^vk53") ; ^{vk53}
MsgBox % Hotkey_HKToSendShort("vk53") ; {vk53}
MsgBox % Hotkey_HKToSendShort("Enter") ; {Enter}
MsgBox % Hotkey_HKToSendShort("sc19 & Enter") ; {sc19}{Enter}
В этих функциях можно прочитать сразу из ини файла, надо указать в p1 ключ, в p2 секцию, p3 путь к файлу. Если путь был установлен с помощью Hotkey_IniPath, то p3 можно не указывать, но если секция была указана с помощью Hotkey_IniSection, то в р2 всё таки придётся указать имя секции или как вызов Hotkey_IniSection().
Получить из ID текущее значение ввиде строки хоткея.
Hotkey_Value(ID)
Получить Hwnd из имени.
Hotkey_ID(Name)
Или имя из Hwnd.
Hotkey_ID(Hwnd)
Получить из строки хоткея, строку такого же вида, как и при отображении в контроле.
Hotkey_HKToStr(HK)
Если требуется отобразить свои клавиши (например при создании контрола) , укажите связанное имя и строку хоткея (вида: "<^vk57", или "+Space" и.т.п.). Также будет назначено значение связанному имени, чего не произойдёт при использовании Hotkey_HKToStr.
Hotkey_Set(Name, Hotkey)
Если требуется в ходе работы скрипта изменить опции контрола.
Hotkey_ChangeOption(ID, Option)
Удалить контрол и данные о нём. Если указать второй параметр равным нулю, то контрол не будет удалён.
Hotkey_Delete(ID[, Destroy = 1])
0 - отключить все хоткеи библиотеки при скрытии окна, на случай если обнаружен конфликт со своими хоткеями.
1 - включить после отключения при показе окна, при запуске скрипта это не требуется.
Hotkey_InitHotkeys(bool)
----------------------------------------------
Примеры:
После старта надо кликнуть по иконке скрипта в трее, появится окно, вводим хоткеи, закрываем окно, когда окно закрыто, установленными хоткеями должны вызыватся MsgBoxы. Можно снова кликнуть по иконке показав окно, и изменить хоткеи.
#NoEnv
#SingleInstance Force
OnExit("ExitApp")
Menu, Tray, Click, 1
Menu, Tray, NoStandard
Menu, Tray, Add, Hide/Show, GuiClose
Menu, Tray, Add, ExitApp, ExitApp
Menu, Tray, Default, Hide/Show
Hotkey_IniPath(A_ScriptDir "\Hotkey.ini"), Hotkey_IniSection("Hotkeys")
#IF !GuiShow ; по данному алгоритму условие не требуется, я добавил чтобы проще было менять на своё.
#IF
GuiShow := 1
Gui, Add, Edit, Center w300 r1 hwndhMyHotkey1, % Hotkey_Read("MyHotkey1")
Gui, Add, Edit, Center wp y+10 r1 hwndhMyHotkey2, % Hotkey_Read("MyHotkey2")
Gui, Add, Edit, Center wp y+10 r1 hwndhMyHotkey3, % Hotkey_Read("MyHotkey3")
Hotkey_Register(["MyHotkey1",hMyHotkey1,"KMJG1"], ["MyHotkey2",hMyHotkey2,"KMJDG1"], ["MyHotkey3",hMyHotkey3,"KMJWG1"])
GuiClose:
HotkeySet(GuiShow := !GuiShow)
Gui, Show, % GuiShow ? "" : "Hide"
Return
HotkeySet(Reset=0) {
Hotkey, IF, !GuiShow ; по данному алгоритму условие не требуется, я добавил чтобы проще было менять на своё.
Loop, 3
{
IF Reset
Hotkey, % Hotkey_Value("MyHotkey" A_Index), Off, UseErrorLevel
Else IF (1, Hotkey_IniWrite("MyHotkey" A_Index))
Hotkey, % Hotkey_Value("MyHotkey" A_Index), Action%A_Index%, UseErrorLevel On
}
Hotkey, IF
}
ExitApp() {
HotkeySet()
ExitApp
}
Action1:
MsgBox, , , % A_ThisLabel "`n" A_ThisHotkey, 0.6
Return
Action2:
MsgBox, , , % A_ThisLabel "`n" A_ThisHotkey, 0.6
Return
Action3:
MsgBox, , , % A_ThisLabel "`n" A_ThisHotkey, 0.6
Return
#NoEnv
#SingleInstance Force
Hotkey_Arr("Empty", "NO")
Hotkey_Arr("OnlyEngSym", True)
Hotkey_IniPath(A_ScriptDir "\Hotkey.ini")
Hotkey_IniSection("Hotkeys")
Loop 4
GuiCreate(A_Index)
GuiCreate(I) {
Gui, %I%:-DPIScale +AlwaysOnTop +LabelGui
Gui, %I%:Add, Edit, Center xm ym w300 r1 hwndh1 gSave, % Hotkey_Read(I "Hotkey1")
Gui, %I%:Add, Edit, Center wp y+10 r1 hwndh2 gSave, % Hotkey_Read(I "Hotkey2")
Gui, %I%:Add, Edit, Center wp y+10 r1 hwndh3 gSave, % Hotkey_Read(I "Hotkey3")
Hotkey_Register([I "Hotkey1",h1,"KMLRJG1W"], [I "Hotkey2",h2,"KMLRJDG1"], [I "Hotkey3",h3,"KMLRJG1"])
Gui, %I%:Show, % "y100 x" 100+(I-1)*350, Hotkey Gui %I%
}
Save(Hwnd) {
Hotkey_IniWrite(Hwnd)
}
GuiClose() {
ExitApp
}
В ключе GroupEvents указываем имя своей функции
Hotkey_Arr("GroupEvents", "GroupEventsProc")
которая в случае совпадения принимает массив с ключами:
names - массив имён в которых есть совпадения.
this - имя в который хотели ввести хоткей.
value - введённый хоткей.
group - номер группы.
#NoEnv
#SingleInstance Force
Hotkey_Group("CheckAll")
Hotkey_Arr("GroupEvents", "GroupEventsProc")
Hotkey_IniPath(A_ScriptDir "\Hotkey.ini")
Hotkey_IniSection("Hotkeys")
Loop 4
GuiCreate(A_Index)
Return
GroupEventsProc(arr) {
for k, v in arr.names
names .= "`n" v
WinGetPos, x, y, w, h, % "ahk_id" Hotkey_ID(arr.this)
S_CoordModeToolTip := A_CoordModeToolTip
CoordMode, ToolTip, Screen
ToolTip, % "'" Hotkey_HKToStr(arr.value) "' уже назначено в группе номер: " arr.group names, x + w + 2, y + h + 2, 20
CoordMode, ToolTip, %S_CoordModeToolTip%
SetTimer HideTooltip20, -500
Return
HideTooltip20:
ToolTip,,,, 20
Return
}
GuiCreate(I) {
Gui, %I%:-DPIScale +AlwaysOnTop +LabelGui
Gui, %I%:Add, Edit, Center xm ym w300 r1 hwndh1 gSave, % Hotkey_Read(I "Hotkey1")
Gui, %I%:Add, Edit, Center wp y+10 r1 hwndh2 gSave, % Hotkey_Read(I "Hotkey2")
Gui, %I%:Add, Edit, Center wp y+10 r1 hwndh3 gSave, % Hotkey_Read(I "Hotkey3")
Hotkey_Register([I "Hotkey1",h1,"KMLRJG1W"], [I "Hotkey2",h2,"KMLRJDG1"], [I "Hotkey3",h3,"KMLRJG1"])
Gui, %I%:Show, % "y100 x" 100+(I-1)*350, Hotkey Gui %I%
}
Save(Hwnd) {
Hotkey_IniWrite(Hwnd)
}
GuiClose() {
ExitApp
}
Win10x64 AhkSpy, Hotkey, ClockGui