0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pywinauto でクラス名やキャプション名を得る

Posted at

Python で他のアプリをコントロールする

VBA で Win32API を利用し、クラス名やキャプション名からのウィンドウハンドルを取得して、SendMessage や PostMessage を駆使して TextBox に文字を放り投げたり、ComboBox を選択したり、Button をクリックしたりという事 (以下①) もやりました。

VBA で、UIAutomation を利用し、エレメントを取得して操ったりする事 (以下②) もありました。
(なんで IsInvokePatternAvailable が False なんだよ!って怒ったりね)

でも、最近は「Python でお願い」されることが増えました。
以前、win32gui と言うのを使ってを行った事はあるんです。
win32gui だと、の方法を、VBA に近い記述で組めるけど、の機能はない(と思う)。
という訳で色々探してたら、pywinauto と言うのが、にもにも対応してるっぽい。
(PyAutoGUI というのもあったけど、②に対応しているかどうかわからないので今回は未確認)

余談ですが、win32guiのインストールは、pip install win32guiじゃなくてpip install pywin32

pywinauto の良い所

pywinauto のドキュメントは英語なんですよ・・・
https://pywinauto.readthedocs.io/en/latest/contents.html
ワタシィ、エイゴォ、ワッカリマセェ〜ン、タメィゴゥ

しかし、も対応なんて、使わない手はない。

しかも、VBA で組むと全然コードが違うが、ほぼ同じ記述の仕方で組めるように Wrap されてる!

ちなみに、pip install pywinautoを眺めてると分かりますが、win32gui も使用している様です。

pywinauto で、意外と見つけにくかった情報

pywinauto で、よく見るやり方

既に起動しているメモ帳に文字を記入する
from pywinauto import Desktop, Application

app = Desktop(backend='win32')
win = app.window(class_name='Notepad', top_level_only=True) #メモ帳アプリのウィンドウを取得
ed_elem = win.child_window(class_name='Edit') #メモ帳内のエディット領域を取得
ed_elem.set_edit_text(u'OK!') #OK!を書き込む

app = Desktop(backend='win32')backend='win32'の部分が、の方法で取得しますよ、という意味です。
app = Desktop(backend='uia')と記載すれば、の方法で取得する事になります。
前述の通り、どちらで取得してもその後のコードは似たようになるので、どちらを選んでも良い様に思えますが、②の方法でしか取得できないコントロールがあるので、注意してください。
この辺は、Spy++ や、inspect を使って確認する事になります。
(inspect で確認できるのに、UIAutomation で取得できないモノも、ちらほら・・・)

余談ですが、

re で正規表現マッチ
win = app.window(title_re='B.*', class_name_re='N.*', top_level_only=True)

title_reclass_name_reを使う事で、。正規表現によるマッチが出来ます。

とあるコントロールの次コントロールが取得したい

ComboBox などは、キャプションが無く、class_name でしか取得できない事が多いですが、ComboBox が複数ある場合、「このコントロールの次の ComboBox が取りたい!」という事も往々にしてあると思います。

当該コントロールの下に有るコントロールをループで回す
from pywinauto import Desktop, Application

app = Desktop(backend='win32')
win = app.window(title_re='BT.*', class_name='#32770', top_level_only=True) #ウィンドウを取得
flg_combo = False
for obj_ctrl in win.descendants():
    if obj_ctrl.element_info.class_name == 'Static' and obj_ctrl.element_info.name == 'フォーマット(&F)':
        flg_combo = True
    elif flg_combo and obj_ctrl.element_info.class_name == 'ComboBox':
        obj_ctrl.select('JPEG')
        break

当該コントロールの子コントロールは、[当該コントロール].descendants()で取得できます。
それを for ループで回せばOK。
今回躓いたのは、クラス名やキャプション名を取得する手法。
上記を見てもらえばわかると思いますが、

  • [当該オブジェクト].element_info.nameでキャプション名
  • [当該オブジェクト].element_info.class_nameでクラス名

が取得できます。

調べるのが面倒なので、覚書

  • [当該オブジェクト].print_control_identifiers()で当該オブジェクトとその子オブジェクトの情報を取得
  • [当該オブジェクト].TypeKeys('{ENTER}')で当該オブジェクトにキー操作(この例ではENTERキー押下)を行う
  • 普通にテキストボックス等に文字を入力する場合は[当該オブジェクト].set_edit_text(u'OK!')で良い
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?