最近公司项目需要,要在三百多台终端上测一个windows软件的兼容性,所以想做个UI自动化,于是看到了pyautogui这个包,从名字就能看出,他是做gui界面相关的包,
pyautogui最方便的地方就是,你不需要去用代码定位元素,你只需要用截图就行,例如你要识别桌面上某个应用,你只需要截图这个应用,然后通过这个截图和当前桌面对比即可找到图标的位置,说到这里,大家应该就会想到,那我是不是把代码封装好之后,后面不管产品怎么迭代,我只要更换截图就可以了?是的!这就是我在这里写篇博客的目的,我看了一下,基本没有一个完整的源码教程,所以我想给大家分享一个完完整整的教程,因为他是真的好用,代码简单,后期维护成本低。
首先是基本用法,这个百度一下一大堆,这里是pypi的文档,自行阅读:https://pypi.org/project/PyAutoGUI/
简单的贴两行官方文档中代码:
import pyautogui
pyautogui.moveTo(100, 150) # Move the mouse to the x, y coordinates 100, 150.
pyautogui.click()
上面就是一个把鼠标移到(100,150)这个坐标的代码,并且左键鼠标单击,当然有鼠标单击就有双击,各种操作查看上面文档即可,那我们如何定位我们想要的内容在哪呢?请继续往下看:
假设我们想要定位“计算机”的坐标然后去点击他,可以通过如下代码:
#这是你的目标图标,也就是你的截图,下面找他的时候会用到
appname = ROOT_DIR + '/testimage/' + target + '.png'
##根据桌面路径进行截屏
##获取当前桌面路径
threshold = 0.85
path = os.path.join(os.path.expanduser('~'), "Desktop")
hwnd = win32gui.FindWindow(None, path)
app = QApplication(sys.argv)
screen = QApplication.primaryScreen()
img = screen.grabWindow(hwnd).toImage()
# img = ImageGrab.grab()
##保存截屏
root_desk = ROOT_DIR + '/desktop.png'
img.save(root_desk)
##等待图片保存
time.sleep(0.5)
# 支持图片名为中英文名,也支持路径中英文
imsrc = cv2.imdecode(np.fromfile(root_desk, dtype=np.uint8), -1)
imobj = cv2.imdecode(np.fromfile(appname, dtype=np.uint8), -1)
# 匹配图标位置
pos = ac.find_template(imsrc, imobj, threshold)
pos就是当前匹配到的路径
这是基本用法,其他方法可以在我后面的源码中查看
下面说坑:
1.python和第三方包版本
之前使用的是python2,于是去安装对应的py包,结果一顿骚造作之后,发现总是有问题,鼠标老是移动不到目标坐标位置,于是换了py3.6版本和对应的依赖包,搞定
2.windows用户权限弹框
你没看错,就是这个玩意,能匹配到,但是不能点击,最后直接把这个弹框在windows系统上禁用了,如果你有好的办法,可以联系我,一起共享
3.突然操作失效,例如定位坐标出错,点击不了鼠标
这个解决办法有点呵呵,重启电脑,然后再运行代码,嗯,没问题了,wtf
说完坑,然后说如何做到前面说的只需要维护截图就能更新迭代。
1、定义好你的操作和对应的函数。
在py文件里面添加如下代码:
ACTIONCN2EN = {"匹配": "match", "输入内容": "input_type", "复制入内容": "input_copy", "左键单击": "click_once",
"左键双击": "click_twice", "Enter键单击": "click_enter_once", "等待出现": "wait", "全选": "select_all",
'点击按键': 'type_write', '检测结果': 'check_result', '鼠标滚动': 'scroll'}
@time_sleep
def click_once():
pyautogui.click()
上面我就举一个鼠标左键点击一次的例子,节约篇幅
然后定义动作,这里新建一个txt文件:
里面输入如下:
定义好之后,可以用python打开这个txt文件,然后通过eval(ACTIONCN2EN[act])(params)
去执行函数即可
如果想要其他的步骤,直接在后面添加即可,例如我以->
作为分隔符,定义了如下测试流程:
通过如下代码即可解析步骤:
with open(file, encoding='utf-8') as f:
action_str = f.read()
# 保证'动作.txt'里,动作最后的换行符无影响
action_str = action_str.strip('\n')
# 保证动作之间的单个换行符跟->等效。若有多个换行符,放到if act=''里
action_str = action_str.replace('\n', '->')
# print(action_str)
action_list = action_str.split('->')
for act in action_list:
if act == '': # 当同时有多个换行符时,act就是空,在这里排除
pass
else:
try: # 防止同一Dj99ZzZz个action里,有一个动作异常就不执行其他动作
act.encode('utf-8')
##提取action具体动作的括号里的内容,如match(QQ-单发输入框)
p1 = re.compile(r'[(](.*?)[)]', re.S)
params = re.findall(p1, act) # 查找后返回list,都放在[0]里,()内无东西,即无参数,返回空列表
act = act.split('(')[0]
if params == []:
print(u"调用方法为:{}".format(act))
eval(ACTIONCN2EN[act])()
else:
params = params[0]
if params == 'arg1':
try:
params = sys.argv[1]
except IndexError as ex:
raise Exception('请传第1个参数')
if params == 'arg2':
try:
params = sys.argv[2]
except IndexError as ex:
raise Exception('请传第2个参数')
print(u"调用方法为:{}({})".format(act, params))
eval(ACTIONCN2EN[act])(params) # 参数可以有多个,参数之间用,分隔。目前只支持一个参数
except Exception as e:
print(e)
break
然后根据动作,截图好每个图标就可以,下次产品迭代或者新增功能,直接更新action.txt文件的步骤和截图就行了
github代码地址:https://github.com/ruhaoyu/uitest