(python tk/ttk制作 安卓群控助手,多台设备多任务多线程执行)


仅供学习,禁止商用,转载请注明出处。

更新记录

更新时间 更新内容
20190926 重大更新,将页面重构,代码重构,交互更加方便,任务模快完成,整个程序可以正常运行,并稳定运行。
20190826 初版成型,页面布局已经完成,但任务执行模块未实现

点击跳转到 GitHub地址

简介

第一次使用python的tkinter库,一个python简单的GUI编程库
通过ADB命令去控制手机,同时使用多线程,对手机进行批量操作,如,多台设备同时安装软件,生成通讯录测试数据等。
由于公司测试组的工作需要,简单开发的安卓手机助手,有助于提升工作效率。

工具界面


  1. 手机列表:主要是获取已连接当前电脑的安卓设备;
  2. 工具列表:加载已写好的单个工具,比如软件安装、软件卸载等,可以自由扩展多个用于对设备进行控制的小工具;
  3. 工具配置:针对小工具编写的配置页面,方便进行工具参数的设定,比如生成通讯录数据的条数,安装软件的软件路径,软件卸载的包名。
  4. 执行顺序:小工具执行的执行顺序,比如先进行软件安装,再进行软件卸载,最后进行通讯录生成。

代码结构

  • PM2

    • mods

      • platform-tools
      • _ init _.py
      • bin_mod.py
      • get_vcardfile.py
      • getphone_mod.py
      • logger_mod.py
      • thread_mod.py
    • page
      • png
      • _ init _ .py
      • mainpg.py
      • mainpg_fun.py
    • toolpg
      • _ init _.py
      • 1_软件安装.py
      • 2_软件卸载.py
      • 3_通讯录.py
    • tools
      • _1_tool.py
      • _2_tool.py
      • _3_tool.py
    • run.py

代码内容

mods:存放一些公共方法,比如获取已连接的手机列表,获取本地adb工具,直接cmd命令的方法目录
platform-tools:为了能使工具在其他windows上可用,我在这里添加了一个自有的adb工具包,直接调用即可,即使本地电脑上没有装adb环境。

bin_mod.py:具体的公共方法文件,含有一系列自定义的方法

import subprocess
import os
import reclass 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 adbdef 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 linesdef package(self, apks=1):# 获取安装包的包名'''通过apk文件,获取对应的包名参数说明:<apks> 默认返回包名组'''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 packagelistelse: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 loggingfrom .bin_mod import bin_modwork_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:123@abc.com" + '\n' + \"ADR;HOME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=E5=9C=B0=E5=9D=80;;;;" + '\n' + \"END:VCARD" + '\n'return vcarddef 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_modclass GetPhone:def __init__(self):#初始化,获取已连设备信息self.adb = bin_mod().adb()#获取ADB工具self.runcmd = bin_mod().run_cmdself.equipment = []def __about_phone(self, devices):# 信息处理为可用数据if devices[1] == 'offline':pid = devices[0]model = '未知'statu = 'offline'return pid, model, statuif devices[1] == 'unauthorized':pid = devices[0]model = '未知'statu = 'unauthorized'return pid, model, statuelif 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, statuelse: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 = Noneelse:for self.devices in self.deviceslist:if self.devices == 'List of devices attached':continueelif self.devices == '':breakelse:self.equipment.append(self.__about_phone(self.devices.split()))logging.info('添加设备%s' % self.devices)logging.info('已读取设备列表%s' % self.equipment)return self.equipmentdef 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_modwork_path = bin_mod().work_pathdef 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 importlibclass 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 importlibclass 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('<Double-1>', self.phonelist_Double_f)  # 绑定左键双击事件===========self.phonelist_tree.bind('<Control-c>', 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('<ButtonRelease-1>', self.tooltree_click_f)  # 绑定左键单击事件===========self.tooltree.bind('<Double-1>', 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 tabself.tablist.append(tab)self.toolpgtabControl.pack(fill=BOTH, expand=True)self.config = {}n = 0for tabpage in self.toolpglist:tempimport = importlib.import_module('toolpg.'+tabpage)temp = tempimport.Page(self.tablist[n])temp.createPage()self.config[n+1] = temp.getconfign = n+1self.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 inspectclass Mainpg_fun(Mainpg):def __init__(self, root):super(Mainpg_fun, self).__init__(root)self.adb = bin_mod().adb()self.run_cmd = bin_mod().run_cmdself.sh_cmd = bin_mod().sh_cmdself.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='重新连接')returnself.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('通知', '请先连接设备')returnx = 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('通知', '请先选择设备')# 抓取logdef 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"' % pidcmd2 = 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=(''))# 删除.Msgdef 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' %pidself.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' %pidmsg = 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:passfor 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'] = targetself.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 ttkclass Page():def __init__(self, master):self.root = masterdef 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 Noneelse: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_modclass Page:def __init__(self, master):self.root = masterdef 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 Noneelse: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 ttkclass Page:def __init__(self, master):self.root = masterself.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 Noneelse: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_cmddef 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 = configself.adb = self.mod.adb()self.cmd = self.mod.run_cmddef 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_pathdef 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 = pathreturn contactspathdef __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模块的朋友,再完全不了解的情况下,按照上方的结构进行编写,并且可以成功运行。

仅供学习,禁止商用,转载请注明出处

