'''
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()