UIの高度な操作¶
この章では、より高度な自動化インターフェースについて紹介します。これらのインターフェースを通じて、さまざまな詳細な操作を完了させることができます。この章は内容が多いため、初めての方は、各項目をじっくりと読むことをお勧めします。
豆知識
要素の取得¶
基礎知識や前の章で、すでにある程度理解されているかもしれませんが、操作を行うにはセレクターを使って関連する要素を見つける必要があります。セレクターのパラメータをどこで取得できるかもご覧になったことでしょう。これからの説明は、この要素を中心に展開されます。この画像の右側で、「同意」という要素の関連情報を見ることができます。

注目
上記の要素を取得するには、一般的に text を使用します。text を使用する条件は、現在のUIに text が「同意」である他の要素が存在しないことです。これが最も簡単な方法です。次に、resourceId を使用することもできますが、ここでの resourceId は一意のIDではなく、リソースIDを表すことに注意してください。一つのUI内には、同じリソースIDを持つ要素が多数含まれている可能性があります。packageName や checkable などは一般的にはあまり使用されませんが、text、resourceId、description などが利用できない場合には、これらのフィールドを試すことができます。この要素は、以下のようないくつかの方法で取得できます。
element = d(text="同意")
element = d(text="同意", resourceId="com.tencent.news:id/btm_first_agree")
element = d(resourceId="com.tencent.news:id/btm_first_agree")
要素のクリック¶
以下のインターフェースを呼び出して、通常の要素クリック操作を行います。以下のコードは、手動で「同意」ボタンをクリックする効果を実装します。
element.click()
要素のクリック位置を指定する必要がある場合は、corner を指定できます。Corner.COR_CENTER は要素の中心点をクリックすることを意味し、左上隅(COR_TOPLEFT)や右下隅(COR_BOTTOMRIGHT)をクリックすることもできます。
element.click_exists(corner=Corner.COR_TOPLEFT)
要素上で長押し操作を実行します。存在しない場合は例外がスローされます。このインターフェースも corner をサポートしていますが、長押し時間を指定することはできません。
element.long_click()
要素が存在すればクリックします。要素が存在しない場合、このインターフェースを呼び出しても例外は発生しません。このインターフェースも同様に corner をサポートしています。
element.click_exists()
>>> element.click_exists()
True
存在確認¶
多くの場合、次の操作に進む前に要素の存在を確認する必要があります。そうしないと、後続のプロセスで例外が発生したり、間違ったUIで誤った操作を行ったりする可能性があります。状況によっては、以下のインターフェースを使用して存在確認を行う必要があります。
element.exists()
要素情報¶
場合によっては、要素の座標、領域、または要素に含まれるテキストや説明などの文字列情報といった、要素の一部の情報を取得したいことがあります。以下のインターフェースを使用して要素情報を読み取ることができます。
element.info()
上記のテスト要素では、このテスト要素が出力する情報は以下の通りです。
>>> info = element.info()
>>> print (info)
bounds { ... }
className: "android.widget.TextView"
clickable: true
enabled: true
focusable: true
packageName: "com.tencent.news"
resourceName: "com.tencent.news:id/btn_first_agree"
text: "\345\220\214\346\204\217"
visibleBounds { ... }
ヒント
この情報はやや複雑に見えるかもしれませんが、これは protobuf のデフォルトの出力形式です。対応するプロパティに直接アクセスすることで、その実際の値を出力できます。例えば、要素の text の値を取得したい場合は、以下のように使用できます。
>>> info = element.info()
>>> print (info.text)
同意
もちろん、中には要素の領域座標に関する情報も含まれており、それらにもアクセスできます。例えば、その要素に対応する領域情報を取得したい場合は、以下のように使用して領域情報を出力できます。また、それを後続の操作のための変数として取得することもできます。
>>> info = element.info()
>>> print (info.bounds)
出力または返される値は領域情報(Bound)です。後で、これが一部のスクリーンショットインターフェースでも使用されるパラメータであることに気づくでしょう。そうです、このパラメータをスクリーンショットインターフェースに渡すことで、この要素だけのスクリーンショットを撮ることができますが、私たちはすでにその機能をラップして提供しています。
また、他の相対的な要素のオフセットを計算するなど、要素の幅と高さを取得したい場合もあるでしょう。その場合は、次のようにします。
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
あるいは、その要素の中心点や、左上隅や右下隅などの角の座標を取得することもできます。もちろん、以下のインターフェースは通常 Point 情報を返します。Point オブジェクトから対応するX、Y軸のデバイス画面座標を取得することもできます。
>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792
以下の呼び出しは、要素の角の座標を取得するために使用します。例では要素の左上隅の座標を取得していますが、その他にも bottom-right、top-right、bottom-left など、合計4つの角の座標を取得できます。
>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550
要素の走査¶
セレクターで選択されたすべての要素を走査することもできます。通常、このコンテキストでは要素は1つしかない可能性があるため、テストする場合は他のセレクターを選択してテストしてください。セレクター上で直接 for ループや他の方法を使用して走査できます。
for i in element: print (i)
または、複数のマッチする要素が存在することがわかっていて、指定したN番目のマッチする要素を取得したい場合は、以下のインターフェースを使用できます。
element_3rd = element.get(3)
要素のカウント¶
通常、このインターフェースを直接使用することはありません。以下の呼び出しで、現在のセレクターにマッチした要素の数を取得できます。
>>> element.count()
1
要素のスクリーンショット¶
要素レベルでのスクリーンショットをサポートしており、全画面をキャプチャしてから切り抜く必要なく、要素の画像だけを個別にキャプチャできます。
element.screenshot(quality=60)
スクリーンショットを撮った後、getvalue を使ってスクリーンショットのバイナリデータを直接取得したり、PIL Imageに直接渡したりすることができます。
>>> element.screenshot(quality=60).getvalue()
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xe2\x02(ICC_PROFILE\x00\x01\x01\x00\x00\x02\x18\x00\x00\x00\x00\x02\x10\x00\x00mntrRGB XYZ \x00\x00...
または、さらなる処理が不要な場合は、スクリーンショットを直接ローカルファイルに保存することもできます。
>>> element.screenshot(quality=60).save("image.png")
要素の待機¶
場合によっては、現在のページが読み込み完了したかどうかを判断する必要があります。通常、関連する要素が表示されたかどうかで現在のページが読み込み完了したかを判断できます。以下の例では、「同意」要素が表示されるのを最大10秒間待ちます。
ヒント
element.wait_for_exists(10*1000)
>>> element.wait_for_exists(10*1000)
True
もちろん、要素の出現を待つだけでなく、要素が消えるのを待つこともサポートしています。つまり、要素がUIから消えるまで待ち続けます。
element.wait_until_gone(10*1000)
>>> element.wait_until_gone(10*1000)
False
テキスト入力¶
テキスト入力は比較的注意が必要な部分です。「同意」ボタンにテキストを入力することはできません。なぜならそれはボタンだからです。ここでは、新たに入力ボックスの要素を例に説明します。この要素の基本情報は以下の通りです。

注目
ヒント
上記の入力ボックスに対して、以下のインターフェースを呼び出して「你好世界」という文字列を入力できます。もちろん、英語や他のUnicode文字列の入力もサポートしています。以下のように使用するだけで、ボックス内にテキストを入力できます。
>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True
あるいは、ふと思いついて、この入力ボックスに現在表示されているテキスト内容を取得したい場合は、次のように呼び出すことができます。
注目
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'
または、現在入力されている内容をクリアすることもできます。通常、テキストを入力すると以前のテキストは自動的にクリアされますが、手動でクリアすることも可能です。
ヒント
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True
注釈
通常のスワイプ¶
以下のインターフェースを使用して、リストの上下スワイプによるページめくりなど、UIのスワイプ操作を行います。以下の呼び出しは上方向へのスワイプを実装します。step は適宜調整してください。値が大きいほどスワイプ速度は遅くなり、精度の高いスワイプに適しています。
注目
d().swipe(direction=Direction.DIR_UP, step=32)
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.swipe(direction=Direction.DIR_UP, step=32)
True
| 方向指示子 | 説明 |
|---|---|
| Direction.DIR_UP | 上へスワイプ |
| Direction.DIR_LEFT | 左へスワイプ |
| Direction.DIR_DOWN | 下へスワイプ |
| Direction.DIR_RIGHT | 右へスワイプ |
高速スワイプ (Fling)¶
高速スワイプは、人が画面を素早くスワイプするような動作に似ています。この操作は画面を素早くスワイプするため、高速で閲覧するような操作のシミュレーションに適しています。以下の例は、画面を上から下へスワイプします。例ではセレクターが空ですが、実際の状況に応じてセレクターを記入するかどうかを選択する必要があります。
d().fling_from_top_to_bottom()
下から上への高速スワイプ
d().fling_from_bottom_to_top()
左から右への高速スワイプ
d().fling_from_left_to_right()
右から左への高速スワイプ
d().fling_from_right_to_left()
注目
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True
更新中...
その他の操作¶
# このアプリを「ショッピング」フォルダにドラッグして分類します(実際の状況に合わせて変更してください)
element.drag_to(Selector(text="ショッピング"))
#########
# 兄弟要素または子要素を検索
#########
# 時には、重複した要素や明確な特徴のない要素があり、特定が難しいことがあります
# その場合、子要素/兄弟要素を検索することで検索範囲を絞り込むことができます
# 子要素の例:チャットのログインフォーム。その中の入力ボックスはログインフォームの子要素です
# 兄弟要素の例:チャットの入力ボックス内のユーザー名とパスワードのボックスは兄弟要素です(通常の場合)
form = d(resourceId="login_form")
form.child(index=1)
# これは login_form の下にある index が 0 の要素を取得します
form.child(index=1).sibling()
# このようにして、login_form と同じ階層にある「パスワードを忘れた場合」ボタンを見つけることもできます
# (実際には文字列で判断できるので、このようにする必要はありません。ここではデモとして示しています)
form.sibling(textContains="パスワードを忘れた場合")
# これら自体が element であり、element に対してあらゆる操作を行うことができます
# その他、下/左/右/上へ、最後までスワイプし続ける
# 必ずしも最後までスワイプできる、または最後までスワイプしたことを検出できるとは限らないため
# max_swipes パラメータは必須です
d().fling_from_top_to_bottom_to_end(max_swipes=32)
d().fling_from_bottom_to_top_to_end(max_swipes=32)
d().fling_from_left_to_right_to_end(max_swipes=32)
d().fling_from_right_to_left_to_end(max_swipes=32)
#########
# scroll: より機械的なスワイプ
#########
step = 60
max_swipes = 32
# 上から下へ step ステップ分スワイプ
d().scroll_from_top_to_bottom(step)
# 下から上へ step ステップ分スワイプ
d().scroll_from_bottom_to_top(step)
# 左から右へ step ステップ分スワイプ
d().scroll_from_left_to_right(step)
# 右から左へ step ステップ分スワイプ
d().scroll_from_right_to_left(step)
# その他、下/左/右/上へ、最後までスワイプし続ける
# 上記の fling の説明と同様
d().scroll_from_top_to_bottom_to_end(max_swipes, step)
d().scroll_from_bottom_to_top_to_end(max_swipes, step)
d().scroll_from_left_to_right_to_end(max_swipes, step)
d().scroll_from_right_to_left_to_end(max_swipes, step)