python tk/ttk制作 安卓群控助手,多台设备多任务多线程执行相关推荐

  1. Python多任务(多线程执行带有参数的任务,利用threading创建线程时传入参数--args参数和kwargs参数)

    1.多线程执行带有参数的任务 以元组形式传参 以字典方式进行传参       (字典的key值和参数名要一致) 2.线程的注意点 线程之间执行是无序的 主线程会等待所有的子线程执行结束再结束 如果要主 ...

  2. 曝肝三天,两千行Python代码,制作B站视频下载工具(附源码)

    曝肝三天,两千行Python代码,制作B站视频下载工具(附源码) 文章目录 一.准备工作 二.预览 1.启动 2.解析 3.下载中 4.下载完成 5.结果 三.设计流程 1.bilibili_vide ...

  3. python人脸识别程序如何嵌入到app_只用Python就能写安卓,简单几步实现人脸识别的App...

    最近闲来无事,研究研究在安卓上跑Python. 想起以前玩过的kivy技术,kivy[1]是一个跨平台的UI框架.当然对我们最有用的是,kivy可以把python代码打包成安卓App.但是由于安卓打包 ...

  4. python利用thinker制作多页面切换的桌面应用实例教程

    本篇文章主要讲解,python利用thinker制作多页面的桌面实例教程.可以在一个主界面中相互进行切换和设置相关的表单信息,布局等方法 代码片段的实际效果 视频演示: python使用thinker ...

  5. kivy制作安卓APP--简单音乐播放器

    简述 python kivy库制作的安卓简单音乐播放器,是一个简单小demo,本来用来年会节目控制音乐流程的,分享出来给大家参考,自带kivy launch将该文件解压到根目录,安装里面的launch ...

  6. 使用Python读取和处理安卓传感器数据与CSV读取

    使用Python读取和处理安卓传感器数据与CSV读取 计算机通过从标准输入.文件和传感器中读取数据,然后将其存储在内存中进行处理.数据处理可以是在线的,也可以是离线的,这取决于用户的喜好. 数据处理是 ...

  7. 使用Python,OpenCV制作图像Mask——截取ROIs及构建透明的叠加层

    使用Python,OpenCV制作图像Mask--截取ROIs及构建透明的叠加层 1. 效果图 2. 源码 参考 这篇博客将介绍如何使用OpenCV制作Mask图像掩码.使用位运算和图像掩码允许我们只 ...

  8. python之matplotlib制作雷达图

    python之matplotlib制作雷达图 示例代码: import numpy as np import matplotlib.pyplot as plt import matplotlibmat ...

  9. python生日贺卡制作以及细节问题的解决最后把python项目发布为exe可执行程序过程

    python生日贺卡制作以及细节问题的解决最后把python项目发布为exe可执行程序过程 参考文章: (1)python生日贺卡制作以及细节问题的解决最后把python项目发布为exe可执行程序过程 ...

  10. python拿什么做可视化界面好-python爬虫如何制作可视化界面?(pyqt5环境配置篇)...

    却道天凉好个秋~ pyqt5+python+qtdesigner+pycharm制作一个可视化的GUI爬虫工具 前言: 最近闲来无聊又开始了py之路,这次需要的是制作一个贴吧的关键词搜索相关帖子的工具 ...

最新文章

  1. 【C语言】算数操作符 移位操作符 以及 sizeof单目操作符讲解
  2. python椭圆面积_利用matplotlib实现2个椭圆的重叠区域
  3. 004_URL 路由 - 对磁盘文件的请求进行路由
  4. 刚安装了Fedora 33,尝尝鲜~,哈哈~~~
  5. UA OPTI512R 傅立叶光学导论17 离散傅立叶变换简介
  6. 计数方法应用于PTB数据集
  7. 从SQL过渡至MongoDB查询对照表
  8. IBM服务器,SQLEXPRESS 服务问题
  9. linux监听报错sp2-0734,Linux中Oracle启动侦听报错TNS:permission denied的解决方法
  10. 【渝粤教育】国家开放大学2018年秋季 1374T班级管理 参考试题
  11. 僵化封闭是世界的新主流?
  12. Ubuntu下 UltraEdit 破解/显色
  13. 五款交友APP对比评测,哪款交友app好用?
  14. 风尚云网学百度地图之react中引入百度地图
  15. 举个栗子!Tableau技巧(22):如何导入其它地图源
  16. GIS应用技巧之景观格局分析(一)
  17. vue中provide和inject 用法
  18. 课程学习:让神经机器翻译模型像人类一样学习
  19. cryptography
  20. 此地不留爷,自有留爷处

热门文章

  1. Go函数和方法的区别
  2. 项目播报 | 方正璞华×联影医疗,赋能医疗行业新升级!
  3. 乔布斯斯坦福毕业演讲
  4. Python实战:将头像变成动漫风
  5. 双甜点预测的几点总结:
  6. 添加打印机计算机无法访问,Win7系统添加打印机提示Windows无法打开“添加打印机”的解决方法...
  7. The conda.compat module is deprecated and will be removed in a future releas错误记录
  8. android 调用百度翻译API 实现在线翻译
  9. ktv点歌系统服务器破解,欧凯KTV卡拉OK点歌系统
  10. c语言 英文歌曲大赛,英文歌曲大赛活动方案