恐らく、 NumLock が On のままスクリプトを動作させているため では無いでしょうか。
同様の処理を PyAutoGUI で実装したが、 NumLock が On だと同様にうまく範囲選択が効かなかったという報告が、 Stack Overflow や 本家 GitHub の issue に寄せられて居り、実際に私も手元の環境で同じ現象を確認出来ました。
もしこの条件に該当しているようでしたら、取り急ぎは スクリプトの実行時、 NumLock を Off にしてから作業を行う ことで改善するのではないかと思われます。
あるいは、 NumLock の状態は GetKeyState()
API によって取得できますから、以下のようなコードで自動的に Off にしてやっても良いかもしれません。
python
1import pyautogui as pag
2import ctypes
3
4VK_NUMLOCK = 0x90
5# あるいは pag.platformModule.keyboardMapping['numlock']
6
7if ctypes.windll.user32.GetKeyState(VK_NUMLOCK):
8 pag.press('numlock')
閑話: 何故 NumLock が On だと正しく動作しないのか
上記の問題発生時に操作対象のウィンドウが受け取るウィンドウメッセージを確認してみた所、 NumLock が On の場合は 「一度 Shift キーの WM_KEYUP
が勝手に送信され、その後 Left キーの WM_KEYDOWN
/ WM_KEYUP
を送信したうえで、状態を元に戻すかのように Shift キーのWM_KEYDOWN
が送信される」 といった動作をして居りました。
つまり Left キーの押し下げ時だけ 勝手に Shift キーが離された扱い になっており、そのため「範囲選択」ではなく「カーソル移動」という結果を生じてしまっているのですが、何故このような変な挙動をするのか? については、正直なところ明確な理由が分かりませんでした。ただ、恐らくは キー操作送信時に KEYEVENTF_EXTENDEDKEY
というフラグを指定するか否か が関係しているものと思われます。
PyAutoGUI は Windows 環境でのキー操作を keybd_event()
API を利用して行っているのですが、ここで第三引数に指定可能な KEYEVENTF_EXTENDEDKEY
というフラグが存在します。このフラグの意味は、上記の keybd_event()
API のドキュメントでは「指定された場合、スキャンコードの前に前置バイト 0xE0
が与えられる」という、理解に苦しむ説明となっていて良く分かりませんが、例えば この辺りの Stack Overflow の回答 を参考にする限り、 スキャンコードの値の範囲内で表現しきれない一部のキーについて、併せて指定することで「拡張キー」として何とか表現可能とする ためのフラグと考えられます。
またこの Stack Overflow の回答が述べているように、同フラグは WM_KEYDOWN
メッセージ の lParam
24 bit 目にも反映されるのですが、そちらの説明にも「101/102 キーボードに登場する、右 ALT キーや右 CTRL キーなどの拡張キーであるか否かを示す」と記載されており、ほぼ同様の結論が得られます。つまり、 特定の「拡張キー」に分類されるキー操作を送信する場合に指定しなければならないフラグであるようだ ということです。
そして、少なくとも私の環境では、実際にキーボードを操作した時の WM_KEYDOWN
メッセージを見る限り、今回問題となっている Left キーもどうやら「拡張キー」である ようでした。
従って、 Left キーを送信するときは KEYEVENTF_EXTENDEDKEY
フラグを指定すべきだと考えられるのですが、残念ながら PyAutoGUI が keybd_event()
API を呼び出す個所 では、このフラグについては一切考慮されておりません。そのため、 不適切な API の使い方となり、結果良く分からない妙な挙動を招いてしまっている のでは、というのが今の所の私の見解です。
実際、 pyautogui.press()
を使わず、以下のようにして KEYEVENTF_EXTENDEDKEY
フラグ付きで自ら直接 keybd_event()
による Left キー送信を行ったところ、特に NumLock の On/Off には関係なく期待通りの動作が得られました。
python
1import ctypes
2
3KEYEVENTF_EXTENDEDKEY = 0x0001
4KEYEVENTF_KEYUP = 0x0002
5VK_LEFT = 0x25
6
7ctypes.windll.user32.keybd_event(VK_LEFT, 0, KEYEVENTF_EXTENDEDKEY, 0)
8ctypes.windll.user32.keybd_event(
9 VK_LEFT,
10 0,
11 KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
12 0
13)
以上の検討から、もしも NumLock を Off にするという ad-hoc な対応をせず、真面目に PyAutoGUI を修正するのであれば、 「拡張キー」に分類される仮想キーコードを列挙しておき、その送信時には KEYEVENTF_EXTENDEDKEY
フラグを指定する という実装に変更するのが適切な対応であるように思われます。
が、まあ、それにはもう少しこの現象の正しい理解が必要ですし、何より一般のユーザとしてはそんな重たい pull-request を送る気にはなれませんから、とりあえず頭の片隅に入れておいて、 特定のキーが妙な挙動をしていたら、「拡張キー」かどうかを疑い、必要に応じて自分で keybd_event()
API を呼び出す ぐらいが丁度良いのではないでしょうか。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/20 10:05