การดำเนินการขั้นสูงบนอินเทอร์เฟซ¶
บทนี้จะแนะนำเกี่ยวกับอินเทอร์เฟซอัตโนมัติที่ลึกซึ้งยิ่งขึ้น ซึ่งคุณสามารถใช้เพื่อดำเนินการที่ละเอียดอ่อนต่างๆ ได้ เนื้อหาในบทนี้ค่อนข้างเยอะ หากคุณเพิ่งเริ่มใช้งาน เราขอแนะนำให้อ่านแต่ละหัวข้ออย่างอดทน
เคล็ดลับ
การรับองค์ประกอบ (Get Element)¶
คุณอาจมีความเข้าใจเกี่ยวกับเรื่องนี้มาบ้างแล้วจากความรู้พื้นฐานหรือบทก่อนหน้า คุณต้องค้นหาองค์ประกอบที่เกี่ยวข้องผ่าน selector เพื่อที่จะดำเนินการกับมัน คุณน่าจะเห็นแล้วว่าสามารถรับพารามิเตอร์ selector ได้จากที่ไหน ตอนนี้เราจะแนะนำเพิ่มเติมเกี่ยวกับองค์ประกอบนี้ คุณสามารถเห็นข้อมูลที่เกี่ยวข้องกับองค์ประกอบ "同意" (ยอมรับ) ได้ทางด้านขวาของภาพนี้

