美文网首页
python 库实战 - 安卓简易自动化框架

python 库实战 - 安卓简易自动化框架

作者: 许忠慧 | 来源:发表于2020-09-05 10:59 被阅读0次

主要功能:
0、安装、启动、卸载app
1、模拟点击(基于坐标)
2、模拟滑动(基于坐标)
3、文本输入(支持中文输入)
4、屏幕截图
3、文本识别(用于断言)
4、图片识别(用于断言)

废话不多说,代码比较清楚

#-- coding: utf-8 --
import sys,os
from PIL import Image
import pytesseract
import time
import cv2 as cv
import numpy as np


"""
使用前提:
1、 本地需要有adb,并且配置了环境变量
2、 检查设备连接状态的时候只检查了是否有模拟器,并且只能一个设备,如果有其他需求可以自行修改 check_devices_state 函数即可
3、 使用了图片文字识别工具(安装好后需要配置环境变量):http://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-setup-4.00.00dev.exe 下载好安装的时候需要勾选下载所有语言库。单独中文语言库安装路径:https://github.com/tesseract-ocr/tessdata/find/master/chi_sim.traineddata
4、 使用到了第三方库:PIL(这是一个图片处理库) , 使用 pip install Pillow 命令进行安装
5、 需要使用三方库:pytesseract(这是一个图片文字识别库), 使用 pip install pytesseract 命令进行安装(如果已经配置过tesseract的环境变量则不需要额外配置)
6、 需要使用第三方库:numpy和cv2 用于做图片图片匹配使用,分别使用领命 pip install opencv-python 和 pip install numpy 进行安装
"""
#-- coding: utf-8 --
import sys,os
from PIL import Image
import pytesseract
import time
import cv2 as cv
import numpy as np
sys.path.append("E:\\myPython\\autoTest")
from printColor import winColor # 导入自己的方法库

