VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphmatplotlib.py@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuihlpgraphmatplotlib.py 76553 2019-01-01 01:45:53Z vboxsync $
3
4"""
5Test Manager Web-UI - Graph Helpers - Implemented using matplotlib.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2019 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.virtualbox.org. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 76553 $"
30
31# Standard Python Import and extensions installed on the system.
32import re;
33import sys;
34if sys.version_info[0] >= 3:
35 from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module
36else:
37 from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module
38
39import matplotlib; # pylint: disable=F0401
40matplotlib.use('Agg'); # Force backend.
41import matplotlib.pyplot; # pylint: disable=F0401
42from numpy import arange as numpy_arange; # pylint: disable=E0611,E0401,wrong-import-order
43
44# Validation Kit imports.
45from testmanager.webui.wuihlpgraphbase import WuiHlpGraphBase;
46
47
48class WuiHlpGraphMatplotlibBase(WuiHlpGraphBase):
49 """ Base class for the matplotlib graphs. """
50
51 def __init__(self, sId, oData, oDisp):
52 WuiHlpGraphBase.__init__(self, sId, oData, oDisp);
53 self._fXkcdStyle = True;
54
55 def setXkcdStyle(self, fEnabled = True):
56 """ Enables xkcd style graphs for implementations that supports it. """
57 self._fXkcdStyle = fEnabled;
58 return True;
59
60 def _createFigure(self):
61 """
62 Wrapper around matplotlib.pyplot.figure that feeds the figure the
63 basic graph configuration.
64 """
65 if self._fXkcdStyle and matplotlib.__version__ > '1.2.9':
66 matplotlib.pyplot.xkcd(); # pylint: disable=E1101
67 matplotlib.rcParams.update({'font.size': self._cPtFont});
68
69 oFigure = matplotlib.pyplot.figure(figsize = (float(self._cxGraph) / self._cDpiGraph,
70 float(self._cyGraph) / self._cDpiGraph),
71 dpi = self._cDpiGraph);
72 return oFigure;
73
74 def _produceSvg(self, oFigure, fTightLayout = True):
75 """ Creates an SVG string from the given figure. """
76 oOutput = StringIO();
77 if fTightLayout:
78 oFigure.tight_layout();
79 oFigure.savefig(oOutput, format = 'svg');
80
81 if self._oDisp and self._oDisp.isBrowserGecko('20100101'):
82 # This browser will stretch images to fit if no size or width is given.
83 sSubstitute = r'\1 \3 reserveAspectRatio="xMidYMin meet"';
84 else:
85 # Chrome and IE likes to have the sizes as well as the viewBox.
86 sSubstitute = r'\1 \3 reserveAspectRatio="xMidYMin meet" \2 \4';
87 return re.sub(r'(<svg) (height="\d+pt") (version="\d+.\d+" viewBox="\d+ \d+ \d+ \d+") (width="\d+pt")',
88 sSubstitute,
89 oOutput.getvalue().decode('utf8'),
90 count = 1);
91
92class WuiHlpBarGraph(WuiHlpGraphMatplotlibBase):
93 """
94 Bar graph.
95 """
96
97 def __init__(self, sId, oData, oDisp = None):
98 WuiHlpGraphMatplotlibBase.__init__(self, sId, oData, oDisp);
99 self.fpMax = None;
100 self.fpMin = 0.0;
101 self.cxBarWidth = None;
102
103 def setRangeMax(self, fpMax):
104 """ Sets the max range."""
105 self.fpMax = float(fpMax);
106 return None;
107
108 def renderGraph(self): # pylint: disable=R0914
109 aoTable = self._oData.aoTable;
110
111 #
112 # Extract/structure the required data.
113 #
114 aoSeries = list();
115 for j in range(len(aoTable[1].aoValues)):
116 aoSeries.append(list());
117 asNames = list();
118 oXRange = numpy_arange(self._oData.getGroupCount());
119 fpMin = self.fpMin;
120 fpMax = self.fpMax;
121 if self.fpMax is None:
122 fpMax = float(aoTable[1].aoValues[0]);
123
124 for i in range(1, len(aoTable)):
125 asNames.append(aoTable[i].sName);
126 for j in range(len(aoTable[i].aoValues)):
127 fpValue = float(aoTable[i].aoValues[j]);
128 aoSeries[j].append(fpValue);
129 if fpValue < fpMin:
130 fpMin = fpValue;
131 if fpValue > fpMax:
132 fpMax = fpValue;
133
134 fpMid = fpMin + (fpMax - fpMin) / 2.0;
135
136 if self.cxBarWidth is None:
137 self.cxBarWidth = 1.0 / (len(aoTable[0].asValues) + 1.1);
138
139 # Render the PNG.
140 oFigure = self._createFigure();
141 oSubPlot = oFigure.add_subplot(1, 1, 1);
142
143 aoBars = list();
144 for i, _ in enumerate(aoSeries):
145 sColor = self.calcSeriesColor(i);
146 aoBars.append(oSubPlot.bar(oXRange + self.cxBarWidth * i,
147 aoSeries[i],
148 self.cxBarWidth,
149 color = sColor,
150 align = 'edge'));
151
152 #oSubPlot.set_title('Title')
153 #oSubPlot.set_xlabel('X-axis')
154 #oSubPlot.set_xticks(oXRange + self.cxBarWidth);
155 oSubPlot.set_xticks(oXRange);
156 oLegend = oSubPlot.legend(aoTable[0].asValues, loc = 'best', fancybox = True);
157 oLegend.get_frame().set_alpha(0.5);
158 oSubPlot.set_xticklabels(asNames, ha = "left");
159 #oSubPlot.set_ylabel('Y-axis')
160 oSubPlot.set_yticks(numpy_arange(fpMin, fpMax + (fpMax - fpMin) / 10 * 0, fpMax / 10));
161 oSubPlot.grid(True);
162 fpPadding = (fpMax - fpMin) * 0.02;
163 for i, _ in enumerate(aoBars):
164 aoRects = aoBars[i]
165 for j, _ in enumerate(aoRects):
166 oRect = aoRects[j];
167 fpValue = float(aoTable[j + 1].aoValues[i]);
168 if fpValue <= fpMid:
169 oSubPlot.text(oRect.get_x() + oRect.get_width() / 2.0,
170 oRect.get_height() + fpPadding,
171 aoTable[j + 1].asValues[i],
172 ha = 'center', va = 'bottom', rotation = 'vertical', alpha = 0.6, fontsize = 'small');
173 else:
174 oSubPlot.text(oRect.get_x() + oRect.get_width() / 2.0,
175 oRect.get_height() - fpPadding,
176 aoTable[j + 1].asValues[i],
177 ha = 'center', va = 'top', rotation = 'vertical', alpha = 0.6, fontsize = 'small');
178
179 return self._produceSvg(oFigure);
180
181
182
183
184class WuiHlpLineGraph(WuiHlpGraphMatplotlibBase):
185 """
186 Line graph.
187 """
188
189 def __init__(self, sId, oData, oDisp = None, fErrorBarY = False):
190 # oData must be a WuiHlpGraphDataTableEx like object.
191 WuiHlpGraphMatplotlibBase.__init__(self, sId, oData, oDisp);
192 self._cMaxErrorBars = 12;
193 self._fErrorBarY = fErrorBarY;
194
195 def setErrorBarY(self, fEnable):
196 """ Enables or Disables error bars, making this work like a line graph. """
197 self._fErrorBarY = fEnable;
198 return True;
199
200 def renderGraph(self): # pylint: disable=R0914
201 aoSeries = self._oData.aoSeries;
202
203 oFigure = self._createFigure();
204 oSubPlot = oFigure.add_subplot(1, 1, 1);
205 if self._oData.sYUnit is not None:
206 oSubPlot.set_ylabel(self._oData.sYUnit);
207 if self._oData.sXUnit is not None:
208 oSubPlot.set_xlabel(self._oData.sXUnit);
209
210 cSeriesNames = 0;
211 cYMin = 1000;
212 cYMax = 0;
213 for iSeries, oSeries in enumerate(aoSeries):
214 sColor = self.calcSeriesColor(iSeries);
215 cYMin = min(cYMin, min(oSeries.aoYValues));
216 cYMax = max(cYMax, max(oSeries.aoYValues));
217 if not self._fErrorBarY:
218 oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues, color = sColor);
219 elif len(oSeries.aoXValues) > self._cMaxErrorBars:
220 if matplotlib.__version__ < '1.3.0':
221 oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues, color = sColor);
222 else:
223 oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues,
224 yerr = [oSeries.aoYErrorBarBelow, oSeries.aoYErrorBarAbove],
225 errorevery = len(oSeries.aoXValues) / self._cMaxErrorBars,
226 color = sColor );
227 else:
228 oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues,
229 yerr = [oSeries.aoYErrorBarBelow, oSeries.aoYErrorBarAbove],
230 color = sColor);
231 cSeriesNames += oSeries.sName is not None;
232
233 if cYMin != 0 or cYMax != 0:
234 oSubPlot.set_ylim(bottom = 0);
235
236 if cSeriesNames > 0:
237 oLegend = oSubPlot.legend([oSeries.sName for oSeries in aoSeries], loc = 'best', fancybox = True);
238 oLegend.get_frame().set_alpha(0.5);
239
240 if self._sTitle is not None:
241 oSubPlot.set_title(self._sTitle);
242
243 if self._cxGraph >= 256:
244 oSubPlot.minorticks_on();
245 oSubPlot.grid(True, 'major', axis = 'both');
246 oSubPlot.grid(True, 'both', axis = 'x');
247
248 if True: # pylint: disable=W0125
249 # oSubPlot.axis('off');
250 #oSubPlot.grid(True, 'major', axis = 'none');
251 #oSubPlot.grid(True, 'both', axis = 'none');
252 matplotlib.pyplot.setp(oSubPlot, xticks = [], yticks = []);
253
254 return self._produceSvg(oFigure);
255
256
257class WuiHlpLineGraphErrorbarY(WuiHlpLineGraph):
258 """
259 Line graph with an errorbar for the Y axis.
260 """
261
262 def __init__(self, sId, oData, oDisp = None):
263 WuiHlpLineGraph.__init__(self, sId, oData, fErrorBarY = True);
264
265
266class WuiHlpMiniSuccessRateGraph(WuiHlpGraphMatplotlibBase):
267 """
268 Mini rate graph.
269 """
270
271 def __init__(self, sId, oData, oDisp = None):
272 """
273 oData must be a WuiHlpGraphDataTableEx like object, but only aoSeries,
274 aoSeries[].aoXValues, and aoSeries[].aoYValues will be used. The
275 values are expected to be a percentage, i.e. values between 0 and 100.
276 """
277 WuiHlpGraphMatplotlibBase.__init__(self, sId, oData, oDisp);
278 self.setFontSize(6);
279
280 def renderGraph(self): # pylint: disable=R0914
281 assert len(self._oData.aoSeries) == 1;
282 oSeries = self._oData.aoSeries[0];
283
284 # hacking
285 #self.setWidth(512);
286 #self.setHeight(128);
287 # end
288
289 oFigure = self._createFigure();
290 from mpl_toolkits.axes_grid.axislines import SubplotZero; # pylint: disable=E0401
291 oAxis = SubplotZero(oFigure, 111);
292 oFigure.add_subplot(oAxis);
293
294 # Disable all the normal axis.
295 oAxis.axis['right'].set_visible(False)
296 oAxis.axis['top'].set_visible(False)
297 oAxis.axis['bottom'].set_visible(False)
298 oAxis.axis['left'].set_visible(False)
299
300 # Use the zero axis instead.
301 oAxis.axis['yzero'].set_axisline_style('-|>');
302 oAxis.axis['yzero'].set_visible(True);
303 oAxis.axis['xzero'].set_axisline_style('-|>');
304 oAxis.axis['xzero'].set_visible(True);
305
306 if oSeries.aoYValues[-1] == 100:
307 sColor = 'green';
308 elif oSeries.aoYValues[-1] > 75:
309 sColor = 'yellow';
310 else:
311 sColor = 'red';
312 oAxis.plot(oSeries.aoXValues, oSeries.aoYValues, '.-', color = sColor, linewidth = 3);
313 oAxis.fill_between(oSeries.aoXValues, oSeries.aoYValues, facecolor = sColor, alpha = 0.5)
314
315 oAxis.set_xlim(left = -0.01);
316 oAxis.set_xticklabels([]);
317 oAxis.set_xmargin(1);
318
319 oAxis.set_ylim(bottom = 0, top = 100);
320 oAxis.set_yticks([0, 50, 100]);
321 oAxis.set_ylabel('%');
322 #oAxis.set_yticklabels([]);
323 oAxis.set_yticklabels(['', '%', '']);
324
325 return self._produceSvg(oFigure, False);
326
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette