四、Poco控件的核心API讲解

1. 前言

本文将以官网上的安卓游戏demo为例,下载地址。下载完对应的 “demo game android” 以后,安装在待测的安卓手机上,启动游戏;再使用IDE连接上待测手机,并在poco辅助窗选择 unity 模式,刷出UI树后即可开始测试。

image

阅读本文,你将了解poco控件的以下核心API功能:

  • 点击操作
  • 滑动操作
  • 读取和设置控件的属性
  • 判断元素是否存在
  • 拖动操作
  • 内部偏移和外部偏移(focus)
  • 等待事件

2. 控件点击操作

目前poco控件的支持的点击操作包含单击和长按,双击和右键单击均未实现。

  1. # 控件单击
  2. poco("star_single").click()
  3. # 控件长按
  4. poco('star_single').long_click()

click

click()long_click() 的API详情可以参看此链接https://poco.readthedocs.io/zh\_CN/latest/source/poco.drivers.std.inputs.html?highlight=click#poco.drivers.std.inputs.StdInput.click

Poco控件的双击和右键点击

目前Poco控件的双击double_click()和右键点击rclick()方法均未实现,同学们在使用时,会报错提示:NotImplementedError

3. 控件的滑动操作

Poco支持对控件进行滑动操作,我们需要先定位到这个控件,然后指定它按照某个方向滑动即可:

  1. # -*- encoding=utf8 -*-
  2. __author__ = "AirtestProject"
  3. from airtest.core.api import *
  4. auto_setup(__file__)
  5. from poco.drivers.unity3d import UnityPoco
  6. poco = UnityPoco()
  7. # 向下滑动0.2个单位距离
  8. poco("Handle").swipe([0,0.2])
  9. sleep(1.0)
  10. # 向上滑动0.2个单位距离
  11. poco("Handle").swipe([0,-0.2])
  12. sleep(1.0)
  13. # 向下滑动0.1个单位距离
  14. poco("Handle").swipe("down")
  15. sleep(1.0)
  16. # 向上滑动0.1个单位距离
  17. poco("Handle").swipe("up")
  18. sleep(1.0)

swipe

控件滑动的API(swipe)详情,可以参看此链接https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html#poco.proxy.UIObjectProxy.swipe

4. 控件属性的读取和设置

1)控件属性的读取

我们在IDE的poco辅助窗检索出来的控件属性,基本上都可以通过 attr 接口读取出来:

image-20211125111929718

  1. # -*- encoding=utf8 -*-
  2. __author__ = "AirtestProject"
  3. from airtest.core.api import *
  4. auto_setup(__file__)
  5. from poco.drivers.unity3d import UnityPoco
  6. poco = UnityPoco()
  7. print("----------------")
  8. print("name:"+poco("star_single").attr("name"))
  9. print("type:"+poco("star_single").attr("type"))
  10. print("texture:"+poco("star_single").attr("texture"))

image-20211125112348499

attr的详情可以参看此链接https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html#poco.proxy.UIObjectProxy.attr

另外,Poco还支持使用特定的API获取控件的某一属性值:

  • 获取控件的name属性:get_name
  • 获取控件的text属性:get_text
  • 获取控件的position属性:get_position
  • 获取控件的size属性:get_size
  • ……
  1. # -*- encoding=utf8 -*-
  2. __author__ = "AirtestProject"
  3. from airtest.core.api import *
  4. auto_setup(__file__)
  5. from poco.drivers.unity3d import UnityPoco
  6. poco = UnityPoco()
  7. print("----------------")
  8. print("name:"+poco("star_single").get_name())
  9. print("position:"+str(poco("star_single").get_position()))
  10. print("size:"+str(poco("star_single").get_size()))

image-20211125113140844

此类API的详情,我们可以参看此链接的内容:https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html?highlight=get\_#poco.proxy.UIObjectProxy.get\_name

2)设置控件的属性值

