본문 바로가기
Back-end/Python

[pynput] 나만의 단축키 만들기

by 허도치 2020. 1. 15.

  요즘 Python으로 웹개발 뿐만 아니라 여러가지 라이브러리들을 학습해보고 있는데, 그 중에서 조금 재밌던 라이브러리를 소개해볼까 한다. 개발을 할 때 마우스를 최대한 적게 사용하려고 한다. 손이 왔다갔다하는게 여간 귀찮고 힘든게 아니다. 그래서 Mac을 사용할 때는 이런저런 단축키들을 등록해놓고 쓸 수 있어서 편했는데, Widnows에서는 별도의 프로그램을 설치해야 하는것 같아서 불편했다.

 

  그래서 Windows 단축키들을 찾아보다가 pynput이라는 라이브러리를 알게되었다. 이 라이브러리는 키보드와 마우스의 입력을 모니터링하고 제어할 수 있는 라이브러리인데, 이를 활용하여 단축키도 만들 수 있다. 자료들을 찾아보면서 정리한 내용을 토대로 나만의 단축키를 만드는 방법을 다루어보도록 하겠다.

 

 

1. pynput란?

   1-1. 키보드와 마우스의 입력을 모니터링하거나 제어할 수 있는 할 수 있는 파이썬 라이브러리.

 

   1-2. 안티 키보드 보안이 실행되는 프로그램을 제외한 거의 모든 프로그램에서 입력되는 것을 모니터링하고 제어할 수 있음.

 

   1-3. 설치

         - pip install pynput

 

 

2. 간단한 사용 예제.

   2-1. 간단한 실행 방법.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pynput.keyboard import Listener, Key
 
def handlePress( key ):
    print'Press: {}'.format( key ) )
 
def handleRelease( key ):
    print'Released: {}'.format( key ) )
        
    # 종료
    if key == Key.esc:
        return False
 
with Listener(on_press=handlePress, on_release=handleRelease) as listener:
    listener.join()
cs

         - 1 ln: keyboard 모듈 Import.

         - 3 ln: 키를 눌렀을 때 발생하는 이벤트를 처리하는 핸들러.

         - 6 ln: 키를 떼었을 때 발생하는 이벤트를 처리하는 핸들러.

         - 10~11 ln: 별도의 종료키가 정의되어 있지않으므로 지정해주어야함.

         - 13~14 ln: on_press, on_release에 핸들러를 매핑하고 이벤트를 감지하기위한 Listener를 실행.

 

   2-2. 실행화면.

         - 어느 화면에서든 키를 입력하면 위와 같이 키가 Press, Release 되는 것을 볼 수 있음.

         - 단, 키가 저장되지않고 한번에 하나의 키만 처리함.

 

 

3. 좀 더 강력한 예제.

   3-1. 동시에 여러키를 처리하는 스크립트.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pynput.keyboard import Listener, Key
 
store = set()
 
def handleKeyPress( key ):
    store.add( key )
 
    print'Press: {}'.format( store ) )
 
def handleKeyRelease( key ):
    print'Released: {}'.format( key ) )
 
    if key in store:
        store.remove( key )
        
    # 종료
    if key == Key.esc:
        return False
 
with Listener(on_press=handleKeyPress, on_release=handleKeyRelease) as listener:
    listener.join()
cs

         - 3 ln: 입력한 키를 중복없이 저장하는 집합 변수.

         - 6 ln: 입력된 키를 저장.

         - 13~14 ln: 키가 Release되면 store에서 삭제.

 

   3-2. 실행결과.

         - 여러 키를 동시에 누르면 위에 같이 저장되고, 키를 떼면 삭제되는 것을 볼 수 있음.

         - 이제 단축키를 만들 준비가 되었음.

 

 

4. 간단한 단축키 만들기 예제.

   4-1. 단축키로 함수 실행하기.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from pynput.keyboard import Listener, Key, KeyCode
 
store = set()
 
HOT_KEYS = {
    'print_hello': set([ Key.alt_l, KeyCode(char='1')] )
}
 
def print_hello():
    print('hello, World!!!')
 
def handleKeyPress( key ):
    store.add( key )
 
    for action, trigger in HOT_KEYS.items():
        CHECK = all([ True if triggerKey in store else False for triggerKey in trigger ])
 
        if CHECK:
            try:
               func = eval( action )
                if callable( func ):
                   func()
            except NameError as err:
                print( err )
 
def handleKeyRelease( key ):
    if key in store:
        store.remove( key )
        
    # 종료
    if key == Key.esc:
        return False
 
with Listener(on_press=handleKeyPress, on_release=handleKeyRelease) as listener:
    listener.join()
