
Simple system monitor powered by Python and Pygame
I’ve continued working on a simple system monitor application for the Ben Nanonote in Python and Pygame by rewriting the code in a more object-oriented style and adding a CPU and Memory monitor. Eventually I’d like to add disk space monitors, and to write a simple application launcher.
#!/usr/bin/python</code>
<code># A simple battery monitor application powered
# Python and Pygame.
#########################################################
# Press CONTROL-q to exit
from pygame import *
from copy import deepcopy
# initialize
init()
########################################################
# colours
white = 255,255,255
black = 0,0,0
red = 255,0,0
green = 0,255,0
yellow = 255,255,0
backcolour = black
solid = 0
########################################################
# background screen
def backgroundScreen(colour):
'''Setup background screen
In: colourTUPLE.
Return: screenOBJ'''
screen = display.set_mode((320,240))
backgrnd = colour
screen.fill(backgrnd)
display.flip()
return screen
# Warning colour
def getWarningColour(powerPercent):
'''Set a colour for the power block and percent text'''
if powerPercent &gt;= 50:
warningColour = green
elif powerPercent &gt;= 25:
warningColour = yellow
else:
warningColour = red
return warningColour
# get file
def getFile(path):
'''Returns a string'''
targetFile = open(path)
contents = targetFile.read()
targetFile.close()
return contents
# Check for keypress
# Exit on control-q
def exitKey():
for e in event.get():
if e.type == KEYDOWN \
and e.key == K_q \
and key.get_mods() &amp; KMOD_CTRL:
return True
######################################################
class Point(object):
'''Point object'''
def __init__(self, x, y=0):
'''Create a point from x,y or (x,y). '''
if type(x) is tuple and y == 0:
self.x = x[0]
self.y = x[1]
elif type(x) is int and type(y) is int:
self.x = x
self.y = y
else:
message = "Point type error: %s, %s" % (x, y)
raise RuntimeError(message)
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
def __add__(self, term):
'''Term can be a Point or a tuple.
Returns a Point.'''
result = Point(0, 0)
if type(term) is tuple and len(term) == 2:
result.x = self.x + term[0]
result.y = self.y + term[1]
return result
elif isinstance(term, Point):
result.x = self.x + term.x
result.y = self.y + term.y
return result
else:
message = "Point type error: %s" % (term)
raise RuntimeError(message)
def tuple(self):
return (self.x, self.y)
######################################
class Txt(object):
def __init__(self, message,
size = 16, fontName = None):
# self.message is used by Txt.write()
self.message = message
# font object
self.fontObj = font.Font(fontName, size)
def write(self, position, forecolour = white):
'''Render font object'''
if isinstance(position, Point):
position = position.tuple()
antialias = True
self.textObj = self.fontObj.render(self.message,
antialias,
forecolour,
backcolour)
# Create a rectangle
self.textRect = self.textObj.get_rect()
self.textRect.topleft = position
draw.rect(screen, backcolour, self.textRect, solid)
# Blit the text
screen.blit(self.textObj, self.textRect)
display.update(self.textRect)
#######################################################
class Header(object):
def __init__(self, text, origin):
self.text=Txt(text, 16)
self.origin = origin
def write(self):
self.text.write(self.origin)
#######################################################
class Monitor(object):
def __init__(self, name, origin):
self.origin = origin
# header text
self.header = Header(name, (origin + (0, 5)))
def draw(self):
self.header.write()
#######################################################
class batteryMonitor(object):
'''Header text, terminal, body'''
def __init__(self, origin):
'''origin is Point object '''
self.origin = origin
# header text
self.header = Header("Battery", (origin + (0, 5)))
# percent text
self.percentOrigin = self.origin + (55, 5)
# battery: body + terminal
batteryOrigin = self.origin + (100, 0)
batteryH = 22
batteryW = 12
self.lineWidth = 1
# battery terminal
terminalOrigin = batteryOrigin + (batteryW/4, 0)
terminalW = batteryW / 2
terminalH = int(round(batteryH * .1))
self.terminal = Rect(terminalOrigin.x,
terminalOrigin.y,
terminalW,
terminalH)
# battery body
bodyOrigin = batteryOrigin + (0, terminalH)
bodyW = batteryW
bodyH = batteryH - terminalH
self.body = Rect(bodyOrigin.x, bodyOrigin.y, bodyW, bodyH)
# list of power readings to average
self.powerReadings = []
def draw(self):
'''header, terminal, body'''
self.header.write()
# terminal
draw.rect(screen,
white,
self.terminal,
solid)
# body
draw.rect(screen,
white,
self.body,
self.lineWidth)
# update screen
display.flip()
def getPower(self):
'''Check powerlevel and return average of last 5 readings
reading is a percentage'''
# Check the battery power level
path = "/sys/class/power_supply/battery/capacity"
powerPercent = int(getFile(path))
# powerPercent = 75
# add to list of past readings
self.powerReadings.append(powerPercent)
# limit the list to 3 readings by deleting first item
if len(self.powerReadings) &gt; 3:
self.powerReadings = self.powerReadings[1:]
# average the list of readings
average = float(sum(self.powerReadings)) / len(self.powerReadings)
# round the average to the nearest 5
powerPercent = int(round(average *2, -1) /2)
return powerPercent
def updateBlock(self, powerPercent):
'''Build power block and display with percentage'''
# Set a colour for the power block and percent text
warningColour = getWarningColour(powerPercent)
# Make a block to represent the power level
# Copy from body Rect
self.power = deepcopy(self.body)
# adjust the height of the power block to reflect the power level
self.power.h = int(round(self.power.h * powerPercent / 100))
# move the power block to the bottom of the battery
self.power.bottom = self.body.bottom - self.lineWidth
# blank the body outline
draw.rect(screen, backcolour, self.body, solid)
# draw solid power block
draw.rect(screen, warningColour, self.power, solid)
# redraw the body outline over the power block
draw.rect(screen, white, self.body, self.lineWidth)
display.update([self.body, self.power])
# Print the power percent
label = "%2s %%" % (powerPercent)
percent = Txt(label, 22)
percent.write(
self.percentOrigin,
forecolour = warningColour)
###############################################################
class CPUMonitor(object):
'''Header text, body rectangle'''
def __init__(self, origin):
'''origin is Point object '''
self.origin = origin
# header text
self.header = Header("CPU", (self.origin + (0, 5)))
# percent text
self.percentOrigin = self.origin + (55, 5)
self.percentBlank = Rect(self.percentOrigin.x, self.percentOrigin.y, 40, 20)
# cpu monitor body rectangle
bodyOrigin = self.origin + (100, 0)
bodyH = 20
bodyW = 12
self.body = Rect(bodyOrigin.x, bodyOrigin.y, bodyW, bodyH)
self.lineWidth = 1
def draw(self):
'''header, body rectangle'''
# header
self.header.write()
# body
draw.rect(screen,
white,
self.body,
self.lineWidth)
# update screen
display.flip()
def getLevel(self):
'''Check cpu level.
reading is a percentage'''
cpuFile = getFile("/proc/loadavg")
# extract first item and convert to percentage
listFromFile = cpuFile.split()
cpuPercent = int(float(listFromFile[0]) * 100)
return cpuPercent
def updateBlock(self, cpuPercent):
'''Build cpu block and display with percentage'''
# Set a colour for the power block and percent text
warningColour = green
# Make a block to represent the cpu level
# Copy from body Rect
self.block = deepcopy(self.body)
# adjust the height of the cpu block to reflect the cpu percent
self.block.h = int(round(self.block.h * cpuPercent / 100))
# move the cpu block to the bottom of the body
self.block.bottom = self.body.bottom - self.lineWidth
# blank the body outline
draw.rect(screen, backcolour, self.body, solid)
# draw solid cpu block
draw.rect(screen, warningColour, self.block, solid)
# redraw the body outline over the cpu block
draw.rect(screen, white, self.body, self.lineWidth)
display.update([self.body, self.block, self.percentBlank])
# Print the cpu percent
label = "%2s %%" % (cpuPercent)
percent = Txt(label, 22)
# blank out the old percent text
draw.rect(screen, backcolour, self.percentBlank, solid)
# write percentage
percent.write(
self.percentOrigin,
forecolour = warningColour)
###############################################################
class MemMonitor(object):
'''Header text, body rectangle'''
def __init__(self, origin):
'''origin is Point object '''
self.origin = origin
# header text
self.header = Header("Mem", (self.origin + (0, 5)))
# percent text
self.percentOrigin = self.origin + (55, 5)
self.percentBlank = Rect(self.percentOrigin.x, self.percentOrigin.y, 40, 20)
# total and free text
self.freeOrigin = self.origin + (100, 20)
self.freeBlank = Rect(self.freeOrigin.x, self.freeOrigin.y, 100, 20)
# monitor body rectangle
bodyOrigin = self.origin + (100, 0)
bodyH = 10
bodyW = 100
self.body = Rect(bodyOrigin.x, bodyOrigin.y, bodyW, bodyH)
self.lineWidth = 1
def draw(self):
'''header, body rectangle'''
# header
self.header.write()
# body
draw.rect(screen,
white,
self.body,
self.lineWidth)
# update screen
display.flip()
def getLevel(self):
'''Check mem level.
reading is a percentage'''
memFile = getFile("/proc/meminfo")
# Convert to list and extract total and free
listFromFile = memFile.split()
memTotal = int(listFromFile[1])
memFree = int(listFromFile[4])
memPercent = int(float(memFree) / memTotal * 100)
return memPercent
def updateBlock(self, memPercent):
'''Build mem block and display with percentage'''
# Set a colour for the power block and percent text
warningColour = getWarningColour(memPercent)
# Make a block to represent the mem level
# Copy from body Rect
self.block = deepcopy(self.body)
# adjust the length of the cpu block to reflect the mem percent
self.block.w = int(round(self.block.w * memPercent / 100))
# blank the body outline
draw.rect(screen, backcolour, self.body, solid)
# draw solid mem block
draw.rect(screen, warningColour, self.block, solid)
# redraw the body outline over the mem block
draw.rect(screen, white, self.body, self.lineWidth)
display.update([self.body, self.block])
# Print the mem percent
label = "%2s %%" % (memPercent)
percent = Txt(label, 22)
# blank out the old percent text
draw.rect(screen, backcolour, self.percentBlank, solid)
# write percentage
percent.write(
self.percentOrigin,
forecolour = warningColour)
###############################################################
# Hide the mouse pointer
mouse.set_visible(0)
# setup screen
screen = backgroundScreen(backcolour)
###############################################################
class TestMonitor(Monitor):
def __init__(self, name, position):
Monitor.__init__(self,name, position)
###############################################################
t = TestMonitor("header", Point(10,200) )
# t.draw()
###############################################################
# header, terminal, battery body
batteryMonitorOrigin = Point(10, 10)
battery = batteryMonitor( batteryMonitorOrigin )
battery.draw()
# header, cpu monitor body
cpuMonitorOrigin = Point(10, 50)
cpu = CPUMonitor( cpuMonitorOrigin )
cpu.draw()
# mem monitor
memMonitorOrigin = Point(10,100)
mem = MemMonitor( memMonitorOrigin )
mem.draw()
# The main loop
oldLevel = 100
oldPower = 0
oldMem = 0
done = False
while not done:
cpuPercent = cpu.getLevel()
if cpuPercent != oldLevel:
cpu.updateBlock(cpuPercent)
oldLevel = cpuPercent
powerPercent = battery.getPower()
if powerPercent != oldPower:
battery.updateBlock(powerPercent)
oldPower = powerPercent
memPercent = mem.getLevel()
if memPercent != oldMem:
mem.updateBlock(memPercent)
oldPower = memPercent
done = exitKey()
time.wait(250)