Display a periodically updated graphical ping time

This commit is contained in:
MikeTheWatchGuy 2018-08-30 09:05:59 -04:00
parent 102322822b
commit b0c6ff3076
1 changed files with 115 additions and 175 deletions

View File

@ -9,154 +9,11 @@ import tkinter as tk
A graph of time to ping Google.com
Demonstrates Matploylib used in an animated way.
Note this file contains a copy of ping.py. Here is some information about it.
If you scroll past the PySimpleGUI code, you will find 100% of the original file
has been copied to this file.
A pure python ping implementation using raw sockets.
(This is Python 3 port of https://github.com/jedie/python-ping)
(Tested and working with python 2.7, should work with 2.6+)
Note that ICMP messages can only be sent from processes running as root
(in Windows, you must run this script as 'Administrator').
Derived from ping.c distributed in Linux's netkit. That code is
copyright (c) 1989 by The Regents of the University of California.
That code is in turn derived from code written by Mike Muuss of the
US Army Ballistic Research Laboratory in December, 1983 and
placed in the public domain. They have my thanks.
Bugs are naturally mine. I'd be glad to hear about them. There are
certainly word - size dependencies here.
Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
Distributable under the terms of the GNU General Public License
version 2. Provided with no warranties of any sort.
Original Version from Matthew Dixon Cowles:
-> ftp://ftp.visi.com/users/mdc/ping.py
Rewrite by Jens Diemer:
-> http://www.python-forum.de/post-69122.html#69122
Rewrite by George Notaras:
-> http://www.g-loaded.eu/2009/10/30/python-ping/
Enhancements by Martin Falatic:
-> http://www.falatic.com/index.php/39/pinging-with-python
Enhancements and fixes by Georgi Kolev:
-> http://github.com/jedie/python-ping/
Bug fix by Andrejs Rozitis:
-> http://github.com/rozitis/python-ping/
Note this file contains a copy of ping.py. It is contained in the first part of this file
"""
#================================================================================
# Globals
# These are needed because callback functions are used.
# Need to retain state across calls
#================================================================================
class MyGlobals:
axis_pings = None
ping_x_array = []
ping_y_array = []
g_my_globals = MyGlobals()
#================================================================================
# Performs *** PING! ***
#================================================================================
def run_a_ping_and_graph():
global g_my_globals # graphs are global so that can be retained across multiple calls to this callback
#===================== Do the ping =====================#
response = quiet_ping('google.com',timeout=1000)
if response[0] == 0:
ping_time = 1000
else:
ping_time = response[0]
#===================== Store current ping in historical array =====================#
g_my_globals.ping_x_array.append(len(g_my_globals.ping_x_array))
g_my_globals.ping_y_array.append(ping_time)
# ===================== Only graph last 100 items =====================#
if len(g_my_globals.ping_x_array) > 100:
x_array = g_my_globals.ping_x_array[-100:]
y_array = g_my_globals.ping_y_array[-100:]
else:
x_array = g_my_globals.ping_x_array
y_array = g_my_globals.ping_y_array
# ===================== Call graphinc functions =====================#
g_my_globals.axis_ping.clear() # clear before graphing
g_my_globals.axis_ping.plot(x_array,y_array) # graph the ping values
#================================================================================
# Function: Set graph titles and Axis labels
# Sets the text for the subplots
# Have to do this in 2 places... initially when creating and when updating
# So, putting into a function so don't have to duplicate code
#================================================================================
def set_chart_labels():
global g_my_globals
g_my_globals.axis_ping.set_xlabel('Time')
g_my_globals.axis_ping.set_ylabel('Ping (ms)')
g_my_globals.axis_ping.set_title('Current Ping Duration', fontsize = 12)
def draw(fig, canvas):
# Magic code that draws the figure onto the Canvas Element's canvas
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(640 / 2, 480 / 2, image=photo)
figure_canvas_agg = FigureCanvasAgg(fig)
figure_canvas_agg.draw()
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
#================================================================================
# Function: MAIN
#================================================================================
def main():
global g_my_globals
canvas_elem = g.Canvas(size=(640, 480)) # get the canvas we'll be drawing on
# define the form layout
layout = [[g.Text('Animated Ping', size=(40,1), justification='center', font='Helvetica 20')],
[canvas_elem],
[g.ReadFormButton('Exit', size=(10,2), pad=((280, 0), 3), font='Helvetica 14')]]
# create the form and show it without the plot
form = g.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI')
form.Layout(layout)
form.ReadNonBlocking()
canvas = canvas_elem.TKCanvas
fig = plt.figure()
g_my_globals.axis_ping = fig.add_subplot(1,1,1)
set_chart_labels()
plt.tight_layout()
while True:
button, values = form.ReadNonBlocking()
if button is 'Exit' or values is None:
break
run_a_ping_and_graph()
photo = draw(fig, canvas)
if __name__ == '__main__':
main()
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A pure python ping implementation using raw sockets.
@ -661,20 +518,19 @@ def verbose_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
dump_stats(myStats)
# =============================================================================#
#=============================================================================#
def quiet_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
packet_size=PACKET_SIZE, path_finder=False):
"""
Same as verbose_ping, but the results are returned as tuple
"""
myStats = MyStats() # Reset the stats
mySeqNumber = 0 # Starting value
myStats = MyStats() # Reset the stats
mySeqNumber = 0 # Starting value
try:
destIP = socket.gethostbyname(hostname)
except socket.gaierror as e:
return False
return 0,0,0,0
myStats.thisIP = destIP
@ -684,12 +540,12 @@ def quiet_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
if path_finder:
fakeStats = MyStats()
do_one(fakeStats, destIP, hostname, timeout,
mySeqNumber, packet_size, quiet=True)
mySeqNumber, packet_size, quiet=True)
time.sleep(0.5)
for i in range(count):
delay = do_one(myStats, destIP, hostname, timeout,
mySeqNumber, packet_size, quiet=True)
mySeqNumber, packet_size, quiet=True)
if delay == None:
delay = 0
@ -698,41 +554,125 @@ def quiet_ping(hostname, timeout=WAIT_TIMEOUT, count=NUM_PACKETS,
# Pause for the remainder of the MAX_SLEEP period (if applicable)
if (MAX_SLEEP > delay):
time.sleep((MAX_SLEEP - delay) / 1000)
time.sleep((MAX_SLEEP - delay)/1000)
if myStats.pktsSent > 0:
myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd) / myStats.pktsSent
myStats.fracLoss = (myStats.pktsSent - myStats.pktsRcvd)/myStats.pktsSent
if myStats.pktsRcvd > 0:
myStats.avrgTime = myStats.totTime / myStats.pktsRcvd
# return tuple(max_rtt, min_rtt, avrg_rtt, percent_lost)
return myStats.maxTime, myStats.minTime, myStats.avrgTime, myStats.fracLoss
# =============================================================================#
def main():
parser = argparse.ArgumentParser(description=__description__)
parser.add_argument('-q', '--quiet', action='store_true',
help='quiet output')
parser.add_argument('-c', '--count', type=int, default=NUM_PACKETS,
help=('number of packets to be sent '
'(default: %(default)s)'))
parser.add_argument('-W', '--timeout', type=float, default=WAIT_TIMEOUT,
help=('time to wait for a response in seoncds '
'(default: %(default)s)'))
parser.add_argument('-s', '--packet-size', type=int, default=PACKET_SIZE,
help=('number of data bytes to be sent '
'(default: %(default)s)'))
parser.add_argument('destination')
# args = parser.parse_args()
ping = verbose_ping
# if args.quiet:
# ping = quiet_ping
ping('Google.com', timeout=1000)
# ping(args.destination, timeout=args.timeout*1000, count=args.count,
# packet_size=args.packet_size)
#================================================================================
# Globals
# These are needed because callback functions are used.
# Need to retain state across calls
#================================================================================
SIZE=(320,240)
class MyGlobals:
axis_pings = None
ping_x_array = []
ping_y_array = []
g_my_globals = MyGlobals()
#================================================================================
# Performs *** PING! ***
#================================================================================
def run_a_ping_and_graph():
global g_my_globals # graphs are global so that can be retained across multiple calls to this callback
#===================== Do the ping =====================#
response = quiet_ping('google.com',timeout=1000)
if response[0] == 0:
ping_time = 1000
else:
ping_time = response[0]
#===================== Store current ping in historical array =====================#
g_my_globals.ping_x_array.append(len(g_my_globals.ping_x_array))
g_my_globals.ping_y_array.append(ping_time)
# ===================== Only graph last 100 items =====================#
if len(g_my_globals.ping_x_array) > 100:
x_array = g_my_globals.ping_x_array[-100:]
y_array = g_my_globals.ping_y_array[-100:]
else:
x_array = g_my_globals.ping_x_array
y_array = g_my_globals.ping_y_array
# ===================== Call graphinc functions =====================#
g_my_globals.axis_ping.clear() # clear before graphing
set_chart_labels()
g_my_globals.axis_ping.plot(x_array,y_array) # graph the ping values
#================================================================================
# Function: Set graph titles and Axis labels
# Sets the text for the subplots
# Have to do this in 2 places... initially when creating and when updating
# So, putting into a function so don't have to duplicate code
#================================================================================
def set_chart_labels():
global g_my_globals
g_my_globals.axis_ping.set_xlabel('Time', fontsize=8)
g_my_globals.axis_ping.set_ylabel('Ping (ms)', fontsize=8)
g_my_globals.axis_ping.set_title('Current Ping Duration', fontsize = 8)
def draw(fig, canvas):
# Magic code that draws the figure onto the Canvas Element's canvas
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)
canvas.create_image(SIZE[0] / 2, SIZE[1] / 2, image=photo)
figure_canvas_agg = FigureCanvasAgg(fig)
figure_canvas_agg.draw()
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
return photo
#================================================================================
# Function: MAIN
#================================================================================
def main():
global g_my_globals
canvas_elem = g.Canvas(size=SIZE, background_color='white') # get the canvas we'll be drawing on
# define the form layout
layout = [[canvas_elem],
[g.ReadFormButton('Exit', size=(4,1), pad=((130, 0), 3))]]
# create the form and show it without the plot
form = g.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI', background_color='white')
form.Layout(layout)
form.ReadNonBlocking()
canvas = canvas_elem.TKCanvas
fig = plt.figure(figsize=(3.1, 2.25), tight_layout={'pad':0})
g_my_globals.axis_ping = fig.add_subplot(1,1,1)
plt.rcParams['xtick.labelsize'] = 8
plt.rcParams['ytick.labelsize'] = 8
set_chart_labels()
plt.tight_layout()
while True:
button, values = form.ReadNonBlocking()
if button is 'Exit' or values is None:
exit(0)
run_a_ping_and_graph()
photo = draw(fig, canvas)
if __name__ == '__main__':
main()
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
if __name__ == '__main__':
main()