2 Star 7 Fork 3

qq385723790 / adb-group-control

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

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

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

更新记录

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

简介

第一次使用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 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文件,获取对应的包名
        参数说明:
            <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 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:123@abc.com" + '\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('<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 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指令,可以自定义图标

但以上程序代码,已经可以完整的加载出一个正常的程序页面,并且页面中的一些交互功能都是正常可用的,后续将不再更新,其中的工具,可以自由扩展,界面可以自动识别并加载。 https://blog.csdn.net/VIP518600/article/details/100074420

本文适合想学习tk\ttk模块的朋友,再完全不了解的情况下,按照上方的结构进行编写,并且可以成功运行。

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

空文件

简介

基于adb可用功能的安卓群控助手 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/qq385723790/adb-group-control.git
git@gitee.com:qq385723790/adb-group-control.git
qq385723790
adb-group-control
adb-group-control
master

搜索帮助