1 (изменено: serzh82saratov, 2017-11-17 03:27:56)

Тема: AHK: Замена gui контролу "Hotkey"

Смысл в устранении таких недостатков:
- Буквенные клавиши сохраняются по имени, и потом могут не работать при другой языковой раскладке (есть обходной путь)
- Определяются не все клавиши клавиатуры
- Не определяются кнопки мыши и джойстиков
- Не определяется клавиша модификатор "Win"
- Не определяются левые и правые модификаторы
- Не определяются двойные хоткеи вида "RControl & RShift"

Также в предложенном варианте возможно создать группы контролов, между которыми не будет повторов.
При повторе, поле ввода сбрасывается, те в которых назначен аналогичный хоткей, обозначаются миганием.
Можно получать сообщение в своей функции, о том в каких контролах было совпадение, см. спойлер "Обработка повторов в группах".



https://ic.pics.livejournal.com/the_bestseller/49384395/14190/14190_900.png

----------------------------------------------

Тема для обсуждения

----------------------------------------------

Библиотека Hotkey.ahk  (правка: 17:46 15.11.2017)

----------------------------------------------

Описание в комментариях примера:


#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.
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 - Кнопки джойстика, определяются только без модификаторов.
; 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}

Можно прочитать сразу из ини файла, надо указать в 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)

Удалить данные о контроле. Если указать второй параметр (например как "1"), то и сам контрол будет удалён.

Hotkey_Delete(ID[, Destroy])

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
}
По вопросам возмездной помощи пишите письма
E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.28.02 (Unicode 32-bit).