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