프로그래밍 관련/PyQt

PyQt() Keyboard 이벤트, Keyboard인터럽트 pynput 라이브러리

존버매니아.임베디드 개발자 2021. 9. 16. 10:41
반응형

키보드로부터 입력을 받아서 인터럽트를 발생시키고 싶었다.

키보드 인풋을 받아오는 방법은 검색을 해보니 다양한게 나왔는데

일단 PyQt에서 자체적으로 지원하는 keyboad event 핸들러를 사용하는 방법이 있었다.

아래는 PyQt 가 제공하는 키보드 이벤트 핸들러 사용한 예시케이스이다.


1.PyQt가 제공하는 키보드 이벤트 핸들러 사용

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt

class MyWindow(QMainWindow):
         
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Test")
        self.setGeometry(1000, 200, 300, 300)

    def keyPressEvent(self, e): #키가 눌러졌을 때 실행됨
        if e.key() == Qt.Key_Escape:
            print("esc pressed")
        elif e.key() == Qt.Key_A:
            print("A is pressed)")
        else:
            print((e.key()))

    def keyReleaseEvent(self,e): #키를 누른상태에서 뗏을 때 실행됨
        print("kye is pressed:")
        print(e.key())
    


if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()

    app.exec_()

코드 구현방법은 간단하다.

이름에서 유추가 가능할 텐데

키보드에 아무거나 키를 누르면 KeyPressEvent 메소드가 실행된다.

그리고 누르고 있던 키를 떼면 KeyReleaseEvent 메소드가 실행된다.

이 때 실행되는 메소드에 argument로 해당 키의 정보가 전달되는 형태이다.

 

한가지 유의할 점은 위 이벤트핸들러에서 받아오는 e 값은 아스키코드 값을 받아오는데

특수키인 shift, backspace, space, enter 키등등은 아스키코드 범위를 벗어나는 매우 큰 숫자가 나오더라.

특수키에 대한 처리 어떻게 해줄지 정의 필요 할 듯.(그 방법은 일단 나중에 알아보고. 우선 여기서는 패스)


2.pynput 라이브러리 사용

근데 여기서 문제가 있었다.

 

내가 하고싶은게 뭐였냐면 

 

PyQt로 만들어진 SW를 실행시킨 후, 이 SW를 활성화시키지 않은 상태에서 키보드 인풋을 받고 싶었다.

(예를 들어서 PyQt GUI SW를 실행시킨 상태에서, 이 sw는 작업표시줄에 최소화 시켜놓고 다른 프로그램을 활성화시킨 상태)

 

이 상태에서는 위의 이벤트 핸들러가 동작을 하지 않았다.

앞에서 살펴봤던 키보드 핸들러는 PyQt SW가 컴퓨터상에서 활성화 돼있을 때만 동작하고 있었다.

 

그래서 PyQt GUI SW가 비활성화 상태에서도 키보드 이벤트를 받을 수 있는 방법을 찾게되었고..

pynput 이라는 라이브러리를 알게되었다.

기본 라이브러리가 아니므로 pip install pynput 으로 설치를 해서 사용해야한다.

 

일단 기본 사용 형태를 보자.

 

from pynput import keyboard
    
def on_press(key):
        try:
            print(f'알파벳 \'{key.char}\' 눌림')
        except AttributeError:
            print(f'특수키 {key} 눌림')

def on_release(key):
    print(f'키보드 {key} 풀림')
    if key == keyboard.Key.esc:
        # esc 키에서 풀림
        return False

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

 

 

 

위 코드에서 with keyboard.Listner 라는 키워드가 정확히 뭐하는 키워드인지는 공부가 더 필요하다.

위의 코드만 놓고보면 while 루프 같은게 없으니까 SW 실행하면 그냥 꺼져야될 거 같은데

정확히 어떻게 돌아가고 있는진 모르겠는데 위 코드를 실행시키면 SW가 게속 돌면서

키보드 누르면 등록된 on_press 함수가 실행되고 ,키보드 눌렀다 떼면 on_release 함수가 실행된다.

 

이게 기초적인 pynput 라이브러리를 사용하는 예시이다.

이 SW의 경우 PyQt의 키보드이벤트리스너와 달리 파이썬 SW가 비활성화되어있어도 동작을 하더라.

 

다음으로, 이 pynput 라이브러리랑 PyQt를 같이 쓰는 예시를 알아보자.


3.pynput 라이브러리를 PyQt랑 같이 사용하기

2번에서 살펴본 것 처럼 코드를 입력하면..

listener.join() 을 호출하고나면 어딘가 루프에 빠지는거 같다.

그래서 저 뒤에 있는 코드 실행이 안된다.

 

그래서 PyQt랑 같이 쓰려고하는데 쓸 수가 없다.

 

내가 찾은 방법이 과연 최선의 방법인지 의문인데 내가 찾은 방법은..

 

PyQt 안에서 쓰레드를 하나 추가로 만들고

이 쓰레드 안에서 pynput listner를 등록해서 사용하는 방식이다.

 

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QThread
from pynput import keyboard

class MyThread(QThread):
    cnt=0
    running= False

    def __init__(self):
        super().__init__()  
  
    def on_press(self,key):
        try:
            print(f'알파벳 \'{key.char}\' 눌림')
        except AttributeError:
            print(f'특수키 {key} 눌림')
 
    def on_release(self,key):
        print(f'키보드 {key} 풀림')
        if key == keyboard.Key.esc:
            # esc 키에서 풀림
            return False

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


class MyWindow(QMainWindow):
         
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Test")
        self.setGeometry(1000, 200, 300, 300)

        self.Thread1 = MyThread()
        self.Thread1.start()
    

if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()

    app.exec_()

 

 

반응형