Python/Python ETC

Python pynput : keyboard (python으로 키보드 컨트롤하기, Python으로 keyboard control)

CosmosProject 2024. 11. 5. 20:17
728x90
반응형

 

 

 

pynput library로 keyboard를 컨트롤 하는 방법을 알아봅시다.

(mac환경에서 진행됩니다.)

 

 

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# press and release space
keyboard.press(Key.space)  # press space
keyboard.release(Key.space)  # release space

 

press, release method를 이용해 keyboard의 key를 눌렀다 뗐다를 할 수 있습니다.

press는 어떠한 key를 누르라는 의미이며

release는 어떠한 key를 누른 상태에서 떼라는 의미입니다.

 

 

 

Key를 for loop를 이용해 출력해보면 어떠한 key들이 Key에 담겨있는지 볼 수 있습니다.

from pynput.keyboard import Key, Controller

keyboard = Controller()

for i in Key:
    print(i)



-- Result
Key.alt
Key.alt_r
Key.backspace
Key.caps_lock
Key.cmd
Key.cmd_r
Key.ctrl
Key.ctrl_r
Key.delete
Key.down
Key.end
Key.enter
Key.esc
Key.f1
Key.f2
Key.f3
Key.f4
Key.f5
Key.f6
Key.f7
Key.f8
Key.f9
Key.f10
Key.f11
Key.f12
Key.f13
Key.f14
Key.f15
Key.f16
Key.f17
Key.f18
Key.f19
Key.f20
Key.home
Key.left
Key.page_down
Key.page_up
Key.right
Key.shift
Key.shift_r
Key.space
Key.tab
Key.up
Key.media_play_pause
Key.media_volume_mute
Key.media_volume_down
Key.media_volume_up
Key.media_previous
Key.media_next

 

보면 위 Key에는 특수한 key들만 들어있습니다.

space, tab, f1~f12, enter, 방향키(Key.up, Key.down, Key.left, Key.right)들이 있죠.

그러면 글자는 어떻게 입력할까요?

 

 

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# type a lower case a.
# this will work even if no key on the physical keyboard is labelled 'A'
keyboard.press('a')
keyboard.release('a')

 

press, release method에 원하는 문자를 입력하면 됩니다.

키보드에 a라는 글자가 없어도 작동합니다.

 

 

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# type upper case A
keyboard.press('A')
keyboard.release('A')

 

대문자를 입력하고 싶으면 대문자 A를 parameter로 전달하면 됩니다.

 

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# type upper case A
with keyboard.pressed(Key.shift):
    keyboard.press('a')
    keyboard.release('a')

 

다른 방법으로는 with과 pressed method를 이용하여 shift를 누른 채로 소문자 a를 전달합니다.

pressed method는 특정 키를 계속 누르고 있으라는 것을 의미합니다.

 

 

 

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

keyboard.press('ab')



-- Result
raise ValueError(key)
ValueError: ab

 

다만 press, release method는 하나의 key만을 parameter로 받을 수 있습니다.

위처럼 ab같이 다양한 글자에 해당하는 key를 전달하면 ValueError가 발생합니다.

왜냐면 press는 어떤 key를 누르라는건데 ab라는 key는 존재하지 않기 때문이죠.

 

만약 여러 개의 문자를 입력하고 싶으면 아래처럼 하면 됩니다.

 

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# Type 'Hello World' using the shortcut type method
keyboard.type('Hello World')

 

type method를 이용하면 주어진 텍스트를 전부 입력합니다.

 

그런데 간혹 위 코드를 실행하면 Hello World가 제대로 입력되지 않는 경우가 있습니다.

 

type method가 Hello World라는 글자를 다 입력하기도 전에 프로그램이 종료되기 때문으로 추정되는데

이럴 땐 아래처럼time.sleep()을 이용해서 type method가 실행된 후 프로그램이 일정 시간 동안 종료되지 않게 하면 잘 입력되는 것을 볼 수 있습니다.

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# Type 'Hello World' using the shortcut type method
keyboard.type('Hello World')

import time
time.sleep(1)

 

 

 

 

 

pynput keyboard에는 listener라는 기능이 있습니다.

어떤 key가 입력되었는지를 감지해서 그게 무슨 key인지 return해주는, key의 입력을 듣는(listen) 기능이라고 이해하면 됩니다.

 

from pynput import keyboard

def on_press(key):
    try:
        print('alphanumeric key {} pressed'.format(key.char))
    except AttributeError:
        print('special key {} pressed'.format(key))

