如何用Airtest實現在圖片範圍內隨機點擊

1. 前言

前幾天有個新手同學在Airtest官羣裏問了這樣一個問題:
我是新手,在圖片範圍內隨機點擊 ,用Airtest怎麼實現?代碼?
那我們就以這個問題爲例,淺淺聊一下,怎麼把需求轉化成我們的Airtest代碼。

2. 瞭解Airtest

首先新手同學對Airtest要有以下幾點認知:
  • ① Airtest是一個圖像識別框架,它能在屏幕上用圖像識別來找到想操作的位置並點擊,所以Airtest本質上點擊的是目標圖片的座標,默認是中心點
  • ② Airtest是python第三方庫,除了Airtest本身的接口之外,我們隨時可以import python的標準庫或者其它第三方庫,一起使用

3. 分析需求,轉化成代碼

1)查文檔,找現成方法
有了以上2點認知後,我們再回到需求,“在圖片範圍內隨機點擊”,第一步我們要做的就是查詢官方教程文檔,看下有沒有現成的方法可以使用:
  • ① 一般情況下,Airtest都是點擊圖片中心位置,不知道有沒有點擊圖片內其它座標的方法
  • ② 隨機點擊,怎麼實現在圖片內隨機點擊這個行爲,不知道Airtest有沒有隨機點的方法
當我們查閱Airtest的教程文檔之後,發現Airtest確實提供了點擊圖片內非中心座標的方法,就是指定target_pos,但是隻能點擊圖片內的9個點:

 

這裏似乎並不太符合在圖片範圍內隨機點的需求。隨後我們又查詢了文檔,發現Airtest也沒有提供隨機點擊的方法。
2)分析需求,自己實現
那麼我們到這裏就需要進入到第二步,分析需求,看看需求都有哪些難點需要我們實現:
  • ① 在圖片範圍內點擊,那意味着在整個圖片的座標範圍內點擊,那我們怎麼拿到整個圖片的座標範圍?
  • ② 怎麼點擊一個隨機的座標?
針對第一個難點,其實我們分別拿到目標圖片x、y座標的最小、最大值,就能確認座標範圍了。再進一步思考,一張圖片x最小值是圖片的最左側,最大值是圖片最右側;y最小值是圖片最上方,最大值是圖片最下方;那就意味着,我們只要拿到圖片左上角和右下角的座標,就能完全確認目標圖片x、y座標的最小、最大值,進而確認圖片座標範圍。第一個難點解決後,再來看第二個難點,隨機點,這裏就要藉助python的標準庫random了,能幫助我們生成一個隨機數的函數。
3)將思考轉化成代碼
最後一步就是將我們剛纔分析需求,得到的思路,轉化成代碼:

圖片

# -*- encoding=utf8 -*-
__author__ = "AirtestProject"

from airtest.core.api import *
import random

auto_setup(__file__)

# 定義函數,得到目標圖片的座標範圍
def pos_range():
if exists(Template(r"tpl1697515204054.png", target_pos=1, record_pos=(-0.16, 0.154), resolution=(2960, 1440))):
up_pos = exists(Template(r"tpl1697515204054.png", target_pos=1, record_pos=(-0.16, 0.154), resolution=(2960, 1440))) # 利用target_pos=0,拿到目標圖片左上角的座標
down_pos = exists(Template(r"tpl1697515204054.png", target_pos=9, record_pos=(-0.16, 0.154), resolution=(2960, 1440))) # 利用target_pos=9,拿到目標圖片右下角的座標
x = (int(up_pos[0]),int(down_pos[0])) # 拿到圖片x座標的範圍
y = (int(up_pos[1]),int(down_pos[1])) # 拿到圖片y座標的範圍
return x,y
else:
print("目標圖片不存在")

# 分別拿到圖片x座標與y座標的範圍值
pos_r = pos_range()
x = pos_r[0]
y = pos_r[1]

# 在x、y的座標範圍內生成隨機數
x_range = random.randint(x[0],x[1])
y_range = random.randint(y[0],y[1])

# 點擊圖片範圍內任意座標
touch([x_range,y_range])

4. 優化版

      以上是簡單的一個實現,效率不是最優。下面介紹一個更優的實現方案:      Moty同學是通過在本地python環境安裝Airtest庫去進行Airtest自動化腳本編寫的,同時該腳本在AirtestIDE上是可以完美適配運行的。      我們先來看看Moty同學代碼的運行情況,可以看到通過生成隨機座標的形式,點擊圖片上的任意點,這裏用計算器去具象化,可以看到每次隨機座標可以點擊不同數字或者數字的不同位置,每次運行都可以獲得隨機的數字序列。图片看起來很厲害的樣子,我們應該怎麼去實現呢?讓我們來看看Moty同學的代碼吧~
