CPU plot demoΒΆ

../_images/CPUplot.png
import os
import sys
import numpy as np

from qwt.qt.QtGui import (QApplication, QColor, QBrush, QWidget, QVBoxLayout,
                          QLabel)
from qwt.qt.QtCore import QRect, QTime
from qwt.qt.QtCore import Qt
from qwt import (QwtPlot, QwtPlotMarker, QwtScaleDraw, QwtLegend, QwtPlotCurve,
                 QwtPlotItem, QwtLegendData, QwtText)


class CpuStat:
    User = 0
    Nice = 1
    System = 2
    Idle = 3
    counter = 0
    dummyValues = (
        ( 103726, 0, 23484, 819556 ),
        ( 103783, 0, 23489, 819604 ),
        ( 103798, 0, 23490, 819688 ),
        ( 103820, 0, 23490, 819766 ),
        ( 103840, 0, 23493, 819843 ),
        ( 103875, 0, 23499, 819902 ),
        ( 103917, 0, 23504, 819955 ),
        ( 103950, 0, 23508, 820018 ),
        ( 103987, 0, 23510, 820079 ),
        ( 104020, 0, 23513, 820143 ),
        ( 104058, 0, 23514, 820204 ),
        ( 104099, 0, 23520, 820257 ),
        ( 104121, 0, 23525, 820330 ),
        ( 104159, 0, 23530, 820387 ),
        ( 104176, 0, 23534, 820466 ),
        ( 104215, 0, 23538, 820523 ),
        ( 104245, 0, 23541, 820590 ),
        ( 104267, 0, 23545, 820664 ),
        ( 104311, 0, 23555, 820710 ),
        ( 104355, 0, 23565, 820756 ),
        ( 104367, 0, 23567, 820842 ),
        ( 104383, 0, 23572, 820921 ),
        ( 104396, 0, 23577, 821003 ),
        ( 104413, 0, 23579, 821084 ),
        ( 104446, 0, 23588, 821142 ),
        ( 104521, 0, 23594, 821161 ),
        ( 104611, 0, 23604, 821161 ),
        ( 104708, 0, 23607, 821161 ),
        ( 104804, 0, 23611, 821161 ),
        ( 104895, 0, 23620, 821161 ),
        ( 104993, 0, 23622, 821161 ),
        ( 105089, 0, 23626, 821161 ),
        ( 105185, 0, 23630, 821161 ),
        ( 105281, 0, 23634, 821161 ),
        ( 105379, 0, 23636, 821161 ),
        ( 105472, 0, 23643, 821161 ),
        ( 105569, 0, 23646, 821161 ),
        ( 105666, 0, 23649, 821161 ),
        ( 105763, 0, 23652, 821161 ),
        ( 105828, 0, 23661, 821187 ),
        ( 105904, 0, 23666, 821206 ),
        ( 105999, 0, 23671, 821206 ),
        ( 106094, 0, 23676, 821206 ),
        ( 106184, 0, 23686, 821206 ),
        ( 106273, 0, 23692, 821211 ),
        ( 106306, 0, 23700, 821270 ),
        ( 106341, 0, 23703, 821332 ),
        ( 106392, 0, 23709, 821375 ),
        ( 106423, 0, 23715, 821438 ),
        ( 106472, 0, 23721, 821483 ),
        ( 106531, 0, 23727, 821517 ),
        ( 106562, 0, 23732, 821582 ),
        ( 106597, 0, 23736, 821643 ),
        ( 106633, 0, 23737, 821706 ),
        ( 106666, 0, 23742, 821768 ),
        ( 106697, 0, 23744, 821835 ),
        ( 106730, 0, 23748, 821898 ),
        ( 106765, 0, 23751, 821960 ),
        ( 106799, 0, 23754, 822023 ),
        ( 106831, 0, 23758, 822087 ),
        ( 106862, 0, 23761, 822153 ),
        ( 106899, 0, 23763, 822214 ),
        ( 106932, 0, 23766, 822278 ),
        ( 106965, 0, 23768, 822343 ),
        ( 107009, 0, 23771, 822396 ),
        ( 107040, 0, 23775, 822461 ),
        ( 107092, 0, 23780, 822504 ),
        ( 107143, 0, 23787, 822546 ),
        ( 107200, 0, 23795, 822581 ),
        ( 107250, 0, 23803, 822623 ),
        ( 107277, 0, 23810, 822689 ),
        ( 107286, 0, 23810, 822780 ),
        ( 107313, 0, 23817, 822846 ),
        ( 107325, 0, 23818, 822933 ),
        ( 107332, 0, 23818, 823026 ),
        ( 107344, 0, 23821, 823111 ),
        ( 107357, 0, 23821, 823198 ),
        ( 107368, 0, 23823, 823284 ),
        ( 107375, 0, 23824, 823377 ),
        ( 107386, 0, 23825, 823465 ),
        ( 107396, 0, 23826, 823554 ),
        ( 107422, 0, 23830, 823624 ),
        ( 107434, 0, 23831, 823711 ),
        ( 107456, 0, 23835, 823785 ),
        ( 107468, 0, 23838, 823870 ),
        ( 107487, 0, 23840, 823949 ),
        ( 107515, 0, 23843, 824018 ),
        ( 107528, 0, 23846, 824102 ),
        ( 107535, 0, 23851, 824190 ),
        ( 107548, 0, 23853, 824275 ),
        ( 107562, 0, 23857, 824357 ),
        ( 107656, 0, 23863, 824357 ),
        ( 107751, 0, 23868, 824357 ),
        ( 107849, 0, 23870, 824357 ),
        ( 107944, 0, 23875, 824357 ),
        ( 108043, 0, 23876, 824357 ),
        ( 108137, 0, 23882, 824357 ),
        ( 108230, 0, 23889, 824357 ),
        ( 108317, 0, 23902, 824357 ),
        ( 108412, 0, 23907, 824357 ),
        ( 108511, 0, 23908, 824357 ),
        ( 108608, 0, 23911, 824357 ),
        ( 108704, 0, 23915, 824357 ),
        ( 108801, 0, 23918, 824357 ),
        ( 108891, 0, 23928, 824357 ),
        ( 108987, 0, 23932, 824357 ),
        ( 109072, 0, 23943, 824361 ),
        ( 109079, 0, 23943, 824454 ),
        ( 109086, 0, 23944, 824546 ),
        ( 109098, 0, 23950, 824628 ),
        ( 109108, 0, 23955, 824713 ),
        ( 109115, 0, 23957, 824804 ),
        ( 109122, 0, 23958, 824896 ),
        ( 109132, 0, 23959, 824985 ),
        ( 109142, 0, 23961, 825073 ),
        ( 109146, 0, 23962, 825168 ),
        ( 109153, 0, 23964, 825259 ),
        ( 109162, 0, 23966, 825348 ),
        ( 109168, 0, 23969, 825439 ),
        ( 109176, 0, 23971, 825529 ),
        ( 109185, 0, 23974, 825617 ),
        ( 109193, 0, 23977, 825706 ),
        ( 109198, 0, 23978, 825800 ),
        ( 109206, 0, 23978, 825892 ),
        ( 109212, 0, 23981, 825983 ),
        ( 109219, 0, 23981, 826076 ),
        ( 109225, 0, 23981, 826170 ),
        ( 109232, 0, 23984, 826260 ),
        ( 109242, 0, 23984, 826350 ),
        ( 109255, 0, 23986, 826435 ),
        ( 109268, 0, 23987, 826521 ),
        ( 109283, 0, 23990, 826603 ),
        ( 109288, 0, 23991, 826697 ),
        ( 109295, 0, 23993, 826788 ),
        ( 109308, 0, 23994, 826874 ),
        ( 109322, 0, 24009, 826945 ),
        ( 109328, 0, 24011, 827037 ),
        ( 109338, 0, 24012, 827126 ),
        ( 109347, 0, 24012, 827217 ),
        ( 109354, 0, 24017, 827305 ),
        ( 109367, 0, 24017, 827392 ),
        ( 109371, 0, 24019, 827486 ),
        )
    
    def __init__(self):
        self.procValues = self.__lookup()

    def statistic(self):
        values = self.__lookup()
        userDelta = 0.0
        for i in [CpuStat.User, CpuStat.Nice]:
            userDelta += (values[i] - self.procValues[i])
        systemDelta = values[CpuStat.System] - self.procValues[CpuStat.System]
        totalDelta = 0.0
        for i in range(len(self.procValues)):
            totalDelta += (values[i] - self.procValues[i])
        self.procValues = values
        return 100.0*userDelta/totalDelta, 100.0*systemDelta/totalDelta

    def upTime(self):
        result = QTime()
        for item in self.procValues:
            result = result.addSecs(item/100)
        return result

    def __lookup(self):
        if os.path.exists("/proc/stat"):
            for line in open("/proc/stat"):
                words = line.split()
                if words[0] == "cpu" and len(words) >= 5:
                    return [float(w) for w in words[1:]]
        else:
            result = CpuStat.dummyValues[CpuStat.counter]
            CpuStat.counter += 1
            CpuStat.counter %= len(CpuStat.dummyValues)
            return result