通常我们需要设置元素属性的情况,就是设置文本框的文本属性(输入文本),可以使用 set_text() 方法或者 setattr() 方法:

  1. # -*- encoding=utf8 -*-
  2. __author__ = "AirtestProject"
  3. from airtest.core.api import *
  4. auto_setup(__file__)
  5. from poco.drivers.unity3d import UnityPoco
  6. poco = UnityPoco()
  7. # 先激活输入光标
  8. poco("pos_input").click()
  9. # 再执行输入动作
  10. poco("pos_input").set_text("123")
  11. sleep(1.0)
  12. poco("pos_input").setattr('text',"456")

set_text

这两方法的API详情可以参看此链接https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html?highlight=set#poco.proxy.UIObjectProxy.set\_text

但如果我们强行设置不可修改的元素属性,就会报 InvalidOperationException 的错误:

image-20211125141812853

5. 判断控件是否存在

判断控件是否存在,我们可以使用exists()方法,它给我们返回的是布尔值,利用这一点,我们可以做很多应用。

1)控件存在则xx,不存在则yy

最常见的应用就是,如果控件存在,我们就点击它,否则就做别的操作,比如打印一条控件不存在的信息之类的:

  1. if poco("star_single").exists():
  2. poco("star_single").click()
  3. else:
  4. print("未找到星星控件")

区分Poco和Airtest的exists

Poco和Airtest框架都有一个exists方法,但我们需要区分它们俩者的用法,Airtest的exists是用于判断图片存在,exists(图片);而Poco的exists是用于判断控件存在,poco(xxx).exists()

2)断言控件存在

利用控件存在返回的布尔值,我们可以巧妙地结合Airtest的断言相等,来断言控件存在:

  1. assert_equal(poco("star_single").exists(),True,"断言星星控件存在")

exists的方法详情,我们可以参考此链接的内容:https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html?highlight=exists#poco.proxy.UIObjectProxy.exists

6. 控件的拖动

控件的drag_to() 方法,终点可以是一个元素控件,也可以是一个固定的相对坐标:

  1. # 拖动到另一个控件上
  2. poco("playDragAndDrop").child("star")[0].drag_to(poco("shell"))
  3. # 拖动到固定目标上
  4. poco("playDragAndDrop").child("star")[1].drag_to([0.503, 0.705])

drag_to

drag_to的API详情,我们可以参考此链接的内容:https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html#poco.proxy.UIObjectProxy.drag\_to

7. 控件的内外部偏移

在之前的文档里面我们就提到过,poco使用的坐标系是 相对坐标系 。举个例子,我们选中下图的pearl图片控件,此时的坐标系就是相对这个控件的坐标系,并且是从0到1进行归一化的。

对这个控件进行点击操作,实际上点击的坐标是控件上(0.5,0.5)的位置:

  1. poco(texture="icon").click()

图片

1)内部偏移

如果选中控件之后,你并不想点击控件的中心位置,而是想点击控件内部的其它位置,我们可以使用 focus() 方法来指定内部偏移量:

  1. # 内部偏移
  2. pearl = poco(texture="icon")
  3. pearl.focus('center').long_click()
  4. sleep(1.0)
  5. pearl.focus([0.1,0.1]).long_click()
  6. sleep(1.0)
  7. pearl.focus([0.9,0.9]).long_click()

图片

2)外部偏移

选中1个控件以后,如果我们想点击控件之外的位置,也可以使用 focus() 方法来指定外部偏移量;并且会出现 点击坐标的值小于0或者大于1 的情况:

图片

如上图所示,我们选中了 pearl 这个文本控件,但是我们想要去点击文本控件上方的图标,此时 focus 里面的Y坐标就是负数;因为相对于这个文本控件来说,上方图标的Y坐标已经小于0了:

  1. # 外部偏移
  2. pearl_text = poco(text="pearl")
  3. pearl_text.focus([0.5,-3]).long_click()

focus01

