19 Commits

Author SHA1 Message Date
96aca5bfd5 Forcing pings to be 1px minimum 2014-07-16 13:15:07 -07:00
4d95ddafec Handling errors, using /tmp 2014-01-18 01:00:08 -07:00
5fe62962eb Removing image in order to avoid write issues 2014-01-12 10:31:53 -07:00
3562257540 Adding numerical ping value 2014-01-09 02:02:26 -07:00
d5b10a498d Adding graphing 2014-01-08 08:09:28 -07:00
09d5a3202a Removing ping debug output from terminal 2014-01-08 03:34:54 -07:00
4c9c3a5c22 Fixing unresponsive Autostart label
Is there a reason for "try with open" over os.path.exists? The issue was separate, but in the course of debugging I switched back to the old style.
2014-01-08 03:26:53 -07:00
6d6b50c3af Merge pull request #6 from altf4/master
Pull altf4's improvements in
2014-01-08 02:13:46 -08:00
70f7357a3a Merge pull request #2 from altf4/missing_path
Missing path
2014-01-08 02:00:49 -08:00
fd518eb4b4 Merge pull request #1 from altf4/args
Add support for command line arguments
2014-01-08 01:57:31 -08:00
AltF4
d29e77dc85 Fix for mismatched Paused/Resume toggle labels
They were set backwards. Fixed.
2014-01-04 23:48:22 -07:00
AltF4
9df9559c9c Made latency labels static size to prevent shifting
Latencies displayed in the widget can be of different precisions, causing
  the widget to redraw when the precision changes. It's a little annoying.
  This makes latency precision static at 2 decimal places.
2014-01-04 23:01:06 -07:00
AltF4
2be6c388ad Fix autostart toggle bug, consolidated some functions
Fixed toggling autostart which was broken last commit.

Consolidated some functions which were only called from one place and
  honestly work more elegantly when merged together.
2014-01-04 22:36:18 -07:00
AltF4
fc6f5bdbcf Added Pause option, fixed bug with accumulating event triggers
Pause option added. So you can easily toggle whether pings are going out
  without having to fully kill pinger.

Fixed a bug where toggling the autostart option would cumulatively add
  new event handlers, eventually making the program unresponsive after
  several toggles.
2014-01-04 22:00:47 -07:00
AltF4
f09333305b Fix bug where .desktop file can't be made due to missing autostart directory
Added a check for ~/.config/autostart/ If if doesn't exist, make it.
2014-01-04 20:13:51 -07:00
AltF4
c74a5198c5 Fix for bad autostart command
Was missing a space between ./pinger.py and the first argument
2014-01-04 20:06:43 -07:00
AltF4
fdb23df9c0 Add support for command line arguments
Used argparse to enable command line arguments for
  user supplied options. Defaults values have been
  kept, so behavior with no arguments is the same.
  Should be easier than modifying the source each
  time this way.

Also added a gitignore file that is the standard
  github python gitignore, plus *~ to handle
  deleted files.
2014-01-04 19:43:13 -07:00
a896903aac Ping graph! 2013-12-17 05:50:58 -07:00
91e91128f9 Fixing issue when there are no ping matches 2013-12-16 13:48:47 -07:00
2 changed files with 235 additions and 40 deletions

56
.gitignore vendored Normal file
View File

@@ -0,0 +1,56 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
# Sphinx documentation
docs/_build/
#deleted files
*~
# Temporary graph file
graph.png

219
pinger.py Normal file → Executable file
View File

