개요
최근에 pyautogui를 통해서 자동으로 클릭하는 매크로 프로그램을 개발중이었는데 생각해보니 마우스가 파이썬으로 계속 제어되고 있는데 어떻게 중단하지? 라는 생각이 들었습니다.
처음에는 그냥 sys의 stdin같은걸 사용해서 제어하려 했지만 몇번 해본결과 안된다는걸 확인하였고 다른 창에서도 제어할 수 있도록하는 키보드 후킹을 사용하여야 했습니다.
결론적으로는 keyboard 라이브러리를 사용하여 백쓰레드에서 계속 후킹해주는 방법으로 간단하게 해결을 했습니다.
keyboard Library
https://github.com/boppreh/keyboard
라이브러리의 github 주소인데 간단한 예제들과 api 설명들이 잘 나와있습니다.
- keyboard.KEY_DOWN
- keyboard.KEY_UP
- keyboard.KeyboardEvent
- keyboard.all_modifiers
- keyboard.sided_modifiers
- keyboard.version
- keyboard.is_modifier
- keyboard.key_to_scan_codes
- keyboard.parse_hotkey
- keyboard.send (aliases: press_and_release)
- keyboard.press
- keyboard.release
- keyboard.is_pressed
- keyboard.call_later
- keyboard.hook
- keyboard.on_press
- keyboard.on_release
- keyboard.hook_key
- keyboard.on_press_key
- keyboard.on_release_key
- keyboard.unhook (aliases: unblock_key, unhook_key, unremap_key)
- keyboard.unhook_all
- keyboard.block_key
- keyboard.remap_key
- keyboard.parse_hotkey_combinations
- keyboard.add_hotkey (aliases: register_hotkey)
- keyboard.remove_hotkey (aliases: clear_hotkey, unregister_hotkey, unremap_hotkey)
- keyboard.unhook_all_hotkeys (aliases: clear_all_hotkeys, remove_all_hotkeys, unregister_all_hotkeys)
- keyboard.remap_hotkey
- keyboard.stash_state
- keyboard.restore_state
- keyboard.restore_modifiers
- keyboard.write
- keyboard.wait
- keyboard.get_hotkey_name
- keyboard.read_event
- keyboard.read_key
- keyboard.read_hotkey
- keyboard.get_typed_strings
- keyboard.start_recording
- keyboard.stop_recording
- keyboard.record
- keyboard.play (aliases: replay)
- keyboard.add_word_listener (aliases: register_word_listener)
- keyboard.remove_word_listener (aliases: remove_abbreviation)
- keyboard.add_abbreviation (aliases: register_abbreviation)
- keyboard.normalize_name
이 중에서 필요한 메서드는 시작/끝에 후킹 등록한걸 해제하기위한 keyboard.unhook_all(),
등록한 hotkey가 실행될 경우 함수를 실행해주는 keyboard.add_hotkey(),
hotkey를 읽어 반환해주는 keyboard.read_hotkey() 입니다.
Code
import keyboard
import threading
class Hook(threading.Thread):
def __init__(self):
super(Hook, self).__init__() # parent class __init__ 실행
self.daemon = True # 데몬쓰레드로 설정
self.event = False # f4가 눌리면 event 발생
keyboard.unhook_all() # 후킹 초기화
keyboard.add_hotkey('f4', print, args=['\nf4 was pressed']) # f4가 눌리면 print 실행
def run(self): # run method override
print('Hooking Started')
while True:
key = keyboard.read_hotkey(suppress=False) # hotkey를 계속 읽음
if key == 'f4': # f4 받은 경우
self.event = True # event 클래스 변수를 True로 설정
break # 반복문 탈출
코드는 매우 간단합니다. Hooking을 백쓰레드에서 돌리기 위해 threading.Thread를 상속받아줍니다.
__init__에서는 데몬쓰레드 설정, 이벤트 변수 설정, 후킹 초기화, hotkey 후킹 추가를 해주게 됩니다.
run에서는 후킹을 시작하고, hotkey를 계속 읽어서 'f4'일 경우 self.event를 True로 변경하고 반복문을 탈출하게 됩니다.
keyboard.read_hotkey의 경우 suppress=False라고 되어있는데 이는 f4를 누르고 f3을 누르게되면
f4+f3 이런식으로 출력이 됩니다. 따라서 연속해서 입력하는걸 막아주는 역할입니다.
import pyautogui
from time import sleep
def track_pos():
h = Hook() # 훅 쓰레드 생성
h.start() # 쓰레드 실행
print('size:', pyautogui.size())
while True:
if h.event == True: # h.event가 True이면(f4 입력받은경우) 종료
break
position = pyautogui.position()
print(f'\r{position.x:4}, {position.y:4}', end='')
sleep(0.05)
h.join()
keyboard.unhook_all() # 후킹해제
track_pos()
실제 적용한 간단한 테스트 코드입니다. 먼저 후킹쓰레드를 생성해 실행시켜주고 while문 안에서는 현재의 마우스 위치를 계속 표시해주게 됩니다. 그러다가 f4를 입력받아 h.event가 True로 바뀌게 되면 반복문을 종료하고 모든 후킹을 해제시킵니다.
결론
간단하게 키보드 후킹 쓰레드를 구현했고 다른 프로그램등에도 키보드를 통한 프로그램 제어에 사용할 수 있을거라고 생각됩니다.
'파이썬' 카테고리의 다른 글
파이썬 순열, 조합 스택으로 구현하기 (1) | 2022.03.29 |
---|---|
백준 1034 번 : 램프 - 파이썬 (1) | 2021.03.18 |
파이썬 pyautogui와 키보드 후킹을 통한 자동 클릭 매크로 (0) | 2021.02.24 |