ข้อควรสนใจ
สำหรับองค์ประกอบข้างต้น โดยทั่วไปเราจะใช้ text ในการรับมัน เงื่อนไขในการใช้ text คือต้องไม่มีองค์ประกอบอื่นบนอินเทอร์เฟซปัจจุบันที่มี text เป็น "同意" (ยอมรับ) เช่นกัน นี่เป็นวิธีที่ง่ายที่สุด นอกจากนี้คุณยังสามารถเลือกใช้ resourceId ได้ แต่โปรดทราบว่า resourceId ในที่นี้ไม่ได้หมายถึง ID ที่ไม่ซ้ำกัน แต่หมายถึง ID ของทรัพยากร และในหนึ่งอินเทอร์เฟซอาจมีองค์ประกอบหลายตัวที่มี resourceId เดียวกัน ส่วนอื่นๆ เช่น 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)¶
เรียกใช้อินเทอร์เฟซต่อไปนี้เพื่อทำการคลิกองค์ประกอบแบบธรรมดา ในบริบทนี้จะทำการคลิกปุ่ม "ยอมรับ" ด้วยตนเอง
element.click()
หากคุณต้องการระบุตำแหน่งที่จะคลิกบนองค์ประกอบ คุณสามารถระบุ corner ได้ Corner.COR_CENTER หมายถึงการคลิกที่จุดศูนย์กลางขององค์ประกอบ คุณยังสามารถคลิกที่มุมซ้ายบนหรือมุมขวาล่าง (COR_BOTTOMRIGHT) ได้อีกด้วย
element.click_exists(corner=Corner.COR_TOPLEFT)
ทำการกดค้าง (long press) บนองค์ประกอบนี้ หากไม่มีอยู่จะเกิด exception อินเทอร์เฟซนี้ยังรองรับ corner แต่ไม่สามารถระบุระยะเวลาการกดค้างได้
element.long_click()
หากองค์ประกอบมีอยู่จะทำการคลิก หากไม่มีอยู่ การเรียกใช้อินเทอร์เฟซนี้จะไม่ทำให้เกิด exception อินเทอร์เฟซนี้ยังรองรับ corner เช่นกัน
element.click_exists()
>>> element.click_exists()
True
การตรวจสอบว่ามีอยู่หรือไม่ (Check Existence)¶
ในหลายกรณี ก่อนที่จะดำเนินการต่อไป จำเป็นต้องตรวจสอบว่าองค์ประกอบนั้นมีอยู่หรือไม่ มิฉะนั้นกระบวนการถัดไปอาจเกิดข้อผิดพลาด หรืออาจเกิดการดำเนินการที่ผิดพลาดบนหน้าจอที่ไม่ถูกต้อง ในบางสถานการณ์คุณอาจต้องใช้อินเทอร์เฟซต่อไปนี้เพื่อตรวจสอบการมีอยู่ขององค์ประกอบ
element.exists()
ข้อมูลองค์ประกอบ (Element Information)¶
ในบางสถานการณ์ คุณอาจต้องการรับข้อมูลบางส่วนขององค์ประกอบ เช่น คุณอาจต้องการทราบพิกัด, พื้นที่, หรือข้อความหรือคำอธิบายที่อยู่บนองค์ประกอบ คุณสามารถใช้อินเทอร์เฟซต่อไปนี้เพื่ออ่านข้อมูลองค์ประกอบ
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 คุณสามารถเข้าถึง property ที่เกี่ยวข้องโดยตรงเพื่อพิมพ์ค่าจริงของมันออกมาได้ ตัวอย่างเช่น หากต้องการอ่านค่า text ขององค์ประกอบ คุณสามารถทำได้ดังนี้
>>> info = element.info()
>>> print (info.text)
同意
แน่นอนว่ายังมีข้อมูลเกี่ยวกับพิกัดพื้นที่ขององค์ประกอบอยู่ด้วย คุณสามารถเข้าถึงข้อมูลเหล่านั้นได้เช่นกัน ตัวอย่างเช่น หากคุณต้องการรับข้อมูลพื้นที่ขององค์ประกอบ คุณสามารถทำได้ดังนี้เพื่อพิมพ์ข้อมูลพื้นที่ออกมา หรือจะเก็บไว้เป็นตัวแปรเพื่อใช้ในการดำเนินการต่อไปก็ได้
>>> info = element.info()
>>> print (info.bounds)
ค่าที่ส่งออกหรือส่งคืนคือข้อมูลพื้นที่ (Bound) ซึ่งคุณจะพบในภายหลังว่าเป็นพารามิเตอร์ที่ใช้ในอินเทอร์เฟซการจับภาพหน้าจอบางตัวด้วย ใช่แล้ว คุณสามารถส่งพารามิเตอร์นี้ไปยังอินเทอร์เฟซการจับภาพหน้าจอเพื่อจับภาพเฉพาะองค์ประกอบนี้ได้ แต่เราได้ทำการ encapsulate ไว้ให้คุณแล้ว
คุณอาจต้องการรับความกว้างและความสูงขององค์ประกอบเพื่อคำนวณค่า offset บางอย่าง เช่น การคำนวณ offset ขององค์ประกอบอื่นที่สัมพันธ์กัน คุณสามารถทำได้ดังนี้
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
หรือรับจุดศูนย์กลางขององค์ประกอบ หรือจุดมุม เช่น มุมซ้ายบนหรือมุมขวาล่าง โดยปกติแล้วอินเทอร์เฟซต่อไปนี้จะส่งคืนข้อมูล Point และคุณยังสามารถรับพิกัดแกน X, Y ของหน้าจออุปกรณ์จากอ็อบเจกต์ Point ได้อีกด้วย
>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792
การเรียกใช้ต่อไปนี้ใช้เพื่อรับพิกัดมุมขององค์ประกอบ ตัวอย่างนี้เป็นการรับพิกัดมุมซ้ายบนขององค์ประกอบ นอกจากนี้ยังรองรับการรับพิกัดของมุมทั้งสี่ เช่น bottom-right, top-right, bottom-left
>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550
การวนซ้ำองค์ประกอบ (Iterating Elements)¶
คุณยังสามารถวนซ้ำองค์ประกอบทั้งหมดที่ถูกเลือกโดย selector ได้ โดยปกติแล้วในบริบทนี้อาจมีองค์ประกอบเพียงตัวเดียว ดังนั้นหากคุณต้องการทดสอบ โปรดเลือก selector อื่นเพื่อทดสอบ คุณสามารถใช้ for loop หรือวิธีอื่นในการวนซ้ำบน selector ได้โดยตรง
for i in element: print (i)
หรือหากคุณทราบว่ามีองค์ประกอบที่ตรงกันหลายตัว และต้องการรับองค์ประกอบที่ N ที่ตรงกัน คุณสามารถใช้อินเทอร์เฟซต่อไปนี้ได้
element_3rd = element.get(3)
การนับองค์ประกอบ (Counting Elements)¶
โดยปกติแล้วคุณจะไม่ค่อยได้ใช้อินเทอร์เฟซนี้โดยตรง การเรียกใช้ต่อไปนี้สามารถรับจำนวนองค์ประกอบที่ตรงกับ selector ปัจจุบันของคุณได้
>>> element.count()
1
การจับภาพหน้าจอองค์ประกอบ (Element Screenshot)¶
เรารองรับการจับภาพหน้าจอในระดับองค์ประกอบ คุณสามารถจับภาพเฉพาะขององค์ประกอบได้โดยไม่ต้องจับภาพทั้งหน้าจอแล้วมาตัดเอง
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")
การรอองค์ประกอบ (Waiting for Element)¶
ในบางสถานการณ์ คุณอาจต้องตรวจสอบว่าหน้าปัจจุบันโหลดเสร็จแล้วหรือไม่ โดยปกติคุณสามารถตรวจสอบได้โดยการดูว่าองค์ประกอบที่เกี่ยวข้องปรากฏขึ้นแล้วหรือยัง เพื่อตัดสินว่าหน้าปัจจุบันโหลดเสร็จแล้วหรือไม่ ตัวอย่างต่อไปนี้จะรอให้องค์ประกอบ "同意" (ยอมรับ) ปรากฏขึ้น โดยมีเวลารอสูงสุด 10 วินาที
คำแนะนำ
element.wait_for_exists(10*1000)
>>> element.wait_for_exists(10*1000)
True
แน่นอนว่าเราไม่เพียงแต่รองรับการรอให้องค์ประกอบปรากฏขึ้นเท่านั้น แต่ยังรองรับการรอให้องค์ประกอบหายไปอีกด้วย กล่าวคือ รอจนกว่าองค์ประกอบจะหายไปจากอินเทอร์เฟซ
element.wait_until_gone(10*1000)
>>> element.wait_until_gone(10*1000)
False
การป้อนข้อความ (Text Input)¶
การป้อนข้อความเป็นส่วนที่ต้องให้ความสนใจเป็นพิเศษ เราไม่สามารถป้อนข้อความลงบนปุ่ม "ยอมรับ" ได้เพราะมันเป็นปุ่ม ตอนนี้เราจะยกตัวอย่างองค์ประกอบช่องป้อนข้อมูล (input box) ขึ้นมาใหม่ ข้อมูลพื้นฐานขององค์ประกอบนี้มีดังนี้