@@ -4,7 +4,8 @@
# Pinger.py -- A ping tool that sits in your system tray # Pinger.py -- A ping tool that sits in your system tray
# Copyright 2013 Will Bradley # Copyright 2013 Will Bradley
# #
# Authors: Will Bradley <bradley.will@gmail.com> # Contributors: Will Bradley <bradley.will@gmail.com>
# AltF4 <altf4@phx2600.org>
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of either or both of the following licenses: # under the terms of either or both of the following licenses:
@@ -25,10 +26,6 @@
# <http://www.gnu.org/licenses/> # <http://www.gnu.org/licenses/>
# #
# User-editable variables
host = "4.2.2.2" # IP or hostname
ping_frequency = 5 # in seconds
# #
# Dependencies # Dependencies
# #
@@ -46,39 +43,118 @@ import re
import signal import signal
# File paths # File paths
import os import os
# Argument parsing
import argparse
# For exit
import sys
# For graphing
import cairo
# Vars # Vars
startup_active_label = "✓ Start Automatically" startup_active_label = "✓ Start Automatically"
startup_inactive_label = "Start Automatically" startup_inactive_label = "Start Automatically"
pause_label = "Pause"
play_label = "Resume"
home_path = os.path.expanduser("~") home_path = os.path.expanduser("~")
startup_path = home_path+'/.config/autostart/pinger.desktop' startup_path = home_path+'/.config/autostart/pinger.desktop'
startup_dir = home_path+'/.config/autostart/'
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--target", help="Target to PING against. (IP / Hostname / Domain name). Defaults to 4.2.2.2")
parser.add_argument("-f", "--freq", help="Timeout between pings, in seconds. Defaults to 5")
parser.add_argument("-m", "--maxlog", help="Maximum amount of pings to log. Defaults to 40")
parser.add_argument("-c", "--color", help="Color scheme ('dark' or 'light'). Defaults to dark.")
args = parser.parse_args()
ubuntu_mono_dark_rgba = [0xdf, 0xd8, 0xc8, 0xff]
ubuntu_mono_light_rgba = [0x3a, 0x39, 0x35, 0xff]
black = [0, 0, 0, 0xff]
red = [0xff, 0, 0, 0xff]
white = [0xff, 0xff, 0xff, 0xff]
dark_bg = [0, 0, 0, 0x3f]
light_bg = [0xff, 0xff, 0xff, 0x3f]
#accumulate the arguments for use later
arguments = " "
for arg in sys.argv[1:]:
arguments += arg + " "
# User-editable variables
if args.target:
host = args.target
else:
host = "4.2.2.2" # IP or hostname
print "Using default target IP of 4.2.2.2"
if args.freq:
try:
ping_frequency = int(args.freq)
except ValueError:
sys.stderr.write("Error parsing argument '--freq'\n")
sys.exit(1)
else:
ping_frequency = 5 # in seconds
if args.maxlog:
try:
ping_log_max_size = int(args.maxlog)
except ValueError:
sys.stderr.write("Error parsing argument '--maxlog'\n")
sys.exit(1)
else:
ping_log_max_size = 40
if args.color == "light":
graph_color = ubuntu_mono_light_rgba
graph_highlight = ubuntu_mono_dark_rgba
graph_background = light_bg
else:
graph_color = ubuntu_mono_dark_rgba
graph_highlight = ubuntu_mono_light_rgba
graph_background = dark_bg
# #
# Main Class # Main Class
# #
class Pinger: class Pinger:
ping_log = []
paused = False
autostart = False
icon_height = 22
def ping(self, widget=None, data=None): def ping(self, widget=None, data=None):
ping = subprocess.Popen( if not self.paused:
["ping", "-c", "1", host], ping = subprocess.Popen(
stdout = subprocess.PIPE, ["ping", "-c", "1", host],
stderr = subprocess.PIPE stdout = subprocess.PIPE,
) stderr = subprocess.PIPE
out, error = ping.communicate() )
if error: out, error = ping.communicate()
label = "PING FAIL"
else:
m = re.search('time=(.*) ms', out) m = re.search('time=(.*) ms', out)
label = m.group(1)+" ms" if error or m == None:
self.ind.set_label (label, "100.0 ms") label = "PING FAIL"
#self.ping_menu_item.set_label(out) self.log_ping(-1)
else:
latency = "%.2f" % float(m.group(1))
label = latency+" ms"
self.log_ping(latency)
#self.ind.set_label(label, "100.0 ms")
gobject.timeout_add_seconds(self.timeout, self.ping) gobject.timeout_add_seconds(self.timeout, self.ping)
def log_ping(self, value):
self.ping_log.append(float(value))
self.update_log_menu()
# limit the size of the log
if len(self.ping_log) >= ping_log_max_size:
# remove the earliest ping, not the latest
self.ping_log.pop(0)
def create_menu_item(self, text, callback): def create_menu_item(self, text, callback):
menu_item = Gtk.MenuItem(text) menu_item = Gtk.MenuItem(text)
self.menu.append(menu_item) self.menu.append(menu_item)
menu_item.connect("activate", callback, text) if callback:
menu_item.connect("activate", callback, text)
menu_item.show() menu_item.show()
return menu_item return menu_item
@@ -86,27 +162,78 @@ class Pinger:
print "Quitting..." print "Quitting..."
Gtk.main_quit() Gtk.main_quit()
def create_autostart(self, widget, data=None): def toggle_autostart(self, widget, data=None):
with open(startup_path,'w') as f: if not self.autostart:
f.write("[Desktop Entry]\r\n" if not os.path.exists(startup_dir):
"Type=Application\r\n" os.makedirs(startup_dir)
"Exec=python "+os.path.abspath( __file__ )+"\r\n" with open(startup_path,'w') as f:
"X-GNOME-Autostart-enabled=true\r\n" f.write("[Desktop Entry]\r\n"
"Name=Pinger\r\n" "Type=Application\r\n"
"Comment=Pings the internet every few seconds") "Exec=python "+os.path.abspath( __file__ )+arguments+"\r\n"
self.update_startup_menu() "X-GNOME-Autostart-enabled=true\r\n"
"Name=Pinger\r\n"
def remove_autostart(self, widget, data=None): "Comment=Pings the internet every few seconds")
os.remove(startup_path) self.autostart = True
self.update_startup_menu()
def update_startup_menu(self):
if os.path.exists(startup_path):
self.startup_menu.set_label(startup_active_label) self.startup_menu.set_label(startup_active_label)
self.startup_menu.connect("activate", self.remove_autostart, startup_active_label)
else: else:
os.remove(startup_path)
self.autostart = False
self.startup_menu.set_label(startup_inactive_label) self.startup_menu.set_label(startup_inactive_label)
self.startup_menu.connect("activate", self.create_autostart, startup_inactive_label)
def toggle_pause(self, widget, data=None):
if self.paused:
self.paused = False
self.pause_menu.set_label(pause_label)
else:
self.paused = True
self.pause_menu.set_label(play_label)
def update_log_menu(self):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, ping_log_max_size, self.icon_height)
ctx = cairo.Context(surface)
# draw semitransparent box
self.draw_rect( ctx, [0,0], [ping_log_max_size,self.icon_height], graph_background )
if(max(self.ping_log) < 100):
max_ping = 100
else:
max_ping = max(self.ping_log)
for index, ping in enumerate(self.ping_log):
if float(ping) == -1: # Ping error
# Draw full-height error bar
self.draw_rect( ctx, [index,self.icon_height], [1,-self.icon_height-1], red )
else:
# draw normal bar
bar_height = -int(self.scale(ping, (0,max_ping), (0,self.icon_height)))
if bar_height > -1:
bar_height = -1
self.draw_rect( ctx, [index,self.icon_height], [1,bar_height], graph_color )
try:
os.remove("/tmp/graph.png")
except:
pass
surface.write_to_png("/tmp/graph.png")
self.ind.set_icon("") # gotta set it to nothing in order to update
self.ind.set_icon("graph")
self.ping_menu.set_label("Ping: "+str(self.ping_log[-1])+" ms")
def draw_rect(self, ctx, point, size, rgba):
ctx.rectangle( point[0], point[1], size[0], size[1] )
ctx.set_source_rgba(rgba[0]/float(255), rgba[1]/float(255), rgba[2]/float(255), rgba[3]/float(255))
ctx.fill()
def scale(self, val, src, dst):
"""
Scale the given value from the scale of src to the scale of dst.
"""
scale = ((val - src[0]) / (src[1]-src[0])) * (dst[1]-dst[0]) + dst[0]
return scale
def __init__(self): def __init__(self):
# Handle ctrl-c # Handle ctrl-c
@@ -119,14 +246,26 @@ class Pinger:
self.ind = appindicator.Indicator.new ( self.ind = appindicator.Indicator.new (
"pinger", "pinger",
"", # no icon "", # no icon
appindicator.IndicatorCategory.COMMUNICATIONS) appindicator.IndicatorCategory.SYSTEM_SERVICES)
self.ind.set_status (appindicator.IndicatorStatus.ACTIVE) self.ind.set_status (appindicator.IndicatorStatus.ACTIVE)
self.ind.set_label ("Pinger Loading...", "Pinger Loading...") #self.ind.set_label ("Pinger Loading...", "Pinger Loading...")
self.ind.set_icon_theme_path("/tmp")
# create a menu # create a menu
self.menu = Gtk.Menu() self.menu = Gtk.Menu()
self.startup_menu = self.create_menu_item(startup_inactive_label, self.create_autostart) # with ping numbers
self.update_startup_menu() self.ping_menu = self.create_menu_item("", None)
# with pause option
self.pause_menu = self.create_menu_item(pause_label, self.toggle_pause)
# with autostart option
# first, check current autostart state by checking existance of .desktop file
if os.path.exists(startup_path):
self.autostart = True
self.startup_menu = self.create_menu_item(startup_active_label, self.toggle_autostart)
else:
self.autostart = False
self.startup_menu = self.create_menu_item(startup_inactive_label, self.toggle_autostart)
# and exit option
self.create_menu_item("Exit", self.destroy) self.create_menu_item("Exit", self.destroy)
self.ind.set_menu(self.menu) self.ind.set_menu(self.menu)