質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

PyAutoGUI

PyAutoGUIは、Windows、Mac OS、Linuxに対応した、Python用のGUI自動化ライブラリです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

7228閲覧

PyAutoGUI keyDown("shift")とpress("left")を用いた文字列の選択ができない

horusutain

総合スコア6

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

PyAutoGUI

PyAutoGUIは、Windows、Mac OS、Linuxに対応した、Python用のGUI自動化ライブラリです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2020/04/18 03:09

#実現したいこと
PyAutoGUIを使用して、文字列の選択を行いたい。

<フロー>

  1. メモ帳を手動で起動
  2. 文字列を入力. typewriteメソッドを使用。
  3. shiftボタンを押しっぱなしにする. keyDown("shift")
  4. 矢印キーを押して、文字列を選択. press("left")など

#発生している問題
keyDown("shift")で、shiftボタンは確実に押しっぱなし状態になっているのに、
press("left")をすると、カーソルは移動するが選択状態が解除される。
※keyDown("shift")を実行した後に、手動でキーボードの矢印キーを押すと、
問題なく文字列を選択できる。

python

1#_*_ coding:utf-8 _*_ 2import pyautogui as pag 3import time 4 5#テキストエディタを開く時間を確保 6time.sleep(5) 7 8#文字の入力 9pag.typewrite("abcdefg",0.1) 10 11#shift keydown 12pag.keyDown("shift") 13 14#手動でキーボードの矢印キーを押すと、問題なく選択される 15time.sleep(5) 16 17#文字列は大文字で問題なく入力される 18pag.typewrite("upper",0.1) 19 20#pag.pressで矢印を操作。文字列の選択が解除される。 21#文字列の選択ができない 22pag.press("left") 23 24pag.keyUp("shift")

#試したこと
・pip, パッケージのアップデート => 効果なし
・keyDown("shift")の実行後に、手動で各種操作を行う。=> shiftキーが押された状態の挙動をする。
・"left","right"など、矢印の操作以外は正常に動作している。
・別のエディタ(IDLE)を使用してみる => 効果なし

#環境
OS: Windows10 Home 1909
VS Code:バージョン: 1.44.2 (user setup)
python 3.7.6

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

恐らく、 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/19 13:27

argparse

総合スコア1017

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

horusutain

2020/04/20 10:05

丁寧なご回答、ありがとうございます!! ご指摘の通り、NumLock が Onの状態で動作させていたのが原因でした。 これからは、ドキュメントだけではなく、Stack OverflowとGitHubのissueも確認するようにします。 閑話の部分、問題を解決するためにはどのように考えていけば良いのかを学ぶことができ、大変勉強になりました。 自分は、PyAutoGUIがWindowsAPIを利用して動作しているという基礎的な部分も理解できておらず、 また、ctypesを使うことでWindowsAPIを呼び出すことができるということも知りませんでした。 これらの基礎的な部分を知ることができ、とても為になりました。 本当に、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問