28 #include <QApplication>
31 #include <QtGui/QPainter>
32 #include <QtGui/QPixmap>
33 #include <QtGui/QPainterPath>
34 #include <QtGui/QPolygon>
39 #include <kstandarddirs.h>
40 #include <kiconloader.h>
48 class SignalPlotterPrivate
51 SignalPlotterPrivate()
55 ~SignalPlotterPrivate()
64 borderColor = fontColor;
65 verticalLinesColor = fontColor;
66 verticalLinesColor.setAlphaF(0.4);
67 horizontalLinesColor = verticalLinesColor;
72 uint bezierCurveOffset;
81 uint verticalLinesOffset;
82 uint verticalLinesDistance;
83 QColor verticalLinesColor;
85 bool showHorizontalLines;
87 uint horizontalLinesCount;
88 QColor horizontalLinesColor;
95 QColor backgroundColor;
96 QPixmap backgroundPixmap;
102 QList<PlotColor> plotColors;
103 QList<QList<double> > plotData;
109 bool useAutoRange : 1;
110 bool showThinFrame : 1;
112 bool showVerticalLines : 1;
113 bool verticalLinesScroll : 1;
118 d(new SignalPlotterPrivate)
121 d->bezierCurveOffset = 0;
123 d->verticalMin = d->verticalMax = 0.0;
124 d->niceVertMin = d->niceVertMax = 0.0;
125 d->niceVertRange = 0;
126 d->useAutoRange =
true;
128 d->showThinFrame =
true;
130 d->showVerticalLines =
true;
131 d->verticalLinesDistance = 30;
132 d->verticalLinesScroll =
true;
133 d->verticalLinesOffset = 0;
134 d->horizontalScale = 1;
136 d->showHorizontalLines =
true;
137 d->horizontalLinesCount = 5;
139 d->showLabels =
true;
140 d->showTopBar =
true;
141 d->stackPlots =
true;
145 setMinimumSize(QSizeF(KIconLoader::SizeSmall, KIconLoader::SizeSmall));
152 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
173 foreach (QList<double> data, d->plotData) {
177 newColor.
color = color;
179 d->plotColors.append(newColor);
184 if (d->samples < 4) {
187 kDebug() <<
"Error - d->samples is only " << d->samples;
189 kDebug() <<
"d->samples is now " << d->samples;
190 if (d->samples < 4) {
194 d->plotData.prepend(sampleBuf);
195 Q_ASSERT(sampleBuf.count() == d->plotColors.count());
196 if ((uint)d->plotData.size() > d->samples) {
197 d->plotData.removeLast();
198 if ((uint)d->plotData.size() > d->samples) {
201 d->plotData.removeLast();
205 if (d->bezierCurveOffset >= 2) {
206 d->bezierCurveOffset = 0;
208 d->bezierCurveOffset++;
211 Q_ASSERT((uint)d->plotData.size() >= d->bezierCurveOffset);
215 if (d->verticalLinesScroll) {
216 d->verticalLinesOffset =
217 (d->verticalLinesOffset + d->horizontalScale) % d->verticalLinesDistance;
224 if (newOrder.count() != d->plotColors.count()) {
225 kDebug() <<
"neworder has " << newOrder.count()
226 <<
" and plot colors is " << d->plotColors.count();
229 foreach (QList<double> data, d->plotData) {
230 if (newOrder.count() != data.count()) {
231 kDebug() <<
"Serious problem in move sample. plotdata[i] has "
232 << data.count() <<
" and neworder has " << newOrder.count();
234 QList<double> newPlot;
235 for (
int i = 0; i < newOrder.count(); i++) {
236 int newIndex = newOrder[i];
237 newPlot.append(data.at(newIndex));
242 QList<PlotColor> newPlotColors;
243 for (
int i = 0; i < newOrder.count(); i++) {
244 int newIndex = newOrder[i];
245 PlotColor newColor = d->plotColors.at(newIndex);
246 newPlotColors.append(newColor);
248 d->plotColors = newPlotColors;
253 d->verticalMin = min;
254 d->verticalMax = max;
260 return d->plotColors;
265 if (pos >= (uint)d->plotColors.size()) {
268 d->plotColors.removeAt(pos);
270 foreach (QList<double> data, d->plotData) {
271 if ((uint)data.size() >= pos) {
279 if (d->scaledBy == delta) {
283 d->backgroundPixmap = QPixmap();
294 if (d->title == title) {
298 d->backgroundPixmap = QPixmap();
308 d->useAutoRange = value;
315 return d->useAutoRange;
320 return d->verticalMin;
325 return d->verticalMax;
330 if (scale == d->horizontalScale) {
334 d->horizontalScale =
scale;
336 d->backgroundPixmap = QPixmap();
341 return d->horizontalScale;
346 if (d->showVerticalLines == value) {
349 d->showVerticalLines = value;
350 d->backgroundPixmap = QPixmap();
355 return d->showVerticalLines;
360 if (d->verticalLinesColor == color) {
363 d->verticalLinesColor = color;
364 d->backgroundPixmap = QPixmap();
369 return d->verticalLinesColor;
374 if (distance == d->verticalLinesDistance) {
377 d->verticalLinesDistance = distance;
378 d->backgroundPixmap = QPixmap();
383 return d->verticalLinesDistance;
388 if (value == d->verticalLinesScroll) {
391 d->verticalLinesScroll = value;
392 d->backgroundPixmap = QPixmap();
397 return d->verticalLinesScroll;
402 if (value == d->showHorizontalLines) {
405 d->showHorizontalLines = value;
406 d->backgroundPixmap = QPixmap();
411 return d->showHorizontalLines;
416 d->fontColor = color;
426 if (color == d->horizontalLinesColor) {
429 d->horizontalLinesColor = color;
430 d->backgroundPixmap = QPixmap();
435 return d->horizontalLinesColor;
440 if (count == d->horizontalLinesCount) {
443 d->horizontalLinesCount = count;
444 d->backgroundPixmap = QPixmap();
450 return d->horizontalLinesCount;
455 if (value == d->showLabels) {
458 d->showLabels = value;
459 d->backgroundPixmap = QPixmap();
464 return d->showLabels;
469 if (d->showTopBar == value) {
472 d->showTopBar = value;
473 d->backgroundPixmap = QPixmap();
478 return d->showTopBar;
484 d->backgroundPixmap = QPixmap();
494 return d->svgFilename;
499 if (d->svgFilename == filename) {
503 if (!filename.isEmpty() && filename[0] ==
'/') {
504 KStandardDirs *kstd = KGlobal::dirs();
505 d->svgFilename = kstd->findResource(
"data",
"ksysguard/" + filename);
507 d->svgFilename = filename;
510 delete d->svgBackground;
511 d->svgBackground = 0;
512 if (!d->svgFilename.isEmpty()) {
513 d->svgBackground =
new Svg(
this);
514 d->svgBackground->setImagePath(d->svgFilename);
521 if (color == d->backgroundColor) {
524 d->backgroundColor = color;
525 d->backgroundPixmap = QPixmap();
530 return d->backgroundColor;
535 if (d->showThinFrame == set) {
538 d->showThinFrame = set;
539 d->backgroundPixmap = QPixmap();
544 return d->showThinFrame;
549 d->stackPlots = stack;
550 d->fillPlots = stack;
555 return d->stackPlots;
567 d->samples =
static_cast<uint
>(((size().width() - 2) /
568 d->horizontalScale) + 4.5);
573 uint horizontalStep = (uint)((1.0 * w / size().width()) + 0.5);
574 uint newWidth = (uint) (horizontalStep * size().width());
575 QPixmap image = QPixmap(newWidth, height);
585 QGraphicsWidget::setGeometry(geometry);
595 uint w = (uint) size().width();
596 uint h = (uint) size().height();
603 drawWidget(painter, w, h, d->horizontalScale);
611 uint fontheight = p->fontMetrics().height();
612 if (d->verticalMin < d->niceVertMin ||
613 d->verticalMax > d->niceVertMax ||
614 d->verticalMax < (d->niceVertRange * 0.75 + d->niceVertMin) ||
615 d->niceVertRange == 0) {
620 pen.setCapStyle(Qt::RoundCap);
623 uint top = p->pen().width() / 2;
629 bool showTopBar = d->showTopBar && h > (fontheight +5);
634 if (d->backgroundPixmap.isNull() ||
635 (uint)d->backgroundPixmap.size().height() != height ||
636 (uint)d->backgroundPixmap.size().width() != w) {
638 d->backgroundPixmap = QPixmap(w, height);
639 d->backgroundPixmap.fill(Qt::transparent);
640 QPainter pCache(&d->backgroundPixmap);
641 pCache.setRenderHint(QPainter::Antialiasing,
false);
642 pCache.setFont(d->font);
646 if (d->showThinFrame) {
651 pCache.setClipRect(0, 0, w, height-1);
655 int separatorX = w / 2;
661 if (!d->verticalLinesScroll && d->showVerticalLines && w > 60) {
665 if (d->showHorizontalLines) {
670 if (d->showThinFrame) {
676 p->drawPixmap(0, 0, d->backgroundPixmap);
677 p->setRenderHint(QPainter::Antialiasing,
true);
680 int separatorX = w / 2;
681 int topBarWidth = w - separatorX -2;
685 p->setClipRect(0, top, w, h, Qt::IntersectClip);
687 if (d->verticalLinesScroll && d->showVerticalLines && w > 60) {
691 drawPlots(p, top, w, h, horizontalScale);
693 if (d->showLabels && w > 60 && h > (fontheight + 1)) {
701 if (d->svgBackground) {
702 d->svgBackground->resize(w, h);
703 d->svgBackground->paint(p, 0, 0);
705 p->fillRect(0, 0, w, h, d->backgroundColor);
713 p->setPen(d->borderColor);
714 p->drawLine(0, h - 1, w - 1, h - 1);
715 p->drawLine(w - 1, 0, w - 1, h - 1);
720 d->niceVertRange = d->verticalMax - d->verticalMin;
723 if (d->niceVertRange < 0.000001) {
724 d->niceVertRange = 1.0;
727 d->niceVertMin = d->verticalMin;
728 if (d->verticalMin != 0.0) {
729 double dim = pow(10, floor(log10(fabs(d->verticalMin)))) / 2;
730 if (d->verticalMin < 0.0) {
731 d->niceVertMin = dim * floor(d->verticalMin / dim);
733 d->niceVertMin = dim * ceil(d->verticalMin / dim);
735 d->niceVertRange = d->verticalMax - d->niceVertMin;
736 if (d->niceVertRange < 0.000001) {
737 d->niceVertRange = 1.0;
741 double step = d->niceVertRange / (d->scaledBy * (d->horizontalLinesCount + 1));
742 int logdim = (int)floor(log10(step));
743 double dim = pow((
double)10.0, logdim) / 2;
744 int a = (int)ceil(step / dim);
747 }
else if (a % 2 == 0) {
748 d->precision = -logdim;
750 d->precision = 1 - logdim;
752 d->niceVertRange = d->scaledBy * dim * a * (d->horizontalLinesCount + 1);
753 d->niceVertMax = d->niceVertMin + d->niceVertRange;
761 p->setPen(Qt::NoPen);
762 p->setPen(d->fontColor);
763 p->drawText(0, 1, separatorX, height, Qt::AlignCenter, d->title);
764 p->setPen(d->horizontalLinesColor);
765 p->drawLine(separatorX - 1, 1, separatorX - 1, height - 1);
772 double bias = -d->niceVertMin;
773 double scaleFac = width / d->niceVertRange;
776 if (!d->plotData.isEmpty()) {
777 QList<double> newestData = d->plotData.first();
778 for (
int i = newestData.count()-1; i >= 0; --i) {
779 double newest_datapoint = newestData.at(i);
780 int start = x + (int)(bias * scaleFac);
781 int end = x + (int)((bias += newest_datapoint) * scaleFac);
782 int start2 = qMin(start, end);
783 end = qMax(start, end);
790 p->setPen(Qt::NoPen);
791 QLinearGradient linearGrad(QPointF(start, 1), QPointF(end, 1));
792 linearGrad.setColorAt(0, d->plotColors[i].darkColor);
793 linearGrad.setColorAt(1, d->plotColors[i].color);
794 p->fillRect(start, 1, end - start, height-1, QBrush(linearGrad));
801 p->setPen(d->verticalLinesColor);
802 for (
int x = d->verticalLinesOffset; x < (w - 2); x += d->verticalLinesDistance) {
803 p->drawLine(w - x, top, w - x, h + top -1);
809 Q_ASSERT(d->niceVertRange != 0);
811 if (d->niceVertRange == 0) {
812 d->niceVertRange = 1;
814 double scaleFac = (h - 1) / d->niceVertRange;
817 QList< QList<double> >::Iterator it = d->plotData.begin();
819 p->setPen(Qt::NoPen);
832 if (d->useAutoRange) {
833 d->verticalMin = d->verticalMax = 0.0;
848 for (uint i = 0; it != d->plotData.end() && i < d->samples; ++i) {
851 pen.setCapStyle(Qt::FlatCap);
857 QList<double> datapoints = *it;
858 QList<double> prev_datapoints = datapoints;
859 QList<double> prev_prev_datapoints = datapoints;
860 QList<double> prev_prev_prev_datapoints = datapoints;
862 if (i == 0 && d->bezierCurveOffset > 0) {
865 xPos += horizontalScale * d->bezierCurveOffset;
866 if (d->bezierCurveOffset == 1) {
867 prev_datapoints = *it;
869 if (it != d->plotData.end()) {
870 prev_prev_prev_datapoints = prev_prev_datapoints = *it;
872 prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
876 prev_datapoints = *it;
877 Q_ASSERT(it != d->plotData.end());
879 prev_prev_datapoints = *it;
880 Q_ASSERT(it != d->plotData.end());
882 if (it != d->plotData.end()) {
883 prev_prev_prev_datapoints = *it;
885 prev_prev_prev_datapoints = prev_prev_datapoints;
890 xPos += horizontalScale * 3;
892 if (it != d->plotData.end()) {
893 prev_datapoints = *it;
895 if (it != d->plotData.end()) {
896 prev_prev_datapoints = *it;
898 if (it != d->plotData.end()) {
900 prev_prev_prev_datapoints = *it;
904 prev_prev_prev_datapoints = prev_prev_datapoints;
907 prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
910 prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints = datapoints;
918 float y0 = h - 1 + top;
926 for (
int j = qMin(datapoints.size(), d->plotColors.size()) - 1; j >=0; --j) {
927 if (d->useAutoRange) {
930 double current_maxvalue =
932 qMax(prev_datapoints[j],
933 qMax(prev_prev_datapoints[j],
934 prev_prev_prev_datapoints[j])));
935 double current_minvalue =
936 qMin<double>(datapoints[j],
937 qMin(prev_datapoints[j],
938 qMin(prev_prev_datapoints[j],
939 prev_prev_prev_datapoints[j])));
940 d->verticalMax = qMax(d->verticalMax, current_maxvalue);
941 d->verticalMin = qMin(d->verticalMin, current_maxvalue);
943 max_y += current_maxvalue;
944 min_y += current_minvalue;
949 if (j < prev_prev_prev_datapoints.count() &&
950 j < prev_prev_datapoints.count() &&
951 j < prev_datapoints.count()) {
959 delta_y0 = (datapoints[j] - d->niceVertMin) * scaleFac;
962 delta_y1 = (prev_datapoints[j] - d->niceVertMin) * scaleFac;
965 delta_y2 = (prev_prev_datapoints[j] - d->niceVertMin) * scaleFac;
968 delta_y3 = (prev_prev_prev_datapoints[j] - d->niceVertMin) * scaleFac;
971 if (d->stackPlots && offset) {
987 path.moveTo(x0, y0 - delta_y0);
988 path.cubicTo(x1, y1 - delta_y1, x2, y2 - delta_y2, x3, y3 - delta_y3);
991 QPainterPath path2(path);
992 QLinearGradient myGradient(0,(h - 1 + top), 0, (h - 1 + top) / 5);
993 Q_ASSERT(d->plotColors.size() >= j);
994 QColor c0(d->plotColors[j].darkColor);
995 QColor c1(d->plotColors[j].color);
998 myGradient.setColorAt(0, c0);
999 myGradient.setColorAt(1, c1);
1001 path2.lineTo(x3, y3 - offset);
1002 if (d->stackPlots) {
1005 path2.cubicTo(x2, y2 - offset, x1, y1 - offset, x0, y0 - offset);
1007 path2.lineTo(x0, y0 - 1);
1009 p->setBrush(myGradient);
1010 p->setPen(Qt::NoPen);
1013 p->setBrush(Qt::NoBrush);
1014 Q_ASSERT(d->plotColors.size() >= j);
1015 pen.setColor(d->plotColors[j].color);
1019 if (d->stackPlots) {
1030 if (d->useAutoRange && d->stackPlots) {
1031 d->verticalMax = qMax(max_y, d->verticalMax);
1032 d->verticalMin = qMin(min_y, d->verticalMin);
1048 p->setPen(d->fontColor);
1049 double stepsize = d->niceVertRange / (d->scaledBy * (d->horizontalLinesCount + 1));
1051 (int)ceil((d->horizontalLinesCount+1) *
1052 (p->fontMetrics().height() + p->fontMetrics().leading() / 2.0) / h);
1056 for (
int y = d->horizontalLinesCount + 1; y >= 1; y-= step) {
1058 top + (y * (h - 1)) / (d->horizontalLinesCount + 1);
1059 if (y_coord - p->fontMetrics().ascent() < top) {
1065 if ((uint)y == d->horizontalLinesCount + 1) {
1066 value = d->niceVertMin;
1068 value = d->niceVertMax / d->scaledBy - y * stepsize;
1071 QString number = KGlobal::locale()->formatNumber(value, d->precision);
1072 val = QString(
"%1 %2").arg(number, d->unit);
1073 p->drawText(6, y_coord - 3, val);
1079 p->setPen(d->horizontalLinesColor);
1080 for (uint y = 0; y <= d->horizontalLinesCount + 1; y++) {
1082 int y_coord = top + (y * (h - 1)) / (d->horizontalLinesCount + 1);
1083 p->drawLine(0, y_coord, w - 2, y_coord);
1089 if (d->plotData.isEmpty() || d->plotData.first().size() <= (int)i) {
1092 return d->plotData.first()[i];
1097 if (d->plotData.isEmpty()) {
1100 double value = d->plotData.first()[i] / d->scaledBy;
1101 QString number = KGlobal::locale()->formatNumber(value, (value >= 100)?0:2);
1102 return QString(
"%1 %2").arg(number, d->unit);
1107 #include "signalplotter.moc"
void setShowTopBar(bool value)
Whether to show the title etc at the top.
void setShowLabels(bool value)
Whether to show the vertical axis labels.
QPixmap getSnapshotImage(uint width, uint height)
Render the graph to the specified width and height, and return it as an image.
void setUnit(const QString &unit)
Set the units.
void drawWidget(QPainter *p, uint w, uint height, int horizontalScale)
bool showHorizontalLines() const
Whether to draw the horizontal grid lines.
void drawHorizontalLines(QPainter *p, int top, int w, int h)
uint horizontalScale() const
The number of pixels horizontally between data points.
void drawAxisText(QPainter *p, int top, int h)
void setUseAutoRange(bool value)
Set the minimum and maximum values on the vertical axis automatically from the data available...
the text color to be used by items resting on the background
QList< PlotColor > & plotColors()
Return the list of plot colors, in the order that the plots were added (or later reordered).
SignalPlotter(QGraphicsItem *parent=0)
Q_INVOKABLE void addPlot(const QColor &color)
Add a new line to the graph plotter, with the specified color.
void drawVerticalLines(QPainter *p, int top, int w, int h)
void setFont(const QFont &font)
The font used for the axis.
void setThinFrame(bool set)
Whether to show a white line on the left and bottom of the widget, for a 3D effect.
QString lastValueAsString(uint i) const
Return a translated string like: "34 %" or "100 KB" for plot i.
QFont font() const
The font used for the axis.
Q_INVOKABLE void removePlot(uint pos)
Removes the plot at the specified index.
void setHorizontalScale(uint scale)
Set the number of pixels horizontally between data points.
bool showTopBar() const
Whether to show the title etc at the top.
void setShowHorizontalLines(bool value)
Whether to draw the horizontal grid lines.
Q_INVOKABLE void addSample(const QList< double > &samples)
Add data to the graph, and advance the graph by one time period.
virtual void setGeometry(const QRectF &geometry)
Overwritten to be notified of size changes.
void setVerticalLinesScroll(bool value)
Whether the vertical lines move with the data.
void drawBackground(QPainter *p, int w, int h)
void setFontColor(const QColor &color)
The color of the font used for the axis.
qreal scaledBy() const
Amount scaled down by.
QColor verticalLinesColor() const
The color of the vertical grid lines.
QColor backgroundColor() const
The color to set the background.
double verticalMinValue() const
Get the min value of the vertical axis.
double verticalMaxValue() const
Get the max value of the vertical axis.
void drawTopBarContents(QPainter *p, int x, int width, int height)
double lastValue(uint i) const
Return the last value that we have for plot i.
bool verticalLinesScroll() const
Whether the vertical lines move with the data.
QString title() const
Get the title of the graph.
void setVerticalLinesColor(const QColor &color)
The color of the vertical grid lines.
void setBackgroundColor(const QColor &color)
The color to set the background.
QString unit() const
Return the units used on the vertical axis of the graph.
QString svgBackground()
The filename of the svg background.
void setStackPlots(bool stack)
Whether to stack the plots on top of each other.
Interface to the Plasma theme.
void drawTopBarFrame(QPainter *p, int separatorX, int height)
QColor horizontalLinesColor() const
The color of the horizontal grid lines.
bool thinFrame() const
show a white line on the left and bottom of the widget for a 3D effect
void setVerticalLinesDistance(uint distance)
The horizontal distance between the vertical grid lines.
static Theme * defaultTheme()
Singleton pattern accessor.
void setHorizontalLinesColor(const QColor &color)
The color of the horizontal grid lines.
void drawThinFrame(QPainter *p, int w, int h)
bool stackPlots() const
Whether to stack the plots.
uint verticalLinesDistance() const
The horizontal distance between the vertical grid lines.
void setShowVerticalLines(bool value)
Whether to draw the vertical grid lines.
void setVerticalRange(double min, double max)
Change the minimum and maximum values drawn on the graph.
Q_INVOKABLE void reorderPlots(const QList< uint > &newOrder)
Reorder the plots into the order given.
Q_INVOKABLE QColor color(ColorRole role) const
Returns the text color to be used by items resting on the background.
void setSvgBackground(const QString &filename)
The filename of the svg background.
bool showVerticalLines() const
Whether the vertical grid lines will be drawn.
the default background color
void calculateNiceRange()
QColor fontColor() const
The color of the font used for the axis.
bool useAutoRange() const
Whether the vertical axis range is set automatically.
bool showLabels() const
Whether to show the vertical axis labels.
A theme aware image-centric SVG class.
void drawPlots(QPainter *p, int top, int w, int h, int horizontalScale)
uint horizontalLinesCount() const
The number of horizontal lines to draw.
void setHorizontalLinesCount(uint count)
The number of horizontal lines to draw.
void setTitle(const QString &title)
Set the title of the graph.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)