3 changed files with 5 additions and 378 deletions
@ -1,373 +0,0 @@
|
||||
#!/usr/bin/env python3 |
||||
# -*- coding: utf-8 -*- |
||||
# |
||||
# License: GPLv2 |
||||
|
||||
import gi |
||||
gi.require_version('Gtk', '3.0') |
||||
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib |
||||
|
||||
import argparse |
||||
import cairo |
||||
import subprocess |
||||
|
||||
import psutil |
||||
|
||||
def normalize_color_hex(s): |
||||
if s[0] == "#": |
||||
s = s[1:] |
||||
if len(s) == 3: |
||||
s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2] |
||||
if len(s) == 4: |
||||
s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2] + s[3] + s[3] |
||||
if len(s) == 6: |
||||
s += "ff" |
||||
assert(len(s) == 8) |
||||
return s |
||||
|
||||
def color_hex_to_float(s): |
||||
s = normalize_color_hex(s) |
||||
return [int(s[0:2], 16)/255.0, int(s[2:4], 16)/255.0, int(s[4:6], 16)/255.0, int(s[6:8], 16)/255.0] |
||||
|
||||
def color_hex_to_int(s): |
||||
s = normalize_color_hex(s) |
||||
return int(s, 16) |
||||
|
||||
def bytes2human(n): |
||||
# http://code.activestate.com/recipes/578019 |
||||
# >>> bytes2human(10000) |
||||
# '9.8 K' |
||||
# >>> bytes2human(100001221) |
||||
# '95.4 M' |
||||
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') |
||||
prefix = {} |
||||
for i, s in enumerate(symbols): |
||||
prefix[s] = 1 << (i + 1) * 10 |
||||
for s in reversed(symbols): |
||||
if n >= prefix[s]: |
||||
value = float(n) / prefix[s] |
||||
return '%.1f %sB' % (value, s) |
||||
return "%s B" % n |
||||
|
||||
|
||||
# Parameters |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument("--cpu", help="Show a CPU activity graph", dest="cpu", action="store_true") |
||||
parser.add_argument("--cpu_alert_max", help="Blink when CPU load goes above defined percentage. Default: 100", dest="cpu_alert_max", default=100, type=int) |
||||
parser.add_argument("--core", help="Show a CPU activity graph for each logical CPU core", dest="core", action="store_true") |
||||
parser.add_argument("--mem", help="Show a memory usage graph", dest="mem", action="store_true") |
||||
parser.add_argument("--mem_percent", help="Tooltip: Show used memory in percentage", dest="mem_percent", action="store_true") |
||||
parser.add_argument("--mem_alert_max", help="Blink when memory usage goes above defined percentage. Default: 100", dest="mem_alert_max", default=100, type=int) |
||||
parser.add_argument("--swap", help="Show a swap usage graph", dest="swap", action="store_true") |
||||
parser.add_argument("--swap_percent", help="Tooltip: Show used swap in percentage", dest="swap_percent", action="store_true") |
||||
parser.add_argument("--swap_alert_max", help="Blink when swap usage goes above defined percentage. Default: 100", dest="swap_alert_max", default=100, type=int) |
||||
parser.add_argument("--net", help="Show a network usage graph", dest="net", action="store_true") |
||||
parser.add_argument("--net_scale", help="Maximum value for the network usage graph, in Mbps. Default: 40.", default=40, type=int) |
||||
parser.add_argument("--net_alert_max", help="Blink when network usage goes above defined percentage. Default: 100 ", dest="net_alert_max", default=100, type=int) |
||||
parser.add_argument("--io", help="Show a disk I/O graph", dest="io", action="store_true") |
||||
parser.add_argument("--io_scale", help="Maximum value for the disk I/O graph, in MB/s. Default: 100.", default=100, type=int) |
||||
parser.add_argument("--io_alert_max", help="Blink when disk I/O goes above defined percentage. Default: 100", dest="io_alert_max", default=100, type=int) |
||||
|
||||
parser.add_argument("--size", help="Icon size in pixels. Default: 22.", default=22, type=int) |
||||
parser.add_argument("--invert", help="Try to invert order of icons ( might not work as it depends highly on the system tray used )", dest="invert", action="store_true") |
||||
parser.add_argument("--interval", help="Refresh interval in miliseconds. Default: 1000 (1sec).", default=1000, type=int) |
||||
parser.add_argument("--alert_interval", help="Alert blink interval in miliseconds. Default: 1000 (1sec).", default=1000, type=int) |
||||
parser.add_argument("--bg", help="Background color (RGBA hex). Default: #DDDDDD60.", default="#DDDDDD60") |
||||
parser.add_argument("--fg_cpu", help="CPU graph color (RGBA hex). Default: #70b433.", default="#70b433") |
||||
parser.add_argument("--fg_mem", help="Memory graph color (RGBA hex). Default: #efc541.", default="#efc541") |
||||
parser.add_argument("--fg_swap", help="Swap graph color (RGBA hex). Default: #ff81ca.", default="#ff81ca") |
||||
parser.add_argument("--fg_net", help="Network graph color (RGBA hex). Default: #368aeb.", default="#368aeb") |
||||
parser.add_argument("--fg_io", help="Disk I/O graph color (RGBA hex). Default: #ff5e56.", default="#ff5e56") |
||||
parser.add_argument("--fg_alert", help="Alert color (RGBA hex). Default: #ff0000cc.", default="#ff0000cc") |
||||
|
||||
parser.add_argument("--task_manager", help="Task manager to execute on left click. Default: None.") |
||||
|
||||
parser.set_defaults(cpu=False) |
||||
parser.set_defaults(core=False) |
||||
parser.set_defaults(mem=False) |
||||
parser.set_defaults(swap=False) |
||||
parser.set_defaults(net=False) |
||||
parser.set_defaults(io=False) |
||||
args = parser.parse_args() |
||||
|
||||
w = h = args.size |
||||
invert = args.invert |
||||
interval = args.interval |
||||
alertInterval = args.alert_interval |
||||
bgCol = color_hex_to_int(args.bg) |
||||
fgCpu = color_hex_to_float(args.fg_cpu) |
||||
fgRam = color_hex_to_float(args.fg_mem) |
||||
fgSwap = color_hex_to_float(args.fg_swap) |
||||
fgNet = color_hex_to_float(args.fg_net) |
||||
fgDiskIo = color_hex_to_float(args.fg_io) |
||||
fgAlert = color_hex_to_int(args.fg_alert) |
||||
|
||||
cpuEnabled = args.cpu or args.core |
||||
cpuAlertMax = args.cpu_alert_max |
||||
mergeCpus = not args.core |
||||
ramEnabled = args.mem |
||||
memPercent = args.mem_percent |
||||
memAlertMax = args.mem_alert_max |
||||
swapEnabled = args.swap |
||||
swapPercent = args.swap_percent |
||||
swapAlertMax = args.swap_alert_max |
||||
netEnabled = args.net |
||||
netScale = args.net_scale |
||||
netAlertMax = args.net_alert_max |
||||
diskIoEnabled = args.io |
||||
diskIoScale = args.io_scale |
||||
diskIoAlertMax = args.io_alert_max |
||||
taskMgr = args.task_manager |
||||
|
||||
if not cpuEnabled and not ramEnabled and not swapEnabled and not netEnabled and not diskIoEnabled: |
||||
cpuEnabled = mergeCpus = ramEnabled = swapEnabled = netEnabled = diskIoEnabled = True |
||||
|
||||
class HardwareMonitor: |
||||
alertState = False |
||||
|
||||
def __init__(self): |
||||
if invert: |
||||
self.initDiskIo() |
||||
self.initNet() |
||||
self.initSwap() |
||||
self.initRam() |
||||
self.initCpus() |
||||
self.drawDiskIo() |
||||
self.drawNet() |
||||
self.drawSwap() |
||||
self.drawRam() |
||||
self.drawCpus() |
||||
else: |
||||
self.initCpus() |
||||
self.initRam() |
||||
self.initSwap() |
||||
self.initNet() |
||||
self.initDiskIo() |
||||
self.drawCpus() |
||||
self.drawRam() |
||||
self.drawSwap() |
||||
self.drawNet() |
||||
self.drawDiskIo() |
||||
GLib.timeout_add(interval, self.update) |
||||
GLib.timeout_add(alertInterval, self.alertFlip) |
||||
|
||||
def alertFlip(self): |
||||
self.alertState = not self.alertState |
||||
return True |
||||
|
||||
def rightClickEvent(self, icon, button, time): |
||||
menu = Gtk.Menu() |
||||
quit = Gtk.MenuItem("Quit") |
||||
quit.connect("activate", Gtk.main_quit) |
||||
menu.append(quit) |
||||
menu.show_all() |
||||
menu.popup(None, None, Gtk.status_icon_position_menu, button, time, icon) |
||||
|
||||
def leftClickEvent (self, icon): |
||||
if not taskMgr: |
||||
return |
||||
subprocess.Popen(taskMgr) |
||||
|
||||
def initCpus(self): |
||||
if not cpuEnabled: |
||||
return |
||||
if mergeCpus: |
||||
self.cpus = [[0 for x in range(w)]] |
||||
psutil.cpu_percent(percpu=mergeCpus) |
||||
else: |
||||
self.cpus = [[0 for x in range(w)] for c in range(len(psutil.cpu_percent(percpu=not mergeCpus)))] |
||||
self.cpuIcons = [Gtk.StatusIcon() for c in range(len(self.cpus))] |
||||
for c in range(len(self.cpus)): |
||||
self.cpuIcons[c].set_title("hwmon 1 cpu{0}".format("" if mergeCpus else (" " + str(c+1)))) |
||||
self.cpuIcons[c].connect("popup-menu", self.rightClickEvent) |
||||
self.cpuIcons[c].connect("activate", self.leftClickEvent) |
||||
self.cpuIcons[c].set_visible(True) |
||||
|
||||
def updateCpus(self): |
||||
if not cpuEnabled: |
||||
return |
||||
vals = psutil.cpu_percent(percpu=not mergeCpus) |
||||
if mergeCpus: |
||||
vals = [vals] |
||||
for c in range(len(vals)): |
||||
self.cpus[c].append(vals[c]) |
||||
self.cpus[c].pop(0) |
||||
self.cpuIcons[c].set_tooltip_text("CPU{0}: {1}%".format("" if mergeCpus else (" " + str(c+1)), vals[c])) |
||||
|
||||
def drawCpus(self): |
||||
if not cpuEnabled: |
||||
return |
||||
for c in range(len(self.cpus)): |
||||
self.drawGraph(self.cpus[c], self.cpuIcons[c], bgCol, fgCpu) |
||||
self.drawAlert(self.cpus[c], self.cpuIcons[c], fgAlert, cpuAlertMax) |
||||
|
||||
def initRam(self): |
||||
if not ramEnabled: |
||||
return |
||||
self.ram = [0 for x in range(w)] |
||||
self.ramIcon = Gtk.StatusIcon() |
||||
self.ramIcon.set_title("hwmon 2 memory") |
||||
self.ramIcon.connect("popup-menu", self.rightClickEvent) |
||||
self.ramIcon.connect("activate", self.leftClickEvent) |
||||
self.ramIcon.set_visible(True) |
||||
|
||||
def updateRam(self): |
||||
if not ramEnabled: |
||||
return |
||||
mem = psutil.virtual_memory() |
||||
total = mem[0] |
||||
used = mem[3] |
||||
used_percent = mem[2] |
||||
self.ram.append(used_percent) |
||||
self.ram.pop(0) |
||||
if memPercent: |
||||
self.ramIcon.set_tooltip_text("Memory: %d%% used of %s" % (used_percent, bytes2human(total))) |
||||
else: |
||||
self.ramIcon.set_tooltip_text("Memory: %s used of %s" % (bytes2human(used), bytes2human(total))) |
||||
|
||||
def drawRam(self): |
||||
if not ramEnabled: |
||||
return |
||||
self.drawGraph(self.ram, self.ramIcon, bgCol, fgRam) |
||||
self.drawAlert(self.ram, self.ramIcon, fgAlert, memAlertMax) |
||||
|
||||
def initSwap(self): |
||||
if not swapEnabled: |
||||
return |
||||
self.swap = [0 for x in range(w)] |
||||
self.swapIcon = Gtk.StatusIcon() |
||||
self.swapIcon.set_title("hwmon 3 swap") |
||||
self.swapIcon.connect("popup-menu", self.rightClickEvent) |
||||
self.swapIcon.connect("activate", self.leftClickEvent) |
||||
|
||||
def updateSwap(self): |
||||
if not swapEnabled: |
||||
return |
||||
swap = psutil.swap_memory() |
||||
total = swap[0] |
||||
used = swap[1] |
||||
used_percent = swap[3] |
||||
if bool(self.swapIcon.get_visible()) != bool(total): |
||||
self.swapIcon.set_visible(total) |
||||
self.swap.append(used_percent) |
||||
self.swap.pop(0) |
||||
if swapPercent: |
||||
self.swapIcon.set_tooltip_text("Swap: %d%% used of %s" % (used_percent, bytes2human(total))) |
||||
else: |
||||
self.swapIcon.set_tooltip_text("Swap: %s used of %s" % (bytes2human(used), bytes2human(total))) |
||||
|
||||
def drawSwap(self): |
||||
if not swapEnabled: |
||||
return |
||||
self.drawGraph(self.swap, self.swapIcon, bgCol, fgSwap) |
||||
self.drawAlert(self.swap, self.swapIcon, fgAlert, swapAlertMax) |
||||
|
||||
def initNet(self): |
||||
if not netEnabled: |
||||
return |
||||
self.net = [0 for x in range(w)] |
||||
v = psutil.net_io_counters(pernic=False) |
||||
self.netBytes = v[0] + v[1] |
||||
self.netIcon = Gtk.StatusIcon() |
||||
self.netIcon.set_title("hwmon 4 network") |
||||
self.netIcon.connect("popup-menu", self.rightClickEvent) |
||||
self.netIcon.connect("activate", self.leftClickEvent) |
||||
self.netIcon.set_visible(True) |
||||
|
||||
def updateNet(self): |
||||
if not netEnabled: |
||||
return |
||||
v = psutil.net_io_counters(pernic=False) |
||||
v = v[0] + v[1] |
||||
delta = v - self.netBytes |
||||
self.netBytes = v |
||||
self.net.append(delta * 8 / 1.0e6) |
||||
self.net.pop(0) |
||||
self.netIcon.set_tooltip_text("Network: %.1f Mb/s" % (delta * 8 / 1.0e6)) |
||||
|
||||
def drawNet(self): |
||||
if not netEnabled: |
||||
return |
||||
self.drawGraph(self.net, self.netIcon, bgCol, fgNet, netScale) |
||||
self.drawAlert(self.net, self.netIcon, fgAlert, netAlertMax, netScale) |
||||
|
||||
def initDiskIo(self): |
||||
if not diskIoEnabled: |
||||
return |
||||
self.diskIo = [0 for x in range(w)] |
||||
v = psutil.disk_io_counters(perdisk=False) |
||||
self.diskIoBytes = v[2] + v[3] |
||||
self.diskIoIcon = Gtk.StatusIcon() |
||||
self.diskIoIcon.set_title("hwmon 5 disk i/o") |
||||
self.diskIoIcon.connect("popup-menu", self.rightClickEvent) |
||||
self.diskIoIcon.connect("activate", self.leftClickEvent) |
||||
self.diskIoIcon.set_visible(True) |
||||
|
||||
def updateDiskIo(self): |
||||
if not diskIoEnabled: |
||||
return |
||||
v = psutil.disk_io_counters(perdisk=False) |
||||
v = v[2] + v[3] |
||||
delta = v - self.diskIoBytes |
||||
self.diskIoBytes = v |
||||
self.diskIo.append(delta / 1.0e6 / 10) |
||||
self.diskIo.pop(0) |
||||
partitions = psutil.disk_partitions(all=False) |
||||
strPartitions = "" |
||||
for part in psutil.disk_partitions(all=False): |
||||
if 'cdrom' in part.opts or part.fstype == '': |
||||
continue |
||||
usage = psutil.disk_usage(part.mountpoint) |
||||
strPartitions += "\n%s %d%% of %s (%s)" % (part.mountpoint, int(usage.percent), bytes2human(usage.total), part.fstype) |
||||
self.diskIoIcon.set_tooltip_text("Disk I/O: %.1f MB/s\n%s" % (delta / 1.0e6 / 10, strPartitions)) |
||||
|
||||
def drawDiskIo(self): |
||||
if not diskIoEnabled: |
||||
return |
||||
self.drawGraph(self.diskIo, self.diskIoIcon, bgCol, fgDiskIo, diskIoScale) |
||||
self.drawAlert(self.diskIo, self.diskIoIcon, fgAlert, diskIoAlertMax, diskIoScale) |
||||
|
||||
def drawGraph(self, graph, icon, bgCol, fgCol, max=100): |
||||
bg = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, w, h) |
||||
bg.fill(bgCol) |
||||
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) |
||||
cr = cairo.Context(surface) |
||||
cr.set_source_rgba(fgCol[2], fgCol[1], fgCol[0], fgCol[3]) |
||||
for x in range(w): |
||||
y = int(round(graph[x]/max * h)) |
||||
if y: |
||||
cr.move_to(x, h) |
||||
cr.line_to(x, h - y) |
||||
cr.stroke() |
||||
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_data(surface.get_data(), GdkPixbuf.Colorspace.RGB, True, 8, w, h, w * 4, None, None) |
||||
pixbuf.composite(bg, 0, 0, w, h, 0, 0, 1, 1, GdkPixbuf.InterpType.NEAREST, 255) |
||||
icon.set_from_pixbuf(bg) |
||||
|
||||
def drawAlert(self, graph, icon, fgCol, threshold, max=100): |
||||
if graph[-1] < threshold or not self.alertState: |
||||
return |
||||
bg = icon.get_pixbuf() |
||||
fg = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, w, h) |
||||
fg.fill(fgCol) |
||||
fg.composite(bg, 0, 0, w, h, 0, 0, 1, 1, GdkPixbuf.InterpType.NEAREST, 255) |
||||
icon.set_from_pixbuf(bg) |
||||
|
||||
def update(self): |
||||
self.updateCpus() |
||||
self.updateRam() |
||||
self.updateSwap() |
||||
self.updateNet() |
||||
self.updateDiskIo() |
||||
self.drawCpus() |
||||
self.drawRam() |
||||
self.drawSwap() |
||||
self.drawNet() |
||||
self.drawDiskIo() |
||||
return True |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
HardwareMonitor() |
||||
Gtk.main() |
Loading…
Reference in new issue