class CpuPieMarker(QwtPlotMarker):
    def __init__(self, *args):
        QwtPlotMarker.__init__(self, *args)
        self.setZ(1000.0)
        self.setRenderHint(QwtPlotItem.RenderAntialiased, True)
        
    def rtti(self):
        return QwtPlotItem.Rtti_PlotUserItem

    def draw(self, painter, xMap, yMap, rect):
        margin = 5
        pieRect = QRect()
        pieRect.setX(rect.x() + margin)
        pieRect.setY(rect.y() + margin)
        pieRect.setHeight(yMap.transform(80.0))
        pieRect.setWidth(pieRect.height())

        angle = 3*5760/4
        for key in ["User", "System", "Idle"]:
            curve = self.plot().cpuPlotCurve(key)
            if curve.dataSize():
                value = int(5760*curve.sample(0).y()/100.0)
                painter.save()
                painter.setBrush(QBrush(curve.pen().color(),
                                              Qt.SolidPattern))
                painter.drawPie(pieRect, -angle, -value)
                painter.restore()
                angle += value


class TimeScaleDraw(QwtScaleDraw):
    def __init__(self, baseTime, *args):
        QwtScaleDraw.__init__(self, *args)
        self.baseTime = baseTime
 
    def label(self, value):
        upTime = self.baseTime.addSecs(int(value))
        return QwtText(upTime.toString())


