有一个多月没发关于GUI的博客了,本次使用Tkinter制作一款安卓自动测试GUI,能够帮我们自动测试指定目录下的所有.apk文件,生成测试报告。
一.准备工作
-
Python
本次主要用到Tkinter、os、re、subprocess、webbrowser、threading、xlwt等依赖库,有些需要自己手动安装一下。
-
模拟器
安卓自动测试可以使用真机或者模拟器,我这里使用的是夜神模拟器海外版配合夜神模拟器去广告补丁可以达到去广告,推荐大家使用! -
依赖
由于本自动测试工具是基于adb命令的,测试报告生成依赖aapt,所以
要先将图中四个文件准备好,这四个文件在模拟器根目录下,复制出来即可。
二.预览
1.启动
设置了测试APP所在目录、所用设备、测试时常等配置项之后,软件就开始自动测试了。
2.开始测试
点击开始测试后,软件就会自动操作模拟器/真机,进行自动测试。
3.测试完成
测试完成会产生测试报告
三.源码
#-*-coding:utf-8-*-
import re
import time
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from tkinter.filedialog import *
import os
import webbrowser
import threading
import pyperclip
from auto_operate_util import My_Util
from adb_install_apks_main import Auto_Process_Apks
from process_result import Process_result
from GUI_tools import ToolTip
class App:
def __init__(self):
self.u=My_Util()
self.a_=Auto_Process_Apks()
self.set_window_start_flag=True
#程序运行标志
self.FLAG=True
#程序启动标志
self.START_FLAG=True
#生成报告
self.s_check1_state=True
#自动关闭
self.s_check2_state=True
#动作事件存储列表
self.u.make_dir('./record')
self.u.make_dir('./logs')
self.u.make_dir('./report')
self.time_string = self.u.get_time_string().replace(' ', '_').replace(':', '_')
self.exception_file = f'./logs/log_{
self.time_string}.txt'
self.record_file = './record/record_{}.txt'.format( self.time_string)
self.report_path = './report'
self.root=Tk()
self.create_window()
self.create_widgets()
self.set_widgets()
self.place_widgets()
self.root.mainloop()
def create_window(self):
self.root.title('安卓自动化测试工具-v1.0')
self.root.resizable(0,0)
width=830
height=600
left=(self.root.winfo_screenwidth()-width)/2
top=(self.root.winfo_screenheight()-height)/2
self.root.geometry('%dx%d+%d+%d'%(width,height,left,top))
def create_widgets(self):
self.btn1=ttk.Button(self.root,text='开始测试 (F10)')
self.btn2=ttk.Button(self.root,text='停止测试 (F12)')
self.btn3=ttk.Button(self.root,text='打开测试报告目录')
self.btn4=ttk.Button(self.root,text='打开测试记录目录')
self.btn6=ttk.Button(self.root,text='打开异常日志目录')
self.treeview=ttk.Treeview(self.root)
self.scrollbar_v=Scrollbar(self.root,)
self.status_label=ttk.Label(self.root,text='这里显示事件')
self.m=Menu()
self.root['menu']=self.m
self.m2=Menu(self.treeview,tearoff=False)
self.l1_var=StringVar()
self.l1=ttk.Label(self.root,textvariable=self.l1_var)
self.progressbar=ttk.Progressbar(self.root)
self.l2_var=StringVar()
self.l2=ttk.Label(self.root,textvariable=self.l2_var,text='progress')
def set_widgets(self):
self.btn1.config(command=lambda :self.thread_it(self.do_start_test))
self.btn2.config(state=DISABLED,command=lambda :self.thread_it(self.do_stop_test))
self.btn3.config(command=lambda :os.startfile(os.path.abspath('./report')))
self.btn4.config(command=lambda :os.startfile(os.path.abspath('./record')))
self.btn6.config(command=lambda :os.startfile(os.path.abspath('./logs')))
self.scrollbar_v.config(orient=VERTICAL,command=self.treeview.yview)
self.treeview['yscrollcommand']=self.scrollbar_v.set
columns=('no','event','time')
self.treeview.config(show='headings',columns=columns,selectmode=BROWSE,displaycolumns ='#all')
self.treeview.column("no", anchor="center",minwidth=40,width=80, stretch=NO)
self.treeview.column("event", anchor="center",minwidth=400,width=550, stretch=NO)
self.treeview.column("time", anchor="center",minwidth=40,width=152, stretch=NO)
self.treeview.heading("no", text="编号")
self.treeview.heading("event", text="事件")
self.treeview.heading("time", text="时间")
self.status_var=StringVar()
self.status_label.config(textvariable=self.status_var)
self.s1=Menu(self.m,tearoff=False)
self.s2=Menu(self.m,tearoff=False)
self.s3=Menu(self.m,tearoff=False)
self.s4=Menu(self.m,tearoff=False)
self.s5=Menu(self.m,tearoff=False)
self.m.add_cascade(label='开始',menu=self.s1)
self.m.add_cascade(label='操作',menu=self.s2)
self.m.add_cascade(label='查看',menu=self.s3)
self.m.add_cascade(label='设置',menu=self.s4)
self.m.add_cascade(label='关于',menu=self.s5)
self.s3.add_command(label='查看本机IP',command=self.show_local_ip)
self.s3.add_command(label='打开测试报告目录',command=lambda :os.startfile(os.path.abspath('./report')))
self.s3.add_command(label='打开测试记录目录',command=lambda :os.startfile(os.path.abspath('./record')))
self.s3.add_command(label='打开异常日志目录',command=lambda :os.startfile(os.path.abspath('./logs')))
self.s1.add_command(label='打开夜神模拟器官网',command=lambda :webbrowser.open('https://www.yeshen.com/'))
self.s1.add_command(label='下载夜神模拟器海外版(无广告)',command=lambda :webbrowser.open('https://down.126g.com/soft/nox.zip'))
self.s1.add_command(label='下载夜神模拟器去广告补丁',command=lambda :webbrowser.open('https://www.cr173.com/soft/1162619.html'))
self.s1.add_separator()
self.s1.add_command(label='退出',command=self.quit_window)
self.s4.add_command(label='首选设置',command=self.open_set_window)
self.s4.add_separator()
self.progressbar.config(maximum=100,value=0)
self.l1_var.set('当前未运行')
self.l2_var.set('0.00%')
self.top_most_var=IntVar()
#这里只能使用checkbutton用于开关
self.s4.add_checkbutton(label='窗口置顶',variable=self.top_most_var,command=self.do_top_most)
self.s2.add_command(label='开始测试 (F10)',command=lambda :self.thread_it(self.do_start_test))
self.s2.add_separator()
self.s2.add_command(label='停止测试 (F12)',command=lambda :self.thread_it(self.do_stop_test))
self.s2.entryconfig('停止测试 (F12)', state=DISABLED)
self.s4_radio=Menu(self.s4,tearoff=False)
self.s4.add_cascade(label='“事件”列位置',menu=self.s4_radio)
self.event_location={
'左':'w','中':'center','右':'e'}
self.s4_radio_var=IntVar()
self.s4_radio_var.set(1)
for i,key in enumerate(self.event_location.keys()):
self.s4_radio.add_radiobutton(label=key,variable=self.s4_radio_var,command=self.event_loaction_selected,value=i)
themes=['default','clam', 'alt', 'classic','winnative']
s4_sub = Menu(self.s4, tearoff=0)
self.s4.add_cascade(label='更换主题',menu=s4_sub)
self.themevar=StringVar()
for i,t in enumerate(themes):
s4_sub.add_radiobutton(label=t,variable=self.themevar,command=lambda:self.thread_it(self.change_theme),value=t)
self.m2.add_command(label='复制事件',command=self.copy_event)
self.s5.add_command(label='关于作者',command=lambda :messagebox.showinfo('关于作者','作者:懷淰メ'))
self.s5.add_command(label='使用说明',command=lambda :messagebox.showinfo('使用说明','1.首先启动GUI、模拟器/真机,将四个依赖放入GUI根目录\n2.解锁模拟器到桌面\n3.设置GUI中APK目录、所用设备以及测试时间\n4.开始测试,所有APK测试结束后程序会自动关闭模拟器,自动生成报告##'))
self.root.protocol('WM_DELETE_WINDOW',self.quit_window)
self.root.bind('<F10>', self.do_start_test)
self.root.bind('<Escape>', self.escape)
self.treeview.bind('<Button-3>',self.do_copy_event)
def place_widgets(self):
self.btn1.place(x=20,y=20)
self.btn2.place(x=150,y=20)
self.btn3.place(x=320,y=20)
self.btn4.place(x=485,y=20)
self.btn6.place(x=675,y=20)
self.treeview.place(x=20,y=55,width=800,height=500)
self.scrollbar_v.place(x=803,y=55,height=500)
self.l1.place(x=20,y=565)
self.progressbar.place(x=93,y=565,width=680)
self.l2.place(x=385,y=566,height=18)
def show_local_ip(self):
local_ip=self.u.get_local_ip()
pyperclip.copy(local_ip)
messagebox.showinfo('提示', f'本机IP为:{
local_ip}\n已成功复制到剪切板!')
def pre_start_check(self):
try:
if not isinstance(self.apk_path,str):
pass
else:
four_rely_file_list=self.a_.check_four_file()
#检查本地四个依赖是否存在
if len(four_rely_file_list)==0:
devices_=self.a_.get_devices()
#检查是否有可用设备
if len(devices_)!=0:
self.crawl_packet_time = self.s_spin1_var.get()
self.a_.set_sleep_time(self.crawl_packet_time)
self.a_.set_device(self.s_combobox_var.get())
return True
else:
messagebox.showerror('错误', '未检测到可用设备,无法启动!')
self.insert_to_treeview(f'未检测到可用设备,无法启动!')
else:
lack_relys='\t'.join(four_rely_file_list)
messagebox.showerror('错误',f'软件根目录缺少{
lack_relys}依赖文件,无法启动!')
self.insert_to_treeview(f'软件根目录缺少{
lack_relys}依赖文件,无法启动!')
except AttributeError:
messagebox.showwarning('警告', '请设置APK所在目录!')
self.insert_to_treeview('请设置APK所在目录!')
self.open_set_window()
def do_start_test(self,*args):
if self.START_FLAG:
if self.pre_start_check():
self.p = Process_result( self.time_string,self.report_path)
self.insert_to_treeview('检查通过,开始测试.......')
self.test_start_time=time.time()
apks = self.a_.get_apks(self.apk_path)
self.apk_nums=len(apks)
self.insert_to_treeview(f'读取到{
self.apk_nums}个安装包.......')
self.START_FLAG=False
self.s2.entryconfig('开始测试 (F10)', state=DISABLED)
self.btn1.config(state=DISABLED)
self.s2.entryconfig('停止测试 (F12)', state=NORMAL)
self.btn2.config(state=NORMAL)
self.root.unbind('<F10>')
self.root.bind('<F12>', self.do_stop_test)
self.l1_var.set('测试进度:')
for i, apk_abs_path in enumerate(apks):
if self.FLAG:
apk_name = apk_abs_path.split('\\')[-1]
#每个APP安装之前都先回到桌面
self.a_.do_back_home()
self.insert_to_treeview(f'尝试安装[{
apk_name}].......')
if self.a_.do_install(apk_abs_path) is True:
self.insert_to_treeview(f'[{
apk_name}]安装成功!')
pkg_name = self.a_.get_pkgName(apk_abs_path)
# 安装成功并且获取到了包名
if pkg_name:
# 抓取流量
self.insert_to_treeview(f'[{
apk_name}]已经打开.......')
self.a_.do_open_app(pkg_name)
self.insert_to_treeview(f'[{
apk_name}]测试完成.......')
# 是否还在桌面
if self.a_.judge_desk_status() == False:
# 是否卸载成功
if self.a_.uninstall_app(pkg_name) is True:
self.insert_to_treeview(f'[{
apk_name}]成功卸载.......')
self.insert_to_treeview(f'[{
apk_name}]正常测试完成.......')
else:
self.u.save_exception(self.exception_file, '[未能成功卸载]', apk_name, apk_abs_path)
self.insert_to_treeview(f'[{
apk_name}]未能成功卸载')
else:
self.insert_to_treeview('模拟器现在位于桌面,没有产生流量.......')
else:
self.u.save_exception(self.exception_file, '[安装包已损坏]', apk_name, apk_abs_path)
self.insert_to_treeview(f'获取[{
apk_name}]包名失败,已跳过此应用!')
else:
self.u.save_exception(self.exception_file, '[无法安装]', apk_name, apk_abs_path)
self.insert_to_treeview(f'[{
apk_name}]未能成功安装')
self.apk_left_nums=self.apk_nums-i-1
#更新进度条
progress='%.2f'%((1-(self.apk_left_nums/self.apk_nums))*100)
self.progressbar['value']=progress
self.l2_var.set(progress+'%')
self.insert_to_treeview(f'第[{
i+1}]个APP测试完成,剩余[{
self.apk_left_nums}]个APP未测试.......')
# 无论是否安装都写一行150**
record = [str(i + 1), apk_name, apk_abs_path, self.u.get_time_string()]
self.u.do_record(self.record_file, record)
if self.apk_left_nums==0:
self.insert_to_treeview('--------------------------------------所有APP已经测试完成--------------------------------------')
if self.s_check2_state==True:
self.a_.kill_program('Nox.exe')
self.a_.kill_program('NoxVMHandle.exe')
self.insert_to_treeview('--------------------------------------模拟器已关闭--------------------------------------')
self.test_end_time=time.time()
self.yield_report_start_time=time.time()
self.yield_report(self.record_file)
self.yield_report_end_time = time.time()
self.insert_to_treeview(
f'--------------------------------------本次测试结束,一共消耗了:{
self.u.conut_cost_time(self.test_start_time,self.test_end_time)}--------------------------------------')
self.insert_to_treeview(
f'--------------------------------------报告生成结束,一共消耗了:{
self.u.conut_cost_time(self.yield_report_start_time,self.yield_report_end_time)}--------------------------------------')
self.set_can_start_flag()
else:
messagebox.showwarning('警告','程序已经启动,请勿重新启动!')
def yield_report(self,recod_file):
recod_content = self.p.read_recod(recod_file)
record_nums=len(recod_content)-1
self.insert_to_treeview('--------------------------------------开始生成TXT报告--------------------------------------')
headers=['应用名','版本','filesha256','包名','文件路径','文件名','time_string']
self.insert_to_treeview('\t'.join(headers))
self.p.do_yield_report(headers)
data_list=[]
self.l1_var.set('报告生成进度')
self.progressbar.place(x=100, y=565, width=680)
num=0
for record_ in recod_content:
apk_name=record_.split('\t')[1].strip()
apk_path=record_.split('\t')[2].strip()
apk_dir=apk_path.replace(apk_name,'').strip('\\')
time_str=record_.split('\t')[-1].strip()
apk_sha256=self.p.get_sha256(apk_path)
app_name,app_pkg_name,app_version=self.p.get2_infos(apk_path)
data_ = [app_name, app_version, apk_sha256, app_pkg_name, apk_dir,
apk_name, time_str]
self.insert_to_treeview("\t".join(data_))
self.p.do_yield_report(data_)
data_list.append(data_)
num+=1
progress = '%.2f' % ((num/ record_nums) * 100)
if round(float(progress))>100:
progress=100
self.progressbar['value'] = progress
self.l2_var.set(str(progress) + '%')
self.insert_to_treeview('--------------------------------------TXT报告生成成功--------------------------------------')
try:
#生成Excel表格Flag
if self.s_check1_state==True:
self.insert_to_treeview(
'--------------------------------------Excel报告开始生成--------------------------------------')
self.p.write_to_excel(excel_path=self.p.excel_path,all_data=data_list)
self.insert_to_treeview(
'--------------------------------------Excel报告生成成功--------------------------------------')
except AttributeError:
self.insert_to_treeview(
'--------------------------------------Excel报告开始生成--------------------------------------')
self.p.write_to_excel(excel_path=self.p.excel_path, all_data=data_list)
self.insert_to_treeview(
'--------------------------------------Excel报告生成成功--------------------------------------')
def set_can_start_flag(self):
"""
设置开始测试
:return:
"""
self.FLAG=True
self.btn2.config(state=DISABLED)
self.s2.entryconfig('停止测试 (F12)', state=DISABLED)
self.btn1.config(state=NORMAL)
self.s2.entryconfig('开始测试 (F10)', state=NORMAL)
self.root.unbind('<F12>')
self.root.bind('<F10>',self.do_start_test)
def set_can_stop_flag(self):
"""
:return:
"""
self.FLAG=False
#点完停止按钮,程序又能重新运行了
self.START_FLAG=True
self.btn1.config(state=NORMAL)
self.s2.entryconfig('停止测试 (F12)', state=DISABLED)
self.btn2.config(state=DISABLED)
self.s2.entryconfig('开始测试 (F10)', state=NORMAL)
self.root.unbind('<F10>')
self.root.bind('<F12>',self.do_stop_test)
def do_stop_test(self):
self.set_can_stop_flag()
self.insert_to_treeview(f'正在停止程序,请耐心等待!')
def event_loaction_selected(self):
"""
“事件”列 所在位置设置
:return:
"""
index=self.s4_radio_var.get()
location=[lo for lo in self.event_location.values()][index]
self.treeview.column("event", anchor=location)
def change_theme(self,):
'''
更换主题
:return:
'''
theme=self.themevar.get()
style = ttk.Style(self.root)
style.theme_use(theme)
def do_copy_event(self,event):
"""
显示“复制事件”菜单
:param event:
:return:
"""
self.m2.post(event.x_root, event.y_root)
def copy_event(self):
"""
复制事件
:return:
"""
try:
curr_index = self.treeview.index(self.treeview.focus())
event=[ self.treeview.item(item,"values") for item in self.treeview.get_children()][curr_index][1]
pyperclip.copy(event)
messagebox.showinfo('提示','选中事件已成功复制到剪切板!')
except (AttributeError,IndexError):
messagebox.showwarning('警告','请先选择事件!')
def insert_to_treeview(self,content):
"""
向Treeview中写入事件,并加入当前时间
:param content:
:return:
"""
timestring=self.u.get_time_string()
item_num=len(list(self.treeview.get_children()))
self.treeview.insert('','end',values=(item_num+1,content,timestring))
#每次插入treeview都会将滚动条移动至最下
self.treeview.yview_moveto(1)
def do_top_most(self):
"""
置顶窗口
:return:
"""
if self.top_most_var.get()==1:
self.root.wm_attributes('-topmost',1)
else:
self.root.wm_attributes('-topmost', 0)
#____“设置”窗口____
def open_set_window(self):
self.set_window=Toplevel()
self.set_window.resizable(0,0)
width=400
height=300
left=(self.set_window.winfo_screenwidth()-width)/2
top=(self.set_window.winfo_screenheight()-height)/2
self.set_window.geometry('%dx%d+%d+%d'%(width,height,left,top))
self.set_window.title('首选设置')
self.set_window.protocol("WM_DELETE_WINDOW",self.set_window_cancle)
self.s_l1=ttk.Label(self.set_window,text='依赖所在目录:')
self.s_e1_var=StringVar()
self.s_e1=ttk.Entry(self.set_window,textvariable=self.s_e1_var)
self.s_e1_var.set('软件所在的根目录')
self.s_btn1=ttk.Button(self.set_window,text='选择目录')
self.s_e1.config(state=DISABLED)
self.s_btn1.config(state=DISABLED)
self.s_l3=ttk.Label(self.set_window,text='选择设备:')
self.s_combobox_var=StringVar()
self.s_combobox=ttk.Combobox(self.set_window,justify='center',state='readonly',textvariable=self.s_combobox_var)
self.s_btn3=ttk.Button(self.set_window,text='重新检测',command=self.re_check_devices)
self.s_l4=ttk.Label(self.set_window,text='测试时常(单位:秒):')
self.s_spin1_var=IntVar()
self.s_spin1_var.set(30)
s_spin1_validatecommand=self.set_window.register(self.s_spin1_update)
self.s_spin1=ttk.Spinbox(self.set_window,from_=0,to_=3000,increment=5,textvariable=self.s_spin1_var,validate='focusout',
validatecommand=(s_spin1_validatecommand, '%P',))
#循环不阻塞验证spinbox输入合法性
self.s_spin1.after_idle(lambda: self.s_spin1.config(validate='focusout'))
self.s_e2_var=StringVar()
self.s_l2=ttk.Label(self.set_window,text='测试App所在目录:')
self.s_e2=ttk.Entry(self.set_window,textvariable=self.s_e2_var)
self.s_btn2=ttk.Button(self.set_window,text='选择目录',command=self.set_apks_dir)
self.s_check1_var=StringVar(value='1')
self.s_check1=ttk.Checkbutton(self.set_window,text='生成Excel报告',variable=self.s_check1_var)
self.s_check2_var=StringVar(value='1')
self.s_check2=ttk.Checkbutton(self.set_window,text='自动关闭模拟器',variable=self.s_check2_var,command=self.show_s_check2_info)
ToolTip(self.s_check1,text='勾选了此选项,软件会在APP自动测试结束后,自动生成Excel格式报告!')
ToolTip(self.s_check2,text='勾选了此选项就不支持多开,请慎重考虑!')
self.s_btn4=ttk.Button(self.set_window,text='保存',command=self.save_set)
self.s_btn5=ttk.Button(self.set_window,command=self.set_window_cancle)
now_devices=self.a_.get_devices()
if len(now_devices)==0:
self.s_combobox.config(value='未发现设备',foreground= 'red')
ToolTip(self.s_combobox,text=f'未发现设备,请先打开设备,再点[重新检测]')
else:
self.s_combobox.config(value=now_devices)
ToolTip(self.s_combobox,text=f'检测到{
len(now_devices)}个可用设备')
self.s_combobox.current(0)
if self.set_window_start_flag is True:
self.s_btn5.config(text='取消')
else:
self.s_btn5.config(text='放弃更改')
try:
if isinstance(self.apk_path,str):
self.s_e2_var.set(self.apk_path)
except AttributeError:
pass
try:
if isinstance(self.crawl_packet_time,int):
self.s_spin1_var.set(self.crawl_packet_time)
except AttributeError:
pass
if self.s_check1_state:
self.s_check1_var.set('1')
else:
self.s_check1_var.set('0')
if self.s_check2_state:
self.s_check2_var.set('1')
else:
self.s_check2_var.set('0')
self.s_l1.place(x=10,y=30)
self.s_e1.place(x=140,y=30)
self.s_btn1.place(x=300,y=30)
self.s_l2.place(x=10,y=80)
self.s_e2.place(x=140,y=80)
self.s_btn2.place(x=300,y=80)
self.s_l3.place(x=10,y=130)
self.s_combobox.place(x=140,y=130,width=145)
self.s_btn3.place(x=300,y=130)
self.s_l4.place(x=10,y=180)
self.s_spin1.place(x=140,y=180,width=145)
self.s_check1.place(x=60,y=220)
self.s_check2.place(x=200,y=220)
self.s_btn4.place(x=50,y=255)
self.s_btn5.place(x=250,y=255)
def set_apks_dir(self):
"""
检查所设置目录是否存在.APK文件
:return:
"""
while True:
self.file = askdirectory(title='App所在目录',initialdir='./')
self.s_e2_var.set(self.file)
apk_path = os.path.abspath(self.s_e2_var.get())
if any([file for file in os.listdir(apk_path) if file.endswith('.apk') ]):
#messagebox.showinfo('提示','App目录设置成功')
self.s_e2_var.set(apk_path)
break
#用户未选择目录
elif apk_path==os.path.abspath('./'):
break
else:
messagebox.showwarning('警告','此文件夹不存在安卓App(.apk)文件,请重新选择!')
continue
def save_set(self):
"""
:return:
"""
if self.s_e2_var.get()!='' and self.s_spin1_var!='' :
if not self.s_combobox_var.get()=='未发现设备':
self.apk_path=self.s_e2_var.get()
if self.s_check1_var.get()!='1':
self.s_check1_state=False
else:
self.s_check1_state=True
if self.s_check2_var.get()!='1':
self.s_check2_state=False
else:
self.s_check2_state=True
self.crawl_packet_time=self.s_spin1_var.get()
messagebox.showinfo('提示', '保存设置成功!')
self.s_e2.config(state="readonly")
self.set_window_start_flag=False
self.set_window.destroy()
else:
messagebox.showwarning('警告', '请选择设备!')
else:
self.apk_path =None
self.crawl_packet_time=30
messagebox.showwarning('警告','请设置正确的目录!')
def set_window_cancle(self):
"""
:return:
"""
if self.s_btn5['text']=='取消':
self.apk_path = None
self.set_window.destroy()
else:
self.set_window.destroy()
def re_check_devices(self):
now_devices=self.a_.get_devices()
if len(now_devices)==0:
self.s_combobox.config(foreground='red',value='未发现设备')
messagebox.showwarning('警告','未发现设备')
self.insert_to_treeview('未发现设备')
ToolTip(self.s_combobox,text='未发现设备')
else:
self.s_combobox.config(foreground='black',value=now_devices)
ToolTip(self.s_combobox,text=f'检测到{
len(now_devices)}个可用设备')
self.s_combobox.current(0)
def s_spin1_update(self,value):
#这里必须设置返回值,要不然只改变一次数值
if value.isdigit():
if int(value)>self.s_spin1['to']:
self.s_spin1_var.set(30)
return True
else:
self.s_spin1_var.set(30)
return False
def show_s_check2_info(self):
if self.s_check2_var.get()=='1':
messagebox.showinfo('提示','勾选了此项,本软件就不支持多开了!')
def escape(self,event):
self.quit_window()
def quit_window(self):
ret=messagebox.askyesno('退出','是否要退出?')
if ret:
self.root.destroy()
def thread_it(self,func,*args):
t=threading.Thread(target=func,args=args)
t.setDaemon(True)
t.start()
if __name__ == '__main__':
a=App()
四.总结
本次使用Tkinter制作了一款安卓APP自动测试工具,通过调用adb命令对模拟器/真机进行相关安装打开卸载应用操作,能够自动产生测试报告,实用性很强,放在了蓝奏云望大家喜欢!思路、代码方面有什么不足欢迎各位大佬指正、批评!