# -*- encoding=utf8 -*-
__author__ = "Moty"

from airtest.core.api import *
from airtest.cli.parser import cli_setup
from airtest.core.error import *
from airtest.core.settings import Settings as ST
import random

"""
獲取模板匹配的目標區域的矩形 這一部分實現參考 cv.py 中 loop_find 部分
: param : tpl 模板
: param : intervalfunc 沒有合適匹配時的回調函數
: return 最佳匹配的矩形區域(x1,y1,x2,y2)
"""

def rectangle(tpl,intervalfunc=None):  
    G.LOGGING.info("Try finding: %s", tpl)
    start_time = time.time()
    while True:
        screen = G.DEVICE.snapshot(filename=None, quality=ST.SNAPSHOT_QUALITY)
        if screen is None:
            # 如果截圖爲空,則可能是屏幕鎖定了
            G.LOGGING.warning("Screen is None, may be locked")
        else:
            match_result = tpl._cv_match(screen)
            if match_result:
                try_log_screen(screen)
                # 這裏 rect 得到的是 4個座標點 取出左上右下角 得到(x1,y1,x2,y2) 元組
                rect = match_result.get("rectangle")
                if rect is not None:
                    return (round(rect[0][0]) , round(rect[0][1]) , round(rect[2][0]) , round(rect[2][1]))

        if intervalfunc is not None:
            intervalfunc()

        # 超時則raise,未超時則進行下次循環:
        if (time.time() - start_time) > ST.FIND_TIMEOUT:
            try_log_screen(screen)
            # 如果超時,則拋出異常
            raise TargetNotFoundError('Picture %s not found in screen' % tpl)
        else:
            time.sleep(0.5)

"""
param : rect : 矩形區域 或模板
return : 區域內的隨機座標 
"""

# 獲取矩形區域內的隨機座標
def random_point(rect):
    # 如果傳入的是圖片,則獲取圖片匹配的矩形區域
    if isinstance(rect, Template):
        x1, y1, x2, y2 = rectangle(rect)
    else:
        x1, y1, x2, y2 = rect
    # 在矩形區域內隨機生成一個座標點
    x = random.randint(x1, x2)
    y = random.randint(y1, y2)
    return x, y

"""
在 矩形範圍內 隨機點擊 
param : v : 目標區域 or 模板 or 座標點(兼容touch)
param : times : 點擊次數
return :最終點擊的點的座標
"""
@logwrap
def random_touch_in_area(v, times=1, **kwargs):
    if isinstance(v, Template) or (isinstance(v, tuple) and len(v) == 4):
        pos = random_point(v)
    else:
        try_log_screen()
        pos = v
# 在目標區域內隨機點擊
    for _ in range(times): 
        G.DEVICE.touch(pos, **kwargs) 
        time.sleep(0.05)  
    delay_after_operation()  
    return pos


if __name__ == '__main__':

    # 如果沒有通過命令行連接設備,則使用該連接命令,若使用IDE運行則可忽略這段代碼,不用寫上
    if not cli_setup():
        auto_setup(__file__, logdir=None, 
                devices=["android:///",])

    # 錄製圖片
    tpl = Template(r"tpl1697636105500.png", record_pos=(0.243, -0.165), resolution=(1080, 2280))

    # 獲取模板匹配的目標區域的矩形
    result = rectangle(tpl)
    print(f"圖片所在矩形區域 {result}")

    # 在目標區域內隨機點擊
    for i in range(10):
        p = random_touch_in_area(tpl)
        print(f"第 {i+1:02d} 次點擊座標 {p}")
        sleep(1)
可以看出來,在編寫代碼的時候,Moty同學將每一個步驟進行函數封裝後,可以減少二次查找的次數以及代碼冗餘,Moty同學的編寫思路很值得我們學習~

5.小結

最後,希望同學們在學習以及使用Airtest的時候,可以先學習方法,掌握代碼編寫技巧後,對自己的腳本可以進行優化再優化,從而實現代碼收益最大化。

---------------------------------------------------------------------------------

關注微信公衆號即可在手機上查閱,並可接收更多測試分享~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章