[Python/pynput]キーボード入力を試してみる – MacOSでは思うように動かない

前回でマウスクリックが取れたので、同じpynputでキーボード入力まで取得してみました。
ただ、今のところはMacOSでは正しく動かせていません。※Windowsでは問題なかったです。
試行錯誤の記録を残します。

やりたいこと

  • キーボード入力とマウス入力を取得して処理をする

問題点

コードを書いてみましたが、以下の問題が発生しました。

  • controlshiftは取得できるが、アルファベットが取得できない
    • sudoで実行することで解決
  • プロセス実行中に入力した内容が、プロセス終了後にターミナルでも再入力されてしまう
    • 未解決

コード

# キーボード入力と記載されているあたりが追記した箇所です。

from pynput import mouse, keyboard

class Monitor:
    def __init__(self):
        self.counter = 0
        self.over_count = 5

    def count(self):
        self.counter += 1
        print('Count:{0}'.format(self.counter))

    def is_over(self):
        return True if self.counter >= self.over_count else False

    def call(self):
        self.count()
        if self.is_over():
            print('Done')
            self.mouse_listener.stop() # 規定回数過ぎたら終了
            self.keyboard_listener.stop()

    # マウス入力
    def on_click(self, x, y, button, pressed):
        """クリック時に呼ばれる
        """
        print('{0} at {1}'.format(
            'Pressed' if pressed else 'Released',
            (x,y)))

        if pressed:
            self.call()

    # キーボード入力
    def on_press(self, key):
        """キーを押したときに呼ばれる"""
        try:
            print('alphanumeric key {0} pressed'.format(key.char))

        except AttributeError:
            print('special key {0} pressed'.format(key))

    def on_release(self, key):
        """キーを離したときに呼ばれる"""
        try:
            print('alphanumeric key {0} released'.format(key.char))

        except AttributeError:
            print('special key {0} released'.format(key))


    def start(self):
        with mouse.Listener(
            on_click=self.on_click) as self.mouse_listener, keyboard.Listener(
            on_press=self.on_press, on_release=self.on_release) as self.keyboard_listener:
            self.mouse_listener.join()
            self.keyboard_listener.join()


monitor = Monitor()
monitor.start()

[小ネタ]複数のlistenerを起動する

    def start(self):
        with mouse.Listener(
            on_click=self.on_click) as self.mouse_listener, keyboard.Listener(
            on_press=self.on_press, on_release=self.on_release) as self.keyboard_listener:
            self.mouse_listener.join()
            self.keyboard_listener.join()

上記のように書くと、マウスクリックとキーボード入力両方取得できるようになります。
2つのファイルを同時に開くときに、with open(f_1) as fr, open(f_2) as fw: というように書くのと同じ要領ですね。

実行結果

コードを実行してみます。
MacOSにおいてkeyboard入力を取得する場合、コードはsudoで実行する必要があります。

実行すると入力待機状態になり、マウスクリックはもちろん、キー入力を取得できます。

ただし、
プロセス終了後、これまで入力されていた内容がまとめてターミナルに入力されてしまいます。

$ sudo python pynput_monitor.py
Password:

special key Key.backspace pressed
special key Key.backspace released
alphanumeric key   pressed
alphanumeric key   released
special key Key.shift pressed
special key Key.shift released
special key Key.shift pressed
special key Key.shift released
alphanumeric key s pressed
salphanumeric key s released
alphanumeric key s pressed
salphanumeric key s released
alphanumeric key v pressed
valphanumeric key v released
special key Key.enter pressed

special key Key.enter released
Pressed at (-134.3828125, 120.80078125)
Count:1
Released at (-134.3828125, 120.80078125)
Pressed at (-134.3828125, 120.80078125)
Count:2
Released at (-134.3828125, 120.80078125)
Pressed at (-134.3828125, 120.80078125)
Count:3
Released at (-134.3828125, 120.80078125)
Pressed at (-134.3828125, 120.80078125)
Count:4
Released at (-134.3828125, 120.80078125)
Pressed at (-134.3828125, 120.80078125)
Count:5
Done

$ ssv # ← ssvと入力していたのが反映されてしまう

まとめ

キーボード入力はなかなか奥深いものがありますね…

今の挙動は、誤操作を引き起こしうる危険な挙動であり、実用的とは言えません。
現在以下のような解決策を模索しているところです。

  • 別のライブラリを試してみる
  • クラスではなく、モジュールにして使ってみる、など。

また、今後ですが同時押しも実装していきたいと思います。

参考