Monitor CPU on up to for Windows Systems

"Russell Every" <[email protected]>
Newsgroups gmane.comp.python.pythoncard
Message-ID <[email protected]>
Ever wondered how your windows-based web server, application server,
and database server are performing but you can't see them all at once?
 The attached files contain a simple CPU monitoring program developed
using PythonCard that displays the CPU utilisation of up to four
systems.  By design, the first system is the one from which the
monitor is launched (for me that is my PC) and the other three (or
less) are listed in the configuration file.  You will need to edit the
configuration file to record the names of the systems you wish to
monitor and the application should work straight away.

The development was in Python 2.5.1 and PythoCard 0.8.2 and the Inno
Setup Compiler is version 5.1.11.

If you want an associated icon, edit the SystemMonitor.rsrc.py file on
line 10 to remove the leading ### and replace the *** YOUR ICON
FILENAME *** with the name of your icon file.

There is a setup.py file to create an executable and, as this is
expecting the name of the icon file, it will require editing before
setup can be run.  When running the setup, I used the python setup.py
py2exe --bundle=2 command.

There is also an Inno Setup file SystemMonitor.iss that will require
some heavy modification for your program location and the icon file
name (again).

I hope it works on your system and that you find it useful.  I expect
that it could be significantly improved, both in functionality and
implementation.  However, it works well for me...

Russell

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

_______________________________________________
Pythoncard-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/pythoncard-users
SystemMonitor.py (text/x-python, 7.2 KB)
""" System Monitor

    A system monitoring utility that shows the CPU activity for four systems
    with the first of these being the system from which the monitor was launched

    There is a configuration file that lists the names of the servers
    to be monitored and the interval for each refresh of the display
"""


from PythonCard import model
from PythonCard.components import staticbox, bitmapcanvas, button
import wx
import time, random
from configobj import ConfigObj
import PerformanceCounter
import win32api

HOST = 0
GRAPH = 1
GAUGE = 2
HEIGHT = 3
WIDTH = 4
MAX_X = 5
POINTS = 6
CPU_DATA = 7
STATIC_TEXT = 8
X_COORDS = 9

