00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "signalplotter.h"
00024
00025 #include <math.h>
00026 #include <string.h>
00027
00028 #include <QList>
00029 #include <QPalette>
00030 #include <QtGui/QPainter>
00031 #include <QtGui/QPixmap>
00032 #include <QtGui/QPainterPath>
00033 #include <QtGui/QPolygon>
00034
00035 #include <kdebug.h>
00036 #include <kglobal.h>
00037 #include <klocale.h>
00038 #include <kapplication.h>
00039 #include <kstandarddirs.h>
00040
00041 #include <plasma/svg.h>
00042 #include <plasma/theme.h>
00043
00044 namespace Plasma
00045 {
00046
00047 class SignalPlotterPrivate
00048 {
00049 public:
00050 SignalPlotterPrivate()
00051 : svgBackground(0)
00052 { }
00053
00054 ~SignalPlotterPrivate()
00055 {
00056 }
00057
00058 void themeChanged()
00059 {
00060 Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00061 backgroundColor = theme->color(Theme::BackgroundColor);
00062 verticalLinesColor = theme->color(Theme::TextColor);
00063 verticalLinesColor.setAlphaF(0.4);
00064 horizontalLinesColor = theme->color(Theme::TextColor);
00065 horizontalLinesColor.setAlphaF(0.4);
00066 }
00067
00068 int precision;
00069 uint samples;
00070 uint bezierCurveOffset;
00071
00072 double scaledBy;
00073 double verticalMin;
00074 double verticalMax;
00075 double niceVertMin;
00076 double niceVertMax;
00077 double niceVertRange;
00078
00079 bool fillPlots;
00080 bool showLabels;
00081 bool showTopBar;
00082 bool stackPlots;
00083 bool useAutoRange;
00084 bool showThinFrame;
00085
00086 bool showVerticalLines;
00087 bool verticalLinesScroll;
00088 uint verticalLinesOffset;
00089 uint verticalLinesDistance;
00090 QColor verticalLinesColor;
00091
00092 bool showHorizontalLines;
00093 uint horizontalScale;
00094 uint horizontalLinesCount;
00095 QColor horizontalLinesColor;
00096
00097 Svg *svgBackground;
00098 QString svgFilename;
00099
00100 QColor fontColor;
00101 QColor backgroundColor;
00102 QPixmap backgroundPixmap;
00103
00104 QFont font;
00105 QString title;
00106 QString unit;
00107
00108 QList<PlotColor> plotColors;
00109 QList<QList<double> > plotData;
00110 };
00111
00112 SignalPlotter::SignalPlotter(QGraphicsItem *parent)
00113 : QGraphicsWidget(parent),
00114 d(new SignalPlotterPrivate)
00115 {
00116 d->precision = 0;
00117 d->bezierCurveOffset = 0;
00118 d->samples = 0;
00119 d->verticalMin = d->verticalMax = 0.0;
00120 d->niceVertMin = d->niceVertMax = 0.0;
00121 d->niceVertRange = 0;
00122 d->useAutoRange = true;
00123 d->scaledBy = 1;
00124 d->showThinFrame = true;
00125
00126
00127 setMinimumSize(QSizeF(16, 16));
00128
00129 d->showVerticalLines = true;
00130 d->verticalLinesDistance = 30;
00131 d->verticalLinesScroll = true;
00132 d->verticalLinesOffset = 0;
00133 d->horizontalScale = 1;
00134
00135 d->showHorizontalLines = true;
00136 d->horizontalLinesCount = 5;
00137
00138 d->showLabels = true;
00139 d->showTopBar = true;
00140 d->stackPlots = true;
00141 d->fillPlots = true;
00142
00143 setSvgBackground("widgets/plot-background");
00144
00145 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), SLOT(themeChanged()));
00146 d->themeChanged();
00147
00148 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00149 }
00150
00151 SignalPlotter::~SignalPlotter()
00152 {
00153 delete d;
00154 }
00155
00156 QString SignalPlotter::unit() const
00157 {
00158 return d->unit;
00159 }
00160 void SignalPlotter::setUnit(const QString &unit)
00161 {
00162 d->unit= unit;
00163 }
00164
00165 void SignalPlotter::addPlot(const QColor &color)
00166 {
00167
00168
00169 foreach (QList<double> data, d->plotData) {
00170 data.append(0);
00171 }
00172 PlotColor newColor;
00173 newColor.color = color;
00174 newColor.darkColor = color.dark(150);
00175 d->plotColors.append(newColor);
00176 }
00177
00178 void SignalPlotter::addSample(const QList<double>& sampleBuf)
00179 {
00180 if (d->samples < 4) {
00181
00182
00183 kDebug() << "Error - d->samples is only " << d->samples;
00184 updateDataBuffers();
00185 kDebug() << "d->samples is now " << d->samples;
00186 if (d->samples < 4) {
00187 return;
00188 }
00189 }
00190 d->plotData.prepend(sampleBuf);
00191 Q_ASSERT(sampleBuf.count() == d->plotColors.count());
00192 if ((uint)d->plotData.size() > d->samples) {
00193 d->plotData.removeLast();
00194 if ((uint)d->plotData.size() > d->samples) {
00195
00196
00197 d->plotData.removeLast();
00198 }
00199 }
00200
00201 if (d->bezierCurveOffset >= 2) {
00202 d->bezierCurveOffset = 0;
00203 } else {
00204 d->bezierCurveOffset++;
00205 }
00206
00207 Q_ASSERT((uint)d->plotData.size() >= d->bezierCurveOffset);
00208
00209
00210
00211 if (d->verticalLinesScroll) {
00212 d->verticalLinesOffset =
00213 (d->verticalLinesOffset + d->horizontalScale) % d->verticalLinesDistance;
00214 }
00215 update();
00216 }
00217
00218 void SignalPlotter::reorderPlots(const QList<uint>& newOrder)
00219 {
00220 if (newOrder.count() != d->plotColors.count()) {
00221 kDebug() << "neworder has " << newOrder.count()
00222 << " and plot colors is " << d->plotColors.count();
00223 return;
00224 }
00225 foreach (QList<double> data, d->plotData) {
00226 if (newOrder.count() != data.count()) {
00227 kDebug() << "Serious problem in move sample. plotdata[i] has "
00228 << data.count() << " and neworder has " << newOrder.count();
00229 } else {
00230 QList<double> newPlot;
00231 for (int i = 0; i < newOrder.count(); i++) {
00232 int newIndex = newOrder[i];
00233 newPlot.append(data.at(newIndex));
00234 }
00235 data = newPlot;
00236 }
00237 }
00238 QList<PlotColor> newPlotColors;
00239 for (int i = 0; i < newOrder.count(); i++) {
00240 int newIndex = newOrder[i];
00241 PlotColor newColor = d->plotColors.at(newIndex);
00242 newPlotColors.append(newColor);
00243 }
00244 d->plotColors = newPlotColors;
00245 }
00246
00247 void SignalPlotter::setVerticalRange(double min, double max)
00248 {
00249 d->verticalMin = min;
00250 d->verticalMax = max;
00251 calculateNiceRange();
00252 }
00253
00254 QList<PlotColor> &SignalPlotter::plotColors()
00255 {
00256 return d->plotColors;
00257 }
00258
00259 void SignalPlotter::removePlot(uint pos)
00260 {
00261 if (pos >= (uint)d->plotColors.size()) {
00262 return;
00263 }
00264 d->plotColors.removeAt(pos);
00265
00266 foreach (QList<double> data, d->plotData) {
00267 if ((uint)data.size() >= pos) {
00268 data.removeAt(pos);
00269 }
00270 }
00271 }
00272
00273 void SignalPlotter::scale(qreal delta)
00274 {
00275 if (d->scaledBy == delta) {
00276 return;
00277 }
00278 d->scaledBy = delta;
00279 d->backgroundPixmap = QPixmap();
00280 calculateNiceRange();
00281 }
00282
00283 qreal SignalPlotter::scaledBy() const
00284 {
00285 return d->scaledBy;
00286 }
00287
00288 void SignalPlotter::setTitle(const QString &title)
00289 {
00290 if (d->title == title) {
00291 return;
00292 }
00293 d->title = title;
00294 d->backgroundPixmap = QPixmap();
00295 }
00296
00297 QString SignalPlotter::title() const
00298 {
00299 return d->title;
00300 }
00301
00302 void SignalPlotter::setUseAutoRange(bool value)
00303 {
00304 d->useAutoRange = value;
00305 calculateNiceRange();
00306
00307 }
00308
00309 bool SignalPlotter::useAutoRange() const
00310 {
00311 return d->useAutoRange;
00312 }
00313
00314 double SignalPlotter::verticalMinValue() const
00315 {
00316 return d->verticalMin;
00317 }
00318
00319 double SignalPlotter::verticalMaxValue() const
00320 {
00321 return d->verticalMax;
00322 }
00323
00324 void SignalPlotter::setHorizontalScale(uint scale)
00325 {
00326 if (scale == d->horizontalScale) {
00327 return;
00328 }
00329
00330 d->horizontalScale = scale;
00331 updateDataBuffers();
00332 d->backgroundPixmap = QPixmap();
00333 }
00334
00335 uint SignalPlotter::horizontalScale() const
00336 {
00337 return d->horizontalScale;
00338 }
00339
00340 void SignalPlotter::setShowVerticalLines(bool value)
00341 {
00342 if (d->showVerticalLines == value) {
00343 return;
00344 }
00345 d->showVerticalLines = value;
00346 d->backgroundPixmap = QPixmap();
00347 }
00348
00349 bool SignalPlotter::showVerticalLines() const
00350 {
00351 return d->showVerticalLines;
00352 }
00353
00354 void SignalPlotter::setVerticalLinesColor(const QColor &color)
00355 {
00356 if (d->verticalLinesColor == color) {
00357 return;
00358 }
00359 d->verticalLinesColor = color;
00360 d->backgroundPixmap = QPixmap();
00361 }
00362
00363 QColor SignalPlotter::verticalLinesColor() const
00364 {
00365 return d->verticalLinesColor;
00366 }
00367
00368 void SignalPlotter::setVerticalLinesDistance(uint distance)
00369 {
00370 if (distance == d->verticalLinesDistance) {
00371 return;
00372 }
00373 d->verticalLinesDistance = distance;
00374 d->backgroundPixmap = QPixmap();
00375 }
00376
00377 uint SignalPlotter::verticalLinesDistance() const
00378 {
00379 return d->verticalLinesDistance;
00380 }
00381
00382 void SignalPlotter::setVerticalLinesScroll(bool value)
00383 {
00384 if (value == d->verticalLinesScroll) {
00385 return;
00386 }
00387 d->verticalLinesScroll = value;
00388 d->backgroundPixmap = QPixmap();
00389 }
00390
00391 bool SignalPlotter::verticalLinesScroll() const
00392 {
00393 return d->verticalLinesScroll;
00394 }
00395
00396 void SignalPlotter::setShowHorizontalLines(bool value)
00397 {
00398 if (value == d->showHorizontalLines) {
00399 return;
00400 }
00401 d->showHorizontalLines = value;
00402 d->backgroundPixmap = QPixmap();
00403 }
00404
00405 bool SignalPlotter::showHorizontalLines() const
00406 {
00407 return d->showHorizontalLines;
00408 }
00409
00410 void SignalPlotter::setFontColor(const QColor &color)
00411 {
00412 d->fontColor = color;
00413 }
00414
00415 QColor SignalPlotter::fontColor() const
00416 {
00417 return d->fontColor;
00418 }
00419
00420 void SignalPlotter::setHorizontalLinesColor(const QColor &color)
00421 {
00422 if (color == d->horizontalLinesColor) {
00423 return;
00424 }
00425 d->horizontalLinesColor = color;
00426 d->backgroundPixmap = QPixmap();
00427 }
00428
00429 QColor SignalPlotter::horizontalLinesColor() const
00430 {
00431 return d->horizontalLinesColor;
00432 }
00433
00434 void SignalPlotter::setHorizontalLinesCount(uint count)
00435 {
00436 if (count == d->horizontalLinesCount) {
00437 return;
00438 }
00439 d->horizontalLinesCount = count;
00440 d->backgroundPixmap = QPixmap();
00441 calculateNiceRange();
00442 }
00443
00444 uint SignalPlotter::horizontalLinesCount() const
00445 {
00446 return d->horizontalLinesCount;
00447 }
00448
00449 void SignalPlotter::setShowLabels(bool value)
00450 {
00451 if (value == d->showLabels) {
00452 return;
00453 }
00454 d->showLabels = value;
00455 d->backgroundPixmap = QPixmap();
00456 }
00457
00458 bool SignalPlotter::showLabels() const
00459 {
00460 return d->showLabels;
00461 }
00462
00463 void SignalPlotter::setShowTopBar(bool value)
00464 {
00465 if (d->showTopBar == value) {
00466 return;
00467 }
00468 d->showTopBar = value;
00469 d->backgroundPixmap = QPixmap();
00470 }
00471
00472 bool SignalPlotter::showTopBar() const
00473 {
00474 return d->showTopBar;
00475 }
00476
00477 void SignalPlotter::setFont(const QFont &font)
00478 {
00479 d->font = font;
00480 d->backgroundPixmap = QPixmap();
00481 }
00482
00483 QFont SignalPlotter::font() const
00484 {
00485 return d->font;
00486 }
00487
00488 QString SignalPlotter::svgBackground()
00489 {
00490 return d->svgFilename;
00491 }
00492
00493 void SignalPlotter::setSvgBackground(const QString &filename)
00494 {
00495 if (d->svgFilename == filename) {
00496 return;
00497 }
00498
00499 if (!filename.isEmpty() && filename[0] == '/') {
00500 KStandardDirs *kstd = KGlobal::dirs();
00501 d->svgFilename = kstd->findResource("data", "ksysguard/" + filename);
00502 } else {
00503 d->svgFilename = filename;
00504 }
00505
00506 if (!d->svgFilename.isEmpty()) {
00507 if (d->svgBackground) {
00508 delete d->svgBackground;
00509 }
00510 d->svgBackground = new Svg(this);
00511 d->svgBackground->setImagePath(d->svgFilename);
00512 }
00513
00514 }
00515
00516 void SignalPlotter::setBackgroundColor(const QColor &color)
00517 {
00518 if (color == d->backgroundColor) {
00519 return;
00520 }
00521 d->backgroundColor = color;
00522 d->backgroundPixmap = QPixmap();
00523 }
00524
00525 QColor SignalPlotter::backgroundColor() const
00526 {
00527 return d->backgroundColor;
00528 }
00529
00530 void SignalPlotter::setThinFrame(bool set)
00531 {
00532 if (d->showThinFrame == set) {
00533 return;
00534 }
00535 d->showThinFrame = set;
00536 d->backgroundPixmap = QPixmap();
00537 }
00538
00539 void SignalPlotter::setStackPlots(bool stack)
00540 {
00541 d->stackPlots = stack;
00542 d->fillPlots = stack;
00543 }
00544
00545 bool SignalPlotter::stackPlots() const
00546 {
00547 return d->stackPlots;
00548 }
00549
00550 void SignalPlotter::updateDataBuffers()
00551 {
00552
00553
00554
00555
00556
00557
00558
00559 d->samples = static_cast<uint>(((size().width() - 2) /
00560 d->horizontalScale) + 4.5);
00561 }
00562
00563 QPixmap SignalPlotter::getSnapshotImage(uint w, uint height)
00564 {
00565 uint horizontalStep = (uint)((1.0 * w / size().width()) + 0.5);
00566 uint newWidth = (uint) (horizontalStep * size().width());
00567 QPixmap image = QPixmap(newWidth, height);
00568 QPainter p(&image);
00569 drawWidget(&p, newWidth, height, newWidth);
00570 p.end();
00571 return image;
00572 }
00573
00574 void SignalPlotter::setGeometry(const QRectF &geometry)
00575 {
00576
00577 QGraphicsWidget::setGeometry(geometry);
00578 updateDataBuffers();
00579 }
00580
00581 void SignalPlotter::paint(QPainter *painter,
00582 const QStyleOptionGraphicsItem *option, QWidget *widget)
00583 {
00584 Q_UNUSED(option);
00585 Q_UNUSED(widget);
00586
00587 uint w = (uint) size().width();
00588 uint h = (uint) size().height();
00589
00590
00591 if (w <= 2) {
00592 return;
00593 }
00594
00595 drawWidget(painter, w, h, d->horizontalScale);
00596 }
00597
00598 void SignalPlotter::drawWidget(QPainter *p, uint w, uint height, int horizontalScale)
00599 {
00600 uint h = height;
00601 p->setFont(d->font);
00602
00603 uint fontheight = p->fontMetrics().height();
00604 if (d->verticalMin < d->niceVertMin ||
00605 d->verticalMax > d->niceVertMax ||
00606 d->verticalMax < (d->niceVertRange * 0.75 + d->niceVertMin) ||
00607 d->niceVertRange == 0) {
00608 calculateNiceRange();
00609 }
00610 QPen pen;
00611 pen.setWidth(1);
00612 pen.setCapStyle(Qt::RoundCap);
00613 p->setPen(pen);
00614
00615 uint top = p->pen().width() / 2;
00616 h-= top;
00617
00618
00619
00620
00621 bool showTopBar = d->showTopBar && h > (fontheight +5);
00622 if (showTopBar) {
00623 top += fontheight;
00624 h -= fontheight;
00625 }
00626 if (d->backgroundPixmap.isNull() ||
00627 (uint)d->backgroundPixmap.size().height() != height ||
00628 (uint)d->backgroundPixmap.size().width() != w) {
00629
00630 d->backgroundPixmap = QPixmap(w, height);
00631 d->backgroundPixmap.fill(Qt::transparent);
00632 QPainter pCache(&d->backgroundPixmap);
00633 pCache.setRenderHint(QPainter::Antialiasing, false);
00634 pCache.setFont(d->font);
00635
00636 drawBackground(&pCache, w, height);
00637
00638 if (d->showThinFrame) {
00639 drawThinFrame(&pCache, w, height);
00640
00641 h--;
00642 w--;
00643 pCache.setClipRect(0, 0, w, height-1);
00644 }
00645
00646 if (showTopBar) {
00647 int separatorX = w / 2;
00648 drawTopBarFrame(&pCache, separatorX, top);
00649 }
00650
00651
00652
00653 if (!d->verticalLinesScroll && d->showVerticalLines && w > 60) {
00654 drawVerticalLines(&pCache, top, w, h);
00655 }
00656
00657 if (d->showHorizontalLines) {
00658 drawHorizontalLines(&pCache, top, w, h);
00659 }
00660
00661 } else {
00662 if (d->showThinFrame) {
00663
00664 h--;
00665 w--;
00666 }
00667 }
00668 p->drawPixmap(0, 0, d->backgroundPixmap);
00669 p->setRenderHint(QPainter::Antialiasing, true);
00670
00671 if (showTopBar) {
00672 int separatorX = w / 2;
00673 int topBarWidth = w - separatorX -2;
00674 drawTopBarContents(p, separatorX, topBarWidth, top -1);
00675 }
00676
00677 p->setClipRect(0, top, w, h);
00678
00679 if (d->verticalLinesScroll && d->showVerticalLines && w > 60) {
00680 drawVerticalLines(p, top, w, h);
00681 }
00682
00683 drawPlots(p, top, w, h, horizontalScale);
00684
00685 if (d->showLabels && w > 60 && h > (fontheight + 1)) {
00686
00687 drawAxisText(p, top, h);
00688 }
00689 }
00690
00691 void SignalPlotter::drawBackground(QPainter *p, int w, int h)
00692 {
00693 if (d->svgBackground) {
00694 d->svgBackground->resize(w, h);
00695 d->svgBackground->paint(p, 0, 0);
00696 } else {
00697 p->fillRect(0, 0, w, h, d->backgroundColor);
00698 }
00699 }
00700
00701 void SignalPlotter::drawThinFrame(QPainter *p, int w, int h)
00702 {
00703
00704
00705 p->setPen(kapp->palette().color(QPalette::Light));
00706 p->drawLine(0, h - 1, w - 1, h - 1);
00707 p->drawLine(w - 1, 0, w - 1, h - 1);
00708 }
00709
00710 void SignalPlotter::calculateNiceRange()
00711 {
00712 d->niceVertRange = d->verticalMax - d->verticalMin;
00713
00714
00715 if (d->niceVertRange < 0.000001) {
00716 d->niceVertRange = 1.0;
00717 }
00718
00719 d->niceVertMin = d->verticalMin;
00720 if (d->verticalMin != 0.0) {
00721 double dim = pow(10, floor(log10(fabs(d->verticalMin)))) / 2;
00722 if (d->verticalMin < 0.0) {
00723 d->niceVertMin = dim * floor(d->verticalMin / dim);
00724 } else {
00725 d->niceVertMin = dim * ceil(d->verticalMin / dim);
00726 }
00727 d->niceVertRange = d->verticalMax - d->niceVertMin;
00728 if (d->niceVertRange < 0.000001) {
00729 d->niceVertRange = 1.0;
00730 }
00731 }
00732
00733 double step = d->niceVertRange / (d->scaledBy * (d->horizontalLinesCount + 1));
00734 int logdim = (int)floor(log10(step));
00735 double dim = pow((double)10.0, logdim) / 2;
00736 int a = (int)ceil(step / dim);
00737 if (logdim >= 0) {
00738 d->precision = 0;
00739 } else if (a % 2 == 0) {
00740 d->precision = -logdim;
00741 } else {
00742 d->precision = 1 - logdim;
00743 }
00744 d->niceVertRange = d->scaledBy * dim * a * (d->horizontalLinesCount + 1);
00745 d->niceVertMax = d->niceVertMin + d->niceVertRange;
00746 }
00747
00748 void SignalPlotter::drawTopBarFrame(QPainter *p, int separatorX, int height)
00749 {
00750
00751
00752
00753 p->setPen(Qt::NoPen);
00754 p->setPen(d->fontColor);
00755 p->drawText(0, 1, separatorX, height, Qt::AlignCenter, d->title);
00756 p->setPen(d->horizontalLinesColor);
00757 p->drawLine(separatorX - 1, 1, separatorX - 1, height - 1);
00758 }
00759
00760 void SignalPlotter::drawTopBarContents(QPainter *p, int x, int width, int height)
00761 {
00762
00763
00764 double bias = -d->niceVertMin;
00765 double scaleFac = width / d->niceVertRange;
00766
00767
00768 if (!d->plotData.isEmpty()) {
00769 QList<double> newestData = d->plotData.first();
00770 for (int i = newestData.count()-1; i >= 0; --i) {
00771 double newest_datapoint = newestData.at(i);
00772 int start = x + (int)(bias * scaleFac);
00773 int end = x + (int)((bias += newest_datapoint) * scaleFac);
00774 int start2 = qMin(start, end);
00775 end = qMax(start, end);
00776 start = start2;
00777
00778
00779
00780
00781
00782 p->setPen(Qt::NoPen);
00783 QLinearGradient linearGrad(QPointF(start, 1), QPointF(end, 1));
00784 linearGrad.setColorAt(0, d->plotColors[i].darkColor);
00785 linearGrad.setColorAt(1, d->plotColors[i].color);
00786 p->fillRect(start, 1, end - start, height-1, QBrush(linearGrad));
00787 }
00788 }
00789 }
00790
00791 void SignalPlotter::drawVerticalLines(QPainter *p, int top, int w, int h)
00792 {
00793 p->setPen(d->verticalLinesColor);
00794 for (int x = d->verticalLinesOffset; x < (w - 2); x += d->verticalLinesDistance) {
00795 p->drawLine(w - x, top, w - x, h + top -1);
00796 }
00797 }
00798
00799 void SignalPlotter::drawPlots(QPainter *p, int top, int w, int h, int horizontalScale)
00800 {
00801 Q_ASSERT(d->niceVertRange != 0);
00802
00803 if (d->niceVertRange == 0) {
00804 d->niceVertRange = 1;
00805 }
00806 double scaleFac = (h - 1) / d->niceVertRange;
00807
00808 int xPos = 0;
00809 QList< QList<double> >::Iterator it = d->plotData.begin();
00810
00811 p->setPen(Qt::NoPen);
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824 if (d->useAutoRange) {
00825 d->verticalMin = d->verticalMax = 0.0;
00826 }
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840 for (uint i = 0; it != d->plotData.end() && i < d->samples; ++i) {
00841 QPen pen;
00842 pen.setWidth(1);
00843 pen.setCapStyle(Qt::FlatCap);
00844
00845
00846
00847
00848
00849 QList<double> datapoints = *it;
00850 QList<double> prev_datapoints = datapoints;
00851 QList<double> prev_prev_datapoints = datapoints;
00852 QList<double> prev_prev_prev_datapoints = datapoints;
00853
00854 if (i == 0 && d->bezierCurveOffset > 0) {
00855
00856
00857 xPos += horizontalScale * d->bezierCurveOffset;
00858 if (d->bezierCurveOffset == 1) {
00859 prev_datapoints = *it;
00860 ++it;
00861 if (it != d->plotData.end()) {
00862 prev_prev_prev_datapoints = prev_prev_datapoints = *it;
00863 } else {
00864 prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
00865 }
00866 } else {
00867
00868 prev_datapoints = *it;
00869 Q_ASSERT(it != d->plotData.end());
00870 ++it;
00871 prev_prev_datapoints = *it;
00872 Q_ASSERT(it != d->plotData.end());
00873 ++it;
00874 if (it != d->plotData.end()) {
00875 prev_prev_prev_datapoints = *it;
00876 } else {
00877 prev_prev_prev_datapoints = prev_prev_datapoints;
00878 }
00879 }
00880 } else {
00881
00882 xPos += horizontalScale * 3;
00883 it++;
00884 if (it != d->plotData.end()) {
00885 prev_datapoints = *it;
00886 it++;
00887 if (it != d->plotData.end()) {
00888 prev_prev_datapoints = *it;
00889 it++;
00890 if (it != d->plotData.end()) {
00891
00892 prev_prev_prev_datapoints = *it;
00893 } else {
00894
00895
00896 prev_prev_prev_datapoints = prev_prev_datapoints;
00897 }
00898 } else {
00899 prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
00900 }
00901 } else {
00902 prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints = datapoints;
00903 }
00904 }
00905
00906 float x0 = w - xPos + 3.0 * horizontalScale;
00907 float x1 = w - xPos + 2.0 * horizontalScale;
00908 float x2 = w - xPos + 1.0 * horizontalScale;
00909 float x3 = w - xPos;
00910 float y0 = h - 1 + top;
00911 float y1 = y0;
00912 float y2 = y0;
00913 float y3 = y0;
00914
00915 int offset = 0;
00916 double max_y = 0;
00917 double min_y = 0;
00918 for (int j = qMin(datapoints.size(), d->plotColors.size()) - 1; j >=0; --j) {
00919 if (d->useAutoRange) {
00920
00921
00922 double current_maxvalue =
00923 qMax(datapoints[j],
00924 qMax(prev_datapoints[j],
00925 qMax(prev_prev_datapoints[j],
00926 prev_prev_prev_datapoints[j])));
00927 double current_minvalue =
00928 qMin(datapoints[j],
00929 qMin(prev_datapoints[j],
00930 qMin(prev_prev_datapoints[j],
00931 prev_prev_prev_datapoints[j])));
00932 d->verticalMax = qMax(d->verticalMax, current_maxvalue);
00933 d->verticalMin = qMin(d->verticalMin, current_maxvalue);
00934 if (d->stackPlots) {
00935 max_y += current_maxvalue;
00936 min_y += current_minvalue;
00937 }
00938 }
00939
00940
00941 if (j < prev_prev_prev_datapoints.count() &&
00942 j < prev_prev_datapoints.count() &&
00943 j < prev_datapoints.count()) {
00944
00945
00946
00947
00948
00949
00950 float delta_y0;
00951 delta_y0 = (datapoints[j] - d->niceVertMin) * scaleFac;
00952
00953 float delta_y1;
00954 delta_y1 = (prev_datapoints[j] - d->niceVertMin) * scaleFac;
00955
00956 float delta_y2;
00957 delta_y2 = (prev_prev_datapoints[j] - d->niceVertMin) * scaleFac;
00958
00959 float delta_y3;
00960 delta_y3 = (prev_prev_prev_datapoints[j] - d->niceVertMin) * scaleFac;
00961
00962 QPainterPath path;
00963 if (d->stackPlots && offset) {
00964
00965
00966 if (delta_y0 < 3) {
00967 delta_y0=3;
00968 }
00969 if (delta_y1 < 3) {
00970 delta_y1=3;
00971 }
00972 if (delta_y2 < 3) {
00973 delta_y2=3;
00974 }
00975 if (delta_y3 < 3) {
00976 delta_y3=3;
00977 }
00978 }
00979 path.moveTo(x0, y0 - delta_y0);
00980 path.cubicTo(x1, y1 - delta_y1, x2, y2 - delta_y2, x3, y3 - delta_y3);
00981
00982 if (d->fillPlots) {
00983 QPainterPath path2(path);
00984 QLinearGradient myGradient(0,(h - 1 + top), 0, (h - 1 + top) / 5);
00985 Q_ASSERT(d->plotColors.size() >= j);
00986 QColor c0(d->plotColors[j].darkColor);
00987 QColor c1(d->plotColors[j].color);
00988 c0.setAlpha(150);
00989 c1.setAlpha(150);
00990 myGradient.setColorAt(0, c0);
00991 myGradient.setColorAt(1, c1);
00992
00993 path2.lineTo(x3, y3 - offset);
00994 if (d->stackPlots) {
00995
00996
00997 path2.cubicTo(x2, y2 - offset, x1, y1 - offset, x0, y0 - offset);
00998 } else {
00999 path2.lineTo(x0, y0 - 1);
01000 }
01001 p->setBrush(myGradient);
01002 p->setPen(Qt::NoPen);
01003 p->drawPath(path2);
01004 }
01005 p->setBrush(Qt::NoBrush);
01006 Q_ASSERT(d->plotColors.size() >= j);
01007 pen.setColor(d->plotColors[j].color);
01008 p->setPen(pen);
01009 p->drawPath(path);
01010
01011 if (d->stackPlots) {
01012
01013
01014
01015 y0 -= delta_y0;
01016 y1 -= delta_y1;
01017 y2 -= delta_y2;
01018 y3 -= delta_y3;
01019 offset = 1;
01020 }
01021 }
01022 if (d->useAutoRange && d->stackPlots) {
01023 d->verticalMax = qMax(max_y, d->verticalMax);
01024 d->verticalMin = qMin(min_y, d->verticalMin);
01025 }
01026 }
01027 }
01028 }
01029
01030 void SignalPlotter::drawAxisText(QPainter *p, int top, int h)
01031 {
01032
01033
01034 QString val;
01035
01036
01037
01038
01039
01040 p->setPen(d->fontColor);
01041 double stepsize = d->niceVertRange / (d->scaledBy * (d->horizontalLinesCount + 1));
01042 int step =
01043 (int)ceil((d->horizontalLinesCount+1) *
01044 (p->fontMetrics().height() + p->fontMetrics().leading() / 2.0) / h);
01045 if (step == 0) {
01046 step = 1;
01047 }
01048 for (int y = d->horizontalLinesCount + 1; y >= 1; y-= step) {
01049 int y_coord =
01050 top + (y * (h - 1)) / (d->horizontalLinesCount + 1);
01051 if (y_coord - p->fontMetrics().ascent() < top) {
01052
01053
01054 continue;
01055 }
01056 double value;
01057 if ((uint)y == d->horizontalLinesCount + 1) {
01058 value = d->niceVertMin;
01059 } else {
01060 value = d->niceVertMax / d->scaledBy - y * stepsize;
01061 }
01062
01063 QString number = KGlobal::locale()->formatNumber(value, d->precision);
01064 val = QString("%1 %2").arg(number, d->unit);
01065 p->drawText(6, y_coord - 3, val);
01066 }
01067 }
01068
01069 void SignalPlotter::drawHorizontalLines(QPainter *p, int top, int w, int h)
01070 {
01071 p->setPen(d->horizontalLinesColor);
01072 for (uint y = 0; y <= d->horizontalLinesCount + 1; y++) {
01073
01074 int y_coord = top + (y * (h - 1)) / (d->horizontalLinesCount + 1);
01075 p->drawLine(0, y_coord, w - 2, y_coord);
01076 }
01077 }
01078
01079 double SignalPlotter::lastValue(uint i) const
01080 {
01081 if (d->plotData.isEmpty() || d->plotData.first().size() <= (int)i) {
01082 return 0;
01083 }
01084 return d->plotData.first()[i];
01085 }
01086
01087 QString SignalPlotter::lastValueAsString(uint i) const
01088 {
01089 if (d->plotData.isEmpty()) {
01090 return QString();
01091 }
01092 double value = d->plotData.first()[i] / d->scaledBy;
01093 QString number = KGlobal::locale()->formatNumber(value, (value >= 100)?0:2);
01094 return QString("%1 %2").arg(number, d->unit);
01095 }
01096
01097 }
01098
01099 #include "signalplotter.moc"
01100