# 安卓基础类
class Android_auto_test:

    

    # 检查设备连接状态的函数,只适用于模拟器
    def check_devices_state(self):
        flag = 0
        while flag < 3:
            command_result = str(os.popen("adb devices").readlines())
            if "127.0.0.1" in command_result or "emulator-5554" in command_result:
                return True
            else:
                    print("未检测到设备,重启adb服务... ...")
                    os.system("adb kill-server")
                    os.system("adb start-server")
            flag = flag+1
        winColor().print_red_text("Error:未找到模拟器设备,请确认模拟器设备是否存在")
        exit(0)

    # 安装apk,传入apk路径
    def install_apk(self, apkPath):
        self.check_devices_state()
        try:
            os.system("adb install -r {}".format(apkPath))
        except:
            winColor().print_red_text("Error: install faild")
            exit(0)

    # 卸载apk,传入包名进行卸载
    def uninstall_apk(self, packageName):
        self.check_devices_state()
        os.system("adb uninstall {}".format(packageName))

    # 获取apk包名。需要使用aapt命令,请确保本地有该命令的运行环境先
    def get_package_name(self, apkPath):
        command_result_name = os.popen("aapt dump badging {} | findstr name".format(apkPath)).read()
        packageName = command_result_name.split()[1].split("'")[1]
        command_result_activty = os.popen("aapt dump badging {} | findstr activity".format(apkPath)).read()
        launchActivityName = command_result_activty.split()[1].split("'")[1]
        return packageName, launchActivityName

    # 启动游戏
    def launch_game(self, packageName, launchActivity):
        self.check_devices_state()
        try:
            os.system(r"adb shell am start {}/{}".format(packageName, launchActivity))
        except:
            winColor().print_red_text("Error: 启动失败, 需要用 包名/包名.activity名 来启动游戏")

    X, Y = 0, 0
    def __init__(self):
        self.check_devices_state()
        X, Y = os.popen("adb shell wm size").read().strip().split(":")[-1].strip().split("x")   # 获取模拟器尺寸
        

    # 截图,返回截图存放路径。可自定义截图尺寸,需要传入左上角x,y和右下角x,y
    def screencap(self, startX = 0, startY = 0, endX = X, endY = Y):
        self.check_devices_state()
        os.system("adb shell screencap /data/local/tmp/test.png")
        os.system("adb pull /data/local/tmp/test.png tmp.png")
        
        if endX != self.X:
            img = Image.open("tmp.png")
            cropped = img.crop((startX, startY, endX, endY))  # (left, upper, right, lower)
            cropped.save("tmp.png")

        pypath = os.path.realpath( os.path.dirname(__file__) )
        return os.path.join(pypath, "tmp.png")

    # 识别图片中的文字。对图片进行灰度、二化值处理
    def get_picture_str(self, picturePath, colorValue = None):

        # 实际多次测试后发现,对于我们游戏内字体,无需进行二化值处理即可识别,但是需要对要识别的图片进行一下剪裁来提高识别效率
        # 图片灰度处理
        picture = Image.open(picturePath).convert('L')  
        picture.save(picturePath, dpi=(300.0,300.0))
        ## 二值化,采用阈值分割法,threshold为分割点
        if colorValue != None:
            winColor().print_green_text("Message: 使用二值化处理, 分割点:{}".format(colorValue))
            threshold = colorValue
            table = []
            for j in range(256):
                if j < threshold:
                    table.append(0)
                else:
                    table.append(1)
            newPicture = picture.point(table, '1')

            ## 保存的时候调整屏幕分辨率为300,有利于 tesseract 识别
            newPicture.save(picturePath, dpi=(300.0,300.0))         
        # 识别
        text=pytesseract.image_to_string(Image.open(picturePath), lang='chi_sim')   
        return text

    # 文字是否在屏幕中 - 检查5次。
    def check_str_exist(self, checkStr, startX = 0, startY = 0, endX = X, endY = Y, times = 5):
        flag = 0
        while flag < times:
            if checkStr in self.get_picture_str(self.screencap(startX, startY, endX, endY), colorValue = None):
                return True
            flag = flag + 1
            time.sleep(1)
        return False
            

    # 点击坐标, 使用的模拟器屏幕分辨率为 1280x720
    def click(self, x, y):
        self.check_devices_state()
        os.system("adb shell input tap {0} {1}".format(x,y))
        time.sleep(0.5)

    # 从一个坐标滑动到另一个坐标,t表示滑动耗时
    def swipe(self, oldx, oldy, newx, newy, t):
        self.check_devices_state()
        cmd = "adb shell input swipe {0} {1} {2} {3} {4}".format(oldx, oldy, newx, newy, t)
        os.system(cmd)
        time.sleep(1)


    # 输入文本
    def set_text(self, s):
        self.check_devices_state()
        #如果要输入的文本中包含空格,则必须使用keyevent 62来代替,以下的写法最后会多输入一个空格;不过对我自己没什么影响所以就没处理了
        if len(s.split()) == 1:
            os.system("adb shell input text '{0}'".format(s) )
        else:
            for i in s.split():
                os.system("adb shell input text '{0}'".format(i))
                os.system("adb shell input keyevent 62")
        time.sleep(1)
        
    # 输入含有中文的文本( 需要在模拟器上预先安装 ADBKeyBoard.apk)
    def set_text_utf8(self, s):
        self.check_devices_state()
        cmd = "adb shell am broadcast -a ADB_INPUT_TEXT --es msg '%s'" %s
        os.system(cmd)

    # 图片识别,根据传入的图片,获取该图片在屏幕中的位置, 传入图片和相似度(取0-1,最好使用0.99), 还可以传入需要获取的point位置,上中下
    def get_flagPicture_point(self, flagPicture, precision = 0.99, locationPoint = "center"):
        flag1 = 0
        while flag1< 5:
            screencapPicture = self.screencap()
            flag = cv.imread(flagPicture)
            screen = cv.imread(screencapPicture)
            result = cv.matchTemplate(screen, flag, cv.TM_CCORR_NORMED) #模板匹配,使用cv.TM_SQDIFF_NORMED方法
            y, x = flag.shape[:2]   #返回图片的高和宽
            min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
            if max_val >= precision:    #这里做一个判断,经过大量测试,能匹配到的图片阈值大于0.99的时候,才是真的匹配到了
                tl = max_loc
                if locationPoint == "center":
                    point = (tl[0] + x/2, tl[1] + y/2)
                if locationPoint == "bottom":   # 1160 439  1160 444
                    point = (tl[0] + x/2, tl[1] + y)
                if locationPoint == "top":
                    point = (tl[0] + x/2, tl[1])
                return point            
            else:
                flag1 = flag1 + 1
                time.sleep(2)
        return False
            
            

    """
    # 模板匹配demo
    # def find_picture(picture, flag):
        # methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED]  #三种比对方式
        # x,y = flag.shape[:2]  #返回图片的长和高
        # for md in methods:
            # result = cv.matchTemplate(picture, flag, md)  #模板匹配
            # cv.TM_SQDIFF_NORMED: minval越小匹配效果越好
            # cv.TM_CCORR_NORMED: max_val越接近1效果越好
            # cv.TM_CCOEFF_NORMED: max_val越接近1效果越好
            # min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result) 
            # if md == cv.TM_SQDIFF_NORMED:
                # tl = min_loc
            # else:
                # tl = max_loc
            # br = (tl[0]+y, tl[1] + x)
            # cv.rectangle(picture, tl, br, [0,0,0])#在picture上画一个矩形,参数:原图,矩阵坐上坐标,矩阵右下坐标,划线的颜色,划线的宽度
            # cv.imshow(np.str(md), picture)    #展示图片(图片名,原图)
    # picture = cv.imread("C:\\Users\\xuzhonghui\\Desktop\\MyTools\\find\\flag1.png")
    # flag = cv.imread("C:\\Users\\xuzhonghui\\Desktop\\MyTools\\find\\picture1.png")
    # find_picture(picture, flag)
    # cv.waitKey()
    # cv.destoryAllWindows()
    """

    # 断言成功则点击对应位置,否则报错停止脚本运行,为了减少代码量,专门写的一个函数
    def click_str(self, checkStr, startX = 0, startY = 0, endX = X, endY = Y, clickX = 0, clickY = 0, ErrorCode = 0):
        if self.check_str_exist(checkStr, startX, startY, endX, endY) == True:
            self.click(clickX, clickY)
        else:
            winColor().print_red_text("stepError: errorCode = {}".format(ErrorCode))
            exit(0)

相关文章

网友评论

      本文标题:python 库实战 - 安卓简易自动化框架

      本文链接:https://www.haomeiwen.com/subject/hpwxektx.html