class Background(QwtPlotItem):
    def __init__(self):
        QwtPlotItem.__init__(self)
        self.setZ(0.0)

    def rtti(self):
        return QwtPlotItem.Rtti_PlotUserItem

    def draw(self, painter, xMap, yMap, rect):
        c = QColor(Qt.white)
        r = QRect(rect)

        for i in range(100, 0, -10):
            r.setBottom(yMap.transform(i - 10))
            r.setTop(yMap.transform(i))
            painter.fillRect(r, c)
            c = c.darker(110)


class CpuCurve(QwtPlotCurve):
    def __init__(self, *args):
        QwtPlotCurve.__init__(self, *args)
        self.setRenderHint(QwtPlotItem.RenderAntialiased)

    def setColor(self, color):
        c = QColor(color)
        c.setAlpha(150)

        self.setPen(c)
        self.setBrush(c)

    
HISTORY = 60

class CpuPlot(QwtPlot):
    def __init__(self, *args):
        QwtPlot.__init__(self, *args)

        self.curves = {}
        self.data = {}
        self.timeData = 1.0 * np.arange(HISTORY-1, -1, -1)
        self.cpuStat = CpuStat()

        self.setAutoReplot(False)

        self.plotLayout().setAlignCanvasToScales(True)
        
        legend = QwtLegend()
        legend.setDefaultItemMode(QwtLegendData.Checkable)
        self.insertLegend(legend, QwtPlot.RightLegend)
        
        self.setAxisTitle(QwtPlot.xBottom, "System Uptime [h:m:s]")
        self.setAxisScaleDraw(
            QwtPlot.xBottom, TimeScaleDraw(self.cpuStat.upTime()))
        self.setAxisScale(QwtPlot.xBottom, 0, HISTORY)
        self.setAxisLabelRotation(QwtPlot.xBottom, -50.0)
        self.setAxisLabelAlignment(
            QwtPlot.xBottom, Qt.AlignLeft | Qt.AlignBottom)

        self.setAxisTitle(QwtPlot.yLeft, "Cpu Usage [%]")
        self.setAxisScale(QwtPlot.yLeft, 0, 100)

        background = Background()
        background.attach(self)

        pie = CpuPieMarker()
        pie.attach(self)
        
        curve = CpuCurve('System')
        curve.setColor(Qt.red)
        curve.attach(self)
        self.curves['System'] = curve
        self.data['System'] = np.zeros(HISTORY, np.float)

        curve = CpuCurve('User')
        curve.setColor(Qt.blue)
        curve.setZ(curve.z() - 1.0)
        curve.attach(self)
        self.curves['User'] = curve
        self.data['User'] = np.zeros(HISTORY, np.float)

        curve = CpuCurve('Total')
        curve.setColor(Qt.black)
        curve.setZ(curve.z() - 2.0)
        curve.attach(self)
        self.curves['Total'] = curve
        self.data['Total'] = np.zeros(HISTORY, np.float)

        curve = CpuCurve('Idle')
        curve.setColor(Qt.darkCyan)
        curve.setZ(curve.z() - 3.0)
        curve.attach(self)
        self.curves['Idle'] = curve
        self.data['Idle'] = np.zeros(HISTORY, np.float)

        self.showCurve(self.curves['System'], True)
        self.showCurve(self.curves['User'], True)
        self.showCurve(self.curves['Total'], False)
        self.showCurve(self.curves['Idle'], False)

        self.startTimer(1000)

        legend.SIG_CHECKED.connect(self.showCurve)
        self.replot()

    def timerEvent(self, e):
        for data in self.data.values():
            data[1:] = data[0:-1]
        self.data["User"][0], self.data["System"][0] = self.cpuStat.statistic()
        self.data["Total"][0] = self.data["User"][0] + self.data["System"][0]
        self.data["Idle"][0] = 100.0 - self.data["Total"][0]

        self.timeData += 1.0

        self.setAxisScale(
            QwtPlot.xBottom, self.timeData[-1], self.timeData[0])
        for key in self.curves.keys():
            self.curves[key].setData(self.timeData, self.data[key])

        self.replot()

    def showCurve(self, item, on, index=None):
        item.setVisible(on)
        self.legend().legendWidget(item).setChecked(on)
        self.replot()

    def cpuPlotCurve(self, key):
        return self.curves[key]


def make():
    demo = QWidget()
    demo.setWindowTitle('Cpu Plot')
    
    plot = CpuPlot(demo)
    plot.setTitle("History")
    
    label = QLabel("Press the legend to en/disable a curve", demo)

    layout = QVBoxLayout(demo)
    layout.addWidget(plot)
    layout.addWidget(label)

    demo.resize(600, 400)
    demo.show()
    return demo


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = make()
    sys.exit(app.exec_())