Measuring memory consumption with python

Last Update: 26.03.2007. By kerim in python


Ever wanted to show your free memory (or other things) in the system tray ?
Since i had a really bad celeron based desktop with 1 gigabyte ram and eclipse consuming some 500 megabytes alone i so often ran into trouble that i thought i might just as well program some tracking program to know when it is time to close some of the applications that i run in paralel.
Since by now i have a 2 gigabyte core 2 duo tower the program is not really needed (for now) anymore.
But i thought i could share it still.

It’s based on windows (and runs only there).

For those that are interested in the binary or sources i will put up some links later this week ok ? Need to clean up things first.

Anyway here is the code explained....

You might still know the mailfetcher. Basically it is based on the same skeleton. Some parts are commented because i didn’t implement some features yet that i thought might be interesting later (as usual i start new things before finishing the old ones ;-) )
I had plans for a cpu meter as well that would change the color of the background or the font depending on cpu usage.

Its not really nice looking but it works and you might remind me to clean up the code later.
I don’t have much time these days.

First the imports

import sys, wx, webbrowser  
import mem  
from ctypes import *

ID_ICON_TIMER = wx.NewId()  
OPEN_BROWSER=wx.NewId()

Now come two classes. the taskbaricon itself and the application.
Mainly we follow the scheme of MailSneaker.

class StatusMeterTaskBarIcon(wx.TaskBarIcon):

    def __init__(self, parent):  
        wx.TaskBarIcon.__init__(self)  
        self.parentApp = parent  
        self.CreateMenu()

    # Create the menu  
    def CreateMenu(self):  
        self.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.ShowMenu)  
        self.Bind(wx.EVT_MENU, self.parentApp.OpenBrowser, id=OPEN_BROWSER)  
        self.menu=wx.Menu()  
        self.menu.Append(OPEN_BROWSER, "Visit codeboje","This will open a new Browser tab")  
        self.menu.AppendSeparator()  
        self.menu.Append(wx.ID_EXIT, "Close App")

    def ShowMenu(self,event):  
        self.PopupMenu(self.menu)

    #This will create and set the icon that is displayed in the taskbar     
    def SetIconImage(self,text,font=None,fgColor=wx.WHITE,bgColor=wx.BLACK_BRUSH):  
        #create a new empty icon and an empty bitmap for the text  
        self.noIcon = wx.EmptyIcon()  
        self.bitmap=wx.EmptyBitmap(48,48)  
        #create a MemoryDC for the pain operations  
        memory = wx.MemoryDC()

        #Set the font depending on the length of the text (TODO: not yet looking good for  
        # sizes > 1 GB)  
        if len(text)>3:  
            if not font:  
                font=self.createDefaultFont()  
            size = font.GetPointSize()-1  
            size=size-2  
            font.SetPointSize(size)  
            memory.SetFont(font)  
        else:  
            if not font:  
                font=self.createDefaultFont()  
            size = font.GetPointSize()-1  
            font.SetPointSize(size)  
            memory.SetFont(font)

        #set the foreground color  
        if fgColor:  
                memory.SetTextForeground( fgColor )

        #write free mem size into the bitmap  
        self.write(text,self.bitmap,memory,(0,0),fgColor,bgColor)

        #copy bitmap data into the icon  
        self.noIcon.CopyFromBitmap(self.bitmap)

        #set the icon  
        self.SetIcon(self.noIcon, "Free mem:"+text+ "megabytes")

    #if no font is given this font will be used  
    def createDefaultFont(self):  
        font=wx.FFont(21,wx.FONTFAMILY_DEFAULT)  
        return font

    #writes the size of free memory onto the bitmap after drawing the background  
    def write(self,  text, bitmap, memory,pos=(0,0), fgColor=wx.WHITE, bgColor=wx.GREEN_BRUSH):  
        memory.SelectObject( bitmap )  
        memory.SetBrush(bgColor)  
        try:  
            memory.DrawRectangle(pos[0],pos[1],48,48)  
            memory.DrawText(text, pos[0],pos[1])  
        finally:  
            memory.SelectObject( wx.NullBitmap)  
        return bitmap

Now the magic goes in SetIconImage and write.
The explanation should be understandable

Now comes the app itself. Basically it’s the same as for MailSneaker. I didn’t even bother to rename every member.

class StatusMeterFrame(wx.Frame):

    def __init__(self, parent, id, title):  
        wx.Frame.__init__(self, parent, -1, title, size = (1, 1),  
            style=wx.FRAME_NO_TASKBAR|wx.NO_FULL_REPAINT_ON_RESIZE)

        self.tbicon = StatusMeterTaskBarIcon(self)  
        self.tbicon.Bind(wx.EVT_MENU, self.exitApp, id=wx.ID_EXIT)   
        self.timer=wx.Timer(self)  
        self.Bind(wx.EVT_TIMER, self.CheckMemory)  
        self.timer.Start(300) # 300 milliseconds  
        self.Show(True)

    def exitApp(self,event):  
        self.timer.Stop()  
        self.tbicon.RemoveIcon()  
        self.tbicon.Destroy()  
        sys.exit()

    def OpenBrowser(self,event):  
        webbrowser.open('http://codeboje.de/blog/pages/donate.html')

    def OpenPrefs(self,event):  
        pass

    def CheckMemory(self,event):  
        memstatus = mem._MEMORYSTATUS()  
        windll.kernel32.GlobalMemoryStatus(byref(memstatus ))  
        total,free= memstatus.show()  
        ffree=int(float(free)/1048576) #in megabytes  
        self.tbicon.SetIconImage(str(ffree))

#---------------- run the program -----------------------  
def main(argv=None):  
    app = wx.App(False)  
    frame = StatusMeterFrame(None, -1, ' ')  
    frame.Show(False)  
    app.MainLoop()

if __name__ == '__main__':  
    main()

To get the memory i use this function. As you can see this will only work on windows. If anybody has some working unix call i would be happy to add those

:::python def CheckMemory(self,event):
memstatus = mem._MEMORYSTATUS()
windll.kernel32.GlobalMemoryStatus(byref(memstatus ))
total,free= memstatus.show()
ffree=int(float(free)/1048576) #in megabytes
self.tbicon.SetIconImage(str(ffree))

PS: Did i mention a flaw (thanks wxPython) ? The program consumes some 20 megabytes itself lol.

EDIT
As you can see in the comments section i forgot something.
The “mem” module. Here it is:

:::python from ctypes import *
from ctypes.wintypes import DWORD

SIZE_T = c_ulong

class _MEMORYSTATUS(Structure):  
    _fields_ = [("dwLength", DWORD),  
    ("dwMemoryLength", DWORD),  
    ("dwTotalPhys", SIZE_T),  
    ("dwAvailPhys", SIZE_T),  
    ("dwTotalPageFile", SIZE_T),  
    ("dwAvailPageFile", SIZE_T),  
    ("dwTotalVirtual", SIZE_T),  
    ("dwAvailVirtualPhys", SIZE_T)]

    def show(self):  
        return (int(getattr(self,"dwTotalPhys")),int(getattr(self,"dwAvailPhys")))

#test routine  
memstatus = _MEMORYSTATUS()  
windll.kernel32.GlobalMemoryStatus(byref(memstatus ))  
total,free= memstatus.show()  
print total  
print free