summaryrefslogtreecommitdiff
path: root/src/python/graph.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/python/graph.py')
-rw-r--r--src/python/graph.py229
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)