인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

Inflearn Community Q&A

피자조와용's profile image
피자조와용

asked

Dr. Nam's Python Basics, 100% Practical Use

섹션8 파이썬 실전 프로그래밍 > 나만의 단축키 만들기 스마트 > 함수호출불가현상

Written on

·

718

2

나만의 단축키 만들기(스마트)에서 코드를 영상과 똑같이 타이핑해서 실행했는데도 함수1이 호출이 안되시는 분들을 위해 공유합니다. 우선 첫번째 그림에서 1, 2번 줄을 보시면 n을 단독으로 입력하였을땐 n키의 press, release가 정상적으로 작동하였습니다. 이후 <ctrl _l + alt_l + n>을 입력하였는데 <ctrl _l + alt_l + 78>로 press되고 <ctrl _l + alt_l + n>으로 release된것을 볼 수 있습니다. ctrl키랑 함께 누르면 영문키가 영어가 아닌 다른 무언가로 입력되는것 같습니다. 그래서 MY_HOT_KEYS변수에서 KeyCode(char="n") 부분을 KeyCode(78)로 바꾸어주니(2, 3번 그림) 함수가 잘 호출되었네요(4번 그림)

따라서, 저처럼 ctrl키와 함께 무언가를 누를때 누른 값이 아닌 다른 값이 입력되는 분들은 코드실행해서 하나하나 입력, 반환받아보고 찾아서 넣어주어야 할것 같습니다. 제 컴퓨터에서는 왜이런지 모르겠네요 ꉂꉂ(ᵔᗜᵔ*) 근본적인 해결책이 있다면 알려주세요!

Key.xx 반환시  -->  그대로 Key.xx

<int> 반환시     -->  KeyCode(int)

'str' 반환시       -->  KeyCode(char = "str")

웹-크롤링python

Answer 1

3

drnam님의 프로필 이미지
drnam
Instructor

어려운걸 찾아내셨습니다. 엄청 고민하셨던 흔적이 보입니다.^^;;;;

78은 n 키의 ascii 코드 값입니다.  78 == char('n') 같은 내용입니다만 왜 그렇게 동작하는지는 pynput 라이브러리의 소스 코드를 분석해봐야 알듯 합니다. [아스키코드값 링크]

사실 pynput 은 윈도우의 경우 WH_KEYBOAD_LL 라는 메세지를 후킹하는 DLL을 파이썬에서 사용하게 래핑한 라이브러리 입니다. (윈도우는 모든 동작이 메세지 형태로 일어나며 그 메세지를 가로채는걸 후킹 한다고 합니다) 그 말인 즉슨 pynput 은 파이썬과 C++로 작성된 DLL 파일과의 다리 역할만 하고 실제 키를 감지, 키를 발생시키는 애는 윈도우 자체가 하고 있는걸로 보입니다.

VS CODE 상에서

listener = keyboard.Listener(
    on_press=on_press,
    on_release=on_release)
listener.start()

키보드를 리스닝하는 keyboard.Listener 코드 에서 Listener를 컨트롤키 + 클릭 하면 pynput 소스 코드로 진입할 수 있습니다. 그러면 거기서 



if sys.platform == 'darwin':
    if not KeyCode and not Key and not Controller and not Listener:
        from ._darwin import KeyCode, Key, Controller, Listener

elif sys.platform == 'win32':
    if not KeyCode and not Key and not Controller and not Listener:
        from ._win32 import KeyCode, Key, Controller, Listener

else:
    if not KeyCode and not Key and not Controller and not Listener:
        try:
            from ._xorg import KeyCode, Key, Controller, Listener
        except ImportError:
            # For now, since we only support Xlib anyway, we re-raise these
            # errors to allow users to determine the cause of failures to import
            raise

위와 같은 부분이 있는데 윈도우인 경우 sys.platform == 'win32' 밑의 코드에서

from ._win32 import KeyCode, Key, Controller, Listener

여기서 Listener 를 컨트롤 + 클릭 하시면 Listener 클래스의 소스 코드를 보실 수 있습니다. 여기서.. 밑으로 스크롤을 조금 내려보시면

@AbstractListener._emitter
    def _process(self, wparam, lparam):
        msg = wparam
        vk = lparam

        # If the key has the UTF-16 flag, we treat it as a unicode character,
        # otherwise convert the event to a KeyCode; this may fail, and in that
        # case we pass None
        is_utf16 = msg & self._UTF16_FLAG
        if is_utf16:
            msg = msg ^ self._UTF16_FLAG
            scan = vk
            key = KeyCode.from_char(six.unichr(scan))
        else:
            try:
                key = self._event_to_key(msg, vk)
            except OSError:
                key = None

        if msg in self._PRESS_MESSAGES:
            self.on_press(key)

        elif msg in self._RELEASE_MESSAGES:
            self.on_release(key)

위의 코드가 on_press 되었을때 호출되는 부분이고 여기 하단의 self.on_press가 호출되는데 이 함수는 _base.py 파일에

def __init__(self, on_press=None, on_release=None, suppress=False,
                 **kwargs):
        prefix = self.__class__.__module__.rsplit('.', 1)[-1][1:+ '_'
        self._options = {
            key[len(prefix):]: value
            for key, value in kwargs.items()
            if key.startswith(prefix)}
        super(Listener, self).__init__(
            on_press=on_press, on_release=on_release, suppress=suppress)

위처럼 초기화 되면서 on_press 함수가  내가 작성한 on_press 함수로 연결되는 부분인걸 확인 하실 수 있습니다. (복잡합니다...)

어쨌든 다시 위의 코드를 보면

def _process(self, wparam, lparam):

이 함수의 인자로 넘어오는 wparam 은 윈도우 메세지 이름이고 lparam 값이 키보드의 키 코드 값입니다. 이런식으로  위에서 말씀드린 컨트롤 + 클릭으로 pynput 소스코드를 분석하셔서 필요한곳에 print 문 같은걸 찍어서 확인해보시는면 찾아볼 수 있지 않을까 생각됩니다. 물론 라이브러리의 소스 코드를 수정하는건 신경써서 하셔야 하고 프린트문 찍었다가 지우시고 만약 라이브러리가 꼬이면 삭제하시고 다시 설치하시면 됩니다. 도움이 되셨으면 좋겠습니다.

피자조와용's profile image
피자조와용

asked

Ask a question