私たちの使っているPyAutoGUIは遅い、遅すぎる
久々に業務でPyAutoGUIを使う機会があったので、使ってみたのですが、やはり遅い。
そう考えた私は、PyAutoGUIを書き換えることを決意しました。
以前にも高速化をしたことはありますが、それよりも明らかに速くなりました。
環境
- Macbook Air 2017
- macOS Big Sur (早くMontereyにしろっていうね)
- Python3.10.1
- PyAutoGUI 0.9.53
PyAutoGUIの中身
GitHubのPyAutoGUIのプロジェクトの中身を見ると、OSX用のシステムではこのように動いてることが確認できます。
def _normalKeyEvent(key, upDown):
assert upDown in ('up', 'down'), "upDown argument must be 'up' or 'down'"
try:
if pyautogui.isShiftCharacter(key):
key_code = keyboardMapping[key.lower()]
event = Quartz.CGEventCreateKeyboardEvent(None,
keyboardMapping['shift'], upDown == 'down')
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
# Tiny sleep to let OS X catch up on us pressing shift
time.sleep(pyautogui.DARWIN_CATCH_UP_TIME)
else:
key_code = keyboardMapping[key]
event = Quartz.CGEventCreateKeyboardEvent(None, key_code, upDown == 'down')
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
time.sleep(pyautogui.DARWIN_CATCH_UP_TIME)
# TODO - wait, is the shift key's keyup not done?
# TODO - get rid of this try-except.
except KeyError:
raise RuntimeError("Key %s not implemented." % (key))
んで、これの何が悪いかというと、大きく分けて2つあります。
- shiftが効くときと効かないときがある
CGEventCreateKeyboardEvent()
を文字が入力される都度召喚しているため、遅い
以下では、これら2つを解決してゆきます。
shiftに依存する文字の入力ルールを変える
上記のコードは時として”!”や”?”を入力してくれないときがあります。
これは、macOSが0.01秒間のスリープ(pyautogui.DARWIN_CATCH_UP_TIME
で与えられている定数のようなもの)をしたからといってshiftを手放してくれるとは限らないということや、なぜかshiftを押してくれないからです。
なんとかして”!”や”?”などの文字を入力させたいと考えた私は、色々と調べてみました。
有効そうだと思っていたApple Developerのサイトは的外れで、このサイトのコードの通りにしてもshiftが機能したりしなかったりしたため、諦めそうになりました。
しかし、やはりstack overflowに集う人々は有能でした(いつものように!)
Quartzで定義されているCGEventSetFlags()
のような関数やkCGEventFlagMaskShift
のような定数を使えば、このshift入力問題を解決できるとのこと。
ただ、提示されていたコードはSwiftのものであり、他に探してみても同様にSwiftで書かれていたため、これをPython用に読み替える必要がありました。
とりあえず、テキトーにコードを組み立ててみて確認してみました。
ev = Quartz.CGEventCreateKeyboardEvent(None, 0x2c, True) # create event to type '/' slash
Quartz.CGEventSetFlags(ev, Quartz.kCGEventFlagMaskShift) # mask event with shift
Quartz.CGEventPost(Quartz.kCGHIDEventTap, ev) # type '?'
このコードを実行してみると、見事に”?”を入力することに成功しました。
関数の呼び出し回数を減らす
上記のshift入力問題を解決するにあたり、「もしかしたら、先にQuartz.CGEventCreateKeyboardEvent()
を用意しておけば、既存のPyAutoGUIよりも速くできるかもしれない」と考えてついた私は、早速、コードを書き換えました。
# ANSI keys
codes = {
'a': 0x00,
's': 0x01,
'd': 0x02,
'f': 0x03,
# ...
'?': 0x2c,
'!': 0x12,
}
# formatting create keyboard event made by Quartz
codes = {
key: Quartz.CGEventCreateKeyboardEvent(None, val, True) for key, val in codes.items()
}
shift_codelist = 'ASDF!?' # ...
# masking characters needed shift key.
for key in shift_codelist:
Quartz.CGEventSetFlags(codes[key], Quartz.kCGEventFlagMaskShift)
def write(text: str):
for ch in text:
Quartz.CGEventPost(Quartz.kCGHIDEventTap, codes[ch])
if __name__ == '__main__':
write('Hello, World!')
イベントを事前に辞書に追加しておくことにより、高速化することに成功しました。
さいごに
和訳調になっちゃっているのは最近、村上春樹の海辺のカフカを読んでしまっているせいです笑
つまり、私のこの文章がオリジナルなので、「元の英語の文章はどこだよ?」と探してみてもありません。
Pythonのサードパーティーライブラリーはいろんな人が作ることができるし、いろんな人が中身を見ることができるので、「なんかこの挙動おかしくね?」と思っているみなさんは、いちど中身を確認してみるのも悪くないかもしれません。
ただし、コードを書き換えてコンピューターを壊してしまわないように注意しましょう。あくまでコードの書き換えは自己責任でお願いします。
- 前の記事
日商簿記2級を60時間の勉強で受かった話 2022.04.04
- 次の記事
どうにかしてDisney plusの字幕フォントを変える方法 2022.04.25