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>
48class 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;
116SignalPlotter::SignalPlotter(QGraphicsItem *parent)
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);
155SignalPlotter::~SignalPlotter()
160QString SignalPlotter::unit()
const
164void SignalPlotter::setUnit(
const QString &unit)
169void SignalPlotter::addPlot(
const QColor &color)
173 foreach (QList<double> data, d->plotData) {
177 newColor.
color = color;
179 d->plotColors.append(newColor);
182void SignalPlotter::addSample(
const QList<double>& sampleBuf)
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;
222void SignalPlotter::reorderPlots(
const QList<uint>& newOrder)
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;
251void SignalPlotter::setVerticalRange(
double min,
double max)
253 d->verticalMin = min;
254 d->verticalMax = max;
258QList<PlotColor> &SignalPlotter::plotColors()
260 return d->plotColors;
263void SignalPlotter::removePlot(uint pos)
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) {
277void SignalPlotter::scale(qreal delta)
279 if (d->scaledBy == delta) {
283 d->backgroundPixmap = QPixmap();
287qreal SignalPlotter::scaledBy()
const
292void SignalPlotter::setTitle(
const QString &title)
294 if (d->title ==
title) {
298 d->backgroundPixmap = QPixmap();
301QString SignalPlotter::title()
const
306void SignalPlotter::setUseAutoRange(
bool value)
308 d->useAutoRange = value;
313bool SignalPlotter::useAutoRange()
const
315 return d->useAutoRange;
318double SignalPlotter::verticalMinValue()
const
320 return d->verticalMin;
323double SignalPlotter::verticalMaxValue()
const
325 return d->verticalMax;
328void SignalPlotter::setHorizontalScale(uint scale)
330 if (
scale == d->horizontalScale) {
334 d->horizontalScale =
scale;
336 d->backgroundPixmap = QPixmap();
339uint SignalPlotter::horizontalScale()
const
341 return d->horizontalScale;
344void SignalPlotter::setShowVerticalLines(
bool value)
346 if (d->showVerticalLines == value) {
349 d->showVerticalLines = value;
350 d->backgroundPixmap = QPixmap();
353bool SignalPlotter::showVerticalLines()
const
355 return d->showVerticalLines;
358void SignalPlotter::setVerticalLinesColor(
const QColor &color)
360 if (d->verticalLinesColor == color) {
363 d->verticalLinesColor = color;
364 d->backgroundPixmap = QPixmap();
367QColor SignalPlotter::verticalLinesColor()
const
369 return d->verticalLinesColor;
372void SignalPlotter::setVerticalLinesDistance(uint distance)
374 if (distance == d->verticalLinesDistance) {
377 d->verticalLinesDistance = distance;
378 d->backgroundPixmap = QPixmap();
381uint SignalPlotter::verticalLinesDistance()
const
383 return d->verticalLinesDistance;
386void SignalPlotter::setVerticalLinesScroll(
bool value)
388 if (value == d->verticalLinesScroll) {
391 d->verticalLinesScroll = value;
392 d->backgroundPixmap = QPixmap();
395bool SignalPlotter::verticalLinesScroll()
const
397 return d->verticalLinesScroll;
400void SignalPlotter::setShowHorizontalLines(
bool value)
402 if (value == d->showHorizontalLines) {
405 d->showHorizontalLines = value;
406 d->backgroundPixmap = QPixmap();
409bool SignalPlotter::showHorizontalLines()
const
411 return d->showHorizontalLines;
414void SignalPlotter::setFontColor(
const QColor &color)
416 d->fontColor = color;
419QColor SignalPlotter::fontColor()
const
424void SignalPlotter::setHorizontalLinesColor(
const QColor &color)
426 if (color == d->horizontalLinesColor) {
429 d->horizontalLinesColor = color;
430 d->backgroundPixmap = QPixmap();
433QColor SignalPlotter::horizontalLinesColor()
const
435 return d->horizontalLinesColor;
438void SignalPlotter::setHorizontalLinesCount(uint count)
440 if (count == d->horizontalLinesCount) {
443 d->horizontalLinesCount = count;
444 d->backgroundPixmap = QPixmap();
448uint SignalPlotter::horizontalLinesCount()
const
450 return d->horizontalLinesCount;
453void SignalPlotter::setShowLabels(
bool value)
455 if (value == d->showLabels) {
458 d->showLabels = value;
459 d->backgroundPixmap = QPixmap();
462bool SignalPlotter::showLabels()
const
464 return d->showLabels;
467void SignalPlotter::setShowTopBar(
bool value)
469 if (d->showTopBar == value) {
472 d->showTopBar = value;
473 d->backgroundPixmap = QPixmap();
476bool SignalPlotter::showTopBar()
const
478 return d->showTopBar;
481void SignalPlotter::setFont(
const QFont &font)
484 d->backgroundPixmap = QPixmap();
487QFont SignalPlotter::font()
const
492QString SignalPlotter::svgBackground()
494 return d->svgFilename;
497void SignalPlotter::setSvgBackground(
const QString &filename)
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);
519void SignalPlotter::setBackgroundColor(
const QColor &color)
521 if (color == d->backgroundColor) {
524 d->backgroundColor = color;
525 d->backgroundPixmap = QPixmap();
528QColor SignalPlotter::backgroundColor()
const
530 return d->backgroundColor;
533void SignalPlotter::setThinFrame(
bool set)
535 if (d->showThinFrame == set) {
538 d->showThinFrame = set;
539 d->backgroundPixmap = QPixmap();
542bool SignalPlotter::thinFrame()
const
544 return d->showThinFrame;
547void SignalPlotter::setStackPlots(
bool stack)
549 d->stackPlots = stack;
550 d->fillPlots = stack;
553bool SignalPlotter::stackPlots()
const
555 return d->stackPlots;
558void SignalPlotter::updateDataBuffers()
567 d->samples =
static_cast<uint
>(((size().width() - 2) /
568 d->horizontalScale) + 4.5);
571QPixmap SignalPlotter::getSnapshotImage(uint w, uint height)
573 uint horizontalStep = (uint)((1.0 * w / size().width()) + 0.5);
574 uint newWidth = (uint) (horizontalStep * size().width());
575 QPixmap image = QPixmap(newWidth, height);
582void SignalPlotter::setGeometry(
const QRectF &geometry)
585 QGraphicsWidget::setGeometry(geometry);
589void SignalPlotter::paint(QPainter *painter,
595 uint w = (uint) size().width();
596 uint h = (uint) size().height();
603 drawWidget(painter, w, h, d->horizontalScale);
606void SignalPlotter::drawWidget(QPainter *p, uint w, uint height,
int 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) {
693 if (d->showLabels && w > 60 && h > (fontheight + 1)) {
699void SignalPlotter::drawBackground(QPainter *p,
int w,
int h)
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);
709void SignalPlotter::drawThinFrame(QPainter *p,
int w,
int h)
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);
718void SignalPlotter::calculateNiceRange()
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;
756void SignalPlotter::drawTopBarFrame(QPainter *p,
int separatorX,
int height)
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);
768void SignalPlotter::drawTopBarContents(QPainter *p,
int x,
int width,
int height)
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));
799void SignalPlotter::drawVerticalLines(QPainter *p,
int top,
int w,
int h)
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);
807void SignalPlotter::drawPlots(QPainter *p,
int top,
int w,
int h,
int horizontalScale)
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) {
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;
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);
1038void SignalPlotter::drawAxisText(QPainter *p,
int top,
int h)
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);
1077void SignalPlotter::drawHorizontalLines(QPainter *p,
int top,
int w,
int h)
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);
1087double SignalPlotter::lastValue(uint i)
const
1089 if (d->plotData.isEmpty() || d->plotData.first().size() <= (
int)i) {
1092 return d->plotData.first()[i];
1095QString SignalPlotter::lastValueAsString(uint i)
const
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 drawTopBarFrame(QPainter *p, int separatorX, int height)
void drawTopBarContents(QPainter *p, int x, int width, int height)
void drawHorizontalLines(QPainter *p, int top, int w, int h)
void setSvgBackground(const QString &filename)
The filename of the svg background.
void drawWidget(QPainter *p, uint w, uint height, int horizontalScale)
void drawPlots(QPainter *p, int top, int w, int h, int horizontalScale)
void drawAxisText(QPainter *p, int top, int h)
void drawBackground(QPainter *p, int w, int h)
void drawThinFrame(QPainter *p, int w, int h)
void drawVerticalLines(QPainter *p, int top, int w, int h)
void calculateNiceRange()
A theme aware image-centric SVG class.
Interface to the Plasma theme.
Q_INVOKABLE QColor color(ColorRole role) const
Returns the text color to be used by items resting on the background.
static Theme * defaultTheme()
Singleton pattern accessor.
@ BackgroundColor
the default background color
@ TextColor
the text color to be used by items resting on the background
Namespace for everything in libplasma.