diff options
Diffstat (limited to 'src/python/graph.py')
-rw-r--r-- | src/python/graph.py | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/python/graph.py b/src/python/graph.py new file mode 100644 index 0000000..9be9d6d --- /dev/null +++ b/src/python/graph.py @@ -0,0 +1,229 @@ +# libavg - Media Playback Engine. +# Copyright (C) 2003-2014 Ulrich von Zadow +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Current versions can be found at www.libavg.de + +import math +import time + +from libavg import avg, player, Point2D + + +class Graph(avg.DivNode): + def __init__(self, title='', getValue=None, parent=None, **kwargs): + super(Graph, self).__init__(**kwargs) + self.registerInstance(self, parent) + + self._getValue = getValue + self._xSkip = 2 + self._curUsage = 0 + self.sensitive = False + + avg.RectNode(parent=self, strokewidth=0, fillopacity=0.6, fillcolor="FFFFFF", + size=self.size) + self._textNode0 = avg.WordsNode(parent=self, x=10, y=self.size.y - 22, + color="000080") + self._textNode1 = avg.WordsNode(parent=self, x=10, y=self.size.y - 39, + color="000080") + self._maxLineNode = avg.PolyLineNode(parent=self, color="880000") + self._lineNode = avg.PolyLineNode(parent=self, color="008000") + self.__graphText = avg.WordsNode(parent=self, x=10, y=0, color="000080") + self.__graphText.text = title + self._setup() + + def _setup(self): + raise RuntimeError('Please overload _setup() function') + + +class AveragingGraph(Graph): + + def __init__(self, title='', getValue=None, parent=None, **kwargs): + super(AveragingGraph, self).__init__(title, getValue, parent, **kwargs) + self.registerInstance(self, None) + + def unlink(self, kill): + player.clearInterval(self.__interval) + self.__interval = None + super(AveragingGraph, self).unlink(kill) + + def _setup(self): + self.__interval = player.setInterval(1000, self._nextMemSample) + self.__numSamples = 0 + self._usage = [0] + self._maxUsage = [0] + self._minutesUsage = [0] + self._minutesMaxUsage = [0] + self._nextMemSample() + + def _nextMemSample(self): + curUsage = self._getValue() + self._usage.append(curUsage) + maxUsage = self._maxUsage[-1] + + if curUsage > maxUsage: + maxUsage = curUsage + lastMaxChangeTime = time.time() + self._textNode1.text = ("Last increase in maximum: " + + time.strftime("%d.%m.%Y %H:%M:%S", + time.localtime(lastMaxChangeTime))) + self._maxUsage.append(maxUsage) + self.__numSamples += 1 + + if self.__numSamples % 60 == 0: + lastMinuteAverage = sum(self._usage[-60:]) / 60 + self._minutesUsage.append(lastMinuteAverage) + self._minutesMaxUsage.append(maxUsage) + + if self.__numSamples < 60 * 60: + self._plotLine(self._usage, self._lineNode, maxUsage) + self._plotLine(self._maxUsage, self._maxLineNode, maxUsage) + else: + self._plotLine(self._minutesUsage, self._lineNode, maxUsage) + self._plotLine(self._minutesMaxUsage, self._maxLineNode, maxUsage) + + self._textNode0.text = ("Max. memory usage: %(size).2f MB" % + {"size": maxUsage / (1024 * 1024.0)}) + + if self.__numSamples % 3600 == 0: + del self._usage[0:3600] + del self._maxUsage[0:3599] + if self.__numSamples == 604800: + self.__numSamples == 0 + + def _plotLine(self, data, node, maxy): + yfactor = (self.size.y - 10.0) / float(maxy) + xfactor = (self.size.x - 10.0) / float(len(data) - 1) + node.pos = [(pos[0] * xfactor + 10, (maxy - pos[1]) * yfactor + 10.0) + for pos in enumerate(data)] + + +class SlidingGraph(Graph): + def __init__(self, title='', getValue=None, limit=120.0, parent=None, **kwargs): + super(SlidingGraph, self).__init__(title, getValue, parent, **kwargs) + self.registerInstance(self, None) + self._limitValue = float(limit) + + def _setup(self): + self.__frameHandlerID = player.subscribe(avg.Player.ON_FRAME, + self._nextFrameTimeSample) + self._numSamples = 0 + self._lastCurUsage = 0 + self._maxFrameTime = 0 + self._values = [] + + def _nextFrameTimeSample(self): + val = self._frameTimeSample() + self._appendValue(val) + self._numSamples += 1 + + def _appendValue(self, value): + maxValue = min(self._limitValue, value) + y = self.height - (self.height * (maxValue / self._limitValue)) + y = max(0, y) + numValues = int(self.width / self._xSkip) + self._values = (self._values + [y])[-numValues:] + self._plotGraph() + + def _frameTimeSample(self): + frameTime = self._getValue() + diff = frameTime - self._lastCurUsage + if self._numSamples < 2: + self._maxFrameTime = 0 + if diff > self._maxFrameTime: + lastMaxChangeTime = time.time() + self._maxFrameTime = diff + self._textNode0.text = ("Max FrameTime: %.f" % self._maxFrameTime + " ms" + + " Time: " + time.strftime("%d.%m.%Y %H:%M:%S", + time.localtime(lastMaxChangeTime))) + + self._lastCurUsage = frameTime + self._textNode1.text = ("Current FrameTime: %.f" % diff + " ms") + return diff + + def _plotGraph(self): + self._lineNode.pos = self._getCoords() + + def _getCoords(self): + return zip(xrange(0, len(self._values) * self._xSkip, self._xSkip), self._values) + + +class BinBar(avg.DivNode): + def __init__(self, label, parent=None, **kwargs): + super(BinBar, self).__init__(**kwargs) + self.registerInstance(self, parent) + + avg.WordsNode(text=label, fontsize=8, alignment='center', + pos=(self.size.x / 2, self.size.y - 12), parent=self) + + self._vbar = avg.RectNode(opacity=0, fillopacity=0.4, + fillcolor='ff0000', parent=self) + + self.update(0) + + def update(self, value): + value = min(max(value, 0), 1) + + height = (self.size.y - 15) * value + self._vbar.size = (self.size.x - 2, height) + self._vbar.pos = (1, self.size.y - height - 12) + + +class BinsGraph(avg.DivNode): + def __init__(self, binsThresholds, parent=None, **kwargs): + super(BinsGraph, self).__init__(**kwargs) + self.registerInstance(self, parent) + + avg.RectNode(size=self.size, parent=self) + colWidth = self.size.x / len(binsThresholds) + self._binBars = [BinBar(str(int(thr)), + pos=(idx * colWidth, 0), + size=(colWidth, self.size.y), + parent=self) + for idx, thr in enumerate(binsThresholds)] + + def update(self, values): + s = sum(values) + + for binBar, value in zip(self._binBars, values): + normValue = float(value) / s + logValue = math.log10(normValue * 9 + 1) if normValue != 0 else 0 + binBar.update(logValue) + + +class SlidingBinnedGraph(SlidingGraph): + def __init__(self, title='', getValue=None, limit=120.0, binsThresholds=[], + parent=None, **kwargs): + super(SlidingBinnedGraph, self).__init__(title, getValue, limit, parent, **kwargs) + if not all([isinstance(x, (int, float)) for x in binsThresholds]): + raise RuntimeError('Bins thresholds must be provided as list of numbers') + + self._bins = [0] * len(binsThresholds) + self._binsThresholds = binsThresholds + self._binsGraph = BinsGraph(binsThresholds=binsThresholds, + pos=(self.size.x - 120, 5), size=(90, self.size.y - 10), + parent=self) + + def _appendValue(self, value): + for i in xrange(len(self._binsThresholds) - 1, -1, -1): + if value >= self._binsThresholds[i]: + self._bins[i] += 1 + break + + if sum(self._bins) % 100 == 0: + self._binsGraph.update(self._bins) + + super(SlidingBinnedGraph, self)._appendValue(value) |