同理,如果点击该文本控件正下方比较远的位置,Y坐标就有可能大于1;当Y坐标大到超出当前屏幕时,就会报错: InvalidOperationException('Click position out of screen.

focus() 方法的API详情可以参看此链接https://poco-chinese.readthedocs.io/en/latest/source/poco.proxy.html?highlight=focus#poco.proxy.UIObjectProxy.focus

8. 控件的等待事件

1)仅等待不报错

我们可以使用wait方法,指定时间等待控件出现,再进行点击操作(该方法的返回值是控件本身,所以后面可以紧跟控件操作,比如点击、长按):

  1. # 在10s内等待控件出现,如出现,则进行长按操作
  2. poco(texture="icon").wait(timeout=10).long_click()

wait

wait的API详情,可以参看此链接https://poco.readthedocs.io/zh\_CN/latest/source/poco.proxy.html#poco.proxy.UIObjectProxy.wait

控件的wait方法

需要注意的是,Poco控件的wait方法,即使在设定时间内未找到控件,也是不会报错的,可以继续往下执行下去。另外我们需要区分下Airtest的wait方法,等待图片目标出现wait(图片);和Poco控件的wait方法,等待控件出现poco(xxx).wait(timeout=3)

2)等待,不满足则报错

Poco控件还支持另外2个等待事件,wait_for_appearance()wait_for_disappearance();这两个API可以帮助我们等待页面上 某1个UI 出现或者消失,等待的超时时间 timeout 默认为120秒,如果在超时时长之内元素没有出现或者消失的话,会报 PocoTargetTimeout 的错误。

  1. # 等待黄色小鱼出现
  2. poco("yellow").wait_for_appearance(timeout=20)
  3. # 等待计分文本控件消失
  4. poco(text="Count:").wait_for_disappearance(timeout=3)

wait_for_apperance

两个方法的API详情参考链接https://poco-chinese.readthedocs.io/en/latest/source/poco.proxy.html?highlight=wait\_for\_appearance#poco.proxy.UIObjectProxy.wait\_for\_appearance

3)拓展:Poco类的等待事件

这里我们拓展一个Poco类的等待事件,wait_for_any()wait_for_all()。与上述等待事件不同的是,wait_for_any()wait_for_all() 可以给定多个UI对象让其等待。(需要注意这两个方法是Poco类的方法)

wait_for_all() 是在超时时长结束之前,需要 等待所有给定的UI对象都显示出来 ,即一次轮询所有UI,例如等待三个图标都显示之后,再点击返回按钮:

  1. poco("wait_ui2").click()
  2. yellow = poco("yellow")
  3. blue = poco("blue")
  4. black = poco("black")
  5. poco.wait_for_all([yellow,blue,black])
  6. poco("btn_back").click()

图片

wait_for_any() 则是在超时时长结束之前,等待任意一个UI显示出来,即一次轮询任何一个给定的UI,例如:

  1. bomb = poco("bomb")
  2. yellow = poco("yellow")
  3. blue = poco("blue")
  4. while True:
  5. fish = poco.wait_for_any([bomb,yellow,blue])
  6. print(fish.get_name())

图片

可以看到,只要页面出出现了等待的任一UI,wait_for_any() 方法都会返回第一个等待到的UI。

俩方法的API详情参考链接https://poco-chinese.readthedocs.io/en/latest/source/poco.pocofw.html?highlight=wait\_for#poco.pocofw.Poco.wait\_for\_any

9. 遍历元素

通过python的for循环,我们可以 遍历任何序列的项目 ,如一个列表或者字符串。

举个例子,poco("playDragAndDrop").child("star") 得到的就是1个控件序列(包含了5个星星元素), star 代表控件序列中的1个元素。因此通过这个循环,我们就遍历了5个星星元素的序列,并把每个星星元素依次拖动到贝壳上:

  1. for star in poco("playDragAndDrop").child("star"):
  2. star.drag_to(poco("shell"))

for