'''
2019-03-21
程序运行说明
测试部分:通过windows自身ping程序运行60秒获取结果,保存为本地文本文件(日志文件)
画图部分:处理保存在本地的日志文件,画成图显示
使用说明
运行平台:Windows7 & Python3.x
依赖第三方包:matplotlib 包(画图要用到)
安装第三方包:
打开 CMD 输入命令 pip install matplotlib 会自动安装 matplotlib 包
BUG:
0、双击运行无效,请使用python IDLE打开,再F5运行
1、测试运行后,如果直接关闭GUI窗口,可能造成后台进程不能终止。请点击停止按钮,等待程序关闭后,再关闭窗口
2、用户输入的参数没有限制类型、范围,不要乱输入
'''
import tkinter
import pickle
import re
import subprocess
from threading import Thread
import time
import os
import time
import logging # 日志
Log = logging.getLogger("__name__") # 获取实例
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') # 指定logger输出格式
file_handler = logging.FileHandler("net.log") # 日志文件路径
file_handler.setFormatter(formatter) # 可以通过setFormatter指定输出格式
Log.addHandler(file_handler) # 为logger添加的日志处理器
Log.setLevel(logging.DEBUG) # 设置记录的日志级别
## 程序运行控制的全局字典变量:'S'控制ping程序运行,'测试按钮计数'控制按钮行为
D_S = {'S':1,'测试按钮计数':1}
#print(D_S)
## 开始测试,把测试结果保存到日志文件(开始测试按钮操作函数)
def PING_TEST():
#print(D_S)
if D_S['测试按钮计数'] %2 != 0:
D_S['测试按钮计数'] += 1
##print("START 开始测试...")
text3.insert(1.0, 'START 开始测试...\n')
D_S['S'] = 1 # 设置为1 允许测试程序运行
K1 = 输入框2.get() # 获取输入框的内容
V1 = 输入框1.get()
K2 = 输入框4.get()
V2 = 输入框3.get()
K3 = 输入框6.get()
V3 = 输入框5.get()
K4 = 输入框8.get()
V4 = 输入框7.get()
D_任务 = {} # 准备进行PING测试的任务字典 {'备注':'IP'}
if V1:
if K1:
D_任务[K1] = V1
else:
K1 = V1
D_任务[K1] = V1
if V2:
if K2:
D_任务[K2] = V2
else:
K2 = V2
D_任务[K2] = V2
if V3:
if K3:
D_任务[K3] = V3
else:
K3 = V3
D_任务[K3] = V3
if V4:
if K4:
D_任务[K4] = V4
else:
K4 = V4
D_任务[K4] = V4
#print("准备进行PING测试的任务字典",D_任务)
text1.delete(1.0, tkinter.END) # 清除文本框内容
if len(D_任务) == 0:
text1.insert(1.0, '请先输入要测试的IP地址') # 写入内容
D_S['S'] = 1
D_S['测试按钮计数'] = 1
else:
for i in D_任务:
text1.insert(1.0, '测试任务 ' + i + ' ' + D_任务[i] + '\n')
text1.insert(5.0, '测试数据保存在 net.log 文件中\n')
text1.insert(6.0, '测试程序运行中...\n')
f = open('PING_TEST_INFO.pkl', 'wb') # 保存到文件,让后续画图程序读取
pickle.dump(D_任务, f)
f.close()
def ping(IP):
while D_S['S']:
##print("线程开始",IP)
###text3.insert(tkinter.END, '线程开始 '+IP+'\n')
###time.sleep(5)
#print("程序开始", time.time())
cmd = 'ping -t ' + IP # 长PING测试
win = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
#print(IP, "子进程PID", win.pid)
#print("程序暂停", time.time())
time.sleep(60) # 每60秒终止长PING进程,win会计算本次测试结果
#print("暂停结束", time.time())
#print("开始KILL", time.time())
os.popen('taskkill /pid:' + str(win.pid)) # windows 需要用自身的命令程序杀死进程
#print("完成KILL", time.time())
R = win.stdout.read()
#print("程序完成",time.time())
#print("输出内容")
#print(R.decode('GBK'))
TXT = R.decode('GBK')
丢包率 = re.search('[0-9]+%',TXT)
延时统计 = re.search('最短(.*)',TXT)
#print("丢包率",丢包率.group())
#print("延时统计",延时统计.group())
if not 延时统计:
if 丢包率:
INFO = IP + ' ' + 丢包率.group() + ' 最短 = 0ms,最长 = 0ms,平均 = 0ms'
Log.info(INFO) # 记录到日志
print('100% lost')
else:
print('ERROR',TXT)
else:
INFO = IP + ' ' + 丢包率.group() + ' ' + 延时统计.group() # 保存到INFO,准备记录到日志
Log.info(INFO) # 记录到日志
#print(INFO)
if D_S['S'] == 1:
##print(IP," 测试中\n")
text3.insert(tkinter.END, IP + ' 测试中\n')
else:
##print(IP," 测试终止\n")
text3.insert(tkinter.END, IP + ' 测试终止\n')
任务IP列表 = []
for i in D_任务:
##print(D_任务[i])
任务IP列表.append(D_任务[i])
任务总数 = len(任务IP列表)
if 任务总数 == 1:
t0=Thread(target=ping,args=(任务IP列表[0],))
t0.start()
elif 任务总数 == 2:
t0=Thread(target=ping,args=(任务IP列表[0],))
t1=Thread(target=ping,args=(任务IP列表[1],))
t0.start()
t1.start()
elif 任务总数 == 3:
t0=Thread(target=ping,args=(任务IP列表[0],))
t1=Thread(target=ping,args=(任务IP列表[1],))
t2=Thread(target=ping,args=(任务IP列表[2],))
t0.start()
t1.start()
t2.start()
elif 任务总数 == 4:
t0=Thread(target=ping,args=(任务IP列表[0],))
t1=Thread(target=ping,args=(任务IP列表[1],))
t2=Thread(target=ping,args=(任务IP列表[2],))
t3=Thread(target=ping,args=(任务IP列表[3],))
t0.start()
t1.start()
t2.start()
t3.start()
else:
##print("ERROR 任务总数范围不在 1到4")
text3.insert(tkinter.END, 'ERROR 任务总数范围不在 1到4 \n')
else:
D_S['测试按钮计数'] += 1
##print("STOP 停止中...")
text3.insert(tkinter.END, 'STOP 停止中... \n')
D_S['S'] = 0
text1.delete(1.0, tkinter.END)
text1.insert(1.0, '测试数据保存在 net.log 文件中\n')
text1.insert(2.0, '测试程序 终止\n')
'''
cmd_find_ping = os.popen('tasklist | findstr -i PING') ## 通过Windows自身命令找到PING进程信息
cmd_txt = cmd_find_ping.read()
PING_INFO_LIST = re.findall('PING(.*)',cmd_txt) ## 用re处理成列表,方便接下来使用
print("找到数量",len(PING_INFO_LIST))
#print("内容",PING_INFO_LIST)
for i in PING_INFO_LIST:
#print("每行内容",i)
print("进程ID",i.split()[1],"开始结束进程")
os.popen('taskkill.exe /F /pid:' + str(i.split()[1]))
'''
## 把测试记录画成图查看(画图分析按钮操作函数)
def INFO_IMG():
D_复选 = {'PLR':0, 'MAX':0, 'AVG':0}
text2.delete(1.0, tkinter.END)
if V1.get() == 1:
D_复选['PLR'] = 1
text2.insert(1.0, '在图中显示 丢包率\n')
if V2.get() == 1:
D_复选['MAX'] = 1
text2.insert(2.0, '在图中显示 最大延时\n')
if V3.get() == 1:
D_复选['AVG'] = 1
text2.insert(3.0, '在图中显示 平均延时\n')
if V1.get() + V2.get() + V3.get() == 0:
print("用户无选择,无Y轴可画")
text2.insert(1.0, '【警告】Y轴无内容,请勾选显示内容:丢包率/最大延时/平均延时\n')
#print("D_复选 设置结果", D_复选)
X轴刻度设置 = 输入框9.get()
if not X轴刻度设置:
X轴密度降低倍数 = 1
else:
X轴密度降低倍数 = int(X轴刻度设置)
text2.insert(4.0, 'X轴刻度设置:每' + str(X轴密度降低倍数) + '分钟一个刻度\n')
单选结果 = V.get()
if 单选结果 == 0:
text2.insert(5.0, '子图排列方式:并排显示子图\n')
else:
text2.insert(5.0, '子图排列方式:并列显示子图\n')
#print("子图排列方式:单选结果",V.get())
## 分离各IP数据
D_各IP信息 = {} # {'GW':[[TIME],[PLR],[MAX],[AVG]]}
def IPs(F,IP,TXT):
IP_INFO = ''
re_txt = '(.*)' + IP + '(.*)'
X = re.finditer(re_txt, TXT)
L_Time = [] # 时间列表
for i in X:
T = i.group()
IP_INFO += T+'\n'
L_Time.append(T[11:16]) # 元素类型字符串 '时间点'
##print(F,"TEXT done")
##print(F,"TIME done")
#print("L_Time \n",L_Time,"\n")
#print(IP_INFO)
L_PLR = [] # 丢包率
L_MAX = [] # 最大延时
L_AVG = [] # 平均延时
MS = re.findall('[0-9]+ms',IP_INFO)
PLR = re.findall('[0-9]+%',IP_INFO)
for x in PLR:
L_PLR.append(int(x[:-1])) # 取%前的值,再转成int,保存到列表
##print(F,"PLR done")
for y in range(1,len(MS),3):
L_MAX.append(int(MS[y][:-2])) # 去掉末尾ms再转int
##print(F,"MAX done")
#print("L_MAX \n",L_MAX,"\n")
for z in range(2,len(MS),3):
L_AVG.append(int(MS[z][:-2]))
##print(F,"AVG done")
#print("L_AVG \n",L_AVG,"\n")
LLLL = [L_Time, L_PLR, L_MAX, L_AVG]
D_各IP信息[F] = LLLL
## 读取测试记录文件
F_Src = 'net.log'
f = open(F_Src, 'r')
TXT = f.read() # 全部内容保存到内存变量TXT
#print(TXT,'\n')
f.close()
## 从文件读取要画图的信息
f = open('PING_TEST_INFO.pkl', 'rb')
D = pickle.load(f) # D = {'GW':'192.168.1.1', 'DNS':'114.114.114.114', 'USA':'8.8.8.8'}
##print("本次画图内容",type(D), D)
f.close()
for i in D:
text2.insert(6.0, '开始画图:' + i + ' ' + D[i] + '\n')
IPs(i, D[i], TXT) # 传入新文件名,要分离的IP数据,整个源文件内容
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
## 为了作图方便,把字典的key单独存为一个列表
L = [] # L = ['GW','DNS','USA']
for i in D:
L.append(i)
# 画出图形
图数量 = len(L)
if 图数量 > 1:
if 单选结果 == 0:
fig, ax = plt.subplots(1,图数量, sharex=True, sharey=True) # 1行多列(并排显示子图),统一X和Y轴刻度,方便横向对比
else:
fig, ax = plt.subplots(图数量,1, sharex=True, sharey=True) # 多行1列(并列显示子图),统一X和Y轴刻度,方便纵向对比
AX_List = ax.ravel() # 子图列表:AX_List[0]第一个子图,AX_List[1]第二个子图...
for i in range(0, 图数量):
L_L = D_各IP信息[L[i]] # L_L内容 = [时间列表,丢包率列表,最大延时列表,平均延时列表]
L_TIME = L_L[0] # 时间作为X轴
L_PLR = L_L[1] # 丢包率作为其中一个Y轴,单位%(百分比)
L_MAX = L_L[2] # 最大延时为其中一个Y轴,单位(ms)
L_AVG = L_L[3] # 平均延时为其中一个Y轴,单位(ms)
if D_复选['PLR'] == 1:
AX_List[i].plot(L_TIME,L_PLR,label='PLR(%)') # 在图中画出丢包率(范围0-100)
if D_复选['MAX'] == 1:
AX_List[i].plot(L_TIME,L_MAX,label='MAX(ms)') # 在图中画出最大延时,可以看波动幅度,但遇到太大的会看不清其他值
if D_复选['AVG'] == 1:
AX_List[i].plot(L_TIME,L_AVG,label='AVG(ms)') # 在图中画出平均延时
AX_List[i].xaxis.set_major_locator(ticker.MultipleLocator(X轴密度降低倍数)) # 当X轴取值太多时会显示太密集,这里设置每30个值显示1个
AX_List[i].legend() # 显示图例label
AX_List[i].set_title(L[i]) # 设置每个子图各自标题
if 单选结果 == 0: # 如果并排显示
AX_List[i].set_xlabel('Time') # 每个图显示X轴标识
#AX_List[i].set_ylabel('MS or %') # 设置Y轴标识
if 单选结果 == 1: # 如果并列显示
plt.xlabel('Time') # 只让最后一张图显示X轴标识
plt.show() # 显示制作好的图
elif 图数量 == 1:
L_L = D_各IP信息[L[0]]
L_TIME = L_L[0]
L_PLR = L_L[1]
L_MAX = L_L[2]
L_AVG = L_L[3]
fig, ax = plt.subplots(1,1)
if D_复选['PLR'] == 1:
ax.plot(L_TIME,L_PLR,label='PLR(%)')
if D_复选['MAX'] == 1:
ax.plot(L_TIME,L_MAX,label='MAX(ms)')
if D_复选['AVG'] == 1:
ax.plot(L_TIME,L_AVG,label='AVG(ms)')
ax.xaxis.set_major_locator(ticker.MultipleLocator(X轴密度降低倍数))
plt.legend()
plt.xlabel('Time')
plt.title(L[0])
plt.show()
else:
##print("图数量 ERROR")
text3.insert(tkinter.END, '图数量 ERROR \n')
## GUI 界面设置
top = tkinter.Tk()
top.wm_title("Windows PING") # 图形窗口标题
top.geometry("720x650+50+60") # 图像窗口初始大小及位置
## 框架布局
frame_root = tkinter.Frame(top)
frame_1 = tkinter.Frame(frame_root)
frame_2 = tkinter.Frame(frame_root)
frame_3 = tkinter.Frame(frame_root)
frame_4 = tkinter.Frame(frame_root)
frame_5 = tkinter.Frame(frame_root)
frame_6 = tkinter.Frame(frame_root)
frame_7 = tkinter.Frame(frame_root)
## 测试部分的用户参数处理
测试 = tkinter.Label(frame_1,text="第一步,测试网络").grid(row=0,column=0,columnspan=4,sticky='W')
备注1 = tkinter.Label(frame_2,text="IP1:").grid(row=1,column=0,sticky='W')
备注2 = tkinter.Label(frame_2,text="备注").grid(row=1,column=2,sticky='W')
备注3 = tkinter.Label(frame_2,text="IP2:").grid(row=2,column=0,sticky='W')
备注4 = tkinter.Label(frame_2,text="备注").grid(row=2,column=2,sticky='W')
备注5 = tkinter.Label(frame_2,text="IP3:").grid(row=3,column=0,sticky='W')
备注6 = tkinter.Label(frame_2,text="备注").grid(row=3,column=2,sticky='W')
备注7 = tkinter.Label(frame_2,text="IP4:").grid(row=4,column=0,sticky='W')
备注8 = tkinter.Label(frame_2,text="备注").grid(row=4,column=2,sticky='W')
输入框1 = tkinter.Entry(frame_2)
输入框2 = tkinter.Entry(frame_2)
输入框3 = tkinter.Entry(frame_2)
输入框4 = tkinter.Entry(frame_2)
输入框5 = tkinter.Entry(frame_2)
输入框6 = tkinter.Entry(frame_2)
输入框7 = tkinter.Entry(frame_2)
输入框8 = tkinter.Entry(frame_2)
输入框1.grid(row=1,column=1,sticky='W')
输入框2.grid(row=1,column=3,sticky='W')
输入框3.grid(row=2,column=1,sticky='W')
输入框4.grid(row=2,column=3,sticky='W')
输入框5.grid(row=3,column=1,sticky='W')
输入框6.grid(row=3,column=3,sticky='W')
输入框7.grid(row=4,column=1,sticky='W')
输入框8.grid(row=4,column=3,sticky='W')
测试按钮 = tkinter.Button(frame_3,text='开始测试/结束测试',command=PING_TEST).grid(row=5,column=0,columnspan=4,sticky='W')
text1 = tkinter.Text(frame_3,width='50',height=9)
text1.grid(row=6,column=0,columnspan=4,sticky='W')
## 画图部分的用户参数处理
画图部分 = tkinter.Label(frame_4,text="第二步,画图分析结果").grid(row=15,column=0,sticky='W')
tkinter.Label(frame_5,text="Y轴显示内容选择 ").grid(row=0,column=0,sticky='W')
tkinter.Label(frame_5,text="多图排列方式选择 ").grid(row=0,column=1,sticky='W')
tkinter.Label(frame_5,text="X轴刻度设置").grid(row=0,column=2,columnspan=3,sticky='W')
V1 = tkinter.IntVar()
V2 = tkinter.IntVar()
V3 = tkinter.IntVar()
复选1 = tkinter.Checkbutton(frame_5,text='显示丢包率', variable=V1)
复选1.select() # 默认选中
复选1.grid(row=1,column=0,sticky='W')
复选2 = tkinter.Checkbutton(frame_5,text='显示最大延时', variable=V2)
复选2.select() # 默认选中
复选2.grid(row=2,column=0,sticky='W')
复选3 = tkinter.Checkbutton(frame_5,text='显示平均延时', variable=V3)
复选3.select() # 默认选中
复选3.grid(row=3,column=0,sticky='W')
tkinter.Label(frame_5,text="每",width=2).grid(row=1,column=2,sticky='W')
输入框9 = tkinter.Entry(frame_5,width=3)
输入框9.grid(row=1,column=3,sticky='W')
tkinter.Label(frame_5,text="分钟一个刻度").grid(row=1,column=4,sticky='W')
tkinter.Label(frame_5,text="默认值:1").grid(row=2,column=2,columnspan=3,sticky='W')
V = tkinter.IntVar()
## value=0 默认选中
单选1 = tkinter.Radiobutton(frame_5,text="并排显示", value=0, variable=V).grid(row=1, column=1, sticky='W')
单选2 = tkinter.Radiobutton(frame_5,text="并列显示", value=1, variable=V).grid(row=2, column=1, sticky='W')
画图按钮 = tkinter.Button(frame_6,text='画图分析',command=INFO_IMG).grid(row=0,column=0,sticky='W')
text2 = tkinter.Text(frame_6,width='50',height=9)
text2.grid(row=1,column=0,sticky='W')
text3 = tkinter.Text(frame_7,width='50',height=42)
text3.grid(row=0,column=0,sticky='W')
## 框架的位置布局
frame_1.grid(row=0,column=0,sticky='W')
frame_2.grid(row=1,column=0,sticky='W')
frame_3.grid(row=2,column=0,sticky='W')
frame_4.grid(row=3,column=0,sticky='W')
frame_5.grid(row=4,column=0,sticky='W')
frame_6.grid(row=5,column=0,sticky='W')
frame_7.grid(row=0,column=1,rowspan=6,sticky='WN')
frame_root.grid(row=0,column=0,sticky='W')
top.mainloop()