iCub-main
Loading...
Searching...
No Matches
qcustomplot.cpp
Go to the documentation of this file.
1/***************************************************************************
2** **
3** QCustomPlot, an easy to use, modern plotting widget for Qt **
4** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer **
5** **
6** This program is free software: you can redistribute it and/or modify **
7** it under the terms of the GNU General Public License as published by **
8** the Free Software Foundation, either version 3 of the License, or **
9** (at your option) any later version. **
10** **
11** This program is distributed in the hope that it will be useful, **
12** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14** GNU General Public License for more details. **
15** **
16** You should have received a copy of the GNU General Public License **
17** along with this program. If not, see http://www.gnu.org/licenses/. **
18** **
19****************************************************************************
20** Author: Emanuel Eichhammer **
21** Website/Contact: http://www.qcustomplot.com/ **
22** Date: 07.04.14 **
23** Version: 1.2.1 **
24****************************************************************************/
25
26#include "qcustomplot.h"
27
28
29
30
34
52 QPainter(),
53 mModes(pmDefault),
54 mIsAntialiasing(false)
55{
56 // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
57 // a call to begin() will follow
58}
59
66QCPPainter::QCPPainter(QPaintDevice *device) :
67 QPainter(device),
68 mModes(pmDefault),
69 mIsAntialiasing(false)
70{
71#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
72 if (isActive())
73 setRenderHint(QPainter::NonCosmeticDefaultPen);
74#endif
75}
76
80
87void QCPPainter::setPen(const QPen &pen)
88{
89 QPainter::setPen(pen);
90 if (mModes.testFlag(pmNonCosmetic))
92}
93
101void QCPPainter::setPen(const QColor &color)
102{
103 QPainter::setPen(color);
104 if (mModes.testFlag(pmNonCosmetic))
106}
107
115void QCPPainter::setPen(Qt::PenStyle penStyle)
116{
117 QPainter::setPen(penStyle);
118 if (mModes.testFlag(pmNonCosmetic))
120}
121
130void QCPPainter::drawLine(const QLineF &line)
131{
132 if (mIsAntialiasing || mModes.testFlag(pmVectorized))
133 QPainter::drawLine(line);
134 else
135 QPainter::drawLine(line.toLine());
136}
137
145{
146 setRenderHint(QPainter::Antialiasing, enabled);
147 if (mIsAntialiasing != enabled)
148 {
149 mIsAntialiasing = enabled;
150 if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
151 {
152 if (mIsAntialiasing)
153 translate(0.5, 0.5);
154 else
155 translate(-0.5, -0.5);
156 }
157 }
158}
159
164void QCPPainter::setModes(QCPPainter::PainterModes modes)
165{
166 mModes = modes;
167}
168
180bool QCPPainter::begin(QPaintDevice *device)
181{
182 bool result = QPainter::begin(device);
183#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
184 if (result)
185 setRenderHint(QPainter::NonCosmeticDefaultPen);
186#endif
187 return result;
188}
189
196{
197 if (!enabled && mModes.testFlag(mode))
198 mModes &= ~mode;
199 else if (enabled && !mModes.testFlag(mode))
200 mModes |= mode;
201}
202
212{
214 QPainter::save();
215}
216
226{
227 if (!mAntialiasingStack.isEmpty())
229 else
230 qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
231 QPainter::restore();
232}
233
239{
240 if (qFuzzyIsNull(pen().widthF()))
241 {
242 QPen p = pen();
243 p.setWidth(1);
244 QPainter::setPen(p);
245 }
246}
247
248
252
314/* start documentation of inline functions */
315
335/* end documentation of inline functions */
336
344 mSize(6),
345 mShape(ssNone),
346 mPen(Qt::NoPen),
347 mBrush(Qt::NoBrush),
348 mPenDefined(false)
349{
350}
351
360 mSize(size),
361 mShape(shape),
362 mPen(Qt::NoPen),
363 mBrush(Qt::NoBrush),
364 mPenDefined(false)
365{
366}
367
372QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
373 mSize(size),
374 mShape(shape),
375 mPen(QPen(color)),
376 mBrush(Qt::NoBrush),
377 mPenDefined(true)
378{
379}
380
385QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
386 mSize(size),
387 mShape(shape),
388 mPen(QPen(color)),
389 mBrush(QBrush(fill)),
390 mPenDefined(true)
391{
392}
393
409QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
410 mSize(size),
411 mShape(shape),
412 mPen(pen),
413 mBrush(brush),
414 mPenDefined(pen.style() != Qt::NoPen)
415{
416}
417
422QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
423 mSize(5),
424 mShape(ssPixmap),
425 mPen(Qt::NoPen),
426 mBrush(Qt::NoBrush),
427 mPixmap(pixmap),
428 mPenDefined(false)
429{
430}
431
441QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
442 mSize(size),
443 mShape(ssCustom),
444 mPen(pen),
445 mBrush(brush),
446 mCustomPath(customPath),
447 mPenDefined(false)
448{
449}
450
457{
458 mSize = size;
459}
460
473
482void QCPScatterStyle::setPen(const QPen &pen)
483{
484 mPenDefined = true;
485 mPen = pen;
486}
487
494void QCPScatterStyle::setBrush(const QBrush &brush)
495{
496 mBrush = brush;
497}
498
506void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
507{
509 mPixmap = pixmap;
510}
511
517void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
518{
521}
522
532void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
533{
534 painter->setPen(mPenDefined ? mPen : defaultPen);
535 painter->setBrush(mBrush);
536}
537
546void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
547{
548 drawShape(painter, pos.x(), pos.y());
549}
550
554void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
555{
556 double w = mSize/2.0;
557 switch (mShape)
558 {
559 case ssNone: break;
560 case ssDot:
561 {
562 painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
563 break;
564 }
565 case ssCross:
566 {
567 painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
568 painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
569 break;
570 }
571 case ssPlus:
572 {
573 painter->drawLine(QLineF(x-w, y, x+w, y));
574 painter->drawLine(QLineF( x, y+w, x, y-w));
575 break;
576 }
577 case ssCircle:
578 {
579 painter->drawEllipse(QPointF(x , y), w, w);
580 break;
581 }
582 case ssDisc:
583 {
584 QBrush b = painter->brush();
585 painter->setBrush(painter->pen().color());
586 painter->drawEllipse(QPointF(x , y), w, w);
587 painter->setBrush(b);
588 break;
589 }
590 case ssSquare:
591 {
592 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
593 break;
594 }
595 case ssDiamond:
596 {
597 painter->drawLine(QLineF(x-w, y, x, y-w));
598 painter->drawLine(QLineF( x, y-w, x+w, y));
599 painter->drawLine(QLineF(x+w, y, x, y+w));
600 painter->drawLine(QLineF( x, y+w, x-w, y));
601 break;
602 }
603 case ssStar:
604 {
605 painter->drawLine(QLineF(x-w, y, x+w, y));
606 painter->drawLine(QLineF( x, y+w, x, y-w));
607 painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
608 painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
609 break;
610 }
611 case ssTriangle:
612 {
613 painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
614 painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
615 painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
616 break;
617 }
619 {
620 painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
621 painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
622 painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
623 break;
624 }
625 case ssCrossSquare:
626 {
627 painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
628 painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
629 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
630 break;
631 }
632 case ssPlusSquare:
633 {
634 painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
635 painter->drawLine(QLineF( x, y+w, x, y-w));
636 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
637 break;
638 }
639 case ssCrossCircle:
640 {
641 painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
642 painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
643 painter->drawEllipse(QPointF(x, y), w, w);
644 break;
645 }
646 case ssPlusCircle:
647 {
648 painter->drawLine(QLineF(x-w, y, x+w, y));
649 painter->drawLine(QLineF( x, y+w, x, y-w));
650 painter->drawEllipse(QPointF(x, y), w, w);
651 break;
652 }
653 case ssPeace:
654 {
655 painter->drawLine(QLineF(x, y-w, x, y+w));
656 painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
657 painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
658 painter->drawEllipse(QPointF(x, y), w, w);
659 break;
660 }
661 case ssPixmap:
662 {
663 painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
664 break;
665 }
666 case ssCustom:
667 {
668 QTransform oldTransform = painter->transform();
669 painter->translate(x, y);
670 painter->scale(mSize/6.0, mSize/6.0);
671 painter->drawPath(mCustomPath);
672 painter->setTransform(oldTransform);
673 break;
674 }
675 }
676}
677
678
682
725/* start documentation of inline functions */
726
741/* end documentation of inline functions */
742
751QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
752 QObject(parentPlot),
753 mParentPlot(parentPlot),
754 mName(layerName),
755 mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
756 mVisible(true)
757{
758 // Note: no need to make sure layerName is unique, because layer
759 // management is done with QCustomPlot functions.
760}
761
763{
764 // If child layerables are still on this layer, detach them, so they don't try to reach back to this
765 // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
766 // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
767 // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
768
769 while (!mChildren.isEmpty())
770 mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
771
772 if (mParentPlot->currentLayer() == this)
773 qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
774}
775
784void QCPLayer::setVisible(bool visible)
785{
787}
788
799void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
800{
801 if (!mChildren.contains(layerable))
802 {
803 if (prepend)
804 mChildren.prepend(layerable);
805 else
806 mChildren.append(layerable);
807 } else
808 qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
809}
810
821{
822 if (!mChildren.removeOne(layerable))
823 qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
824}
825
826
830
843/* start documentation of inline functions */
844
858/* end documentation of inline functions */
859/* start documentation of pure virtual functions */
860
901/* end documentation of pure virtual functions */
902/* start documentation of signals */
903
912/* end documentation of signals */
913
932QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
933 QObject(plot),
934 mVisible(true),
935 mParentPlot(plot),
936 mParentLayerable(parentLayerable),
937 mLayer(0),
938 mAntialiased(true)
939{
940 if (mParentPlot)
941 {
942 if (targetLayer.isEmpty())
944 else if (!setLayer(targetLayer))
945 qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
946 }
947}
948
950{
951 if (mLayer)
952 {
953 mLayer->removeChild(this);
954 mLayer = 0;
955 }
956}
957
964{
965 mVisible = on;
966}
967
975{
976 return moveToLayer(layer, false);
977}
978
984bool QCPLayerable::setLayer(const QString &layerName)
985{
986 if (!mParentPlot)
987 {
988 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
989 return false;
990 }
991 if (QCPLayer *layer = mParentPlot->layer(layerName))
992 {
993 return setLayer(layer);
994 } else
995 {
996 qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
997 return false;
998 }
999}
1000
1008{
1009 mAntialiased = enabled;
1010}
1011
1026{
1027 return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1028}
1029
1064double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1065{
1066 Q_UNUSED(pos)
1067 Q_UNUSED(onlySelectable)
1068 Q_UNUSED(details)
1069 return -1.0;
1070}
1071
1090{
1091 if (mParentPlot)
1092 {
1093 qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1094 return;
1095 }
1096
1097 if (!parentPlot)
1098 qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1099
1102}
1103
1119
1128bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1129{
1130 if (layer && !mParentPlot)
1131 {
1132 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1133 return false;
1134 }
1135 if (layer && layer->parentPlot() != mParentPlot)
1136 {
1137 qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1138 return false;
1139 }
1140
1141 QCPLayer *oldLayer = mLayer;
1142 if (mLayer)
1143 mLayer->removeChild(this);
1144 mLayer = layer;
1145 if (mLayer)
1146 mLayer->addChild(this, prepend);
1147 if (mLayer != oldLayer)
1148 emit layerChanged(mLayer);
1149 return true;
1150}
1151
1159void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1160{
1161 if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1162 painter->setAntialiasing(false);
1163 else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1164 painter->setAntialiasing(true);
1165 else
1166 painter->setAntialiasing(localAntialiased);
1167}
1168
1186{
1187 Q_UNUSED(parentPlot)
1188}
1189
1205
1216{
1217 if (mParentPlot)
1218 return mParentPlot->viewport();
1219 else
1220 return QRect();
1221}
1222
1251void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1252{
1253 Q_UNUSED(event)
1254 Q_UNUSED(additive)
1255 Q_UNUSED(details)
1256 Q_UNUSED(selectionStateChanged)
1257}
1258
1271void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1272{
1273 Q_UNUSED(selectionStateChanged)
1274}
1275
1276
1280
1295const double QCPRange::minRange = 1e-280;
1296
1305const double QCPRange::maxRange = 1e250;
1306
1311 lower(0),
1312 upper(0)
1313{
1314}
1315
1319QCPRange::QCPRange(double lower, double upper) :
1320 lower(lower),
1321 upper(upper)
1322{
1323 normalize();
1324}
1325
1329double QCPRange::size() const
1330{
1331 return upper-lower;
1332}
1333
1337double QCPRange::center() const
1338{
1339 return (upper+lower)*0.5;
1340}
1341
1347{
1348 if (lower > upper)
1349 qSwap(lower, upper);
1350}
1351
1360void QCPRange::expand(const QCPRange &otherRange)
1361{
1362 if (lower > otherRange.lower)
1363 lower = otherRange.lower;
1364 if (upper < otherRange.upper)
1365 upper = otherRange.upper;
1366}
1367
1368
1375QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1376{
1377 QCPRange result = *this;
1378 result.expand(otherRange);
1379 return result;
1380}
1381
1395{
1396 double rangeFac = 1e-3;
1397 QCPRange sanitizedRange(lower, upper);
1398 sanitizedRange.normalize();
1399 // can't have range spanning negative and positive values in log plot, so change range to fix it
1400 //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1401 if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1402 {
1403 // case lower is 0
1404 if (rangeFac < sanitizedRange.upper*rangeFac)
1405 sanitizedRange.lower = rangeFac;
1406 else
1407 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1408 } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1409 else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1410 {
1411 // case upper is 0
1412 if (-rangeFac > sanitizedRange.lower*rangeFac)
1413 sanitizedRange.upper = -rangeFac;
1414 else
1415 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1416 } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1417 {
1418 // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1419 if (-sanitizedRange.lower > sanitizedRange.upper)
1420 {
1421 // negative is wider, do same as in case upper is 0
1422 if (-rangeFac > sanitizedRange.lower*rangeFac)
1423 sanitizedRange.upper = -rangeFac;
1424 else
1425 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1426 } else
1427 {
1428 // positive is wider, do same as in case lower is 0
1429 if (rangeFac < sanitizedRange.upper*rangeFac)
1430 sanitizedRange.lower = rangeFac;
1431 else
1432 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1433 }
1434 }
1435 // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1436 return sanitizedRange;
1437}
1438
1444{
1445 QCPRange sanitizedRange(lower, upper);
1446 sanitizedRange.normalize();
1447 return sanitizedRange;
1448}
1449
1453bool QCPRange::contains(double value) const
1454{
1455 return value >= lower && value <= upper;
1456}
1457
1466bool QCPRange::validRange(double lower, double upper)
1467{
1468 /*
1469 return (lower > -maxRange &&
1470 upper < maxRange &&
1471 qAbs(lower-upper) > minRange &&
1472 (lower < -minRange || lower > minRange) &&
1473 (upper < -minRange || upper > minRange));
1474 */
1475 return (lower > -maxRange &&
1476 upper < maxRange &&
1477 qAbs(lower-upper) > minRange &&
1478 qAbs(lower-upper) < maxRange);
1479}
1480
1491{
1492 /*
1493 return (range.lower > -maxRange &&
1494 range.upper < maxRange &&
1495 qAbs(range.lower-range.upper) > minRange &&
1496 qAbs(range.lower-range.upper) < maxRange &&
1497 (range.lower < -minRange || range.lower > minRange) &&
1498 (range.upper < -minRange || range.upper > minRange));
1499 */
1500 return (range.lower > -maxRange &&
1501 range.upper < maxRange &&
1502 qAbs(range.lower-range.upper) > minRange &&
1503 qAbs(range.lower-range.upper) < maxRange);
1504}
1505
1506
1625
1662/* start documentation of inline functions */
1663
1670/* end documentation of inline functions */
1671
1676 QObject(parentPlot),
1677 mParentPlot(parentPlot)
1678{
1679 mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1680 mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1681 mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1682 mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1683}
1684
1689
1695{
1696 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1697 while (it.hasNext())
1698 {
1699 it.next();
1700 if (!it.value().isEmpty())
1701 return false;
1702 }
1703 return true;
1704}
1705
1711{
1712 // make all children remove themselves from this margin group:
1713 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1714 while (it.hasNext())
1715 {
1716 it.next();
1717 const QList<QCPLayoutElement*> elements = it.value();
1718 for (int i=elements.size()-1; i>=0; --i)
1719 elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1720 }
1721}
1722
1734{
1735 // query all automatic margins of the layout elements in this margin group side and find maximum:
1736 int result = 0;
1737 const QList<QCPLayoutElement*> elements = mChildren.value(side);
1738 for (int i=0; i<elements.size(); ++i)
1739 {
1740 if (!elements.at(i)->autoMargins().testFlag(side))
1741 continue;
1742 int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1743 if (m > result)
1744 result = m;
1745 }
1746 return result;
1747}
1748
1756{
1757 if (!mChildren[side].contains(element))
1758 mChildren[side].append(element);
1759 else
1760 qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1761}
1762
1770{
1771 if (!mChildren[side].removeOne(element))
1772 qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1773}
1774
1775
1779
1806/* start documentation of inline functions */
1807
1855/* end documentation of inline functions */
1856
1861 QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1862 mParentLayout(0),
1863 mMinimumSize(),
1864 mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1865 mRect(0, 0, 0, 0),
1866 mOuterRect(0, 0, 0, 0),
1867 mMargins(0, 0, 0, 0),
1868 mMinimumMargins(0, 0, 0, 0),
1869 mAutoMargins(QCP::msAll)
1870{
1871}
1872
1874{
1875 setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1876 // unregister at layout:
1877 if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
1878 mParentLayout->take(this);
1879}
1880
1892void QCPLayoutElement::setOuterRect(const QRect &rect)
1893{
1894 if (mOuterRect != rect)
1895 {
1896 mOuterRect = rect;
1897 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1898 }
1899}
1900
1912void QCPLayoutElement::setMargins(const QMargins &margins)
1913{
1914 if (mMargins != margins)
1915 {
1916 mMargins = margins;
1917 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1918 }
1919}
1920
1930void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1931{
1932 if (mMinimumMargins != margins)
1933 {
1935 }
1936}
1937
1948void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1949{
1950 mAutoMargins = sides;
1951}
1952
1963{
1964 if (mMinimumSize != size)
1965 {
1966 mMinimumSize = size;
1967 if (mParentLayout)
1969 }
1970}
1971
1976void QCPLayoutElement::setMinimumSize(int width, int height)
1977{
1978 setMinimumSize(QSize(width, height));
1979}
1980
1986{
1987 if (mMaximumSize != size)
1988 {
1989 mMaximumSize = size;
1990 if (mParentLayout)
1992 }
1993}
1994
1999void QCPLayoutElement::setMaximumSize(int width, int height)
2000{
2001 setMaximumSize(QSize(width, height));
2002}
2003
2015void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
2016{
2017 QVector<QCP::MarginSide> sideVector;
2018 if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
2019 if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
2020 if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
2021 if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
2022
2023 for (int i=0; i<sideVector.size(); ++i)
2024 {
2025 QCP::MarginSide side = sideVector.at(i);
2026 if (marginGroup(side) != group)
2027 {
2028 QCPMarginGroup *oldGroup = marginGroup(side);
2029 if (oldGroup) // unregister at old group
2030 oldGroup->removeChild(side, this);
2031
2032 if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
2033 {
2034 mMarginGroups.remove(side);
2035 } else // setting to a new group
2036 {
2037 mMarginGroups[side] = group;
2038 group->addChild(side, this);
2040 }
2057{
2058 if (phase == upMargins)
2059 {
2061 {
2062 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
2063 QMargins newMargins = mMargins;
2064 foreach (QCP::MarginSide side, QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom)
2065 {
2066 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
2067 {
2068 if (mMarginGroups.contains(side))
2069 QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
2070 else
2071 QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
2072 // apply minimum margin restrictions:
2073 if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
2075 }
2076 }
2077 setMargins(newMargins);
2078 }
2079 }
2080}
2081
2090{
2091 return mMinimumSize;
2092}
2093
2102{
2103 return mMaximumSize;
2104}
2105
2113QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
2114{
2115 Q_UNUSED(recursive)
2116 return QList<QCPLayoutElement*>();
2117}
2118
2130double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2131{
2132 Q_UNUSED(details)
2133
2134 if (onlySelectable)
2135 return -1;
2136
2137 if (QRectF(mOuterRect).contains(pos))
2138 {
2139 if (mParentPlot)
2140 return mParentPlot->selectionTolerance()*0.99;
2141 else
2142 {
2143 qDebug() << Q_FUNC_INFO << "parent plot not defined";
2144 return -1;
2145 }
2146 } else
2147 return -1;
2148}
2149
2156{
2157 foreach (QCPLayoutElement* el, elements(false))
2158 {
2159 if (!el->parentPlot())
2161 }
2162}
2163
2177
2181
2205/* start documentation of pure virtual functions */
2206
2249/* end documentation of pure virtual functions */
2250
2258
2268{
2270
2271 // set child element rects according to layout:
2272 if (phase == upLayout)
2273 updateLayout();
2274
2275 // propagate update call to child elements:
2276 const int elCount = elementCount();
2277 for (int i=0; i<elCount; ++i)
2278 {
2279 if (QCPLayoutElement *el = elementAt(i))
2280 el->update(phase);
2281 }
2282}
2283
2284/* inherits documentation from base class */
2285QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2286{
2287 const int c = elementCount();
2288 QList<QCPLayoutElement*> result;
2289#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2290 result.reserve(c);
2291#endif
2292 for (int i=0; i<c; ++i)
2293 result.append(elementAt(i));
2294 if (recursive)
2295 {
2296 for (int i=0; i<c; ++i)
2297 {
2298 if (result.at(i))
2299 result << result.at(i)->elements(recursive);
2300 }
2301 }
2302 return result;
2303}
2304
2313{
2314}
2315
2325bool QCPLayout::removeAt(int index)
2326{
2327 if (QCPLayoutElement *el = takeAt(index))
2328 {
2329 delete el;
2330 return true;
2331 } else
2332 return false;
2333}
2334
2345{
2346 if (take(element))
2347 {
2348 delete element;
2349 return true;
2350 } else
2351 return false;
2352}
2353
2360{
2361 for (int i=elementCount()-1; i>=0; --i)
2362 {
2363 if (elementAt(i))
2364 removeAt(i);
2365 }
2366 simplify();
2367}
2368
2378{
2379 if (QWidget *w = qobject_cast<QWidget*>(parent()))
2380 w->updateGeometry();
2381 else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2382 l->sizeConstraintsChanged();
2383}
2384
2398{
2399}
2400
2401
2415{
2416 if (el)
2417 {
2418 el->mParentLayout = this;
2419 el->setParentLayerable(this);
2420 el->setParent(this);
2421 if (!el->parentPlot())
2423 } else
2424 qDebug() << Q_FUNC_INFO << "Null element passed";
2425}
2426
2438{
2439 if (el)
2440 {
2441 el->mParentLayout = 0;
2442 el->setParentLayerable(0);
2443 el->setParent(mParentPlot);
2444 // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2445 } else
2446 qDebug() << Q_FUNC_INFO << "Null element passed";
2447}
2448
2478QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2479{
2480 if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2481 {
2482 qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2483 return QVector<int>();
2484 }
2485 if (stretchFactors.isEmpty())
2486 return QVector<int>();
2487 int sectionCount = stretchFactors.size();
2488 QVector<double> sectionSizes(sectionCount);
2489 // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2490 int minSizeSum = 0;
2491 for (int i=0; i<sectionCount; ++i)
2492 minSizeSum += minSizes.at(i);
2493 if (totalSize < minSizeSum)
2494 {
2495 // new stretch factors are minimum sizes and minimum sizes are set to zero:
2496 for (int i=0; i<sectionCount; ++i)
2497 {
2498 stretchFactors[i] = minSizes.at(i);
2499 minSizes[i] = 0;
2500 }
2501 }
2502
2503 QList<int> minimumLockedSections;
2504 QList<int> unfinishedSections;
2505 for (int i=0; i<sectionCount; ++i)
2506 unfinishedSections.append(i);
2507 double freeSize = totalSize;
2508
2509 int outerIterations = 0;
2510 while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2511 {
2512 ++outerIterations;
2513 int innerIterations = 0;
2514 while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2515 {
2516 ++innerIterations;
2517 // find section that hits its maximum next:
2518 int nextId = -1;
2519 double nextMax = 1e12;
2520 for (int i=0; i<unfinishedSections.size(); ++i)
2521 {
2522 int secId = unfinishedSections.at(i);
2523 double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2524 if (hitsMaxAt < nextMax)
2525 {
2526 nextMax = hitsMaxAt;
2527 nextId = secId;
2528 }
2529 }
2530 // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
2531 // actually hits its maximum, without exceeding the total size when we add up all sections)
2532 double stretchFactorSum = 0;
2533 for (int i=0; i<unfinishedSections.size(); ++i)
2534 stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2535 double nextMaxLimit = freeSize/stretchFactorSum;
2536 if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2537 {
2538 for (int i=0; i<unfinishedSections.size(); ++i)
2539 {
2540 sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2541 freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2542 }
2543 unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2544 } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2545 {
2546 for (int i=0; i<unfinishedSections.size(); ++i)
2547 sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2548 unfinishedSections.clear();
2549 }
2550 }
2551 if (innerIterations == sectionCount*2)
2552 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2553
2554 // now check whether the resulting section sizes violate minimum restrictions:
2555 bool foundMinimumViolation = false;
2556 for (int i=0; i<sectionSizes.size(); ++i)
2557 {
2558 if (minimumLockedSections.contains(i))
2559 continue;
2560 if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2561 {
2562 sectionSizes[i] = minSizes.at(i); // set it to minimum
2563 foundMinimumViolation = true; // make sure we repeat the whole optimization process
2564 minimumLockedSections.append(i);
2565 }
2566 }
2567 if (foundMinimumViolation)
2568 {
2569 freeSize = totalSize;
2570 for (int i=0; i<sectionCount; ++i)
2571 {
2572 if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2573 unfinishedSections.append(i);
2574 else
2575 freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2576 }
2577 // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2578 for (int i=0; i<unfinishedSections.size(); ++i)
2579 sectionSizes[unfinishedSections.at(i)] = 0;
2580 }
2581 }
2582 if (outerIterations == sectionCount*2)
2583 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2584
2585 QVector<int> result(sectionCount);
2586 for (int i=0; i<sectionCount; ++i)
2587 result[i] = qRound(sectionSizes.at(i));
2588 return result;
2589}
2590
2591
2595
2616 mColumnSpacing(5),
2617 mRowSpacing(5)
2618{
2619}
2620
2622{
2623 // clear all child layout elements. This is important because only the specific layouts know how
2624 // to handle removing elements (clear calls virtual removeAt method to do that).
2625 clear();
2626}
2627
2636QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2637{
2638 if (row >= 0 && row < mElements.size())
2639 {
2640 if (column >= 0 && column < mElements.first().size())
2641 {
2642 if (QCPLayoutElement *result = mElements.at(row).at(column))
2643 return result;
2644 else
2645 qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2646 } else
2647 qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2648 } else
2649 qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2650 return 0;
2651}
2652
2659{
2660 return mElements.size();
2661}
2662
2669{
2670 if (mElements.size() > 0)
2671 return mElements.first().size();
2672 else
2673 return 0;
2674}
2675
2686bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2687{
2688 if (element)
2689 {
2690 if (!hasElement(row, column))
2691 {
2692 if (element->layout()) // remove from old layout first
2694 expandTo(row+1, column+1);
2695 mElements[row][column] = element;
2697 return true;
2698 } else
2699 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2700 } else
2701 qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2702 return false;
2703}
2704
2711bool QCPLayoutGrid::hasElement(int row, int column)
2712{
2713 if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2714 return mElements.at(row).at(column);
2715 else
2716 return false;
2717}
2718
2730void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2731{
2732 if (column >= 0 && column < columnCount())
2733 {
2734 if (factor > 0)
2735 mColumnStretchFactors[column] = factor;
2736 else
2737 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2738 } else
2739 qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2740}
2741
2753void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2754{
2755 if (factors.size() == mColumnStretchFactors.size())
2756 {
2757 mColumnStretchFactors = factors;
2758 for (int i=0; i<mColumnStretchFactors.size(); ++i)
2759 {
2760 if (mColumnStretchFactors.at(i) <= 0)
2761 {
2762 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2763 mColumnStretchFactors[i] = 1;
2764 }
2765 }
2766 } else
2767 qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2768}
2769
2781void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2782{
2783 if (row >= 0 && row < rowCount())
2784 {
2785 if (factor > 0)
2786 mRowStretchFactors[row] = factor;
2787 else
2788 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2789 } else
2790 qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2791}
2792
2804void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2805{
2806 if (factors.size() == mRowStretchFactors.size())
2807 {
2808 mRowStretchFactors = factors;
2809 for (int i=0; i<mRowStretchFactors.size(); ++i)
2810 {
2811 if (mRowStretchFactors.at(i) <= 0)
2812 {
2813 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2814 mRowStretchFactors[i] = 1;
2815 }
2816 }
2817 } else
2818 qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2819}
2820
2827{
2828 mColumnSpacing = pixels;
2829}
2830
2837{
2838 mRowSpacing = pixels;
2839}
2840
2855void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2856{
2857 // add rows as necessary:
2858 while (rowCount() < newRowCount)
2859 {
2860 mElements.append(QList<QCPLayoutElement*>());
2861 mRowStretchFactors.append(1);
2862 }
2863 // go through rows and expand columns as necessary:
2864 int newColCount = qMax(columnCount(), newColumnCount);
2865 for (int i=0; i<rowCount(); ++i)
2866 {
2867 while (mElements.at(i).size() < newColCount)
2868 mElements[i].append(0);
2869 }
2870 while (mColumnStretchFactors.size() < newColCount)
2871 mColumnStretchFactors.append(1);
2872}
2873
2881{
2882 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2883 {
2884 expandTo(1, 1);
2885 return;
2886 }
2887
2888 if (newIndex < 0)
2889 newIndex = 0;
2890 if (newIndex > rowCount())
2891 newIndex = rowCount();
2892
2893 mRowStretchFactors.insert(newIndex, 1);
2894 QList<QCPLayoutElement*> newRow;
2895 for (int col=0; col<columnCount(); ++col)
2896 newRow.append((QCPLayoutElement*)0);
2897 mElements.insert(newIndex, newRow);
2898}
2899
2907{
2908 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2909 {
2910 expandTo(1, 1);
2911 return;
2912 }
2913
2914 if (newIndex < 0)
2915 newIndex = 0;
2916 if (newIndex > columnCount())
2917 newIndex = columnCount();
2918
2919 mColumnStretchFactors.insert(newIndex, 1);
2920 for (int row=0; row<rowCount(); ++row)
2921 mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2922}
2923
2924/* inherits documentation from base class */
2926{
2927 QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2928 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2929 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2930
2931 int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2932 int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2933 QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2934 QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2935
2936 // go through cells and set rects accordingly:
2937 int yOffset = mRect.top();
2938 for (int row=0; row<rowCount(); ++row)
2939 {
2940 if (row > 0)
2941 yOffset += rowHeights.at(row-1)+mRowSpacing;
2942 int xOffset = mRect.left();
2943 for (int col=0; col<columnCount(); ++col)
2944 {
2945 if (col > 0)
2946 xOffset += colWidths.at(col-1)+mColumnSpacing;
2947 if (mElements.at(row).at(col))
2948 mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2949 }
2950 }
2951}
2952
2953/* inherits documentation from base class */
2955{
2956 return rowCount()*columnCount();
2957}
2958
2959/* inherits documentation from base class */
2961{
2962 if (index >= 0 && index < elementCount())
2963 return mElements.at(index / columnCount()).at(index % columnCount());
2964 else
2965 return 0;
2966}
2967
2968/* inherits documentation from base class */
2970{
2971 if (QCPLayoutElement *el = elementAt(index))
2972 {
2973 releaseElement(el);
2974 mElements[index / columnCount()][index % columnCount()] = 0;
2975 return el;
2976 } else
2977 {
2978 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2979 return 0;
2980 }
2981}
2982
2983/* inherits documentation from base class */
2985{
2986 if (element)
2987 {
2988 for (int i=0; i<elementCount(); ++i)
2989 {
2990 if (elementAt(i) == element)
2991 {
2992 takeAt(i);
2993 return true;
2994 }
2995 }
2996 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2997 } else
2998 qDebug() << Q_FUNC_INFO << "Can't take null element";
2999 return false;
3000}
3001
3002/* inherits documentation from base class */
3003QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
3004{
3005 QList<QCPLayoutElement*> result;
3006 int colC = columnCount();
3007 int rowC = rowCount();
3008#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3009 result.reserve(colC*rowC);
3010#endif
3011 for (int row=0; row<rowC; ++row)
3012 {
3013 for (int col=0; col<colC; ++col)
3014 {
3015 result.append(mElements.at(row).at(col));
3016 }
3017 }
3018 if (recursive)
3019 {
3020 int c = result.size();
3021 for (int i=0; i<c; ++i)
3022 {
3023 if (result.at(i))
3024 result << result.at(i)->elements(recursive);
3025 }
3026 }
3027 return result;
3028}
3029
3034{
3035 // remove rows with only empty cells:
3036 for (int row=rowCount()-1; row>=0; --row)
3037 {
3038 bool hasElements = false;
3039 for (int col=0; col<columnCount(); ++col)
3040 {
3041 if (mElements.at(row).at(col))
3042 {
3043 hasElements = true;
3044 break;
3045 }
3046 }
3047 if (!hasElements)
3048 {
3049 mRowStretchFactors.removeAt(row);
3050 mElements.removeAt(row);
3051 if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
3052 mColumnStretchFactors.clear();
3053 }
3054 }
3055
3056 // remove columns with only empty cells:
3057 for (int col=columnCount()-1; col>=0; --col)
3058 {
3059 bool hasElements = false;
3060 for (int row=0; row<rowCount(); ++row)
3061 {
3062 if (mElements.at(row).at(col))
3063 {
3064 hasElements = true;
3065 break;
3066 }
3067 }
3068 if (!hasElements)
3069 {
3070 mColumnStretchFactors.removeAt(col);
3071 for (int row=0; row<rowCount(); ++row)
3072 mElements[row].removeAt(col);
3073 }
3074 }
3075}
3076
3077/* inherits documentation from base class */
3079{
3080 QVector<int> minColWidths, minRowHeights;
3081 getMinimumRowColSizes(&minColWidths, &minRowHeights);
3082 QSize result(0, 0);
3083 for (int i=0; i<minColWidths.size(); ++i)
3084 result.rwidth() += minColWidths.at(i);
3085 for (int i=0; i<minRowHeights.size(); ++i)
3086 result.rheight() += minRowHeights.at(i);
3087 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
3088 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
3089 return result;
3090}
3091
3092/* inherits documentation from base class */
3094{
3095 QVector<int> maxColWidths, maxRowHeights;
3096 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
3097
3098 QSize result(0, 0);
3099 for (int i=0; i<maxColWidths.size(); ++i)
3100 result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
3101 for (int i=0; i<maxRowHeights.size(); ++i)
3102 result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
3103 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
3104 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
3105 return result;
3106}
3107
3120void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
3121{
3122 *minColWidths = QVector<int>(columnCount(), 0);
3123 *minRowHeights = QVector<int>(rowCount(), 0);
3124 for (int row=0; row<rowCount(); ++row)
3125 {
3126 for (int col=0; col<columnCount(); ++col)
3127 {
3128 if (mElements.at(row).at(col))
3129 {
3130 QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
3131 QSize min = mElements.at(row).at(col)->minimumSize();
3132 QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3133 if (minColWidths->at(col) < final.width())
3134 (*minColWidths)[col] = final.width();
3135 if (minRowHeights->at(row) < final.height())
3136 (*minRowHeights)[row] = final.height();
3137 }
3138 }
3139 }
3140}
3141
3154void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3155{
3156 *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3157 *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3158 for (int row=0; row<rowCount(); ++row)
3159 {
3160 for (int col=0; col<columnCount(); ++col)
3161 {
3162 if (mElements.at(row).at(col))
3163 {
3164 QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3165 QSize max = mElements.at(row).at(col)->maximumSize();
3166 QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3167 if (maxColWidths->at(col) > final.width())
3168 (*maxColWidths)[col] = final.width();
3169 if (maxRowHeights->at(row) > final.height())
3170 (*maxRowHeights)[row] = final.height();
3171 }
3172 }
3173 }
3174}
3175
3176
3180
3198/* start documentation of inline functions */
3199
3206/* end documentation of inline functions */
3207
3214
3216{
3217 // clear all child layout elements. This is important because only the specific layouts know how
3218 // to handle removing elements (clear calls virtual removeAt method to do that).
3219 clear();
3220}
3221
3226{
3227 if (elementAt(index))
3228 return mInsetPlacement.at(index);
3229 else
3230 {
3231 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3232 return ipFree;
3233 }
3234}
3235
3240Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3241{
3242 if (elementAt(index))
3243 return mInsetAlignment.at(index);
3244 else
3245 {
3246 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3247 return 0;
3248 }
3249}
3250
3255QRectF QCPLayoutInset::insetRect(int index) const
3256{
3257 if (elementAt(index))
3258 return mInsetRect.at(index);
3259 else
3260 {
3261 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3262 return QRectF();
3263 }
3264}
3265
3272{
3273 if (elementAt(index))
3274 mInsetPlacement[index] = placement;
3275 else
3276 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3277}
3278
3287void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3288{
3289 if (elementAt(index))
3290 mInsetAlignment[index] = alignment;
3291 else
3292 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3293}
3294
3306void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3307{
3308 if (elementAt(index))
3309 mInsetRect[index] = rect;
3310 else
3311 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3312}
3313
3314/* inherits documentation from base class */
3316{
3317 for (int i=0; i<mElements.size(); ++i)
3318 {
3319 QRect insetRect;
3320 QSize finalMinSize, finalMaxSize;
3321 QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3322 QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3323 finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3324 finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3325 finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3326 finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3327 if (mInsetPlacement.at(i) == ipFree)
3328 {
3329 insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3330 rect().y()+rect().height()*mInsetRect.at(i).y(),
3331 rect().width()*mInsetRect.at(i).width(),
3332 rect().height()*mInsetRect.at(i).height());
3333 if (insetRect.size().width() < finalMinSize.width())
3334 insetRect.setWidth(finalMinSize.width());
3335 if (insetRect.size().height() < finalMinSize.height())
3336 insetRect.setHeight(finalMinSize.height());
3337 if (insetRect.size().width() > finalMaxSize.width())
3338 insetRect.setWidth(finalMaxSize.width());
3339 if (insetRect.size().height() > finalMaxSize.height())
3340 insetRect.setHeight(finalMaxSize.height());
3341 } else if (mInsetPlacement.at(i) == ipBorderAligned)
3342 {
3343 insetRect.setSize(finalMinSize);
3344 Qt::Alignment al = mInsetAlignment.at(i);
3345 if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3346 else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3347 else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3348 if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3349 else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3350 else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3351 }
3352 mElements.at(i)->setOuterRect(insetRect);
3353 }
3354}
3355
3356/* inherits documentation from base class */
3358{
3359 return mElements.size();
3360}
3361
3362/* inherits documentation from base class */
3364{
3365 if (index >= 0 && index < mElements.size())
3366 return mElements.at(index);
3367 else
3368 return 0;
3369}
3370
3371/* inherits documentation from base class */
3373{
3374 if (QCPLayoutElement *el = elementAt(index))
3375 {
3376 releaseElement(el);
3377 mElements.removeAt(index);
3378 mInsetPlacement.removeAt(index);
3379 mInsetAlignment.removeAt(index);
3380 mInsetRect.removeAt(index);
3381 return el;
3382 } else
3383 {
3384 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3385 return 0;
3386 }
3387}
3388
3389/* inherits documentation from base class */
3391{
3392 if (element)
3393 {
3394 for (int i=0; i<elementCount(); ++i)
3395 {
3396 if (elementAt(i) == element)
3397 {
3398 takeAt(i);
3399 return true;
3400 }
3401 }
3402 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3403 } else
3404 qDebug() << Q_FUNC_INFO << "Can't take null element";
3405 return false;
3406}
3407
3417double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3418{
3419 Q_UNUSED(details)
3420 if (onlySelectable)
3421 return -1;
3422
3423 for (int i=0; i<mElements.size(); ++i)
3424 {
3425 // inset layout shall only return positive selectTest, if actually an inset object is at pos
3426 // else it would block the entire underlying QCPAxisRect with its surface.
3427 if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3428 return mParentPlot->selectionTolerance()*0.99;
3429 }
3430 return -1;
3431}
3432
3444void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3445{
3446 if (element)
3447 {
3448 if (element->layout()) // remove from old layout first
3449 element->layout()->take(element);
3450 mElements.append(element);
3452 mInsetAlignment.append(alignment);
3453 mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3454 adoptElement(element);
3455 } else
3456 qDebug() << Q_FUNC_INFO << "Can't add null element";
3457}
3458
3470void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3471{
3472 if (element)
3473 {
3474 if (element->layout()) // remove from old layout first
3475 element->layout()->take(element);
3476 mElements.append(element);
3477 mInsetPlacement.append(ipFree);
3478 mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3479 mInsetRect.append(rect);
3480 adoptElement(element);
3481 } else
3482 qDebug() << Q_FUNC_INFO << "Can't add null element";
3483}
3484
3485
3489
3514 mStyle(esNone),
3515 mWidth(8),
3516 mLength(10),
3517 mInverted(false)
3518{
3519}
3520
3524QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3525 mStyle(style),
3526 mWidth(width),
3527 mLength(length),
3528 mInverted(inverted)
3529{
3530}
3531
3539
3546void QCPLineEnding::setWidth(double width)
3547{
3548 mWidth = width;
3549}
3550
3557void QCPLineEnding::setLength(double length)
3558{
3559 mLength = length;
3560}
3561
3571{
3573}
3574
3585{
3586 switch (mStyle)
3587 {
3588 case esNone:
3589 return 0;
3590
3591 case esFlatArrow:
3592 case esSpikeArrow:
3593 case esLineArrow:
3594 case esSkewedBar:
3595 return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3596
3597 case esDisc:
3598 case esSquare:
3599 case esDiamond:
3600 case esBar:
3601 case esHalfBar:
3602 return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3603
3604 }
3605 return 0;
3606}
3607
3620{
3621 switch (mStyle)
3622 {
3623 case esNone:
3624 case esLineArrow:
3625 case esSkewedBar:
3626 case esBar:
3627 case esHalfBar:
3628 return 0;
3629
3630 case esFlatArrow:
3631 return mLength;
3632
3633 case esDisc:
3634 case esSquare:
3635 case esDiamond:
3636 return mWidth*0.5;
3637
3638 case esSpikeArrow:
3639 return mLength*0.8;
3640 }
3641 return 0;
3642}
3643
3649void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3650{
3651 if (mStyle == esNone)
3652 return;
3653
3654 QVector2D lengthVec(dir.normalized());
3655 if (lengthVec.isNull())
3656 lengthVec = QVector2D(1, 0);
3657 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3658 lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3659 widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3660
3661 QPen penBackup = painter->pen();
3662 QBrush brushBackup = painter->brush();
3663 QPen miterPen = penBackup;
3664 miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3665 QBrush brush(painter->pen().color(), Qt::SolidPattern);
3666 switch (mStyle)
3667 {
3668 case esNone: break;
3669 case esFlatArrow:
3670 {
3671 QPointF points[3] = {pos.toPointF(),
3672 (pos-lengthVec+widthVec).toPointF(),
3673 (pos-lengthVec-widthVec).toPointF()
3674 };
3675 painter->setPen(miterPen);
3676 painter->setBrush(brush);
3677 painter->drawConvexPolygon(points, 3);
3678 painter->setBrush(brushBackup);
3679 painter->setPen(penBackup);
3680 break;
3681 }
3682 case esSpikeArrow:
3683 {
3684 QPointF points[4] = {pos.toPointF(),
3685 (pos-lengthVec+widthVec).toPointF(),
3686 (pos-lengthVec*0.8f).toPointF(),
3687 (pos-lengthVec-widthVec).toPointF()
3688 };
3689 painter->setPen(miterPen);
3690 painter->setBrush(brush);
3691 painter->drawConvexPolygon(points, 4);
3692 painter->setBrush(brushBackup);
3693 painter->setPen(penBackup);
3694 break;
3695 }
3696 case esLineArrow:
3697 {
3698 QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3699 pos.toPointF(),
3700 (pos-lengthVec-widthVec).toPointF()
3701 };
3702 painter->setPen(miterPen);
3703 painter->drawPolyline(points, 3);
3704 painter->setPen(penBackup);
3705 break;
3706 }
3707 case esDisc:
3708 {
3709 painter->setBrush(brush);
3710 painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3711 painter->setBrush(brushBackup);
3712 break;
3713 }
3714 case esSquare:
3715 {
3716 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3717 QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3718 (pos-widthVecPerp-widthVec).toPointF(),
3719 (pos+widthVecPerp-widthVec).toPointF(),
3720 (pos+widthVecPerp+widthVec).toPointF()
3721 };
3722 painter->setPen(miterPen);
3723 painter->setBrush(brush);
3724 painter->drawConvexPolygon(points, 4);
3725 painter->setBrush(brushBackup);
3726 painter->setPen(penBackup);
3727 break;
3728 }
3729 case esDiamond:
3730 {
3731 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3732 QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3733 (pos-widthVec).toPointF(),
3734 (pos+widthVecPerp).toPointF(),
3735 (pos+widthVec).toPointF()
3736 };
3737 painter->setPen(miterPen);
3738 painter->setBrush(brush);
3739 painter->drawConvexPolygon(points, 4);
3740 painter->setBrush(brushBackup);
3741 painter->setPen(penBackup);
3742 break;
3743 }
3744 case esBar:
3745 {
3746 painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3747 break;
3748 }
3749 case esHalfBar:
3750 {
3751 painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3752 break;
3753 }
3754 case esSkewedBar:
3755 {
3756 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3757 {
3758 // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3759 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3760 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3761 } else
3762 {
3763 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3764 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3765 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3766 }
3767 break;
3768 }
3769 }
3770}
3771
3777void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3778{
3779 draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3780}
3781
3782
3786
3806 QCPLayerable(parentAxis->parentPlot(), "", parentAxis),
3807 mParentAxis(parentAxis)
3808{
3809 // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3810 setParent(parentAxis);
3811 setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3812 setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3813 setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3814 setSubGridVisible(false);
3815 setAntialiased(false);
3816 setAntialiasedSubGrid(false);
3818}
3819
3826{
3828}
3829
3834{
3835 mAntialiasedSubGrid = enabled;
3836}
3837
3842{
3843 mAntialiasedZeroLine = enabled;
3844}
3845
3849void QCPGrid::setPen(const QPen &pen)
3850{
3851 mPen = pen;
3852}
3853
3857void QCPGrid::setSubGridPen(const QPen &pen)
3858{
3859 mSubGridPen = pen;
3860}
3861
3868void QCPGrid::setZeroLinePen(const QPen &pen)
3869{
3870 mZeroLinePen = pen;
3871}
3872
3890
3897{
3898 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3899
3900 if (mSubGridVisible)
3901 drawSubGridLines(painter);
3902 drawGridLines(painter);
3903}
3904
3912{
3913 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3914
3915 int lowTick = mParentAxis->mLowestVisibleTick;
3916 int highTick = mParentAxis->mHighestVisibleTick;
3917 double t; // helper variable, result of coordinate-to-pixel transforms
3918 if (mParentAxis->orientation() == Qt::Horizontal)
3919 {
3920 // draw zeroline:
3921 int zeroLineIndex = -1;
3922 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3923 {
3925 painter->setPen(mZeroLinePen);
3926 double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3927 for (int i=lowTick; i <= highTick; ++i)
3928 {
3929 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3930 {
3931 zeroLineIndex = i;
3933 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3934 break;
3935 }
3936 }
3937 }
3938 // draw grid lines:
3940 painter->setPen(mPen);
3941 for (int i=lowTick; i <= highTick; ++i)
3942 {
3943 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3945 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3946 }
3947 } else
3948 {
3949 // draw zeroline:
3950 int zeroLineIndex = -1;
3951 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3952 {
3954 painter->setPen(mZeroLinePen);
3955 double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3956 for (int i=lowTick; i <= highTick; ++i)
3957 {
3958 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3959 {
3960 zeroLineIndex = i;
3962 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3963 break;
3964 }
3965 }
3966 }
3967 // draw grid lines:
3969 painter->setPen(mPen);
3970 for (int i=lowTick; i <= highTick; ++i)
3971 {
3972 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3974 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3975 }
3976 }
3977}
3978
3986{
3987 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3988
3990 double t; // helper variable, result of coordinate-to-pixel transforms
3991 painter->setPen(mSubGridPen);
3992 if (mParentAxis->orientation() == Qt::Horizontal)
3993 {
3994 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3995 {
3997 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3998 }
3999 } else
4000 {
4001 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
4002 {
4004 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
4005 }
4006 }
4007}
4008
4009
4013
4032/* start of documentation of inline functions */
4033
4055/* end of documentation of inline functions */
4056/* start of documentation of signals */
4057
4103/* end of documentation of signals */
4104
4110 QCPLayerable(parent->parentPlot(), "", parent),
4111 // axis base:
4112 mAxisType(type),
4113 mAxisRect(parent),
4114 mPadding(5),
4115 mOrientation(orientation(type)),
4116 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4117 mSelectedParts(spNone),
4118 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4119 mSelectedBasePen(QPen(Qt::blue, 2)),
4120 // axis label:
4121 mLabel(""),
4122 mLabelFont(mParentPlot->font()),
4123 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4124 mLabelColor(Qt::black),
4125 mSelectedLabelColor(Qt::blue),
4126 // tick labels:
4127 mTickLabels(true),
4128 mAutoTickLabels(true),
4129 mTickLabelType(ltNumber),
4130 mTickLabelFont(mParentPlot->font()),
4131 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4132 mTickLabelColor(Qt::black),
4133 mSelectedTickLabelColor(Qt::blue),
4134 mDateTimeFormat("hh:mm:ss\ndd.MM.yy"),
4135 mDateTimeSpec(Qt::LocalTime),
4136 mNumberPrecision(6),
4137 mNumberFormatChar('g'),
4138 mNumberBeautifulPowers(true),
4139 // ticks and subticks:
4140 mTicks(true),
4141 mTickStep(1),
4142 mSubTickCount(4),
4143 mAutoTickCount(6),
4144 mAutoTicks(true),
4145 mAutoTickStep(true),
4146 mAutoSubTicks(true),
4147 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4148 mSelectedTickPen(QPen(Qt::blue, 2)),
4149 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4150 mSelectedSubTickPen(QPen(Qt::blue, 2)),
4151 // scale and range:
4152 mRange(0, 5),
4153 mRangeReversed(false),
4154 mScaleType(stLinear),
4155 mScaleLogBase(10),
4156 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4157 // internal members:
4158 mGrid(new QCPGrid(this)),
4159 mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4160 mLowestVisibleTick(0),
4161 mHighestVisibleTick(-1),
4162 mCachedMarginValid(false),
4163 mCachedMargin(0)
4164{
4165 mGrid->setVisible(false);
4166 setAntialiased(false);
4167 setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
4168
4169 if (type == atTop)
4170 {
4172 setLabelPadding(6);
4173 } else if (type == atRight)
4174 {
4176 setLabelPadding(12);
4177 } else if (type == atBottom)
4178 {
4180 setLabelPadding(3);
4181 } else if (type == atLeft)
4182 {
4184 setLabelPadding(10);
4185 }
4186}
4187
4189{
4190 delete mAxisPainter;
4191}
4192
4193/* No documentation as it is a property getter */
4195{
4197}
4198
4199/* No documentation as it is a property getter */
4201{
4203}
4204
4205/* No documentation as it is a property getter */
4207{
4208 QString result;
4209 result.append(mNumberFormatChar);
4211 {
4212 result.append("b");
4214 result.append("c");
4215 }
4216 return result;
4217}
4218
4219/* No documentation as it is a property getter */
4221{
4222 return mAxisPainter->tickLengthIn;
4223}
4224
4225/* No documentation as it is a property getter */
4227{
4229}
4230
4231/* No documentation as it is a property getter */
4233{
4235}
4236
4237/* No documentation as it is a property getter */
4239{
4241}
4242
4243/* No documentation as it is a property getter */
4245{
4246 return mAxisPainter->labelPadding;
4247}
4248
4249/* No documentation as it is a property getter */
4251{
4252 return mAxisPainter->offset;
4253}
4254
4255/* No documentation as it is a property getter */
4260
4261/* No documentation as it is a property getter */
4266
4281{
4282 if (mScaleType != type)
4283 {
4284 mScaleType = type;
4287 mCachedMarginValid = false;
4289 }
4290}
4291
4300{
4301 if (base > 1)
4302 {
4303 mScaleLogBase = base;
4304 mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4305 mCachedMarginValid = false;
4306 } else
4307 qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4308}
4309
4318void QCPAxis::setRange(const QCPRange &range)
4319{
4321 return;
4322
4323 if (!QCPRange::validRange(range)) return;
4324 QCPRange oldRange = mRange;
4326 {
4328 } else
4329 {
4331 }
4332 mCachedMarginValid = false;
4333 emit rangeChanged(mRange);
4334 emit rangeChanged(mRange, oldRange);
4335}
4336
4347void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4348{
4349 if (mSelectableParts != selectable)
4350 {
4351 mSelectableParts = selectable;
4353 }
4354}
4355
4371void QCPAxis::setSelectedParts(const SelectableParts &selected)
4372{
4373 if (mSelectedParts != selected)
4374 {
4375 mSelectedParts = selected;
4377 }
4378}
4379
4389void QCPAxis::setRange(double lower, double upper)
4390{
4391 if (lower == mRange.lower && upper == mRange.upper)
4392 return;
4393
4394 if (!QCPRange::validRange(lower, upper)) return;
4395 QCPRange oldRange = mRange;
4396 mRange.lower = lower;
4397 mRange.upper = upper;
4399 {
4401 } else
4402 {
4404 }
4405 mCachedMarginValid = false;
4406 emit rangeChanged(mRange);
4407 emit rangeChanged(mRange, oldRange);
4408}
4409
4421void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4422{
4423 if (alignment == Qt::AlignLeft)
4424 setRange(position, position+size);
4425 else if (alignment == Qt::AlignRight)
4426 setRange(position-size, position);
4427 else // alignment == Qt::AlignCenter
4428 setRange(position-size/2.0, position+size/2.0);
4429}
4430
4435void QCPAxis::setRangeLower(double lower)
4436{
4437 if (mRange.lower == lower)
4438 return;
4439
4440 QCPRange oldRange = mRange;
4441 mRange.lower = lower;
4443 {
4445 } else
4446 {
4448 }
4449 mCachedMarginValid = false;
4450 emit rangeChanged(mRange);
4451 emit rangeChanged(mRange, oldRange);
4452}
4453
4458void QCPAxis::setRangeUpper(double upper)
4459{
4460 if (mRange.upper == upper)
4461 return;
4462
4463 QCPRange oldRange = mRange;
4464 mRange.upper = upper;
4466 {
4468 } else
4469 {
4471 }
4472 mCachedMarginValid = false;
4473 emit rangeChanged(mRange);
4474 emit rangeChanged(mRange, oldRange);
4475}
4476
4486void QCPAxis::setRangeReversed(bool reversed)
4487{
4488 if (mRangeReversed != reversed)
4489 {
4490 mRangeReversed = reversed;
4491 mCachedMarginValid = false;
4492 }
4493}
4494
4511{
4512 if (mAutoTicks != on)
4513 {
4514 mAutoTicks = on;
4515 mCachedMarginValid = false;
4516 }
4517}
4518
4530void QCPAxis::setAutoTickCount(int approximateCount)
4531{
4532 if (mAutoTickCount != approximateCount)
4533 {
4534 if (approximateCount > 0)
4535 {
4536 mAutoTickCount = approximateCount;
4537 mCachedMarginValid = false;
4538 } else
4539 qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4540 }
4541}
4542
4559{
4560 if (mAutoTickLabels != on)
4561 {
4563 mCachedMarginValid = false;
4564 }
4565}
4566
4580{
4581 if (mAutoTickStep != on)
4582 {
4583 mAutoTickStep = on;
4584 mCachedMarginValid = false;
4585 }
4586}
4587
4598{
4599 if (mAutoSubTicks != on)
4600 {
4601 mAutoSubTicks = on;
4602 mCachedMarginValid = false;
4603 }
4604}
4605
4612void QCPAxis::setTicks(bool show)
4613{
4614 if (mTicks != show)
4615 {
4616 mTicks = show;
4617 mCachedMarginValid = false;
4618 }
4619}
4620
4625{
4626 if (mTickLabels != show)
4627 {
4628 mTickLabels = show;
4629 mCachedMarginValid = false;
4630 }
4631}
4632
4638{
4640 {
4642 mCachedMarginValid = false;
4643 }
4644}
4645
4668{
4669 if (mTickLabelType != type)
4670 {
4671 mTickLabelType = type;
4672 mCachedMarginValid = false;
4673 }
4674}
4675
4681void QCPAxis::setTickLabelFont(const QFont &font)
4682{
4683 if (font != mTickLabelFont)
4684 {
4685 mTickLabelFont = font;
4686 mCachedMarginValid = false;
4687 }
4688}
4689
4695void QCPAxis::setTickLabelColor(const QColor &color)
4696{
4697 if (color != mTickLabelColor)
4698 {
4699 mTickLabelColor = color;
4700 mCachedMarginValid = false;
4701 }
4702}
4703
4714{
4715 if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4716 {
4717 mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4718 mCachedMarginValid = false;
4719 }
4720}
4721
4730void QCPAxis::setDateTimeFormat(const QString &format)
4731{
4732 if (mDateTimeFormat != format)
4733 {
4734 mDateTimeFormat = format;
4735 mCachedMarginValid = false;
4736 }
4737}
4738
4749void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4750{
4751 mDateTimeSpec = timeSpec;
4752}
4753
4790void QCPAxis::setNumberFormat(const QString &formatCode)
4791{
4792 if (formatCode.isEmpty())
4793 {
4794 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4795 return;
4796 }
4797 mCachedMarginValid = false;
4798
4799 // interpret first char as number format char:
4800 QString allowedFormatChars = "eEfgG";
4801 if (allowedFormatChars.contains(formatCode.at(0)))
4802 {
4803 mNumberFormatChar = formatCode.at(0).toLatin1();
4804 } else
4805 {
4806 qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4807 return;
4808 }
4809 if (formatCode.length() < 2)
4810 {
4811 mNumberBeautifulPowers = false;
4813 return;
4814 }
4815
4816 // interpret second char as indicator for beautiful decimal powers:
4817 if (formatCode.at(1) == 'b' && (mNumberFormatChar == 'e' || mNumberFormatChar == 'g'))
4818 {
4820 } else
4821 {
4822 qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4823 return;
4824 }
4825 if (formatCode.length() < 3)
4826 {
4828 return;
4829 }
4830
4831 // interpret third char as indicator for dot or cross multiplication symbol:
4832 if (formatCode.at(2) == 'c')
4833 {
4835 } else if (formatCode.at(2) == 'd')
4836 {
4838 } else
4839 {
4840 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4841 return;
4842 }
4843}
4844
4857{
4858 if (mNumberPrecision != precision)
4859 {
4860 mNumberPrecision = precision;
4861 mCachedMarginValid = false;
4862 }
4863}
4864
4870void QCPAxis::setTickStep(double step)
4871{
4872 if (mTickStep != step)
4873 {
4874 mTickStep = step;
4875 mCachedMarginValid = false;
4876 }
4877}
4878
4892void QCPAxis::setTickVector(const QVector<double> &vec)
4893{
4894 // don't check whether mTickVector != vec here, because it takes longer than we would save
4895 mTickVector = vec;
4896 mCachedMarginValid = false;
4897}
4898
4910void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4911{
4912 // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4913 mTickVectorLabels = vec;
4914 mCachedMarginValid = false;
4915}
4916
4925void QCPAxis::setTickLength(int inside, int outside)
4926{
4927 setTickLengthIn(inside);
4928 setTickLengthOut(outside);
4929}
4930
4938{
4939 if (mAxisPainter->tickLengthIn != inside)
4940 {
4941 mAxisPainter->tickLengthIn = inside;
4942 }
4943}
4944
4953{
4954 if (mAxisPainter->tickLengthOut != outside)
4955 {
4956 mAxisPainter->tickLengthOut = outside;
4957 mCachedMarginValid = false; // only outside tick length can change margin
4958 }
4959}
4960
4973{
4974 mSubTickCount = count;
4975}
4976
4985void QCPAxis::setSubTickLength(int inside, int outside)
4986{
4987 setSubTickLengthIn(inside);
4988 setSubTickLengthOut(outside);
4989}
4990
4998{
4999 if (mAxisPainter->subTickLengthIn != inside)
5000 {
5001 mAxisPainter->subTickLengthIn = inside;
5002 }
5003}
5004
5013{
5014 if (mAxisPainter->subTickLengthOut != outside)
5015 {
5016 mAxisPainter->subTickLengthOut = outside;
5017 mCachedMarginValid = false; // only outside tick length can change margin
5018 }
5019}
5020
5026void QCPAxis::setBasePen(const QPen &pen)
5027{
5028 mBasePen = pen;
5029}
5030
5036void QCPAxis::setTickPen(const QPen &pen)
5037{
5038 mTickPen = pen;
5039}
5040
5046void QCPAxis::setSubTickPen(const QPen &pen)
5047{
5048 mSubTickPen = pen;
5049}
5050
5056void QCPAxis::setLabelFont(const QFont &font)
5057{
5058 if (mLabelFont != font)
5059 {
5060 mLabelFont = font;
5061 mCachedMarginValid = false;
5062 }
5063}
5064
5070void QCPAxis::setLabelColor(const QColor &color)
5071{
5072 mLabelColor = color;
5073}
5074
5079void QCPAxis::setLabel(const QString &str)
5080{
5081 if (mLabel != str)
5082 {
5083 mLabel = str;
5084 mCachedMarginValid = false;
5085 }
5086}
5087
5094{
5096 {
5098 mCachedMarginValid = false;
5099 }
5100}
5101
5112void QCPAxis::setPadding(int padding)
5113{
5114 if (mPadding != padding)
5115 {
5116 mPadding = padding;
5117 mCachedMarginValid = false;
5118 }
5119}
5120
5130{
5132}
5133
5140{
5141 if (font != mSelectedTickLabelFont)
5142 {
5144 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5145 }
5146}
5147
5153void QCPAxis::setSelectedLabelFont(const QFont &font)
5154{
5155 mSelectedLabelFont = font;
5156 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5157}
5158
5164void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5165{
5166 if (color != mSelectedTickLabelColor)
5167 {
5169 }
5170}
5171
5177void QCPAxis::setSelectedLabelColor(const QColor &color)
5178{
5179 mSelectedLabelColor = color;
5180}
5181
5187void QCPAxis::setSelectedBasePen(const QPen &pen)
5188{
5189 mSelectedBasePen = pen;
5190}
5191
5197void QCPAxis::setSelectedTickPen(const QPen &pen)
5198{
5199 mSelectedTickPen = pen;
5200}
5201
5208{
5209 mSelectedSubTickPen = pen;
5210}
5211
5223{
5224 mAxisPainter->lowerEnding = ending;
5225}
5226
5238{
5239 mAxisPainter->upperEnding = ending;
5240}
5241
5249void QCPAxis::moveRange(double diff)
5250{
5251 QCPRange oldRange = mRange;
5252 if (mScaleType == stLinear)
5253 {
5254 mRange.lower += diff;
5255 mRange.upper += diff;
5256 } else // mScaleType == stLogarithmic
5257 {
5258 mRange.lower *= diff;
5259 mRange.upper *= diff;
5260 }
5261 mCachedMarginValid = false;
5262 emit rangeChanged(mRange);
5263 emit rangeChanged(mRange, oldRange);
5264}
5265
5272void QCPAxis::scaleRange(double factor, double center)
5273{
5274 QCPRange oldRange = mRange;
5275 if (mScaleType == stLinear)
5276 {
5277 QCPRange newRange;
5278 newRange.lower = (mRange.lower-center)*factor + center;
5279 newRange.upper = (mRange.upper-center)*factor + center;
5280 if (QCPRange::validRange(newRange))
5281 mRange = newRange.sanitizedForLinScale();
5282 } else // mScaleType == stLogarithmic
5283 {
5284 if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5285 {
5286 QCPRange newRange;
5287 newRange.lower = pow(mRange.lower/center, factor)*center;
5288 newRange.upper = pow(mRange.upper/center, factor)*center;
5289 if (QCPRange::validRange(newRange))
5290 mRange = newRange.sanitizedForLogScale();
5291 } else
5292 qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5293 }
5294 mCachedMarginValid = false;
5295 emit rangeChanged(mRange);
5296 emit rangeChanged(mRange, oldRange);
5297}
5298
5312void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5313{
5314 int otherPixelSize, ownPixelSize;
5315
5316 if (otherAxis->orientation() == Qt::Horizontal)
5317 otherPixelSize = otherAxis->axisRect()->width();
5318 else
5319 otherPixelSize = otherAxis->axisRect()->height();
5320
5321 if (orientation() == Qt::Horizontal)
5322 ownPixelSize = axisRect()->width();
5323 else
5324 ownPixelSize = axisRect()->height();
5325
5326 double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5327 setRange(range().center(), newRangeSize, Qt::AlignCenter);
5328}
5329
5336void QCPAxis::rescale(bool onlyVisiblePlottables)
5337{
5338 QList<QCPAbstractPlottable*> p = plottables();
5339 QCPRange newRange;
5340 bool haveRange = false;
5341 for (int i=0; i<p.size(); ++i)
5342 {
5343 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5344 continue;
5345 QCPRange plottableRange;
5346 bool currentFoundRange;
5350 if (p.at(i)->keyAxis() == this)
5351 plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5352 else
5353 plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5354 if (currentFoundRange)
5355 {
5356 if (!haveRange)
5357 newRange = plottableRange;
5358 else
5359 newRange.expand(plottableRange);
5360 haveRange = true;
5361 }
5362 }
5363 if (haveRange)
5364 {
5365 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
5366 {
5367 double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
5368 if (mScaleType == stLinear)
5369 {
5370 newRange.lower = center-mRange.size()/2.0;
5371 newRange.upper = center+mRange.size()/2.0;
5372 } else // mScaleType == stLogarithmic
5373 {
5374 newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5375 newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5376 }
5377 }
5378 setRange(newRange);
5379 }
5380}
5381
5385double QCPAxis::pixelToCoord(double value) const
5386{
5387 if (orientation() == Qt::Horizontal)
5388 {
5389 if (mScaleType == stLinear)
5390 {
5391 if (!mRangeReversed)
5392 return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5393 else
5394 return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5395 } else // mScaleType == stLogarithmic
5396 {
5397 if (!mRangeReversed)
5398 return pow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5399 else
5400 return pow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5401 }
5402 } else // orientation() == Qt::Vertical
5403 {
5404 if (mScaleType == stLinear)
5405 {
5406 if (!mRangeReversed)
5407 return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5408 else
5409 return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5410 } else // mScaleType == stLogarithmic
5411 {
5412 if (!mRangeReversed)
5413 return pow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5414 else
5415 return pow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5416 }
5417 }
5418}
5419
5423double QCPAxis::coordToPixel(double value) const
5424{
5425 if (orientation() == Qt::Horizontal)
5426 {
5427 if (mScaleType == stLinear)
5428 {
5429 if (!mRangeReversed)
5430 return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5431 else
5432 return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5433 } else // mScaleType == stLogarithmic
5434 {
5435 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5436 return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5437 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5438 return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5439 else
5440 {
5441 if (!mRangeReversed)
5443 else
5445 }
5446 }
5447 } else // orientation() == Qt::Vertical
5448 {
5449 if (mScaleType == stLinear)
5450 {
5451 if (!mRangeReversed)
5452 return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5453 else
5454 return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5455 } else // mScaleType == stLogarithmic
5456 {
5457 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5458 return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5459 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5460 return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5461 else
5462 {
5463 if (!mRangeReversed)
5465 else
5467 }
5468 }
5469 }
5470}
5471
5482{
5483 if (!mVisible)
5484 return spNone;
5485
5486 if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5487 return spAxis;
5488 else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5489 return spTickLabels;
5490 else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5491 return spAxisLabel;
5492 else
5493 return spNone;
5494}
5495
5496/* inherits documentation from base class */
5497double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5498{
5499 if (!mParentPlot) return -1;
5500 SelectablePart part = getPartAt(pos);
5501 if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5502 return -1;
5503
5504 if (details)
5505 details->setValue(part);
5506 return mParentPlot->selectionTolerance()*0.99;
5507}
5508
5516QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5517{
5518 QList<QCPAbstractPlottable*> result;
5519 if (!mParentPlot) return result;
5520
5521 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5522 {
5523 if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5524 result.append(mParentPlot->mPlottables.at(i));
5525 }
5526 return result;
5527}
5528
5534QList<QCPGraph*> QCPAxis::graphs() const
5535{
5536 QList<QCPGraph*> result;
5537 if (!mParentPlot) return result;
5538
5539 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5540 {
5541 if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5542 result.append(mParentPlot->mGraphs.at(i));
5543 }
5544 return result;
5545}
5546
5553QList<QCPAbstractItem*> QCPAxis::items() const
5554{
5555 QList<QCPAbstractItem*> result;
5556 if (!mParentPlot) return result;
5557
5558 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5559 {
5560 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5561 for (int posId=0; posId<positions.size(); ++posId)
5562 {
5563 if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5564 {
5565 result.append(mParentPlot->mItems.at(itemId));
5566 break;
5567 }
5568 }
5569 }
5570 return result;
5571}
5572
5578{
5579 switch (side)
5580 {
5581 case QCP::msLeft: return atLeft;
5582 case QCP::msRight: return atRight;
5583 case QCP::msTop: return atTop;
5584 case QCP::msBottom: return atBottom;
5585 default: break;
5586 }
5587 qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5588 return atLeft;
5589}
5590
5595{
5596 switch (type)
5597 {
5598 case atLeft: return atRight; break;
5599 case atRight: return atLeft; break;
5600 case atBottom: return atTop; break;
5601 case atTop: return atBottom; break;
5602 default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5603 }
5604}
5605
5614{
5615 if (!mParentPlot) return;
5616 if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5617
5618 // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5619 if (mAutoTicks)
5620 {
5622 } else
5623 {
5624 emit ticksRequest();
5625 }
5626
5628 if (mTickVector.isEmpty())
5629 {
5630 mSubTickVector.clear();
5631 return;
5632 }
5633
5634 // generate subticks between ticks:
5635 mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5636 if (mSubTickCount > 0)
5637 {
5638 double subTickStep = 0;
5639 double subTickPosition = 0;
5640 int subTickIndex = 0;
5641 bool done = false;
5644 for (int i=lowTick+1; i<=highTick; ++i)
5645 {
5646 subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5647 for (int k=1; k<=mSubTickCount; ++k)
5648 {
5649 subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5650 if (subTickPosition < mRange.lower)
5651 continue;
5652 if (subTickPosition > mRange.upper)
5653 {
5654 done = true;
5655 break;
5656 }
5657 mSubTickVector[subTickIndex] = subTickPosition;
5658 subTickIndex++;
5659 }
5660 if (done) break;
5661 }
5662 mSubTickVector.resize(subTickIndex);
5663 }
5664
5665 // generate tick labels according to tick positions:
5666 if (mAutoTickLabels)
5667 {
5668 int vecsize = mTickVector.size();
5669 mTickVectorLabels.resize(vecsize);
5670 if (mTickLabelType == ltNumber)
5671 {
5672 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5674 } else if (mTickLabelType == ltDateTime)
5675 {
5676 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5677 {
5678#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
5679 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5680#else
5681 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5682#endif
5683 }
5684 }
5685 } else // mAutoTickLabels == false
5686 {
5687 if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5688 {
5689 emit ticksRequest();
5690 }
5691 // make sure provided tick label vector has correct (minimal) length:
5692 if (mTickVectorLabels.size() < mTickVector.size())
5693 mTickVectorLabels.resize(mTickVector.size());
5694 }
5695}
5696
5707{
5708 if (mScaleType == stLinear)
5709 {
5710 if (mAutoTickStep)
5711 {
5712 // Generate tick positions according to linear scaling:
5713 mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5714 double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5715 double tickStepMantissa = mTickStep/magnitudeFactor;
5716 if (tickStepMantissa < 5)
5717 {
5718 // round digit after decimal point to 0.5
5719 mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5720 } else
5721 {
5722 // round to first digit in multiples of 2
5723 mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5724 }
5725 }
5726 if (mAutoSubTicks)
5728 // Generate tick positions according to mTickStep:
5729 qint64 firstStep = floor(mRange.lower/mTickStep);
5730 qint64 lastStep = ceil(mRange.upper/mTickStep);
5731 int tickcount = lastStep-firstStep+1;
5732 if (tickcount < 0) tickcount = 0;
5733 mTickVector.resize(tickcount);
5734 for (int i=0; i<tickcount; ++i)
5735 mTickVector[i] = (firstStep+i)*mTickStep;
5736 } else // mScaleType == stLogarithmic
5737 {
5738 // Generate tick positions according to logbase scaling:
5739 if (mRange.lower > 0 && mRange.upper > 0) // positive range
5740 {
5741 double lowerMag = basePow((int)floor(baseLog(mRange.lower)));
5742 double currentMag = lowerMag;
5743 mTickVector.clear();
5744 mTickVector.append(currentMag);
5745 while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5746 {
5747 currentMag *= mScaleLogBase;
5748 mTickVector.append(currentMag);
5749 }
5750 } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5751 {
5752 double lowerMag = -basePow((int)ceil(baseLog(-mRange.lower)));
5753 double currentMag = lowerMag;
5754 mTickVector.clear();
5755 mTickVector.append(currentMag);
5756 while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5757 {
5758 currentMag /= mScaleLogBase;
5759 mTickVector.append(currentMag);
5760 }
5761 } else // invalid range for logarithmic scale, because lower and upper have different sign
5762 {
5763 mTickVector.clear();
5764 qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5765 }
5766 }
5767}
5768
5782int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5783{
5784 int result = mSubTickCount; // default to current setting, if no proper value can be found
5785
5786 // get mantissa of tickstep:
5787 double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5788 double tickStepMantissa = tickStep/magnitudeFactor;
5789
5790 // separate integer and fractional part of mantissa:
5791 double epsilon = 0.01;
5792 double intPartf;
5793 int intPart;
5794 double fracPart = modf(tickStepMantissa, &intPartf);
5795 intPart = intPartf;
5796
5797 // handle cases with (almost) integer mantissa:
5798 if (fracPart < epsilon || 1.0-fracPart < epsilon)
5799 {
5800 if (1.0-fracPart < epsilon)
5801 ++intPart;
5802 switch (intPart)
5803 {
5804 case 1: result = 4; break; // 1.0 -> 0.2 substep
5805 case 2: result = 3; break; // 2.0 -> 0.5 substep
5806 case 3: result = 2; break; // 3.0 -> 1.0 substep
5807 case 4: result = 3; break; // 4.0 -> 1.0 substep
5808 case 5: result = 4; break; // 5.0 -> 1.0 substep
5809 case 6: result = 2; break; // 6.0 -> 2.0 substep
5810 case 7: result = 6; break; // 7.0 -> 1.0 substep
5811 case 8: result = 3; break; // 8.0 -> 2.0 substep
5812 case 9: result = 2; break; // 9.0 -> 3.0 substep
5813 }
5814 } else
5815 {
5816 // handle cases with significantly fractional mantissa:
5817 if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5818 {
5819 switch (intPart)
5820 {
5821 case 1: result = 2; break; // 1.5 -> 0.5 substep
5822 case 2: result = 4; break; // 2.5 -> 0.5 substep
5823 case 3: result = 4; break; // 3.5 -> 0.7 substep
5824 case 4: result = 2; break; // 4.5 -> 1.5 substep
5825 case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5826 case 6: result = 4; break; // 6.5 -> 1.3 substep
5827 case 7: result = 2; break; // 7.5 -> 2.5 substep
5828 case 8: result = 4; break; // 8.5 -> 1.7 substep
5829 case 9: result = 4; break; // 9.5 -> 1.9 substep
5830 }
5831 }
5832 // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5833 }
5834
5835 return result;
5836}
5837
5838/* inherits documentation from base class */
5839void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5840{
5841 Q_UNUSED(event)
5842 SelectablePart part = details.value<SelectablePart>();
5843 if (mSelectableParts.testFlag(part))
5844 {
5845 SelectableParts selBefore = mSelectedParts;
5846 setSelectedParts(additive ? mSelectedParts^part : part);
5847 if (selectionStateChanged)
5848 *selectionStateChanged = mSelectedParts != selBefore;
5849 }
5850}
5851
5852/* inherits documentation from base class */
5853void QCPAxis::deselectEvent(bool *selectionStateChanged)
5854{
5855 SelectableParts selBefore = mSelectedParts;
5857 if (selectionStateChanged)
5858 *selectionStateChanged = mSelectedParts != selBefore;
5859}
5860
5878
5885{
5886 const int lowTick = mLowestVisibleTick;
5887 const int highTick = mHighestVisibleTick;
5888 QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5889 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5890 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5891 tickPositions.reserve(highTick-lowTick+1);
5892 tickLabels.reserve(highTick-lowTick+1);
5893 subTickPositions.reserve(mSubTickVector.size());
5894
5895 if (mTicks)
5896 {
5897 for (int i=lowTick; i<=highTick; ++i)
5898 {
5899 tickPositions.append(coordToPixel(mTickVector.at(i)));
5900 if (mTickLabels)
5901 tickLabels.append(mTickVectorLabels.at(i));
5902 }
5903
5904 if (mSubTickCount > 0)
5905 {
5906 const int subTickCount = mSubTickVector.size();
5907 for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5908 subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5909 }
5910 }
5911 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5912 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5927 mAxisPainter->tickPositions = tickPositions;
5929 mAxisPainter->subTickPositions = subTickPositions;
5930 mAxisPainter->draw(painter);
5931}
5932
5949void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5950{
5951 bool lowFound = false;
5952 bool highFound = false;
5953 lowIndex = 0;
5954 highIndex = -1;
5955
5956 for (int i=0; i < mTickVector.size(); ++i)
5957 {
5958 if (mTickVector.at(i) >= mRange.lower)
5959 {
5960 lowFound = true;
5961 lowIndex = i;
5962 break;
5963 }
5964 }
5965 for (int i=mTickVector.size()-1; i >= 0; --i)
5966 {
5967 if (mTickVector.at(i) <= mRange.upper)
5968 {
5969 highFound = true;
5970 highIndex = i;
5971 break;
5972 }
5973 }
5974
5975 if (!lowFound && highFound)
5976 lowIndex = highIndex+1;
5977 else if (lowFound && !highFound)
5978 highIndex = lowIndex-1;
5979}
5980
5989double QCPAxis::baseLog(double value) const
5990{
5991 return qLn(value)*mScaleLogBaseLogInv;
5992}
5993
6001double QCPAxis::basePow(double value) const
6002{
6003 return qPow(mScaleLogBase, value);
6004}
6005
6012{
6013 return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
6014}
6015
6022{
6023 return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
6024}
6025
6032{
6034}
6035
6045
6052{
6054}
6055
6065
6072{
6074}
6075
6091{
6092 if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
6093 return 0;
6094
6096 return mCachedMargin;
6097
6098 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6099 int margin = 0;
6100
6101 int lowTick, highTick;
6102 visibleTickBounds(lowTick, highTick);
6103 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6104 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6105 tickPositions.reserve(highTick-lowTick+1);
6106 tickLabels.reserve(highTick-lowTick+1);
6107 if (mTicks)
6108 {
6109 for (int i=lowTick; i<=highTick; ++i)
6110 {
6111 tickPositions.append(coordToPixel(mTickVector.at(i)));
6112 if (mTickLabels)
6113 tickLabels.append(mTickVectorLabels.at(i));
6114 }
6115 }
6116 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6117 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6124 mAxisPainter->tickPositions = tickPositions;
6126 margin += mAxisPainter->size();
6127 margin += mPadding;
6128
6129 mCachedMargin = margin;
6130 mCachedMarginValid = true;
6131 return margin;
6132}
6133
6134/* inherits documentation from base class */
6139
6140
6144
6162 type(QCPAxis::atLeft),
6163 basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6164 lowerEnding(QCPLineEnding::esNone),
6165 upperEnding(QCPLineEnding::esNone),
6166 labelPadding(0),
6167 tickLabelPadding(0),
6168 tickLabelRotation(0),
6169 substituteExponent(true),
6170 numberMultiplyCross(false),
6171 tickLengthIn(5),
6172 tickLengthOut(0),
6173 subTickLengthIn(2),
6174 subTickLengthOut(0),
6175 tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6176 subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6177 offset(0),
6178 abbreviateDecimalPowers(false),
6179 reversedEndings(false),
6180 mParentPlot(parentPlot),
6181 mLabelCache(16) // cache at most 16 (tick) labels
6182{
6183}
6184
6188
6197{
6198 QByteArray newHash = generateLabelParameterHash();
6199 if (newHash != mLabelParameterHash)
6200 {
6201 mLabelCache.clear();
6202 mLabelParameterHash = newHash;
6203 }
6204
6205 QPoint origin;
6206 switch (type)
6207 {
6208 case QCPAxis::atLeft: origin = alignmentRect.bottomLeft() +QPoint(-offset, 0); break;
6209 case QCPAxis::atRight: origin = alignmentRect.bottomRight()+QPoint(+offset, 0); break;
6210 case QCPAxis::atTop: origin = alignmentRect.topLeft() +QPoint(0, -offset); break;
6211 case QCPAxis::atBottom: origin = alignmentRect.bottomLeft() +QPoint(0, +offset); break;
6212 }
6213
6214 double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6215 switch (type)
6216 {
6217 case QCPAxis::atTop: yCor = -1; break;
6218 case QCPAxis::atRight: xCor = 1; break;
6219 default: break;
6220 }
6221
6222 int margin = 0;
6223 // draw baseline:
6224 QLineF baseLine;
6225 painter->setPen(basePen);
6226 if (QCPAxis::orientation(type) == Qt::Horizontal)
6227 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(alignmentRect.width()+xCor, yCor));
6228 else
6229 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -alignmentRect.height()+yCor));
6230 if (reversedEndings)
6231 baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6232 painter->drawLine(baseLine);
6233
6234 // draw ticks:
6235 if (!tickPositions.isEmpty())
6236 {
6237 painter->setPen(tickPen);
6238 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6239 if (QCPAxis::orientation(type) == Qt::Horizontal)
6240 {
6241 for (int i=0; i<tickPositions.size(); ++i)
6242 painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6243 } else
6244 {
6245 for (int i=0; i<tickPositions.size(); ++i)
6246 painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6247 }
6248 }
6249
6250 // draw subticks:
6251 if (!subTickPositions.isEmpty())
6252 {
6253 painter->setPen(subTickPen);
6254 // direction of ticks ("inward" is right for left axis and left for right axis)
6255 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6256 if (QCPAxis::orientation(type) == Qt::Horizontal)
6257 {
6258 for (int i=0; i<subTickPositions.size(); ++i)
6259 painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6260 } else
6261 {
6262 for (int i=0; i<subTickPositions.size(); ++i)
6263 painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6264 }
6265 }
6266 margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6267
6268 // draw axis base endings:
6269 bool antialiasingBackup = painter->antialiasing();
6270 painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6271 painter->setBrush(QBrush(basePen.color()));
6272 QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6274 lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6276 upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6277 painter->setAntialiasing(antialiasingBackup);
6278
6279 // tick labels:
6280 QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6281 if (!tickLabels.isEmpty())
6282 {
6283 margin += tickLabelPadding;
6284 painter->setFont(tickLabelFont);
6285 painter->setPen(QPen(tickLabelColor));
6286 const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6287 for (int i=0; i<maxLabelIndex; ++i)
6288 placeTickLabel(painter, tickPositions.at(i), margin, tickLabels.at(i), &tickLabelsSize);
6289 if (QCPAxis::orientation(type) == Qt::Horizontal)
6290 margin += tickLabelsSize.height();
6291 else
6292 margin += tickLabelsSize.width();
6293 }
6294
6295 // axis label:
6296 QRect labelBounds;
6297 if (!label.isEmpty())
6298 {
6299 margin += labelPadding;
6300 painter->setFont(labelFont);
6301 painter->setPen(QPen(labelColor));
6302 labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6303 if (type == QCPAxis::atLeft)
6304 {
6305 QTransform oldTransform = painter->transform();
6306 painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6307 painter->rotate(-90);
6308 painter->drawText(0, 0, alignmentRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6309 painter->setTransform(oldTransform);
6310 }
6311 else if (type == QCPAxis::atRight)
6312 {
6313 QTransform oldTransform = painter->transform();
6314 painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-alignmentRect.height());
6315 painter->rotate(90);
6316 painter->drawText(0, 0, alignmentRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6317 painter->setTransform(oldTransform);
6318 }
6319 else if (type == QCPAxis::atTop)
6320 painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), alignmentRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6321 else if (type == QCPAxis::atBottom)
6322 painter->drawText(origin.x(), origin.y()+margin, alignmentRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6323 }
6324
6325 // set selection boxes:
6326 int selectionTolerance = 0;
6327 if (mParentPlot)
6328 selectionTolerance = mParentPlot->selectionTolerance();
6329 else
6330 qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6331 int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6332 int selAxisInSize = selectionTolerance;
6333 int selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6334 int selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6335 int selLabelSize = labelBounds.height();
6336 int selLabelOffset = selTickLabelOffset+selTickLabelSize+labelPadding;
6337 if (type == QCPAxis::atLeft)
6338 {
6339 mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, alignmentRect.top(), origin.x()+selAxisInSize, alignmentRect.bottom());
6340 mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, alignmentRect.top(), origin.x()-selTickLabelOffset, alignmentRect.bottom());
6341 mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, alignmentRect.top(), origin.x()-selLabelOffset, alignmentRect.bottom());
6342 } else if (type == QCPAxis::atRight)
6343 {
6344 mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, alignmentRect.top(), origin.x()+selAxisOutSize, alignmentRect.bottom());
6345 mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, alignmentRect.top(), origin.x()+selTickLabelOffset, alignmentRect.bottom());
6346 mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, alignmentRect.top(), origin.x()+selLabelOffset, alignmentRect.bottom());
6347 } else if (type == QCPAxis::atTop)
6348 {
6349 mAxisSelectionBox.setCoords(alignmentRect.left(), origin.y()-selAxisOutSize, alignmentRect.right(), origin.y()+selAxisInSize);
6350 mTickLabelsSelectionBox.setCoords(alignmentRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, alignmentRect.right(), origin.y()-selTickLabelOffset);
6351 mLabelSelectionBox.setCoords(alignmentRect.left(), origin.y()-selLabelOffset-selLabelSize, alignmentRect.right(), origin.y()-selLabelOffset);
6352 } else if (type == QCPAxis::atBottom)
6353 {
6354 mAxisSelectionBox.setCoords(alignmentRect.left(), origin.y()-selAxisInSize, alignmentRect.right(), origin.y()+selAxisOutSize);
6355 mTickLabelsSelectionBox.setCoords(alignmentRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, alignmentRect.right(), origin.y()+selTickLabelOffset);
6356 mLabelSelectionBox.setCoords(alignmentRect.left(), origin.y()+selLabelOffset+selLabelSize, alignmentRect.right(), origin.y()+selLabelOffset);
6357 }
6358 // draw hitboxes for debug purposes:
6359 //painter->setBrush(Qt::NoBrush);
6360 //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6361}
6362
6369{
6370 int result = 0;
6371
6372 // get length of tick marks pointing outwards:
6373 if (!tickPositions.isEmpty())
6374 result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6375
6376 // calculate size of tick labels:
6377 QSize tickLabelsSize(0, 0);
6378 if (!tickLabels.isEmpty())
6379 {
6380 for (int i=0; i<tickLabels.size(); ++i)
6381 getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6382 result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6383 result += tickLabelPadding;
6384 }
6385
6386 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6387 if (!label.isEmpty())
6388 {
6389 QFontMetrics fontMetrics(labelFont);
6390 QRect bounds;
6391 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6392 result += bounds.height() + labelPadding;
6393 }
6394
6395 return result;
6396}
6397
6405{
6406 mLabelCache.clear();
6407}
6408
6417{
6418 QByteArray result;
6419 result.append(QByteArray::number(tickLabelRotation));
6420 result.append(QByteArray::number((int)substituteExponent));
6421 result.append(QByteArray::number((int)numberMultiplyCross));
6422 result.append(tickLabelColor.name()+QByteArray::number(tickLabelColor.alpha(), 16));
6423 result.append(tickLabelFont.toString());
6424 return result;
6425}
6426
6446void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6447{
6448 // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6449 if (text.isEmpty()) return;
6450 QSize finalSize;
6451 QPointF labelAnchor;
6452 switch (type)
6453 {
6454 case QCPAxis::atLeft: labelAnchor = QPointF(alignmentRect.left()-distanceToAxis-offset, position); break;
6455 case QCPAxis::atRight: labelAnchor = QPointF(alignmentRect.right()+distanceToAxis+offset, position); break;
6456 case QCPAxis::atTop: labelAnchor = QPointF(position, alignmentRect.top()-distanceToAxis-offset); break;
6457 case QCPAxis::atBottom: labelAnchor = QPointF(position, alignmentRect.bottom()+distanceToAxis+offset); break;
6458 }
6459 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6460 {
6461 if (!mLabelCache.contains(text)) // no cached label exists, create it
6462 {
6463 CachedLabel *newCachedLabel = new CachedLabel;
6464 TickLabelData labelData = getTickLabelData(painter->font(), text);
6465 QPointF drawOffset = getTickLabelDrawOffset(labelData);
6466 newCachedLabel->offset = drawOffset+labelData.rotatedTotalBounds.topLeft();
6467 newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6468 newCachedLabel->pixmap.fill(Qt::transparent);
6469 QCPPainter cachePainter(&newCachedLabel->pixmap);
6470 cachePainter.setPen(painter->pen());
6471 drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6472 mLabelCache.insert(text, newCachedLabel, 1);
6473 }
6474 // draw cached label:
6475 const CachedLabel *cachedLabel = mLabelCache.object(text);
6476 // if label would be partly clipped by widget border on sides, don't draw it:
6477 if (QCPAxis::orientation(type) == Qt::Horizontal)
6478 {
6479 if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() ||
6480 labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left())
6481 return;
6482 } else
6483 {
6484 if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() >viewportRect.bottom() ||
6485 labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top())
6486 return;
6487 }
6488 painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6489 finalSize = cachedLabel->pixmap.size();
6490 } else // label caching disabled, draw text directly on surface:
6491 {
6492 TickLabelData labelData = getTickLabelData(painter->font(), text);
6493 QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6494 // if label would be partly clipped by widget border on sides, don't draw it:
6495 if (QCPAxis::orientation(type) == Qt::Horizontal)
6496 {
6497 if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() ||
6498 finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left())
6499 return;
6500 } else
6501 {
6502 if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() ||
6503 finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top())
6504 return;
6505 }
6506 drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6507 finalSize = labelData.rotatedTotalBounds.size();
6508 }
6509
6510 // expand passed tickLabelsSize if current tick label is larger:
6511 if (finalSize.width() > tickLabelsSize->width())
6512 tickLabelsSize->setWidth(finalSize.width());
6513 if (finalSize.height() > tickLabelsSize->height())
6514 tickLabelsSize->setHeight(finalSize.height());
6515}
6516
6526void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6527{
6528 // backup painter settings that we're about to change:
6529 QTransform oldTransform = painter->transform();
6530 QFont oldFont = painter->font();
6531
6532 // transform painter to position/rotation:
6533 painter->translate(x, y);
6534 if (!qFuzzyIsNull(tickLabelRotation))
6535 painter->rotate(tickLabelRotation);
6536
6537 // draw text:
6538 if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6539 {
6540 painter->setFont(labelData.baseFont);
6541 painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6542 painter->setFont(labelData.expFont);
6543 painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6544 } else
6545 {
6546 painter->setFont(labelData.baseFont);
6547 painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6548 }
6549
6550 // reset painter settings to what it was before:
6551 painter->setTransform(oldTransform);
6552 painter->setFont(oldFont);
6553}
6554
6564{
6565 TickLabelData result;
6566
6567 // determine whether beautiful decimal powers should be used
6568 bool useBeautifulPowers = false;
6569 int ePos = -1;
6571 {
6572 ePos = text.indexOf('e');
6573 if (ePos > -1)
6574 useBeautifulPowers = true;
6575 }
6576
6577 // calculate text bounding rects and do string preparation for beautiful decimal powers:
6578 result.baseFont = font;
6579 if (result.baseFont.pointSizeF() > 0) // On some rare systems, this sometimes is initialized with -1 (Qt bug?), so we check here before possibly setting a negative value in the next line
6580 result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
6581 if (useBeautifulPowers)
6582 {
6583 // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6584 result.basePart = text.left(ePos);
6585 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6586 if (abbreviateDecimalPowers && result.basePart == "1")
6587 result.basePart = "10";
6588 else
6589 result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
6590 result.expPart = text.mid(ePos+1);
6591 // clip "+" and leading zeros off expPart:
6592 while (result.expPart.length() > 2 && result.expPart.at(1) == '0') // length > 2 so we leave one zero when numberFormatChar is 'e'
6593 result.expPart.remove(1, 1);
6594 if (!result.expPart.isEmpty() && result.expPart.at(0) == '+')
6595 result.expPart.remove(0, 1);
6596 // prepare smaller font for exponent:
6597 result.expFont = font;
6598 result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6599 // calculate bounding rects of base part, exponent part and total one:
6600 result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6601 result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6602 result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
6603 } else // useBeautifulPowers == false
6604 {
6605 result.basePart = text;
6606 result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6607 }
6608 result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
6609
6610 // calculate possibly different bounding rect after rotation:
6611 result.rotatedTotalBounds = result.totalBounds;
6612 if (!qFuzzyIsNull(tickLabelRotation))
6613 {
6614 QTransform transform;
6615 transform.rotate(tickLabelRotation);
6616 result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6617 }
6618
6619 return result;
6620}
6621
6633{
6634 /*
6635 calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6636 explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6637 horizontally under the corresponding tick is always on the label side that is closer to the
6638 axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6639 is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6640 will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6641 time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6642 labels.
6643 */
6644 bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6645 bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6646 double radians = tickLabelRotation/180.0*M_PI;
6647 int x=0, y=0;
6648 if (type == QCPAxis::atLeft)
6649 {
6650 if (doRotation)
6651 {
6652 if (tickLabelRotation > 0)
6653 {
6654 x = -qCos(radians)*labelData.totalBounds.width();
6655 y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6656 } else
6657 {
6658 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6659 y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6660 }
6661 } else
6662 {
6663 x = -labelData.totalBounds.width();
6664 y = -labelData.totalBounds.height()/2.0;
6665 }
6666 } else if (type == QCPAxis::atRight)
6667 {
6668 if (doRotation)
6669 {
6670 if (tickLabelRotation > 0)
6671 {
6672 x = +qSin(radians)*labelData.totalBounds.height();
6673 y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6674 } else
6675 {
6676 x = 0;
6677 y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6678 }
6679 } else
6680 {
6681 x = 0;
6682 y = -labelData.totalBounds.height()/2.0;
6683 }
6684 } else if (type == QCPAxis::atTop)
6685 {
6686 if (doRotation)
6687 {
6688 if (tickLabelRotation > 0)
6689 {
6690 x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6691 y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6692 } else
6693 {
6694 x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6695 y = -qCos(-radians)*labelData.totalBounds.height();
6696 }
6697 } else
6698 {
6699 x = -labelData.totalBounds.width()/2.0;
6700 y = -labelData.totalBounds.height();
6701 }
6702 } else if (type == QCPAxis::atBottom)
6703 {
6704 if (doRotation)
6705 {
6706 if (tickLabelRotation > 0)
6707 {
6708 x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6709 y = 0;
6710 } else
6711 {
6712 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6713 y = +qSin(-radians)*labelData.totalBounds.width();
6714 }
6715 } else
6716 {
6717 x = -labelData.totalBounds.width()/2.0;
6718 y = 0;
6719 }
6720 }
6721
6722 return QPointF(x, y);
6723}
6724
6732void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6733{
6734 // note: this function must return the same tick label sizes as the placeTickLabel function.
6735 QSize finalSize;
6736 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6737 {
6738 const CachedLabel *cachedLabel = mLabelCache.object(text);
6739 finalSize = cachedLabel->pixmap.size();
6740 } else // label caching disabled or no label with this text cached:
6741 {
6742 TickLabelData labelData = getTickLabelData(font, text);
6743 finalSize = labelData.rotatedTotalBounds.size();
6744 }
6745
6746 // expand passed tickLabelsSize if current tick label is larger:
6747 if (finalSize.width() > tickLabelsSize->width())
6748 tickLabelsSize->setWidth(finalSize.width());
6749 if (finalSize.height() > tickLabelsSize->height())
6750 tickLabelsSize->setHeight(finalSize.height());
6751}
6752
6753
6757
6824/* start of documentation of pure virtual functions */
6825
6871/* end of documentation of pure virtual functions */
6872/* start of documentation of signals */
6873
6887/* end of documentation of signals */
6888
6901 QCPLayerable(keyAxis->parentPlot(), "", keyAxis->axisRect()),
6902 mName(""),
6903 mAntialiasedFill(true),
6904 mAntialiasedScatters(true),
6905 mAntialiasedErrorBars(false),
6906 mPen(Qt::black),
6907 mSelectedPen(Qt::black),
6908 mBrush(Qt::NoBrush),
6909 mSelectedBrush(Qt::NoBrush),
6910 mKeyAxis(keyAxis),
6911 mValueAxis(valueAxis),
6912 mSelectable(true),
6913 mSelected(false)
6914{
6916 qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6918 qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6919}
6920
6925void QCPAbstractPlottable::setName(const QString &name)
6926{
6927 mName = name;
6928}
6929
6937{
6938 mAntialiasedFill = enabled;
6939}
6940
6948{
6949 mAntialiasedScatters = enabled;
6950}
6951
6959{
6960 mAntialiasedErrorBars = enabled;
6961}
6962
6963
6973void QCPAbstractPlottable::setPen(const QPen &pen)
6974{
6975 mPen = pen;
6976}
6977
6985{
6986 mSelectedPen = pen;
6987}
6988
6998void QCPAbstractPlottable::setBrush(const QBrush &brush)
6999{
7000 mBrush = brush;
7001}
7002
7010{
7012}
7013
7026{
7027 mKeyAxis = axis;
7028}
7029
7042{
7043 mValueAxis = axis;
7044}
7045
7056{
7057 if (mSelectable != selectable)
7058 {
7061 }
7062}
7063
7079{
7080 if (mSelected != selected)
7081 {
7084 }
7085}
7086
7100void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7101{
7102 rescaleKeyAxis(onlyEnlarge);
7103 rescaleValueAxis(onlyEnlarge);
7104}
7105
7111void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7112{
7113 QCPAxis *keyAxis = mKeyAxis.data();
7114 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7115
7116 SignDomain signDomain = sdBoth;
7118 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7119
7120 bool foundRange;
7121 QCPRange newRange = getKeyRange(foundRange, signDomain);
7122 if (foundRange)
7123 {
7124 if (onlyEnlarge)
7125 newRange.expand(keyAxis->range());
7126 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7127 {
7128 double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
7130 {
7131 newRange.lower = center-keyAxis->range().size()/2.0;
7132 newRange.upper = center+keyAxis->range().size()/2.0;
7133 } else // scaleType() == stLogarithmic
7134 {
7135 newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7136 newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7137 }
7138 }
7139 keyAxis->setRange(newRange);
7140 }
7141}
7142
7151void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7152{
7153 QCPAxis *valueAxis = mValueAxis.data();
7154 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7155
7156 SignDomain signDomain = sdBoth;
7158 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7159
7160 bool foundRange;
7161 QCPRange newRange = getValueRange(foundRange, signDomain);
7162 if (foundRange)
7163 {
7164 if (onlyEnlarge)
7165 newRange.expand(valueAxis->range());
7166 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7167 {
7168 double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
7170 {
7171 newRange.lower = center-valueAxis->range().size()/2.0;
7172 newRange.upper = center+valueAxis->range().size()/2.0;
7173 } else // scaleType() == stLogarithmic
7174 {
7175 newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7176 newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7177 }
7178 }
7179 valueAxis->setRange(newRange);
7180 }
7181}
7182
7196{
7197 if (!mParentPlot || !mParentPlot->legend)
7198 return false;
7199
7201 {
7203 return true;
7204 } else
7205 return false;
7206}
7207
7219{
7220 if (!mParentPlot->legend)
7221 return false;
7222
7224 return mParentPlot->legend->removeItem(lip);
7225 else
7226 return false;
7227}
7228
7229/* inherits documentation from base class */
7231{
7232 if (mKeyAxis && mValueAxis)
7233 return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7234 else
7235 return QRect();
7236}
7237
7238/* inherits documentation from base class */
7243
7254void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7255{
7256 QCPAxis *keyAxis = mKeyAxis.data();
7257 QCPAxis *valueAxis = mValueAxis.data();
7258 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7259
7260 if (keyAxis->orientation() == Qt::Horizontal)
7261 {
7262 x = keyAxis->coordToPixel(key);
7263 y = valueAxis->coordToPixel(value);
7264 } else
7265 {
7266 y = keyAxis->coordToPixel(key);
7267 x = valueAxis->coordToPixel(value);
7268 }
7269}
7270
7276const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7277{
7278 QCPAxis *keyAxis = mKeyAxis.data();
7279 QCPAxis *valueAxis = mValueAxis.data();
7280 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7281
7282 if (keyAxis->orientation() == Qt::Horizontal)
7283 return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7284 else
7285 return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7286}
7287
7298void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7299{
7300 QCPAxis *keyAxis = mKeyAxis.data();
7301 QCPAxis *valueAxis = mValueAxis.data();
7302 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7303
7304 if (keyAxis->orientation() == Qt::Horizontal)
7305 {
7306 key = keyAxis->pixelToCoord(x);
7307 value = valueAxis->pixelToCoord(y);
7308 } else
7309 {
7310 key = keyAxis->pixelToCoord(y);
7311 value = valueAxis->pixelToCoord(x);
7312 }
7313}
7314
7320void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7321{
7322 pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7323}
7324
7331{
7332 return mSelected ? mSelectedPen : mPen;
7333}
7334
7341{
7342 return mSelected ? mSelectedBrush : mBrush;
7343}
7344
7362
7378
7394
7410
7421double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7422{
7423 QVector2D a(start);
7424 QVector2D b(end);
7425 QVector2D p(point);
7426 QVector2D v(b-a);
7427
7428 double vLengthSqr = v.lengthSquared();
7429 if (!qFuzzyIsNull(vLengthSqr))
7430 {
7431 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7432 if (mu < 0)
7433 return (a-p).lengthSquared();
7434 else if (mu > 1)
7435 return (b-p).lengthSquared();
7436 else
7437 return ((a + mu*v)-p).lengthSquared();
7438 } else
7439 return (a-p).lengthSquared();
7440}
7441
7442/* inherits documentation from base class */
7443void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7444{
7445 Q_UNUSED(event)
7446 Q_UNUSED(details)
7447 if (mSelectable)
7448 {
7449 bool selBefore = mSelected;
7450 setSelected(additive ? !mSelected : true);
7451 if (selectionStateChanged)
7452 *selectionStateChanged = mSelected != selBefore;
7453 }
7454}
7455
7456/* inherits documentation from base class */
7457void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7458{
7459 if (mSelectable)
7460 {
7461 bool selBefore = mSelected;
7462 setSelected(false);
7463 if (selectionStateChanged)
7464 *selectionStateChanged = mSelected != selBefore;
7465 }
7466}
7467
7468
7472
7494/* start documentation of inline functions */
7495
7506/* end documentation of inline functions */
7507
7513QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7514 mName(name),
7515 mParentPlot(parentPlot),
7516 mParentItem(parentItem),
7517 mAnchorId(anchorId)
7518{
7519}
7520
7522{
7523 // unregister as parent at children:
7524 QList<QCPItemPosition*> currentChildren(mChildren.toList());
7525 for (int i=0; i<currentChildren.size(); ++i)
7526 currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
7527}
7528
7536{
7537 if (mParentItem)
7538 {
7539 if (mAnchorId > -1)
7540 {
7542 } else
7543 {
7544 qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7545 return QPointF();
7546 }
7547 } else
7548 {
7549 qDebug() << Q_FUNC_INFO << "no parent item set";
7550 return QPointF();
7551 }
7552}
7553
7562{
7563 if (!mChildren.contains(pos))
7564 mChildren.insert(pos);
7565 else
7566 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7567}
7568
7576{
7577 if (!mChildren.remove(pos))
7578 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7579}
7580
7581
7585
7617QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7618 QCPItemAnchor(parentPlot, parentItem, name),
7619 mPositionType(ptAbsolute),
7620 mKey(0),
7621 mValue(0),
7622 mParentAnchor(0)
7623{
7624}
7625
7627{
7628 // unregister as parent at children:
7629 // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7630 // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7631 QList<QCPItemPosition*> currentChildren(mChildren.toList());
7632 for (int i=0; i<currentChildren.size(); ++i)
7633 currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
7634 // unregister as child in parent:
7635 if (mParentAnchor)
7637}
7638
7639/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7641{
7642 return mAxisRect.data();
7643}
7644
7668{
7669 if (mPositionType != type)
7670 {
7671 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7672 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7673 bool recoverPixelPosition = true;
7675 recoverPixelPosition = false;
7677 recoverPixelPosition = false;
7678
7679 QPointF pixelP;
7680 if (recoverPixelPosition)
7681 pixelP = pixelPoint();
7682
7684
7685 if (recoverPixelPosition)
7686 setPixelPoint(pixelP);
7687 }
7688}
7689
7705bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7706{
7707 // make sure self is not assigned as parent:
7708 if (parentAnchor == this)
7709 {
7710 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7711 return false;
7712 }
7713 // make sure no recursive parent-child-relationships are created:
7714 QCPItemAnchor *currentParent = parentAnchor;
7715 while (currentParent)
7716 {
7717 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7718 {
7719 // is a QCPItemPosition, might have further parent, so keep iterating
7720 if (currentParentPos == this)
7721 {
7722 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7723 return false;
7724 }
7725 currentParent = currentParentPos->mParentAnchor;
7726 } else
7727 {
7728 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7729 // same, to prevent a position being child of an anchor which itself depends on the position,
7730 // because they're both on the same item:
7731 if (currentParent->mParentItem == mParentItem)
7732 {
7733 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7734 return false;
7735 }
7736 break;
7737 }
7738 }
7739
7740 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7743
7744 // save pixel position:
7745 QPointF pixelP;
7746 if (keepPixelPosition)
7747 pixelP = pixelPoint();
7748 // unregister at current parent anchor:
7749 if (mParentAnchor)
7751 // register at new parent anchor:
7752 if (parentAnchor)
7753 parentAnchor->addChild(this);
7755 // restore pixel position under new parent:
7756 if (keepPixelPosition)
7757 setPixelPoint(pixelP);
7758 else
7759 setCoords(0, 0);
7760 return true;
7761}
7762
7776void QCPItemPosition::setCoords(double key, double value)
7777{
7778 mKey = key;
7779 mValue = value;
7780}
7781
7787void QCPItemPosition::setCoords(const QPointF &pos)
7788{
7789 setCoords(pos.x(), pos.y());
7790}
7791
7799{
7800 switch (mPositionType)
7801 {
7802 case ptAbsolute:
7803 {
7804 if (mParentAnchor)
7805 return QPointF(mKey, mValue) + mParentAnchor->pixelPoint();
7806 else
7807 return QPointF(mKey, mValue);
7808 }
7809
7810 case ptViewportRatio:
7811 {
7812 if (mParentAnchor)
7813 {
7814 return QPointF(mKey*mParentPlot->viewport().width(),
7816 } else
7817 {
7818 return QPointF(mKey*mParentPlot->viewport().width(),
7819 mValue*mParentPlot->viewport().height()) + mParentPlot->viewport().topLeft();
7820 }
7821 }
7822
7823 case ptAxisRectRatio:
7824 {
7825 if (mAxisRect)
7826 {
7827 if (mParentAnchor)
7828 {
7829 return QPointF(mKey*mAxisRect.data()->width(),
7830 mValue*mAxisRect.data()->height()) + mParentAnchor->pixelPoint();
7831 } else
7832 {
7833 return QPointF(mKey*mAxisRect.data()->width(),
7834 mValue*mAxisRect.data()->height()) + mAxisRect.data()->topLeft();
7835 }
7836 } else
7837 {
7838 qDebug() << Q_FUNC_INFO << "No axis rect defined";
7839 return QPointF(mKey, mValue);
7840 }
7841 }
7842
7843 case ptPlotCoords:
7844 {
7845 double x, y;
7846 if (mKeyAxis && mValueAxis)
7847 {
7848 // both key and value axis are given, translate key/value to x/y coordinates:
7849 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
7850 {
7851 x = mKeyAxis.data()->coordToPixel(mKey);
7852 y = mValueAxis.data()->coordToPixel(mValue);
7853 } else
7854 {
7855 y = mKeyAxis.data()->coordToPixel(mKey);
7856 x = mValueAxis.data()->coordToPixel(mValue);
7857 }
7858 } else if (mKeyAxis)
7859 {
7860 // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
7861 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
7862 {
7863 x = mKeyAxis.data()->coordToPixel(mKey);
7864 y = mValue;
7865 } else
7866 {
7867 y = mKeyAxis.data()->coordToPixel(mKey);
7868 x = mValue;
7869 }
7870 } else if (mValueAxis)
7871 {
7872 // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
7873 if (mValueAxis.data()->orientation() == Qt::Horizontal)
7874 {
7875 x = mValueAxis.data()->coordToPixel(mValue);
7876 y = mKey;
7877 } else
7878 {
7879 y = mValueAxis.data()->coordToPixel(mValue);
7880 x = mKey;
7881 }
7882 } else
7883 {
7884 // no axis given, basically the same as if mPositionType were ptAbsolute
7885 qDebug() << Q_FUNC_INFO << "No axes defined";
7886 x = mKey;
7887 y = mValue;
7888 }
7889 return QPointF(x, y);
7890 }
7891 }
7892 return QPointF();
7893}
7894
7900void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
7901{
7902 mKeyAxis = keyAxis;
7904}
7905
7915
7926void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
7927{
7928 switch (mPositionType)
7929 {
7930 case ptAbsolute:
7931 {
7932 if (mParentAnchor)
7934 else
7936 break;
7937 }
7938
7939 case ptViewportRatio:
7940 {
7941 if (mParentAnchor)
7942 {
7944 p.rx() /= (double)mParentPlot->viewport().width();
7945 p.ry() /= (double)mParentPlot->viewport().height();
7946 setCoords(p);
7947 } else
7948 {
7949 QPointF p(pixelPoint-mParentPlot->viewport().topLeft());
7950 p.rx() /= (double)mParentPlot->viewport().width();
7951 p.ry() /= (double)mParentPlot->viewport().height();
7952 setCoords(p);
7953 }
7954 break;
7955 }
7956
7957 case ptAxisRectRatio:
7958 {
7959 if (mAxisRect)
7960 {
7961 if (mParentAnchor)
7962 {
7964 p.rx() /= (double)mAxisRect.data()->width();
7965 p.ry() /= (double)mAxisRect.data()->height();
7966 setCoords(p);
7967 } else
7968 {
7969 QPointF p(pixelPoint-mAxisRect.data()->topLeft());
7970 p.rx() /= (double)mAxisRect.data()->width();
7971 p.ry() /= (double)mAxisRect.data()->height();
7972 setCoords(p);
7973 }
7974 } else
7975 {
7976 qDebug() << Q_FUNC_INFO << "No axis rect defined";
7978 }
7979 break;
7980 }
7981
7982 case ptPlotCoords:
7983 {
7984 double newKey, newValue;
7985 if (mKeyAxis && mValueAxis)
7986 {
7987 // both key and value axis are given, translate point to key/value coordinates:
7988 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
7989 {
7990 newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.x());
7991 newValue = mValueAxis.data()->pixelToCoord(pixelPoint.y());
7992 } else
7993 {
7994 newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.y());
7995 newValue = mValueAxis.data()->pixelToCoord(pixelPoint.x());
7996 }
7997 } else if (mKeyAxis)
7998 {
7999 // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
8000 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
8001 {
8002 newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.x());
8003 newValue = pixelPoint.y();
8004 } else
8005 {
8006 newKey = mKeyAxis.data()->pixelToCoord(pixelPoint.y());
8007 newValue = pixelPoint.x();
8008 }
8009 } else if (mValueAxis)
8010 {
8011 // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
8012 if (mValueAxis.data()->orientation() == Qt::Horizontal)
8013 {
8014 newKey = pixelPoint.y();
8015 newValue = mValueAxis.data()->pixelToCoord(pixelPoint.x());
8016 } else
8017 {
8018 newKey = pixelPoint.x();
8019 newValue = mValueAxis.data()->pixelToCoord(pixelPoint.y());
8020 }
8021 } else
8022 {
8023 // no axis given, basically the same as if mPositionType were ptAbsolute
8024 qDebug() << Q_FUNC_INFO << "No axes defined";
8025 newKey = pixelPoint.x();
8026 newValue = pixelPoint.y();
8027 }
8028 setCoords(newKey, newValue);
8029 break;
8030 }
8031 }
8032}
8033
8034
8038
8161/* start of documentation of inline functions */
8162
8178/* end of documentation of inline functions */
8179/* start documentation of pure virtual functions */
8180
8191/* end documentation of pure virtual functions */
8192/* start documentation of signals */
8193
8199/* end documentation of signals */
8200
8205 QCPLayerable(parentPlot),
8206 mClipToAxisRect(false),
8207 mSelectable(true),
8208 mSelected(false)
8209{
8210 QList<QCPAxisRect*> rects = parentPlot->axisRects();
8211 if (rects.size() > 0)
8212 {
8213 setClipToAxisRect(true);
8214 setClipAxisRect(rects.first());
8215 }
8216}
8217
8219{
8220 // don't delete mPositions because every position is also an anchor and thus in mAnchors
8221 qDeleteAll(mAnchors);
8222}
8223
8224/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8226{
8227 return mClipAxisRect.data();
8228}
8229
8237{
8238 mClipToAxisRect = clip;
8239 if (mClipToAxisRect)
8241}
8242
8255
8266{
8267 if (mSelectable != selectable)
8268 {
8271 }
8272}
8273
8289{
8290 if (mSelected != selected)
8291 {
8294 }
8295}
8296
8308{
8309 for (int i=0; i<mPositions.size(); ++i)
8310 {
8311 if (mPositions.at(i)->name() == name)
8312 return mPositions.at(i);
8313 }
8314 qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8315 return 0;
8316}
8317
8328QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8329{
8330 for (int i=0; i<mAnchors.size(); ++i)
8331 {
8332 if (mAnchors.at(i)->name() == name)
8333 return mAnchors.at(i);
8334 }
8335 qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8336 return 0;
8337}
8338
8347bool QCPAbstractItem::hasAnchor(const QString &name) const
8348{
8349 for (int i=0; i<mAnchors.size(); ++i)
8350 {
8351 if (mAnchors.at(i)->name() == name)
8352 return true;
8353 }
8354 return false;
8355}
8356
8367{
8369 return mClipAxisRect.data()->rect();
8370 else
8371 return mParentPlot->viewport();
8372}
8373
8391
8404double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8405{
8406 QVector2D a(start);
8407 QVector2D b(end);
8408 QVector2D p(point);
8409 QVector2D v(b-a);
8410
8411 double vLengthSqr = v.lengthSquared();
8412 if (!qFuzzyIsNull(vLengthSqr))
8413 {
8414 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8415 if (mu < 0)
8416 return (a-p).lengthSquared();
8417 else if (mu > 1)
8418 return (b-p).lengthSquared();
8419 else
8420 return ((a + mu*v)-p).lengthSquared();
8421 } else
8422 return (a-p).lengthSquared();
8423}
8424
8442double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8443{
8444 double result = -1;
8445
8446 // distance to border:
8447 QList<QLineF> lines;
8448 lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8449 << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8450 double minDistSqr = std::numeric_limits<double>::max();
8451 for (int i=0; i<lines.size(); ++i)
8452 {
8453 double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8454 if (distSqr < minDistSqr)
8455 minDistSqr = distSqr;
8456 }
8457 result = qSqrt(minDistSqr);
8458
8459 // filled rect, allow click inside to count as hit:
8460 if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8461 {
8462 if (rect.contains(pos))
8463 result = mParentPlot->selectionTolerance()*0.99;
8464 }
8465 return result;
8466}
8467
8478QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8479{
8480 qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8481 return QPointF();
8482}
8483
8499{
8500 if (hasAnchor(name))
8501 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8502 QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8503 mPositions.append(newPosition);
8504 mAnchors.append(newPosition); // every position is also an anchor
8505 newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8507 if (mParentPlot->axisRect())
8508 newPosition->setAxisRect(mParentPlot->axisRect());
8509 newPosition->setCoords(0, 0);
8510 return newPosition;
8511}
8512
8532QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8533{
8534 if (hasAnchor(name))
8535 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8536 QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8537 mAnchors.append(newAnchor);
8538 return newAnchor;
8539}
8540
8541/* inherits documentation from base class */
8542void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8543{
8544 Q_UNUSED(event)
8545 Q_UNUSED(details)
8546 if (mSelectable)
8547 {
8548 bool selBefore = mSelected;
8549 setSelected(additive ? !mSelected : true);
8550 if (selectionStateChanged)
8551 *selectionStateChanged = mSelected != selBefore;
8552 }
8553}
8554
8555/* inherits documentation from base class */
8556void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8557{
8558 if (mSelectable)
8559 {
8560 bool selBefore = mSelected;
8561 setSelected(false);
8562 if (selectionStateChanged)
8563 *selectionStateChanged = mSelected != selBefore;
8564 }
8565}
8566
8567/* inherits documentation from base class */
8572
8573
8770
8780/* start of documentation of inline functions */
8781
8803/* end of documentation of inline functions */
8804/* start of documentation of signals */
8805
8993/* end of documentation of signals */
8994/* start of documentation of public members */
8995
9067/* end of documentation of public members */
9068
9073 QWidget(parent),
9074 xAxis(0),
9075 yAxis(0),
9076 xAxis2(0),
9077 yAxis2(0),
9078 legend(0),
9079 mPlotLayout(0),
9080 mAutoAddPlottableToLegend(true),
9081 mAntialiasedElements(QCP::aeNone),
9082 mNotAntialiasedElements(QCP::aeNone),
9083 mInteractions(0),
9084 mSelectionTolerance(8),
9085 mNoAntialiasingOnDrag(false),
9086 mBackgroundBrush(Qt::white, Qt::SolidPattern),
9087 mBackgroundScaled(true),
9088 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9089 mCurrentLayer(0),
9090 mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9091 mMultiSelectModifier(Qt::ControlModifier),
9092 mPaintBuffer(size()),
9093 mMouseEventElement(0),
9094 mReplotting(false)
9095{
9096 setAttribute(Qt::WA_NoMousePropagation);
9097 setAttribute(Qt::WA_OpaquePaintEvent);
9098 setMouseTracking(true);
9099 QLocale currentLocale = locale();
9100 currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9101 setLocale(currentLocale);
9102
9103 // create initial layers:
9104 mLayers.append(new QCPLayer(this, "background"));
9105 mLayers.append(new QCPLayer(this, "grid"));
9106 mLayers.append(new QCPLayer(this, "main"));
9107 mLayers.append(new QCPLayer(this, "axes"));
9108 mLayers.append(new QCPLayer(this, "legend"));
9110 setCurrentLayer("main");
9111
9112 // create initial layout, axis rect and legend:
9115 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9116 mPlotLayout->setLayer("main");
9117 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9118 mPlotLayout->addElement(0, 0, defaultAxisRect);
9119 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9120 yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9121 xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9122 yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9123 legend = new QCPLegend;
9124 legend->setVisible(false);
9125 defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9126 defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9127
9128 defaultAxisRect->setLayer("background");
9129 xAxis->setLayer("axes");
9130 yAxis->setLayer("axes");
9131 xAxis2->setLayer("axes");
9132 yAxis2->setLayer("axes");
9133 xAxis->grid()->setLayer("grid");
9134 yAxis->grid()->setLayer("grid");
9135 xAxis2->grid()->setLayer("grid");
9136 yAxis2->grid()->setLayer("grid");
9137 legend->setLayer("legend");
9138
9139 setViewport(rect()); // needs to be called after mPlotLayout has been created
9140
9141 replot();
9142}
9143
9145{
9147 clearItems();
9148
9149 if (mPlotLayout)
9150 {
9151 delete mPlotLayout;
9152 mPlotLayout = 0;
9153 }
9154
9155 mCurrentLayer = 0;
9156 qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9157 mLayers.clear();
9158}
9159
9177void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9178{
9180
9181 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9183 mNotAntialiasedElements |= ~mAntialiasedElements;
9184}
9185
9194{
9195 if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9196 mAntialiasedElements &= ~antialiasedElement;
9197 else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9198 mAntialiasedElements |= antialiasedElement;
9199
9200 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9202 mNotAntialiasedElements |= ~mAntialiasedElements;
9203}
9204
9223void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
9224{
9226
9227 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9229 mAntialiasedElements |= ~mNotAntialiasedElements;
9230}
9231
9240{
9241 if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9242 mNotAntialiasedElements &= ~notAntialiasedElement;
9243 else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9244 mNotAntialiasedElements |= notAntialiasedElement;
9245
9246 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9248 mAntialiasedElements |= ~mNotAntialiasedElements;
9249}
9250
9261
9316void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9317{
9319}
9320
9328void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9329{
9330 if (!enabled && mInteractions.testFlag(interaction))
9331 mInteractions &= ~interaction;
9332 else if (enabled && !mInteractions.testFlag(interaction))
9333 mInteractions |= interaction;
9334}
9335
9350{
9351 mSelectionTolerance = pixels;
9352}
9353
9364{
9365 mNoAntialiasingOnDrag = enabled;
9366}
9367
9373void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9374{
9375 mPlottingHints = hints;
9376}
9377
9384{
9385 QCP::PlottingHints newHints = mPlottingHints;
9386 if (!enabled)
9387 newHints &= ~hint;
9388 else
9389 newHints |= hint;
9390
9391 if (newHints != mPlottingHints)
9392 setPlottingHints(newHints);
9393}
9394
9405void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9406{
9407 mMultiSelectModifier = modifier;
9408}
9409
9417void QCustomPlot::setViewport(const QRect &rect)
9418{
9419 mViewport = rect;
9420 if (mPlotLayout)
9422}
9423
9439void QCustomPlot::setBackground(const QPixmap &pm)
9440{
9441 mBackgroundPixmap = pm;
9442 mScaledBackgroundPixmap = QPixmap();
9443}
9444
9458void QCustomPlot::setBackground(const QBrush &brush)
9459{
9460 mBackgroundBrush = brush;
9461}
9462
9470void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9471{
9472 mBackgroundPixmap = pm;
9473 mScaledBackgroundPixmap = QPixmap();
9474 mBackgroundScaled = scaled;
9475 mBackgroundScaledMode = mode;
9476}
9477
9489{
9490 mBackgroundScaled = scaled;
9491}
9492
9499void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9500{
9501 mBackgroundScaledMode = mode;
9502}
9503
9513{
9514 if (index >= 0 && index < mPlottables.size())
9515 {
9516 return mPlottables.at(index);
9517 } else
9518 {
9519 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9520 return 0;
9521 }
9522}
9523
9532{
9533 if (!mPlottables.isEmpty())
9534 {
9535 return mPlottables.last();
9536 } else
9537 return 0;
9538}
9539
9551{
9552 if (mPlottables.contains(plottable))
9553 {
9554 qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9555 return false;
9556 }
9557 if (plottable->parentPlot() != this)
9558 {
9559 qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9560 return false;
9561 }
9562
9563 mPlottables.append(plottable);
9564 // possibly add plottable to legend:
9567 // special handling for QCPGraphs to maintain the simple graph interface:
9568 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9569 mGraphs.append(graph);
9570 if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9572 return true;
9573}
9574
9583{
9584 if (!mPlottables.contains(plottable))
9585 {
9586 qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9587 return false;
9588 }
9589
9590 // remove plottable from legend:
9592 // special handling for QCPGraphs to maintain the simple graph interface:
9593 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9594 mGraphs.removeOne(graph);
9595 // remove plottable:
9596 delete plottable;
9597 mPlottables.removeOne(plottable);
9598 return true;
9599}
9600
9606{
9607 if (index >= 0 && index < mPlottables.size())
9608 return removePlottable(mPlottables[index]);
9609 else
9610 {
9611 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9612 return false;
9613 }
9614}
9615
9624{
9625 int c = mPlottables.size();
9626 for (int i=c-1; i >= 0; --i)
9628 return c;
9629}
9630
9637{
9638 return mPlottables.size();
9639}
9640
9648QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9649{
9650 QList<QCPAbstractPlottable*> result;
9652 {
9653 if (plottable->selected())
9654 result.append(plottable);
9655 }
9656 return result;
9657}
9658
9671QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9672{
9673 QCPAbstractPlottable *resultPlottable = 0;
9674 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9675
9677 {
9678 if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
9679 continue;
9680 if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
9681 {
9682 double currentDistance = plottable->selectTest(pos, false);
9683 if (currentDistance >= 0 && currentDistance < resultDistance)
9684 {
9685 resultPlottable = plottable;
9686 resultDistance = currentDistance;
9687 }
9688 }
9689 }
9690
9691 return resultPlottable;
9692}
9693
9700{
9701 return mPlottables.contains(plottable);
9702}
9703
9713{
9714 if (index >= 0 && index < mGraphs.size())
9715 {
9716 return mGraphs.at(index);
9717 } else
9718 {
9719 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9720 return 0;
9721 }
9722}
9723
9732{
9733 if (!mGraphs.isEmpty())
9734 {
9735 return mGraphs.last();
9736 } else
9737 return 0;
9738}
9739
9753{
9754 if (!keyAxis) keyAxis = xAxis;
9755 if (!valueAxis) valueAxis = yAxis;
9756 if (!keyAxis || !valueAxis)
9757 {
9758 qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9759 return 0;
9760 }
9761 if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9762 {
9763 qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9764 return 0;
9765 }
9766
9767 QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9768 if (addPlottable(newGraph))
9769 {
9770 newGraph->setName("Graph "+QString::number(mGraphs.size()));
9771 return newGraph;
9772 } else
9773 {
9774 delete newGraph;
9775 return 0;
9776 }
9777}
9778
9789{
9790 return removePlottable(graph);
9791}
9792
9798{
9799 if (index >= 0 && index < mGraphs.size())
9800 return removeGraph(mGraphs[index]);
9801 else
9802 return false;
9803}
9804
9813{
9814 int c = mGraphs.size();
9815 for (int i=c-1; i >= 0; --i)
9816 removeGraph(mGraphs[i]);
9817 return c;
9818}
9819
9826{
9827 return mGraphs.size();
9828}
9829
9838QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9839{
9840 QList<QCPGraph*> result;
9841 foreach (QCPGraph *graph, mGraphs)
9842 {
9843 if (graph->selected())
9844 result.append(graph);
9845 }
9846 return result;
9847}
9848
9858{
9859 if (index >= 0 && index < mItems.size())
9860 {
9861 return mItems.at(index);
9862 } else
9863 {
9864 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9865 return 0;
9866 }
9867}
9868
9877{
9878 if (!mItems.isEmpty())
9879 {
9880 return mItems.last();
9881 } else
9882 return 0;
9883}
9884
9894{
9895 if (!mItems.contains(item) && item->parentPlot() == this)
9896 {
9897 mItems.append(item);
9898 return true;
9899 } else
9900 {
9901 qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9902 return false;
9903 }
9904}
9905
9914{
9915 if (mItems.contains(item))
9916 {
9917 delete item;
9918 mItems.removeOne(item);
9919 return true;
9920 } else
9921 {
9922 qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9923 return false;
9924 }
9925}
9926
9932{
9933 if (index >= 0 && index < mItems.size())
9934 return removeItem(mItems[index]);
9935 else
9936 {
9937 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9938 return false;
9939 }
9940}
9941
9950{
9951 int c = mItems.size();
9952 for (int i=c-1; i >= 0; --i)
9953 removeItem(mItems[i]);
9954 return c;
9955}
9956
9963{
9964 return mItems.size();
9965}
9966
9972QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9973{
9974 QList<QCPAbstractItem*> result;
9975 foreach (QCPAbstractItem *item, mItems)
9976 {
9977 if (item->selected())
9978 result.append(item);
9979 }
9980 return result;
9981}
9982
9996QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9997{
9998 QCPAbstractItem *resultItem = 0;
9999 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
10000
10001 foreach (QCPAbstractItem *item, mItems)
10002 {
10003 if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
10004 continue;
10005 if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
10006 {
10007 double currentDistance = item->selectTest(pos, false);
10008 if (currentDistance >= 0 && currentDistance < resultDistance)
10009 {
10010 resultItem = item;
10011 resultDistance = currentDistance;
10012 }
10013 }
10014 }
10015
10016 return resultItem;
10017}
10018
10025{
10026 return mItems.contains(item);
10027}
10028
10037QCPLayer *QCustomPlot::layer(const QString &name) const
10038{
10039 foreach (QCPLayer *layer, mLayers)
10040 {
10041 if (layer->name() == name)
10042 return layer;
10043 }
10044 return 0;
10045}
10046
10054{
10055 if (index >= 0 && index < mLayers.size())
10056 {
10057 return mLayers.at(index);
10058 } else
10059 {
10060 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10061 return 0;
10062 }
10063}
10064
10069{
10070 return mCurrentLayer;
10071}
10072
10083bool QCustomPlot::setCurrentLayer(const QString &name)
10084{
10085 if (QCPLayer *newCurrentLayer = layer(name))
10086 {
10087 return setCurrentLayer(newCurrentLayer);
10088 } else
10089 {
10090 qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10091 return false;
10092 }
10093}
10094
10104{
10105 if (!mLayers.contains(layer))
10106 {
10107 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10108 return false;
10109 }
10110
10112 return true;
10113}
10114
10121{
10122 return mLayers.size();
10123}
10124
10138bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10139{
10140 if (!otherLayer)
10141 otherLayer = mLayers.last();
10142 if (!mLayers.contains(otherLayer))
10143 {
10144 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10145 return false;
10146 }
10147 if (layer(name))
10148 {
10149 qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10150 return false;
10151 }
10152
10153 QCPLayer *newLayer = new QCPLayer(this, name);
10154 mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10156 return true;
10157}
10158
10174{
10175 if (!mLayers.contains(layer))
10176 {
10177 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10178 return false;
10179 }
10180 if (mLayers.size() < 2)
10181 {
10182 qDebug() << Q_FUNC_INFO << "can't remove last layer";
10183 return false;
10184 }
10185
10186 // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10187 int removedIndex = layer->index();
10188 bool isFirstLayer = removedIndex==0;
10189 QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10190 QList<QCPLayerable*> children = layer->children();
10191 if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10192 {
10193 for (int i=children.size()-1; i>=0; --i)
10194 children.at(i)->moveToLayer(targetLayer, true);
10195 } else // append normally
10196 {
10197 for (int i=0; i<children.size(); ++i)
10198 children.at(i)->moveToLayer(targetLayer, false);
10199 }
10200 // if removed layer is current layer, change current layer to layer below/above:
10201 if (layer == mCurrentLayer)
10202 setCurrentLayer(targetLayer);
10203 // remove layer:
10204 delete layer;
10205 mLayers.removeOne(layer);
10207 return true;
10208}
10209
10220{
10221 if (!mLayers.contains(layer))
10222 {
10223 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10224 return false;
10225 }
10226 if (!mLayers.contains(otherLayer))
10227 {
10228 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10229 return false;
10230 }
10231
10232 mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10234 return true;
10235}
10236
10247{
10248 return axisRects().size();
10249}
10250
10261{
10262 const QList<QCPAxisRect*> rectList = axisRects();
10263 if (index >= 0 && index < rectList.size())
10264 {
10265 return rectList.at(index);
10266 } else
10267 {
10268 qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10269 return 0;
10270 }
10271}
10272
10278QList<QCPAxisRect*> QCustomPlot::axisRects() const
10279{
10280 QList<QCPAxisRect*> result;
10281 QStack<QCPLayoutElement*> elementStack;
10282 if (mPlotLayout)
10283 elementStack.push(mPlotLayout);
10284
10285 while (!elementStack.isEmpty())
10286 {
10287 foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10288 {
10289 if (element)
10290 {
10291 elementStack.push(element);
10292 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10293 result.append(ar);
10294 }
10295 }
10296 }
10297
10298 return result;
10299}
10300
10311{
10312 QCPLayoutElement *currentElement = mPlotLayout;
10313 bool searchSubElements = true;
10314 while (searchSubElements && currentElement)
10315 {
10316 searchSubElements = false;
10317 foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10318 {
10319 if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10320 {
10321 currentElement = subElement;
10322 searchSubElements = true;
10323 break;
10324 }
10325 }
10326 }
10327 return currentElement;
10328}
10329
10337QList<QCPAxis*> QCustomPlot::selectedAxes() const
10338{
10339 QList<QCPAxis*> result, allAxes;
10340 foreach (QCPAxisRect *rect, axisRects())
10341 allAxes << rect->axes();
10342
10343 foreach (QCPAxis *axis, allAxes)
10344 {
10345 if (axis->selectedParts() != QCPAxis::spNone)
10346 result.append(axis);
10347 }
10348
10349 return result;
10350}
10351
10359QList<QCPLegend*> QCustomPlot::selectedLegends() const
10360{
10361 QList<QCPLegend*> result;
10362
10363 QStack<QCPLayoutElement*> elementStack;
10364 if (mPlotLayout)
10365 elementStack.push(mPlotLayout);
10366
10367 while (!elementStack.isEmpty())
10368 {
10369 foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10370 {
10371 if (subElement)
10372 {
10373 elementStack.push(subElement);
10374 if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10375 {
10376 if (leg->selectedParts() != QCPLegend::spNone)
10377 result.append(leg);
10378 }
10379 }
10380 }
10381 }
10382
10383 return result;
10384}
10385
10396{
10397 foreach (QCPLayer *layer, mLayers)
10398 {
10399 foreach (QCPLayerable *layerable, layer->children())
10400 layerable->deselectEvent(0);
10401 }
10402}
10403
10418{
10419 if (mReplotting) // incase signals loop back to replot slot
10420 return;
10421 mReplotting = true;
10422 emit beforeReplot();
10423
10424 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10425 QCPPainter painter;
10426 painter.begin(&mPaintBuffer);
10427 if (painter.isActive())
10428 {
10429 painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10430 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10431 painter.fillRect(mViewport, mBackgroundBrush);
10432 draw(&painter);
10433 painter.end();
10434 if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10435 repaint();
10436 else
10437 update();
10438 } else // might happen if QCustomPlot has width or height zero
10439 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
10440
10441 emit afterReplot();
10442 mReplotting = false;
10443}
10444
10453void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10454{
10455 QList<QCPAxis*> allAxes;
10456 foreach (QCPAxisRect *rect, axisRects())
10457 allAxes << rect->axes();
10458
10459 foreach (QCPAxis *axis, allAxes)
10460 axis->rescale(onlyVisiblePlottables);
10461}
10462
10500bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10501{
10502 bool success = false;
10503#ifdef QT_NO_PRINTER
10504 Q_UNUSED(fileName)
10505 Q_UNUSED(noCosmeticPen)
10506 Q_UNUSED(width)
10507 Q_UNUSED(height)
10508 qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10509#else
10510 int newWidth, newHeight;
10511 if (width == 0 || height == 0)
10512 {
10513 newWidth = this->width();
10514 newHeight = this->height();
10515 } else
10516 {
10517 newWidth = width;
10518 newHeight = height;
10519 }
10520
10521 QPrinter printer(QPrinter::ScreenResolution);
10522 printer.setOutputFileName(fileName);
10523 printer.setOutputFormat(QPrinter::PdfFormat);
10524 printer.setFullPage(true);
10525 printer.setColorMode(QPrinter::Color);
10526 printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10527 printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10528 QRect oldViewport = viewport();
10529 setViewport(QRect(0, 0, newWidth, newHeight));
10530 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10531 QCPPainter printpainter;
10532 if (printpainter.begin(&printer))
10533 {
10534 printpainter.setMode(QCPPainter::pmVectorized);
10535 printpainter.setMode(QCPPainter::pmNoCaching);
10536 printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10537 printpainter.setWindow(mViewport);
10538 if (mBackgroundBrush.style() != Qt::NoBrush &&
10539 mBackgroundBrush.color() != Qt::white &&
10540 mBackgroundBrush.color() != Qt::transparent &&
10541 mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10542 printpainter.fillRect(viewport(), mBackgroundBrush);
10543 draw(&printpainter);
10544 printpainter.end();
10545 success = true;
10546 }
10547 setViewport(oldViewport);
10548#endif // QT_NO_PRINTER
10549 return success;
10550}
10551
10589bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10590{
10591 return saveRastered(fileName, width, height, scale, "PNG", quality);
10592}
10593
10628bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10629{
10630 return saveRastered(fileName, width, height, scale, "JPG", quality);
10631}
10632
10664bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10665{
10666 return saveRastered(fileName, width, height, scale, "BMP");
10667}
10668
10678{
10679 return mPlotLayout->minimumSizeHint();
10680}
10681
10688{
10689 return mPlotLayout->minimumSizeHint();
10690}
10691
10697void QCustomPlot::paintEvent(QPaintEvent *event)
10698{
10699 Q_UNUSED(event);
10700 QPainter painter(this);
10701 painter.drawPixmap(0, 0, mPaintBuffer);
10702}
10703
10710void QCustomPlot::resizeEvent(QResizeEvent *event)
10711{
10712 // resize and repaint the buffer:
10713 mPaintBuffer = QPixmap(event->size());
10714 setViewport(rect());
10715 replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10716}
10717
10728{
10729 emit mouseDoubleClick(event);
10730
10731 QVariant details;
10732 QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10733
10734 // emit specialized object double click signals:
10735 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10736 emit plottableDoubleClick(ap, event);
10737 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10738 emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10739 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10740 emit itemDoubleClick(ai, event);
10741 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10742 emit legendDoubleClick(lg, 0, event);
10743 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10744 emit legendDoubleClick(li->parentLegend(), li, event);
10745 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10746 emit titleDoubleClick(event, pt);
10747
10748 // call double click event of affected layout element:
10749 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10750 el->mouseDoubleClickEvent(event);
10751
10752 // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10754 {
10755 mMouseEventElement->mouseReleaseEvent(event);
10757 }
10758
10759 //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10760}
10761
10769void QCustomPlot::mousePressEvent(QMouseEvent *event)
10770{
10771 emit mousePress(event);
10772 mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10773
10774 // call event of affected layout element:
10775 mMouseEventElement = layoutElementAt(event->pos());
10777 mMouseEventElement->mousePressEvent(event);
10778
10779 QWidget::mousePressEvent(event);
10780}
10781
10791void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10792{
10793 emit mouseMove(event);
10794
10795 // call event of affected layout element:
10797 mMouseEventElement->mouseMoveEvent(event);
10798
10799 QWidget::mouseMoveEvent(event);
10800}
10801
10816void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10817{
10818 emit mouseRelease(event);
10819 bool doReplot = false;
10820
10821 if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10822 {
10823 if (event->button() == Qt::LeftButton)
10824 {
10825 // handle selection mechanism:
10826 QVariant details;
10827 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10828 bool selectionStateChanged = false;
10829 bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10830 // deselect all other layerables if not additive selection:
10831 if (!additive)
10832 {
10833 foreach (QCPLayer *layer, mLayers)
10834 {
10835 foreach (QCPLayerable *layerable, layer->children())
10836 {
10837 if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10838 {
10839 bool selChanged = false;
10840 layerable->deselectEvent(&selChanged);
10841 selectionStateChanged |= selChanged;
10842 }
10843 }
10844 }
10845 }
10846 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10847 {
10848 // a layerable was actually clicked, call its selectEvent:
10849 bool selChanged = false;
10850 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10851 selectionStateChanged |= selChanged;
10852 }
10853 doReplot = true;
10854 if (selectionStateChanged)
10856 }
10857
10858 // emit specialized object click signals:
10859 QVariant details;
10860 QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false
10861 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10862 emit plottableClick(ap, event);
10863 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10864 emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10865 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10866 emit itemClick(ai, event);
10867 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10868 emit legendClick(lg, 0, event);
10869 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10870 emit legendClick(li->parentLegend(), li, event);
10871 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10872 emit titleClick(event, pt);
10873 }
10874
10875 // call event of affected layout element:
10877 {
10878 mMouseEventElement->mouseReleaseEvent(event);
10880 }
10881
10882 if (doReplot || noAntialiasingOnDrag())
10883 replot();
10884
10885 QWidget::mouseReleaseEvent(event);
10886}
10887
10894void QCustomPlot::wheelEvent(QWheelEvent *event)
10895{
10896 emit mouseWheel(event);
10897
10898 // call event of affected layout element:
10899 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10900 el->wheelEvent(event);
10901
10902 QWidget::wheelEvent(event);
10903}
10904
10913{
10914 // run through layout phases:
10918
10919 // draw viewport background pixmap:
10920 drawBackground(painter);
10921
10922 // draw all layered objects (grid, axes, plottables, items, legend,...):
10923 foreach (QCPLayer *layer, mLayers)
10924 {
10925 foreach (QCPLayerable *child, layer->children())
10926 {
10927 if (child->realVisibility())
10928 {
10929 painter->save();
10930 painter->setClipRect(child->clipRect().translated(0, -1));
10931 child->applyDefaultAntialiasingHint(painter);
10932 child->draw(painter);
10933 painter->restore();
10934 }
10935 }
10936 }
10937
10938 /* Debug code to draw all layout element rects
10939 foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10940 {
10941 painter->setBrush(Qt::NoBrush);
10942 painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10943 painter->drawRect(el->rect());
10944 painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10945 painter->drawRect(el->outerRect());
10946 }
10947 */
10948}
10949
10968{
10969 // Note: background color is handled in individual replot/save functions
10970
10971 // draw background pixmap (on top of fill, if brush specified):
10972 if (!mBackgroundPixmap.isNull())
10973 {
10975 {
10976 // check whether mScaledBackground needs to be updated:
10977 QSize scaledSize(mBackgroundPixmap.size());
10978 scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10979 if (mScaledBackgroundPixmap.size() != scaledSize)
10980 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10981 painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10982 } else
10983 {
10984 painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10985 }
10986 }
10987}
10988
10989
10996{
10997 if (xAxis == axis)
10998 xAxis = 0;
10999 if (xAxis2 == axis)
11000 xAxis2 = 0;
11001 if (yAxis == axis)
11002 yAxis = 0;
11003 if (yAxis2 == axis)
11004 yAxis2 = 0;
11005
11006 // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
11007}
11008
11015{
11016 if (this->legend == legend)
11017 this->legend = 0;
11018}
11019
11027{
11028 for (int i=0; i<mLayers.size(); ++i)
11029 mLayers.at(i)->mIndex = i;
11030}
11031
11044QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11045{
11046 for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11047 {
11048 const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11049 double minimumDistance = selectionTolerance()*1.1;
11050 QCPLayerable *minimumDistanceLayerable = 0;
11051 for (int i=layerables.size()-1; i>=0; --i)
11052 {
11053 if (!layerables.at(i)->realVisibility())
11054 continue;
11055 QVariant details;
11056 double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11057 if (dist >= 0 && dist < minimumDistance)
11058 {
11059 minimumDistance = dist;
11060 minimumDistanceLayerable = layerables.at(i);
11061 if (selectionDetails) *selectionDetails = details;
11062 }
11063 }
11064 if (minimumDistance < selectionTolerance())
11065 return minimumDistanceLayerable;
11066 }
11067 return 0;
11068}
11069
11081bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11082{
11083 QPixmap buffer = toPixmap(width, height, scale);
11084 if (!buffer.isNull())
11085 return buffer.save(fileName, format, quality);
11086 else
11087 return false;
11088}
11089
11098QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11099{
11100 // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11101 int newWidth, newHeight;
11102 if (width == 0 || height == 0)
11103 {
11104 newWidth = this->width();
11105 newHeight = this->height();
11106 } else
11107 {
11108 newWidth = width;
11109 newHeight = height;
11110 }
11111 int scaledWidth = qRound(scale*newWidth);
11112 int scaledHeight = qRound(scale*newHeight);
11113
11114 QPixmap result(scaledWidth, scaledHeight);
11115 result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11116 QCPPainter painter;
11117 painter.begin(&result);
11118 if (painter.isActive())
11119 {
11120 QRect oldViewport = viewport();
11121 setViewport(QRect(0, 0, newWidth, newHeight));
11123 if (!qFuzzyCompare(scale, 1.0))
11124 {
11125 if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11127 painter.scale(scale, scale);
11128 }
11129 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
11130 painter.fillRect(mViewport, mBackgroundBrush);
11131 draw(&painter);
11132 setViewport(oldViewport);
11133 painter.end();
11134 } else // might happen if pixmap has width or height zero
11135 {
11136 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11137 return QPixmap();
11138 }
11139 return result;
11140}
11141
11154void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11155{
11156 // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11157 int newWidth, newHeight;
11158 if (width == 0 || height == 0)
11159 {
11160 newWidth = this->width();
11161 newHeight = this->height();
11162 } else
11163 {
11164 newWidth = width;
11165 newHeight = height;
11166 }
11167
11168 if (painter->isActive())
11169 {
11170 QRect oldViewport = viewport();
11171 setViewport(QRect(0, 0, newWidth, newHeight));
11173 // warning: the following is different in toPixmap, because a solid background color is applied there via QPixmap::fill
11174 // here, we need to do this via QPainter::fillRect.
11175 if (mBackgroundBrush.style() != Qt::NoBrush)
11176 painter->fillRect(mViewport, mBackgroundBrush);
11177 draw(painter);
11178 setViewport(oldViewport);
11179 } else
11180 qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11181}
11182
11183
11187
11221 mLevelCount(350),
11222 mColorInterpolation(ciRGB),
11223 mPeriodic(false),
11224 mColorBufferInvalidated(true)
11225{
11226 mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11227 loadPreset(preset);
11228}
11229
11230/* undocumented operator */
11232{
11233 return ((other.mLevelCount == this->mLevelCount) &&
11234 (other.mColorInterpolation == this->mColorInterpolation) &&
11235 (other.mPeriodic == this->mPeriodic) &&
11236 (other.mColorStops == this->mColorStops));
11237}
11238
11246{
11247 if (n < 2)
11248 {
11249 qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11250 n = 2;
11251 }
11252 if (n != mLevelCount)
11253 {
11254 mLevelCount = n;
11256 }
11257}
11258
11269void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11270{
11273}
11274
11281void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11282{
11283 mColorStops.insert(position, color);
11285}
11286
11295{
11296 if (interpolation != mColorInterpolation)
11297 {
11298 mColorInterpolation = interpolation;
11300 }
11301}
11302
11319{
11320 mPeriodic = enabled;
11321}
11322
11335void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11336{
11337 // If you change something here, make sure to also adapt ::color()
11338 if (!data)
11339 {
11340 qDebug() << Q_FUNC_INFO << "null pointer given as data";
11341 return;
11342 }
11343 if (!scanLine)
11344 {
11345 qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11346 return;
11347 }
11350
11351 if (!logarithmic)
11352 {
11353 const double posToIndexFactor = mLevelCount/range.size();
11354 if (mPeriodic)
11355 {
11356 for (int i=0; i<n; ++i)
11357 {
11358 int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11359 if (index < 0)
11360 index += mLevelCount;
11361 scanLine[i] = mColorBuffer.at(index);
11362 }
11363 } else
11364 {
11365 for (int i=0; i<n; ++i)
11366 {
11367 int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11368 if (index < 0)
11369 index = 0;
11370 else if (index >= mLevelCount)
11371 index = mLevelCount-1;
11372 scanLine[i] = mColorBuffer.at(index);
11373 }
11374 }
11375 } else // logarithmic == true
11376 {
11377 if (mPeriodic)
11378 {
11379 for (int i=0; i<n; ++i)
11380 {
11381 int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*mLevelCount) % mLevelCount;
11382 if (index < 0)
11383 index += mLevelCount;
11384 scanLine[i] = mColorBuffer.at(index);
11385 }
11386 } else
11387 {
11388 for (int i=0; i<n; ++i)
11389 {
11390 int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*mLevelCount;
11391 if (index < 0)
11392 index = 0;
11393 else if (index >= mLevelCount)
11394 index = mLevelCount-1;
11395 scanLine[i] = mColorBuffer.at(index);
11396 }
11397 }
11398 }
11399}
11400
11410QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11411{
11412 // If you change something here, make sure to also adapt ::colorize()
11415 int index = 0;
11416 if (!logarithmic)
11417 index = (position-range.lower)*mLevelCount/range.size();
11418 else
11419 index = qLn(position/range.lower)/qLn(range.upper/range.lower)*mLevelCount;
11420 if (mPeriodic)
11421 {
11422 index = index % mLevelCount;
11423 if (index < 0)
11424 index += mLevelCount;
11425 } else
11426 {
11427 if (index < 0)
11428 index = 0;
11429 else if (index >= mLevelCount)
11430 index = mLevelCount-1;
11431 }
11432 return mColorBuffer.at(index);
11433}
11434
11443{
11445 switch (preset)
11446 {
11447 case gpGrayscale:
11449 setColorStopAt(0, Qt::black);
11450 setColorStopAt(1, Qt::white);
11451 break;
11452 case gpHot:
11454 setColorStopAt(0, QColor(50, 0, 0));
11455 setColorStopAt(0.2, QColor(180, 10, 0));
11456 setColorStopAt(0.4, QColor(245, 50, 0));
11457 setColorStopAt(0.6, QColor(255, 150, 10));
11458 setColorStopAt(0.8, QColor(255, 255, 50));
11459 setColorStopAt(1, QColor(255, 255, 255));
11460 break;
11461 case gpCold:
11463 setColorStopAt(0, QColor(0, 0, 50));
11464 setColorStopAt(0.2, QColor(0, 10, 180));
11465 setColorStopAt(0.4, QColor(0, 50, 245));
11466 setColorStopAt(0.6, QColor(10, 150, 255));
11467 setColorStopAt(0.8, QColor(50, 255, 255));
11468 setColorStopAt(1, QColor(255, 255, 255));
11469 break;
11470 case gpNight:
11472 setColorStopAt(0, QColor(10, 20, 30));
11473 setColorStopAt(1, QColor(250, 255, 250));
11474 break;
11475 case gpCandy:
11477 setColorStopAt(0, QColor(0, 0, 255));
11478 setColorStopAt(1, QColor(255, 250, 250));
11479 break;
11480 case gpGeography:
11482 setColorStopAt(0, QColor(70, 170, 210));
11483 setColorStopAt(0.20, QColor(90, 160, 180));
11484 setColorStopAt(0.25, QColor(45, 130, 175));
11485 setColorStopAt(0.30, QColor(100, 140, 125));
11486 setColorStopAt(0.5, QColor(100, 140, 100));
11487 setColorStopAt(0.6, QColor(130, 145, 120));
11488 setColorStopAt(0.7, QColor(140, 130, 120));
11489 setColorStopAt(0.9, QColor(180, 190, 190));
11490 setColorStopAt(1, QColor(210, 210, 230));
11491 break;
11492 case gpIon:
11494 setColorStopAt(0, QColor(50, 10, 10));
11495 setColorStopAt(0.45, QColor(0, 0, 255));
11496 setColorStopAt(0.8, QColor(0, 255, 255));
11497 setColorStopAt(1, QColor(0, 255, 0));
11498 break;
11499 case gpThermal:
11501 setColorStopAt(0, QColor(0, 0, 50));
11502 setColorStopAt(0.15, QColor(20, 0, 120));
11503 setColorStopAt(0.33, QColor(200, 30, 140));
11504 setColorStopAt(0.6, QColor(255, 100, 0));
11505 setColorStopAt(0.85, QColor(255, 255, 40));
11506 setColorStopAt(1, QColor(255, 255, 255));
11507 break;
11508 case gpPolar:
11510 setColorStopAt(0, QColor(50, 255, 255));
11511 setColorStopAt(0.18, QColor(10, 70, 255));
11512 setColorStopAt(0.28, QColor(10, 10, 190));
11513 setColorStopAt(0.5, QColor(0, 0, 0));
11514 setColorStopAt(0.72, QColor(190, 10, 10));
11515 setColorStopAt(0.82, QColor(255, 70, 10));
11516 setColorStopAt(1, QColor(255, 255, 50));
11517 break;
11518 case gpSpectrum:
11520 setColorStopAt(0, QColor(50, 0, 50));
11521 setColorStopAt(0.15, QColor(0, 0, 255));
11522 setColorStopAt(0.35, QColor(0, 255, 255));
11523 setColorStopAt(0.6, QColor(255, 255, 0));
11524 setColorStopAt(0.75, QColor(255, 30, 0));
11525 setColorStopAt(1, QColor(50, 0, 0));
11526 break;
11527 case gpJet:
11529 setColorStopAt(0, QColor(0, 0, 100));
11530 setColorStopAt(0.15, QColor(0, 50, 255));
11531 setColorStopAt(0.35, QColor(0, 255, 255));
11532 setColorStopAt(0.65, QColor(255, 255, 0));
11533 setColorStopAt(0.85, QColor(255, 30, 0));
11534 setColorStopAt(1, QColor(100, 0, 0));
11535 break;
11536 case gpHues:
11538 setColorStopAt(0, QColor(255, 0, 0));
11539 setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11540 setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11541 setColorStopAt(1, QColor(255, 0, 0));
11542 break;
11543 }
11544}
11545
11552{
11553 mColorStops.clear();
11555}
11556
11564{
11565 QCPColorGradient result(*this);
11566 result.clearColorStops();
11567 for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11568 result.setColorStopAt(1.0-it.key(), it.value());
11569 return result;
11570}
11571
11578{
11579 if (mColorBuffer.size() != mLevelCount)
11580 mColorBuffer.resize(mLevelCount);
11581 if (mColorStops.size() > 1)
11582 {
11583 double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11584 for (int i=0; i<mLevelCount; ++i)
11585 {
11586 double position = i*indexToPosFactor;
11587 QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11588 if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11589 {
11590 mColorBuffer[i] = (it-1).value().rgb();
11591 } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11592 {
11593 mColorBuffer[i] = it.value().rgb();
11594 } else // position is in between stops (or on an intermediate stop), interpolate color
11595 {
11596 QMap<double, QColor>::const_iterator high = it;
11597 QMap<double, QColor>::const_iterator low = it-1;
11598 double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11599 switch (mColorInterpolation)
11600 {
11601 case ciRGB:
11602 {
11603 mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11604 (1-t)*low.value().green() + t*high.value().green(),
11605 (1-t)*low.value().blue() + t*high.value().blue());
11606 break;
11607 }
11608 case ciHSV:
11609 {
11610 QColor lowHsv = low.value().toHsv();
11611 QColor highHsv = high.value().toHsv();
11612 double hue = 0;
11613 double hueDiff = highHsv.hueF()-lowHsv.hueF();
11614 if (hueDiff > 0.5)
11615 hue = lowHsv.hueF() - t*(1.0-hueDiff);
11616 else if (hueDiff < -0.5)
11617 hue = lowHsv.hueF() + t*(1.0+hueDiff);
11618 else
11619 hue = lowHsv.hueF() + t*hueDiff;
11620 if (hue < 0) hue += 1.0;
11621 else if (hue >= 1.0) hue -= 1.0;
11622 mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11623 break;
11624 }
11625 }
11626 }
11627 }
11628 } else if (mColorStops.size() == 1)
11629 {
11630 mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11631 } else // mColorStops is empty, fill color buffer with black
11632 {
11633 mColorBuffer.fill(qRgb(0, 0, 0));
11634 }
11636}
11637
11638
11642
11680/* start documentation of inline functions */
11681
11762/* end documentation of inline functions */
11763
11768QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11769 QCPLayoutElement(parentPlot),
11770 mBackgroundBrush(Qt::NoBrush),
11771 mBackgroundScaled(true),
11772 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11773 mInsetLayout(new QCPLayoutInset),
11774 mRangeDrag(Qt::Horizontal|Qt::Vertical),
11775 mRangeZoom(Qt::Horizontal|Qt::Vertical),
11776 mRangeZoomFactorHorz(0.85),
11777 mRangeZoomFactorVert(0.85),
11778 mDragging(false)
11779{
11782 mInsetLayout->setParent(this);
11783
11784 setMinimumSize(50, 50);
11785 setMinimumMargins(QMargins(15, 15, 15, 15));
11786 mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11787 mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11788 mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11789 mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11790
11791 if (setupDefaultAxes)
11792 {
11795 QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11796 QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11797 setRangeDragAxes(xAxis, yAxis);
11798 setRangeZoomAxes(xAxis, yAxis);
11799 xAxis2->setVisible(false);
11800 yAxis2->setVisible(false);
11801 xAxis->grid()->setVisible(true);
11802 yAxis->grid()->setVisible(true);
11803 xAxis2->grid()->setVisible(false);
11804 yAxis2->grid()->setVisible(false);
11805 xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11806 yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11807 xAxis2->grid()->setVisible(false);
11808 yAxis2->grid()->setVisible(false);
11809 }
11810}
11811
11813{
11814 delete mInsetLayout;
11815 mInsetLayout = 0;
11816
11817 QList<QCPAxis*> axesList = axes();
11818 for (int i=0; i<axesList.size(); ++i)
11819 removeAxis(axesList.at(i));
11820}
11821
11828{
11829 return mAxes.value(type).size();
11830}
11831
11838{
11839 QList<QCPAxis*> ax(mAxes.value(type));
11840 if (index >= 0 && index < ax.size())
11841 {
11842 return ax.at(index);
11843 } else
11844 {
11845 qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11846 return 0;
11847 }
11848}
11849
11858QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11859{
11860 QList<QCPAxis*> result;
11861 if (types.testFlag(QCPAxis::atLeft))
11862 result << mAxes.value(QCPAxis::atLeft);
11863 if (types.testFlag(QCPAxis::atRight))
11864 result << mAxes.value(QCPAxis::atRight);
11865 if (types.testFlag(QCPAxis::atTop))
11866 result << mAxes.value(QCPAxis::atTop);
11867 if (types.testFlag(QCPAxis::atBottom))
11868 result << mAxes.value(QCPAxis::atBottom);
11869 return result;
11870}
11871
11876QList<QCPAxis*> QCPAxisRect::axes() const
11877{
11878 QList<QCPAxis*> result;
11879 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11880 while (it.hasNext())
11881 {
11882 it.next();
11883 result << it.value();
11884 }
11885 return result;
11886}
11887
11898{
11899 QCPAxis *newAxis = new QCPAxis(this, type);
11900 if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11901 {
11902 bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11903 newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11904 newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11905 }
11906 mAxes[type].append(newAxis);
11907 return newAxis;
11908}
11909
11918QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11919{
11920 QList<QCPAxis*> result;
11921 if (types.testFlag(QCPAxis::atLeft))
11922 result << addAxis(QCPAxis::atLeft);
11923 if (types.testFlag(QCPAxis::atRight))
11924 result << addAxis(QCPAxis::atRight);
11925 if (types.testFlag(QCPAxis::atTop))
11926 result << addAxis(QCPAxis::atTop);
11927 if (types.testFlag(QCPAxis::atBottom))
11928 result << addAxis(QCPAxis::atBottom);
11929 return result;
11930}
11931
11940{
11941 // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11942 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11943 while (it.hasNext())
11944 {
11945 it.next();
11946 if (it.value().contains(axis))
11947 {
11948 mAxes[it.key()].removeOne(axis);
11949 if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
11951 delete axis;
11952 return true;
11953 }
11954 }
11955 qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11956 return false;
11957}
11958
11985void QCPAxisRect::setupFullAxesBox(bool connectRanges)
11986{
11987 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
11988 if (axisCount(QCPAxis::atBottom) == 0)
11989 xAxis = addAxis(QCPAxis::atBottom);
11990 else
11991 xAxis = axis(QCPAxis::atBottom);
11992
11993 if (axisCount(QCPAxis::atLeft) == 0)
11994 yAxis = addAxis(QCPAxis::atLeft);
11995 else
11996 yAxis = axis(QCPAxis::atLeft);
11997
11998 if (axisCount(QCPAxis::atTop) == 0)
11999 xAxis2 = addAxis(QCPAxis::atTop);
12000 else
12001 xAxis2 = axis(QCPAxis::atTop);
12002
12003 if (axisCount(QCPAxis::atRight) == 0)
12004 yAxis2 = addAxis(QCPAxis::atRight);
12005 else
12006 yAxis2 = axis(QCPAxis::atRight);
12007
12008 xAxis->setVisible(true);
12009 yAxis->setVisible(true);
12010 xAxis2->setVisible(true);
12011 yAxis2->setVisible(true);
12012 xAxis2->setTickLabels(false);
12013 yAxis2->setTickLabels(false);
12014
12015 xAxis2->setRange(xAxis->range());
12016 xAxis2->setRangeReversed(xAxis->rangeReversed());
12017 xAxis2->setScaleType(xAxis->scaleType());
12018 xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12019 xAxis2->setTicks(xAxis->ticks());
12020 xAxis2->setAutoTickCount(xAxis->autoTickCount());
12021 xAxis2->setSubTickCount(xAxis->subTickCount());
12022 xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12023 xAxis2->setTickStep(xAxis->tickStep());
12024 xAxis2->setAutoTickStep(xAxis->autoTickStep());
12025 xAxis2->setNumberFormat(xAxis->numberFormat());
12026 xAxis2->setNumberPrecision(xAxis->numberPrecision());
12027 xAxis2->setTickLabelType(xAxis->tickLabelType());
12028 xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12029 xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12030
12031 yAxis2->setRange(yAxis->range());
12032 yAxis2->setRangeReversed(yAxis->rangeReversed());
12033 yAxis2->setScaleType(yAxis->scaleType());
12034 yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12035 yAxis2->setTicks(yAxis->ticks());
12036 yAxis2->setAutoTickCount(yAxis->autoTickCount());
12037 yAxis2->setSubTickCount(yAxis->subTickCount());
12038 yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12039 yAxis2->setTickStep(yAxis->tickStep());
12040 yAxis2->setAutoTickStep(yAxis->autoTickStep());
12041 yAxis2->setNumberFormat(yAxis->numberFormat());
12042 yAxis2->setNumberPrecision(yAxis->numberPrecision());
12043 yAxis2->setTickLabelType(yAxis->tickLabelType());
12044 yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12045 yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12046
12047 if (connectRanges)
12048 {
12049 connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12050 connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12051 }
12052}
12053
12062QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12063{
12064 // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12065 QList<QCPAbstractPlottable*> result;
12066 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12067 {
12068 if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12069 result.append(mParentPlot->mPlottables.at(i));
12070 }
12071 return result;
12072}
12073
12082QList<QCPGraph*> QCPAxisRect::graphs() const
12083{
12084 // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12085 QList<QCPGraph*> result;
12086 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12087 {
12088 if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12089 result.append(mParentPlot->mGraphs.at(i));
12090 }
12091 return result;
12092}
12093
12104QList<QCPAbstractItem *> QCPAxisRect::items() const
12105{
12106 // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12107 // and miss those items that have this axis rect as clipAxisRect.
12108 QList<QCPAbstractItem*> result;
12109 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12110 {
12111 if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12112 {
12113 result.append(mParentPlot->mItems.at(itemId));
12114 continue;
12115 }
12116 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12117 for (int posId=0; posId<positions.size(); ++posId)
12118 {
12119 if (positions.at(posId)->axisRect() == this ||
12120 positions.at(posId)->keyAxis()->axisRect() == this ||
12121 positions.at(posId)->valueAxis()->axisRect() == this)
12122 {
12123 result.append(mParentPlot->mItems.at(itemId));
12124 break;
12125 }
12126 }
12127 }
12128 return result;
12129}
12130
12140{
12142
12143 switch (phase)
12144 {
12145 case upPreparation:
12146 {
12147 QList<QCPAxis*> allAxes = axes();
12148 for (int i=0; i<allAxes.size(); ++i)
12149 allAxes.at(i)->setupTickVectors();
12150 break;
12151 }
12152 case upLayout:
12153 {
12155 break;
12156 }
12157 default: break;
12158 }
12159
12160 // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12161 mInsetLayout->update(phase);
12162}
12163
12164/* inherits documentation from base class */
12165QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12166{
12167 QList<QCPLayoutElement*> result;
12168 if (mInsetLayout)
12169 {
12170 result << mInsetLayout;
12171 if (recursive)
12172 result << mInsetLayout->elements(recursive);
12173 }
12174 return result;
12175}
12176
12177/* inherits documentation from base class */
12179{
12180 painter->setAntialiasing(false);
12181}
12182
12183/* inherits documentation from base class */
12185{
12186 drawBackground(painter);
12187}
12188
12204void QCPAxisRect::setBackground(const QPixmap &pm)
12205{
12206 mBackgroundPixmap = pm;
12207 mScaledBackgroundPixmap = QPixmap();
12208}
12209
12223void QCPAxisRect::setBackground(const QBrush &brush)
12224{
12225 mBackgroundBrush = brush;
12226}
12227
12235void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12236{
12237 mBackgroundPixmap = pm;
12238 mScaledBackgroundPixmap = QPixmap();
12239 mBackgroundScaled = scaled;
12240 mBackgroundScaledMode = mode;
12241}
12242
12254{
12255 mBackgroundScaled = scaled;
12256}
12257
12263void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12264{
12265 mBackgroundScaledMode = mode;
12266}
12267
12273QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12274{
12275 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12276}
12277
12283QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12284{
12285 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12286}
12287
12293double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12294{
12295 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12296}
12297
12314void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12315{
12316 mRangeDrag = orientations;
12317}
12318
12334void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12335{
12336 mRangeZoom = orientations;
12337}
12338
12346{
12347 mRangeDragHorzAxis = horizontal;
12348 mRangeDragVertAxis = vertical;
12349}
12350
12359{
12360 mRangeZoomHorzAxis = horizontal;
12361 mRangeZoomVertAxis = vertical;
12362}
12363
12374void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12375{
12376 mRangeZoomFactorHorz = horizontalFactor;
12377 mRangeZoomFactorVert = verticalFactor;
12378}
12379
12385{
12386 mRangeZoomFactorHorz = factor;
12387 mRangeZoomFactorVert = factor;
12388}
12389
12409{
12410 // draw background fill:
12411 if (mBackgroundBrush != Qt::NoBrush)
12412 painter->fillRect(mRect, mBackgroundBrush);
12413
12414 // draw background pixmap (on top of fill, if brush specified):
12415 if (!mBackgroundPixmap.isNull())
12416 {
12418 {
12419 // check whether mScaledBackground needs to be updated:
12420 QSize scaledSize(mBackgroundPixmap.size());
12421 scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12422 if (mScaledBackgroundPixmap.size() != scaledSize)
12423 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12424 painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12425 } else
12426 {
12427 painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12428 }
12429 }
12430}
12431
12443{
12444 const QList<QCPAxis*> axesList = mAxes.value(type);
12445 if (axesList.isEmpty())
12446 return;
12447
12448 bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
12449 for (int i=1; i<axesList.size(); ++i)
12450 {
12451 int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12452 if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
12453 {
12454 if (!isFirstVisible)
12455 offset += axesList.at(i)->tickLengthIn();
12456 isFirstVisible = false;
12457 }
12458 axesList.at(i)->setOffset(offset);
12459 }
12460}
12461
12462/* inherits documentation from base class */
12464{
12465 if (!mAutoMargins.testFlag(side))
12466 qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12467
12469
12470 // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12471 const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12472 if (axesList.size() > 0)
12473 return axesList.last()->offset() + axesList.last()->calculateMargin();
12474 else
12475 return 0;
12476}
12477
12489void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12490{
12491 mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
12492 if (event->buttons() & Qt::LeftButton)
12493 {
12494 mDragging = true;
12495 // initialize antialiasing backup in case we start dragging:
12497 {
12500 }
12501 // Mouse range dragging interaction:
12502 if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12503 {
12505 mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12507 mDragStartVertRange = mRangeDragVertAxis.data()->range();
12508 }
12509 }
12510}
12511
12519void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12520{
12521 // Mouse range dragging interaction:
12523 {
12524 if (mRangeDrag.testFlag(Qt::Horizontal))
12525 {
12526 if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12527 {
12528 if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12529 {
12530 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12531 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12532 } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12533 {
12534 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12535 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12536 }
12537 }
12538 }
12539 if (mRangeDrag.testFlag(Qt::Vertical))
12540 {
12541 if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12542 {
12543 if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12544 {
12545 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12546 rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12547 } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12548 {
12549 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12550 rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12551 }
12552 }
12553 }
12554 if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12555 {
12559 }
12560 }
12561}
12562
12563/* inherits documentation from base class */
12564void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12565{
12566 Q_UNUSED(event)
12567 mDragging = false;
12569 {
12572 }
12573}
12574
12589void QCPAxisRect::wheelEvent(QWheelEvent *event)
12590{
12591 // Mouse range zooming interaction:
12592 if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12593 {
12594 if (mRangeZoom != 0)
12595 {
12596 double factor;
12597 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12598 if (mRangeZoom.testFlag(Qt::Horizontal))
12599 {
12600 factor = pow(mRangeZoomFactorHorz, wheelSteps);
12601 if (mRangeZoomHorzAxis.data())
12602 mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12603 }
12604 if (mRangeZoom.testFlag(Qt::Vertical))
12605 {
12606 factor = pow(mRangeZoomFactorVert, wheelSteps);
12607 if (mRangeZoomVertAxis.data())
12608 mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12609 }
12611 }
12612 }
12613}
12614
12615
12619
12644/* start of documentation of signals */
12645
12652/* end of documentation of signals */
12653
12659 QCPLayoutElement(parent->parentPlot()),
12660 mParentLegend(parent),
12661 mFont(parent->font()),
12662 mTextColor(parent->textColor()),
12663 mSelectedFont(parent->selectedFont()),
12664 mSelectedTextColor(parent->selectedTextColor()),
12665 mSelectable(true),
12666 mSelected(false)
12667{
12668 setLayer("legend");
12669 setMargins(QMargins(8, 2, 8, 2));
12670}
12671
12677void QCPAbstractLegendItem::setFont(const QFont &font)
12678{
12679 mFont = font;
12680}
12681
12688{
12689 mTextColor = color;
12690}
12691
12699{
12701}
12702
12710{
12711 mSelectedTextColor = color;
12712}
12713
12720{
12721 if (mSelectable != selectable)
12722 {
12725 }
12726}
12727
12737{
12738 if (mSelected != selected)
12739 {
12742 }
12743}
12744
12745/* inherits documentation from base class */
12746double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12747{
12748 Q_UNUSED(details)
12749 if (!mParentPlot) return -1;
12750 if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12751 return -1;
12752
12753 if (mRect.contains(pos.toPoint()))
12754 return mParentPlot->selectionTolerance()*0.99;
12755 else
12756 return -1;
12757}
12758
12759/* inherits documentation from base class */
12764
12765/* inherits documentation from base class */
12767{
12768 return mOuterRect;
12769}
12770
12771/* inherits documentation from base class */
12772void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12773{
12774 Q_UNUSED(event)
12775 Q_UNUSED(details)
12777 {
12778 bool selBefore = mSelected;
12779 setSelected(additive ? !mSelected : true);
12780 if (selectionStateChanged)
12781 *selectionStateChanged = mSelected != selBefore;
12782 }
12783}
12784
12785/* inherits documentation from base class */
12786void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12787{
12789 {
12790 bool selBefore = mSelected;
12791 setSelected(false);
12792 if (selectionStateChanged)
12793 *selectionStateChanged = mSelected != selBefore;
12794 }
12795}
12796
12800
12835 QCPAbstractLegendItem(parent),
12836 mPlottable(plottable)
12837{
12838}
12839
12849
12856{
12858}
12859
12866{
12867 return mSelected ? mSelectedFont : mFont;
12868}
12869
12877{
12878 if (!mPlottable) return;
12879 painter->setFont(getFont());
12880 painter->setPen(QPen(getTextColor()));
12881 QSizeF iconSize = mParentLegend->iconSize();
12882 QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12883 QRectF iconRect(mRect.topLeft(), iconSize);
12884 int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12885 painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12886 // draw icon:
12887 painter->save();
12888 painter->setClipRect(iconRect, Qt::IntersectClip);
12889 mPlottable->drawLegendIcon(painter, iconRect);
12890 painter->restore();
12891 // draw icon border:
12892 if (getIconBorderPen().style() != Qt::NoPen)
12893 {
12894 painter->setPen(getIconBorderPen());
12895 painter->setBrush(Qt::NoBrush);
12896 painter->drawRect(iconRect);
12897 }
12898}
12899
12906{
12907 if (!mPlottable) return QSize();
12908 QSize result(0, 0);
12909 QRect textRect;
12910 QFontMetrics fontMetrics(getFont());
12911 QSize iconSize = mParentLegend->iconSize();
12912 textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12913 result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12914 result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12915 return result;
12916}
12917
12918
12922
12947/* start of documentation of signals */
12948
12956/* end of documentation of signals */
12957
12965{
12966 setRowSpacing(0);
12967 setColumnSpacing(10);
12968 setMargins(QMargins(2, 3, 2, 2));
12969 setAntialiased(false);
12970 setIconSize(32, 18);
12971
12973
12976
12977 setBorderPen(QPen(Qt::black));
12978 setSelectedBorderPen(QPen(Qt::blue, 2));
12979 setIconBorderPen(Qt::NoPen);
12980 setSelectedIconBorderPen(QPen(Qt::blue, 2));
12981 setBrush(Qt::white);
12982 setSelectedBrush(Qt::white);
12983 setTextColor(Qt::black);
12984 setSelectedTextColor(Qt::blue);
12985}
12986
12988{
12989 clearItems();
12990 if (mParentPlot)
12992}
12993
12994/* no doc for getter, see setSelectedParts */
12995QCPLegend::SelectableParts QCPLegend::selectedParts() const
12996{
12997 // check whether any legend elements selected, if yes, add spItems to return value
12998 bool hasSelectedItems = false;
12999 for (int i=0; i<itemCount(); ++i)
13000 {
13001 if (item(i) && item(i)->selected())
13002 {
13003 hasSelectedItems = true;
13004 break;
13005 }
13006 }
13007 if (hasSelectedItems)
13008 return mSelectedParts | spItems;
13009 else
13010 return mSelectedParts & ~spItems;
13011}
13012
13016void QCPLegend::setBorderPen(const QPen &pen)
13017{
13018 mBorderPen = pen;
13019}
13020
13024void QCPLegend::setBrush(const QBrush &brush)
13025{
13026 mBrush = brush;
13027}
13028
13038void QCPLegend::setFont(const QFont &font)
13039{
13040 mFont = font;
13041 for (int i=0; i<itemCount(); ++i)
13042 {
13043 if (item(i))
13044 item(i)->setFont(mFont);
13045 }
13046}
13047
13057void QCPLegend::setTextColor(const QColor &color)
13058{
13059 mTextColor = color;
13060 for (int i=0; i<itemCount(); ++i)
13061 {
13062 if (item(i))
13063 item(i)->setTextColor(color);
13064 }
13065}
13066
13071void QCPLegend::setIconSize(const QSize &size)
13072{
13073 mIconSize = size;
13074}
13075
13078void QCPLegend::setIconSize(int width, int height)
13079{
13080 mIconSize.setWidth(width);
13081 mIconSize.setHeight(height);
13082}
13083
13090{
13091 mIconTextPadding = padding;
13092}
13093
13100void QCPLegend::setIconBorderPen(const QPen &pen)
13101{
13102 mIconBorderPen = pen;
13103}
13104
13115void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13116{
13117 if (mSelectableParts != selectable)
13118 {
13119 mSelectableParts = selectable;
13121 }
13122}
13123
13145void QCPLegend::setSelectedParts(const SelectableParts &selected)
13146{
13147 SelectableParts newSelected = selected;
13148 mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13149
13150 if (mSelectedParts != newSelected)
13151 {
13152 if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13153 {
13154 qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13155 newSelected &= ~spItems;
13156 }
13157 if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13158 {
13159 for (int i=0; i<itemCount(); ++i)
13160 {
13161 if (item(i))
13162 item(i)->setSelected(false);
13163 }
13164 }
13165 mSelectedParts = newSelected;
13167 }
13168}
13169
13177{
13178 mSelectedBorderPen = pen;
13179}
13180
13187{
13189}
13190
13197void QCPLegend::setSelectedBrush(const QBrush &brush)
13198{
13200}
13201
13209void QCPLegend::setSelectedFont(const QFont &font)
13210{
13212 for (int i=0; i<itemCount(); ++i)
13213 {
13214 if (item(i))
13216 }
13217}
13218
13226void QCPLegend::setSelectedTextColor(const QColor &color)
13227{
13228 mSelectedTextColor = color;
13229 for (int i=0; i<itemCount(); ++i)
13230 {
13231 if (item(i))
13232 item(i)->setSelectedTextColor(color);
13233 }
13234}
13235
13242{
13243 return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13244}
13245
13253{
13254 for (int i=0; i<itemCount(); ++i)
13255 {
13256 if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13257 {
13258 if (pli->plottable() == plottable)
13259 return pli;
13260 }
13261 }
13262 return 0;
13263}
13264
13270{
13271 return elementCount();
13272}
13273
13278{
13279 for (int i=0; i<itemCount(); ++i)
13280 {
13281 if (item == this->item(i))
13282 return true;
13283 }
13284 return false;
13285}
13286
13294{
13295 return itemWithPlottable(plottable);
13296}
13297
13306{
13307 if (!hasItem(item))
13308 {
13309 return addElement(rowCount(), 0, item);
13310 } else
13311 return false;
13312}
13313
13322{
13323 if (QCPAbstractLegendItem *ali = item(index))
13324 {
13325 bool success = remove(ali);
13326 simplify();
13327 return success;
13328 } else
13329 return false;
13330}
13331
13341{
13342 bool success = remove(item);
13343 simplify();
13344 return success;
13345}
13346
13351{
13352 for (int i=itemCount()-1; i>=0; --i)
13353 removeItem(i);
13354}
13355
13362QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13363{
13364 QList<QCPAbstractLegendItem*> result;
13365 for (int i=0; i<itemCount(); ++i)
13366 {
13367 if (QCPAbstractLegendItem *ali = item(i))
13368 {
13369 if (ali->selected())
13370 result.append(ali);
13371 }
13372 }
13373 return result;
13374}
13375
13393
13400{
13402}
13403
13410{
13411 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13412}
13413
13420{
13421 // draw background rect:
13422 painter->setBrush(getBrush());
13423 painter->setPen(getBorderPen());
13424 painter->drawRect(mOuterRect);
13425}
13426
13427/* inherits documentation from base class */
13428double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13429{
13430 if (!mParentPlot) return -1;
13431 if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13432 return -1;
13433
13434 if (mOuterRect.contains(pos.toPoint()))
13435 {
13436 if (details) details->setValue(spLegendBox);
13437 return mParentPlot->selectionTolerance()*0.99;
13438 }
13439 return -1;
13440}
13441
13442/* inherits documentation from base class */
13443void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13444{
13445 Q_UNUSED(event)
13446 mSelectedParts = selectedParts(); // in case item selection has changed
13447 if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13448 {
13449 SelectableParts selBefore = mSelectedParts;
13450 setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
13451 if (selectionStateChanged)
13452 *selectionStateChanged = mSelectedParts != selBefore;
13453 }
13454}
13455
13456/* inherits documentation from base class */
13457void QCPLegend::deselectEvent(bool *selectionStateChanged)
13458{
13459 mSelectedParts = selectedParts(); // in case item selection has changed
13460 if (mSelectableParts.testFlag(spLegendBox))
13461 {
13462 SelectableParts selBefore = mSelectedParts;
13464 if (selectionStateChanged)
13465 *selectionStateChanged = mSelectedParts != selBefore;
13466 }
13467}
13468
13469/* inherits documentation from base class */
13474
13475/* inherits documentation from base class */
13480
13481/* inherits documentation from base class */
13483{
13484 Q_UNUSED(parentPlot)
13485}
13486
13487
13491
13510/* start documentation of signals */
13511
13520/* end documentation of signals */
13521
13528 QCPLayoutElement(parentPlot),
13529 mFont(QFont("sans serif", 13*1.5, QFont::Bold)),
13530 mTextColor(Qt::black),
13531 mSelectedFont(QFont("sans serif", 13*1.6, QFont::Bold)),
13532 mSelectedTextColor(Qt::blue),
13533 mSelectable(false),
13534 mSelected(false)
13535{
13536 if (parentPlot)
13537 {
13539 mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13540 mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13541 }
13542 setMargins(QMargins(5, 5, 5, 0));
13543}
13544
13549QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13550 QCPLayoutElement(parentPlot),
13551 mText(text),
13552 mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13553 mTextColor(Qt::black),
13554 mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13555 mSelectedTextColor(Qt::blue),
13556 mSelectable(false),
13557 mSelected(false)
13558{
13559 setLayer("axes");
13560 setMargins(QMargins(5, 5, 5, 0));
13561}
13562
13568void QCPPlotTitle::setText(const QString &text)
13569{
13570 mText = text;
13571}
13572
13578void QCPPlotTitle::setFont(const QFont &font)
13579{
13580 mFont = font;
13581}
13582
13588void QCPPlotTitle::setTextColor(const QColor &color)
13589{
13590 mTextColor = color;
13591}
13592
13598void QCPPlotTitle::setSelectedFont(const QFont &font)
13599{
13601}
13602
13608void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13609{
13610 mSelectedTextColor = color;
13611}
13612
13619void QCPPlotTitle::setSelectable(bool selectable)
13620{
13621 if (mSelectable != selectable)
13622 {
13625 }
13626}
13627
13635void QCPPlotTitle::setSelected(bool selected)
13636{
13637 if (mSelected != selected)
13638 {
13641 }
13642}
13643
13644/* inherits documentation from base class */
13649
13650/* inherits documentation from base class */
13652{
13653 painter->setFont(mainFont());
13654 painter->setPen(QPen(mainTextColor()));
13655 painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13656}
13657
13658/* inherits documentation from base class */
13660{
13661 QFontMetrics metrics(mFont);
13662 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13663 result.rwidth() += mMargins.left() + mMargins.right();
13664 result.rheight() += mMargins.top() + mMargins.bottom();
13665 return result;
13666}
13667
13668/* inherits documentation from base class */
13670{
13671 QFontMetrics metrics(mFont);
13672 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13673 result.rheight() += mMargins.top() + mMargins.bottom();
13674 result.setWidth(QWIDGETSIZE_MAX);
13675 return result;
13676}
13677
13678/* inherits documentation from base class */
13679void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13680{
13681 Q_UNUSED(event)
13682 Q_UNUSED(details)
13683 if (mSelectable)
13684 {
13685 bool selBefore = mSelected;
13686 setSelected(additive ? !mSelected : true);
13687 if (selectionStateChanged)
13688 *selectionStateChanged = mSelected != selBefore;
13689 }
13690}
13691
13692/* inherits documentation from base class */
13693void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13694{
13695 if (mSelectable)
13696 {
13697 bool selBefore = mSelected;
13698 setSelected(false);
13699 if (selectionStateChanged)
13700 *selectionStateChanged = mSelected != selBefore;
13701 }
13702}
13703
13704/* inherits documentation from base class */
13705double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13706{
13707 Q_UNUSED(details)
13708 if (onlySelectable && !mSelectable)
13709 return -1;
13710
13711 if (mTextBoundingRect.contains(pos.toPoint()))
13712 return mParentPlot->selectionTolerance()*0.99;
13713 else
13714 return -1;
13715}
13716
13723{
13724 return mSelected ? mSelectedFont : mFont;
13725}
13726
13733{
13735}
13736
13737
13741
13791/* start documentation of inline functions */
13792
13806/* end documentation of signals */
13807/* start documentation of signals */
13808
13830/* end documentation of signals */
13831
13836 QCPLayoutElement(parentPlot),
13837 mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13838 mDataScaleType(QCPAxis::stLinear),
13839 mBarWidth(20),
13840 mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13841{
13842 setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
13844 setDataRange(QCPRange(0, 6));
13845}
13846
13848{
13849 delete mAxisRect;
13850}
13851
13852/* undocumented getter */
13854{
13855 if (!mColorAxis)
13856 {
13857 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13858 return QString();
13859 }
13860
13861 return mColorAxis.data()->label();
13862}
13863
13864/* undocumented getter */
13866{
13867 if (!mAxisRect)
13868 {
13869 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13870 return false;
13871 }
13872
13873 return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13874 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13875 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13876}
13877
13878/* undocumented getter */
13880{
13881 if (!mAxisRect)
13882 {
13883 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13884 return false;
13885 }
13886
13887 return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13888 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13889 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13890}
13891
13900{
13901 if (!mAxisRect)
13902 {
13903 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13904 return;
13905 }
13906 if (mType != type)
13907 {
13908 mType = type;
13909 QCPRange rangeTransfer(0, 6);
13910 double logBaseTransfer = 10;
13911 QString labelTransfer;
13912 // revert some settings on old axis:
13913 if (mColorAxis)
13914 {
13915 rangeTransfer = mColorAxis.data()->range();
13916 labelTransfer = mColorAxis.data()->label();
13917 logBaseTransfer = mColorAxis.data()->scaleLogBase();
13918 mColorAxis.data()->setLabel("");
13919 disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13920 disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13921 }
13922 foreach (QCPAxis::AxisType atype, QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop)
13923 {
13924 mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13925 mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13926 }
13927 // set new mColorAxis pointer:
13928 mColorAxis = mAxisRect.data()->axis(mType);
13929 // transfer settings to new axis:
13930 mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13931 mColorAxis.data()->setLabel(labelTransfer);
13932 mColorAxis.data()->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here
13933 connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13934 connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13935 mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13936 QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13937 }
13938}
13939
13950{
13952 {
13954 if (mColorAxis)
13955 mColorAxis.data()->setRange(mDataRange);
13957 }
13958}
13959
13971{
13972 if (mDataScaleType != scaleType)
13973 {
13974 mDataScaleType = scaleType;
13975 if (mColorAxis)
13976 mColorAxis.data()->setScaleType(mDataScaleType);
13980 }
13981}
13982
13991{
13992 if (mGradient != gradient)
13993 {
13995 if (mAxisRect)
13996 mAxisRect.data()->mGradientImageInvalidated = true;
13998 }
13999}
14000
14005void QCPColorScale::setLabel(const QString &str)
14006{
14007 if (!mColorAxis)
14008 {
14009 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14010 return;
14011 }
14012
14013 mColorAxis.data()->setLabel(str);
14014}
14015
14021{
14022 mBarWidth = width;
14023}
14024
14032{
14033 if (!mAxisRect)
14034 {
14035 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14036 return;
14037 }
14038
14039 if (enabled)
14040 mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14041 else
14042 mAxisRect.data()->setRangeDrag(0);
14043}
14044
14052{
14053 if (!mAxisRect)
14054 {
14055 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14056 return;
14057 }
14058
14059 if (enabled)
14060 mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14061 else
14062 mAxisRect.data()->setRangeZoom(0);
14063}
14064
14068QList<QCPColorMap*> QCPColorScale::colorMaps() const
14069{
14070 QList<QCPColorMap*> result;
14071 for (int i=0; i<mParentPlot->plottableCount(); ++i)
14072 {
14073 if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14074 if (cm->colorScale() == this)
14075 result.append(cm);
14076 }
14077 return result;
14078}
14079
14086void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14087{
14088 QList<QCPColorMap*> maps = colorMaps();
14089 QCPRange newRange;
14090 bool haveRange = false;
14091 int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14093 sign = (mDataRange.upper < 0 ? -1 : 1);
14094 for (int i=0; i<maps.size(); ++i)
14095 {
14096 if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14097 continue;
14098 QCPRange mapRange;
14099 if (maps.at(i)->colorScale() == this)
14100 {
14101 bool currentFoundRange = true;
14102 mapRange = maps.at(i)->data()->dataBounds();
14103 if (sign == 1)
14104 {
14105 if (mapRange.lower <= 0 && mapRange.upper > 0)
14106 mapRange.lower = mapRange.upper*1e-3;
14107 else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14108 currentFoundRange = false;
14109 } else if (sign == -1)
14110 {
14111 if (mapRange.upper >= 0 && mapRange.lower < 0)
14112 mapRange.upper = mapRange.lower*1e-3;
14113 else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14114 currentFoundRange = false;
14115 }
14116 if (currentFoundRange)
14117 {
14118 if (!haveRange)
14119 newRange = mapRange;
14120 else
14121 newRange.expand(mapRange);
14122 haveRange = true;
14123 }
14124 }
14125 }
14126 if (haveRange)
14127 {
14128 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
14129 {
14130 double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
14132 {
14133 newRange.lower = center-mDataRange.size()/2.0;
14134 newRange.upper = center+mDataRange.size()/2.0;
14135 } else // mScaleType == stLogarithmic
14136 {
14137 newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14138 newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14139 }
14140 }
14141 setDataRange(newRange);
14142 }
14143}
14144
14145/* inherits documentation from base class */
14147{
14149 if (!mAxisRect)
14150 {
14151 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14152 return;
14153 }
14154
14155 mAxisRect.data()->update(phase);
14156
14157 switch (phase)
14158 {
14159 case upMargins:
14160 {
14162 {
14163 setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14164 setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14165 } else
14166 {
14167 setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14168 setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14169 }
14170 break;
14171 }
14172 case upLayout:
14173 {
14174 mAxisRect.data()->setOuterRect(rect());
14175 break;
14176 }
14177 default: break;
14178 }
14179}
14180
14181/* inherits documentation from base class */
14183{
14184 painter->setAntialiasing(false);
14185}
14186
14187/* inherits documentation from base class */
14188void QCPColorScale::mousePressEvent(QMouseEvent *event)
14189{
14190 if (!mAxisRect)
14191 {
14192 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14193 return;
14194 }
14195 mAxisRect.data()->mousePressEvent(event);
14196}
14197
14198/* inherits documentation from base class */
14199void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14200{
14201 if (!mAxisRect)
14202 {
14203 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14204 return;
14205 }
14206 mAxisRect.data()->mouseMoveEvent(event);
14207}
14208
14209/* inherits documentation from base class */
14210void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14211{
14212 if (!mAxisRect)
14213 {
14214 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14215 return;
14216 }
14217 mAxisRect.data()->mouseReleaseEvent(event);
14218}
14219
14220/* inherits documentation from base class */
14221void QCPColorScale::wheelEvent(QWheelEvent *event)
14222{
14223 if (!mAxisRect)
14224 {
14225 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14226 return;
14227 }
14228 mAxisRect.data()->wheelEvent(event);
14229}
14230
14234
14250 QCPAxisRect(parentColorScale->parentPlot(), true),
14251 mParentColorScale(parentColorScale),
14252 mGradientImageInvalidated(true)
14253{
14254 setParentLayerable(parentColorScale);
14255 setMinimumMargins(QMargins(0, 0, 0, 0));
14256 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14257 {
14258 axis(type)->setVisible(true);
14259 axis(type)->grid()->setVisible(false);
14260 axis(type)->setPadding(0);
14261 connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14262 connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14263 }
14264
14265 connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14266 connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14267 connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14268 connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14269 connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14270 connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14271 connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14272 connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14273
14274 // make layer transfers of color scale transfer to axis rect and axes
14275 // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14276 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14277 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14278 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14279}
14280
14286{
14289
14290 bool mirrorHorz = false;
14291 bool mirrorVert = false;
14293 {
14294 mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14295 mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14296 }
14297
14298 painter->drawImage(rect(), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14299 QCPAxisRect::draw(painter);
14300}
14301
14308{
14309 if (rect().isEmpty())
14310 return;
14311
14313 int w, h;
14314 QVector<double> data(n);
14315 for (int i=0; i<n; ++i)
14316 data[i] = i;
14318 {
14319 w = n;
14320 h = rect().height();
14321 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14322 QVector<QRgb*> pixels;
14323 for (int y=0; y<h; ++y)
14324 pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14325 mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14326 for (int y=1; y<h; ++y)
14327 memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14328 } else
14329 {
14330 w = rect().width();
14331 h = n;
14332 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14333 for (int y=0; y<h; ++y)
14334 {
14335 QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14336 const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14337 for (int x=0; x<w; ++x)
14338 pixels[x] = lineColor;
14339 }
14340 }
14342}
14343
14349void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14350{
14351 // axis bases of four axes shall always (de-)selected synchronously:
14352 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14353 {
14354 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14355 if (senderAxis->axisType() == type)
14356 continue;
14357
14358 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14359 {
14360 if (selectedParts.testFlag(QCPAxis::spAxis))
14361 axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14362 else
14363 axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14364 }
14365 }
14366}
14367
14373void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14374{
14375 // synchronize axis base selectability:
14376 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14377 {
14378 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14379 if (senderAxis->axisType() == type)
14380 continue;
14381
14382 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14383 {
14384 if (selectableParts.testFlag(QCPAxis::spAxis))
14385 axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14386 else
14387 axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14388 }
14389 }
14390}
14391
14392
14396
14417 key(0),
14418 value(0),
14419 keyErrorPlus(0),
14420 keyErrorMinus(0),
14421 valueErrorPlus(0),
14422 valueErrorMinus(0)
14423{
14424}
14425
14429QCPData::QCPData(double key, double value) :
14430 key(key),
14431 value(value),
14432 keyErrorPlus(0),
14433 keyErrorMinus(0),
14434 valueErrorPlus(0),
14435 valueErrorMinus(0)
14436{
14437}
14438
14439
14443
14478/* start of documentation of inline functions */
14479
14487/* end of documentation of inline functions */
14488
14500QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14501 QCPAbstractPlottable(keyAxis, valueAxis)
14502{
14503 mData = new QCPDataMap;
14504
14505 setPen(QPen(Qt::blue, 0));
14506 setErrorPen(QPen(Qt::black));
14507 setBrush(Qt::NoBrush);
14508 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14509 setSelectedBrush(Qt::NoBrush);
14510
14513 setErrorBarSize(6);
14516 setAdaptiveSampling(true);
14517}
14518
14520{
14521 delete mData;
14522}
14523
14535{
14536 if (copy)
14537 {
14538 *mData = *data;
14539 } else
14540 {
14541 delete mData;
14542 mData = data;
14543 }
14544}
14545
14552void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14553{
14554 mData->clear();
14555 int n = key.size();
14556 n = qMin(n, value.size());
14557 QCPData newData;
14558 for (int i=0; i<n; ++i)
14559 {
14560 newData.key = key[i];
14561 newData.value = value[i];
14562 mData->insertMulti(newData.key, newData);
14563 }
14564}
14565
14575void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14576{
14577 mData->clear();
14578 int n = key.size();
14579 n = qMin(n, value.size());
14580 n = qMin(n, valueError.size());
14581 QCPData newData;
14582 for (int i=0; i<n; ++i)
14583 {
14584 newData.key = key[i];
14585 newData.value = value[i];
14586 newData.valueErrorMinus = valueError[i];
14587 newData.valueErrorPlus = valueError[i];
14588 mData->insertMulti(key[i], newData);
14589 }
14590}
14591
14601void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14602{
14603 mData->clear();
14604 int n = key.size();
14605 n = qMin(n, value.size());
14606 n = qMin(n, valueErrorMinus.size());
14607 n = qMin(n, valueErrorPlus.size());
14608 QCPData newData;
14609 for (int i=0; i<n; ++i)
14610 {
14611 newData.key = key[i];
14612 newData.value = value[i];
14613 newData.valueErrorMinus = valueErrorMinus[i];
14614 newData.valueErrorPlus = valueErrorPlus[i];
14615 mData->insertMulti(key[i], newData);
14616 }
14617}
14618
14628void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14629{
14630 mData->clear();
14631 int n = key.size();
14632 n = qMin(n, value.size());
14633 n = qMin(n, keyError.size());
14634 QCPData newData;
14635 for (int i=0; i<n; ++i)
14636 {
14637 newData.key = key[i];
14638 newData.value = value[i];
14639 newData.keyErrorMinus = keyError[i];
14640 newData.keyErrorPlus = keyError[i];
14641 mData->insertMulti(key[i], newData);
14642 }
14643}
14644
14654void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14655{
14656 mData->clear();
14657 int n = key.size();
14658 n = qMin(n, value.size());
14659 n = qMin(n, keyErrorMinus.size());
14660 n = qMin(n, keyErrorPlus.size());
14661 QCPData newData;
14662 for (int i=0; i<n; ++i)
14663 {
14664 newData.key = key[i];
14665 newData.value = value[i];
14666 newData.keyErrorMinus = keyErrorMinus[i];
14667 newData.keyErrorPlus = keyErrorPlus[i];
14668 mData->insertMulti(key[i], newData);
14669 }
14670}
14671
14681void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14682{
14683 mData->clear();
14684 int n = key.size();
14685 n = qMin(n, value.size());
14686 n = qMin(n, valueError.size());
14687 n = qMin(n, keyError.size());
14688 QCPData newData;
14689 for (int i=0; i<n; ++i)
14690 {
14691 newData.key = key[i];
14692 newData.value = value[i];
14693 newData.keyErrorMinus = keyError[i];
14694 newData.keyErrorPlus = keyError[i];
14695 newData.valueErrorMinus = valueError[i];
14696 newData.valueErrorPlus = valueError[i];
14697 mData->insertMulti(key[i], newData);
14698 }
14699}
14700
14710void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14711{
14712 mData->clear();
14713 int n = key.size();
14714 n = qMin(n, value.size());
14715 n = qMin(n, valueErrorMinus.size());
14716 n = qMin(n, valueErrorPlus.size());
14717 n = qMin(n, keyErrorMinus.size());
14718 n = qMin(n, keyErrorPlus.size());
14719 QCPData newData;
14720 for (int i=0; i<n; ++i)
14721 {
14722 newData.key = key[i];
14723 newData.value = value[i];
14724 newData.keyErrorMinus = keyErrorMinus[i];
14725 newData.keyErrorPlus = keyErrorPlus[i];
14726 newData.valueErrorMinus = valueErrorMinus[i];
14727 newData.valueErrorPlus = valueErrorPlus[i];
14728 mData->insertMulti(key[i], newData);
14729 }
14730}
14731
14732
14740{
14741 mLineStyle = ls;
14742}
14743
14751{
14752 mScatterStyle = style;
14753}
14754
14764{
14766}
14767
14772void QCPGraph::setErrorPen(const QPen &pen)
14773{
14774 mErrorPen = pen;
14775}
14776
14781{
14782 mErrorBarSize = size;
14783}
14784
14797{
14798 mErrorBarSkipSymbol = enabled;
14799}
14800
14811{
14812 // prevent setting channel target to this graph itself:
14813 if (targetGraph == this)
14814 {
14815 qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14817 return;
14818 }
14819 // prevent setting channel target to a graph not in the plot:
14820 if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14821 {
14822 qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14824 return;
14825 }
14826
14827 mChannelFillGraph = targetGraph;
14828}
14829
14862{
14863 mAdaptiveSampling = enabled;
14864}
14865
14874void QCPGraph::addData(const QCPDataMap &dataMap)
14875{
14876 mData->unite(dataMap);
14877}
14878
14888{
14889 mData->insertMulti(data.key, data);
14890}
14891
14900void QCPGraph::addData(double key, double value)
14901{
14902 QCPData newData;
14903 newData.key = key;
14904 newData.value = value;
14905 mData->insertMulti(newData.key, newData);
14906}
14907
14916void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14917{
14918 int n = qMin(keys.size(), values.size());
14919 QCPData newData;
14920 for (int i=0; i<n; ++i)
14921 {
14922 newData.key = keys[i];
14923 newData.value = values[i];
14924 mData->insertMulti(newData.key, newData);
14925 }
14926}
14927
14933{
14934 QCPDataMap::iterator it = mData->begin();
14935 while (it != mData->end() && it.key() < key)
14936 it = mData->erase(it);
14937}
14938
14944{
14945 if (mData->isEmpty()) return;
14946 QCPDataMap::iterator it = mData->upperBound(key);
14947 while (it != mData->end())
14948 it = mData->erase(it);
14949}
14950
14958void QCPGraph::removeData(double fromKey, double toKey)
14959{
14960 if (fromKey >= toKey || mData->isEmpty()) return;
14961 QCPDataMap::iterator it = mData->upperBound(fromKey);
14962 QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14963 while (it != itEnd)
14964 it = mData->erase(it);
14965}
14966
14975void QCPGraph::removeData(double key)
14976{
14977 mData->remove(key);
14978}
14979
14985{
14986 mData->clear();
14987}
14988
14989/* inherits documentation from base class */
14990double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
14991{
14992 Q_UNUSED(details)
14993 if ((onlySelectable && !mSelectable) || mData->isEmpty())
14994 return -1;
14995 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14996
14997 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
14998 return pointDistance(pos);
14999 else
15000 return -1;
15001}
15002
15010void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
15011{
15012 rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15013 rescaleValueAxis(onlyEnlarge, includeErrorBars);
15014}
15015
15023void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15024{
15025 // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15026 // that getKeyRange is passed the includeErrorBars value.
15027 if (mData->isEmpty()) return;
15028
15029 QCPAxis *keyAxis = mKeyAxis.data();
15030 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15031
15032 SignDomain signDomain = sdBoth;
15034 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15035
15036 bool foundRange;
15037 QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15038
15039 if (foundRange)
15040 {
15041 if (onlyEnlarge)
15042 {
15043 if (keyAxis->range().lower < newRange.lower)
15044 newRange.lower = keyAxis->range().lower;
15045 if (keyAxis->range().upper > newRange.upper)
15046 newRange.upper = keyAxis->range().upper;
15047 }
15048 keyAxis->setRange(newRange);
15049 }
15050}
15051
15059void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15060{
15061 // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15062 // is that getValueRange is passed the includeErrorBars value.
15063 if (mData->isEmpty()) return;
15064
15065 QCPAxis *valueAxis = mValueAxis.data();
15066 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15067
15068 SignDomain signDomain = sdBoth;
15070 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15071
15072 bool foundRange;
15073 QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15074
15075 if (foundRange)
15076 {
15077 if (onlyEnlarge)
15078 {
15079 if (valueAxis->range().lower < newRange.lower)
15080 newRange.lower = valueAxis->range().lower;
15081 if (valueAxis->range().upper > newRange.upper)
15082 newRange.upper = valueAxis->range().upper;
15083 }
15084 valueAxis->setRange(newRange);
15085 }
15086}
15087
15088/* inherits documentation from base class */
15090{
15091 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15092 if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15093 if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15094
15095 // allocate line and (if necessary) point vectors:
15096 QVector<QPointF> *lineData = new QVector<QPointF>;
15097 QVector<QCPData> *scatterData = 0;
15098 if (!mScatterStyle.isNone())
15099 scatterData = new QVector<QCPData>;
15100
15101 // fill vectors with data appropriate to plot style:
15102 getPlotData(lineData, scatterData);
15103
15104 // check data validity if flag set:
15105#ifdef QCUSTOMPLOT_CHECK_DATA
15106 QCPDataMap::const_iterator it;
15107 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15108 {
15109 if (QCP::isInvalidData(it.value().key, it.value().value) ||
15110 QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15111 QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15112 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15113 }
15114#endif
15115
15116 // draw fill of graph:
15117 drawFill(painter, lineData);
15118
15119 // draw line:
15120 if (mLineStyle == lsImpulse)
15121 drawImpulsePlot(painter, lineData);
15122 else if (mLineStyle != lsNone)
15123 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15124
15125 // draw scatters:
15126 if (scatterData)
15127 drawScatterPlot(painter, scatterData);
15128
15129 // free allocated line and point vectors:
15130 delete lineData;
15131 if (scatterData)
15132 delete scatterData;
15133}
15134
15135/* inherits documentation from base class */
15136void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15137{
15138 // draw fill:
15139 if (mBrush.style() != Qt::NoBrush)
15140 {
15142 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15143 }
15144 // draw line vertically centered:
15145 if (mLineStyle != lsNone)
15146 {
15148 painter->setPen(mPen);
15149 painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
15150 }
15151 // draw scatter symbol:
15152 if (!mScatterStyle.isNone())
15153 {
15155 // scale scatter pixmap if it's too large to fit in legend icon rect:
15156 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15157 {
15158 QCPScatterStyle scaledStyle(mScatterStyle);
15159 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15160 scaledStyle.applyTo(painter, mPen);
15161 scaledStyle.drawShape(painter, QRectF(rect).center());
15162 } else
15163 {
15164 mScatterStyle.applyTo(painter, mPen);
15165 mScatterStyle.drawShape(painter, QRectF(rect).center());
15166 }
15167 }
15168}
15169
15188void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15189{
15190 switch(mLineStyle)
15191 {
15192 case lsNone: getScatterPlotData(scatterData); break;
15193 case lsLine: getLinePlotData(lineData, scatterData); break;
15194 case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15195 case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15196 case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15197 case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15198 }
15199}
15200
15212void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15213{
15214 getPreparedData(0, scatterData);
15215}
15216
15228void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15229{
15230 QCPAxis *keyAxis = mKeyAxis.data();
15231 QCPAxis *valueAxis = mValueAxis.data();
15232 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15233 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15234
15235 QVector<QCPData> lineData;
15236 getPreparedData(&lineData, scatterData);
15237 linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15238 linePixelData->resize(lineData.size());
15239
15240 // transform lineData points to pixels:
15241 if (keyAxis->orientation() == Qt::Vertical)
15242 {
15243 for (int i=0; i<lineData.size(); ++i)
15244 {
15245 (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15246 (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15247 }
15248 } else // key axis is horizontal
15249 {
15250 for (int i=0; i<lineData.size(); ++i)
15251 {
15252 (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15253 (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15254 }
15255 }
15256}
15257
15269void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15270{
15271 QCPAxis *keyAxis = mKeyAxis.data();
15272 QCPAxis *valueAxis = mValueAxis.data();
15273 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15274 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15275
15276 QVector<QCPData> lineData;
15277 getPreparedData(&lineData, scatterData);
15278 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15279 linePixelData->resize(lineData.size()*2);
15280
15281 // calculate steps from lineData and transform to pixel coordinates:
15282 if (keyAxis->orientation() == Qt::Vertical)
15283 {
15284 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15285 double key;
15286 for (int i=0; i<lineData.size(); ++i)
15287 {
15288 key = keyAxis->coordToPixel(lineData.at(i).key);
15289 (*linePixelData)[i*2+0].setX(lastValue);
15290 (*linePixelData)[i*2+0].setY(key);
15291 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15292 (*linePixelData)[i*2+1].setX(lastValue);
15293 (*linePixelData)[i*2+1].setY(key);
15294 }
15295 } else // key axis is horizontal
15296 {
15297 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15298 double key;
15299 for (int i=0; i<lineData.size(); ++i)
15300 {
15301 key = keyAxis->coordToPixel(lineData.at(i).key);
15302 (*linePixelData)[i*2+0].setX(key);
15303 (*linePixelData)[i*2+0].setY(lastValue);
15304 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15305 (*linePixelData)[i*2+1].setX(key);
15306 (*linePixelData)[i*2+1].setY(lastValue);
15307 }
15308 }
15309}
15310
15322void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15323{
15324 QCPAxis *keyAxis = mKeyAxis.data();
15325 QCPAxis *valueAxis = mValueAxis.data();
15326 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15327 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15328
15329 QVector<QCPData> lineData;
15330 getPreparedData(&lineData, scatterData);
15331 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15332 linePixelData->resize(lineData.size()*2);
15333
15334 // calculate steps from lineData and transform to pixel coordinates:
15335 if (keyAxis->orientation() == Qt::Vertical)
15336 {
15337 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15338 double value;
15339 for (int i=0; i<lineData.size(); ++i)
15340 {
15341 value = valueAxis->coordToPixel(lineData.at(i).value);
15342 (*linePixelData)[i*2+0].setX(value);
15343 (*linePixelData)[i*2+0].setY(lastKey);
15344 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15345 (*linePixelData)[i*2+1].setX(value);
15346 (*linePixelData)[i*2+1].setY(lastKey);
15347 }
15348 } else // key axis is horizontal
15349 {
15350 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15351 double value;
15352 for (int i=0; i<lineData.size(); ++i)
15353 {
15354 value = valueAxis->coordToPixel(lineData.at(i).value);
15355 (*linePixelData)[i*2+0].setX(lastKey);
15356 (*linePixelData)[i*2+0].setY(value);
15357 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15358 (*linePixelData)[i*2+1].setX(lastKey);
15359 (*linePixelData)[i*2+1].setY(value);
15360 }
15361 }
15362}
15363
15375void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15376{
15377 QCPAxis *keyAxis = mKeyAxis.data();
15378 QCPAxis *valueAxis = mValueAxis.data();
15379 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15380 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15381
15382 QVector<QCPData> lineData;
15383 getPreparedData(&lineData, scatterData);
15384 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15385 linePixelData->resize(lineData.size()*2);
15386 // calculate steps from lineData and transform to pixel coordinates:
15387 if (keyAxis->orientation() == Qt::Vertical)
15388 {
15389 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15390 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15391 double key;
15392 (*linePixelData)[0].setX(lastValue);
15393 (*linePixelData)[0].setY(lastKey);
15394 for (int i=1; i<lineData.size(); ++i)
15395 {
15396 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15397 (*linePixelData)[i*2-1].setX(lastValue);
15398 (*linePixelData)[i*2-1].setY(key);
15399 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15400 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15401 (*linePixelData)[i*2+0].setX(lastValue);
15402 (*linePixelData)[i*2+0].setY(key);
15403 }
15404 (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15405 (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15406 } else // key axis is horizontal
15407 {
15408 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15409 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15410 double key;
15411 (*linePixelData)[0].setX(lastKey);
15412 (*linePixelData)[0].setY(lastValue);
15413 for (int i=1; i<lineData.size(); ++i)
15414 {
15415 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15416 (*linePixelData)[i*2-1].setX(key);
15417 (*linePixelData)[i*2-1].setY(lastValue);
15418 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15419 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15420 (*linePixelData)[i*2+0].setX(key);
15421 (*linePixelData)[i*2+0].setY(lastValue);
15422 }
15423 (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15424 (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15425 }
15426
15427}
15428
15440void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15441{
15442 QCPAxis *keyAxis = mKeyAxis.data();
15443 QCPAxis *valueAxis = mValueAxis.data();
15444 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15445 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15446
15447 QVector<QCPData> lineData;
15448 getPreparedData(&lineData, scatterData);
15449 linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15450
15451 // transform lineData points to pixels:
15452 if (keyAxis->orientation() == Qt::Vertical)
15453 {
15454 double zeroPointX = valueAxis->coordToPixel(0);
15455 double key;
15456 for (int i=0; i<lineData.size(); ++i)
15457 {
15458 key = keyAxis->coordToPixel(lineData.at(i).key);
15459 (*linePixelData)[i*2+0].setX(zeroPointX);
15460 (*linePixelData)[i*2+0].setY(key);
15461 (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15462 (*linePixelData)[i*2+1].setY(key);
15463 }
15464 } else // key axis is horizontal
15465 {
15466 double zeroPointY = valueAxis->coordToPixel(0);
15467 double key;
15468 for (int i=0; i<lineData.size(); ++i)
15469 {
15470 key = keyAxis->coordToPixel(lineData.at(i).key);
15471 (*linePixelData)[i*2+0].setX(key);
15472 (*linePixelData)[i*2+0].setY(zeroPointY);
15473 (*linePixelData)[i*2+1].setX(key);
15474 (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15475 }
15476 }
15477}
15478
15492void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15493{
15494 if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15495 if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15496
15498 if (!mChannelFillGraph)
15499 {
15500 // draw base fill under graph, fill goes all the way to the zero-value-line:
15501 addFillBasePoints(lineData);
15502 painter->setPen(Qt::NoPen);
15503 painter->setBrush(mainBrush());
15504 painter->drawPolygon(QPolygonF(*lineData));
15505 removeFillBasePoints(lineData);
15506 } else
15507 {
15508 // draw channel fill between this graph and mChannelFillGraph:
15509 painter->setPen(Qt::NoPen);
15510 painter->setBrush(mainBrush());
15511 painter->drawPolygon(getChannelFillPolygon(lineData));
15512 }
15513}
15514
15524void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15525{
15526 QCPAxis *keyAxis = mKeyAxis.data();
15527 QCPAxis *valueAxis = mValueAxis.data();
15528 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15529
15530 // draw error bars:
15531 if (mErrorType != etNone)
15532 {
15534 painter->setPen(mErrorPen);
15535 if (keyAxis->orientation() == Qt::Vertical)
15536 {
15537 for (int i=0; i<scatterData->size(); ++i)
15538 drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15539 } else
15540 {
15541 for (int i=0; i<scatterData->size(); ++i)
15542 drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15543 }
15544 }
15545
15546 // draw scatter point symbols:
15548 mScatterStyle.applyTo(painter, mPen);
15549 if (keyAxis->orientation() == Qt::Vertical)
15550 {
15551 for (int i=0; i<scatterData->size(); ++i)
15552 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15553 } else
15554 {
15555 for (int i=0; i<scatterData->size(); ++i)
15556 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15557 }
15558}
15559
15569void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15570{
15571 // draw line of graph:
15572 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15573 {
15575 painter->setPen(mainPen());
15576 painter->setBrush(Qt::NoBrush);
15577
15578 /* Draws polyline in batches, currently not used:
15579 int p = 0;
15580 while (p < lineData->size())
15581 {
15582 int batch = qMin(25, lineData->size()-p);
15583 if (p != 0)
15584 {
15585 ++batch;
15586 --p; // to draw the connection lines between two batches
15587 }
15588 painter->drawPolyline(lineData->constData()+p, batch);
15589 p += batch;
15590 }
15591 */
15592
15593 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15595 painter->pen().style() == Qt::SolidLine &&
15596 !painter->modes().testFlag(QCPPainter::pmVectorized)&&
15597 !painter->modes().testFlag(QCPPainter::pmNoCaching))
15598 {
15599 for (int i=1; i<lineData->size(); ++i)
15600 painter->drawLine(lineData->at(i-1), lineData->at(i));
15601 } else
15602 {
15603 painter->drawPolyline(QPolygonF(*lineData));
15604 }
15605 }
15606}
15607
15615void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15616{
15617 // draw impulses:
15618 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15619 {
15621 QPen pen = mainPen();
15622 pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15623 painter->setPen(pen);
15624 painter->setBrush(Qt::NoBrush);
15625 painter->drawLines(*lineData);
15626 }
15627}
15628
15641void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15642{
15643 QCPAxis *keyAxis = mKeyAxis.data();
15644 QCPAxis *valueAxis = mValueAxis.data();
15645 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15646 // get visible data range:
15647 QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15648 getVisibleDataBounds(lower, upper);
15649 if (lower == mData->constEnd() || upper == mData->constEnd())
15650 return;
15651
15652 // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15653 int maxCount = std::numeric_limits<int>::max();
15655 {
15656 int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15657 maxCount = 2*keyPixelSpan+2;
15658 }
15659 int dataCount = countDataInBounds(lower, upper, maxCount);
15660
15661 if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15662 {
15663 if (lineData)
15664 {
15665 QCPDataMap::const_iterator it = lower;
15666 QCPDataMap::const_iterator upperEnd = upper+1;
15667 double minValue = it.value().value;
15668 double maxValue = it.value().value;
15669 QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15670 int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15671 int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15672 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15673 double lastIntervalEndKey = currentIntervalStartKey;
15674 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15675 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15676 int intervalDataCount = 1;
15677 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15678 while (it != upperEnd)
15679 {
15680 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15681 {
15682 if (it.value().value < minValue)
15683 minValue = it.value().value;
15684 else if (it.value().value > maxValue)
15685 maxValue = it.value().value;
15686 ++intervalDataCount;
15687 } else // new pixel interval started
15688 {
15689 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15690 {
15691 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15692 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15693 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15694 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15695 if (it.key() > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point
15696 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15697 } else
15698 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15699 lastIntervalEndKey = (it-1).value().key;
15700 minValue = it.value().value;
15701 maxValue = it.value().value;
15702 currentIntervalFirstPoint = it;
15703 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15704 if (keyEpsilonVariable)
15705 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15706 intervalDataCount = 1;
15707 }
15708 ++it;
15709 }
15710 // handle last interval:
15711 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15712 {
15713 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15714 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15715 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15716 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15717 } else
15718 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15719 }
15720
15721 if (scatterData)
15722 {
15723 double valueMaxRange = valueAxis->range().upper;
15724 double valueMinRange = valueAxis->range().lower;
15725 QCPDataMap::const_iterator it = lower;
15726 QCPDataMap::const_iterator upperEnd = upper+1;
15727 double minValue = it.value().value;
15728 double maxValue = it.value().value;
15729 QCPDataMap::const_iterator minValueIt = it;
15730 QCPDataMap::const_iterator maxValueIt = it;
15731 QCPDataMap::const_iterator currentIntervalStart = it;
15732 int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15733 int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15734 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15735 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15736 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15737 int intervalDataCount = 1;
15738 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15739 while (it != upperEnd)
15740 {
15741 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15742 {
15743 if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15744 {
15745 minValue = it.value().value;
15746 minValueIt = it;
15747 } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15748 {
15749 maxValue = it.value().value;
15750 maxValueIt = it;
15751 }
15752 ++intervalDataCount;
15753 } else // new pixel started
15754 {
15755 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15756 {
15757 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15758 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15759 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15760 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15761 int c = 0;
15762 while (intervalIt != it)
15763 {
15764 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15765 scatterData->append(intervalIt.value());
15766 ++c;
15767 ++intervalIt;
15768 }
15769 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15770 scatterData->append(currentIntervalStart.value());
15771 minValue = it.value().value;
15772 maxValue = it.value().value;
15773 currentIntervalStart = it;
15774 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15775 if (keyEpsilonVariable)
15776 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15777 intervalDataCount = 1;
15778 }
15779 ++it;
15780 }
15781 // handle last interval:
15782 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15783 {
15784 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15785 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15786 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15787 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15788 int c = 0;
15789 while (intervalIt != it)
15790 {
15791 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15792 scatterData->append(intervalIt.value());
15793 ++c;
15794 ++intervalIt;
15795 }
15796 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15797 scatterData->append(currentIntervalStart.value());
15798 }
15799 } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15800 {
15801 QVector<QCPData> *dataVector = 0;
15802 if (lineData)
15803 dataVector = lineData;
15804 else if (scatterData)
15805 dataVector = scatterData;
15806 if (dataVector)
15807 {
15808 QCPDataMap::const_iterator it = lower;
15809 QCPDataMap::const_iterator upperEnd = upper+1;
15810 dataVector->reserve(dataCount+2); // +2 for possible fill end points
15811 while (it != upperEnd)
15812 {
15813 dataVector->append(it.value());
15814 ++it;
15815 }
15816 }
15817 if (lineData && scatterData)
15818 *scatterData = *dataVector;
15819 }
15820}
15821
15829void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15830{
15831 QCPAxis *keyAxis = mKeyAxis.data();
15832 QCPAxis *valueAxis = mValueAxis.data();
15833 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15834
15835 double a, b; // positions of error bar bounds in pixels
15836 double barWidthHalf = mErrorBarSize*0.5;
15837 double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15838
15839 if (keyAxis->orientation() == Qt::Vertical)
15840 {
15841 // draw key error vertically and value error horizontally
15842 if (mErrorType == etKey || mErrorType == etBoth)
15843 {
15844 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15845 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15846 if (keyAxis->rangeReversed())
15847 qSwap(a,b);
15848 // draw spine:
15850 {
15851 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15852 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15853 if (y-b > skipSymbolMargin)
15854 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15855 } else
15856 painter->drawLine(QLineF(x, a, x, b));
15857 // draw handles:
15858 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15859 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15860 }
15861 if (mErrorType == etValue || mErrorType == etBoth)
15862 {
15863 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15864 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15865 if (valueAxis->rangeReversed())
15866 qSwap(a,b);
15867 // draw spine:
15869 {
15870 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15871 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15872 if (b-x > skipSymbolMargin)
15873 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15874 } else
15875 painter->drawLine(QLineF(a, y, b, y));
15876 // draw handles:
15877 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15878 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15879 }
15880 } else // mKeyAxis->orientation() is Qt::Horizontal
15881 {
15882 // draw value error vertically and key error horizontally
15883 if (mErrorType == etKey || mErrorType == etBoth)
15884 {
15885 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15886 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15887 if (keyAxis->rangeReversed())
15888 qSwap(a,b);
15889 // draw spine:
15891 {
15892 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15893 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15894 if (b-x > skipSymbolMargin)
15895 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15896 } else
15897 painter->drawLine(QLineF(a, y, b, y));
15898 // draw handles:
15899 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15900 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15901 }
15902 if (mErrorType == etValue || mErrorType == etBoth)
15903 {
15904 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15905 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15906 if (valueAxis->rangeReversed())
15907 qSwap(a,b);
15908 // draw spine:
15910 {
15911 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15912 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15913 if (y-b > skipSymbolMargin)
15914 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15915 } else
15916 painter->drawLine(QLineF(x, a, x, b));
15917 // draw handles:
15918 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15919 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15920 }
15921 }
15922}
15923
15938void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
15939{
15940 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15941 if (mData->isEmpty())
15942 {
15943 lower = mData->constEnd();
15944 upper = mData->constEnd();
15945 return;
15946 }
15947
15948 // get visible data range as QMap iterators
15949 QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
15950 QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
15951 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
15952 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
15953
15954 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15955 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15956}
15957
15968int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
15969{
15970 if (upper == mData->constEnd() && lower == mData->constEnd())
15971 return 0;
15972 QCPDataMap::const_iterator it = lower;
15973 int count = 1;
15974 while (it != upper && count < maxCount)
15975 {
15976 ++it;
15977 ++count;
15978 }
15979 return count;
15980}
15981
15997void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
15998{
15999 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16000
16001 // append points that close the polygon fill at the key axis:
16002 if (mKeyAxis.data()->orientation() == Qt::Vertical)
16003 {
16004 *lineData << upperFillBasePoint(lineData->last().y());
16005 *lineData << lowerFillBasePoint(lineData->first().y());
16006 } else
16007 {
16008 *lineData << upperFillBasePoint(lineData->last().x());
16009 *lineData << lowerFillBasePoint(lineData->first().x());
16010 }
16011}
16012
16019void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16020{
16021 lineData->remove(lineData->size()-2, 2);
16022}
16023
16038QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16039{
16040 QCPAxis *keyAxis = mKeyAxis.data();
16041 QCPAxis *valueAxis = mValueAxis.data();
16042 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16043
16044 QPointF point;
16046 {
16048 {
16049 point.setX(valueAxis->coordToPixel(0));
16050 point.setY(lowerKey);
16051 } else if (keyAxis->axisType() == QCPAxis::atRight)
16052 {
16053 point.setX(valueAxis->coordToPixel(0));
16054 point.setY(lowerKey);
16055 } else if (keyAxis->axisType() == QCPAxis::atTop)
16056 {
16057 point.setX(lowerKey);
16058 point.setY(valueAxis->coordToPixel(0));
16059 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16060 {
16061 point.setX(lowerKey);
16062 point.setY(valueAxis->coordToPixel(0));
16063 }
16064 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16065 {
16066 // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16067 // to the axis which is in the direction towards zero
16068 if (keyAxis->orientation() == Qt::Vertical)
16069 {
16070 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16071 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16072 point.setX(keyAxis->axisRect()->right());
16073 else
16074 point.setX(keyAxis->axisRect()->left());
16075 point.setY(lowerKey);
16077 {
16078 point.setX(lowerKey);
16079 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16080 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16081 point.setY(keyAxis->axisRect()->top());
16082 else
16083 point.setY(keyAxis->axisRect()->bottom());
16084 }
16085 }
16086 return point;
16087}
16088
16103QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16104{
16105 QCPAxis *keyAxis = mKeyAxis.data();
16106 QCPAxis *valueAxis = mValueAxis.data();
16107 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16108
16109 QPointF point;
16111 {
16113 {
16114 point.setX(valueAxis->coordToPixel(0));
16115 point.setY(upperKey);
16116 } else if (keyAxis->axisType() == QCPAxis::atRight)
16117 {
16118 point.setX(valueAxis->coordToPixel(0));
16119 point.setY(upperKey);
16120 } else if (keyAxis->axisType() == QCPAxis::atTop)
16121 {
16122 point.setX(upperKey);
16123 point.setY(valueAxis->coordToPixel(0));
16124 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16125 {
16126 point.setX(upperKey);
16127 point.setY(valueAxis->coordToPixel(0));
16128 }
16129 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16130 {
16131 // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16132 // to the axis which is in the direction towards 0
16133 if (keyAxis->orientation() == Qt::Vertical)
16134 {
16135 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16136 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16137 point.setX(keyAxis->axisRect()->right());
16138 else
16139 point.setX(keyAxis->axisRect()->left());
16140 point.setY(upperKey);
16142 {
16143 point.setX(upperKey);
16144 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16145 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16146 point.setY(keyAxis->axisRect()->top());
16147 else
16148 point.setY(keyAxis->axisRect()->bottom());
16149 }
16150 }
16151 return point;
16152}
16153
16163const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16164{
16165 if (!mChannelFillGraph)
16166 return QPolygonF();
16167
16168 QCPAxis *keyAxis = mKeyAxis.data();
16169 QCPAxis *valueAxis = mValueAxis.data();
16170 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16171 if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16172
16173 if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16174 return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
16175
16176 if (lineData->isEmpty()) return QPolygonF();
16177 QVector<QPointF> otherData;
16178 mChannelFillGraph.data()->getPlotData(&otherData, 0);
16179 if (otherData.isEmpty()) return QPolygonF();
16180 QVector<QPointF> thisData;
16181 thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16182 for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16183 thisData << lineData->at(i);
16184
16185 // pointers to be able to swap them, depending which data range needs cropping:
16186 QVector<QPointF> *staticData = &thisData;
16187 QVector<QPointF> *croppedData = &otherData;
16188
16189 // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16190 if (keyAxis->orientation() == Qt::Horizontal)
16191 {
16192 // x is key
16193 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16194 if (staticData->first().x() > staticData->last().x())
16195 {
16196 int size = staticData->size();
16197 for (int i=0; i<size/2; ++i)
16198 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16199 }
16200 if (croppedData->first().x() > croppedData->last().x())
16201 {
16202 int size = croppedData->size();
16203 for (int i=0; i<size/2; ++i)
16204 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16205 }
16206 // crop lower bound:
16207 if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16208 qSwap(staticData, croppedData);
16209 int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16210 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16211 croppedData->remove(0, lowBound);
16212 // set lowest point of cropped data to fit exactly key position of first static data
16213 // point via linear interpolation:
16214 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16215 double slope;
16216 if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16217 slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16218 else
16219 slope = 0;
16220 (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16221 (*croppedData)[0].setX(staticData->first().x());
16222
16223 // crop upper bound:
16224 if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16225 qSwap(staticData, croppedData);
16226 int highBound = findIndexAboveX(croppedData, staticData->last().x());
16227 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16228 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16229 // set highest point of cropped data to fit exactly key position of last static data
16230 // point via linear interpolation:
16231 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16232 int li = croppedData->size()-1; // last index
16233 if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16234 slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16235 else
16236 slope = 0;
16237 (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16238 (*croppedData)[li].setX(staticData->last().x());
16239 } else // mKeyAxis->orientation() == Qt::Vertical
16240 {
16241 // y is key
16242 // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16243 // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16244 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16245 if (staticData->first().y() < staticData->last().y())
16246 {
16247 int size = staticData->size();
16248 for (int i=0; i<size/2; ++i)
16249 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16250 }
16251 if (croppedData->first().y() < croppedData->last().y())
16252 {
16253 int size = croppedData->size();
16254 for (int i=0; i<size/2; ++i)
16255 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16256 }
16257 // crop lower bound:
16258 if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16259 qSwap(staticData, croppedData);
16260 int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16261 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16262 croppedData->remove(0, lowBound);
16263 // set lowest point of cropped data to fit exactly key position of first static data
16264 // point via linear interpolation:
16265 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16266 double slope;
16267 if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16268 slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16269 else
16270 slope = 0;
16271 (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16272 (*croppedData)[0].setY(staticData->first().y());
16273
16274 // crop upper bound:
16275 if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16276 qSwap(staticData, croppedData);
16277 int highBound = findIndexBelowY(croppedData, staticData->last().y());
16278 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16279 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16280 // set highest point of cropped data to fit exactly key position of last static data
16281 // point via linear interpolation:
16282 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16283 int li = croppedData->size()-1; // last index
16284 if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16285 slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16286 else
16287 slope = 0;
16288 (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16289 (*croppedData)[li].setY(staticData->last().y());
16290 }
16291
16292 // return joined:
16293 for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16294 thisData << otherData.at(i);
16295 return QPolygonF(thisData);
16296}
16297
16305int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16306{
16307 for (int i=data->size()-1; i>=0; --i)
16308 {
16309 if (data->at(i).x() < x)
16310 {
16311 if (i<data->size()-1)
16312 return i+1;
16313 else
16314 return data->size()-1;
16315 }
16316 }
16317 return -1;
16318}
16319
16327int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16328{
16329 for (int i=0; i<data->size(); ++i)
16330 {
16331 if (data->at(i).x() > x)
16332 {
16333 if (i>0)
16334 return i-1;
16335 else
16336 return 0;
16337 }
16338 }
16339 return -1;
16340}
16341
16349int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16350{
16351 for (int i=0; i<data->size(); ++i)
16352 {
16353 if (data->at(i).y() < y)
16354 {
16355 if (i>0)
16356 return i-1;
16357 else
16358 return 0;
16359 }
16360 }
16361 return -1;
16362}
16363
16374double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16375{
16376 if (mData->isEmpty())
16377 {
16378 qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data";
16379 return 500;
16380 }
16381 if (mData->size() == 1)
16382 {
16383 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
16384 return QVector2D(dataPoint-pixelPoint).length();
16385 }
16386
16388 return 500;
16389
16390 // calculate minimum distances to graph representation:
16391 if (mLineStyle == lsNone)
16392 {
16393 // no line displayed, only calculate distance to scatter points:
16394 QVector<QCPData> *scatterData = new QVector<QCPData>;
16395 getScatterPlotData(scatterData);
16396 double minDistSqr = std::numeric_limits<double>::max();
16397 QPointF ptA;
16398 QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
16399 for (int i=1; i<scatterData->size(); ++i)
16400 {
16401 ptA = ptB;
16402 ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value);
16403 double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
16404 if (currentDistSqr < minDistSqr)
16405 minDistSqr = currentDistSqr;
16406 }
16407 delete scatterData;
16408 return sqrt(minDistSqr);
16409 } else
16410 {
16411 // line displayed calculate distance to line segments:
16412 QVector<QPointF> *lineData = new QVector<QPointF>;
16413 getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16414 double minDistSqr = std::numeric_limits<double>::max();
16415 if (mLineStyle == lsImpulse)
16416 {
16417 // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16418 for (int i=0; i<lineData->size()-1; i+=2) // iterate pairs
16419 {
16420 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16421 if (currentDistSqr < minDistSqr)
16422 minDistSqr = currentDistSqr;
16423 }
16424 } else
16425 {
16426 // all other line plots (line and step) connect points directly:
16427 for (int i=0; i<lineData->size()-1; ++i)
16428 {
16429 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16430 if (currentDistSqr < minDistSqr)
16431 minDistSqr = currentDistSqr;
16432 }
16433 }
16434 delete lineData;
16435 return sqrt(minDistSqr);
16436 }
16437}
16438
16447int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16448{
16449 for (int i=data->size()-1; i>=0; --i)
16450 {
16451 if (data->at(i).y() > y)
16452 {
16453 if (i<data->size()-1)
16454 return i+1;
16455 else
16456 return data->size()-1;
16457 }
16458 }
16459 return -1;
16460}
16461
16462/* inherits documentation from base class */
16463QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16464{
16465 // just call the specialized version which takes an additional argument whether error bars
16466 // should also be taken into consideration for range calculation. We set this to true here.
16467 return getKeyRange(foundRange, inSignDomain, true);
16468}
16469
16470/* inherits documentation from base class */
16471QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16472{
16473 // just call the specialized version which takes an additional argument whether error bars
16474 // should also be taken into consideration for range calculation. We set this to true here.
16475 return getValueRange(foundRange, inSignDomain, true);
16476}
16477
16484QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16485{
16486 QCPRange range;
16487 bool haveLower = false;
16488 bool haveUpper = false;
16489
16490 double current, currentErrorMinus, currentErrorPlus;
16491
16492 if (inSignDomain == sdBoth) // range may be anywhere
16493 {
16494 QCPDataMap::const_iterator it = mData->constBegin();
16495 while (it != mData->constEnd())
16496 {
16497 current = it.value().key;
16498 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16499 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16500 if (current-currentErrorMinus < range.lower || !haveLower)
16501 {
16502 range.lower = current-currentErrorMinus;
16503 haveLower = true;
16504 }
16505 if (current+currentErrorPlus > range.upper || !haveUpper)
16506 {
16507 range.upper = current+currentErrorPlus;
16508 haveUpper = true;
16509 }
16510 ++it;
16511 }
16512 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16513 {
16514 QCPDataMap::const_iterator it = mData->constBegin();
16515 while (it != mData->constEnd())
16516 {
16517 current = it.value().key;
16518 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16519 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16520 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16521 {
16522 range.lower = current-currentErrorMinus;
16523 haveLower = true;
16524 }
16525 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16526 {
16527 range.upper = current+currentErrorPlus;
16528 haveUpper = true;
16529 }
16530 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16531 {
16532 if ((current < range.lower || !haveLower) && current < 0)
16533 {
16534 range.lower = current;
16535 haveLower = true;
16536 }
16537 if ((current > range.upper || !haveUpper) && current < 0)
16538 {
16539 range.upper = current;
16540 haveUpper = true;
16541 }
16542 }
16543 ++it;
16544 }
16545 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16546 {
16547 QCPDataMap::const_iterator it = mData->constBegin();
16548 while (it != mData->constEnd())
16549 {
16550 current = it.value().key;
16551 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16552 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16553 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16554 {
16555 range.lower = current-currentErrorMinus;
16556 haveLower = true;
16557 }
16558 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16559 {
16560 range.upper = current+currentErrorPlus;
16561 haveUpper = true;
16562 }
16563 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16564 {
16565 if ((current < range.lower || !haveLower) && current > 0)
16566 {
16567 range.lower = current;
16568 haveLower = true;
16569 }
16570 if ((current > range.upper || !haveUpper) && current > 0)
16571 {
16572 range.upper = current;
16573 haveUpper = true;
16574 }
16575 }
16576 ++it;
16577 }
16578 }
16579
16580 foundRange = haveLower && haveUpper;
16581 return range;
16582}
16583
16590QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16591{
16592 QCPRange range;
16593 bool haveLower = false;
16594 bool haveUpper = false;
16595
16596 double current, currentErrorMinus, currentErrorPlus;
16597
16598 if (inSignDomain == sdBoth) // range may be anywhere
16599 {
16600 QCPDataMap::const_iterator it = mData->constBegin();
16601 while (it != mData->constEnd())
16602 {
16603 current = it.value().value;
16604 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16605 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16606 if (current-currentErrorMinus < range.lower || !haveLower)
16607 {
16608 range.lower = current-currentErrorMinus;
16609 haveLower = true;
16610 }
16611 if (current+currentErrorPlus > range.upper || !haveUpper)
16612 {
16613 range.upper = current+currentErrorPlus;
16614 haveUpper = true;
16615 }
16616 ++it;
16617 }
16618 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16619 {
16620 QCPDataMap::const_iterator it = mData->constBegin();
16621 while (it != mData->constEnd())
16622 {
16623 current = it.value().value;
16624 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16625 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16626 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16627 {
16628 range.lower = current-currentErrorMinus;
16629 haveLower = true;
16630 }
16631 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16632 {
16633 range.upper = current+currentErrorPlus;
16634 haveUpper = true;
16635 }
16636 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16637 {
16638 if ((current < range.lower || !haveLower) && current < 0)
16639 {
16640 range.lower = current;
16641 haveLower = true;
16642 }
16643 if ((current > range.upper || !haveUpper) && current < 0)
16644 {
16645 range.upper = current;
16646 haveUpper = true;
16647 }
16648 }
16649 ++it;
16650 }
16651 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16652 {
16653 QCPDataMap::const_iterator it = mData->constBegin();
16654 while (it != mData->constEnd())
16655 {
16656 current = it.value().value;
16657 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16658 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16659 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16660 {
16661 range.lower = current-currentErrorMinus;
16662 haveLower = true;
16663 }
16664 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16665 {
16666 range.upper = current+currentErrorPlus;
16667 haveUpper = true;
16668 }
16669 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16670 {
16671 if ((current < range.lower || !haveLower) && current > 0)
16672 {
16673 range.lower = current;
16674 haveLower = true;
16675 }
16676 if ((current > range.upper || !haveUpper) && current > 0)
16677 {
16678 range.upper = current;
16679 haveUpper = true;
16680 }
16681 }
16682 ++it;
16683 }
16684 }
16685
16686 foundRange = haveLower && haveUpper;
16687 return range;
16688}
16689
16690
16694
16712 t(0),
16713 key(0),
16714 value(0)
16715{
16716}
16717
16721QCPCurveData::QCPCurveData(double t, double key, double value) :
16722 t(t),
16723 key(key),
16724 value(value)
16725{
16726}
16727
16728
16732
16774QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16775 QCPAbstractPlottable(keyAxis, valueAxis)
16776{
16777 mData = new QCPCurveDataMap;
16778 mPen.setColor(Qt::blue);
16779 mPen.setStyle(Qt::SolidLine);
16780 mBrush.setColor(Qt::blue);
16781 mBrush.setStyle(Qt::NoBrush);
16783 mSelectedPen.setWidthF(2.5);
16784 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16786
16789}
16790
16792{
16793 delete mData;
16794}
16795
16804{
16805 if (copy)
16806 {
16807 *mData = *data;
16808 } else
16809 {
16810 delete mData;
16811 mData = data;
16812 }
16813}
16814
16821void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16822{
16823 mData->clear();
16824 int n = t.size();
16825 n = qMin(n, key.size());
16826 n = qMin(n, value.size());
16827 QCPCurveData newData;
16828 for (int i=0; i<n; ++i)
16829 {
16830 newData.t = t[i];
16831 newData.key = key[i];
16832 newData.value = value[i];
16833 mData->insertMulti(newData.t, newData);
16834 }
16835}
16836
16842void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16843{
16844 mData->clear();
16845 int n = key.size();
16846 n = qMin(n, value.size());
16847 QCPCurveData newData;
16848 for (int i=0; i<n; ++i)
16849 {
16850 newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16851 newData.key = key[i];
16852 newData.value = value[i];
16853 mData->insertMulti(newData.t, newData);
16854 }
16855}
16856
16865{
16866 mScatterStyle = style;
16867}
16868
16877{
16878 mLineStyle = style;
16879}
16880
16886{
16887 mData->unite(dataMap);
16888}
16889
16895{
16896 mData->insertMulti(data.t, data);
16897}
16898
16903void QCPCurve::addData(double t, double key, double value)
16904{
16905 QCPCurveData newData;
16906 newData.t = t;
16907 newData.key = key;
16908 newData.value = value;
16909 mData->insertMulti(newData.t, newData);
16910}
16911
16920void QCPCurve::addData(double key, double value)
16921{
16922 QCPCurveData newData;
16923 if (!mData->isEmpty())
16924 newData.t = (mData->constEnd()-1).key()+1;
16925 else
16926 newData.t = 0;
16927 newData.key = key;
16928 newData.value = value;
16929 mData->insertMulti(newData.t, newData);
16930}
16931
16936void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
16937{
16938 int n = ts.size();
16939 n = qMin(n, keys.size());
16940 n = qMin(n, values.size());
16941 QCPCurveData newData;
16942 for (int i=0; i<n; ++i)
16943 {
16944 newData.t = ts[i];
16945 newData.key = keys[i];
16946 newData.value = values[i];
16947 mData->insertMulti(newData.t, newData);
16948 }
16949}
16950
16956{
16957 QCPCurveDataMap::iterator it = mData->begin();
16958 while (it != mData->end() && it.key() < t)
16959 it = mData->erase(it);
16960}
16961
16967{
16968 if (mData->isEmpty()) return;
16969 QCPCurveDataMap::iterator it = mData->upperBound(t);
16970 while (it != mData->end())
16971 it = mData->erase(it);
16972}
16973
16981void QCPCurve::removeData(double fromt, double tot)
16982{
16983 if (fromt >= tot || mData->isEmpty()) return;
16984 QCPCurveDataMap::iterator it = mData->upperBound(fromt);
16985 QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
16986 while (it != itEnd)
16987 it = mData->erase(it);
16988}
16989
17000{
17001 mData->remove(t);
17002}
17003
17009{
17010 mData->clear();
17011}
17012
17013/* inherits documentation from base class */
17014double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17015{
17016 Q_UNUSED(details)
17017 if ((onlySelectable && !mSelectable) || mData->isEmpty())
17018 return -1;
17019 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17020
17021 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17022 return pointDistance(pos);
17023 else
17024 return -1;
17025}
17026
17027/* inherits documentation from base class */
17029{
17030 if (mData->isEmpty()) return;
17031
17032 // allocate line vector:
17033 QVector<QPointF> *lineData = new QVector<QPointF>;
17034
17035 // fill with curve data:
17036 getCurveData(lineData);
17037
17038 // check data validity if flag set:
17039#ifdef QCUSTOMPLOT_CHECK_DATA
17040 QCPCurveDataMap::const_iterator it;
17041 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17042 {
17043 if (QCP::isInvalidData(it.value().t) ||
17044 QCP::isInvalidData(it.value().key, it.value().value))
17045 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17046 }
17047#endif
17048
17049 // draw curve fill:
17050 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17051 {
17053 painter->setPen(Qt::NoPen);
17054 painter->setBrush(mainBrush());
17055 painter->drawPolygon(QPolygonF(*lineData));
17056 }
17057
17058 // draw curve line:
17059 if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17060 {
17062 painter->setPen(mainPen());
17063 painter->setBrush(Qt::NoBrush);
17064 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17066 painter->pen().style() == Qt::SolidLine &&
17067 !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17068 !painter->modes().testFlag(QCPPainter::pmNoCaching))
17069 {
17070 for (int i=1; i<lineData->size(); ++i)
17071 painter->drawLine(lineData->at(i-1), lineData->at(i));
17072 } else
17073 {
17074 painter->drawPolyline(QPolygonF(*lineData));
17075 }
17076 }
17077
17078 // draw scatters:
17079 if (!mScatterStyle.isNone())
17080 drawScatterPlot(painter, lineData);
17081
17082 // free allocated line data:
17083 delete lineData;
17084}
17085
17086/* inherits documentation from base class */
17087void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17088{
17089 // draw fill:
17090 if (mBrush.style() != Qt::NoBrush)
17091 {
17093 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17094 }
17095 // draw line vertically centered:
17096 if (mLineStyle != lsNone)
17097 {
17099 painter->setPen(mPen);
17100 painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
17101 }
17102 // draw scatter symbol:
17103 if (!mScatterStyle.isNone())
17104 {
17106 // scale scatter pixmap if it's too large to fit in legend icon rect:
17107 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17108 {
17109 QCPScatterStyle scaledStyle(mScatterStyle);
17110 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17111 scaledStyle.applyTo(painter, mPen);
17112 scaledStyle.drawShape(painter, QRectF(rect).center());
17113 } else
17114 {
17115 mScatterStyle.applyTo(painter, mPen);
17116 mScatterStyle.drawShape(painter, QRectF(rect).center());
17117 }
17118 }
17119}
17120
17126void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17127{
17128 // draw scatter point symbols:
17130 mScatterStyle.applyTo(painter, mPen);
17131 for (int i=0; i<pointData->size(); ++i)
17132 mScatterStyle.drawShape(painter, pointData->at(i));
17133}
17134
17141void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17142{
17143 /* Extended sides of axis rect R divide space into 9 regions:
17144 1__|_4_|__7
17145 2__|_R_|__8
17146 3 | 6 | 9
17147 General idea: If the two points of a line segment are in the same region (that is not R), the line segment corner is removed.
17148 Curves outside R become straight lines closely outside of R which greatly reduces drawing time, yet keeps the look of lines and
17149 fills inside R consistent.
17150 The region R has index 5.
17151 */
17152 QCPAxis *keyAxis = mKeyAxis.data();
17153 QCPAxis *valueAxis = mValueAxis.data();
17154 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17155
17156 QRect axisRect = mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
17157 lineData->reserve(mData->size());
17158 QCPCurveDataMap::const_iterator it;
17159 int lastRegion = 5;
17160 int currentRegion = 5;
17161 double RLeft = keyAxis->range().lower;
17162 double RRight = keyAxis->range().upper;
17163 double RBottom = valueAxis->range().lower;
17164 double RTop = valueAxis->range().upper;
17165 double x, y; // current key/value
17166 bool addedLastAlready = true;
17167 bool firstPoint = true; // first point must always be drawn, to make sure fill works correctly
17168 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17169 {
17170 x = it.value().key;
17171 y = it.value().value;
17172 // determine current region:
17173 if (x < RLeft) // region 123
17174 {
17175 if (y > RTop)
17176 currentRegion = 1;
17177 else if (y < RBottom)
17178 currentRegion = 3;
17179 else
17180 currentRegion = 2;
17181 } else if (x > RRight) // region 789
17182 {
17183 if (y > RTop)
17184 currentRegion = 7;
17185 else if (y < RBottom)
17186 currentRegion = 9;
17187 else
17188 currentRegion = 8;
17189 } else // region 456
17190 {
17191 if (y > RTop)
17192 currentRegion = 4;
17193 else if (y < RBottom)
17194 currentRegion = 6;
17195 else
17196 currentRegion = 5;
17197 }
17198
17199 /*
17200 Watch out, the next part is very tricky. It modifies the curve such that it seems like the
17201 whole thing is still drawn, but actually the points outside the axisRect are simplified
17202 ("optimized") greatly. There are some subtle special cases when line segments are large and
17203 thereby each subsequent point may be in a different region or even skip some.
17204 */
17205 // determine whether to keep current point:
17206 if (currentRegion == 5 || (firstPoint && mBrush.style() != Qt::NoBrush)) // current is in R, add current and last if it wasn't added already
17207 {
17208 if (!addedLastAlready) // in case curve just entered R, make sure the last point outside R is also drawn correctly
17209 lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value)); // add last point to vector
17210 else if (lastRegion != 5) // added last already. If that's the case, we probably added it at optimized position. So go back and make sure it's at original position (else the angle changes under which this segment enters R)
17211 {
17212 if (!firstPoint) // because on firstPoint, currentRegion is 5 and addedLastAlready is true, although there is no last point
17213 lineData->replace(lineData->size()-1, coordsToPixels((it-1).value().key, (it-1).value().value));
17214 }
17215 lineData->append(coordsToPixels(it.value().key, it.value().value)); // add current point to vector
17216 addedLastAlready = true; // so in next iteration, we don't add this point twice
17217 } else if (currentRegion != lastRegion) // changed region, add current and last if not added already
17218 {
17219 // using outsideCoordsToPixels instead of coorsToPixels for optimized point placement (places points just outside axisRect instead of potentially far away)
17220
17221 // if we're coming from R or we skip diagonally over the corner regions (so line might still be visible in R), we can't place points optimized
17222 if (lastRegion == 5 || // coming from R
17223 ((lastRegion==2 && currentRegion==4) || (lastRegion==4 && currentRegion==2)) || // skip top left diagonal
17224 ((lastRegion==4 && currentRegion==8) || (lastRegion==8 && currentRegion==4)) || // skip top right diagonal
17225 ((lastRegion==8 && currentRegion==6) || (lastRegion==6 && currentRegion==8)) || // skip bottom right diagonal
17226 ((lastRegion==6 && currentRegion==2) || (lastRegion==2 && currentRegion==6)) // skip bottom left diagonal
17227 )
17228 {
17229 // always add last point if not added already, original:
17230 if (!addedLastAlready)
17231 lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value));
17232 // add current point, original:
17233 lineData->append(coordsToPixels(it.value().key, it.value().value));
17234 } else // no special case that forbids optimized point placement, so do it:
17235 {
17236 // always add last point if not added already, optimized:
17237 if (!addedLastAlready)
17238 lineData->append(outsideCoordsToPixels((it-1).value().key, (it-1).value().value, currentRegion, axisRect));
17239 // add current point, optimized:
17240 lineData->append(outsideCoordsToPixels(it.value().key, it.value().value, currentRegion, axisRect));
17241 }
17242 addedLastAlready = true; // so that if next point enters 5, or crosses another region boundary, we don't add this point twice
17243 } else // neither in R, nor crossed a region boundary, skip current point
17244 {
17245 addedLastAlready = false;
17246 }
17247 lastRegion = currentRegion;
17248 firstPoint = false;
17249 }
17250 // If curve ends outside R, we want to add very last point so the fill looks like it should when the curve started inside R:
17251 if (lastRegion != 5 && mBrush.style() != Qt::NoBrush && !mData->isEmpty())
17252 lineData->append(coordsToPixels((mData->constEnd()-1).value().key, (mData->constEnd()-1).value().value));
17253}
17254
17261double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17262{
17263 if (mData->isEmpty())
17264 {
17265 qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17266 return 500;
17267 }
17268 if (mData->size() == 1)
17269 {
17270 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17271 return QVector2D(dataPoint-pixelPoint).length();
17272 }
17273
17274 // calculate minimum distance to line segments:
17275 QVector<QPointF> *lineData = new QVector<QPointF>;
17276 getCurveData(lineData);
17277 double minDistSqr = std::numeric_limits<double>::max();
17278 for (int i=0; i<lineData->size()-1; ++i)
17279 {
17280 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17281 if (currentDistSqr < minDistSqr)
17282 minDistSqr = currentDistSqr;
17283 }
17284 delete lineData;
17285 return sqrt(minDistSqr);
17286}
17287
17299QPointF QCPCurve::outsideCoordsToPixels(double key, double value, int region, QRect axisRect) const
17300{
17301 int margin = qCeil(qMax(mScatterStyle.size(), (double)mPen.widthF())) + 2;
17302 QPointF result = coordsToPixels(key, value);
17303 switch (region)
17304 {
17305 case 2: result.setX(axisRect.left()-margin); break; // left
17306 case 8: result.setX(axisRect.right()+margin); break; // right
17307 case 4: result.setY(axisRect.top()-margin); break; // top
17308 case 6: result.setY(axisRect.bottom()+margin); break; // bottom
17309 case 1: result.setX(axisRect.left()-margin);
17310 result.setY(axisRect.top()-margin); break; // top left
17311 case 7: result.setX(axisRect.right()+margin);
17312 result.setY(axisRect.top()-margin); break; // top right
17313 case 9: result.setX(axisRect.right()+margin);
17314 result.setY(axisRect.bottom()+margin); break; // bottom right
17315 case 3: result.setX(axisRect.left()-margin);
17316 result.setY(axisRect.bottom()+margin); break; // bottom left
17317 }
17318 return result;
17319}
17320
17321/* inherits documentation from base class */
17322QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17323{
17324 QCPRange range;
17325 bool haveLower = false;
17326 bool haveUpper = false;
17327
17328 double current;
17329
17330 QCPCurveDataMap::const_iterator it = mData->constBegin();
17331 while (it != mData->constEnd())
17332 {
17333 current = it.value().key;
17334 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17335 {
17336 if (current < range.lower || !haveLower)
17337 {
17338 range.lower = current;
17339 haveLower = true;
17340 }
17341 if (current > range.upper || !haveUpper)
17342 {
17343 range.upper = current;
17344 haveUpper = true;
17345 }
17346 }
17347 ++it;
17348 }
17349
17350 foundRange = haveLower && haveUpper;
17351 return range;
17352}
17353
17354/* inherits documentation from base class */
17355QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17356{
17357 QCPRange range;
17358 bool haveLower = false;
17359 bool haveUpper = false;
17360
17361 double current;
17362
17363 QCPCurveDataMap::const_iterator it = mData->constBegin();
17364 while (it != mData->constEnd())
17365 {
17366 current = it.value().value;
17367 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17368 {
17369 if (current < range.lower || !haveLower)
17370 {
17371 range.lower = current;
17372 haveLower = true;
17373 }
17374 if (current > range.upper || !haveUpper)
17375 {
17376 range.upper = current;
17377 haveUpper = true;
17378 }
17379 }
17380 ++it;
17381 }
17382
17383 foundRange = haveLower && haveUpper;
17384 return range;
17385}
17386
17387
17391
17408 key(0),
17409 value(0)
17410{
17411}
17412
17416QCPBarData::QCPBarData(double key, double value) :
17417 key(key),
17418 value(value)
17419{
17420}
17421
17422
17426
17483QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
17484 QCPAbstractPlottable(keyAxis, valueAxis)
17485{
17486 mData = new QCPBarDataMap;
17487 mPen.setColor(Qt::blue);
17488 mPen.setStyle(Qt::SolidLine);
17489 mBrush.setColor(QColor(40, 50, 255, 30));
17490 mBrush.setStyle(Qt::SolidPattern);
17492 mSelectedPen.setWidthF(2.5);
17493 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
17495
17496 mWidth = 0.75;
17497}
17498
17500{
17501 if (mBarBelow || mBarAbove)
17502 connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
17503 delete mData;
17504}
17505
17509void QCPBars::setWidth(double width)
17510{
17511 mWidth = width;
17512}
17513
17522{
17523 if (copy)
17524 {
17525 *mData = *data;
17526 } else
17527 {
17528 delete mData;
17529 mData = data;
17530 }
17531}
17532
17539void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
17540{
17541 mData->clear();
17542 int n = key.size();
17543 n = qMin(n, value.size());
17544 QCPBarData newData;
17545 for (int i=0; i<n; ++i)
17546 {
17547 newData.key = key[i];
17548 newData.value = value[i];
17549 mData->insertMulti(newData.key, newData);
17550 }
17551}
17552
17568{
17569 if (bars == this) return;
17570 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
17571 {
17572 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
17573 return;
17574 }
17575 // remove from stacking:
17576 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
17577 // if new bar given, insert this bar below it:
17578 if (bars)
17579 {
17580 if (bars->mBarBelow)
17581 connectBars(bars->mBarBelow.data(), this);
17582 connectBars(this, bars);
17583 }
17584}
17585
17601{
17602 if (bars == this) return;
17603 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
17604 {
17605 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
17606 return;
17607 }
17608 // remove from stacking:
17609 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
17610 // if new bar given, insert this bar above it:
17611 if (bars)
17612 {
17613 if (bars->mBarAbove)
17614 connectBars(this, bars->mBarAbove.data());
17615 connectBars(bars, this);
17616 }
17617}
17618
17624{
17625 mData->unite(dataMap);
17626}
17627
17633{
17634 mData->insertMulti(data.key, data);
17635}
17636
17641void QCPBars::addData(double key, double value)
17642{
17643 QCPBarData newData;
17644 newData.key = key;
17645 newData.value = value;
17646 mData->insertMulti(newData.key, newData);
17647}
17648
17653void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
17654{
17655 int n = keys.size();
17656 n = qMin(n, values.size());
17657 QCPBarData newData;
17658 for (int i=0; i<n; ++i)
17659 {
17660 newData.key = keys[i];
17661 newData.value = values[i];
17662 mData->insertMulti(newData.key, newData);
17663 }
17664}
17665
17671{
17672 QCPBarDataMap::iterator it = mData->begin();
17673 while (it != mData->end() && it.key() < key)
17674 it = mData->erase(it);
17675}
17676
17682{
17683 if (mData->isEmpty()) return;
17684 QCPBarDataMap::iterator it = mData->upperBound(key);
17685 while (it != mData->end())
17686 it = mData->erase(it);
17687}
17688
17696void QCPBars::removeData(double fromKey, double toKey)
17697{
17698 if (fromKey >= toKey || mData->isEmpty()) return;
17699 QCPBarDataMap::iterator it = mData->upperBound(fromKey);
17700 QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
17701 while (it != itEnd)
17702 it = mData->erase(it);
17703}
17704
17713void QCPBars::removeData(double key)
17714{
17715 mData->remove(key);
17716}
17717
17723{
17724 mData->clear();
17725}
17726
17727/* inherits documentation from base class */
17728double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17729{
17730 Q_UNUSED(details)
17731 if (onlySelectable && !mSelectable)
17732 return -1;
17733 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17734
17735 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17736 {
17737 QCPBarDataMap::ConstIterator it;
17738 double posKey, posValue;
17739 pixelsToCoords(pos, posKey, posValue);
17740 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17741 {
17742 double baseValue = getBaseValue(it.key(), it.value().value >=0);
17743 QCPRange keyRange(it.key()-mWidth*0.5, it.key()+mWidth*0.5);
17744 QCPRange valueRange(baseValue, baseValue+it.value().value);
17745 if (keyRange.contains(posKey) && valueRange.contains(posValue))
17746 return mParentPlot->selectionTolerance()*0.99;
17747 }
17748 }
17749 return -1;
17750}
17751
17752/* inherits documentation from base class */
17754{
17755 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17756 if (mData->isEmpty()) return;
17757
17758 QCPBarDataMap::const_iterator it;
17759 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17760 {
17761 // skip bar if not visible in key axis range:
17762 if (it.key()+mWidth*0.5 < mKeyAxis.data()->range().lower || it.key()-mWidth*0.5 > mKeyAxis.data()->range().upper)
17763 continue;
17764 // check data validity if flag set:
17765#ifdef QCUSTOMPLOT_CHECK_DATA
17766 if (QCP::isInvalidData(it.value().key, it.value().value))
17767 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
17768#endif
17769 QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
17770 // draw bar fill:
17771 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17772 {
17774 painter->setPen(Qt::NoPen);
17775 painter->setBrush(mainBrush());
17776 painter->drawPolygon(barPolygon);
17777 }
17778 // draw bar line:
17779 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17780 {
17782 painter->setPen(mainPen());
17783 painter->setBrush(Qt::NoBrush);
17784 painter->drawPolyline(barPolygon);
17785 }
17786 }
17787}
17788
17789/* inherits documentation from base class */
17790void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17791{
17792 // draw filled rect:
17794 painter->setBrush(mBrush);
17795 painter->setPen(mPen);
17796 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
17797 r.moveCenter(rect.center());
17798 painter->drawRect(r);
17799}
17800
17806QPolygonF QCPBars::getBarPolygon(double key, double value) const
17807{
17808 QPolygonF result;
17809 double baseValue = getBaseValue(key, value >= 0);
17810 result << coordsToPixels(key-mWidth*0.5, baseValue);
17811 result << coordsToPixels(key-mWidth*0.5, baseValue+value);
17812 result << coordsToPixels(key+mWidth*0.5, baseValue+value);
17813 result << coordsToPixels(key+mWidth*0.5, baseValue);
17814 return result;
17815}
17816
17826double QCPBars::getBaseValue(double key, bool positive) const
17827{
17828 if (mBarBelow)
17829 {
17830 double max = 0;
17831 // find bars of mBarBelow that are approximately at key and find largest one:
17832 QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-mWidth*0.1);
17833 QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+mWidth*0.1);
17834 while (it != itEnd)
17835 {
17836 if ((positive && it.value().value > max) ||
17837 (!positive && it.value().value < max))
17838 max = it.value().value;
17839 ++it;
17840 }
17841 // recurse down the bar-stack to find the total height:
17842 return max + mBarBelow.data()->getBaseValue(key, positive);
17843 } else
17844 return 0;
17845}
17846
17856{
17857 if (!lower && !upper) return;
17858
17859 if (!lower) // disconnect upper at bottom
17860 {
17861 // disconnect old bar below upper:
17862 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
17863 upper->mBarBelow.data()->mBarAbove = 0;
17864 upper->mBarBelow = 0;
17865 } else if (!upper) // disconnect lower at top
17866 {
17867 // disconnect old bar above lower:
17868 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
17869 lower->mBarAbove.data()->mBarBelow = 0;
17870 lower->mBarAbove = 0;
17871 } else // connect lower and upper
17872 {
17873 // disconnect old bar above lower:
17874 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
17875 lower->mBarAbove.data()->mBarBelow = 0;
17876 // disconnect old bar below upper:
17877 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
17878 upper->mBarBelow.data()->mBarAbove = 0;
17879 lower->mBarAbove = upper;
17880 upper->mBarBelow = lower;
17881 }
17882}
17883
17884/* inherits documentation from base class */
17885QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17886{
17887 QCPRange range;
17888 bool haveLower = false;
17889 bool haveUpper = false;
17890
17891 double current;
17892 double barWidthHalf = mWidth*0.5;
17893 QCPBarDataMap::const_iterator it = mData->constBegin();
17894 while (it != mData->constEnd())
17895 {
17896 current = it.value().key;
17897 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current+barWidthHalf < 0) || (inSignDomain == sdPositive && current-barWidthHalf > 0))
17898 {
17899 if (current-barWidthHalf < range.lower || !haveLower)
17900 {
17901 range.lower = current-barWidthHalf;
17902 haveLower = true;
17903 }
17904 if (current+barWidthHalf > range.upper || !haveUpper)
17905 {
17906 range.upper = current+barWidthHalf;
17907 haveUpper = true;
17908 }
17909 }
17910 ++it;
17911 }
17912
17913 foundRange = haveLower && haveUpper;
17914 return range;
17915}
17916
17917/* inherits documentation from base class */
17918QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17919{
17920 QCPRange range;
17921 bool haveLower = true; // set to true, because 0 should always be visible in bar charts
17922 bool haveUpper = true; // set to true, because 0 should always be visible in bar charts
17923
17924 double current;
17925
17926 QCPBarDataMap::const_iterator it = mData->constBegin();
17927 while (it != mData->constEnd())
17928 {
17929 current = it.value().value + getBaseValue(it.value().key, it.value().value >= 0);
17930 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17931 {
17932 if (current < range.lower || !haveLower)
17933 {
17934 range.lower = current;
17935 haveLower = true;
17936 }
17937 if (current > range.upper || !haveUpper)
17938 {
17939 range.upper = current;
17940 haveUpper = true;
17941 }
17942 }
17943 ++it;
17944 }
17945
17946 foundRange = true; // return true because bar charts always have the 0-line visible
17947 return range;
17948}
17949
17950
17954
18018 QCPAbstractPlottable(keyAxis, valueAxis),
18019 mKey(0),
18020 mMinimum(0),
18021 mLowerQuartile(0),
18022 mMedian(0),
18023 mUpperQuartile(0),
18024 mMaximum(0)
18025{
18027 setWhiskerWidth(0.2);
18028 setWidth(0.5);
18029
18030 setPen(QPen(Qt::black));
18031 setSelectedPen(QPen(Qt::blue, 2.5));
18032 setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
18033 setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
18034 setWhiskerBarPen(QPen(Qt::black));
18035 setBrush(Qt::NoBrush);
18036 setSelectedBrush(Qt::NoBrush);
18037}
18038
18043{
18044 mKey = key;
18045}
18046
18054{
18055 mMinimum = value;
18056}
18057
18066{
18067 mLowerQuartile = value;
18068}
18069
18078{
18079 mMedian = value;
18080}
18081
18090{
18091 mUpperQuartile = value;
18092}
18093
18101{
18102 mMaximum = value;
18103}
18104
18112void QCPStatisticalBox::setOutliers(const QVector<double> &values)
18113{
18114 mOutliers = values;
18115}
18116
18122void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
18123{
18124 setKey(key);
18130}
18131
18138{
18139 mWidth = width;
18140}
18141
18148{
18150}
18151
18161{
18162 mWhiskerPen = pen;
18163}
18164
18172{
18174}
18175
18180{
18181 mMedianPen = pen;
18182}
18183
18190{
18191 mOutlierStyle = style;
18192}
18193
18194/* inherits documentation from base class */
18196{
18197 setOutliers(QVector<double>());
18198 setKey(0);
18199 setMinimum(0);
18201 setMedian(0);
18203 setMaximum(0);
18204}
18205
18206/* inherits documentation from base class */
18207double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18208{
18209 Q_UNUSED(details)
18210 if (onlySelectable && !mSelectable)
18211 return -1;
18212 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18213
18214 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18215 {
18216 double posKey, posValue;
18217 pixelsToCoords(pos, posKey, posValue);
18218 // quartile box:
18219 QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18221 if (keyRange.contains(posKey) && valueRange.contains(posValue))
18222 return mParentPlot->selectionTolerance()*0.99;
18223
18224 // min/max whiskers:
18225 if (QCPRange(mMinimum, mMaximum).contains(posValue))
18226 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
18227 }
18228 return -1;
18229}
18230
18231/* inherits documentation from base class */
18233{
18234 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18235
18236 // check data validity if flag set:
18237#ifdef QCUSTOMPLOT_CHECK_DATA
18241 qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
18242 for (int i=0; i<mOutliers.size(); ++i)
18243 if (QCP::isInvalidData(mOutliers.at(i)))
18244 qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
18245#endif
18246
18247 QRectF quartileBox;
18248 drawQuartileBox(painter, &quartileBox);
18249
18250 painter->save();
18251 painter->setClipRect(quartileBox, Qt::IntersectClip);
18252 drawMedian(painter);
18253 painter->restore();
18254
18255 drawWhiskers(painter);
18256 drawOutliers(painter);
18257}
18258
18259/* inherits documentation from base class */
18260void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18261{
18262 // draw filled rect:
18264 painter->setPen(mPen);
18265 painter->setBrush(mBrush);
18266 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18267 r.moveCenter(rect.center());
18268 painter->drawRect(r);
18269}
18270
18277void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
18278{
18279 QRectF box;
18280 box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
18281 box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
18283 painter->setPen(mainPen());
18284 painter->setBrush(mainBrush());
18285 painter->drawRect(box);
18286 if (quartileBox)
18287 *quartileBox = box;
18288}
18289
18295{
18296 QLineF medianLine;
18297 medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
18298 medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
18300 painter->setPen(mMedianPen);
18301 painter->drawLine(medianLine);
18302}
18303
18309{
18310 QLineF backboneMin, backboneMax, barMin, barMax;
18311 backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
18312 backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
18316 painter->setPen(mWhiskerPen);
18317 painter->drawLine(backboneMin);
18318 painter->drawLine(backboneMax);
18319 painter->setPen(mWhiskerBarPen);
18320 painter->drawLine(barMin);
18321 painter->drawLine(barMax);
18322}
18323
18329{
18331 mOutlierStyle.applyTo(painter, mPen);
18332 for (int i=0; i<mOutliers.size(); ++i)
18334}
18335
18336/* inherits documentation from base class */
18337QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
18338{
18339 foundRange = true;
18340 if (inSignDomain == sdBoth)
18341 {
18342 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18343 } else if (inSignDomain == sdNegative)
18344 {
18345 if (mKey+mWidth*0.5 < 0)
18346 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18347 else if (mKey < 0)
18348 return QCPRange(mKey-mWidth*0.5, mKey);
18349 else
18350 {
18351 foundRange = false;
18352 return QCPRange();
18353 }
18354 } else if (inSignDomain == sdPositive)
18355 {
18356 if (mKey-mWidth*0.5 > 0)
18357 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18358 else if (mKey > 0)
18359 return QCPRange(mKey, mKey+mWidth*0.5);
18360 else
18361 {
18362 foundRange = false;
18363 return QCPRange();
18364 }
18365 }
18366 foundRange = false;
18367 return QCPRange();
18368}
18369
18370/* inherits documentation from base class */
18371QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
18372{
18373 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
18374 values.reserve(mOutliers.size() + 5);
18376 values << mOutliers;
18377 // go through values and find the ones in legal range:
18378 bool haveUpper = false;
18379 bool haveLower = false;
18380 double upper = 0;
18381 double lower = 0;
18382 for (int i=0; i<values.size(); ++i)
18383 {
18384 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
18385 (inSignDomain == sdPositive && values.at(i) > 0) ||
18386 (inSignDomain == sdBoth))
18387 {
18388 if (values.at(i) > upper || !haveUpper)
18389 {
18390 upper = values.at(i);
18391 haveUpper = true;
18392 }
18393 if (values.at(i) < lower || !haveLower)
18394 {
18395 lower = values.at(i);
18396 haveLower = true;
18397 }
18398 }
18399 }
18400 // return the bounds if we found some sensible values:
18401 if (haveLower && haveUpper)
18402 {
18403 foundRange = true;
18404 return QCPRange(lower, upper);
18405 } else // might happen if all values are in other sign domain
18406 {
18407 foundRange = false;
18408 return QCPRange();
18409 }
18410}
18411
18412
18416
18445/* start of documentation of inline functions */
18446
18453/* end of documentation of inline functions */
18454
18462QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
18463 mKeySize(0),
18464 mValueSize(0),
18465 mKeyRange(keyRange),
18466 mValueRange(valueRange),
18467 mIsEmpty(true),
18468 mData(0),
18469 mDataModified(true)
18470{
18472 fill(0);
18473}
18474
18476{
18477 if (mData)
18478 delete[] mData;
18479}
18480
18485 mKeySize(0),
18486 mValueSize(0),
18487 mIsEmpty(true),
18488 mData(0),
18489 mDataModified(true)
18490{
18491 *this = other;
18492}
18493
18498{
18499 if (&other != this)
18500 {
18501 const int keySize = other.keySize();
18502 const int valueSize = other.valueSize();
18504 setRange(other.keyRange(), other.valueRange());
18505 if (!mIsEmpty)
18506 memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
18507 mDataBounds = other.mDataBounds;
18508 mDataModified = true;
18509 }
18510 return *this;
18511}
18512
18513/* undocumented getter */
18514double QCPColorMapData::data(double key, double value)
18515{
18516 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
18517 int valueCell = (1.0-(value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower))*(mValueSize-1)+0.5;
18518 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
18519 return mData[valueCell*mKeySize + keyCell];
18520 else
18521 return 0;
18522}
18523
18524/* undocumented getter */
18525double QCPColorMapData::cell(int keyIndex, int valueIndex)
18526{
18527 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
18528 return mData[valueIndex*mKeySize + keyIndex];
18529 else
18530 return 0;
18531}
18532
18545void QCPColorMapData::setSize(int keySize, int valueSize)
18546{
18547 if (keySize != mKeySize || valueSize != mValueSize)
18548 {
18549 mKeySize = keySize;
18551 if (mData)
18552 delete[] mData;
18553 mIsEmpty = mKeySize == 0 || mValueSize == 0;
18554 if (!mIsEmpty)
18555 {
18556#ifdef __EXCEPTIONS
18557 try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
18558#endif
18559 mData = new double[mKeySize*mValueSize];
18560#ifdef __EXCEPTIONS
18561 } catch (...) { mData = 0; }
18562#endif
18563 if (mData)
18564 fill(0);
18565 else
18566 qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
18567 } else
18568 mData = 0;
18569 mDataModified = true;
18570 }
18571}
18572
18584{
18586}
18587
18599{
18601}
18602
18613void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
18614{
18617}
18618
18630{
18632}
18633
18645{
18647}
18648
18655void QCPColorMapData::setData(double key, double value, double z)
18656{
18657 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
18658 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
18659 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
18660 {
18661 mData[valueCell*mKeySize + keyCell] = z;
18662 if (z < mDataBounds.lower)
18664 if (z > mDataBounds.upper)
18666 mDataModified = true;
18667 }
18668}
18669
18681void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
18682{
18683 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
18684 {
18685 mData[valueIndex*mKeySize + keyIndex] = z;
18686 if (z < mDataBounds.lower)
18688 if (z > mDataBounds.upper)
18690 mDataModified = true;
18691 }
18692}
18693
18708{
18709 if (mKeySize > 0 && mValueSize > 0)
18710 {
18711 double minHeight = mData[0];
18712 double maxHeight = mData[0];
18713 const int dataCount = mValueSize*mKeySize;
18714 for (int i=0; i<dataCount; ++i)
18715 {
18716 if (mData[i] > maxHeight)
18717 maxHeight = mData[i];
18718 if (mData[i] < minHeight)
18719 minHeight = mData[i];
18720 }
18721 mDataBounds.lower = minHeight;
18722 mDataBounds.upper = maxHeight;
18723 }
18724}
18725
18732{
18733 setSize(0, 0);
18734}
18735
18740{
18741 const int dataCount = mValueSize*mKeySize;
18742 for (int i=0; i<dataCount; ++i)
18743 mData[i] = z;
18744 mDataBounds = QCPRange(z, z);
18745}
18746
18759void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
18760{
18761 if (keyIndex)
18762 *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
18763 if (valueIndex)
18764 *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
18765}
18766
18777void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
18778{
18779 if (key)
18780 *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
18781 if (value)
18782 *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
18783}
18784
18785
18789
18862/* start documentation of inline functions */
18863
18872/* end documentation of inline functions */
18873
18874/* start documentation of signals */
18875
18897/* end documentation of signals */
18898
18906 QCPAbstractPlottable(keyAxis, valueAxis),
18907 mDataScaleType(QCPAxis::stLinear),
18908 mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
18909 mInterpolate(true),
18910 mTightBoundary(false),
18911 mMapImageInvalidated(true)
18912{
18913}
18914
18916{
18917 delete mMapData;
18918}
18919
18928{
18929 if (copy)
18930 {
18931 *mMapData = *data;
18932 } else
18933 {
18934 delete mMapData;
18935 mMapData = data;
18936 }
18937 mMapImageInvalidated = true;
18938}
18939
18949{
18950 if (!QCPRange::validRange(dataRange)) return;
18952 {
18955 else
18957 mMapImageInvalidated = true;
18959 }
18960}
18961
18968{
18969 if (mDataScaleType != scaleType)
18970 {
18971 mDataScaleType = scaleType;
18972 mMapImageInvalidated = true;
18976 }
18977}
18978
18991{
18992 if (mGradient != gradient)
18993 {
18995 mMapImageInvalidated = true;
18997 }
18998}
18999
19007{
19008 mInterpolate = enabled;
19009}
19010
19023{
19024 mTightBoundary = enabled;
19025}
19026
19042{
19043 if (mColorScale) // unconnect signals from old color scale
19044 {
19045 disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
19047 disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
19048 disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19049 disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
19051 }
19053 if (mColorScale) // connect signals to new color scale
19054 {
19055 setGradient(mColorScale.data()->gradient());
19056 setDataRange(mColorScale.data()->dataRange());
19057 setDataScaleType(mColorScale.data()->dataScaleType());
19058 connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
19060 connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
19061 connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19062 connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
19064 }
19065}
19066
19087void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
19088{
19089 if (recalculateDataBounds)
19092}
19093
19108void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
19109{
19110 if (mMapImage.isNull() && !data()->isEmpty())
19111 updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
19112
19113 if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
19114 {
19115 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
19116 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
19117 mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
19118 }
19119}
19120
19126{
19127 mMapData->clear();
19128}
19129
19130/* inherits documentation from base class */
19131double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19132{
19133 Q_UNUSED(details)
19134 if (onlySelectable && !mSelectable)
19135 return -1;
19136 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19137
19138 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19139 {
19140 double posKey, posValue;
19141 pixelsToCoords(pos, posKey, posValue);
19142 if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
19143 return mParentPlot->selectionTolerance()*0.99;
19144 }
19145 return -1;
19146}
19147
19158{
19159 QCPAxis *keyAxis = mKeyAxis.data();
19160 if (!keyAxis) return;
19161
19162 // resize mMapImage to correct dimensions, according to key/value axes orientation:
19163 if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.size().width() != mMapData->keySize() || mMapImage.size().height() != mMapData->valueSize()))
19164 mMapImage = QImage(QSize(mMapData->keySize(), mMapData->valueSize()), QImage::Format_RGB32);
19165 else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.size().width() != mMapData->valueSize() || mMapImage.size().height() != mMapData->keySize()))
19166 mMapImage = QImage(QSize(mMapData->valueSize(), mMapData->keySize()), QImage::Format_RGB32);
19167
19168 const int keySize = mMapData->keySize();
19169 const int valueSize = mMapData->valueSize();
19170 const double *rawData = mMapData->mData;
19171
19172 if (keyAxis->orientation() == Qt::Horizontal)
19173 {
19174 const int lineCount = valueSize;
19175 const int rowCount = keySize;
19176 for (int line=0; line<lineCount; ++line)
19177 {
19178 QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
19179 mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
19180 }
19181 } else // keyAxis->orientation() == Qt::Vertical
19182 {
19183 const int lineCount = keySize;
19184 const int rowCount = valueSize;
19185 for (int line=0; line<lineCount; ++line)
19186 {
19187 QRgb* pixels = reinterpret_cast<QRgb*>(mMapImage.scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
19188 mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
19189 }
19190 }
19191
19192 mMapData->mDataModified = false;
19193 mMapImageInvalidated = false;
19194}
19195
19196/* inherits documentation from base class */
19198{
19199 if (mMapData->isEmpty()) return;
19200 if (!mKeyAxis || !mValueAxis) return;
19202
19205
19206 double halfSampleKey = 0;
19207 double halfSampleValue = 0;
19208 if (mMapData->keySize() > 1)
19209 halfSampleKey = 0.5*mMapData->keyRange().size()/(double)(mMapData->keySize()-1);
19210 if (mMapData->valueSize() > 1)
19211 halfSampleValue = 0.5*mMapData->valueRange().size()/(double)(mMapData->valueSize()-1);
19212 QRectF imageRect(coordsToPixels(mMapData->keyRange().lower-halfSampleKey, mMapData->valueRange().lower-halfSampleValue),
19213 coordsToPixels(mMapData->keyRange().upper+halfSampleKey, mMapData->valueRange().upper+halfSampleValue));
19214 imageRect = imageRect.normalized();
19215 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
19216 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
19217 bool smoothBackup = painter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
19218 painter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
19219 QRegion clipBackup;
19220 if (mTightBoundary)
19221 {
19222 clipBackup = painter->clipRegion();
19223 painter->setClipRect(QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
19224 coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(), Qt::IntersectClip);
19225 }
19226 painter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
19227 if (mTightBoundary)
19228 painter->setClipRegion(clipBackup);
19229 painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
19230}
19231
19232/* inherits documentation from base class */
19233void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19234{
19236 // draw map thumbnail:
19237 if (!mLegendIcon.isNull())
19238 {
19239 QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
19240 QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
19241 iconRect.moveCenter(rect.center());
19242 painter->drawPixmap(iconRect.topLeft(), scaledIcon);
19243 }
19244 /*
19245 // draw frame:
19246 painter->setBrush(Qt::NoBrush);
19247 painter->setPen(Qt::black);
19248 painter->drawRect(rect.adjusted(1, 1, 0, 0));
19249 */
19250}
19251
19252/* inherits documentation from base class */
19253QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19254{
19255 foundRange = true;
19256 QCPRange result = mMapData->keyRange();
19257 result.normalize();
19258 if (inSignDomain == QCPAbstractPlottable::sdPositive)
19259 {
19260 if (result.lower <= 0 && result.upper > 0)
19261 result.lower = result.upper*1e-3;
19262 else if (result.lower <= 0 && result.upper <= 0)
19263 foundRange = false;
19264 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
19265 {
19266 if (result.upper >= 0 && result.lower < 0)
19267 result.upper = result.lower*1e-3;
19268 else if (result.upper >= 0 && result.lower >= 0)
19269 foundRange = false;
19270 }
19271 return result;
19272}
19273
19274/* inherits documentation from base class */
19275QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19276{
19277 foundRange = true;
19278 QCPRange result = mMapData->valueRange();
19279 result.normalize();
19280 if (inSignDomain == QCPAbstractPlottable::sdPositive)
19281 {
19282 if (result.lower <= 0 && result.upper > 0)
19283 result.lower = result.upper*1e-3;
19284 else if (result.lower <= 0 && result.upper <= 0)
19285 foundRange = false;
19286 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
19287 {
19288 if (result.upper >= 0 && result.lower < 0)
19289 result.upper = result.lower*1e-3;
19290 else if (result.upper >= 0 && result.lower >= 0)
19291 foundRange = false;
19292 }
19293 return result;
19294}
19295
19296
19300
19315 QCPAbstractItem(parentPlot),
19316 point1(createPosition("point1")),
19317 point2(createPosition("point2"))
19318{
19319 point1->setCoords(0, 0);
19320 point2->setCoords(1, 1);
19321
19322 setPen(QPen(Qt::black));
19323 setSelectedPen(QPen(Qt::blue,2));
19324}
19325
19329
19335void QCPItemStraightLine::setPen(const QPen &pen)
19336{
19337 mPen = pen;
19338}
19339
19346{
19347 mSelectedPen = pen;
19348}
19349
19350/* inherits documentation from base class */
19351double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19352{
19353 Q_UNUSED(details)
19354 if (onlySelectable && !mSelectable)
19355 return -1;
19356
19357 return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
19358}
19359
19360/* inherits documentation from base class */
19362{
19363 QVector2D start(point1->pixelPoint());
19364 QVector2D end(point2->pixelPoint());
19365 // get visible segment of straight line inside clipRect:
19366 double clipPad = mainPen().widthF();
19367 QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
19368 // paint visible segment, if existent:
19369 if (!line.isNull())
19370 {
19371 painter->setPen(mainPen());
19372 painter->drawLine(line);
19373 }
19374}
19375
19383double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
19384{
19385 return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
19386}
19387
19395QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
19396{
19397 double bx, by;
19398 double gamma;
19399 QLineF result;
19400 if (vec.x() == 0 && vec.y() == 0)
19401 return result;
19402 if (qFuzzyIsNull(vec.x())) // line is vertical
19403 {
19404 // check top of rect:
19405 bx = rect.left();
19406 by = rect.top();
19407 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
19408 if (gamma >= 0 && gamma <= rect.width())
19409 result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
19410 } else if (qFuzzyIsNull(vec.y())) // line is horizontal
19411 {
19412 // check left of rect:
19413 bx = rect.left();
19414 by = rect.top();
19415 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
19416 if (gamma >= 0 && gamma <= rect.height())
19417 result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
19418 } else // line is skewed
19419 {
19420 QList<QVector2D> pointVectors;
19421 // check top of rect:
19422 bx = rect.left();
19423 by = rect.top();
19424 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
19425 if (gamma >= 0 && gamma <= rect.width())
19426 pointVectors.append(QVector2D(bx+gamma, by));
19427 // check bottom of rect:
19428 bx = rect.left();
19429 by = rect.bottom();
19430 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
19431 if (gamma >= 0 && gamma <= rect.width())
19432 pointVectors.append(QVector2D(bx+gamma, by));
19433 // check left of rect:
19434 bx = rect.left();
19435 by = rect.top();
19436 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
19437 if (gamma >= 0 && gamma <= rect.height())
19438 pointVectors.append(QVector2D(bx, by+gamma));
19439 // check right of rect:
19440 bx = rect.right();
19441 by = rect.top();
19442 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
19443 if (gamma >= 0 && gamma <= rect.height())
19444 pointVectors.append(QVector2D(bx, by+gamma));
19445
19446 // evaluate points:
19447 if (pointVectors.size() == 2)
19448 {
19449 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
19450 } else if (pointVectors.size() > 2)
19451 {
19452 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
19453 double distSqrMax = 0;
19454 QVector2D pv1, pv2;
19455 for (int i=0; i<pointVectors.size()-1; ++i)
19456 {
19457 for (int k=i+1; k<pointVectors.size(); ++k)
19458 {
19459 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
19460 if (distSqr > distSqrMax)
19461 {
19462 pv1 = pointVectors.at(i);
19463 pv2 = pointVectors.at(k);
19464 distSqrMax = distSqr;
19465 }
19466 }
19467 }
19468 result.setPoints(pv1.toPointF(), pv2.toPointF());
19469 }
19470 }
19471 return result;
19472}
19473
19480{
19481 return mSelected ? mSelectedPen : mPen;
19482}
19483
19484
19488
19505 QCPAbstractItem(parentPlot),
19506 start(createPosition("start")),
19507 end(createPosition("end"))
19508{
19509 start->setCoords(0, 0);
19510 end->setCoords(1, 1);
19511
19512 setPen(QPen(Qt::black));
19513 setSelectedPen(QPen(Qt::blue,2));
19514}
19515
19519
19525void QCPItemLine::setPen(const QPen &pen)
19526{
19527 mPen = pen;
19528}
19529
19535void QCPItemLine::setSelectedPen(const QPen &pen)
19536{
19537 mSelectedPen = pen;
19538}
19539
19549{
19550 mHead = head;
19551}
19552
19562{
19563 mTail = tail;
19564}
19565
19566/* inherits documentation from base class */
19567double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19568{
19569 Q_UNUSED(details)
19570 if (onlySelectable && !mSelectable)
19571 return -1;
19572
19573 return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
19574}
19575
19576/* inherits documentation from base class */
19578{
19579 QVector2D startVec(start->pixelPoint());
19580 QVector2D endVec(end->pixelPoint());
19581 if (startVec.toPoint() == endVec.toPoint())
19582 return;
19583 // get visible segment of straight line inside clipRect:
19584 double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
19585 clipPad = qMax(clipPad, (double)mainPen().widthF());
19586 QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
19587 // paint visible segment, if existent:
19588 if (!line.isNull())
19589 {
19590 painter->setPen(mainPen());
19591 painter->drawLine(line);
19592 painter->setBrush(Qt::SolidPattern);
19594 mTail.draw(painter, startVec, startVec-endVec);
19596 mHead.draw(painter, endVec, endVec-startVec);
19597 }
19598}
19599
19607QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
19608{
19609 bool containsStart = rect.contains(start.x(), start.y());
19610 bool containsEnd = rect.contains(end.x(), end.y());
19611 if (containsStart && containsEnd)
19612 return QLineF(start.toPointF(), end.toPointF());
19613
19614 QVector2D base = start;
19615 QVector2D vec = end-start;
19616 double bx, by;
19617 double gamma, mu;
19618 QLineF result;
19619 QList<QVector2D> pointVectors;
19620
19621 if (!qFuzzyIsNull(vec.y())) // line is not horizontal
19622 {
19623 // check top of rect:
19624 bx = rect.left();
19625 by = rect.top();
19626 mu = (by-base.y())/vec.y();
19627 if (mu >= 0 && mu <= 1)
19628 {
19629 gamma = base.x()-bx + mu*vec.x();
19630 if (gamma >= 0 && gamma <= rect.width())
19631 pointVectors.append(QVector2D(bx+gamma, by));
19632 }
19633 // check bottom of rect:
19634 bx = rect.left();
19635 by = rect.bottom();
19636 mu = (by-base.y())/vec.y();
19637 if (mu >= 0 && mu <= 1)
19638 {
19639 gamma = base.x()-bx + mu*vec.x();
19640 if (gamma >= 0 && gamma <= rect.width())
19641 pointVectors.append(QVector2D(bx+gamma, by));
19642 }
19643 }
19644 if (!qFuzzyIsNull(vec.x())) // line is not vertical
19645 {
19646 // check left of rect:
19647 bx = rect.left();
19648 by = rect.top();
19649 mu = (bx-base.x())/vec.x();
19650 if (mu >= 0 && mu <= 1)
19651 {
19652 gamma = base.y()-by + mu*vec.y();
19653 if (gamma >= 0 && gamma <= rect.height())
19654 pointVectors.append(QVector2D(bx, by+gamma));
19655 }
19656 // check right of rect:
19657 bx = rect.right();
19658 by = rect.top();
19659 mu = (bx-base.x())/vec.x();
19660 if (mu >= 0 && mu <= 1)
19661 {
19662 gamma = base.y()-by + mu*vec.y();
19663 if (gamma >= 0 && gamma <= rect.height())
19664 pointVectors.append(QVector2D(bx, by+gamma));
19665 }
19666 }
19667
19668 if (containsStart)
19669 pointVectors.append(start);
19670 if (containsEnd)
19671 pointVectors.append(end);
19672
19673 // evaluate points:
19674 if (pointVectors.size() == 2)
19675 {
19676 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
19677 } else if (pointVectors.size() > 2)
19678 {
19679 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
19680 double distSqrMax = 0;
19681 QVector2D pv1, pv2;
19682 for (int i=0; i<pointVectors.size()-1; ++i)
19683 {
19684 for (int k=i+1; k<pointVectors.size(); ++k)
19685 {
19686 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
19687 if (distSqr > distSqrMax)
19688 {
19689 pv1 = pointVectors.at(i);
19690 pv2 = pointVectors.at(k);
19691 distSqrMax = distSqr;
19692 }
19693 }
19694 }
19695 result.setPoints(pv1.toPointF(), pv2.toPointF());
19696 }
19697 return result;
19698}
19699
19706{
19707 return mSelected ? mSelectedPen : mPen;
19708}
19709
19710
19714
19738 QCPAbstractItem(parentPlot),
19739 start(createPosition("start")),
19740 startDir(createPosition("startDir")),
19741 endDir(createPosition("endDir")),
19742 end(createPosition("end"))
19743{
19744 start->setCoords(0, 0);
19745 startDir->setCoords(0.5, 0);
19746 endDir->setCoords(0, 0.5);
19747 end->setCoords(1, 1);
19748
19749 setPen(QPen(Qt::black));
19750 setSelectedPen(QPen(Qt::blue,2));
19751}
19752
19756
19762void QCPItemCurve::setPen(const QPen &pen)
19763{
19764 mPen = pen;
19765}
19766
19772void QCPItemCurve::setSelectedPen(const QPen &pen)
19773{
19774 mSelectedPen = pen;
19775}
19776
19786{
19787 mHead = head;
19788}
19789
19799{
19800 mTail = tail;
19801}
19802
19803/* inherits documentation from base class */
19804double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19805{
19806 Q_UNUSED(details)
19807 if (onlySelectable && !mSelectable)
19808 return -1;
19809
19810 QPointF startVec(start->pixelPoint());
19811 QPointF startDirVec(startDir->pixelPoint());
19812 QPointF endDirVec(endDir->pixelPoint());
19813 QPointF endVec(end->pixelPoint());
19814
19815 QPainterPath cubicPath(startVec);
19816 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
19817
19818 QPolygonF polygon = cubicPath.toSubpathPolygons().first();
19819 double minDistSqr = std::numeric_limits<double>::max();
19820 for (int i=1; i<polygon.size(); ++i)
19821 {
19822 double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
19823 if (distSqr < minDistSqr)
19824 minDistSqr = distSqr;
19825 }
19826 return qSqrt(minDistSqr);
19827}
19828
19829/* inherits documentation from base class */
19831{
19832 QPointF startVec(start->pixelPoint());
19833 QPointF startDirVec(startDir->pixelPoint());
19834 QPointF endDirVec(endDir->pixelPoint());
19835 QPointF endVec(end->pixelPoint());
19836 if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
19837 return;
19838
19839 QPainterPath cubicPath(startVec);
19840 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
19841
19842 // paint visible segment, if existent:
19843 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
19844 QRect cubicRect = cubicPath.controlPointRect().toRect();
19845 if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
19846 cubicRect.adjust(0, 0, 1, 1);
19847 if (clip.intersects(cubicRect))
19848 {
19849 painter->setPen(mainPen());
19850 painter->drawPath(cubicPath);
19851 painter->setBrush(Qt::SolidPattern);
19853 mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
19855 mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
19856 }
19857}
19858
19865{
19866 return mSelected ? mSelectedPen : mPen;
19867}
19868
19869
19873
19888 QCPAbstractItem(parentPlot),
19889 topLeft(createPosition("topLeft")),
19890 bottomRight(createPosition("bottomRight")),
19891 top(createAnchor("top", aiTop)),
19892 topRight(createAnchor("topRight", aiTopRight)),
19893 right(createAnchor("right", aiRight)),
19894 bottom(createAnchor("bottom", aiBottom)),
19895 bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
19896 left(createAnchor("left", aiLeft))
19897{
19898 topLeft->setCoords(0, 1);
19899 bottomRight->setCoords(1, 0);
19900
19901 setPen(QPen(Qt::black));
19902 setSelectedPen(QPen(Qt::blue,2));
19903 setBrush(Qt::NoBrush);
19904 setSelectedBrush(Qt::NoBrush);
19905}
19906
19910
19916void QCPItemRect::setPen(const QPen &pen)
19917{
19918 mPen = pen;
19919}
19920
19926void QCPItemRect::setSelectedPen(const QPen &pen)
19927{
19928 mSelectedPen = pen;
19929}
19930
19937void QCPItemRect::setBrush(const QBrush &brush)
19938{
19939 mBrush = brush;
19940}
19941
19948void QCPItemRect::setSelectedBrush(const QBrush &brush)
19949{
19951}
19952
19953/* inherits documentation from base class */
19954double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19955{
19956 Q_UNUSED(details)
19957 if (onlySelectable && !mSelectable)
19958 return -1;
19959
19960 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
19961 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
19962 return rectSelectTest(rect, pos, filledRect);
19963}
19964
19965/* inherits documentation from base class */
19967{
19968 QPointF p1 = topLeft->pixelPoint();
19969 QPointF p2 = bottomRight->pixelPoint();
19970 if (p1.toPoint() == p2.toPoint())
19971 return;
19972 QRectF rect = QRectF(p1, p2).normalized();
19973 double clipPad = mainPen().widthF();
19974 QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
19975 if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
19976 {
19977 painter->setPen(mainPen());
19978 painter->setBrush(mainBrush());
19979 painter->drawRect(rect);
19980 }
19981}
19982
19983/* inherits documentation from base class */
19984QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
19985{
19986 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
19987 switch (anchorId)
19988 {
19989 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
19990 case aiTopRight: return rect.topRight();
19991 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
19992 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
19993 case aiBottomLeft: return rect.bottomLeft();
19994 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
19995 }
19996
19997 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
19998 return QPointF();
19999}
20000
20007{
20008 return mSelected ? mSelectedPen : mPen;
20009}
20010
20017{
20018 return mSelected ? mSelectedBrush : mBrush;
20019}
20020
20021
20025
20046 QCPAbstractItem(parentPlot),
20047 position(createPosition("position")),
20048 topLeft(createAnchor("topLeft", aiTopLeft)),
20049 top(createAnchor("top", aiTop)),
20050 topRight(createAnchor("topRight", aiTopRight)),
20051 right(createAnchor("right", aiRight)),
20052 bottomRight(createAnchor("bottomRight", aiBottomRight)),
20053 bottom(createAnchor("bottom", aiBottom)),
20054 bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
20055 left(createAnchor("left", aiLeft))
20056{
20057 position->setCoords(0, 0);
20058
20059 setRotation(0);
20060 setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
20061 setPositionAlignment(Qt::AlignCenter);
20062 setText("text");
20063
20064 setPen(Qt::NoPen);
20065 setSelectedPen(Qt::NoPen);
20066 setBrush(Qt::NoBrush);
20067 setSelectedBrush(Qt::NoBrush);
20068 setColor(Qt::black);
20069 setSelectedColor(Qt::blue);
20070}
20071
20075
20079void QCPItemText::setColor(const QColor &color)
20080{
20081 mColor = color;
20082}
20083
20087void QCPItemText::setSelectedColor(const QColor &color)
20088{
20090}
20091
20098void QCPItemText::setPen(const QPen &pen)
20099{
20100 mPen = pen;
20101}
20102
20109void QCPItemText::setSelectedPen(const QPen &pen)
20110{
20111 mSelectedPen = pen;
20112}
20113
20120void QCPItemText::setBrush(const QBrush &brush)
20121{
20122 mBrush = brush;
20123}
20124
20131void QCPItemText::setSelectedBrush(const QBrush &brush)
20132{
20134}
20135
20141void QCPItemText::setFont(const QFont &font)
20142{
20143 mFont = font;
20144}
20145
20151void QCPItemText::setSelectedFont(const QFont &font)
20152{
20154}
20155
20162void QCPItemText::setText(const QString &text)
20163{
20164 mText = text;
20165}
20166
20179void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
20180{
20181 mPositionAlignment = alignment;
20182}
20183
20187void QCPItemText::setTextAlignment(Qt::Alignment alignment)
20188{
20189 mTextAlignment = alignment;
20190}
20191
20196void QCPItemText::setRotation(double degrees)
20197{
20198 mRotation = degrees;
20199}
20200
20205void QCPItemText::setPadding(const QMargins &padding)
20206{
20207 mPadding = padding;
20208}
20209
20210/* inherits documentation from base class */
20211double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20212{
20213 Q_UNUSED(details)
20214 if (onlySelectable && !mSelectable)
20215 return -1;
20216
20217 // The rect may be rotated, so we transform the actual clicked pos to the rotated
20218 // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
20219 QPointF positionPixels(position->pixelPoint());
20220 QTransform inputTransform;
20221 inputTransform.translate(positionPixels.x(), positionPixels.y());
20222 inputTransform.rotate(-mRotation);
20223 inputTransform.translate(-positionPixels.x(), -positionPixels.y());
20224 QPointF rotatedPos = inputTransform.map(pos);
20225 QFontMetrics fontMetrics(mFont);
20226 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
20227 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
20228 QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
20229 textBoxRect.moveTopLeft(textPos.toPoint());
20230
20231 return rectSelectTest(textBoxRect, rotatedPos, true);
20232}
20233
20234/* inherits documentation from base class */
20236{
20237 QPointF pos(position->pixelPoint());
20238 QTransform transform = painter->transform();
20239 transform.translate(pos.x(), pos.y());
20240 if (!qFuzzyIsNull(mRotation))
20241 transform.rotate(mRotation);
20242 painter->setFont(mainFont());
20243 QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
20244 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
20245 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
20246 textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
20247 textBoxRect.moveTopLeft(textPos.toPoint());
20248 double clipPad = mainPen().widthF();
20249 QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
20250 if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
20251 {
20252 painter->setTransform(transform);
20253 if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
20254 (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
20255 {
20256 painter->setPen(mainPen());
20257 painter->setBrush(mainBrush());
20258 painter->drawRect(textBoxRect);
20259 }
20260 painter->setBrush(Qt::NoBrush);
20261 painter->setPen(QPen(mainColor()));
20262 painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
20263 }
20264}
20265
20266/* inherits documentation from base class */
20267QPointF QCPItemText::anchorPixelPoint(int anchorId) const
20268{
20269 // get actual rect points (pretty much copied from draw function):
20270 QPointF pos(position->pixelPoint());
20271 QTransform transform;
20272 transform.translate(pos.x(), pos.y());
20273 if (!qFuzzyIsNull(mRotation))
20274 transform.rotate(mRotation);
20275 QFontMetrics fontMetrics(mainFont());
20276 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
20277 QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
20278 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
20279 textBoxRect.moveTopLeft(textPos.toPoint());
20280 QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
20281
20282 switch (anchorId)
20283 {
20284 case aiTopLeft: return rectPoly.at(0);
20285 case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
20286 case aiTopRight: return rectPoly.at(1);
20287 case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
20288 case aiBottomRight: return rectPoly.at(2);
20289 case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
20290 case aiBottomLeft: return rectPoly.at(3);
20291 case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
20292 }
20293
20294 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
20295 return QPointF();
20296}
20297
20308QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
20309{
20310 if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
20311 return pos;
20312
20313 QPointF result = pos; // start at top left
20314 if (positionAlignment.testFlag(Qt::AlignHCenter))
20315 result.rx() -= rect.width()/2.0;
20316 else if (positionAlignment.testFlag(Qt::AlignRight))
20317 result.rx() -= rect.width();
20318 if (positionAlignment.testFlag(Qt::AlignVCenter))
20319 result.ry() -= rect.height()/2.0;
20320 else if (positionAlignment.testFlag(Qt::AlignBottom))
20321 result.ry() -= rect.height();
20322 return result;
20323}
20324
20331{
20332 return mSelected ? mSelectedFont : mFont;
20333}
20334
20341{
20342 return mSelected ? mSelectedColor : mColor;
20343}
20344
20351{
20352 return mSelected ? mSelectedPen : mPen;
20353}
20354
20361{
20362 return mSelected ? mSelectedBrush : mBrush;
20363}
20364
20365
20369
20384 QCPAbstractItem(parentPlot),
20385 topLeft(createPosition("topLeft")),
20386 bottomRight(createPosition("bottomRight")),
20387 topLeftRim(createAnchor("topLeftRim", aiTopLeftRim)),
20388 top(createAnchor("top", aiTop)),
20389 topRightRim(createAnchor("topRightRim", aiTopRightRim)),
20390 right(createAnchor("right", aiRight)),
20391 bottomRightRim(createAnchor("bottomRightRim", aiBottomRightRim)),
20392 bottom(createAnchor("bottom", aiBottom)),
20393 bottomLeftRim(createAnchor("bottomLeftRim", aiBottomLeftRim)),
20394 left(createAnchor("left", aiLeft)),
20395 center(createAnchor("center", aiCenter))
20396{
20397 topLeft->setCoords(0, 1);
20398 bottomRight->setCoords(1, 0);
20399
20400 setPen(QPen(Qt::black));
20401 setSelectedPen(QPen(Qt::blue, 2));
20402 setBrush(Qt::NoBrush);
20403 setSelectedBrush(Qt::NoBrush);
20404}
20405
20409
20415void QCPItemEllipse::setPen(const QPen &pen)
20416{
20417 mPen = pen;
20418}
20419
20426{
20427 mSelectedPen = pen;
20428}
20429
20436void QCPItemEllipse::setBrush(const QBrush &brush)
20437{
20438 mBrush = brush;
20439}
20440
20447void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
20448{
20450}
20451
20452/* inherits documentation from base class */
20453double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20454{
20455 Q_UNUSED(details)
20456 if (onlySelectable && !mSelectable)
20457 return -1;
20458
20459 double result = -1;
20460 QPointF p1 = topLeft->pixelPoint();
20461 QPointF p2 = bottomRight->pixelPoint();
20462 QPointF center((p1+p2)/2.0);
20463 double a = qAbs(p1.x()-p2.x())/2.0;
20464 double b = qAbs(p1.y()-p2.y())/2.0;
20465 double x = pos.x()-center.x();
20466 double y = pos.y()-center.y();
20467
20468 // distance to border:
20469 double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
20470 result = qAbs(c-1)*qSqrt(x*x+y*y);
20471 // filled ellipse, allow click inside to count as hit:
20472 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
20473 {
20474 if (x*x/(a*a) + y*y/(b*b) <= 1)
20475 result = mParentPlot->selectionTolerance()*0.99;
20476 }
20477 return result;
20478}
20479
20480/* inherits documentation from base class */
20482{
20483 QPointF p1 = topLeft->pixelPoint();
20484 QPointF p2 = bottomRight->pixelPoint();
20485 if (p1.toPoint() == p2.toPoint())
20486 return;
20487 QRectF ellipseRect = QRectF(p1, p2).normalized();
20488 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
20489 if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
20490 {
20491 painter->setPen(mainPen());
20492 painter->setBrush(mainBrush());
20493#ifdef __EXCEPTIONS
20494 try // drawEllipse sometimes throws exceptions if ellipse is too big
20495 {
20496#endif
20497 painter->drawEllipse(ellipseRect);
20498#ifdef __EXCEPTIONS
20499 } catch (...)
20500 {
20501 qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
20502 setVisible(false);
20503 }
20504#endif
20505 }
20506}
20507
20508/* inherits documentation from base class */
20509QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
20510{
20511 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
20512 switch (anchorId)
20513 {
20514 case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
20515 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
20516 case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
20517 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
20518 case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
20519 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
20520 case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
20521 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
20522 case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
20523 }
20524
20525 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
20526 return QPointF();
20527}
20528
20535{
20536 return mSelected ? mSelectedPen : mPen;
20537}
20538
20545{
20546 return mSelected ? mSelectedBrush : mBrush;
20547}
20548
20549
20553
20574 QCPAbstractItem(parentPlot),
20575 topLeft(createPosition("topLeft")),
20576 bottomRight(createPosition("bottomRight")),
20577 top(createAnchor("top", aiTop)),
20578 topRight(createAnchor("topRight", aiTopRight)),
20579 right(createAnchor("right", aiRight)),
20580 bottom(createAnchor("bottom", aiBottom)),
20581 bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
20582 left(createAnchor("left", aiLeft))
20583{
20584 topLeft->setCoords(0, 1);
20585 bottomRight->setCoords(1, 0);
20586
20587 setPen(Qt::NoPen);
20588 setSelectedPen(QPen(Qt::blue));
20589 setScaled(false, Qt::KeepAspectRatio);
20590}
20591
20595
20599void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
20600{
20601 mPixmap = pixmap;
20602 if (mPixmap.isNull())
20603 qDebug() << Q_FUNC_INFO << "pixmap is null";
20604}
20605
20610void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode)
20611{
20612 mScaled = scaled;
20615}
20616
20622void QCPItemPixmap::setPen(const QPen &pen)
20623{
20624 mPen = pen;
20625}
20626
20633{
20634 mSelectedPen = pen;
20635}
20636
20637/* inherits documentation from base class */
20638double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20639{
20640 Q_UNUSED(details)
20641 if (onlySelectable && !mSelectable)
20642 return -1;
20643
20644 return rectSelectTest(getFinalRect(), pos, true);
20645}
20646
20647/* inherits documentation from base class */
20649{
20650 bool flipHorz = false;
20651 bool flipVert = false;
20652 QRect rect = getFinalRect(&flipHorz, &flipVert);
20653 double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
20654 QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
20655 if (boundingRect.intersects(clipRect()))
20656 {
20657 updateScaledPixmap(rect, flipHorz, flipVert);
20658 painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
20659 QPen pen = mainPen();
20660 if (pen.style() != Qt::NoPen)
20661 {
20662 painter->setPen(pen);
20663 painter->setBrush(Qt::NoBrush);
20664 painter->drawRect(rect);
20665 }
20666 }
20667}
20668
20669/* inherits documentation from base class */
20670QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
20671{
20672 bool flipHorz;
20673 bool flipVert;
20674 QRect rect = getFinalRect(&flipHorz, &flipVert);
20675 // we actually want denormal rects (negative width/height) here, so restore
20676 // the flipped state:
20677 if (flipHorz)
20678 rect.adjust(rect.width(), 0, -rect.width(), 0);
20679 if (flipVert)
20680 rect.adjust(0, rect.height(), 0, -rect.height());
20681
20682 switch (anchorId)
20683 {
20684 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
20685 case aiTopRight: return rect.topRight();
20686 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
20687 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
20688 case aiBottomLeft: return rect.bottomLeft();
20689 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
20690 }
20691
20692 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
20693 return QPointF();
20694}
20695
20709void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
20710{
20711 if (mPixmap.isNull())
20712 return;
20713
20714 if (mScaled)
20715 {
20716 if (finalRect.isNull())
20717 finalRect = getFinalRect(&flipHorz, &flipVert);
20718 if (finalRect.size() != mScaledPixmap.size())
20719 {
20720 mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, Qt::SmoothTransformation);
20721 if (flipHorz || flipVert)
20722 mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
20723 }
20724 } else if (!mScaledPixmap.isNull())
20725 mScaledPixmap = QPixmap();
20726}
20727
20742QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
20743{
20744 QRect result;
20745 bool flipHorz = false;
20746 bool flipVert = false;
20747 QPoint p1 = topLeft->pixelPoint().toPoint();
20748 QPoint p2 = bottomRight->pixelPoint().toPoint();
20749 if (p1 == p2)
20750 return QRect(p1, QSize(0, 0));
20751 if (mScaled)
20752 {
20753 QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
20754 QPoint topLeft = p1;
20755 if (newSize.width() < 0)
20756 {
20757 flipHorz = true;
20758 newSize.rwidth() *= -1;
20759 topLeft.setX(p2.x());
20760 }
20761 if (newSize.height() < 0)
20762 {
20763 flipVert = true;
20764 newSize.rheight() *= -1;
20765 topLeft.setY(p2.y());
20766 }
20767 QSize scaledSize = mPixmap.size();
20768 scaledSize.scale(newSize, mAspectRatioMode);
20769 result = QRect(topLeft, scaledSize);
20770 } else
20771 {
20772 result = QRect(p1, mPixmap.size());
20773 }
20774 if (flippedHorz)
20775 *flippedHorz = flipHorz;
20776 if (flippedVert)
20777 *flippedVert = flipVert;
20778 return result;
20779}
20780
20787{
20788 return mSelected ? mSelectedPen : mPen;
20789}
20790
20791
20795
20832 QCPAbstractItem(parentPlot),
20833 position(createPosition("position")),
20834 mGraph(0)
20835{
20836 position->setCoords(0, 0);
20837
20838 setBrush(Qt::NoBrush);
20839 setSelectedBrush(Qt::NoBrush);
20840 setPen(QPen(Qt::black));
20841 setSelectedPen(QPen(Qt::blue, 2));
20843 setSize(6);
20844 setInterpolating(false);
20845 setGraphKey(0);
20846}
20847
20851
20857void QCPItemTracer::setPen(const QPen &pen)
20858{
20859 mPen = pen;
20860}
20861
20868{
20869 mSelectedPen = pen;
20870}
20871
20877void QCPItemTracer::setBrush(const QBrush &brush)
20878{
20879 mBrush = brush;
20880}
20881
20887void QCPItemTracer::setSelectedBrush(const QBrush &brush)
20888{
20890}
20891
20896void QCPItemTracer::setSize(double size)
20897{
20898 mSize = size;
20899}
20900
20911
20923{
20924 if (graph)
20925 {
20926 if (graph->parentPlot() == mParentPlot)
20927 {
20930 mGraph = graph;
20932 } else
20933 qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
20934 } else
20935 {
20936 mGraph = 0;
20937 }
20938}
20939
20950{
20951 mGraphKey = key;
20952}
20953
20966{
20967 mInterpolating = enabled;
20968}
20969
20970/* inherits documentation from base class */
20971double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20972{
20973 Q_UNUSED(details)
20974 if (onlySelectable && !mSelectable)
20975 return -1;
20976
20977 QPointF center(position->pixelPoint());
20978 double w = mSize/2.0;
20979 QRect clip = clipRect();
20980 switch (mStyle)
20981 {
20982 case tsNone: return -1;
20983 case tsPlus:
20984 {
20985 if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
20986 return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
20987 distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
20988 break;
20989 }
20990 case tsCrosshair:
20991 {
20992 return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
20993 distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
20994 }
20995 case tsCircle:
20996 {
20997 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
20998 {
20999 // distance to border:
21000 double centerDist = QVector2D(center-pos).length();
21001 double circleLine = w;
21002 double result = qAbs(centerDist-circleLine);
21003 // filled ellipse, allow click inside to count as hit:
21004 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
21005 {
21006 if (centerDist <= circleLine)
21007 result = mParentPlot->selectionTolerance()*0.99;
21008 }
21009 return result;
21010 }
21011 break;
21012 }
21013 case tsSquare:
21014 {
21015 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21016 {
21017 QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
21018 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
21019 return rectSelectTest(rect, pos, filledRect);
21020 }
21021 break;
21022 }
21023 }
21024 return -1;
21025}
21026
21027/* inherits documentation from base class */
21029{
21031 if (mStyle == tsNone)
21032 return;
21033
21034 painter->setPen(mainPen());
21035 painter->setBrush(mainBrush());
21036 QPointF center(position->pixelPoint());
21037 double w = mSize/2.0;
21038 QRect clip = clipRect();
21039 switch (mStyle)
21040 {
21041 case tsNone: return;
21042 case tsPlus:
21043 {
21044 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21045 {
21046 painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
21047 painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
21048 }
21049 break;
21050 }
21051 case tsCrosshair:
21052 {
21053 if (center.y() > clip.top() && center.y() < clip.bottom())
21054 painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
21055 if (center.x() > clip.left() && center.x() < clip.right())
21056 painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
21057 break;
21058 }
21059 case tsCircle:
21060 {
21061 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21062 painter->drawEllipse(center, w, w);
21063 break;
21064 }
21065 case tsSquare:
21066 {
21067 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
21068 painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
21069 break;
21070 }
21071 }
21072}
21073
21087{
21088 if (mGraph)
21089 {
21091 {
21092 if (mGraph->data()->size() > 1)
21093 {
21094 QCPDataMap::const_iterator first = mGraph->data()->constBegin();
21095 QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
21096 if (mGraphKey < first.key())
21097 position->setCoords(first.key(), first.value().value);
21098 else if (mGraphKey > last.key())
21099 position->setCoords(last.key(), last.value().value);
21100 else
21101 {
21102 QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
21103 if (it != first) // mGraphKey is somewhere between iterators
21104 {
21105 QCPDataMap::const_iterator prevIt = it-1;
21106 if (mInterpolating)
21107 {
21108 // interpolate between iterators around mGraphKey:
21109 double slope = 0;
21110 if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
21111 slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
21112 position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
21113 } else
21114 {
21115 // find iterator with key closest to mGraphKey:
21116 if (mGraphKey < (prevIt.key()+it.key())*0.5)
21117 it = prevIt;
21118 position->setCoords(it.key(), it.value().value);
21119 }
21120 } else // mGraphKey is exactly on first iterator
21121 position->setCoords(it.key(), it.value().value);
21122 }
21123 } else if (mGraph->data()->size() == 1)
21124 {
21125 QCPDataMap::const_iterator it = mGraph->data()->constBegin();
21126 position->setCoords(it.key(), it.value().value);
21127 } else
21128 qDebug() << Q_FUNC_INFO << "graph has no data";
21129 } else
21130 qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
21131 }
21132}
21133
21140{
21141 return mSelected ? mSelectedPen : mPen;
21142}
21143
21150{
21151 return mSelected ? mSelectedBrush : mBrush;
21152}
21153
21154
21158
21185 QCPAbstractItem(parentPlot),
21186 left(createPosition("left")),
21187 right(createPosition("right")),
21188 center(createAnchor("center", aiCenter))
21189{
21190 left->setCoords(0, 0);
21191 right->setCoords(1, 1);
21192
21193 setPen(QPen(Qt::black));
21194 setSelectedPen(QPen(Qt::blue, 2));
21195 setLength(8);
21197}
21198
21202
21212void QCPItemBracket::setPen(const QPen &pen)
21213{
21214 mPen = pen;
21215}
21216
21223{
21224 mSelectedPen = pen;
21225}
21226
21235void QCPItemBracket::setLength(double length)
21236{
21237 mLength = length;
21238}
21239
21249
21250/* inherits documentation from base class */
21251double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21252{
21253 Q_UNUSED(details)
21254 if (onlySelectable && !mSelectable)
21255 return -1;
21256
21257 QVector2D leftVec(left->pixelPoint());
21258 QVector2D rightVec(right->pixelPoint());
21259 if (leftVec.toPoint() == rightVec.toPoint())
21260 return -1;
21261
21262 QVector2D widthVec = (rightVec-leftVec)*0.5f;
21263 QVector2D lengthVec(-widthVec.y(), widthVec.x());
21264 lengthVec = lengthVec.normalized()*mLength;
21265 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
21266
21267 return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
21268}
21269
21270/* inherits documentation from base class */
21272{
21273 QVector2D leftVec(left->pixelPoint());
21274 QVector2D rightVec(right->pixelPoint());
21275 if (leftVec.toPoint() == rightVec.toPoint())
21276 return;
21277
21278 QVector2D widthVec = (rightVec-leftVec)*0.5f;
21279 QVector2D lengthVec(-widthVec.y(), widthVec.x());
21280 lengthVec = lengthVec.normalized()*mLength;
21281 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
21282
21283 QPolygon boundingPoly;
21284 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
21285 << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
21286 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
21287 if (clip.intersects(boundingPoly.boundingRect()))
21288 {
21289 painter->setPen(mainPen());
21290 switch (mStyle)
21291 {
21292 case bsSquare:
21293 {
21294 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
21295 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
21296 painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21297 break;
21298 }
21299 case bsRound:
21300 {
21301 painter->setBrush(Qt::NoBrush);
21302 QPainterPath path;
21303 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
21304 path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
21305 path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21306 painter->drawPath(path);
21307 break;
21308 }
21309 case bsCurly:
21310 {
21311 painter->setBrush(Qt::NoBrush);
21312 QPainterPath path;
21313 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
21314 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
21315 path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21316 painter->drawPath(path);
21317 break;
21318 }
21319 case bsCalligraphic:
21320 {
21321 painter->setPen(Qt::NoPen);
21322 painter->setBrush(QBrush(mainPen().color()));
21323 QPainterPath path;
21324 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
21325
21326 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
21327 path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
21328
21329 path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
21330 path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
21331
21332 painter->drawPath(path);
21333 break;
21334 }
21335 }
21336 }
21337}
21338
21339/* inherits documentation from base class */
21340QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
21341{
21342 QVector2D leftVec(left->pixelPoint());
21343 QVector2D rightVec(right->pixelPoint());
21344 if (leftVec.toPoint() == rightVec.toPoint())
21345 return leftVec.toPointF();
21346
21347 QVector2D widthVec = (rightVec-leftVec)*0.5f;
21348 QVector2D lengthVec(-widthVec.y(), widthVec.x());
21349 lengthVec = lengthVec.normalized()*mLength;
21350 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
21351
21352 switch (anchorId)
21353 {
21354 case aiCenter:
21355 return centerVec.toPointF();
21356 }
21357 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
21358 return QPointF();
21359}
21360
21367{
21368 return mSelected ? mSelectedPen : mPen;
21369}
21370
@ data
#define M_PI
Definition XSensMTx.cpp:24
The abstract base class for all items in a plot.
QCPItemAnchor * anchor(const QString &name) const
Returns the QCPItemAnchor with the specified name.
Q_SLOT void setSelected(bool selected)
Sets whether this item is selected or not.
QCPItemPosition * position(const QString &name) const
Returns the QCPItemPosition with the specified name.
double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
virtual ~QCPAbstractItem()
void setClipToAxisRect(bool clip)
Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the entire Q...
QPointer< QCPAxisRect > mClipAxisRect
bool clipToAxisRect() const
virtual QRect clipRect() const
virtual QPointF anchorPixelPoint(int anchorId) const
virtual QCP::Interaction selectionCategory() const
void selectableChanged(bool selectable)
friend class QCPItemAnchor
QCPItemPosition * createPosition(const QString &name)
void setClipAxisRect(QCPAxisRect *rect)
Sets the clip axis rect.
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
bool hasAnchor(const QString &name) const
Returns whether this item has an anchor with the specified name.
Q_SLOT void setSelectable(bool selectable)
Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
QList< QCPItemAnchor * > mAnchors
virtual void deselectEvent(bool *selectionStateChanged)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
This function is used to decide whether a click hits a layerable object or not.
QCPAbstractItem(QCustomPlot *parentPlot)
Base class constructor which initializes base class members.
bool selected() const
void selectionChanged(bool selected)
This signal is emitted when the selection state of this item has changed, either by user interaction ...
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPAxisRect * clipAxisRect() const
bool selectable() const
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
QList< QCPItemPosition * > mPositions
The abstract base class for all entries in a QCPLegend.
void setFont(const QFont &font)
Sets the default font of this specific legend item to font.
void setSelectedTextColor(const QColor &color)
When this legend item is selected, color is used to draw generic text, instead of the normal color se...
void setTextColor(const QColor &color)
Sets the default text color of this specific legend item to color.
Q_SLOT void setSelected(bool selected)
Sets whether this specific legend item is selected.
void selectionChanged(bool selected)
This signal is emitted when the selection state of this legend item has changed, either by user inter...
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setSelectedFont(const QFont &font)
When this legend item is selected, font is used to draw generic text, instead of the normal font set ...
Q_SLOT void setSelectable(bool selectable)
Sets whether this specific legend item is selectable.
virtual QCP::Interaction selectionCategory() const
QCPLegend * mParentLegend
void selectableChanged(bool selectable)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
virtual QRect clipRect() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
virtual void deselectEvent(bool *selectionStateChanged)
bool selectable() const
QCPAbstractLegendItem(QCPLegend *parent)
Constructs a QCPAbstractLegendItem and associates it with the QCPLegend parent.
The abstract base class for all data representing objects in a plot.
void selectableChanged(bool selectable)
This signal is emitted when the selectability of this plottable has changed.
void applyErrorBarsAntialiasingHint(QCPPainter *painter) const
void setAntialiasedFill(bool enabled)
Sets whether fills of this plottable is drawn antialiased or not.
bool selected() const
friend class QCPPlottableLegendItem
void rescaleAxes(bool onlyEnlarge=false) const
Rescales the key and value axes associated with this plottable to contain all displayed data,...
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Q_SLOT void setSelectable(bool selectable)
Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
QPointer< QCPAxis > mValueAxis
void setAntialiasedScatters(bool enabled)
Sets whether the scatter symbols of this plottable are drawn antialiased or not.
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
This function is used to decide whether a click hits a layerable object or not.
void pixelsToCoords(double x, double y, double &key, double &value) const
void selectionChanged(bool selected)
This signal is emitted when the selection state of this plottable has changed, either by user interac...
QPointer< QCPAxis > mKeyAxis
QString name() const
void applyDefaultAntialiasingHint(QCPPainter *painter) const
SignDomain
Represents negative and positive sign domain for passing to getKeyRange and getValueRange.
@ sdPositive
The positive sign domain, i.e. numbers greater than zero.
@ sdBoth
Both sign domains, including zero, i.e. all (rational) numbers.
@ sdNegative
The negative sign domain, i.e. numbers smaller than zero.
void setSelectedPen(const QPen &pen)
When the plottable is selected, this pen is used to draw basic lines instead of the normal pen set vi...
virtual void deselectEvent(bool *selectionStateChanged)
virtual bool addToLegend()
Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
void setValueAxis(QCPAxis *axis)
The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal to...
void setAntialiasedErrorBars(bool enabled)
Sets whether the error bars of this plottable are drawn antialiased or not.
void setBrush(const QBrush &brush)
The brush is used to draw basic fills of the plottable representation in the plot.
void coordsToPixels(double key, double value, double &x, double &y) const
QCPAxis * valueAxis() const
void setKeyAxis(QCPAxis *axis)
The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal to t...
QBrush brush() const
void applyFillAntialiasingHint(QCPPainter *painter) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
void rescaleValueAxis(bool onlyEnlarge=false) const
Rescales the value axis of the plottable so the whole plottable is visible.
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const =0
QCPAxis * keyAxis() const
virtual QRect clipRect() const
void setPen(const QPen &pen)
The pen is used to draw basic lines that make up the plottable representation in the plot.
void setName(const QString &name)
The name is the textual representation of this plottable as it is displayed in the legend (QCPLegend)...
QBrush mainBrush() const
void applyScattersAntialiasingHint(QCPPainter *painter) const
virtual bool removeFromLegend() const
Removes the plottable from the legend of the parent QCustomPlot.
bool selectable() const
void setSelectedBrush(const QBrush &brush)
When the plottable is selected, this brush is used to draw fills instead of the normal brush set via ...
void rescaleKeyAxis(bool onlyEnlarge=false) const
Rescales the key axis of the plottable so the whole plottable is visible.
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs an abstract plottable which uses keyAxis as its key axis ("x") and valueAxis as its value ...
double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
virtual QCP::Interaction selectionCategory() const
Q_SLOT void setSelected(bool selected)
Sets whether this plottable is selected or not.
virtual void draw(QCPPainter *painter)
QCPLineEnding lowerEnding
QCache< QString, CachedLabel > mLabelCache
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
Constructs a QCPAxisPainterPrivate instance.
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
virtual int size() const
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
QCustomPlot * mParentPlot
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
QByteArray mLabelParameterHash
QRect axisSelectionBox() const
virtual QByteArray generateLabelParameterHash() const
QRect tickLabelsSelectionBox() const
QVector< QString > tickLabels
QCPAxis::AxisType type
QVector< double > tickPositions
QRect labelSelectionBox() const
QCPLineEnding upperEnding
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
QVector< double > subTickPositions
Holds multiple axes and arranges them in a rectangular shape.
QPoint mDragStart
QList< QCPAbstractItem * > items() const
Returns a list of all the items that are associated with this axis rect.
bool removeAxis(QCPAxis *axis)
Removes the specified axis from the axis rect and deletes it.
QList< QCPAxis * > axes() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QCPRange mDragStartVertRange
int width() const
Returns the pixel width of this axis rect.
Qt::Orientations mRangeZoom
virtual void update(UpdatePhase phase)
This method is called automatically upon replot and doesn't need to be called by users of QCPAxisRect...
QCPLayoutInset * mInsetLayout
QList< QCPGraph * > graphs() const
Returns a list of all the graphs that are associated with this axis rect.
double mRangeZoomFactorVert
QPixmap mBackgroundPixmap
QPointer< QCPAxis > mRangeDragVertAxis
int right() const
Returns the pixel position of the right border of this axis rect.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
QCPRange mDragStartHorzRange
int top() const
Returns the pixel position of the top border of this axis rect.
virtual ~QCPAxisRect()
virtual void mouseMoveEvent(QMouseEvent *event)
This event is called, if the mouse is moved inside the outer rect of this layout element.
QBrush mBackgroundBrush
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
Returns the axis with the given index on the axis rect side specified with type.
QList< QCPAbstractPlottable * > plottables() const
Returns a list of all the plottables that are associated with this axis rect.
virtual void wheelEvent(QWheelEvent *event)
This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this layo...
bool mBackgroundScaled
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
If scaling of the axis background pixmap is enabled (setBackgroundScaled), use this function to defin...
void setupFullAxesBox(bool connectRanges=false)
Convenience function to create an axis on each side that doesn't have any axes yet and set their visi...
void updateAxesOffset(QCPAxis::AxisType type)
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
Sets the axes whose range will be dragged when setRangeDrag enables mouse range dragging on the QCust...
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
Returns the range zoom axis of the orientation provided.
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
Returns the range drag axis of the orientation provided.
QCP::AntialiasedElements mNotAADragBackup
virtual void mousePressEvent(QMouseEvent *event)
This event is called, if the mouse was pressed while being inside the outer rect of this layout eleme...
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
Adds a new axis with addAxis to each axis rect side specified in types.
void setRangeZoom(Qt::Orientations orientations)
Sets which axis orientation may be zoomed by the user with the mouse wheel.
QSize size() const
Returns the pixel size of this axis rect.
Qt::AspectRatioMode mBackgroundScaledMode
int axisCount(QCPAxis::AxisType type) const
Returns the number of axes on the axis rect side specified with type.
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with setRan...
QPointer< QCPAxis > mRangeZoomVertAxis
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
Returns all axes on the axis rect sides specified with types.
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
Sets the axes whose range will be zoomed when setRangeZoom enables mouse wheel zooming on the QCustom...
QCPLayoutInset * insetLayout() const
Returns the inset layout of this axis rect.
QCP::AntialiasedElements mAADragBackup
QPixmap mScaledBackgroundPixmap
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
Qt::Orientations mRangeDrag
void drawBackground(QCPPainter *painter)
QCPAxis * addAxis(QCPAxis::AxisType type)
Adds a new axis to the axis rect side specified with type, and returns it.
int height() const
Returns the pixel height of this axis rect.
int bottom() const
Returns the pixel position of the bottom border of this axis rect.
double mRangeZoomFactorHorz
virtual void mouseReleaseEvent(QMouseEvent *event)
This event is called, if the mouse was previously pressed inside the outer rect of this layout elemen...
QPointer< QCPAxis > mRangeZoomHorzAxis
double rangeZoomFactor(Qt::Orientation orientation)
Returns the range zoom factor of the orientation provided.
void setRangeDrag(Qt::Orientations orientations)
Sets which axis orientation may be range dragged by the user with mouse interaction.
void setBackgroundScaled(bool scaled)
Sets whether the axis background pixmap shall be scaled to fit the axis rect or not.
virtual int calculateAutoMargin(QCP::MarginSide side)
QPointer< QCPAxis > mRangeDragHorzAxis
void setBackground(const QPixmap &pm)
Sets pm as the axis background pixmap.
virtual void draw(QCPPainter *painter)
int left() const
Returns the pixel position of the left border of this axis rect.
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Manages a single axis inside a QCustomPlot.
QCPAxisRect * axisRect() const
void setSelectedLabelFont(const QFont &font)
Sets the font that is used for the axis label when it is selected.
void setOffset(int offset)
Sets the offset the axis has to its axis rect side.
virtual QCP::Interaction selectionCategory() const
void setTickLabels(bool show)
Sets whether tick labels are displayed.
int padding() const
void rangeChanged(const QCPRange &newRange)
This signal is emitted when the range of this axis has changed.
void setLowerEnding(const QCPLineEnding &ending)
Sets the style for the lower axis ending.
QString mDateTimeFormat
LabelType tickLabelType() const
QCPLineEnding lowerEnding() const
bool autoTickStep() const
QCPGrid * mGrid
void moveRange(double diff)
If the scale type (setScaleType) is stLinear, diff is added to the lower and upper bounds of the rang...
void setTickLabelRotation(double degrees)
Sets the rotation of the tick labels.
QPen mTickPen
QCPRange mRange
QString numberFormat() const
void setRangeReversed(bool reversed)
Sets whether the axis range (direction) is displayed reversed.
void setNumberPrecision(int precision)
Sets the precision of the tick label numbers.
SelectablePart getPartAt(const QPointF &pos) const
Returns the part of the axis that is hit by pos (in pixels).
int numberPrecision() const
virtual void draw(QCPPainter *painter)
void setDateTimeSpec(const Qt::TimeSpec &timeSpec)
Sets the time spec that is used for the date time values when setTickLabelType is ltDateTime.
QVector< double > mSubTickVector
void setSelectedSubTickPen(const QPen &pen)
Sets the pen that is used to draw the subticks when selected.
void setTickLabelFont(const QFont &font)
Sets the font of the tick labels.
QString dateTimeFormat() const
bool mCachedMarginValid
void setDateTimeFormat(const QString &format)
Sets the format in which dates and times are displayed as tick labels, if setTickLabelType is ltDateT...
QPen mSubTickPen
void setLabel(const QString &str)
Sets the text of the axis label that will be shown below/above or next to the axis,...
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
This signal is emitted when the scale type changes, by calls to setScaleType.
ScaleType
Defines the scale of an axis.
@ stLogarithmic
Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power...
@ stLinear
Linear scaling.
QFont mLabelFont
void setTickLabelColor(const QColor &color)
Sets the color of the tick labels.
void setTickLengthOut(int outside)
Sets the length of the outward ticks in pixels.
QColor mSelectedTickLabelColor
bool mTickLabels
int autoTickCount() const
QList< QCPAbstractItem * > items() const
Returns a list of all the items that are associated with this axis.
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
void setLabelPadding(int padding)
Sets the distance between the tick labels and the axis label.
QColor mLabelColor
virtual int calculateMargin()
int mCachedMargin
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void rescale(bool onlyVisiblePlottables=false)
Changes the axis range such that all plottables associated with this axis are fully visible in that d...
int mAutoTickCount
LabelType
When automatic tick label generation is enabled (setAutoTickLabels), defines how the coordinate of th...
@ ltNumber
Tick coordinate is regarded as normal number and will be displayed as such. (see setNumberFormat)
@ ltDateTime
Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displa...
void setSubTickCount(int count)
Sets the number of sub ticks in one (major) tick step.
void setSubTickLengthOut(int outside)
Sets the length of the outward subticks in pixels.
QFont mSelectedTickLabelFont
double mTickStep
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
Sets whether the user can (de-)select the parts in selectable by clicking on the QCustomPlot surface.
virtual void deselectEvent(bool *selectionStateChanged)
double pixelToCoord(double value) const
Transforms value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
void setTickLabelType(LabelType type)
Sets whether the tick labels display numbers or dates/times.
void setPadding(int padding)
Sets the padding of the axis.
virtual void setupTickVectors()
bool ticks() const
double tickLabelRotation() const
bool mRangeReversed
void setSelectedLabelColor(const QColor &color)
Sets the color that is used for the axis label when it is selected.
char mNumberFormatChar
virtual void generateAutoTicks()
void selectionChanged(const QCPAxis::SelectableParts &parts)
This signal is emitted when the selection state of this axis has changed, either by user interaction ...
void setTickLength(int inside, int outside=0)
Sets the length of the ticks in pixels.
QColor mTickLabelColor
QCPGrid * grid() const
Returns the QCPGrid instance belonging to this axis.
void setUpperEnding(const QCPLineEnding &ending)
Sets the style for the upper axis ending.
QFont getTickLabelFont() const
void setLabelColor(const QColor &color)
Sets the color of the axis label.
int labelPadding() const
LabelType mTickLabelType
QCPAxisRect * mAxisRect
void scaleRange(double factor, double center)
Scales the range of this axis by factor around the coordinate center.
void setLabelFont(const QFont &font)
Sets the font of the axis label.
bool mAutoTickLabels
void setScaleLogBase(double base)
If setScaleType is set to stLogarithmic, base will be the logarithm base of the scaling.
void setBasePen(const QPen &pen)
Sets the pen, the axis base line is drawn with.
void setAutoTickCount(int approximateCount)
When setAutoTickStep is true, approximateCount determines how many ticks should be generated in the v...
virtual ~QCPAxis()
QPen mSelectedBasePen
void setSelectedTickPen(const QPen &pen)
Sets the pen that is used to draw the (major) ticks when selected.
void setSelectedTickLabelFont(const QFont &font)
Sets the font that is used for tick labels when they are selected.
void setTickVector(const QVector< double > &vec)
If you want full control over what ticks (and possibly labels) the axes show, this function is used t...
SelectableParts selectedParts() const
QPen getBasePen() const
QColor getTickLabelColor() const
double scaleLogBase() const
SelectableParts mSelectedParts
void setTickVectorLabels(const QVector< QString > &vec)
If you want full control over what ticks and labels the axes show, this function is used to set a num...
double mScaleLogBaseLogInv
QColor mSelectedLabelColor
double tickStep() const
QPen mSelectedTickPen
void setAutoTickStep(bool on)
Sets whether the tick step, i.e.
void setSelectedTickLabelColor(const QColor &color)
Sets the color that is used for tick labels when they are selected.
QCPLineEnding upperEnding() const
AxisType axisType() const
QPen mSelectedSubTickPen
void selectableChanged(const QCPAxis::SelectableParts &parts)
This signal is emitted when the selectability changes, by calls to setSelectableParts.
static AxisType opposite(AxisType type)
Returns the axis type that describes the opposite axis of an axis with the specified type.
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
void setAutoTickLabels(bool on)
Sets whether the tick labels are generated automatically.
double basePow(double value) const
bool mAutoSubTicks
bool mAutoTicks
QPen getSubTickPen() const
bool mAutoTickStep
QVector< double > mTickVector
bool mTicks
void setSubTickLength(int inside, int outside=0)
Sets the length of the subticks in pixels.
SelectableParts mSelectableParts
bool rangeReversed() const
Qt::Orientation orientation() const
Returns the orientation of this axis.
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
Sets the selected state of the respective axis parts described by SelectablePart.
int subTickCount() const
int mHighestVisibleTick
double mScaleLogBase
SelectablePart
Defines the selectable parts of an axis.
@ spTickLabels
Tick labels (numbers) of this axis (as a whole, not individually)
@ spAxisLabel
The axis label.
@ spAxis
The axis backbone and tick marks.
@ spNone
None of the selectable parts.
static AxisType marginSideToAxisType(QCP::MarginSide side)
Transforms a margin side to the logically corresponding axis type.
const QCPRange range() const
void setSubTickLengthIn(int inside)
Sets the length of the inward subticks in pixels.
QList< QCPAbstractPlottable * > plottables() const
Returns a list of all the plottables that have this axis as key or value axis.
QCPAxis(QCPAxisRect *parent, AxisType type)
Constructs an Axis instance of Type type for the axis rect parent.
void setTicks(bool show)
Sets whether tick marks are displayed.
int subTickLengthOut() const
virtual int calculateAutoSubTickCount(double tickStep) const
void setRangeUpper(double upper)
Sets the upper bound of the axis range.
int mNumberPrecision
int tickLengthIn() const
ScaleType scaleType() const
int tickLengthOut() const
QList< QCPGraph * > graphs() const
Returns a list of all the graphs that have this axis as key or value axis.
QPen mBasePen
int mSubTickCount
ScaleType mScaleType
void setTickPen(const QPen &pen)
Sets the pen, tick marks will be drawn with.
void setAutoSubTicks(bool on)
Sets whether the number of sub ticks in one tick interval is determined automatically.
QFont mTickLabelFont
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
Sets whether the axis uses a linear scale or a logarithmic scale.
AxisType
Defines at which side of the axis rect the axis will appear.
@ atBottom
0x08 Axis is horizontal and on the bottom side of the axis rect
@ atTop
0x04 Axis is horizontal and on the top side of the axis rect
@ atRight
0x02 Axis is vertical and on the right side of the axis rect
@ atLeft
0x01 Axis is vertical and on the left side of the axis rect
bool autoSubTicks() const
bool tickLabels() const
QFont mSelectedLabelFont
void setNumberFormat(const QString &formatCode)
Sets the number format for the numbers drawn as tick labels (if tick label type is ltNumber).
AxisType mAxisType
double baseLog(double value) const
QString mLabel
void setAutoTicks(bool on)
Sets whether the tick positions should be calculated automatically (either from an automatically gene...
QColor getLabelColor() const
Qt::TimeSpec dateTimeSpec() const
QFont getLabelFont() const
void setSelectedBasePen(const QPen &pen)
Sets the pen that is used to draw the axis base line when selected.
int mLowestVisibleTick
Q_SLOT void setRange(const QCPRange &range)
Sets the range of the axis.
void visibleTickBounds(int &lowIndex, int &highIndex) const
void setSubTickPen(const QPen &pen)
Sets the pen, subtick marks will be drawn with.
QCPAxisPainterPrivate * mAxisPainter
QVector< QString > mTickVectorLabels
int offset() const
bool mNumberBeautifulPowers
double coordToPixel(double value) const
Transforms value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
void setTickLabelPadding(int padding)
Sets the distance between the axis base line (including any outward ticks) and the tick labels.
void ticksRequest()
This signal is emitted when setAutoTicks is false and the axis is about to generate tick labels for a...
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
Scales the range of this axis to have a certain scale ratio to otherAxis.
void setTickStep(double step)
If setAutoTickStep is set to false, use this function to set the tick step manually.
Qt::TimeSpec mDateTimeSpec
int subTickLengthIn() const
int tickLabelPadding() const
void setTickLengthIn(int inside)
Sets the length of the inward ticks in pixels.
void setRangeLower(double lower)
Sets the lower bound of the axis range.
QPen getTickPen() const
Holds the data of one single data point (one bar) for QCPBars.
QCPBarData()
Constructs a bar data point with key and value set to zero.
A plottable representing a bar chart in a plot.
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QPointer< QCPBars > mBarAbove
virtual void clearData()
Removes all data points.
void addData(const QCPBarDataMap &dataMap)
Adds the provided data points in dataMap to the current data.
void removeData(double fromKey, double toKey)
Removes all data points with key between fromKey and toKey.
QCPBarDataMap * data() const
QPolygonF getBarPolygon(double key, double value) const
virtual void draw(QCPPainter *painter)
virtual ~QCPBars()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a bar chart which uses keyAxis as its key axis ("x") and valueAxis as its value axis ("y")...
void moveBelow(QCPBars *bars)
Moves this bars plottable below bars.
static void connectBars(QCPBars *lower, QCPBars *upper)
double mWidth
void removeDataAfter(double key)
Removes all data points with key greater than key.
void removeDataBefore(double key)
Removes all data points with key smaller than key.
void setData(QCPBarDataMap *data, bool copy=false)
Replaces the current data with the provided data.
double width() const
void moveAbove(QCPBars *bars)
Moves this bars plottable above bars.
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QPointer< QCPBars > mBarBelow
double getBaseValue(double key, bool positive) const
QCPBarDataMap * mData
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setWidth(double width)
Sets the width of the bars in plot (key) coordinates.
Defines a color gradient for use with e.g.
ColorInterpolation mColorInterpolation
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
void setLevelCount(int n)
Sets the number of discretization levels of the color gradient to n.
void setPeriodic(bool enabled)
Sets whether data points that are outside the configured data range (e.g.
void setColorStopAt(double position, const QColor &color)
Sets the color the gradient will have at the specified position (from 0 to 1).
QCPColorGradient(GradientPreset preset=gpCold)
Constructs a new QCPColorGradient initialized with the colors and color interpolation according to pr...
QMap< double, QColor > colorStops() const
void setColorStops(const QMap< double, QColor > &colorStops)
Sets at which positions from 0 to 1 which color shall occur.
bool operator==(const QCPColorGradient &other) const
void clearColorStops()
Clears all color stops.
QMap< double, QColor > mColorStops
QCPColorGradient inverted() const
Returns an inverted gradient.
void loadPreset(GradientPreset preset)
Clears the current color stops and loads the specified preset.
void setColorInterpolation(ColorInterpolation interpolation)
Sets whether the colors in between the configured color stops (see setColorStopAt) shall be interpola...
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
This method is used to quickly convert a data array to colors.
int levelCount() const
ColorInterpolation
Defines the color spaces in which color interpolation between gradient stops can be performed.
@ ciRGB
Color channels red, green and blue are linearly interpolated.
@ ciHSV
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
GradientPreset
Defines the available presets that can be loaded with loadPreset.
@ gpNight
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
@ gpHues
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
@ gpGeography
Colors suitable to represent different elevations on geographical maps.
@ gpIon
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
@ gpHot
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
@ gpJet
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
@ gpCandy
Blue over pink to white.
@ gpPolar
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
@ gpSpectrum
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
@ gpGrayscale
Continuous lightness from black to white (suited for non-biased data representation)
@ gpCold
Continuous lightness from black over icey colors to white (suited for non-biased data representation)
@ gpThermal
Colors suitable for thermal imaging, ranging from dark blue over purple to orange,...
QVector< QRgb > mColorBuffer
Holds the two-dimensional data of a QCPColorMap plottable.
void setKeyRange(const QCPRange &keyRange)
Sets the coordinate range the data shall be distributed over in the key dimension.
void setValueSize(int valueSize)
Resizes the data array to have valueSize cells in the value dimension.
void setSize(int keySize, int valueSize)
Resizes the data array to have keySize cells in the key dimension and valueSize cells in the value di...
QCPRange mDataBounds
QCPRange keyRange() const
QCPRange mValueRange
double data(double key, double value)
void fill(double z)
Sets all cells to the value z.
QCPRange valueRange() const
int valueSize() const
void setCell(int keyIndex, int valueIndex, double z)
Sets the data of the cell with indices keyIndex and valueIndex to z.
void clear()
Frees the internal data memory.
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
Constructs a new QCPColorMapData instance.
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
Sets the coordinate ranges the data shall be distributed over.
void recalculateDataBounds()
Goes through the data and updates the buffered minimum and maximum data values.
QCPRange dataBounds() const
int keySize() const
void setKeySize(int keySize)
Resizes the data array to have keySize cells in the key dimension.
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
Transforms plot coordinates given by key and value to cell indices of this QCPColorMapData instance.
void setValueRange(const QCPRange &valueRange)
Sets the coordinate range the data shall be distributed over in the value dimension.
bool isEmpty() const
Returns whether this instance carries no data.
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
Transforms cell indices given by keyIndex and valueIndex to cell indices of this QCPColorMapData inst...
double cell(int keyIndex, int valueIndex)
void setData(double key, double value, double z)
Sets the data of the cell, which lies at the plot coordinates given by key and value,...
QCPColorMapData & operator=(const QCPColorMapData &other)
Overwrites this color map data instance with the data stored in other.
A plottable representing a two-dimensional color map in a plot.
QCPColorMapData * data() const
Returns a pointer to the internal data storage of type QCPColorMapData.
virtual void clearData()
Clears the colormap data by calling QCPColorMapData::clear() on the internal data.
QCPColorScale * colorScale() const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual void draw(QCPPainter *painter)
void dataRangeChanged(QCPRange newRange)
This signal is emitted when the data range changes.
void setInterpolate(bool enabled)
Sets whether the color map image shall use bicubic interpolation when displaying the color map shrink...
void setData(QCPColorMapData *data, bool copy=false)
Replaces the current data with the provided data.
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
Takes the current appearance of the color map and updates the legend icon, which is used to represent...
virtual void updateMapImage()
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
QImage mMapImage
Q_SLOT void setGradient(const QCPColorGradient &gradient)
Sets the color gradient that is used to represent the data.
void rescaleDataRange(bool recalculateDataBounds=false)
Sets the data range (setDataRange) to span the minimum and maximum values that occur in the current d...
QCPColorMapData * mMapData
QPointer< QCPColorScale > mColorScale
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
This signal is emitted when the data scale type changes.
Q_SLOT void setDataRange(const QCPRange &dataRange)
Sets the data range of this color map to dataRange.
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
Sets whether the data is correlated with the color gradient linearly or logarithmically.
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a color map with the specified keyAxis and valueAxis.
void setColorScale(QCPColorScale *colorScale)
Associates the color scale colorScale with this color map.
QCPColorGradient mGradient
QCPAxis::ScaleType mDataScaleType
QCPRange mDataRange
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void gradientChanged(QCPColorGradient newGradient)
This signal is emitted when the gradient changes.
bool mTightBoundary
virtual ~QCPColorMap()
bool mMapImageInvalidated
QCPColorGradient gradient() const
void setTightBoundary(bool enabled)
Sets whether the outer most data rows and columns are clipped to the specified key and value range (s...
QPixmap mLegendIcon
QCPRange dataRange() const
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
QCPColorScale * mParentColorScale
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
Creates a new instance, as a child of parentColorScale.
virtual void draw(QCPPainter *painter)
A color scale for use with color coding data such as QCPColorMap.
virtual void mouseReleaseEvent(QMouseEvent *event)
This event is called, if the mouse was previously pressed inside the outer rect of this layout elemen...
void setType(QCPAxis::AxisType type)
Sets at which side of the color scale the axis is placed, and thus also its orientation.
Q_SLOT void setGradient(const QCPColorGradient &gradient)
Sets the color gradient that will be used to represent data values.
void setRangeDrag(bool enabled)
Sets whether the user can drag the data range (setDataRange).
QCPAxis::ScaleType mDataScaleType
void dataRangeChanged(QCPRange newRange)
This signal is emitted when the data range changes.
bool rangeDrag() const
QPointer< QCPAxis > mColorAxis
QCPColorGradient gradient() const
virtual void mouseMoveEvent(QMouseEvent *event)
This event is called, if the mouse is moved inside the outer rect of this layout element.
QString label() const
void rescaleDataRange(bool onlyVisibleMaps)
Changes the data range such that all color maps associated with this color scale are fully mapped to ...
virtual ~QCPColorScale()
QCPRange dataRange() const
QList< QCPColorMap * > colorMaps() const
Returns a list of all the color maps associated with this color scale.
QCPRange mDataRange
virtual void mousePressEvent(QMouseEvent *event)
This event is called, if the mouse was pressed while being inside the outer rect of this layout eleme...
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
This signal is emitted when the data scale type changes.
void gradientChanged(QCPColorGradient newGradient)
This signal is emitted when the gradient changes.
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
QCPAxis::AxisType mType
QCPAxis::AxisType type() const
void setRangeZoom(bool enabled)
Sets whether the user can zoom the data range (setDataRange) by scrolling the mouse wheel.
QCPColorScale(QCustomPlot *parentPlot)
Constructs a new QCPColorScale.
virtual void wheelEvent(QWheelEvent *event)
This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this layo...
virtual void update(UpdatePhase phase)
Updates the layout element and sub-elements.
void setBarWidth(int width)
Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed will ...
Q_SLOT void setDataRange(const QCPRange &dataRange)
Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
bool rangeZoom() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPColorGradient mGradient
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
Sets the scale type of the color scale, i.e.
void setLabel(const QString &str)
Sets the axis label of the color scale.
Holds the data of one single data point for QCPCurve.
QCPCurveData()
Constructs a curve data point with t, key and value set to zero.
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > *pointData) const
void removeDataAfter(double t)
Removes all data points with curve parameter t greater than t.
QCPScatterStyle mScatterStyle
QCPCurveDataMap * data() const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual void draw(QCPPainter *painter)
LineStyle
Defines how the curve's line is represented visually in the plot.
@ lsLine
Data points are connected with a straight line.
@ lsNone
No line is drawn between data points (e.g. only scatters)
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a curve which uses keyAxis as its key axis ("x") and valueAxis as its value axis ("y").
void getCurveData(QVector< QPointF > *lineData) const
QPointF outsideCoordsToPixels(double key, double value, int region, QRect axisRect) const
void setLineStyle(LineStyle style)
Sets how the single data points are connected in the plot or how they are represented visually apart ...
void addData(const QCPCurveDataMap &dataMap)
Adds the provided data points in dataMap to the current data.
void setScatterStyle(const QCPScatterStyle &style)
Sets the visual appearance of single data points in the plot.
void setData(QCPCurveDataMap *data, bool copy=false)
Replaces the current data with the provided data.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QCPCurveDataMap * mData
virtual ~QCPCurve()
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void removeData(double fromt, double tot)
Removes all data points with curve parameter t between fromt and tot.
double pointDistance(const QPointF &pixelPoint) const
virtual void clearData()
Removes all data points.
LineStyle mLineStyle
void removeDataBefore(double t)
Removes all data points with curve parameter t smaller than t.
Holds the data of one single data point for QCPGraph.
QCPData()
Constructs a data point with key, value and all errors set to zero.
double key
double valueErrorMinus
double valueErrorPlus
double keyErrorPlus
double value
double keyErrorMinus
A plottable representing a graph in a plot.
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a graph which uses keyAxis as its key axis ("x") and valueAxis as its value axis ("y").
void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setErrorBarSize(double size)
Sets the width of the handles at both ends of an error bar in pixels.
void setScatterStyle(const QCPScatterStyle &style)
Sets the visual appearance of single data points in the plot.
void setData(QCPDataMap *data, bool copy=false)
Replaces the current data with the provided data.
ErrorType errorType() const
ErrorType mErrorType
void setChannelFillGraph(QCPGraph *targetGraph)
Sets the target graph for filling the area between this graph and targetGraph with the current brush ...
QPointer< QCPGraph > mChannelFillGraph
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void removeData(double fromKey, double toKey)
Removes all data points with keys between fromKey and toKey.
QCPScatterStyle mScatterStyle
void setLineStyle(LineStyle ls)
Sets how the single data points are connected in the plot.
void getStepRightPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
int findIndexBelowY(const QVector< QPointF > *data, double y) const
void getPlotData(QVector< QPointF > *lineData, QVector< QCPData > *scatterData) const
virtual void draw(QCPPainter *painter)
void getScatterPlotData(QVector< QCPData > *scatterData) const
void getLinePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
QPointF upperFillBasePoint(double upperKey) const
double mErrorBarSize
QCPDataMap * mData
LineStyle mLineStyle
void setDataBothError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError, const QVector< double > &valueError)
Replaces the current data with the provided points in key and value pairs.
int findIndexAboveY(const QVector< QPointF > *data, double y) const
int findIndexBelowX(const QVector< QPointF > *data, double x) const
void addFillBasePoints(QVector< QPointF > *lineData) const
void getStepLeftPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
double pointDistance(const QPointF &pixelPoint) const
void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lineData) const
void removeDataBefore(double key)
Removes all data points with keys smaller than key.
QPen mErrorPen
void addData(const QCPDataMap &dataMap)
Adds the provided data points in dataMap to the current data.
bool mAdaptiveSampling
int findIndexAboveX(const QVector< QPointF > *data, double x) const
void getImpulsePlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void setErrorBarSkipSymbol(bool enabled)
If enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol b...
void setAdaptiveSampling(bool enabled)
Sets whether adaptive sampling shall be used when plotting this graph.
void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
void getStepCenterPlotData(QVector< QPointF > *linePixelData, QVector< QCPData > *scatterData) const
void setDataKeyError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &keyError)
Replaces the current data with the provided points in key and value pairs.
void setErrorPen(const QPen &pen)
Sets the pen with which the error bars will be drawn.
void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
void setErrorType(ErrorType errorType)
Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data point.
virtual void drawImpulsePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
void setDataValueError(const QVector< double > &key, const QVector< double > &value, const QVector< double > &valueError)
Replaces the current data with the provided points in key and value pairs.
QCPDataMap * data() const
Returns a pointer to the internal data storage of type QCPDataMap.
QPointF lowerFillBasePoint(double lowerKey) const
bool mErrorBarSkipSymbol
ErrorType
Defines what kind of error bars are drawn for each data point.
@ etValue
Error bars for the value dimension of the data point are shown.
@ etKey
Error bars for the key dimension of the data point are shown.
@ etBoth
Error bars for both key and value dimensions of the data point are shown.
@ etNone
No error bars are shown.
virtual void clearData()
Removes all data points.
LineStyle
Defines how the graph's line is represented visually in the plot.
@ lsLine
data points are connected by a straight line
@ lsStepCenter
line is drawn as steps where the step is in between two data points
@ lsStepRight
line is drawn as steps where the step height is the value of the right data point
@ lsImpulse
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
@ lsStepLeft
line is drawn as steps where the step height is the value of the left data point
@ lsNone
data points are not connected with any lines (e.g.
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData) const
virtual void drawScatterPlot(QCPPainter *painter, QVector< QCPData > *scatterData) const
int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
void removeDataAfter(double key)
Removes all data points with keys greater than key.
void getPreparedData(QVector< QCPData > *lineData, QVector< QCPData > *scatterData) const
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
virtual ~QCPGraph()
virtual void drawLinePlot(QCPPainter *painter, QVector< QPointF > *lineData) const
void removeFillBasePoints(QVector< QPointF > *lineData) const
Responsible for drawing the grid of a QCPAxis.
QPen mPen
QPen pen() const
void setZeroLinePen(const QPen &pen)
Sets the pen with which zero lines are drawn.
QPen mZeroLinePen
void setAntialiasedZeroLine(bool enabled)
Sets whether zero lines are drawn antialiased.
bool mSubGridVisible
void setAntialiasedSubGrid(bool enabled)
Sets whether sub grid lines are drawn antialiased.
bool mAntialiasedSubGrid
void drawSubGridLines(QCPPainter *painter) const
bool mAntialiasedZeroLine
QCPAxis * mParentAxis
void setSubGridPen(const QPen &pen)
Sets the pen with which sub grid lines are drawn.
void setPen(const QPen &pen)
Sets the pen with which (major) grid lines are drawn.
QPen mSubGridPen
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QCPGrid(QCPAxis *parentAxis)
Creates a QCPGrid instance and sets default values.
virtual void draw(QCPPainter *painter)
void setSubGridVisible(bool visible)
Sets whether grid lines at sub tick marks are drawn.
void drawGridLines(QCPPainter *painter) const
An anchor of an item to which positions can be attached to.
virtual ~QCPItemAnchor()
QCustomPlot * mParentPlot
QSet< QCPItemPosition * > mChildren
QCPAbstractItem * mParentItem
friend class QCPItemPosition
void removeChild(QCPItemPosition *pos)
virtual QCPItemPosition * toQCPItemPosition()
Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if...
void addChild(QCPItemPosition *pos)
virtual QPointF pixelPoint() const
Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1)
Creates a new QCPItemAnchor.
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the bracket when selected.
QCPItemBracket(QCustomPlot *parentPlot)
Creates a bracket item and sets default values.
virtual QPointF anchorPixelPoint(int anchorId) const
BracketStyle style() const
QPen pen() const
void setStyle(BracketStyle style)
Sets the style of the bracket, i.e.
@ bsRound
A brace with round edges.
@ bsCurly
A curly brace.
@ bsSquare
A brace with angled edges.
@ bsCalligraphic
A curly brace with varying stroke width giving a calligraphic impression.
virtual void draw(QCPPainter *painter)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the bracket.
void setLength(double length)
Sets the length in pixels how far the bracket extends in the direction towards the embraced span of t...
BracketStyle mStyle
virtual ~QCPItemBracket()
QPen mainPen() const
double length() const
QCPItemPosition *const left
QCPItemPosition *const right
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line.
void setHead(const QCPLineEnding &head)
Sets the line ending style of the head.
QCPItemPosition *const start
QCPItemPosition *const end
QCPItemPosition *const endDir
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line when selected.
QPen mainPen() const
virtual void draw(QCPPainter *painter)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QCPLineEnding head() const
QCPItemPosition *const startDir
QCPLineEnding tail() const
void setTail(const QCPLineEnding &tail)
Sets the line ending style of the tail.
QCPItemCurve(QCustomPlot *parentPlot)
Creates a curve item and sets default values.
virtual ~QCPItemCurve()
QPen pen() const
QCPLineEnding mTail
QCPLineEnding mHead
QCPItemPosition *const topLeft
QBrush mSelectedBrush
virtual ~QCPItemEllipse()
void setBrush(const QBrush &brush)
Sets the brush that will be used to fill the ellipse.
QBrush mainBrush() const
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line of the ellipse when selected.
QCPItemEllipse(QCustomPlot *parentPlot)
Creates an ellipse item and sets default values.
QPen pen() const
QCPItemAnchor *const center
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used to fill the ellipse when selected.
QPen mainPen() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QBrush brush() const
QCPItemPosition *const bottomRight
virtual QPointF anchorPixelPoint(int anchorId) const
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line of the ellipse.
virtual void draw(QCPPainter *painter)
QCPItemPosition *const end
QCPItemLine(QCustomPlot *parentPlot)
Creates a line item and sets default values.
virtual void draw(QCPPainter *painter)
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line when selected.
QCPLineEnding mHead
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line.
QCPItemPosition *const start
QCPLineEnding head() const
QPen pen() const
virtual ~QCPItemLine()
QCPLineEnding mTail
QCPLineEnding tail() const
void setTail(const QCPLineEnding &tail)
Sets the line ending style of the tail.
QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void setHead(const QCPLineEnding &head)
Sets the line ending style of the head.
QPen mainPen() const
virtual QPointF anchorPixelPoint(int anchorId) const
QPixmap mScaledPixmap
QCPItemPosition *const topLeft
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
Qt::AspectRatioMode aspectRatioMode() const
QPen pen() const
void setPixmap(const QPixmap &pixmap)
Sets the pixmap that will be displayed.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QPixmap pixmap() const
virtual ~QCPItemPixmap()
bool scaled() const
virtual void draw(QCPPainter *painter)
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
Qt::AspectRatioMode mAspectRatioMode
QCPItemPixmap(QCustomPlot *parentPlot)
Creates a rectangle item and sets default values.
QPen mainPen() const
QCPItemPosition *const bottomRight
void setPen(const QPen &pen)
Sets the pen that will be used to draw a border around the pixmap.
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio)
Sets whether the pixmap will be scaled to fit the rectangle defined by the topLeft and bottomRight po...
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw a border around the pixmap when selected.
Manages the position of an item.
QCPItemAnchor * parentAnchor() const
void setAxisRect(QCPAxisRect *axisRect)
When setType is ptAxisRectRatio, this function may be used to specify the axis rect the coordinates s...
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
When setType is ptPlotCoords, this function may be used to specify the axes the coordinates set with ...
QCPItemAnchor * mParentAnchor
QPointer< QCPAxis > mValueAxis
QPointer< QCPAxis > mKeyAxis
virtual QPointF pixelPoint() const
Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface.
double key() const
void setType(PositionType type)
Sets the type of the position.
void setCoords(double key, double value)
Sets the coordinates of this QCPItemPosition.
QCPAxis * valueAxis() const
PositionType
Defines the ways an item position can be specified.
@ ptAxisRectRatio
Static positioning given by a fraction of the axis rect size (see setAxisRect).
@ ptAbsolute
Static positioning in pixels, starting from the top left corner of the viewport/widget.
@ ptViewportRatio
Static positioning given by a fraction of the viewport size.
@ ptPlotCoords
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
void setPixelPoint(const QPointF &pixelPoint)
Sets the apparent pixel position.
QCPAxis * keyAxis() const
PositionType type() const
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
Sets the parent of this QCPItemPosition to parentAnchor.
PositionType mPositionType
double value() const
virtual ~QCPItemPosition()
QPointer< QCPAxisRect > mAxisRect
QCPAxisRect * axisRect() const
virtual void draw(QCPPainter *painter)
QBrush mSelectedBrush
QPen pen() const
QCPItemPosition *const bottomRight
QCPItemRect(QCustomPlot *parentPlot)
Creates a rectangle item and sets default values.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line of the rectangle.
QBrush brush() const
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line of the rectangle when selected.
QBrush mainBrush() const
QCPItemPosition *const topLeft
void setBrush(const QBrush &brush)
Sets the brush that will be used to fill the rectangle.
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used to fill the rectangle when selected.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
virtual QPointF anchorPixelPoint(int anchorId) const
QPen mainPen() const
virtual ~QCPItemRect()
double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
virtual void draw(QCPPainter *painter)
QCPItemStraightLine(QCustomPlot *parentPlot)
Creates a straight line item and sets default values.
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line when selected.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line.
QCPItemPosition *const point1
QCPItemPosition *const point2
QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const
QColor color() const
void setSelectedFont(const QFont &font)
Sets the font of the text that will be used when the item is selected.
QCPItemPosition *const position
Qt::Alignment positionAlignment() const
void setBrush(const QBrush &brush)
Sets the brush that will be used do fill the background of the text.
virtual ~QCPItemText()
QBrush brush() const
QBrush mSelectedBrush
void setSelectedPen(const QPen &pen)
Sets the pen that will be used do draw a rectangular border around the text, when the item is selecte...
QString mText
QPen mainPen() const
void setText(const QString &text)
Sets the text that will be displayed.
virtual QPointF anchorPixelPoint(int anchorId) const
QFont font() const
void setRotation(double degrees)
Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated arou...
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
QMargins padding() const
QFont mSelectedFont
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used do fill the background of the text, when the item is selected.
Qt::Alignment mPositionAlignment
QPen pen() const
QCPItemText(QCustomPlot *parentPlot)
Creates a text item and sets default values.
void setPositionAlignment(Qt::Alignment alignment)
Sets which point of the text rect shall be aligned with position.
QColor mSelectedColor
virtual void draw(QCPPainter *painter)
void setFont(const QFont &font)
Sets the font of the text.
void setPen(const QPen &pen)
Sets the pen that will be used do draw a rectangular border around the text.
void setColor(const QColor &color)
Sets the color of the text.
void setTextAlignment(Qt::Alignment alignment)
Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft,...
QColor mainColor() const
double mRotation
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
Qt::Alignment mTextAlignment
QBrush mainBrush() const
QString text() const
QMargins mPadding
void setSelectedColor(const QColor &color)
Sets the color of the text that will be used when the item is selected.
void setPadding(const QMargins &padding)
Sets the distance between the border of the text rectangle and the text.
QFont mainFont() const
void setSelectedBrush(const QBrush &brush)
Sets the brush that will be used to draw any fills of the tracer, when selected.
QBrush mSelectedBrush
void setBrush(const QBrush &brush)
Sets the brush that will be used to draw any fills of the tracer.
QCPGraph * mGraph
TracerStyle
The different visual appearances a tracer item can have.
@ tsPlus
A plus shaped crosshair with limited size.
@ tsSquare
A square.
@ tsNone
The tracer is not visible.
@ tsCircle
A circle.
@ tsCrosshair
A plus shaped crosshair which spans the complete axis rect.
void setStyle(TracerStyle style)
Sets the style/visual appearance of the tracer.
virtual ~QCPItemTracer()
double size() const
void updatePosition()
If the tracer is connected with a graph (setGraph), this function updates the tracer's position to re...
QCPGraph * graph() const
void setGraphKey(double key)
Sets the key of the graph's data point the tracer will be positioned at.
QCPItemPosition *const position
void setInterpolating(bool enabled)
Sets whether the value of the graph's data points shall be interpolated, when positioning the tracer.
QBrush brush() const
QPen pen() const
QBrush mainBrush() const
virtual void draw(QCPPainter *painter)
QPen mainPen() const
QCPItemTracer(QCustomPlot *parentPlot)
Creates a tracer item and sets default values.
void setSelectedPen(const QPen &pen)
Sets the pen that will be used to draw the line of the tracer when selected.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
void setSize(double size)
Sets the size of the tracer in pixels, if the style supports setting a size (e.g.
void setGraph(QCPGraph *graph)
Sets the QCPGraph this tracer sticks to.
void setPen(const QPen &pen)
Sets the pen that will be used to draw the line of the tracer.
TracerStyle mStyle
TracerStyle style() const
A layer that may contain objects, to control the rendering order.
QList< QCPLayerable * > children() const
Returns a list of all layerables on this layer.
bool mVisible
QCustomPlot * mParentPlot
QString name() const
void addChild(QCPLayerable *layerable, bool prepend)
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
Creates a new QCPLayer instance.
QList< QCPLayerable * > mChildren
QCustomPlot * parentPlot() const
void setVisible(bool visible)
Sets whether this layer is visible or not.
void removeChild(QCPLayerable *layerable)
bool visible() const
int index() const
Returns the index this layer has in the QCustomPlot.
Base class for all drawable objects.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
QPointer< QCPLayerable > mParentLayerable
QCustomPlot * parentPlot() const
void setVisible(bool on)
Sets the visibility of this layerable object.
void setAntialiased(bool enabled)
Sets whether this object will be drawn antialiased or not.
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QCPLayerable(QCustomPlot *plot, QString targetLayer="", QCPLayerable *parentLayerable=0)
Creates a new QCPLayerable instance.
void initializeParentPlot(QCustomPlot *parentPlot)
virtual QCP::Interaction selectionCategory() const
QCPLayer * layer() const
void setParentLayerable(QCPLayerable *parentLayerable)
QCustomPlot * mParentPlot
QCPLayer * mLayer
QCPLayerable * parentLayerable() const
Returns the parent layerable of this layerable.
bool realVisibility() const
Returns whether this layerable is visible, taking the visibility of the layerable parent and the visi...
Q_SLOT bool setLayer(QCPLayer *layer)
Sets the layer of this layerable object.
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
void layerChanged(QCPLayer *newLayer)
This signal is emitted when the layer of this layerable changes, i.e.
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
virtual QRect clipRect() const
friend class QCPAxisRect
virtual void deselectEvent(bool *selectionStateChanged)
virtual void draw(QCPPainter *painter)=0
bool visible() const
bool moveToLayer(QCPLayer *layer, bool prepend)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
The abstract base class for all objects that form the layout system.
virtual int calculateAutoMargin(QCP::MarginSide side)
void setMinimumMargins(const QMargins &margins)
If setAutoMargins is enabled on some or all margins, this function is used to provide minimum values ...
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
UpdatePhase
Defines the phases of the update process, that happens just before a replot.
@ upMargins
Phase in which the margins are calculated and set.
@ upLayout
Final phase in which the layout system places the rects of the elements.
@ upPreparation
Phase used for any type of preparation that needs to be done before margin calculation and layout.
virtual ~QCPLayoutElement()
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
QRect rect() const
Returns the inner rect of this layout element.
void setOuterRect(const QRect &rect)
Sets the outer rect of this layout element.
QCPLayout * layout() const
Returns the parent layout of this layout element.
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
Sets the margin group of the specified margin sides.
QMargins mMinimumMargins
void setMinimumSize(const QSize &size)
Sets the minimum size for the inner rect of this layout element.
void setMaximumSize(const QSize &size)
Sets the maximum size for the inner rect of this layout element.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
QCPLayoutElement(QCustomPlot *parentPlot=0)
Creates an instance of QCPLayoutElement and sets default values.
void setMargins(const QMargins &margins)
Sets the margins of this layout element.
virtual void update(UpdatePhase phase)
Updates the layout element and sub-elements.
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
virtual QSize maximumSizeHint() const
Returns the maximum size this layout element (the inner rect) may be expanded to.
void setAutoMargins(QCP::MarginSides sides)
Sets on which sides the margin shall be calculated automatically.
QCPLayout * mParentLayout
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
QMargins margins() const
QCP::MarginSides mAutoMargins
A layout that arranges child elements in a grid.
virtual void updateLayout()
virtual void simplify()
Simplifies the layout by collapsing rows and columns which only contain empty cells.
int rowCount() const
Returns the number of rows in the layout.
int columnCount() const
Returns the number of columns in the layout.
void insertColumn(int newIndex)
Inserts a new column with empty cells at the column index newIndex.
void setRowStretchFactors(const QList< double > &factors)
Sets the stretch factors of all rows.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
QList< QList< QCPLayoutElement * > > mElements
QList< double > mRowStretchFactors
virtual QSize maximumSizeHint() const
Returns the maximum size this layout element (the inner rect) may be expanded to.
void setColumnSpacing(int pixels)
Sets the gap that is left blank between columns to pixels.
void insertRow(int newIndex)
Inserts a new row with empty cells at the row index newIndex.
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
QCPLayoutElement * element(int row, int column) const
Returns the element in the cell in row and column.
virtual bool take(QCPLayoutElement *element)
Removes the specified element from the layout and returns true on success.
void setColumnStretchFactors(const QList< double > &factors)
Sets the stretch factors of all columns.
virtual int elementCount() const
Returns the number of elements/cells in the layout.
void setRowStretchFactor(int row, double factor)
Sets the stretch factor of row.
void expandTo(int newRowCount, int newColumnCount)
Expands the layout to have newRowCount rows and newColumnCount columns.
virtual QCPLayoutElement * elementAt(int index) const
Returns the element in the cell with the given index.
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
void setRowSpacing(int pixels)
Sets the gap that is left blank between rows to pixels.
bool hasElement(int row, int column)
Returns whether the cell at row and column exists and contains a valid element, i....
QCPLayoutGrid()
Creates an instance of QCPLayoutGrid and sets default values.
QList< double > mColumnStretchFactors
virtual QCPLayoutElement * takeAt(int index)
Removes the element with the given index from the layout and returns it.
bool addElement(int row, int column, QCPLayoutElement *element)
Adds the element to cell with row and column.
void setColumnStretchFactor(int column, double factor)
Sets the stretch factor of column.
virtual ~QCPLayoutGrid()
A layout that places child elements aligned to the border or arbitrarily positioned.
virtual int elementCount() const
Returns the number of elements/cells in the layout.
QCPLayoutInset()
Creates an instance of QCPLayoutInset and sets default values.
QList< Qt::Alignment > mInsetAlignment
QList< InsetPlacement > mInsetPlacement
Qt::Alignment insetAlignment(int index) const
Returns the alignment of the element with the specified index.
void setInsetAlignment(int index, Qt::Alignment alignment)
If the inset placement (setInsetPlacement) is ipBorderAligned, this function is used to set the align...
void setInsetPlacement(int index, InsetPlacement placement)
Sets the inset placement type of the element with the specified index to placement.
InsetPlacement insetPlacement(int index) const
Returns the placement type of the element with the specified index.
virtual void updateLayout()
virtual ~QCPLayoutInset()
virtual QCPLayoutElement * elementAt(int index) const
Returns the element in the cell with the given index.
InsetPlacement
Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset.
@ ipFree
The element may be positioned/sized arbitrarily, see setInsetRect.
@ ipBorderAligned
The element is aligned to one of the layout sides, see setInsetAlignment.
QList< QCPLayoutElement * > mElements
virtual bool take(QCPLayoutElement *element)
Removes the specified element from the layout and returns true on success.
void setInsetRect(int index, const QRectF &rect)
If the inset placement (setInsetPlacement) is ipFree, this function is used to set the position and s...
QList< QRectF > mInsetRect
QRectF insetRect(int index) const
Returns the rect of the element with the specified index.
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
Adds the specified element to the layout as an inset aligned at the border (setInsetAlignment is init...
virtual QCPLayoutElement * takeAt(int index)
Removes the element with the given index from the layout and returns it.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
The inset layout is sensitive to events only at areas where its (visible) child elements are sensitiv...
The abstract base class for layouts.
void clear()
Removes and deletes all layout elements in this layout.
QCPLayout()
Creates an instance of QCPLayout and sets default values.
virtual void updateLayout()
bool removeAt(int index)
Removes and deletes the element at the provided index.
virtual void update(UpdatePhase phase)
First calls the QCPLayoutElement::update base class implementation to update the margins on this layo...
virtual int elementCount() const =0
Returns the number of elements/cells in the layout.
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
virtual void simplify()
Simplifies the layout by collapsing empty cells.
void releaseElement(QCPLayoutElement *el)
virtual QCPLayoutElement * takeAt(int index)=0
Removes the element with the given index from the layout and returns it.
bool remove(QCPLayoutElement *element)
Removes and deletes the provided element.
virtual bool take(QCPLayoutElement *element)=0
Removes the specified element from the layout and returns true on success.
virtual QList< QCPLayoutElement * > elements(bool recursive) const
Returns a list of all child elements in this layout element.
void sizeConstraintsChanged() const
Subclasses call this method to report changed (minimum/maximum) size constraints.
void adoptElement(QCPLayoutElement *el)
virtual QCPLayoutElement * elementAt(int index) const =0
Returns the element in the cell with the given index.
Manages a legend inside a QCustomPlot.
QCPLegend()
Constructs a new QCPLegend instance with parentPlot as the containing plot and default values.
SelectableParts mSelectableParts
int iconTextPadding() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
QPen getBorderPen() const
void clearItems()
Removes all items from the legend.
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
Sets the selected state of the respective legend parts described by SelectablePart.
void setSelectedBorderPen(const QPen &pen)
When the legend box is selected, this pen is used to draw the border instead of the normal pen set vi...
void setIconBorderPen(const QPen &pen)
Sets the pen used to draw a border around each legend icon.
QSize mIconSize
bool addItem(QCPAbstractLegendItem *item)
Adds item to the legend, if it's not present already.
SelectableParts selectedParts() const
virtual void draw(QCPPainter *painter)
QColor mTextColor
void setBrush(const QBrush &brush)
Sets the brush of the legend background.
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
Returns whether the legend contains a QCPPlottableLegendItem which is associated with plottable (e....
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
virtual void deselectEvent(bool *selectionStateChanged)
QPen mBorderPen
virtual ~QCPLegend()
SelectablePart
Defines the selectable parts of a legend.
@ spLegendBox
0x001 The legend box (frame)
@ spNone
0x000 None
@ spItems
0x002 Legend items individually (see selectedItems)
int itemCount() const
Returns the number of items currently in the legend.
QPen iconBorderPen() const
QPen mSelectedBorderPen
void setIconTextPadding(int padding)
Sets the horizontal space in pixels between the legend icon and the text next to it.
QColor mSelectedTextColor
QPen mSelectedIconBorderPen
void setSelectedTextColor(const QColor &color)
Sets the default text color that is used by legend items when they are selected.
QPen mIconBorderPen
void selectionChanged(QCPLegend::SelectableParts parts)
This signal is emitted when the selection state of this legend has changed.
void setBorderPen(const QPen &pen)
Sets the pen, the border of the entire legend is drawn with.
QFont mSelectedFont
void setSelectedBrush(const QBrush &brush)
When the legend box is selected, this brush is used to draw the legend background instead of the norm...
void selectableChanged(QCPLegend::SelectableParts parts)
int mIconTextPadding
void setIconSize(const QSize &size)
Sets the size of legend icons.
virtual QCP::Interaction selectionCategory() const
SelectableParts mSelectedParts
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
Returns the QCPPlottableLegendItem which is associated with plottable (e.g.
QBrush mBrush
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
Sets whether the user can (de-)select the parts in selectable by clicking on the QCustomPlot surface.
void setFont(const QFont &font)
Sets the default font of legend text.
QBrush brush() const
QBrush getBrush() const
QBrush mSelectedBrush
void setSelectedFont(const QFont &font)
Sets the default font that is used by legend items when they are selected.
QList< QCPAbstractLegendItem * > selectedItems() const
Returns the legend items that are currently selected.
bool removeItem(int index)
Removes the item with index index from the legend.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
QCPAbstractLegendItem * item(int index) const
Returns the item with index i.
SelectableParts selectableParts() const
bool hasItem(QCPAbstractLegendItem *item) const
Returns whether the legend contains itm.
QPen selectedIconBorderPen() const
void setSelectedIconBorderPen(const QPen &pen)
Sets the pen legend items will use to draw their icon borders, when they are selected.
void setTextColor(const QColor &color)
Sets the default color of legend text.
QFont font() const
QSize iconSize() const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Handles the different ending decorations for line-like items.
EndingStyle style() const
void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
double boundingDistance() const
bool inverted() const
void setWidth(double width)
Sets the width of the ending decoration, if the style supports it.
EndingStyle mStyle
void setStyle(EndingStyle style)
Sets the style of the ending decoration.
void setInverted(bool inverted)
Sets whether the ending decoration shall be inverted.
EndingStyle
Defines the type of ending decoration for line-like items, e.g.
@ esHalfBar
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
@ esSkewedBar
A bar that is skewed (skew controllable via setLength)
@ esBar
A bar perpendicular to the line.
@ esDiamond
A filled diamond (45° rotated square)
@ esFlatArrow
A filled arrow head with a straight/flat back (a triangle)
@ esLineArrow
A non-filled arrow head with open back.
@ esSpikeArrow
A filled arrow head with an indented back.
@ esNone
No ending decoration.
@ esSquare
A filled square.
@ esDisc
A filled circle.
double realLength() const
Starting from the origin of this line ending (which is style specific), returns the length covered by...
void setLength(double length)
Sets the length of the ending decoration, if the style supports it.
double width() const
double length() const
QCPLineEnding()
Creates a QCPLineEnding instance with default values (style esNone).
A margin group allows synchronization of margin sides if working with multiple layout elements.
void clear()
Clears this margin group.
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
QCPMarginGroup(QCustomPlot *parentPlot)
Creates a new QCPMarginGroup instance in parentPlot.
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Returns a list of all layout elements that have their margin side associated with this margin group.
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
bool isEmpty() const
Returns whether this margin group is empty.
int commonMargin(QCP::MarginSide side) const
QPainter subclass used internally.
QStack< bool > mAntialiasingStack
bool begin(QPaintDevice *device)
Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on device...
void drawLine(const QLineF &line)
This is an overloaded member function, provided for convenience. It differs from the above function o...
PainterMode
Defines special modes the painter can operate in.
@ pmNonCosmetic
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
@ pmNoCaching
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
@ pmVectorized
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
QCPPainter()
Creates a new QCPPainter instance and sets default values.
bool antialiasing() const
void setModes(PainterModes modes)
Sets the mode of the painter.
void restore()
Restores the painter (see QPainter::restore).
bool mIsAntialiasing
void makeNonCosmetic()
Changes the pen width to 1 if it currently is 0.
void save()
Saves the painter (see QPainter::save).
void setAntialiasing(bool enabled)
Sets whether painting uses antialiasing or not.
PainterModes modes() const
PainterModes mModes
void setMode(PainterMode mode, bool enabled=true)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setPen(const QPen &pen)
Sets the pen of the painter and applies certain fixes to it, depending on the mode of this QCPPainter...
A layout element displaying a plot title text.
void setSelectedTextColor(const QColor &color)
Sets the color of the title text that will be used if the plot title is selected (setSelected).
void setFont(const QFont &font)
Sets the font of the title text.
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
QFont font() const
void selectionChanged(bool selected)
This signal is emitted when the selection state has changed to selected, either by user interaction o...
void setSelectedFont(const QFont &font)
Sets the font of the title text that will be used if the plot title is selected (setSelected).
QFont mainFont() const
QColor mTextColor
void selectableChanged(bool selectable)
bool selected() const
void setTextColor(const QColor &color)
Sets the color of the title text.
QRect mTextBoundingRect
Q_SLOT void setSelectable(bool selectable)
Sets whether the user may select this plot title to selectable.
QColor mSelectedTextColor
Q_SLOT void setSelected(bool selected)
Sets the selection state of this plot title to selected.
QFont mSelectedFont
QCPPlotTitle(QCustomPlot *parentPlot)
Creates a new QCPPlotTitle instance and sets default values.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
Layout elements are sensitive to events inside their outer rect.
void setText(const QString &text)
Sets the text that will be displayed to text.
QColor mainTextColor() const
QString text() const
bool selectable() const
virtual void deselectEvent(bool *selectionStateChanged)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const
virtual QSize maximumSizeHint() const
Returns the maximum size this layout element (the inner rect) may be expanded to.
virtual void draw(QCPPainter *painter)
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
A legend item representing a plottable with an icon and the plottable name.
virtual QSize minimumSizeHint() const
Returns the minimum size this layout element (the inner rect) may be compressed to.
QColor getTextColor() const
virtual void draw(QCPPainter *painter)
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
Creates a new legend item associated with plottable.
QCPAbstractPlottable * mPlottable
Represents the range an axis is encompassing.
void expand(const QCPRange &otherRange)
Expands this range such that otherRange is contained in the new range.
QCPRange sanitizedForLogScale() const
Returns a sanitized version of the range.
static const double maxRange
Maximum values (negative and positive) the range will accept in range-changing functions.
double size() const
Returns the size of the range, i.e.
QCPRange sanitizedForLinScale() const
Returns a sanitized version of the range.
QCPRange expanded(const QCPRange &otherRange) const
Returns an expanded range that contains this and otherRange.
double lower
static bool validRange(double lower, double upper)
Checks, whether the specified range is within valid bounds, which are defined as QCPRange::maxRange a...
static const double minRange
Minimum range size (upper - lower) the range changing functions will accept.
QCPRange()
Constructs a range with lower and upper set to zero.
double upper
bool contains(double value) const
Returns true when value lies within or exactly on the borders of the range.
double center() const
Returns the center of the range, i.e.
void normalize()
Makes sure lower is numerically smaller than upper.
Represents the visual appearance of scatter points.
double size() const
void drawShape(QCPPainter *painter, QPointF pos) const
Draws the scatter shape with painter at position pos.
void setPixmap(const QPixmap &pixmap)
Sets the pixmap that will be drawn as scatter point to pixmap.
bool isNone() const
Returns whether the scatter shape is ssNone.
void setBrush(const QBrush &brush)
Sets the brush that will be used to fill scatter points to brush.
void setPen(const QPen &pen)
Sets the pen that will be used to draw scatter points to pen.
void setShape(ScatterShape shape)
Sets the shape to shape.
QPainterPath mCustomPath
QCPScatterStyle()
Creates a new QCPScatterStyle instance with size set to 6.
void setCustomPath(const QPainterPath &customPath)
Sets the custom shape that will be drawn as scatter point to customPath.
QPixmap pixmap() const
void setSize(double size)
Sets the size (pixel diameter) of the drawn scatter points to size.
QPen pen() const
ScatterShape
Defines the shape used for scatter points.
@ ssDot
\enumimage{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain...
@ ssCustom
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath)
@ ssSquare
\enumimage{ssSquare.png} a square
@ ssDisc
\enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle)
@ ssPlus
\enumimage{ssPlus.png} a plus
@ ssDiamond
\enumimage{ssDiamond.png} a diamond
@ ssCrossCircle
\enumimage{ssCrossCircle.png} a circle with a cross inside
@ ssPlusSquare
\enumimage{ssPlusSquare.png} a square with a plus inside
@ ssStar
\enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
@ ssTriangleInverted
\enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner
@ ssPlusCircle
\enumimage{ssPlusCircle.png} a circle with a plus inside
@ ssCrossSquare
\enumimage{ssCrossSquare.png} a square with a cross inside
@ ssTriangle
\enumimage{ssTriangle.png} an equilateral triangle, standing on baseline
@ ssCircle
\enumimage{ssCircle.png} a circle
@ ssPixmap
a custom pixmap specified by setPixmap, centered on the data point coordinates
@ ssCross
\enumimage{ssCross.png} a cross
@ ssNone
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
@ ssPeace
\enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines
QBrush brush() const
QPainterPath customPath() const
ScatterShape shape() const
ScatterShape mShape
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
Applies the pen and the brush of this scatter style to painter.
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
This function is used to decide whether a click hits a layerable object or not.
double key() const
void setWidth(double width)
Sets the width of the box in key coordinates.
virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
virtual void clearData()
Clears all data in the plottable.
QVector< double > mOutliers
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
void setWhiskerPen(const QPen &pen)
Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
double maximum() const
virtual void drawMedian(QCPPainter *painter) const
void setMedian(double value)
Sets the parameter "median" of the statistical box plot.
void setUpperQuartile(double value)
Sets the parameter "upper Quartile" of the statistical box plot.
void setLowerQuartile(double value)
Sets the parameter "lower Quartile" of the statistical box plot.
virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const
void setMedianPen(const QPen &pen)
Sets the pen used for drawing the median indicator line inside the statistical box.
virtual void draw(QCPPainter *painter)
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
Constructs a statistical box which uses keyAxis as its key axis ("x") and valueAxis as its value axis...
void setKey(double key)
Sets the key coordinate of the statistical box.
void setMinimum(double value)
Sets the parameter "minimum" of the statistical box plot.
virtual void drawOutliers(QCPPainter *painter) const
void setWhiskerBarPen(const QPen &pen)
Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at each ...
virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const
double minimum() const
double median() const
void setMaximum(double value)
Sets the parameter "maximum" of the statistical box plot.
void setOutlierStyle(const QCPScatterStyle &style)
Sets the appearance of the outlier data points.
void setWhiskerWidth(double width)
Sets the width of the whiskers (setMinimum, setMaximum) in key coordinates.
void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
Sets all parameters of the statistical box plot at once.
QCPScatterStyle mOutlierStyle
double width() const
virtual void drawWhiskers(QCPPainter *painter) const
void setOutliers(const QVector< double > &values)
Sets a vector of outlier values that will be drawn as scatters.
double lowerQuartile() const
double upperQuartile() const
The central class of the library.
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
This signal is emitted when a legend (item) is double clicked.
QCPLayer * currentLayer() const
Returns the layer that is set as current layer (see setCurrentLayer).
void drawBackground(QCPPainter *painter)
QPixmap mScaledBackgroundPixmap
QCPLayer * layer(const QString &name) const
Returns the layer with the specified name.
void beforeReplot()
This signal is emitted immediately before a replot takes place (caused by a call to the slot replot).
Qt::KeyboardModifier mMultiSelectModifier
virtual QSize minimumSizeHint() const
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
QList< QCPAxisRect * > axisRects() const
Returns all axis rects in the plot.
QCPAbstractItem * item() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setBackground(const QPixmap &pm)
Sets pm as the viewport background pixmap (see setViewport).
virtual void resizeEvent(QResizeEvent *event)
int itemCount() const
Returns the number of currently existing items in the plot.
QRect viewport() const
Returns the viewport rect of this QCustomPlot instance.
void toPainter(QCPPainter *painter, int width=0, int height=0)
Renders the plot using the passed painter.
void titleClick(QMouseEvent *event, QCPPlotTitle *title)
This signal is emitted when a plot title is clicked.
QCP::AntialiasedElements mNotAntialiasedElements
virtual void paintEvent(QPaintEvent *event)
const QCP::Interactions interactions() const
QPointer< QCPLayoutElement > mMouseEventElement
QCPAbstractPlottable * plottable(int index)
Returns the plottable with index.
void setBackgroundScaled(bool scaled)
Sets whether the viewport background pixmap shall be scaled to fit the viewport.
QBrush mBackgroundBrush
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
Sets the specified plotting hint to enabled.
void setViewport(const QRect &rect)
Sets the viewport of this QCustomPlot.
bool removeLayer(QCPLayer *layer)
Removes the specified layer and returns true on success.
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
Sets the single interaction of this QCustomPlot to enabled.
QCustomPlot(QWidget *parent=0)
Constructs a QCustomPlot and sets reasonable default values.
RefreshPriority
Defines with what timing the QCustomPlot surface is refreshed after a replot.
@ rpImmediate
The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot.
@ rpQueued
Queues the refresh such that it is performed at a slightly delayed point in time after the replot,...
@ rpHint
Whether to use immediate repaint or queued update depends on whether the plotting hint QCP::phForceRe...
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
Saves a JPG image file to fileName on disc.
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
If scaling of the viewport background pixmap is enabled (setBackgroundScaled), use this function to d...
void setSelectionTolerance(int pixels)
Sets the tolerance that is used to decide whether a click selects an object (e.g.
QCPLegend * legend
A pointer to the default legend of the main axis rect.
void selectionChangedByUser()
This signal is emitted after the user has changed the selection in the QCustomPlot,...
virtual QSize sizeHint() const
int selectionTolerance() const
void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
This signal is emitted when a plottable is clicked.
friend class QCPLayer
int graphCount() const
Returns the number of currently existing graphs in the plot.
void setInteractions(const QCP::Interactions &interactions)
Sets the possible interactions of this QCustomPlot as an or-combination of QCP::Interaction enums.
int plottableCount() const
Returns the number of currently existing plottables in the plot.
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint)
Causes a complete replot into the internal buffer.
QList< QCPAbstractPlottable * > mPlottables
bool mBackgroundScaled
QCP::AntialiasedElements antialiasedElements() const
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0)
Saves a BMP image file to fileName on disc.
QList< QCPAbstractItem * > mItems
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
This signal is emitted when an axis is double clicked.
void afterReplot()
This signal is emitted immediately after a replot has taken place (caused by a call to the slot replo...
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
Creates a new graph inside the plot.
virtual void mouseReleaseEvent(QMouseEvent *event)
bool hasPlottable(QCPAbstractPlottable *plottable) const
Returns whether this QCustomPlot instance contains the plottable.
QList< QCPLayer * > mLayers
bool setCurrentLayer(const QString &name)
Sets the layer with the specified name to be the current layer.
void mouseMove(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse move event.
QList< QCPAbstractPlottable * > selectedPlottables() const
Returns a list of the selected plottables.
QPixmap mPaintBuffer
QCP::AntialiasedElements notAntialiasedElements() const
LayerInsertMode
Defines how a layer should be inserted relative to an other layer.
@ limAbove
Layer is inserted above other layer.
virtual ~QCustomPlot()
bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1)
Saves a PNG image file to fileName on disc.
virtual void mouseDoubleClickEvent(QMouseEvent *event)
void setNoAntialiasingOnDrag(bool enabled)
Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes ranges.
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
This signal is emitted when a legend (item) is clicked.
virtual void wheelEvent(QWheelEvent *event)
QList< QCPAxis * > selectedAxes() const
Returns the axes that currently have selected parts, i.e.
void updateLayerIndices() const
friend class QCPLegend
virtual void axisRemoved(QCPAxis *axis)
int axisRectCount() const
Returns the number of axis rects in the plot.
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
Sets the keyboard modifier that will be recognized as multi-select-modifier.
bool removeGraph(QCPGraph *graph)
Removes the specified graph from the plot and, if necessary, from the QCustomPlot::legend.
void setPlottingHints(const QCP::PlottingHints &hints)
Sets the plotting hints for this QCustomPlot instance as an or combination of QCP::PlottingHint.
int clearPlottables()
Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
QCPAxis * xAxis
A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
void mouseDoubleClick(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse double click event.
virtual void legendRemoved(QCPLegend *legend)
Q_SLOT void deselectAll()
Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
QCP::PlottingHints mPlottingHints
QCPLayer * mCurrentLayer
QCP::AntialiasedElements mAntialiasedElements
bool addItem(QCPAbstractItem *item)
Adds the specified item to the plot.
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
Renders the plot to a pixmap and returns it.
QCPGraph * graph() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator="", const QString &pdfTitle="")
Saves a PDF with the vectorized plot to the file fileName.
bool mAutoAddPlottableToLegend
int clearGraphs()
Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1)
Saves the plot to a rastered image file fileName in the image format format.
bool addPlottable(QCPAbstractPlottable *plottable)
Adds the specified plottable to the plot and, if setAutoAddPlottableToLegend is enabled,...
Qt::AspectRatioMode mBackgroundScaledMode
int mSelectionTolerance
virtual void mousePressEvent(QMouseEvent *event)
int clearItems()
Removes all items from the plot.
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
This signal is emitted when an axis is clicked.
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
Returns the item at the pixel position pos.
QPoint mMousePressPos
virtual void mouseMoveEvent(QMouseEvent *event)
QCP::PlottingHints plottingHints() const
void mouseWheel(QWheelEvent *event)
This signal is emitted when the QCustomPlot receives a mouse wheel event.
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
This signal is emitted when an item is double clicked.
bool mNoAntialiasingOnDrag
QList< QCPLegend * > selectedLegends() const
Returns the legends that currently have selected parts, i.e.
void mouseRelease(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse release event.
QCPLayoutGrid * mPlotLayout
bool noAntialiasingOnDrag() const
void mousePress(QMouseEvent *event)
This signal is emitted when the QCustomPlot receives a mouse press event.
friend class QCPAxisRect
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
Returns the plottable at the pixel position pos.
QList< QCPGraph * > selectedGraphs() const
Returns a list of the selected graphs.
void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
This signal is emitted when a plot title is double clicked.
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
Adds a new layer to this QCustomPlot instance.
QCP::Interactions mInteractions
virtual void draw(QCPPainter *painter)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
void setAutoAddPlottableToLegend(bool on)
If set to true, adding a plottable (e.g.
QCPAxis * xAxis2
A pointer to the secondary x Axis (top) of the main axis rect of the plot.
QList< QCPGraph * > mGraphs
QCPAbstractPlottable * plottable()
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool removeItem(QCPAbstractItem *item)
Removes the specified item from the plot.
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
Sets which elements are forcibly drawn not antialiased as an or combination of QCP::AntialiasedElemen...
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
This signal is emitted when an item is clicked.
QCPAxisRect * axisRect(int index=0) const
Returns the axis rect with index.
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
Moves the specified layer either above or below otherLayer.
QPixmap mBackgroundPixmap
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
Sets whether the specified antialiasedElement is forcibly drawn antialiased.
bool hasItem(QCPAbstractItem *item) const
Returns whether this QCustomPlot contains the item.
QCPAxis * yAxis2
A pointer to the secondary y Axis (right) of the main axis rect of the plot.
void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
This signal is emitted when a plottable is double clicked.
bool removePlottable(QCPAbstractPlottable *plottable)
Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend...
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
Sets which elements are forcibly drawn antialiased as an or combination of QCP::AntialiasedElement.
QCPAxis * yAxis
A pointer to the primary y Axis (left) of the main axis rect of the plot.
int layerCount() const
Returns the number of currently existing layers in the plot.
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
Returns the layout element at pixel position pos.
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
Sets whether the specified notAntialiasedElement is forcibly drawn not antialiased.
QList< QCPAbstractItem * > selectedItems() const
Returns a list of the selected items.
int n
bool done
Definition main.cpp:42
The QCP Namespace contains general enums and QFlags used throughout the QCustomPlot library.
Definition qcustomplot.h:93
bool isInvalidData(double value)
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Interaction
Defines the mouse interactions possible with QCustomPlot.
@ iSelectLegend
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts)
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iSelectPlottables
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
@ iSelectAxes
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
@ iSelectItems
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem)
@ iMultiSelect
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
@ iSelectOther
0x080 All other objects are selectable (e.g. your own derived layerables, the plot title,...
PlottingHint
Defines plotting hints that control various aspects of the quality and speed of plotting.
@ phCacheLabels
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
@ phForceRepaint
0x002 causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called w...
@ phFastPolylines
0x001 Graph/Curve lines are drawn with a faster method.
MarginSide
Defines the sides of a rectangular entity to which margins can be applied.
Definition qcustomplot.h:99
@ msAll
0xFF all margins
@ msBottom
0x08 bottom margin
@ msTop
0x04 top margin
@ msNone
0x00 no margin
@ msRight
0x02 right margin
@ msLeft
0x01 left margin
Definition qcustomplot.h:99
AntialiasedElement
Defines what objects of a plot can be forcibly drawn antialiased/not antialiased.
@ aeLegendItems
0x0010 Legend items
@ aeZeroLine
0x0400 Zero-lines, see QCPGrid::setZeroLinePen
@ aePlottables
0x0020 Main lines of plottables (excluding error bars, see element aeErrorBars)
@ aeGrid
0x0002 Grid lines
@ aeFills
0x0200 Borders of fills (e.g. under or between graphs)
@ aeErrorBars
0x0100 Error bars
@ aeLegend
0x0008 Legend box
@ aeAll
0xFFFF All elements
@ aeNone
0x0000 No elements
@ aeSubGrid
0x0004 Sub grid lines
@ aeScatters
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)
@ aeAxes
0x0001 Axis base line and tick marks
@ aeItems
0x0040 Main lines of items
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
QMap< double, QCPCurveData > QCPCurveDataMap
Container for storing QCPCurveData items in a sorted fashion.
QMap< double, QCPData > QCPDataMap
Container for storing QCPData items in a sorted fashion.
QMap< double, QCPBarData > QCPBarDataMap
Container for storing QCPBarData items in a sorted fashion.
grid on
function white
degrees offset
Definition sine.m:4