class SystemMonitor(model.Background):
    def on_initialize(self, event):
        self.statusBar.text = "Starting data collection (it can take a while)"

        """Content on plotList is:
            HOST        Hostname
            GRAPH       Graph bitmap
            GAUGE       Gauge bitmap
            HEIGHT      Height of Graph and Gauge -- might delete this
            WIDTH       Width of Graph -- might delete this
            MAX_X       Maximum X coordinate of the Graph -- might delete this
            POINTS      Points to be plotted on the Graph
            CPU_DATA    Current CPU Utilisation (as a percentage)
            STATIC_TEXT Label for the Graph and Gauge
            X_COORDS    x coordinates for each graph
        """
        self.plotList = {}
        config = ConfigObj("SystemMonitor.config")
        machines = config["MACHINES"]["machines"]
        self.interval = float(config["MACHINES"]["interval"])

        # Build the list of machines.  The first machine in the list is the current computer
        current = win32api.GetComputerName()
        self.plotList[current] = [current, self.components.bmpPlot1,
                self.components.gInstant1, 0, 0, 0, [], 0, self.components.stbBox1, []]

        numMc = len(machines)
        if numMc > 0:
            self.plotList[machines[0]] = [machines[0], self.components.bmpPlot2,
                self.components.gInstant2, 0, 0, 0, [], 0, self.components.stbBox2, []]
        if numMc > 1:
            self.plotList[machines[1]] = [machines[1], self.components.bmpPlot3,
                self.components.gInstant3, 0, 0, 0, [], 0, self.components.stbBox3, []]
        if numMc > 2:
            self.plotList[machines[2]] = [machines[2], self.components.bmpPlot4,
                self.components.gInstant4, 0, 0, 0, [], 0, self.components.stbBox4, []]

        # Setup the graphs and gauges for each machine
        for key, details in self.plotList.iteritems():
            details[GRAPH].autoRefresh = False
            details[GRAPH].backgroundColor = "black"
            details[GRAPH].clear()
            details[GRAPH].enabled = True

            details[GAUGE].autoRefresh = False
            details[GAUGE].backgroundColor = "black"
            details[GAUGE].clear()
            details[GAUGE].enabled = True

            # Remove These at some stage
            details[WIDTH], details[HEIGHT] = details[GRAPH].size
            details[MAX_X] = details[WIDTH] - 1
            # End of Remove These

            details[STATIC_TEXT].label = details[HOST]

            # Setup the counter for this machine
            details[CPU_DATA] = PerformanceCounter.HostCPU(details[HOST])
            points = []
            points.append(details[CPU_DATA].getUsage()*details[HEIGHT]//100)
            details[POINTS] = points

            # Set up the x coordindates as they don't vary (except for the range used)
            xCoords = []
            for x in xrange(details[WIDTH]):
                xCoords.append(x)
            details[X_COORDS] = xCoords

        wx.Yield()

        self.go = True
        self.statusBar.text = ""

        # Plot the points on an ongoing basis
        while self.go:
            time.sleep(self.interval)
            for key, details in self.plotList.iteritems():
                points = details[POINTS]
                canvas = details[GRAPH]
                gauge = details[GAUGE]
                maxX = details[MAX_X]
                xCoords = details[X_COORDS]

                canvas.autoRefresh = False
                canvas.clear()

                # Draw the grid on an empty canvase
                self.DrawGrid(canvas, "forest green", "forest green", 1, 10, 10)

                # Add the new point to be added to the list and check ths list is not too long
                cpuUsage = details[CPU_DATA].getUsage()
                value = cpuUsage*details[HEIGHT]//100
                points.append(value)
                if len(points) > maxX:
                    points = points[1:]
                details[POINTS] = points

                # Now draw the line -- as you have to draw the line somewhere :-)
                self.DrawLineSegment(canvas, points, "green", 1, maxX, xCoords)

                # Update the instantaneous gauge (and value)
                gauge.autoRefresh = False
                gauge.clear()
                self.UpdateGauge(gauge, cpuUsage, "forest green")

                # Display the canvas now
                canvas.refresh(True)
                gauge.refresh(True)

            wx.SafeYield(self)

    def DrawGrid(self, canvas, xColour, yColour, thickness, xSpacing, ySpacing):
        canvas.thickness = thickness
        width, height = canvas.size

        # Vertical grid lines
        for x in xrange(canvas.size[0]/xSpacing):
            canvas.foregroundColor = xColour

            canvas.drawLine((x*xSpacing, 0), (x*xSpacing, height))
        canvas.drawLine((width, 0), (width, height))

        # Horizontal grid lines
        for y in xrange(canvas.size[1]/ySpacing):
            canvas.foregroundColor = yColour
            canvas.drawLine((0, y*ySpacing), (width, y*ySpacing))
        canvas.drawLine((0, height), (width, height))

    def DrawLineSegment(self, canvas, points, colour, thickness, maxX, xCoords):
        # This routine requires line segments so build them now
        width, height = canvas.size
        lines = []
        numPoints = len(points)
        startX = maxX - numPoints
        for ptr in xrange(numPoints - 1):
            x1 = xCoords[startX] + ptr
            y1 = points[ptr]
            y2 = points[ptr + 1]
            segment = (self.TransformPoints(x1, y1, x1 + 1, y2, height))
            lines.append(segment)

        canvas.foregroundColor = colour
        canvas.thickness = thickness

        # Draw the lines now!
        canvas.drawLineList(lines)

    def TransformPoints(self, x1, y1, x2, y2, height):
        # The wxBitmap origin is at the top left, so need to transform y coordinates
        return (x1, height - y1, x2, height - y2)

    def UpdateGauge(self, gauge, value, colour):
        width, height = gauge.size
        gauge.foregroundColor = colour
        gauge.fillColor = colour

        graphHeight = height - 20
        topY = (100 - value)*graphHeight//100
        sideY = graphHeight - topY

        gauge.drawRectangle((5, topY), (width - 10, sideY))
        gauge.foregroundColor = "green"

        text = "%s%%" % value
        gauge.drawText("%s%%" % value, (width//2 - 15, height - 15))

    def on_btnExit_mouseClick(self, event):
        self.go = False
        self.close()

if __name__ == '__main__':
    app = model.Application(SystemMonitor)
    app.MainLoop()
PerformanceCounter.py (text/x-python, 608 B)
import time
import win32pdh

class HostCPU:
    def __init__(self, machine=None):
        path = win32pdh.MakeCounterPath((machine, "Processor", "_Total", None, -1, "% Processor Time"))
        self.base = win32pdh.OpenQuery()
        self.counter = win32pdh.AddCounter(self.base, path)
        self.reset()
    #
    def reset(self):
        win32pdh.CollectQueryData(self.base)
    #
    def getUsage(self):
        win32pdh.CollectQueryData(self.base)
        # Get the formatted value of the counter
        return win32pdh.GetFormattedCounterValue(self.counter, win32pdh.PDH_FMT_LONG)[1]
SystemMonitor.rsrc.py (text/x-python, 3.1 KB)
{'application':{'type':'Application',
          'name':'Minimal',
    'backgrounds': [
    {'type':'Background',
          'name':'bgMin',
          'title':'System Monitor, V0.0.1',
          'size':(665, 630),
          'statusBar':1,
          # Enter the name of your icon file here
###          'icon':'*** YOUR ICON FILENAME ***',

        'menubar': {'type':'MenuBar',
         'menus': [
             {'type':'Menu',
             'name':'menuFile',
             'label':'&File',
             'items': [
                  {'type':'MenuItem',
                   'name':'menuFileExit',
                   'label':'E&xit\tAlt+X',
                   'command':'exit',
                  },
              ]
             },
         ]
     },
         'components': [

{'type':'StaticBox', 
    'name':'stbBox4', 
    'position':(350, 270), 
    'size':(285, 230), 
    'label':'No 4', 
    },

{'type':'BitmapCanvas', 
    'name':'bmpPlot4', 
    'position':(420, 290), 
    'size':(200, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'enabled':False, 
    },

{'type':'BitmapCanvas', 
    'name':'gInstant4', 
    'position':(360, 290), 
    'size':(39, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'font':{'faceName': 'Verdana', 'family': 'sansSerif', 'size': 8}, 
    },

{'type':'BitmapCanvas', 
    'name':'bmpPlot3', 
    'position':(90, 290), 
    'size':(200, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'enabled':False, 
    },

{'type':'StaticBox', 
    'name':'stbBox3', 
    'position':(20, 270), 
    'size':(285, 230), 
    'label':'No 3', 
    },

{'type':'BitmapCanvas', 
    'name':'gInstant3', 
    'position':(30, 290), 
    'size':(39, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'font':{'faceName': 'Verdana', 'family': 'sansSerif', 'size': 8}, 
    },

{'type':'BitmapCanvas', 
    'name':'gInstant2', 
    'position':(360, 30), 
    'size':(39, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'font':{'faceName': 'Verdana', 'family': 'sansSerif', 'size': 8}, 
    },

{'type':'BitmapCanvas', 
    'name':'bmpPlot2', 
    'position':(420, 30), 
    'size':(200, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'enabled':False, 
    },

{'type':'StaticBox', 
    'name':'stbBox2', 
    'position':(350, 10), 
    'size':(285, 230), 
    'label':'No 2', 
    },

{'type':'StaticBox', 
    'name':'stbBox1', 
    'position':(20, 10), 
    'size':(285, 230), 
    'label':'No 1', 
    },

{'type':'Button', 
    'name':'btnExit', 
    'position':(560, 520), 
    'label':'Exit', 
    },

{'type':'BitmapCanvas', 
    'name':'gInstant1', 
    'position':(30, 30), 
    'size':(39, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'font':{'faceName': 'Verdana', 'family': 'sansSerif', 'size': 8}, 
    },

{'type':'BitmapCanvas', 
    'name':'bmpPlot1', 
    'position':(90, 30), 
    'size':(200, 200), 
    'backgroundColor':(255, 255, 255, 255), 
    'enabled':False, 
    },

] # end components
} # end background
] # end backgrounds
} }
SystemMonitor.config (application/octet-stream, 154 B) - not displayed
setup.py (text/x-python, 1.8 KB)
# Setup for System Monitor

# This stuff makes the windows look like XP themes
manifest_template = '''
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="5.0.0.0"
    processorArchitecture="x86"
    name="%(prog)s"
    type="win32"
/>
<description>%(prog)s Program</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="X86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>
'''

RT_MANIFEST = 24

from distutils.core import setup
import py2exe
import os
import string

# find pythoncard resources, to add as 'data_files'
resources = []
for filename in os.listdir('.'):
    if filename.find('.rsrc.') > -1:
        resources += [filename]

# Add in the config file
for filename in os.listdir('.'):
    if filename.find('.config') > -1:
        resources += [filename]

# Add in the icon files
for filename in os.listdir('.'):
    if filename.find('.ico') > -1:
        resources += [filename]

# Relace "*** YOUR ICON FILENAME ***" with the name of your icon 

setup(
    windows=[{
        "script": "SystemMonitor.py",
        "icon_resources": [(1, "*** YOUR ICON FILENAME ***")],
        "other_resources": [(RT_MANIFEST, 1, manifest_template % dict(prog="System Monitor"))]
        }],
    py_modules = (["PerformanceCounter"]),
    options = {"py2exe": {
        "dll_excludes": (["gdiplus.dll"]),
        "excludes": (["Image"])
        }
    },
    data_files = [("", resources)]
)
SystemMonitor.iss (text/plain, 2 KB)
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "System Monitor"
#define MyAppVerName "System Monitor V0.0.1"
#define MyAppPublisher "*** YOUR COMPANY NAME ***"
#define MyAppExeName "SystemMonitor.exe"

[Setup]
AppName={#MyAppName}
AppVerName={#MyAppVerName}
AppPublisher={#MyAppPublisher}
DefaultDirName={pf}\{#MyAppName}
DisableDirPage=yes
DefaultGroupName={#MyAppName}
OutputBaseFilename=System Monitor Setup V0.0.1
SetupIconFile=C:\Projects\Python\Tools\SystemMonitor\dist\*** YOUR ICON FILENAME ***
Compression=lzma
SolidCompression=yes

[Languages]
Name: english; MessagesFile: compiler:Default.isl

[Tasks]
Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked

[Files]
Source: C:\Projects\Python\Tools\SystemMonitor\dist\SystemMonitor.exe; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\*** YOUR ICON FILENAME ***; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\library.zip; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\MSVCR71.dll; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\python25.dll; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\SystemMonitor.config; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\SystemMonitor.rsrc.py; DestDir: {app}; Flags: ignoreversion
Source: C:\Projects\Python\Tools\SystemMonitor\dist\w9xpopen.exe; DestDir: {app}; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: {group}\{#MyAppName}; Filename: {app}\{#MyAppExeName}
Name: {commondesktop}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; Tasks: desktopicon

[Run]
Filename: {app}\{#MyAppExeName}; Description: {cm:LaunchProgram,{#MyAppName}}; Flags: nowait postinstall skipifsilent
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.