(python tk/ttk制作 安卓群控助手,多台设备多任务多线程执行)
✔
仅供学习,禁止商用,转载请注明出处。
更新记录:
更新时间 | 更新内容 |
---|---|
20190926 | 重大更新,将页面重构,代码重构,交互更加方便,任务模快完成,整个程序可以正常运行,并稳定运行。 |
20190826 | 初版成型,页面布局已经完成,但任务执行模块未实现 |
点击跳转到 GitHub地址
第一次使用python的tkinter库,一个python简单的GUI编程库
通过ADB命令去控制手机,同时使用多线程,对手机进行批量操作,如,多台设备同时安装软件,生成通讯录测试数据等。
由于公司测试组的工作需要,简单开发的安卓手机助手,有助于提升工作效率。
mods:存放一些公共方法,比如获取已连接的手机列表,获取本地adb工具,直接cmd命令的方法目录
platform-tools:为了能使工具在其他windows上可用,我在这里添加了一个自有的adb工具包,直接调用即可,即使本地电脑上没有装adb环境。
bin_mod.py:具体的公共方法文件,含有一系列自定义的方法
import subprocess
import os
import re
class bin_mod:
work_name = "PMTool"
tempath = os.path.abspath('').rpartition(work_name)
work_path = tempath[0] + tempath[1]
def adb(self):
'''内置adb工具路径'''
adb = self.work_path + r'\mods\platform-tools\adb.exe '
return adb
def run_cmd(self, command):# 执行cmd命令
'''执行CMD命令
以列表形式返回执行内容'''
output = subprocess.Popen(command, stdout=subprocess.PIPE, encoding='utf-8')
return output.stdout.read().split("\n")
# 实时输出
def sh_cmd(self, command):
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, encoding='utf-8')
lines = []
for line in iter(p.stdout.readline, b''):
print(">>>", line)
lines.append(line)
return lines
def package(self, apks=1):# 获取安装包的包名
'''通过apk文件,获取对应的包名
参数说明:
默认
返回包名组'''
aapt = self.work_path + r'\mods\platform-tools\aapt.exe'
packagelist = []
if type(apks) == str:
aapt = self.work_path + r'\mods\platform-tools\aapt.exe'
cmd = (aapt + ' dump badging ' + apks)
packagelist.append(re.split('[:=\' ]+', self.run_cmd(cmd)[0])[2])
return packagelist
else:
apks = self.otherapk()
for apk in apks:
cmd = (aapt + ' dump badging ' + apk)
packagelist.append(re.split('[:=\' ]+', self.run_cmd(cmd)[0])[2])
return packagelist
# 转换路径‘/,\’
def convert_path(slef, path: str) -> str:
return path.replace(r'\/'.replace(os.sep, ''), os.sep)
get_vcardfile.py:通讯录生成模块,可以生成对应数量的通讯录条数,便于导入到手机端生成数据,通讯录工具模块所调用的文件
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 2 08:40:20 2019
@author: REXLEE
"""
import random
import quopri
import logging
from .bin_mod import bin_mod
work_path = bin_mod.work_path # 获取工作目录
# 生成中文名(百家姓任选一,名字在常用选2个)
def gen_name():
first_names = ['赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈', '褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '许',
'何', '吕', '施', '张', '孔', '曹', '严', '华', '金', '魏', '陶', '姜', '戚', '谢', '邹', '喻', '柏', '水', '窦', '章',
'云', '苏', '潘', '葛', '奚', '范', '彭', '郎', '鲁', '韦', '昌', '马', '苗', '凤', '花', '方', '俞', '任', '袁', '柳',
'酆', '鲍', '史', '唐', '费', '廉', '岑', '薛', '雷', '贺', '倪', '汤', '滕', '殷', '罗', '毕', '郝', '邬', '安', '常',
'乐', '于', '时', '傅', '皮', '卞', '齐', '康', '伍', '余', '元', '卜', '顾', '孟', '平', '黄', '和', '穆', '萧', '尹',
'姚', '邵', '堪', '汪', '祁', '毛', '禹', '狄', '米', '贝', '明', '臧', '计', '伏', '成', '戴', '谈', '宋', '茅', '庞',
'熊', '纪', '舒', '屈', '项', '祝', '董', '梁']
last_names = ['的', '一', '是', '了', '我', '不', '人', '在', '他', '有', '这', '个', '上', '们', '来', '到', '时', '大', '地', '为',
'子', '中', '你', '说', '生', '国', '年', '着', '就', '那', '和', '要', '她', '出', '也', '得', '里', '后', '自', '以',
'会', '家', '可', '下', '而', '过', '天', '去', '能', '对', '小', '多', '然', '于', '心', '学', '么', '之', '都', '好',
'看', '起', '发', '当', '没', '成', '只', '如', '事', '把', '还', '用', '第', '样', '道', '想', '作', '种', '开', '美',
'总', '从', '无', '情', '己', '面', '最', '女', '但', '现', '前', '些', '所', '同', '日', '手', '又', '行', '意', '动',
'方', '期', '它', '头', '经', '长', '儿', '回', '位', '分', '爱', '老', '因', '很', '给', '名', '法', '间', '斯', '知',
'世', '什', '两', '次', '使', '身', '者', '被', '高', '已', '亲', '其', '进', '此', '话', '常', '与', '活', '正', '感',
'见', '明', '问', '力', '理', '尔', '点', '文', '几', '定', '本', '公', '特', '做', '外', '孩', '相', '西', '果', '走',
'将', '月', '十', '实', '向', '声', '车', '全', '信', '重', '三', '机', '工', '物', '气', '每', '并', '别', '真', '打',
'太', '新', '比', '才', '便', '夫', '再', '书', '部', '水', '像', '眼', '等', '体', '却', '加', '电', '主', '界', '门',
'利', '海', '受', '听', '表', '德', '少', '克', '代', '员', '许', '稜', '先', '口', '由', '死', '安', '写', '性', '马',
'光', '白', '或', '住', '难', '望', '教', '命', '花', '结', '乐', '色', '更', '拉', '东', '神', '记', '处', '让', '母',
'父', '应', '直', '字', '场', '平', '报', '友', '关', '放', '至', '张', '认', '接', '告', '入', '笑', '内', '英', '军',
'候', '民', '岁', '往', '何', '度', '山', '觉', '路', '带', '万', '男', '边', '风', '解', '叫', '任', '金', '快', '原',
'吃', '妈', '变', '通', '师', '立', '象', '数', '四', '失', '满', '战', '远', '格', '士', '音', '轻', '目', '条', '呢',
'病', '始', '达', '深', '完', '今', '提', '求', '清', '王', '化', '空', '业', '思', '切', '怎', '非', '找', '片', '罗',
'钱', '紶', '吗', '语', '元', '喜', '曾', '离', '飞', '科', '言', '干', '流', '欢', '约', '各', '即', '指', '合', '反',
'题', '必', '该', '论', '交', '终', '林', '请', '医', '晚', '制', '球', '决', '窢', '传', '画', '保', '读', '运', '及',
'则', '房', '早', '院', '量', '苦', '火', '布', '品', '近', '坐', '产', '答', '星', '精', '视', '五', '连', '司', '巴',
'奇', '管', '类', '未', '朋', '且', '婚', '台', '夜', '青', '北', '队', '久', '乎', '越', '观', '落', '尽', '形', '影',
'红', '爸', '百', '令', '周', '吧', '识', '步', '希', '亚', '术', '留', '市', '半', '热', '送', '兴', '造', '谈', '容',
'极', '随', '演', '收', '首', '根', '讲', '整', '式', '取', '照', '办', '强', '石', '古', '华', '諣', '拿', '计', '您',
'装', '似', '足', '双', '妻', '尼', '转', '诉', '米', '称', '丽', '客', '南', '领', '节', '衣', '站', '黑', '刻', '统',
'断', '福', '城', '故', '历', '惊', '脸', '选', '包', '紧', '争', '另', '建', '维', '绝', '树', '系', '伤', '示', '愿',
'持', '千', '史', '谁', '准', '联', '妇', '纪', '基', '买', '志', '静', '阿', '诗', '独', '复', '痛', '消', '社', '算',
'义', '竟', '确', '酒', '需', '单', '治', '卡', '幸', '兰', '念', '举', '仅', '钟', '怕', '共', '毛', '句', '息', '功',
'官', '待', '究', '跟', '穿', '室', '易', '游', '程', '号', '居', '考', '突', '皮', '哪', '费', '倒', '价', '图', '具',
'刚', '脑', '永', '歌', '响', '商', '礼', '细', '专', '黄', '块', '脚', '味', '灵', '改', '据', '般', '破', '引', '食',
'仍', '存', '众', '注', '笔', '甚', '某', '沉', '血', '备', '习', '校', '默', '务', '土', '微', '娘', '须', '试', '怀',
'料', '调', '广', '蜖', '苏', '显', '赛', '查', '密', '议', '底', '列', '富', '梦', '错', '座', '参', '八', '除', '跑',
'亮', '假', '印', '设', '线', '温', '虽', '掉', '京', '初', '养', '香', '停', '际', '致', '阳', '纸', '李', '纳', '验',
'助', '激', '够', '严', '证', '帝', '饭', '忘', '趣', '支', '春', '集', '丈', '木', '研', '班', '普', '导', '顿', '睡',
'展', '跳', '获', '艺', '六', '波', '察', '群', '皇', '段', '急', '庭', '创', '区', '奥', '器', '谢', '弟', '店', '否',
'害', '草', '排', '背', '止', '组', '州', '朝', '封', '睛', '板', '角', '况', '曲', '馆', '育', '忙', '质', '河', '续',
'哥', '呼', '若', '推', '境', '遇', '雨', '标', '姐', '充', '围', '案', '伦', '护', '冷', '警', '贝', '著', '雪', '索',
'剧', '啊', '船', '险', '烟', '依', '斗', '值', '帮', '汉', '慢', '佛', '肯', '闻', '唱', '沙', '局', '伯', '族', '低',
'玩', '资', '屋', '击', '速', '顾', '泪', '洲', '团', '圣', '旁', '堂', '兵', '七', '露', '园', '牛', '哭', '旅', '街',
'劳', '型', '烈', '姑', '陈', '莫', '鱼', '异', '抱', '宝', '权', '鲁', '简', '态', '级', '票', '怪', '寻', '杀', '律',
'胜', '份', '汽', '右', '洋', '范', '床', '舞', '秘', '午', '登', '楼', '贵', '吸', '责', '例', '追', '较', '职', '属',
'渐', '左', '录', '丝', '牙', '党', '继', '托', '赶', '章', '智', '冲', '叶', '胡', '吉', '卖', '坚', '喝', '肉', '遗',
'救', '修', '松', '临', '藏', '担', '戏', '善', '卫', '药', '悲', '敢', '靠', '伊', '村', '戴', '词', '森', '耳', '差',
'短', '祖', '云', '规', '窗', '散', '迷', '油', '旧', '适', '乡', '架', '恩', '投', '弹', '铁', '博', '雷', '府', '压',
'超', '负', '勒', '杂', '醒', '洗', '采', '毫', '嘴', '毕', '九', '冰', '既', '状', '乱', '景', '席', '珍', '童', '顶',
'派', '素', '脱', '农', '疑', '练', '野', '按', '犯', '拍', '征', '坏', '骨', '余', '承', '置', '臓', '彩', '灯', '巨',
'琴', '免', '环', '姆', '暗', '换', '技', '翻', '束', '增', '忍', '餐', '洛', '塞', '缺', '忆', '判', '欧', '层', '付',
'阵', '玛', '批', '岛', '项', '狗', '休', '懂', '武', '革', '良', '恶', '恋', '委', '拥', '娜', '妙', '探', '呀', '营',
'退', '摇', '弄', '桌', '熟', '诺', '宣', '银', '势', '奖', '宫', '忽', '套', '康', '供', '优', '课', '鸟', '喊', '降',
'夏', '困', '刘', '罪', '亡', '鞋', '健', '模', '败', '伴', '守', '挥', '鲜', '财', '孤', '枪', '禁', '恐', '伙', '杰',
'迹', '妹', '藸', '遍', '盖', '副', '坦', '牌', '江', '顺', '秋', '萨', '菜', '划', '授', '归', '浪', '听', '凡', '预',
'奶', '雄', '升', '碃', '编', '典', '袋', '莱', '含', '盛', '济', '蒙', '棋', '端', '腿', '招', '释', '介', '烧', '误',
'乾', '坤']
return random.choice(first_names) + random.choice(last_names) + random.choice(last_names)
# 生成电话号码(任选一个3位开头,后面8位随机生成)
def gen_tel():
prelist=["130","131","132","133","134","135","136","137","138","139","147","150","151","152","153","155","156","157","158","159","186","187","188", "177", "176"]
return random.choice(prelist)+"".join(random.choice("0123456789") for i in range(8))
# 创建一个vcard(名字需要使用QUOTED-PRINTABLE编码)
def gen_vcard(name, tel):
vcard = "BEGIN:VCARD" + '\n' + \
"VERSION:2.1" + '\n' + \
"N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;" + name + ";;;" + '\n' + \
"FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:" + name + '\n' + \
"TEL;CELL:" + tel + '\n' + \
"TEL;HOME:" + tel + '\n' + \
"EMAIL;HOME:[email protected]" + '\n' + \
"ADR;HOME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=E5=9C=B0=E5=9D=80;;;;" + '\n' + \
"END:VCARD" + '\n'
return vcard
def get_vcardfile(numb):
path = work_path+'\\temp\\_vcard_temp\\'
if int(numb) > 0:
try:
f = open(path+'contacts.vcf', 'w')
counts = int(numb)
for x in range(1, counts + 1):
name = gen_name().encode('utf8')
name = str(quopri.encodestring(name))[2:-1]
f.write(gen_vcard(name, gen_tel()))
f.close()
except:
logging.info('生成失败')
else:
logging.info('生成数量不正确')
getphone_mod.py:获取已连接设备的列表
# -- coding: utf-8 --
"""
Created on Wed Jul 3 09:34:26 2019
@author: REXLEE
"""
import logging
from .bin_mod import bin_mod
class GetPhone:
def __init__(self):#初始化,获取已连设备信息
self.adb = bin_mod().adb()#获取ADB工具
self.runcmd = bin_mod().run_cmd
self.equipment = []
def __about_phone(self, devices):# 信息处理为可用数据
if devices[1] == 'offline':
pid = devices[0]
model = '未知'
statu = 'offline'
return pid, model, statu
if devices[1] == 'unauthorized':
pid = devices[0]
model = '未知'
statu = 'unauthorized'
return pid, model, statu
elif devices[1]=='device':
pid = devices[0]
model = self.runcmd(self.adb+' -s %s shell getprop ro.product.model' % pid)[0]
statu = 'online'
return pid, model, statu
else:
logging.info("请检查设备连接状态")
def usb_connect(self):
logging.info("正在读取设备列表")
cmd = (self.adb + ' devices')
self.deviceslist = self.runcmd(cmd)
if self.deviceslist[1] == '':
logging.info('未检测到连入设备')
self.equipment = None
else:
for self.devices in self.deviceslist:
if self.devices == 'List of devices attached':
continue
elif self.devices == '':
break
else:
self.equipment.append(self.__about_phone(self.devices.split()))
logging.info('添加设备%s' % self.devices)
logging.info('已读取设备列表%s' % self.equipment)
return self.equipment
def wifi_connect(self):
print("wifi_connect")
logger_mod.py:日志模块
# -*- coding: utf-8 -*-
"""
Created on Wed Jul 3 09:21:06 2019
@author: REXLEE
"""
import logging
import datetime
from .bin_mod import bin_mod
work_path = bin_mod().work_path
def logger_mod(loglevel=logging.DEBUG):
logpath = work_path+'\\temp\\_run_log\\'
logging.basicConfig(
level = loglevel,
filename = logpath+"%s.log" % (datetime.datetime.now().strftime('%Y-%m-%d')), #文件名称
datefmt = '%m-%d %H:%M:%S',#日期格式
format = '%(asctime)-4s File \'%(pathname)s\', line %(lineno)d, in %(funcName)s(), %(levelname)s -> %(message)s', #格式
filemode = 'w')#文件模式
# 将日志输出到控制台
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('in %(filename)s, line %(lineno)d, %(levelname)s -> %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
if __name__=="__main__":
print('不支持单独运行文件,请运行run.py')
thread_mod.py:线程模块
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 2 15:05:29 2019
@author: REXLEE
"""
from threading import Thread
import logging
import datetime
import importlib
class ThreadClass:
def thread_start(self, equipment, toollist, config):#线程执行方法
self.threads = []
#运行开始,记录开始时间
self.starttime = datetime.datetime.now()
logging.info('运行开始时间:%s' % self.starttime.strftime('%H:%M:%S'))
for phone in equipment:
thread = Thread(target=Plan().myplan, args=(phone, toollist, config,))
thread.start()
# self.threads.append(thread)
# for thread in self.threads:
# thread.join()
class Plan:
def myplan(self, phone, toollist, config):
for num in toollist:
tempimport = importlib.import_module('tools.'+'_'+num+'_tool')
temp = tempimport.toolclass(phone, config[int(num)]())
temp.start()
page:用于存放整个工具的页面代码,以及页面逻辑等文档
png:用于存放整个工具页面中 所用到的一些图标文件
类似与下方中的run图标
main.py:主要为整个工具的界面相关的代码,生成界面的样式
PS:更新后,主要对这部分内容进行了更新,将其中的功能代码完全剥离出来,放到了后面的 main_fun.py 的文件中,此文件中,完全为静态的界面代码
# -- coding: utf-8 --
"""
Created on Tue Jul 23 10:25:38 2019
@author: REXLEE
"""
from tkinter import *
import tkinter.ttk as ttk
import tkinter as tk
import tkinter.font as tkFont
from toolpg import configPage
from mods.bin_mod import bin_mod
import importlib
class Mainpg(object):
def __init__(self, root):
self.root = root
# 初始化窗口
self.root.title('安卓助手')
self.root.attributes("-alpha", 0.9)
self.root.resizable(width=False, height=False)
rootsize = '%dx%d+%d+%d' % (285, 480, (self.root.winfo_screenwidth() - 285) / 2, (self.root.winfo_screenheight() - 480) / 2)
self.root.geometry(rootsize)
self.root.attributes('-alpha', 0.98,)
self.root.protocol('WM_DELETE_WINDOW', self.closeWindow)
self.createpg()
def createpg(self):
self.pic_path = bin_mod.work_path + '\\page\\png\\' # 获取图标目录
self.toolpglist = configPage # 获取页面列表
self.mainframe = Frame(self.root)
self.mainframe.pack(fill=BOTH, expand=True)
self.phonepg()
ttk.Frame(self.mainframe, width=5,).pack(side=LEFT, fill=Y)
self.toolpgmain()
def phonepg(self):
self.phonepg_Frame = ttk.LabelFrame(self.mainframe, text='手机列表')
# self.phonepg_Frame.pack_propagate(1)
self.phonepg_Frame.pack(side=LEFT, fill=Y)
self.button_frame = ttk.Frame(self.phonepg_Frame)
self.button_frame.pack(fill=X)
# ------------------操作按钮1-----------------
self.phonebn_Frame1 = ttk.Frame(self.button_frame)
self.phonebn_Frame1.grid(row=1,column=1,sticky=W)
self.connect_bn = ttk.Button(self.phonebn_Frame1, text='连接设备', width=8, command=self.connect_bn_f)
self.connect_bn.pack(fill=X, side=LEFT, expand=True)
self.refresh_bn = ttk.Button(self.phonebn_Frame1, text='刷新列表', width=8, command=self.refresh_bn_f)
self.refresh_bn.pack(fill=X, side=LEFT, expand=True)
self.terminal_bn = ttk.Button(self.phonebn_Frame1, text='ADB终端', width=8, command=self.terminal_bn_f)
self.terminal_bn.pack(fill=X, side=LEFT, expand=True)
self.log_bn = ttk.Button(self.phonebn_Frame1, text='抓取LOG', width=8, command=self.log_bn_f)
self.log_bn.pack(fill=X, side=LEFT, expand=True)
# ------------------操作按钮2------------------------------------------------
self.phonebn_Frame2 = ttk.Frame(self.button_frame)
self.phonebn_Frame2.grid(row=2,column=1,sticky=W)
self.choose_bn = ttk.Button(self.phonebn_Frame2, text='载入所有', width=8, command=self.choose_bn_f)
self.choose_bn.pack(fill=X, side=LEFT, expand=True)
# self.twinkle_bn = ttk.Button(self.phonebn_Frame2, text='闪烁手机', width=8, command=self.twinkle_bn_f)
# self.twinkle_bn.pack(fill=X, side=LEFT, expand=True)
self.twinkle_bn = ttk.Button(self.phonebn_Frame2, text='删除Msg', width=8, command=self.deleMsg_bn_f)
self.twinkle_bn.pack(fill=X, side=LEFT, expand=True)
self.NULL2_bn = ttk.Button(self.phonebn_Frame2, text='软件列表', width=8, command=self.package_bn_f)
self.NULL2_bn.pack(fill=X, side=LEFT, expand=True)
self.run_pic = tk.PhotoImage(file=self.pic_path + r"\run_pic.png")
self.run_bn = ttk.Button(self.phonebn_Frame2, text='RUN ',
width=5,
compound="left",
image=self.run_pic,
command=self.run_fun)
self.run_bn.pack(fill=X, side=LEFT, expand=True)
self.mini_bn_frame = tk.Frame(self.button_frame)
self.mini_bn_frame.grid(row=1,column=2,rowspan=2,sticky=N+E+W+S)
self.mini_bn = ttk.Button(self.mini_bn_frame, text='》',
width=1,
compound="left",
command=self.mini_bn_f)
self.mini_bn.pack(fill=Y, expand=True)
self.phonebn_Frame3 = ttk.Frame(self.button_frame)
self.phonebn_Frame3.grid(row=3, column=1,columnspan=2,sticky=W+E)
self.more_button = ttk.Button(self.phonebn_Frame3,text='微信缓存清空',width=26, command = self.more_bn_f).pack(fill=X, side=LEFT, expand=True)
# ------------------设备列表1------------------------------------------------
self.phonelist_Frame1 = Frame(self.phonepg_Frame,bd=1, relief="sunken")
self.phonelist_Frame1.pack(fill=BOTH, expand=True)
self.phonelist_tree = ttk.Treeview(self.phonelist_Frame1, selectmode="extended", show="headings")
self.phonelist_tree["columns"] = ("选中", "型号", "PID")
self.phonelist_tree.heading("#0", text="#")
self.phonelist_tree.column("#0", minwidth=0, stretch=YES, anchor='center')
self.phonelist_tree.heading("选中", text="选中")
self.phonelist_tree.column("选中", minwidth=0, width=50, stretch=YES, anchor='center')
self.phonelist_tree.heading("型号", text="型号")
self.phonelist_tree.column("型号", minwidth=0, width=80, stretch=YES, anchor='center')
self.phonelist_tree.heading("PID", text="PID")
self.phonelist_tree.column("PID", minwidth=0, width=130, stretch=YES, anchor='center')
# 垂直滚动条
self.scrolly1 = ttk.Scrollbar(self.phonelist_Frame1, orient=VERTICAL, command=self.phonelist_tree.yview)
self.phonelist_tree.configure(yscrollcommand=self.scrolly1.set)
self.phonelist_tree.pack(fill=BOTH, side=LEFT, expand=True)
# self.scrolly1.pack(fill=Y, side=LEFT, expand=True)
self.phonelist_tree.bind('' , self.phonelist_Double_f) # 绑定左键双击事件===========
self.phonelist_tree.bind('' , self.copy_pid_f) # 绑定左键双击事件===========
def toolpgmain(self):
self.toolpg_all = ttk.Frame(self.mainframe)
self.toolpg_all.pack(side=LEFT, fill=BOTH, expand=True)
self.toolchoose_Frame = ttk.LabelFrame(self.toolpg_all,height=180, text='执行顺序')
self.toolchoose_Frame.pack_propagate(0)
self.toolchoose_Frame.pack(side=BOTTOM, fill=BOTH)
self.toolchoose_Button = Frame(self.toolchoose_Frame, width=211)
self.toolchoose_Button.pack_propagate(0)
self.toolchoose_Button.pack(side=RIGHT, fill=BOTH, expand=0)
ttk.Button(self.toolchoose_Button,text = '清空内容',command = self.toolchoose_Button_f).pack(fill=BOTH)
self.choose_Frame = LabelFrame(self.toolchoose_Frame)
self.choose_Frame.pack(side=RIGHT, fill=BOTH, expand=True)
self.choose_Label=Label(self.choose_Frame,
text='',
font=tkFont.Font(family='微软雅黑', size=12),
wraplength=600,
justify='left',
anchor='nw'
)
self.choose_Label.pack(fill=BOTH, expand=True)
self.tool_top = ttk.Frame(self.toolpg_all)
self.tool_top.pack(side=BOTTOM, fill=BOTH, expand=True)
self.toollist = ttk.LabelFrame(self.tool_top, text='工具列表(双击载入)', width=211)
self.toollist.pack_propagate(0)
self.toollist.pack(side=RIGHT, fill=BOTH, expand=0)
ttk.Frame(self.tool_top, width=5).pack(side=RIGHT, fill=BOTH, expand=0)
self.toollist_Frame1 = Frame(self.toollist)
self.toollist_Frame1.pack(fill=BOTH, expand=True)
self.tooltree = ttk.Treeview(self.toollist_Frame1, show="headings")
self.tooltree["columns"] = ("序号", "工具名称", "选中")
self.tooltree.heading("#0", text="#")
self.tooltree.column("#0", minwidth=0, stretch=YES, anchor='center')
self.tooltree.heading("序号", text="序号")
self.tooltree.column("序号", minwidth=0, width=35, stretch=YES, anchor='center')
self.tooltree.heading("工具名称", text="工具名称")
self.tooltree.column("工具名称", minwidth=0, width=100, stretch=YES, anchor='center')
self.tooltree.heading("选中", text="选中")
self.tooltree.column("选中", minwidth=0, width=50, stretch=YES, anchor='center')
for l in self.toolpglist:
self.tooltree.insert('', 'end', values=(l.split('_')[1], l.split('_')[2], ''))
self.tooltree.pack(fill=None, side=LEFT, expand=True)
self.tooltree.bind('' , self.tooltree_click_f) # 绑定左键单击事件===========
self.tooltree.bind('' , self.tooltree_click1_f) # 绑定左键双击事件===========
self.tooltree.pack(fill=BOTH, expand=True)
self.toolpg = ttk.LabelFrame(self.tool_top, text='工具配置')
self.toolpg.pack(side=RIGHT, fill=BOTH, expand=True)
self.tablist = []
self.toolpgtabControl = ttk.Notebook(self.toolpg,padding=-2)
for tabname in self.toolpglist:
tab = Label(self.toolpgtabControl,)
self.toolpgtabControl.add(tab, text=tabname.split("_")[2]) # Add the tab
self.tablist.append(tab)
self.toolpgtabControl.pack(fill=BOTH, expand=True)
self.config = {}
n = 0
for tabpage in self.toolpglist:
tempimport = importlib.import_module('toolpg.'+tabpage)
temp = tempimport.Page(self.tablist[n])
temp.createPage()
self.config[n+1] = temp.getconfig
n = n+1
self.toolhead = tk.Label(self.toolpg, text=' └ 软件安装',
font=tkFont.Font(family='微软雅黑', size=10, weight=tkFont.BOLD),
width=67,
anchor=W,
relief='ridge',
pady=3,
bd=1,
fg='MidnightBlue',
bg='AliceBlue')
self.toolhead.place(x=0, y=0)
main_fun.py:整个工具的页面交互逻辑方法文件
import tkinter as tk
from tkinter import *
from tkinter import messagebox
from mods.getphone_mod import GetPhone
import time
import datetime
import logging
from .mainpg import Mainpg
from mods.bin_mod import *
from mods.thread_mod import ThreadClass
from threading import Thread
import ctypes
import inspect
class Mainpg_fun(Mainpg):
def __init__(self, root):
super(Mainpg_fun, self).__init__(root)
self.adb = bin_mod().adb()
self.run_cmd = bin_mod().run_cmd
self.sh_cmd = bin_mod().sh_cmd
self.mytools_num = []
# 连接设备
def connect_bn_f(self):
if self.connect_bn["text"] == '断开连接':
os.popen(self.adb+'kill-server')
x=self.phonelist_tree.get_children()
for item in x:
self.phonelist_tree.delete(item)
self.connect_bn.config(text='重新连接')
return
self.phonelist = GetPhone().usb_connect()
if self.phonelist==None:
messagebox.showinfo('通知', '请检查设备是否正确连接')
else:
for p in self.phonelist:
self.phonelist_tree.insert('', 'end', values=('', p[1], p[0]))
self.connect_bn.config(text = '断开连接')
# 刷新列表
def refresh_bn_f(self):
if self.connect_bn["text"] == '重新连接' or self.connect_bn["text"] == '连接设备':
messagebox.showinfo('通知', '请先连接设备')
return
x = self.phonelist_tree.get_children()
for item in x:
self.phonelist_tree.delete(item)
self.phonelist = GetPhone().usb_connect()
if self.phonelist==None:
messagebox.showinfo('通知', '无设备')
else:
for p in self.phonelist:
self.phonelist_tree.insert('', 'end', values=('', p[1], p[0]))
# adb终端
def terminal_bn_f(self):
try:
pid = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2]
os.popen('start ' + self.adb + '-s %s shell' % pid)
except:
messagebox.showinfo('通知', '请先选择设备')
# 抓取log
def log_bn_f(self):
if self.log_bn["text"] == '抓取LOG':
try:
pid = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2]
pname = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[1]
nowtime = datetime.datetime.now().strftime('%Y%m%d%H')
filename = pname + '_log_' + nowtime + ".log"
cmd1 = 'start ' + self.adb + '-s %s logcat -v time -s "tencent"' % pid
cmd2 = self.adb + '-s %s logcat -v time -s "tencent">%s' % (pid, filename)
self.thread1 = Thread(target=os.system, args=(cmd1,))
self.thread2 = Thread(target=self.sh_cmd, args=(cmd2,))
self.thread1.start()
self.thread2.start()
self.log_bn.config(text='停止抓取')
except:
messagebox.showinfo('通知', '请先选择设备')
else:
self.log_bn.config(text='抓取LOG')
#停止进程
print(self.thread2.ident)
self.stop_thread(self.thread2)
self.stop_thread(self.thread1)
# 载入所有
def choose_bn_f(self):
if self.phonelist_tree.get_children() and self.choose_bn['text'] == '载入所有':
self.choose_bn['text'] = '清空所有'
for item in self.phonelist_tree.get_children():
if self.phonelist_tree.item(item, "values")[0] == '':
self.phonelist_tree.set(item, column='#1', value=('√'))
else:
self.choose_bn['text'] = '载入所有'
for item in self.phonelist_tree.get_children():
if self.phonelist_tree.item(item, "values")[0] == '√':
self.phonelist_tree.set(item, column='#1', value=(''))
# 删除.Msg
def deleMsg_bn_f(self):
try:
pid = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2]
pname = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[1]
cmd = self.adb + '-s %s shell rm -rf sdcard/tencent/.Msg' %pid
self.run_cmd(cmd)
logging.info('设备:%s .Msg文件夹删除 成功' % pname)
except:
messagebox.showinfo('通知', '请先选择设备')
# # 闪烁手机
# def twinkle_bn_f(self):
# try:
# pid = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2]
# infos = os.popen(self.adb + "-s %s shell dumpsys power" %pid).readlines()
# for i in infos:
# if "Display Power: state=" in i:
# info = i.split("=")[1].strip("\n") # 分割,然后去掉"\n"
# if info == "OFF":
# os.popen(self.adb + ' -s %s shell input keyevent 26' % pid)
# time.sleep(1)
# cmd = self.adb + '-s %s shell settings put system screen_brightness 1' %pid
# cmd2 = self.adb + '-s %s shell settings put system screen_brightness 255' %pid
# for num in range(4):
# if (num % 2) == 0:
# os.popen(cmd)
# else:
# os.popen(cmd2)
# time.sleep(0.5)
# except:
# messagebox.showinfo('通知', '请先选择设备')
# 展示软件列表&包名
def package_bn_f(self):
try:
pid = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2]
pname = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[1]
nowtime = datetime.datetime.now().strftime('%Y%m%d')
path = 'temp\\' + pname+'_Packagelist\\'
if not os.path.exists(path):
os.makedirs(path)
filename = path + 'Packagelist_' + nowtime + ".txt"
else:
filename = path + 'Packagelist_' + nowtime + ".txt"
logcat_file = open(filename, 'w')
logcmd = self.adb + '-s {0} shell pm list packages'.format(pid)
self.pro = subprocess.Popen(logcmd, stdout=logcat_file, stderr=subprocess.PIPE)
packagelist = subprocess.getstatusoutput(logcmd)# 包列表
currentPackage = subprocess.getstatusoutput(self.adb + 'shell dumpsys window | findstr mCurrentFocus ')# 当前包名
top = tk.Toplevel(self.root)
top.wm_attributes('-topmost', 1)
top.title('--包名列表(%s)' % pname)
top.geometry('%dx%d+%d+%d' % (300, self.root.winfo_height(), self.root.winfo_x()+285, self.root.winfo_y())) # 设置窗口大小
t = tk.Text(top)
t.pack(fill=BOTH, expand=True)
t.insert('1.0', "------------------------------\n")
t.insert('2.0', "当前应用的包名及activity:\n")
t.insert('3.0', "{}\n".format(currentPackage[1]))
t.insert('4.0', "------------------------------\n")
t.insert('5.0', "所有包列表:\n")
t.insert('6.0', "{}".format(packagelist[1]))
# 插入文本,用引号引起来“2.0” 这个是插入文本的坐标,且1与0之间为点,而不是逗号,切记
top.mainloop()
except:
messagebox.showinfo('通知', '请先选择设备')
# 微信清缓存
def more_bn_f(self):
try:
pid = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2]
pname = (self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[1]
cmd = self.adb + '-s %s shell pm clear com.tencent.mm' %pid
msg = self.run_cmd(cmd)
if msg[0] == 'Success':
logging.warning('设备:%s 微信清空 成功' % pname)
except:
messagebox.showinfo('通知', '请先选择设备')
# 已连接设备的双击功能
def phonelist_Double_f(self, event):
for item in self.phonelist_tree.selection():
if self.phonelist_tree.item(item, "values")[0]=='':
self.phonelist_tree.set(item, column='#1', value=('√'))
else:
self.phonelist_tree.set(item, column='#1', value=(''))
# 设置工具标题
def tooltree_click_f(self, event):
try:
# 切换分页
self.toolpgtabControl.select(int(self.tooltree.item(self.tooltree.selection(), "values")[0])-1)
# 设置页签头
self.toolhead['text'] = (' └ '+self.tooltree.item(self.tooltree.selection(), "values")[1])
except:
pass
# 勾选工具
def tooltree_click1_f(self,event):
try:
temp_choose = self.tooltree.item(self.tooltree.selection(), "values")[1]
temp_choose_num = self.tooltree.item(self.tooltree.selection(), "values")[0]
target = self.choose_Label['text'].split(' >> ')
except:
pass
for item in self.tooltree.selection():
if temp_choose in target:
self.mytools_num.remove(temp_choose_num)
target.remove(temp_choose)
target =" >> ".join(target)
self.choose_Label['text'] = target
self.tooltree.set(item, column='#3', value=(''))
else:
self.mytools_num.append(temp_choose_num)
# 勾选状态
self.tooltree.set(item, column='#3', value=('√'))
# 目标内容
self.choose_Label['text'] = self.choose_Label['text']+temp_choose+' >> '
# 清空工具
def toolchoose_Button_f(self):
for item in self.tooltree.get_children():
if self.tooltree.item(item,"values")[2] == '√':
self.tooltree.set(item, column='#3', value=(''))
self.choose_Label['text'] = ''
#手机ID复制(ctrl+c方法)
def copy_pid_f(self, event):
logging.info('已复制到剪贴板:'+(self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2])
self.root.clipboard_clear()
self.root.clipboard_append((self.phonelist_tree.item(self.phonelist_tree.selection()[0], "values"))[2])
# 关闭程序提示
def closeWindow(self):
ans = tk.messagebox.askokcancel(title='确认', message='是否要退出程序?')
if ans:
self.root.destroy()
else:
return
# 迷你窗口
def mini_bn_f(self):
if self.root.winfo_width() > 285:
self.root.geometry('285x480')
self.mini_bn.config(text='》')
else:
alignstr = '%dx%d+%d+%d' % (
1050, 600, (self.root.winfo_screenwidth() - 1050) / 2, (self.root.winfo_screenheight() - 650) / 2)
self.root.geometry(alignstr)
self.mini_bn.config(text='—')
def _async_raise(self, tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
def stop_thread(self, thread):
self._async_raise(thread.ident, SystemExit)
# RUN按钮
def run_fun(self):
equipment = []
for item in self.phonelist_tree.get_children(): # 获取设备列表
if self.phonelist_tree.item(item, "values")[0] == '√':
equipment.append(
(self.phonelist_tree.item(item, "values")[1], self.phonelist_tree.item(item, "values")[2]))
# 检查勾选状态
if len(equipment) == 0 or len(self.mytools_num) == 0:
if len(equipment) == 0:
messagebox.showinfo('通知', '请先选择设备以及要执行的功能')
elif len(self.mytools_num) == 0:
messagebox.showinfo('通知', '请先选择要执行的功能')
if self.root.winfo_width() == 285:
alignstr = '%dx%d+%d+%d' % (
1050, 600, (self.root.winfo_screenwidth() - 1050) / 2,
(self.root.winfo_screenheight() - 650) / 2)
self.root.geometry(alignstr)
self.mini_bn.config(text='—')
return
# 检查配置情况
message = ''
for num in self.mytools_num:
if self.config[int(num)]()==None:
for item in self.tooltree.get_children():
if self.tooltree.item(item, "values")[0] == num:
message = message+'【'+self.tooltree.item(item, "values")[1]+'】'
if message != '':
messagebox.showinfo('通知', message + '\n\t功能没有配置参数')
else:
ThreadClass().thread_start(equipment, self.mytools_num, self.config)
run_fun.py:工具中的Start按钮的功能,用于启动任务的执行按钮,我单独给他写一个文件,是因为它需要给任务开启多线程,内容我暂时没有写,后续添加进去。
PS:run_fun.py的内容,已经完全写到了,main_fun.py中的
run_fun()方法
toolpg:存放每个小工具的配置页面,比如通讯录小工具,我们需要进行生成数量的设置,那么这个页面就负责与用户交互
_init.py_: 这里的py文件为 toolpg 目录中的__ init __.py文件,目的是使得当前目录变为python package目录,其中我添加了一些代码,使得当前的目录中的所有py文件,自动导包 mainpg.py 中,以至于,我每次添加新的工具时,不用在 mainpg.py 中进行import的操作
PS:目前_init_.py文件内容为空,我已经在main.py中使用了动态导
包模块‘importlib’
1_软件安装.py: 此类文件为小工具前端页面文件,生成工具的配置页面,我这里约定以此方式命名,便于我对小工具进行排序,小工具在页面上的名称。
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 23 10:25:38 2019
@author: REXLEE
"""
from tkinter import *
import tkinter.filedialog
import tkinter.ttk as ttk
class Page():
def __init__(self, master):
self.root = master
def createPage(self):
self.myFrame = Frame(self.root)
self.myFrame.place(x=20, y=50)
Label(self.myFrame,text='选择APK:').grid(row=1, column=1,pady=20)
self.entry_apk_package = ttk.Entry(self.myFrame,font=('微软雅黑', 10 ),width = 28)
self.entry_apk_package.grid(row=1, column=2)
self.Bn_search1 = ttk.Button(self.myFrame, text='浏览', command=self._Bn_search_F1)
self.Bn_search1.grid(row=1, column=3)
Label(self.myFrame,text='批量安装路径:').grid(row=2, column=1)
self.entry_apk_path = ttk.Entry(self.myFrame, font=('微软雅黑', 10), width = 28,state='disabled')
self.entry_apk_path.grid(row=2, column=2)
self.Bn_search2 = ttk.Button(self.myFrame,text='浏览', state='disabled', command = self._Bn_search_F2)
self.Bn_search2.grid(row=2, column=3)
def _Bn_search_F1(self):
self.filename = tkinter.filedialog.askopenfilename(title=u"选择文件",filetypes=[("apk格式", "apk")])
self.entry_apk_package.delete(0, END)
self.entry_apk_package.insert(0, self.filename)
def _Bn_search_F2(self):
self.filename = tkinter.filedialog.askdirectory(title=u"选择文件夹")
self.entry_apk_path.insert(0, self.filename)
def getconfig(self):
if self.entry_apk_package.get()=='' and self.entry_apk_path.get()=='':
return None
else:
return (self.entry_apk_package.get(), self.entry_apk_path.get())
2_卸载安装.py: 卸载软件的页面
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 23 10:25:38 2019
@author: REXLEE
"""
from tkinter import *
import tkinter.filedialog
import tkinter.ttk as ttk
from mods.bin_mod import bin_mod
class Page:
def __init__(self, master):
self.root = master
def createPage(self):
self.myFrame = Frame(self.root)
self.myFrame.place(x=20, y=50)
Label(self.myFrame, text='选择APK:').grid(row=1, column=1, pady=20)
self.entry_apk_package = ttk.Entry(self.myFrame, font=('微软雅黑', 10), width=28)
self.entry_apk_package.grid(row=1, column=2)
self.Bn_search1 = ttk.Button(self.myFrame, text='浏览', command=self.Bn_search_F1)
self.Bn_search1.grid(row=1, column=3)
Label(self.myFrame, text='(选择APK查看对应包名)').grid(row=2, column=2, pady=0, sticky=W)
Label(self.myFrame, text='卸载应用(包名):').grid(row=3, column=1, pady=20)
self.entry_pack_name = ttk.Entry(self.myFrame, font=('微软雅黑', 10), width=28)
self.entry_pack_name.grid(row=3, column=2)
def Bn_search_F1(self):
default_dir = r"" # 设置默认打开目录
self.filename = tkinter.filedialog.askopenfilename(title=u"选择文件",filetypes=[("apk格式", "apk")])
self.entry_apk_package.delete(0, END)
self.entry_apk_package.insert(0, self.filename)
if self.filename:
self.entry_pack_name.delete(0, END)
self.entry_pack_name.insert(0, bin_mod().package(self.filename))
def getconfig(self):
if self.entry_pack_name.get()=='':
return None
else:
return (self.entry_pack_name.get())
3_通讯录.py: 生成通讯录的页面
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 23 10:25:38 2019
@author: REXLEE
"""
from tkinter import *
import tkinter.ttk as ttk
class Page:
def __init__(self, master):
self.root = master
self.ischooes = IntVar()
def createPage(self):
self.myFrame = Frame(self.root)
self.myFrame.place(x=20, y=50)
Label(self.myFrame, text='输入生成数量:').grid(row=1, column=1, pady=20)
self.entry_vcard_num = ttk.Entry(self.myFrame, font=('微软雅黑', 10), width=28)
self.entry_vcard_num.grid(row=1, column=2)
self.checkbox_vcard = ttk.Checkbutton(self.myFrame, text='是否先清除原有通讯录')
self.checkbox_vcard.config(variable=self.ischooes, onvalue=1, offvalue=0)
self.checkbox_vcard.grid(row=2, column=1,sticky=W+E)
def getconfig(self):
if self.entry_vcard_num.get() == '':
return None
else:
return (self.entry_vcard_num.get(),self.ischooes.get())
tools:前面的toolpg目录中的类似与‘__3_通讯录.py’这种文件,主要是前端界面文件,作用是生成一个配置的页面,但页面的功能,是由tools目录中的对应工具实现
_1_tool.py:此文件与“__1_安装软件.py”对应,此文件为执行文件,真正去做软件安装的操作,而“__1_安装软件.py”只是去渲染出一个配置页面,供用户去做配置,比如安装哪一个软件,需要用户在这个页面上去选择apk文件,“_1_tool.py”文件读取到配置文件中的所选apk然后进行安装
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 5 11:20:41 2019
@author: REXLEE
"""
import logging
from mods.bin_mod import *
class toolclass:
def __init__(self, phone, config):
self.phoneid = phone[1]
self.phonename = phone[0]
self.apk = bin_mod().convert_path(config[0])
self.adb = bin_mod().adb()
self.cmd = bin_mod().run_cmd
def start(self):
logging.info('正在对设备:%s 进行软件安装操作' % self.phonename)
command = self.adb + ' -s %s install -r %s' % (self.phoneid, self.apk)
if self.cmd(command)[1] == 'Success':
logging.info('设备:%s 软件安装 成功(包名:%s)' % (self.phonename, self.apk))
else:
logging.info('设备:%s 软件安装 失败(包名:%s)' % (self.phonename, self.apk))
_2_tool.py:此文件与“__2_卸载软件.py”对应,此文件为执行文件,真正去做软件卸载的操作,而“__2_卸载软件.py”只是去渲染出一个配置页面,供用户去做配置,比如卸载哪一个软件,需要用户在这个页面上去选择apk文件(选择apk后,程序会读取出apk对应的包名,如果手机上存在此包名,就进行卸载),或者直接输入包名,“_2_tool.py”文件读取到配置文件中的包名后,进行卸载
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 5 11:20:41 2019
@author: REXLEE
"""
import logging
from mods.bin_mod import *
class toolclass:
def __init__(self, phone, config):
self.mod = bin_mod()
self.phoneid = phone[1]
self.phonename = phone[0]
self.apk = config
self.adb = self.mod.adb()
self.cmd = self.mod.run_cmd
def start(self):
logging.info('正在对设备:%s 进行软件卸载操作' % self.phonename)
command = self.adb + ' -s %s uninstall %s' % (self.phoneid, self.apk)
if self.cmd(command)[0]=='Success':
logging.info('设备:%s 软件卸载 成功(包名:%s)' % (self.phonename, self.apk))
elif self.cmd(command)[0] == 'Failure [DELETE_FAILED_INTERNAL_ERROR]':
logging.info('设备:%s 软件卸载 失败 无法卸载系统应用(包名:%s)' % (self.phonename, self.apk))
else:
logging.info('设备:%s 软件卸载 失败 应用不存在(包名:%s)' % (self.phonename, self.apk))
_2_tool.py:此文件与“__3_通讯录.py”对应。
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 5 11:20:41 2019
@author: REXLEE
"""
from mods.get_vcardfile import *
class toolclass:
def __init__(self, phone, config):
self.phoneid = phone[1]
self.phonename = phone[0]
self.num = config[0]
self.isclear = config[1]
self.adb = bin_mod().adb()
self.work_path = bin_mod.work_path
def start(self):
# 创建vcf文件
contactspath = self.__get_vcardfile()
if self.isclear == 1:
self.__vcard_clean_data()
self.__vcard_add_data(contactspath)
else:
self.__vcard_add_data(contactspath)
def __get_vcardfile(self):
get_vcardfile(self.num)
path = self.work_path + r'\temp\_vcard_temp\contacts.vcf'
contactspath = path
return contactspath
def __vcard_clean_data(self):
logging.info('正在对设备:%s 进行通讯录清空操作' % self.phonename)
command = self.adb + (' -s %s shell pm clear com.android.providers.contacts' % self.phoneid)
code = bin_mod().run_cmd(command)[0]
if code == 'Success':
logging.warning('设备:%s 通讯录清空 成功' % self.phonename)
else:
logging.warning('设备:%s 通讯录清空 失败' % self.phonename)
def __vcard_add_data(self,contactspath):
logging.info('正在对设备:%s 生成通讯录数据' % self.phonename)
command = self.adb + '-s %s push %s /sdcard/contacts.vcf' % (self.phoneid, contactspath)
code = bin_mod().run_cmd(command)[0].split(':')[1]
if code != ' error':
command = self.adb + (
' -s %s shell am start -t "text/x-vcard" -d "file:///sdcard/contacts.vcf" -a android.intent.action.VIEW com.android.contacts' %
self.phoneid)
bin_mod().run_cmd(command)[0]
logging.warning('设备:%s 通讯录数据生成 成功' % self.phonename)
else:
logging.warning('设备:%s 通讯录数据生成 失败' % self.phonename)
run.py:执行此文件即可启动程序,我写的比较简单,当然你可以在此文件中添加logging功能,进行一些其他初始化的工作
# -- coding: utf-8 --
from mods import logger_mod
from page.mainpg_fun import *
logger_mod.logger_mod()
root = Tk()
Mainpg_fun(root)
root.mainloop()
run.bat:双击执行run.py
@echo off
python run.py
当程序完成后,并且可以正常运行,即可对软件进行打包,最后生成“.exe”文件。
打包方式:
进入到run.py同级目录
执行:
pyinstaller -F -w -i=my.ico test.py;
my.ico 是一个图标名,和当前的test.py文件在同一个目录下
【注意:ico必须用专业的制ico软件制作,可用这个在线制作软件制作:http://www.bitbug.net/】
-w指令,在指令内加入-w命令可以屏蔽发布的exe应用带命令行调试窗口;
-F指令,使用-F指令可以把应用打包成一个独立的exe文件,否则是一个带各种dll和依赖文件的文件夹;
-i指令,可以自定义图标
附上github地址
https://github.com/REXLEE1995/adb-group-control.git
以上程序代码,已经可以完整的加载出一个正常的程序页面,并且页面中的一些交互功能都是正常可用的,后续将不再更新,其中的工具,可以自由扩展,界面可以自动识别并加载。
https://blog.csdn.net/VIP518600/article/details/100074420
本文适合想学习tk\ttk模块的朋友,再完全不了解的情况下,按照上方的结构进行编写,并且可以成功运行。
仅供学习,禁止商用,转载请注明出处