cs

         - 1 ln: 입력키를 정의할 수 있는 KeyCode 모듈 추가.

         - 6 ln: Dictionary의 키는 실행할 함수명, 함수를 실행시킬 키의 집합.

           *키보드 왼쪽의 [ ALT ]와 [ 숫자 1 ]을 누르면, print_hello 함수를 실행.

         - 9~10 ln: 단축키로 실행시킬 함수 정의.

         - 15 ln: 정의된 단축키들을 확인.

         - 16 ln: 키의 집합과 저장된 키가 모두 일치하면 True를 반환.

         - 20 ln: eval()은 문자열로 작성된 스크립트를 실행시키는 함수.

           *여기서는 func 변수에 print_hello 함수를 대입.

         - 21~22 ln: func변수가 함수인 경우 실행.

 

   4-2. 실행 결과.

         - 키보드 왼쪽의 [ ALT ]와 [ 숫자 1 ]을 누르면 함수가 실행되면서 'hello, World!!!'가 출력됨.

 

 

5. 심화된 단축키 예제.

   5-1. pypiwin32 라이브러리 설치.

         - 파이썬에서 Windows 시스템을 컨트롤할 수 있는 라이브러리.

         - pip install pypiwin32

 

   5-1. 단축키를 누르면 프로그램을 실행시키는 스크립트.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pynput.keyboard import Listener, Key, KeyCode
import win32api
 
store = set()
 
HOT_KEYS = {
    'print_hello': set([ Key.alt_l, KeyCode(char='1')] )
    , 'open_notepad': set([ Key.alt_l, KeyCode(char='2')] )
}
 
def print_hello():
    print('hello, World!!!')
 
def open_notepad():
    print('open_notepad')
    try:
        win32api.WinExec('notepad.exe')
    except Exception as err:
        print( err )
 
def handleKeyPress( key ):
    store.add( key )
 
    for action, trigger in HOT_KEYS.items():
        CHECK = all([ True if triggerKey in store else False for triggerKey in trigger ])
 
        if CHECK:
            try:
                action = eval( action )
                if callable( action ):
                    action()
            except NameError as err:
                print( err )
 
def handleKeyRelease( key ):
    if key in store:
        store.remove( key )
        
    # 종료
    if key == Key.esc:
        return False
 
    
with Listener(on_press=handleKeyPress, on_release=handleKeyRelease) as listener:
    listener.join()
cs

         - 2 ln: win32api Import

         - 8 ln: 키보드 왼쪽의 [ ALT ]와 [ 숫자 2 ]을 누르면, open_notepad함수를 실행.

         - 17 ln: 함수가 실행되면 메모장을 실행.

 

   5-2. 실행결과.

             - 설정한 단축키에 따라서 지정한 이벤트가 발생하는 것을 확인할 수 있음.

             - win32api를 사용하면 화면 스크린샷이나 특정 픽셀의 색상을 가져오는 등 다양한 처리가 가능.

 

 

6. 마치며.

   - 현재 만든 예제는 키를 계속 누르고 있으면 계속 실행되는 단점이 있다. 이 부분은 숙제(?)로 남겨둘테니 각자 처리해보길 바란다. 예를들어, 실행상태를 저장하는 변수를 두고 키가 계속 눌려진 상태라면 함수가 실행되지 않도록 처리하면된다. 또는, 키입력이 완료된 후에 이벤트가 처리되도록 하면 된다.

   - pynput과 win32api를 나만의 단축키들을 만들어 나가면 재밌을것 같다.

 

 

 

 

*** 키입력이 완료된 후 이벤트가 실행되도록 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from pynput.keyboard import Listener, Key, KeyCode
import win32api
 
store = set()
 
HOT_KEYS = {
    'print_hello': set([ Key.alt_l, KeyCode(char='1')] )
    , 'open_notepad': set([ Key.alt_l, KeyCode(char='2')] )
}
 
def print_hello():
    print('hello, World!!!')
 
def open_notepad():
    print('open_notepad')
    try:
        win32api.WinExec('notepad.exe')
    except Exception as err:
        print( err )
 
def handleKeyPress( key, **kwargs ):
    store.add( key )
 
def handleKeyRelease( key ):
    for action, trigger in HOT_KEYS.items():
        CHECK = all([ True if triggerKey in store else False for triggerKey in trigger ])
 
        if CHECK:
            try:
                action = eval( action )
                if callable( action ):
                    action()
            except NameError as err:
                print( err )
                
    # 종료
    if key == Key.esc:
        return False
    elif key in store:
        store.remove( key )
    
with Listener(on_press=handleKeyPress, on_release=handleKeyRelease) as listener:
    listener.join()
 
cs

댓글