2
This file is part of the KDE project
4
Copyright (c) 2006 - 2009 John Tapsell <tapsell@kde.org>
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of the GNU General Public
8
License version 2 or at your option version 3 as published by
9
the Free Software Foundation.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#ifndef KSIGNALPLOTTER_H
23
#define KSIGNALPLOTTER_H
25
#include <QtCore/QList>
26
#include <QtCore/QString>
27
#include <QtGui/QColor>
28
#include <QtGui/QFont>
29
#include <QtGui/QWidget>
30
#include <klocalizedstring.h>
31
#include <kcomponentdata.h>
35
class QGraphicsSceneResizeEvent;
36
class KSignalPlotterPrivate;
38
//Make sure only declare KLocalizedString once
39
#ifndef KGRAPHICSSIGNALPLOTTER_H
40
Q_DECLARE_METATYPE(KLocalizedString)
43
/** \class KSignalPlotter
44
* \brief The KSignalPlotter widget draws a real time graph of data that updates continually.
47
* \li Points are joined by a bezier curve.
48
* \li Lines are anti-aliased
49
* \li Background can be set as a specified SVG
50
* \li The lines can be reordered
51
* \li Uses as little memory and CPU as possible
52
* \li Graph can be smoothed using the formula (value * 2 + last_value)/3
53
* \li Can cope with positive/negative infinity and NaN values.
57
* KSignalPlotter *s = KSignalPlotter(parent);
58
* s->addBeam(Qt::blue);
59
* s->addBeam(Qt::green);
65
* Note that the number of horizontal lines is calculated automatically based on the axis font size, even if the axis labels are not shown.
67
* Smoothing looks very nice visually and is enabled by default. It can be disabled with setSmoothGraph().
69
* \image KSignalPlotter.png Example KSignalPlotter with two beams
71
class KDE_EXPORT KSignalPlotter : public QWidget
74
Q_PROPERTY(qreal minimumValue READ minimumValue WRITE setMinimumValue)
75
Q_PROPERTY(qreal maximumValue READ maximumValue WRITE setMaximumValue)
76
Q_PROPERTY(bool useAutoRange READ useAutoRange WRITE setUseAutoRange)
77
Q_PROPERTY(KLocalizedString unit READ unit WRITE setUnit)
78
Q_PROPERTY(qreal scaleDownBy READ scaleDownBy WRITE setScaleDownBy)
79
Q_PROPERTY(uint horizontalScale READ horizontalScale WRITE setHorizontalScale)
80
Q_PROPERTY(bool showHorizontalLines READ showHorizontalLines WRITE setShowHorizontalLines)
81
Q_PROPERTY(bool showVerticalLines READ showVerticalLines WRITE setShowVerticalLines)
82
Q_PROPERTY(bool verticalLinesScroll READ verticalLinesScroll WRITE setVerticalLinesScroll)
83
Q_PROPERTY(uint verticalLinesDistance READ verticalLinesDistance WRITE setVerticalLinesDistance)
84
Q_PROPERTY(bool showAxis READ showAxis WRITE setShowAxis)
85
Q_PROPERTY(QString svgBackground READ svgBackground WRITE setSvgBackground)
86
Q_PROPERTY(bool thinFrame READ thinFrame WRITE setThinFrame)
87
Q_PROPERTY(int maxAxisTextWidth READ maxAxisTextWidth WRITE setMaxAxisTextWidth)
88
Q_PROPERTY(bool smoothGraph READ smoothGraph WRITE setSmoothGraph)
89
Q_PROPERTY(bool stackGraph READ stackGraph WRITE setStackGraph)
90
Q_PROPERTY(int fillOpacity READ fillOpacity WRITE setFillOpacity)
93
KSignalPlotter( QWidget *parent = 0);
94
virtual ~KSignalPlotter();
96
/** \brief Add a new line to the graph plotter, with the specified color.
98
* Note that the order you add the beams in must be the same order that
99
* the beam data is given in (Unless you reorder the beams).
101
* \param color Color of beam - does not have to be unique.
103
void addBeam( const QColor &color );
105
/** \brief Add data to the graph, and advance the graph by one time period.
107
* The data must be given as a list in the same order that the beams were
108
* added (or consequently reordered). If samples.count() != numBeams(),
109
* a warning is printed and the data discarded.
111
* To indicate that no data is available for a given beam, set its value to
112
* (non-signalling) NaN.
117
* KSignalPlotter *s = KSignalPlotter(parent);
118
* s->addBeam(Qt::red);
119
* s->addBeam(Qt::green);
120
* s->addBeam(Qt::blue);
121
* signalPlotter->addSample( QList<qreal>() << std::numeric_limits<double>::quiet_NaN() << 1.0/0 << 10.0 );
124
* This indicates that no data is available yet for red (so the beam will not be drawn for this section),
125
* that's it positive infinity for green, and 10 for blue.
127
* Infinity is handled by drawing a straight line up to the top or bottom of the display, and does not affect the range.
128
* For the above example, the displayed range would now be 0 to 10.
130
void addSample( const QList<qreal> &samples );
132
/** \brief Reorder the beams into the order given.
136
* KSignalPlotter *s = KSignalPlotter(parent);
137
* s->addBeam(Qt::blue);
138
* s->addBeam(Qt::green);
139
* s->addBeam(Qt::red);
140
* QList<int> neworder;
141
* neworder << 2 << 0 << 1;
142
* s->reorderBeams( newOrder);
143
* //Now the order is red, blue then green
146
* The size of the \p newOrder list must be equal to the result of numBeams().
147
* \param newOrder New order of beams.
149
void reorderBeams( const QList<int>& newOrder );
151
/** \brief Removes the beam at the specified index.
153
* This causes the graph to be redrawn with the specified beam completely
156
void removeBeam( int index );
158
/** \brief Get the color of the beam at the specified index.
162
* KSignalPlotter *s = KSignalPlotter(parent);
163
* s->addBeam(Qt::blue);
164
* s->addBeam(Qt::green);
165
* s->addBeam(Qt::red);
167
* QColor color = s->beamColor(0); //returns blue
172
QColor beamColor( int index ) const;
174
/** \brief Set the color of the beam at the specified index.
178
void setBeamColor( int index, const QColor &color );
180
/** \brief Returns the number of beams. */
181
int numBeams() const;
183
/** \brief Set the axis units with a localized string.
185
* The localized string must contain a placeholder "%1" which is substituted for the value.
186
* The plural form (ki18np) can be used if the unit string changes depending on the number (for example
187
* "1 second", "2 seconds").
192
* KSignalPlotter plotter;
193
* plotter.setUnit( ki18ncp("Units", "%1 second", "%1 seconds") );
194
* QString formattedString = plotter.valueAsString(3.4); //returns "3.4 seconds"
197
* Typically a new unit would be set when setScaleDownBy is called.
198
* Note that even the singular should use "%1 second" instead of "1 second", so that a value of -1 works correctly.
200
* \see unit(), setScaleDownBy()
202
void setUnit( const KLocalizedString &unit );
204
/** \brief The localizable units used on the vertical axis of the graph.
206
* The returns the localizable string set with setUnit().
208
* Default is the string "%1" - i.e. to just display the number.
212
KLocalizedString unit() const;
214
/** \brief Scale all the values down by the given amount.
216
* This is useful when the data is given in, say, kilobytes, but you set
217
* the units as megabytes. Thus you would have to call this with @p value
218
* set to 1024. This affects all the data already entered.
220
* Typically this is followed by calling setUnit() to set the display axis
221
* units. Default value is 1.
223
void setScaleDownBy( qreal value );
225
/** \brief Amount scaled down by.
227
* \sa setScaleDownBy */
228
qreal scaleDownBy() const;
230
/** \brief Set whether to scale the graph automatically beyond the given range.
232
* If true, the range on vertical axis is automatically expanded from the
233
* data available, expanding beyond the range set by changeRange() if data
234
* values are outside of this range.
236
* Regardless whether this is set of not, the range of the vertical axis
237
* will never be less than the range given by maximumValue() and minimumvalue().
239
* \param value Whether to scale beyond the given range. Default is true.
243
void setUseAutoRange( bool value );
245
/** \brief Whether the vertical axis range is set automatically.
247
bool useAutoRange() const;
249
/** \brief Change the minimum and maximum values drawn on the graph.
251
* Note that these values are sanitised. For example, if you
252
* set the minimum as 3, and the maximum as 97, then the graph
253
* would be drawn between 0 and 100. The algorithm to determine
254
* this "nice range" attempts to minimize the number of non-zero
257
* If autoRange() is true, then this range is taking as a 'hint'.
258
* The range will never be smaller than the given range, but can grow
259
* if there are values larger than the given range.
261
* This is equivalent to calling
263
* setMinimumValue(min);
264
* setMaximumValue(max);
267
* \sa setMinimumValue(), setMaximumValue(), minimumValue(), maximumValue()
269
void changeRange( qreal min, qreal max );
271
/** \brief Set the min value hint for the vertical axis.
273
* \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */
274
void setMinimumValue( qreal min );
276
/** \brief Get the min value hint for the vertical axis.
278
* \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */
279
qreal minimumValue() const;
281
/** \brief Set the max value hint for the vertical axis. *
283
* \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */
284
void setMaximumValue( qreal max );
286
/** \brief Get the maximum value hint for the vertical axis.
288
* \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */
289
qreal maximumValue() const;
291
/** \brief Get the current maximum value on the y-axis.
293
* This will never be lower than maximumValue(), and if autoRange() is true,
294
* it will be equal or larger (due to rounding up to make it a nice number)
295
* than the highest value being shown.
297
qreal currentMaximumRangeValue() const;
298
/** \brief Get the current minimum value on the y-axis.
300
* This will never be lower than minimumValue(), and if autoRange() is true,
301
* it will be equal or larger (due to rounding up to make it a nice number)
302
* than the highest value being shown.
304
qreal currentMinimumRangeValue() const;
306
/** \brief Set the number of pixels horizontally between data points.
308
void setHorizontalScale( uint scale );
309
/** \brief The number of pixels horizontally between data points.
311
int horizontalScale() const;
313
/** \brief Set whether to draw the vertical grid lines.
314
* Default is false. */
315
void setShowVerticalLines( bool value );
316
/** \brief Whether to draw the vertical grid lines.
317
* Default is false. */
318
bool showVerticalLines() const;
320
/** \brief Set the horizontal distance, in pixels, between the vertical grid lines.
321
* Must be a distance of 1 or more.
322
* Default is 30 pixels. */
323
void setVerticalLinesDistance( uint distance );
324
/** \brief The horizontal distance, in pixels, between the vertical grid lines.
325
* Default is 30 pixels. */
326
uint verticalLinesDistance() const;
328
/** \brief Set whether the vertical lines move with the data.
329
* Default is true. This has no effect is showVerticalLines is false. */
330
void setVerticalLinesScroll( bool value );
331
/** \brief Whether the vertical lines move with the data.
332
* Default is true. This has no effect is showVerticalLines is false. */
333
bool verticalLinesScroll() const;
335
/** \brief Set whether to draw the horizontal grid lines.
336
* Default is true. */
337
void setShowHorizontalLines( bool value );
338
/** \brief Whether to draw the horizontal grid lines.
339
* Default is true. */
340
bool showHorizontalLines() const;
342
/** \brief The number of decimal places used for the axis labels
344
* This is calculated based on the current range */
345
int currentAxisPrecision() const;
347
/** \brief Set whether to show the vertical axis labels.
350
* \sa showAxis(), setAxisFont(), setAxisFontColor(), setMaxAxisTextWidth() */
351
void setShowAxis( bool show );
352
/** \brief Whether to show the vertical axis labels.
355
* \sa setShowAxis(), axisFont(), axisFontColor(), maxAxisTextWidth() */
356
bool showAxis() const;
358
/** \brief Set the filename of the SVG background.
360
* Set to empty (default) to disable again. */
361
void setSvgBackground( const QString &filename );
363
/** \brief The filename of the SVG background. */
364
QString svgBackground() const;
366
/** \brief Return the last value that we have for the given beam index.
368
* \return last value, or 0 if not known. */
369
qreal lastValue( int index) const;
371
/** \brief Return a translated string for the last value at the given index.
373
* Returns, for example, "34 %" or "100 KB" for the given beam index,
374
* using the last value set for the beam, using the given precision.
376
* If precision is -1 (the default) then if @p value is greater than 99.5, no decimal figures are shown,
377
* otherwise if @p value is greater than 0.995, 1 decimal figure is used, otherwise 2.
379
QString lastValueAsString( int index, int precision = -1) const;
381
/** \brief Return a translated string for the given value.
383
* Returns, for example, "34 %" or "100 KB" for the given value in unscaled units.
385
* If precision is -1 (the default) then if @p value is greater than 99.5, no decimal figures are shown,
386
* otherwise if @p value is greater than 0.995, 1 decimal figure is used, otherwise 2.
390
* KSignalPlotter plotter;
391
* plotter.setUnit( ki18ncp("Units", "1 hour", "%1 hours") );
392
* plotter.scaleDownBy( 60 ); //The input will be in seconds, and there's 60 seconds in an hour
393
* QString formattedString = plotter.valueAsString(150); //returns "2.5 hours"
397
QString valueAsString( qreal value, int precision = -1) const;
399
/** \brief Set the distance between the left of the widget and the left of the plotting region.
403
* int axisTextWidth = fontMetrics().width(i18nc("Largest axis title", "99999 XXXX"));
404
* plotter->setMaxAxisTextWidth(axisTextWidth);
407
* If this is 0, the default, then the text will be shown inside the plotting area.
409
void setMaxAxisTextWidth(int maxAxisTextWidth);
410
/** \brief Get the distance between the left of the widget and the left of the plotting region. */
411
int maxAxisTextWidth() const;
413
/** \brief Set whether to smooth the graph by averaging the points.
415
* This uses the formula: (value*2 + last_value)/3.
416
* Default is true. */
417
void setSmoothGraph(bool smooth);
418
/** \brief Whether to smooth the graph by averaging the points.
420
* This uses the formula: (value*2 + last_value)/3.
421
* Default is true. */
422
bool smoothGraph() const;
424
/** \brief Set whether to stack the beams on top of each other.
426
* Default is false */
427
void setStackGraph(bool stack);
428
/** \brief Whether to stack the beams on top of each other.
430
* Default is false */
431
bool stackGraph() const;
433
/** \brief Alpha value for filling the area underneath the graph lines.
435
* Set to 0 to disable filling the graph, and 255 for a solid fill. Default is 20*/
436
void setFillOpacity(int fill);
437
/** \brief Alpha value for filling the area underneath the graph lines. */
438
int fillOpacity() const;
440
/* Whether to show a thin line on the left and bottom of the widget, for a slight 3D effect */
441
bool thinFrame() const;
442
/* Set whether to show a thin line on the left and bottom of the widget, for a slight 3D effect */
443
void setThinFrame(bool thinFrame);
446
/** When the axis has changed this signal is emitted. */
447
void axisScaleChanged();
451
virtual void resizeEvent( QResizeEvent* );
452
virtual void paintEvent( QPaintEvent* );
453
virtual QSize sizeHint() const;
454
virtual void changeEvent ( QEvent * event );
457
KSignalPlotterPrivate * const d;
458
friend class KSignalPlotterPrivate;