ข้อควรสนใจ
คำแนะนำ
สำหรับช่องป้อนข้อมูลข้างต้น เราสามารถเรียกใช้อินเทอร์เฟซต่อไปนี้เพื่อป้อนสตริง "你好世界" (สวัสดีชาวโลก) ลงในช่องป้อนข้อมูลได้ แน่นอนว่ายังรองรับการป้อนภาษาอังกฤษหรือสตริง 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
หมายเหตุ
การปัดแบบธรรมดา (Normal Swipe)¶
ใช้อินเทอร์เฟซต่อไปนี้เพื่อทำการปัดหน้าจอ เช่น การเลื่อนรายการขึ้นลงเพื่อเปลี่ยนหน้า การเรียกใช้ต่อไปนี้เป็นการปัดขึ้น 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
การปัดเร็ว (Fast Swipe / Fling)¶
การปัดเร็วคล้ายกับการที่คนปัดหน้าจออย่างรวดเร็ว การดำเนินการนี้จะปัดหน้าจออย่างรวดเร็ว เหมาะสำหรับการจำลองการกระทำเช่นการเรียกดูอย่างรวดเร็ว ตัวอย่างต่อไปนี้เป็นการปัดหน้าจอจากบนลงล่าง ในตัวอย่าง selector เป็นค่าว่าง คุณยังคงต้องพิจารณาว่าจะกรอก selector หรือไม่ตามสถานการณ์จริง
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="购物"))
#########
# ค้นหาองค์ประกอบระดับเดียวกัน (sibling) หรือองค์ประกอบลูก (child)
#########
# บางครั้งอาจมีองค์ประกอบที่ซ้ำกันหรือไม่มีลักษณะเด่นชัด ทำให้ระบุตำแหน่งได้ยาก
# ในกรณีนี้ คุณสามารถจำกัดขอบเขตการค้นหาโดยการค้นหาองค์ประกอบลูก/องค์ประกอบระดับเดียวกัน
# องค์ประกอบลูก ตัวอย่างเช่น: ในกล่องล็อกอินการแชท ช่องป้อนข้อมูลจะเป็นองค์ประกอบลูกของกล่องล็อกอิน
# องค์ประกอบระดับเดียวกัน ตัวอย่างเช่น: ในช่องป้อนข้อมูลการแชท ช่องชื่อผู้ใช้และรหัสผ่านจะเป็นองค์ประกอบระดับเดียวกัน (ในสถานการณ์ปกติ)
form = d(resourceId="login_form")
form.child(index=1)
# นี่จะรับองค์ประกอบที่มี index เป็น 0 ภายใต้ login_form
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)