def on_release(key):
    print('{} released'.format(key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False

# Collect events until released
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

 

 

위 코드를 실행하고 key를 입력하면 콘솔창에 key가 입력될 때 마다 입력된 key가 무엇인지 출력될 것입니다.

 

https://pynput.readthedocs.io/en/latest/keyboard.html

공식 문서를 보면 이를 다음과 같이 설명하고 있습니다.

 

A keyboard listener is a threading.Thread, and all callbacks will be invoked from the thread.

Call pynput.keyboard.Listener.stop from anywhere, raise StopException or return False from a callback to stop the listener.

The key parameter passed to callbacks is a pynput.keyboard.Key, for special keys, a pynput.keyboard.KeyCode for normal alphanumeric keys, or just None for unknown keys.

 

위 코드에서 on_press, on_release 함수의 parameter로서 전달되는 key는 pynput.keyboard.Key 객체이거나 특정 key에 대해선 pynput.keyboard.KeyCode 객체입니다.

만약 그 외에 인식할 수 없는 key가 입력된다면 None이 됩니다.

 

Listener를 정지하고 싶으면 StopException을 일으키거나 False를 return하면 됩니다.

그러면 Listener는 멈출것입니다.

 

def on_release(key):
    print('{} released'.format(key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False

 

그래서 on_release 함수를 보면

입력된 Key가 esc인 경우 False를 return하는데

이는 esc가 입력된 경우 Listener를 종료시키기 위함입니다.

 

 

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

 

그게 가능한 이유는 위와 같습니다.

Listener 객체는 2개의 parameter를 받습니다. on_press, on_release이죠.

여기서는 2개의 parameter를 어떠한 값이 아닌 함수로 전달했습니다.

 

Listener는 일단 실행되면 아래와 같은 과정을 거칩니다.

Step 1 -> key의 입력을 대기

 

Step 2 -> key가 눌리면(press) on_press parameter에서 정의된 함수로 해당 key의 정보를 전달하여 on_press 함수 실행.

Step 3 -> on_press 함수로부터 return된 값을 받아 체크 (on_press 함수에서 return된 값이 없으면 그냥 다시 대기상태로 돌아오고, False가 return되거나 StopException이 발생하면 Listener는 종료됨.)

 

Step 4 -> key가 떼어지면(release) on_release parameter에서 정의된 함수로 해당 key의 정보를 전달하여 on_release 함수 실행.

Step 5 -> on_release 함수로부터 return된 값을 받아 체크 (on_release 함수에서 return된 값이 없으면 그냥 다시 대기상태로 돌아오고, False가 return되거나 StopException이 발생하면 Listener는 종료됨.)

 

 

 

 

 

 

from pynput import keyboard

def on_press(key):
    try:
        print('alphanumeric key {} pressed'.format(key.char))
    except AttributeError:
        print('special key {} pressed'.format(key))

def on_release(key):
    print('{} released'.format(key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False

# ...or, in a non-blocking fashion:
listener = keyboard.Listener(on_press=on_press, on_release=on_release)
listener.start()

 

Listener는 위처럶 non blocking 방식으로 실행할 수도 있습니다.

다만 공식 설명을 보면 아래와 같습니다.

 

When using the non-blocking version above, the current thread will continue executing. This might be necessary when integrating with other GUI frameworks that incorporate a main-loop, but when run from a script, this will cause the program to terminate immediately.

 

non blocking version은 다른 GUI framework와 상호작용하며 사용하는 용도이며

그냥 Python script로서 실행하게되면 key입력 대기 시간 없이 바로 종료될 것입니다.

 

 

 

 

 

추가로 이를 이용하여 한/영 입력을 바꿀 수도 있습니다.

 

from pynput.keyboard import Key, Controller

keyboard = Controller()

# convert keyboard language
keyboard.press(Key.ctrl_l)
keyboard.press(Key.space)
keyboard.release(Key.ctrl_l)
keyboard.release(Key.space)

 

Mac 기준으로 한/영 변환은 left control 버튼과 space를 동시에 눌렀다 떼는 것이므로

코드를 위처럼 구성해주면 한/영 전환이 되는 것을 볼 수 있습니다.

 

 

 

 

캡쳐도 할 수 있습니다.

 

import time
from pynput.keyboard import Key, Controller

keyboard = Controller()

keyboard.press(Key.cmd)
keyboard.press(Key.shift_l)
keyboard.press('5')
keyboard.release(Key.cmd)
keyboard.release(Key.shift_l)
keyboard.release('5')

time.sleep(0.3)

keyboard.press(Key.enter)
keyboard.release(Key.enter)

 

Mac 기준 캡쳐는 Left Command + Left Shift + 5입니다.

그래서 이 키를 누르면 캡쳐모드에 진입하게 되고 0.3초정도 후에 enter를 누르면 캡쳐가 됩니다.

 

중간에 time.sleep(0.3)을 넣은 이유는

Key입력이 너무 빠르면 캡쳐모드에 진입하기도 전에 enter를 눌러서 제대로 캡쳐가 안될 수 있기 때문입니다.

 

 

 

 

FYI

https://pynput.readthedocs.io/en/latest/keyboard.html

 

 

 

 

 

728x90
반응형