##// END OF EJS Templates
Updated QCustomPlot to 1.2
jeandet -
r18:a9eb4604e26a default
parent child
Show More
@@ -0,0 +1,62
1 #!/usr/bin/socexplorer -e
2 import numpy as np
3 freq1 = 30
4 freq2 = 300
5 time_step = 0.001
6
7 t_ini = -50 * 1.0/(max(freq1,freq2))
8 t_fin = -1 * t_ini
9
10 time_vec = np.arange(t_ini, t_fin, time_step)
11
12 #input signal
13 input_sig1 = np.sin(2 * np.pi * freq1 * time_vec)
14 input_sig2 = np.sin(2 * np.pi * freq2 * time_vec)
15 input_sig = input_sig1 + input_sig2
16
17
18 plot=PySocExplorer.SocExplorerPlot()
19 plot.setTitle("demo")
20 plot.setXaxisLabel("Time(s)")
21 plot.setYaxisLabel("Values")
22
23 Courbe1=plot.addGraph()
24 Courbe2=plot.addGraph()
25 Courbe3=plot.addGraph()
26
27 plot.setGraphData(Courbe1,time_vec.tolist(),input_sig1.tolist())
28 plot.setGraphData(Courbe2,time_vec.tolist(),input_sig2.tolist())
29 plot.setGraphData(Courbe3,time_vec.tolist(),input_sig.tolist())
30 # none line stepleft stepright stepcenter impulse
31 plot.setGraphLineStyle(0,"stepleft")
32 plot.setGraphLineStyle(Courbe2,"impulse")
33 # none dot cross plus circle disc square diamond star triangle invertedtriangle crosssquare plussquare crosscircle pluscircle peace
34 plot.setGraphScatterStyle(Courbe3,"square")
35
36 pen=plot.getGraphPen(1)
37 pen.setWidth(4)
38 color=pen.color()
39 color.setRgb(0x00FF00)
40 pen.setColor(color)
41 plot.setGraphPen(1,pen)
42
43 pen=plot.getGraphPen(0)
44 pen.setWidth(2)
45 color=pen.color()
46 color.setRgb(0xFF0000)
47 pen.setColor(color)
48 plot.setGraphPen(2,pen)
49
50 plot.rescaleAxis()
51
52
53
54
55
56
57
58
59
60
61
62
@@ -0,0 +1,62
1 #!/usr/bin/socexplorer -e
2 import numpy as np
3 freq1 = 30
4 freq2 = 300
5 time_step = 0.001
6
7 t_ini = -10 * 1.0/(max(freq1,freq2))
8 t_fin = -1 * t_ini
9
10 time_vec = np.arange(t_ini, t_fin, time_step)
11
12 #input signal
13 input_sig1 = np.sin(2 * np.pi * freq1 * time_vec)
14 input_sig2 = np.sin(2 * np.pi * freq2 * time_vec)
15 input_sig = input_sig1 + input_sig2
16
17
18 plot=PySocExplorer.SocExplorerPlot()
19 plot.setTitle("demo")
20 plot.setXaxisLabel("Time(s)")
21 plot.setYaxisLabel("Values")
22
23 Courbe1=plot.addGraph()
24 Courbe2=plot.addGraph()
25 Courbe3=plot.addGraph()
26
27 plot.setGraphData(Courbe1,time_vec.tolist(),input_sig1.tolist())
28 plot.setGraphData(Courbe2,time_vec.tolist(),input_sig2.tolist())
29 plot.setGraphData(Courbe3,time_vec.tolist(),input_sig.tolist())
30 # none line stepleft stepright stepcenter impulse
31 #plot.setGraphLineStyle(0,"stepleft")
32 #plot.setGraphLineStyle(Courbe2,"impulse")
33 # none dot cross plus circle disc square diamond star triangle invertedtriangle crosssquare plussquare crosscircle pluscircle peace
34 #plot.setGraphScatterStyle(Courbe3,"square")
35
36 pen=plot.getGraphPen(1)
37 pen.setWidth(1)
38 color=pen.color()
39 color.setRgb(0x00FF00)
40 pen.setColor(color)
41 plot.setGraphPen(1,pen)
42
43 pen=plot.getGraphPen(0)
44 pen.setWidth(1)
45 color=pen.color()
46 color.setRgb(0xFF0000)
47 pen.setColor(color)
48 plot.setGraphPen(2,pen)
49
50 plot.rescaleAxis()
51
52
53
54
55
56
57
58
59
60
61
62
@@ -0,0 +1,37
1 #!/usr/bin/socexplorer -e
2 import numpy as np
3 freq1 = 30
4 freq2 = 300
5 time_step = 0.000001
6
7 t_ini = -500 * 1.0/(max(freq1,freq2))
8 t_fin = -1 * t_ini
9
10 time_vec = np.arange(t_ini, t_fin, time_step)
11
12 #input signal
13 input_sig1 = np.sin(2 * np.pi * freq1 * time_vec)
14
15
16 plot=PySocExplorer.SocExplorerPlot()
17 plot.setTitle("demo")
18 plot.setXaxisLabel("Time(s)")
19 plot.setYaxisLabel("Values")
20
21 Courbe1=plot.addGraph()
22
23 plot.setGraphData(Courbe1,time_vec.tolist(),input_sig1.tolist())
24
25 plot.rescaleAxis()
26
27
28
29
30
31
32
33
34
35
36
37
This diff has been collapsed as it changes many lines, (9445 lines changed) Show them Hide them
@@ -1,7 +1,7
1 1 /***************************************************************************
2 2 ** **
3 3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011, 2012, 2013 Emanuel Eichhammer **
4 ** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer **
5 5 ** **
6 6 ** This program is free software: you can redistribute it and/or modify **
7 7 ** it under the terms of the GNU General Public License as published by **
@@ -19,8 +19,8
19 19 ****************************************************************************
20 20 ** Author: Emanuel Eichhammer **
21 21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 09.12.13 **
23 ** Version: 1.1.1 **
22 ** Date: 14.03.14 **
23 ** Version: 1.2.0 **
24 24 ****************************************************************************/
25 25
26 26 #include "qcustomplot.h"
@@ -35,7 +35,7
35 35 /*! \class QCPPainter
36 36 \brief QPainter subclass used internally
37 37
38 This internal class is used to provide some extended functionality e.g. for tweaking position
38 This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
39 39 consistency between antialiased and non-antialiased painting. Further it provides workarounds
40 40 for QPainter quirks.
41 41
@@ -135,7 +135,7 void QCPPainter::drawLine(const QLineF &
135 135 QPainter::drawLine(line.toLine());
136 136 }
137 137
138 /*!
138 /*!
139 139 Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
140 140 with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
141 141 antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for
@@ -752,7 +752,8 QCPLayer::QCPLayer(QCustomPlot *parentPl
752 752 QObject(parentPlot),
753 753 mParentPlot(parentPlot),
754 754 mName(layerName),
755 mIndex(-1) // will be set to a proper value by the QCustomPlot layer creation function
755 mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
756 mVisible(true)
756 757 {
757 758 // Note: no need to make sure layerName is unique, because layer
758 759 // management is done with QCustomPlot functions.
@@ -772,6 +773,19 QCPLayer::~QCPLayer()
772 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.";
773 774 }
774 775
776 /*!
777 Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
778 layer will be invisible.
779
780 This function doesn't change the visibility property of the layerables (\ref
781 QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
782 visibility of the parent layer into account.
783 */
784 void QCPLayer::setVisible(bool visible)
785 {
786 mVisible = visible;
787 }
788
775 789 /*! \internal
776 790
777 791 Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
@@ -885,6 +899,17 void QCPLayer::removeChild(QCPLayerable
885 899 */
886 900
887 901 /* end documentation of pure virtual functions */
902 /* start documentation of signals */
903
904 /*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
905
906 This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
907 a different layer.
908
909 \see setLayer
910 */
911
912 /* end documentation of signals */
888 913
889 914 /*!
890 915 Creates a new QCPLayerable instance.
@@ -985,9 +1010,9 void QCPLayerable::setAntialiased(bool e
985 1010 }
986 1011
987 1012 /*!
988 Returns whether this layerable is visible, taking possible direct layerable parent visibility
989 into account. This is the method that is consulted to decide whether a layerable shall be drawn
990 or not.
1013 Returns whether this layerable is visible, taking the visibility of the layerable parent and the
1014 visibility of the layer this layerable is on into account. This is the method that is consulted
1015 to decide whether a layerable shall be drawn or not.
991 1016
992 1017 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
993 1018 subclasses, like in the case of QCPLayoutElement), this function returns true only if this
@@ -999,7 +1024,7 void QCPLayerable::setAntialiased(bool e
999 1024 */
1000 1025 bool QCPLayerable::realVisibility() const
1001 1026 {
1002 return mVisible && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1027 return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1003 1028 }
1004 1029
1005 1030 /*!
@@ -1113,11 +1138,14 bool QCPLayerable::moveToLayer(QCPLayer
1113 1138 return false;
1114 1139 }
1115 1140
1141 QCPLayer *oldLayer = mLayer;
1116 1142 if (mLayer)
1117 1143 mLayer->removeChild(this);
1118 1144 mLayer = layer;
1119 1145 if (mLayer)
1120 1146 mLayer->addChild(this, prepend);
1147 if (mLayer != oldLayer)
1148 emit layerChanged(mLayer);
1121 1149 return true;
1122 1150 }
1123 1151
@@ -1258,7 +1286,7 void QCPLayerable::deselectEvent(bool *s
1258 1286 \see QCPAxis::setRange
1259 1287 */
1260 1288
1261 /*!
1289 /*!
1262 1290 Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
1263 1291 intervals would cause errors due to the 11-bit exponent of double precision numbers,
1264 1292 corresponding to a minimum magnitude of roughly 1e-308.
@@ -1266,7 +1294,7 void QCPLayerable::deselectEvent(bool *s
1266 1294 */
1267 1295 const double QCPRange::minRange = 1e-280;
1268 1296
1269 /*!
1297 /*!
1270 1298 Maximum values (negative and positive) the range will accept in range-changing functions.
1271 1299 Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
1272 1300 corresponding to a maximum magnitude of roughly 1e308.
@@ -1276,7 +1304,7 const double QCPRange::minRange = 1e-280
1276 1304 */
1277 1305 const double QCPRange::maxRange = 1e250;
1278 1306
1279 /*!
1307 /*!
1280 1308 Constructs a range with \a lower and \a upper set to zero.
1281 1309 */
1282 1310 QCPRange::QCPRange() :
@@ -1295,7 +1323,7 QCPRange::QCPRange(double lower, double
1295 1323 normalize();
1296 1324 }
1297 1325
1298 /*!
1326 /*!
1299 1327 Returns the size of the range, i.e. \a upper-\a lower
1300 1328 */
1301 1329 double QCPRange::size() const
@@ -1303,7 +1331,7 double QCPRange::size() const
1303 1331 return upper-lower;
1304 1332 }
1305 1333
1306 /*!
1334 /*!
1307 1335 Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1308 1336 */
1309 1337 double QCPRange::center() const
@@ -1311,7 +1339,7 double QCPRange::center() const
1311 1339 return (upper+lower)*0.5;
1312 1340 }
1313 1341
1314 /*!
1342 /*!
1315 1343 Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
1316 1344 are swapped.
1317 1345 */
@@ -1321,7 +1349,7 void QCPRange::normalize()
1321 1349 qSwap(lower, upper);
1322 1350 }
1323 1351
1324 /*!
1352 /*!
1325 1353 Expands this range such that \a otherRange is contained in the new range. It is assumed that both
1326 1354 this range and \a otherRange are normalized (see \ref normalize).
1327 1355
@@ -1338,7 +1366,7 void QCPRange::expand(const QCPRange &ot
1338 1366 }
1339 1367
1340 1368
1341 /*!
1369 /*!
1342 1370 Returns an expanded range that contains this and \a otherRange. It is assumed that both this
1343 1371 range and \a otherRange are normalized (see \ref normalize).
1344 1372
@@ -1351,7 +1379,7 QCPRange QCPRange::expanded(const QCPRan
1351 1379 return result;
1352 1380 }
1353 1381
1354 /*!
1382 /*!
1355 1383 Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
1356 1384 the range won't span the positive and negative sign domain, i.e. contain zero. Further
1357 1385 \a lower will always be numerically smaller (or equal) to \a upper.
@@ -1408,7 +1436,7 QCPRange QCPRange::sanitizedForLogScale(
1408 1436 return sanitizedRange;
1409 1437 }
1410 1438
1411 /*!
1439 /*!
1412 1440 Returns a sanitized version of the range. Sanitized means for linear scales, that
1413 1441 \a lower will always be numerically smaller (or equal) to \a upper.
1414 1442 */
@@ -1419,7 +1447,7 QCPRange QCPRange::sanitizedForLinScale(
1419 1447 return sanitizedRange;
1420 1448 }
1421 1449
1422 /*!
1450 /*!
1423 1451 Returns true when \a value lies within or exactly on the borders of the range.
1424 1452 */
1425 1453 bool QCPRange::contains(double value) const
@@ -1427,7 +1455,7 bool QCPRange::contains(double value) co
1427 1455 return value >= lower && value <= upper;
1428 1456 }
1429 1457
1430 /*!
1458 /*!
1431 1459 Checks, whether the specified range is within valid bounds, which are defined
1432 1460 as QCPRange::maxRange and QCPRange::minRange.
1433 1461 A valid range means:
@@ -1450,7 +1478,7 bool QCPRange::validRange(double lower,
1450 1478 qAbs(lower-upper) < maxRange);
1451 1479 }
1452 1480
1453 /*!
1481 /*!
1454 1482 \overload
1455 1483 Checks, whether the specified range is within valid bounds, which are defined
1456 1484 as QCPRange::maxRange and QCPRange::minRange.
@@ -1532,17 +1560,11 bool QCPRange::validRange(const QCPRange
1532 1560 Since a QCPLayout is a layout element itself, it may be placed inside other layouts. This way,
1533 1561 complex hierarchies may be created, offering very flexible arrangements.
1534 1562
1535 <div style="text-align:center">
1536 <div style="display:inline-block; margin-left:auto; margin-right:auto">\image html LayoutsystemSketch0.png ""</div>
1537 <div style="display:inline-block; margin-left:auto; margin-right:auto">\image html LayoutsystemSketch1.png ""</div>
1538 <div style="clear:both"></div>
1539 <div style="display:inline-block; max-width:1000px; text-align:justify">
1540 Sketch of the default QCPLayoutGrid accessible via \ref QCustomPlot::plotLayout. The left image
1541 shows the outer and inner rect of the grid layout itself while the right image shows how two
1542 child layout elements are placed inside the grid layout next to each other in cells (0, 0) and
1543 (0, 1).
1544 </div>
1545 </div>
1563 \image html LayoutsystemSketch.png
1564
1565 Above is a sketch of the default \ref QCPLayoutGrid accessible via \ref QCustomPlot::plotLayout.
1566 It shows how two child layout elements are placed inside the grid layout next to each other in
1567 cells (0, 0) and (0, 1).
1546 1568
1547 1569 \subsection layoutsystem-plotlayout The top level plot layout
1548 1570
@@ -1561,7 +1583,7 bool QCPRange::validRange(const QCPRange
1561 1583 title->setFont(QFont("sans", 12, QFont::Bold));
1562 1584 // then we add it to the main plot layout:
1563 1585 customPlot->plotLayout()->insertRow(0); // insert an empty row above the axis rect
1564 customPlot->plotLayout()->addElement(0, 0, title); // insert the title in the empty cell we just created
1586 customPlot->plotLayout()->addElement(0, 0, title); // place the title in the empty cell we've just created
1565 1587 \endcode
1566 1588 \image html layoutsystem-addingplottitle.png
1567 1589
@@ -1569,15 +1591,28 bool QCPRange::validRange(const QCPRange
1569 1591 \code
1570 1592 customPlot->plotLayout()->clear(); // let's start from scratch and remove the default axis rect
1571 1593 // add the first axis rect in second row (row index 1):
1572 customPlot->plotLayout()->addElement(1, 0, new QCPAxisRect(customPlot));
1594 QCPAxisRect *topAxisRect = new QCPAxisRect(customPlot);
1595 customPlot->plotLayout()->addElement(1, 0, topAxisRect);
1573 1596 // create a sub layout that we'll place in first row:
1574 1597 QCPLayoutGrid *subLayout = new QCPLayoutGrid;
1575 1598 customPlot->plotLayout()->addElement(0, 0, subLayout);
1576 // add two axis rects in the sub layout next to eachother:
1577 subLayout->addElement(0, 0, new QCPAxisRect(customPlot));
1578 subLayout->addElement(0, 1, new QCPAxisRect(customPlot));
1599 // add two axis rects in the sub layout next to each other:
1600 QCPAxisRect *leftAxisRect = new QCPAxisRect(customPlot);
1601 QCPAxisRect *rightAxisRect = new QCPAxisRect(customPlot);
1602 subLayout->addElement(0, 0, leftAxisRect);
1603 subLayout->addElement(0, 1, rightAxisRect);
1579 1604 subLayout->setColumnStretchFactor(0, 3); // left axis rect shall have 60% of width
1580 1605 subLayout->setColumnStretchFactor(1, 2); // right one only 40% (3:2 = 60:40)
1606 // since we've created the axis rects and axes from scratch, we need to place them on
1607 // according layers, if we don't want the grid to be drawn above the axes etc.
1608 // place the axis on "axes" layer and grids on the "grid" layer, which is below "axes":
1609 QList<QCPAxis*> allAxes;
1610 allAxes << topAxisRect->axes() << leftAxisRect->axes() << rightAxisRect->axes();
1611 foreach (QCPAxis *axis, allAxes)
1612 {
1613 axis->setLayer("axes");
1614 axis->grid()->setLayer("grid");
1615 }
1581 1616 \endcode
1582 1617 \image html layoutsystem-multipleaxisrects.png
1583 1618
@@ -1612,7 +1647,7 bool QCPRange::validRange(const QCPRange
1612 1647 \section QCPMarginGroup-example Example
1613 1648
1614 1649 First create a margin group:
1615 \code
1650 \code
1616 1651 QCPMarginGroup *group = new QCPMarginGroup(customPlot);
1617 1652 \endcode
1618 1653 Then set this group on the layout element sides:
@@ -2007,37 +2042,40 void QCPLayoutElement::setMarginGroup(QC
2007 2042 }
2008 2043
2009 2044 /*!
2010 Updates the layout element and sub-elements. This function is automatically called upon replot by
2011 the parent layout element.
2045 Updates the layout element and sub-elements. This function is automatically called before every
2046 replot by the parent layout element. It is called multiple times, once for every \ref
2047 UpdatePhase. The phases are run through in the order of the enum values. For details about what
2048 happens at the different phases, see the documentation of \ref UpdatePhase.
2012 2049
2013 2050 Layout elements that have child elements should call the \ref update method of their child
2014 elements.
2015
2016 The default implementation executes the automatic margin mechanism, so subclasses should make
2017 sure to call the base class implementation.
2018 */
2019 void QCPLayoutElement::update()
2020 {
2021 if (mAutoMargins != QCP::msNone)
2022 {
2023 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
2024 QMargins newMargins = mMargins;
2025 QVector<QCP::MarginSide> marginSides = QVector<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
2026 for (int i=0; i<marginSides.size(); ++i)
2027 {
2028 QCP::MarginSide side = marginSides.at(i);
2029 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
2030 {
2031 if (mMarginGroups.contains(side))
2032 QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
2033 else
2034 QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
2035 // apply minimum margin restrictions:
2036 if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
2037 QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
2038 }
2039 }
2040 setMargins(newMargins);
2051 elements, and pass the current \a phase unchanged.
2052
2053 The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
2054 Subclasses should make sure to call the base class implementation.
2055 */
2056 void QCPLayoutElement::update(UpdatePhase phase)
2057 {
2058 if (phase == upMargins)
2059 {
2060 if (mAutoMargins != QCP::msNone)
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))
2074 QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
2075 }
2076 }
2077 setMargins(newMargins);
2078 }
2041 2079 }
2042 2080 }
2043 2081
@@ -2069,7 +2107,7 QSize QCPLayoutElement::maximumSizeHint(
2069 2107 Returns a list of all child elements in this layout element. If \a recursive is true, all
2070 2108 sub-child elements are included in the list, too.
2071 2109
2072 Note that there may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
2110 \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
2073 2111 empty cells which yield 0 at the respective index.)
2074 2112 */
2075 2113 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
@@ -2109,22 +2147,21 double QCPLayoutElement::selectTest(cons
2109 2147 return -1;
2110 2148 }
2111 2149
2112 /*! \internal
2150 /*! \internal
2113 2151
2114 2152 propagates the parent plot initialization to all child elements, by calling \ref
2115 2153 QCPLayerable::initializeParentPlot on them.
2116 2154 */
2117 2155 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2118 2156 {
2119 QList<QCPLayoutElement*> els = elements(false);
2120 for (int i=0; i<els.size(); ++i)
2121 {
2122 if (!els.at(i)->parentPlot())
2123 els.at(i)->initializeParentPlot(parentPlot);
2124 }
2125 }
2126
2127 /*! \internal
2157 foreach (QCPLayoutElement* el, elements(false))
2158 {
2159 if (!el->parentPlot())
2160 el->initializeParentPlot(parentPlot);
2161 }
2162 }
2163
2164 /*! \internal
2128 2165
2129 2166 Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
2130 2167 side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
@@ -2227,25 +2264,27 QCPLayout::QCPLayout()
2227 2264
2228 2265 Finally, \ref update is called on all child elements.
2229 2266 */
2230 void QCPLayout::update()
2231 {
2232 QCPLayoutElement::update(); // recalculates (auto-)margins
2267 void QCPLayout::update(UpdatePhase phase)
2268 {
2269 QCPLayoutElement::update(phase);
2233 2270
2234 2271 // set child element rects according to layout:
2235 updateLayout();
2272 if (phase == upLayout)
2273 updateLayout();
2236 2274
2237 2275 // propagate update call to child elements:
2238 for (int i=0; i<elementCount(); ++i)
2276 const int elCount = elementCount();
2277 for (int i=0; i<elCount; ++i)
2239 2278 {
2240 2279 if (QCPLayoutElement *el = elementAt(i))
2241 el->update();
2280 el->update(phase);
2242 2281 }
2243 2282 }
2244 2283
2245 2284 /* inherits documentation from base class */
2246 2285 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2247 2286 {
2248 int c = elementCount();
2287 const int c = elementCount();
2249 2288 QList<QCPLayoutElement*> result;
2250 2289 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2251 2290 result.reserve(c);
@@ -2657,7 +2696,7 bool QCPLayoutGrid::addElement(int row,
2657 2696 adoptElement(element);
2658 2697 return true;
2659 2698 } else
2660 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2699 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2661 2700 } else
2662 2701 qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2663 2702 return false;
@@ -3616,8 +3655,8 void QCPLineEnding::draw(QCPPainter *pai
3616 3655 if (lengthVec.isNull())
3617 3656 lengthVec = QVector2D(1, 0);
3618 3657 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3619 lengthVec *= mLength*(mInverted ? -1 : 1);
3620 widthVec *= mWidth*0.5*(mInverted ? -1 : 1);
3658 lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3659 widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3621 3660
3622 3661 QPen penBackup = painter->pen();
3623 3662 QBrush brushBackup = painter->brush();
@@ -3644,7 +3683,7 void QCPLineEnding::draw(QCPPainter *pai
3644 3683 {
3645 3684 QPointF points[4] = {pos.toPointF(),
3646 3685 (pos-lengthVec+widthVec).toPointF(),
3647 (pos-lengthVec*0.8).toPointF(),
3686 (pos-lengthVec*0.8f).toPointF(),
3648 3687 (pos-lengthVec-widthVec).toPointF()
3649 3688 };
3650 3689 painter->setPen(miterPen);
@@ -3717,13 +3756,13 void QCPLineEnding::draw(QCPPainter *pai
3717 3756 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3718 3757 {
3719 3758 // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3720 painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(),
3721 (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
3759 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3760 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3722 3761 } else
3723 3762 {
3724 3763 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3725 painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0, (double)painter->pen().widthF())*0.5).toPointF(),
3726 (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0, (double)painter->pen().widthF())*0.5).toPointF());
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());
3727 3766 }
3728 3767 break;
3729 3768 }
@@ -3994,8 +4033,10 void QCPGrid::drawSubGridLines(QCPPainte
3994 4033
3995 4034 /*! \fn Qt::Orientation QCPAxis::orientation() const
3996 4035
3997 Returns the orientation of the axis. The axis orientation (horizontal or vertical) is deduced
4036 Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
3998 4037 from the axis type (left, top, right or bottom).
4038
4039 \see orientation(AxisType type)
3999 4040 */
4000 4041
4001 4042 /*! \fn QCPGrid *QCPAxis::grid() const
@@ -4004,6 +4045,13 void QCPGrid::drawSubGridLines(QCPPainte
4004 4045 grid is displayed.
4005 4046 */
4006 4047
4048 /*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
4049
4050 Returns the orientation of the specified axis type
4051
4052 \see orientation()
4053 */
4054
4007 4055 /* end of documentation of inline functions */
4008 4056 /* start of documentation of signals */
4009 4057
@@ -4036,12 +4084,22 void QCPGrid::drawSubGridLines(QCPPainte
4036 4084 \a oldRange.
4037 4085 */
4038 4086
4087 /*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
4088
4089 This signal is emitted when the scale type changes, by calls to \ref setScaleType
4090 */
4091
4039 4092 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
4040 4093
4041 4094 This signal is emitted when the selection state of this axis has changed, either by user interaction
4042 4095 or by a direct call to \ref setSelectedParts.
4043 4096 */
4044 4097
4098 /*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
4099
4100 This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
4101 */
4102
4045 4103 /* end of documentation of signals */
4046 4104
4047 4105 /*!
@@ -4053,27 +4111,21 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4053 4111 // axis base:
4054 4112 mAxisType(type),
4055 4113 mAxisRect(parent),
4056 mOffset(0),
4057 4114 mPadding(5),
4058 mOrientation((type == atBottom || type == atTop) ? Qt::Horizontal : Qt::Vertical),
4115 mOrientation(orientation(type)),
4059 4116 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4060 4117 mSelectedParts(spNone),
4061 4118 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4062 4119 mSelectedBasePen(QPen(Qt::blue, 2)),
4063 mLowerEnding(QCPLineEnding::esNone),
4064 mUpperEnding(QCPLineEnding::esNone),
4065 4120 // axis label:
4066 mLabelPadding(0),
4067 4121 mLabel(""),
4068 4122 mLabelFont(mParentPlot->font()),
4069 4123 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4070 4124 mLabelColor(Qt::black),
4071 4125 mSelectedLabelColor(Qt::blue),
4072 4126 // tick labels:
4073 mTickLabelPadding(0),
4074 4127 mTickLabels(true),
4075 4128 mAutoTickLabels(true),
4076 mTickLabelRotation(0),
4077 4129 mTickLabelType(ltNumber),
4078 4130 mTickLabelFont(mParentPlot->font()),
4079 4131 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
@@ -4084,7 +4136,6 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4084 4136 mNumberPrecision(6),
4085 4137 mNumberFormatChar('g'),
4086 4138 mNumberBeautifulPowers(true),
4087 mNumberMultiplyCross(false),
4088 4139 // ticks and subticks:
4089 4140 mTicks(true),
4090 4141 mTickStep(1),
@@ -4093,10 +4144,6 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4093 4144 mAutoTicks(true),
4094 4145 mAutoTickStep(true),
4095 4146 mAutoSubTicks(true),
4096 mTickLengthIn(5),
4097 mTickLengthOut(0),
4098 mSubTickLengthIn(2),
4099 mSubTickLengthOut(0),
4100 4147 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4101 4148 mSelectedTickPen(QPen(Qt::blue, 2)),
4102 4149 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
@@ -4109,11 +4156,9 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4109 4156 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4110 4157 // internal members:
4111 4158 mGrid(new QCPGrid(this)),
4112 mLabelCache(16), // cache at most 16 (tick) labels
4159 mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4113 4160 mLowestVisibleTick(0),
4114 4161 mHighestVisibleTick(-1),
4115 mExponentialChar('e'), // will be updated with locale sensitive values in setupTickVector
4116 mPositiveSignChar('+'), // will be updated with locale sensitive values in setupTickVector
4117 4162 mCachedMarginValid(false),
4118 4163 mCachedMargin(0)
4119 4164 {
@@ -4140,6 +4185,23 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4140 4185 }
4141 4186 }
4142 4187
4188 QCPAxis::~QCPAxis()
4189 {
4190 delete mAxisPainter;
4191 }
4192
4193 /* No documentation as it is a property getter */
4194 int QCPAxis::tickLabelPadding() const
4195 {
4196 return mAxisPainter->tickLabelPadding;
4197 }
4198
4199 /* No documentation as it is a property getter */
4200 double QCPAxis::tickLabelRotation() const
4201 {
4202 return mAxisPainter->tickLabelRotation;
4203 }
4204
4143 4205 /* No documentation as it is a property getter */
4144 4206 QString QCPAxis::numberFormat() const
4145 4207 {
@@ -4148,12 +4210,60 QString QCPAxis::numberFormat() const
4148 4210 if (mNumberBeautifulPowers)
4149 4211 {
4150 4212 result.append("b");
4151 if (mNumberMultiplyCross)
4213 if (mAxisPainter->numberMultiplyCross)
4152 4214 result.append("c");
4153 4215 }
4154 4216 return result;
4155 4217 }
4156 4218
4219 /* No documentation as it is a property getter */
4220 int QCPAxis::tickLengthIn() const
4221 {
4222 return mAxisPainter->tickLengthIn;
4223 }
4224
4225 /* No documentation as it is a property getter */
4226 int QCPAxis::tickLengthOut() const
4227 {
4228 return mAxisPainter->tickLengthOut;
4229 }
4230
4231 /* No documentation as it is a property getter */
4232 int QCPAxis::subTickLengthIn() const
4233 {
4234 return mAxisPainter->subTickLengthIn;
4235 }
4236
4237 /* No documentation as it is a property getter */
4238 int QCPAxis::subTickLengthOut() const
4239 {
4240 return mAxisPainter->subTickLengthOut;
4241 }
4242
4243 /* No documentation as it is a property getter */
4244 int QCPAxis::labelPadding() const
4245 {
4246 return mAxisPainter->labelPadding;
4247 }
4248
4249 /* No documentation as it is a property getter */
4250 int QCPAxis::offset() const
4251 {
4252 return mAxisPainter->offset;
4253 }
4254
4255 /* No documentation as it is a property getter */
4256 QCPLineEnding QCPAxis::lowerEnding() const
4257 {
4258 return mAxisPainter->lowerEnding;
4259 }
4260
4261 /* No documentation as it is a property getter */
4262 QCPLineEnding QCPAxis::upperEnding() const
4263 {
4264 return mAxisPainter->upperEnding;
4265 }
4266
4157 4267 /*!
4158 4268 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4159 4269 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
@@ -4167,14 +4277,15 QString QCPAxis::numberFormat() const
4167 4277 part). To only display the decimal power, set the number precision to zero with
4168 4278 \ref setNumberPrecision.
4169 4279 */
4170 void QCPAxis::setScaleType(ScaleType type)
4280 void QCPAxis::setScaleType(QCPAxis::ScaleType type)
4171 4281 {
4172 4282 if (mScaleType != type)
4173 4283 {
4174 4284 mScaleType = type;
4175 4285 if (mScaleType == stLogarithmic)
4176 mRange = mRange.sanitizedForLogScale();
4286 setRange(mRange.sanitizedForLogScale());
4177 4287 mCachedMarginValid = false;
4288 emit scaleTypeChanged(mScaleType);
4178 4289 }
4179 4290 }
4180 4291
@@ -4235,7 +4346,11 void QCPAxis::setRange(const QCPRange &r
4235 4346 */
4236 4347 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4237 4348 {
4238 mSelectableParts = selectable;
4349 if (mSelectableParts != selectable)
4350 {
4351 mSelectableParts = selectable;
4352 emit selectableChanged(mSelectableParts);
4353 }
4239 4354 }
4240 4355
4241 4356 /*!
@@ -4257,8 +4372,6 void QCPAxis::setSelectedParts(const Sel
4257 4372 {
4258 4373 if (mSelectedParts != selected)
4259 4374 {
4260 if (mSelectedParts.testFlag(spTickLabels) != selected.testFlag(spTickLabels))
4261 mLabelCache.clear();
4262 4375 mSelectedParts = selected;
4263 4376 emit selectionChanged(mSelectedParts);
4264 4377 }
@@ -4454,7 +4567,7 void QCPAxis::setAutoTickLabels(bool on)
4454 4567 /*!
4455 4568 Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
4456 4569 automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
4457 readable plots.
4570 readable plots.
4458 4571
4459 4572 The number of ticks the algorithm aims for within the visible range can be specified with \ref
4460 4573 setAutoTickCount.
@@ -4523,9 +4636,9 void QCPAxis::setTickLabels(bool show)
4523 4636 */
4524 4637 void QCPAxis::setTickLabelPadding(int padding)
4525 4638 {
4526 if (mTickLabelPadding != padding)
4527 {
4528 mTickLabelPadding = padding;
4639 if (mAxisPainter->tickLabelPadding != padding)
4640 {
4641 mAxisPainter->tickLabelPadding = padding;
4529 4642 mCachedMarginValid = false;
4530 4643 }
4531 4644 }
@@ -4571,7 +4684,6 void QCPAxis::setTickLabelFont(const QFo
4571 4684 {
4572 4685 mTickLabelFont = font;
4573 4686 mCachedMarginValid = false;
4574 mLabelCache.clear();
4575 4687 }
4576 4688 }
4577 4689
@@ -4586,7 +4698,6 void QCPAxis::setTickLabelColor(const QC
4586 4698 {
4587 4699 mTickLabelColor = color;
4588 4700 mCachedMarginValid = false;
4589 mLabelCache.clear();
4590 4701 }
4591 4702 }
4592 4703
@@ -4601,11 +4712,10 void QCPAxis::setTickLabelColor(const QC
4601 4712 */
4602 4713 void QCPAxis::setTickLabelRotation(double degrees)
4603 4714 {
4604 if (!qFuzzyIsNull(degrees-mTickLabelRotation))
4605 {
4606 mTickLabelRotation = qBound(-90.0, degrees, 90.0);
4715 if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4716 {
4717 mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4607 4718 mCachedMarginValid = false;
4608 mLabelCache.clear();
4609 4719 }
4610 4720 }
4611 4721
@@ -4623,7 +4733,6 void QCPAxis::setDateTimeFormat(const QS
4623 4733 {
4624 4734 mDateTimeFormat = format;
4625 4735 mCachedMarginValid = false;
4626 mLabelCache.clear();
4627 4736 }
4628 4737 }
4629 4738
@@ -4685,7 +4794,6 void QCPAxis::setNumberFormat(const QStr
4685 4794 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4686 4795 return;
4687 4796 }
4688 mLabelCache.clear();
4689 4797 mCachedMarginValid = false;
4690 4798
4691 4799 // interpret first char as number format char:
@@ -4701,7 +4809,7 void QCPAxis::setNumberFormat(const QStr
4701 4809 if (formatCode.length() < 2)
4702 4810 {
4703 4811 mNumberBeautifulPowers = false;
4704 mNumberMultiplyCross = false;
4812 mAxisPainter->numberMultiplyCross = false;
4705 4813 return;
4706 4814 }
4707 4815
@@ -4716,17 +4824,17 void QCPAxis::setNumberFormat(const QStr
4716 4824 }
4717 4825 if (formatCode.length() < 3)
4718 4826 {
4719 mNumberMultiplyCross = false;
4827 mAxisPainter->numberMultiplyCross = false;
4720 4828 return;
4721 4829 }
4722 4830
4723 4831 // interpret third char as indicator for dot or cross multiplication symbol:
4724 4832 if (formatCode.at(2) == 'c')
4725 4833 {
4726 mNumberMultiplyCross = true;
4834 mAxisPainter->numberMultiplyCross = true;
4727 4835 } else if (formatCode.at(2) == 'd')
4728 4836 {
4729 mNumberMultiplyCross = false;
4837 mAxisPainter->numberMultiplyCross = false;
4730 4838 } else
4731 4839 {
4732 4840 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
@@ -4812,32 +4920,25 void QCPAxis::setTickVectorLabels(const
4812 4920 zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4813 4921 they won't collide with the ticks.
4814 4922
4815 \see setSubTickLength
4923 \see setSubTickLength, setTickLengthIn, setTickLengthOut
4816 4924 */
4817 4925 void QCPAxis::setTickLength(int inside, int outside)
4818 4926 {
4819 if (mTickLengthIn != inside)
4820 {
4821 mTickLengthIn = inside;
4822 }
4823 if (mTickLengthOut != outside)
4824 {
4825 mTickLengthOut = outside;
4826 mCachedMarginValid = false; // only outside tick length can change margin
4827 }
4927 setTickLengthIn(inside);
4928 setTickLengthOut(outside);
4828 4929 }
4829 4930
4830 4931 /*!
4831 4932 Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4832 4933 inside the plot.
4833 4934
4834 \see setTickLengthOut, setSubTickLength
4935 \see setTickLengthOut, setTickLength, setSubTickLength
4835 4936 */
4836 4937 void QCPAxis::setTickLengthIn(int inside)
4837 4938 {
4838 if (mTickLengthIn != inside)
4839 {
4840 mTickLengthIn = inside;
4939 if (mAxisPainter->tickLengthIn != inside)
4940 {
4941 mAxisPainter->tickLengthIn = inside;
4841 4942 }
4842 4943 }
4843 4944
@@ -4846,13 +4947,13 void QCPAxis::setTickLengthIn(int inside
4846 4947 outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4847 4948 increase their distance to the axis accordingly, so they won't collide with the ticks.
4848 4949
4849 \see setTickLengthIn, setSubTickLength
4950 \see setTickLengthIn, setTickLength, setSubTickLength
4850 4951 */
4851 4952 void QCPAxis::setTickLengthOut(int outside)
4852 4953 {
4853 if (mTickLengthOut != outside)
4854 {
4855 mTickLengthOut = outside;
4954 if (mAxisPainter->tickLengthOut != outside)
4955 {
4956 mAxisPainter->tickLengthOut = outside;
4856 4957 mCachedMarginValid = false; // only outside tick length can change margin
4857 4958 }
4858 4959 }
@@ -4878,31 +4979,26 void QCPAxis::setSubTickCount(int count)
4878 4979 the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4879 4980 than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4880 4981 so they won't collide with the ticks.
4982
4983 \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4881 4984 */
4882 4985 void QCPAxis::setSubTickLength(int inside, int outside)
4883 4986 {
4884 if (mSubTickLengthIn != inside)
4885 {
4886 mSubTickLengthIn = inside;
4887 }
4888 if (mSubTickLengthOut != outside)
4889 {
4890 mSubTickLengthOut = outside;
4891 mCachedMarginValid = false; // only outside tick length can change margin
4892 }
4987 setSubTickLengthIn(inside);
4988 setSubTickLengthOut(outside);
4893 4989 }
4894 4990
4895 4991 /*!
4896 4992 Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4897 4993 the plot.
4898 4994
4899 \see setSubTickLengthOut, setTickLength
4995 \see setSubTickLengthOut, setSubTickLength, setTickLength
4900 4996 */
4901 4997 void QCPAxis::setSubTickLengthIn(int inside)
4902 4998 {
4903 if (mSubTickLengthIn != inside)
4904 {
4905 mSubTickLengthIn = inside;
4999 if (mAxisPainter->subTickLengthIn != inside)
5000 {
5001 mAxisPainter->subTickLengthIn = inside;
4906 5002 }
4907 5003 }
4908 5004
@@ -4911,13 +5007,13 void QCPAxis::setSubTickLengthIn(int ins
4911 5007 outside the plot. If \a outside is greater than zero, the tick labels will increase their
4912 5008 distance to the axis accordingly, so they won't collide with the ticks.
4913 5009
4914 \see setSubTickLengthIn, setTickLength
5010 \see setSubTickLengthIn, setSubTickLength, setTickLength
4915 5011 */
4916 5012 void QCPAxis::setSubTickLengthOut(int outside)
4917 5013 {
4918 if (mSubTickLengthOut != outside)
4919 {
4920 mSubTickLengthOut = outside;
5014 if (mAxisPainter->subTickLengthOut != outside)
5015 {
5016 mAxisPainter->subTickLengthOut = outside;
4921 5017 mCachedMarginValid = false; // only outside tick length can change margin
4922 5018 }
4923 5019 }
@@ -4996,9 +5092,9 void QCPAxis::setLabel(const QString &st
4996 5092 */
4997 5093 void QCPAxis::setLabelPadding(int padding)
4998 5094 {
4999 if (mLabelPadding != padding)
5000 {
5001 mLabelPadding = padding;
5095 if (mAxisPainter->labelPadding != padding)
5096 {
5097 mAxisPainter->labelPadding = padding;
5002 5098 mCachedMarginValid = false;
5003 5099 }
5004 5100 }
@@ -5025,12 +5121,14 void QCPAxis::setPadding(int padding)
5025 5121 /*!
5026 5122 Sets the offset the axis has to its axis rect side.
5027 5123
5028 If an axis rect side has multiple axes, only the offset of the inner most axis has meaning. The offset of the other axes
5029 is controlled automatically, to place the axes at appropriate positions to prevent them from overlapping.
5124 If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
5125 only the offset of the inner most axis has meaning (even if it is set to be invisible). The
5126 offset of the other, outer axes is controlled automatically, to place them at appropriate
5127 positions.
5030 5128 */
5031 5129 void QCPAxis::setOffset(int offset)
5032 5130 {
5033 mOffset = offset;
5131 mAxisPainter->offset = offset;
5034 5132 }
5035 5133
5036 5134 /*!
@@ -5043,7 +5141,6 void QCPAxis::setSelectedTickLabelFont(c
5043 5141 if (font != mSelectedTickLabelFont)
5044 5142 {
5045 5143 mSelectedTickLabelFont = font;
5046 mLabelCache.clear();
5047 5144 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5048 5145 }
5049 5146 }
@@ -5069,7 +5166,6 void QCPAxis::setSelectedTickLabelColor(
5069 5166 if (color != mSelectedTickLabelColor)
5070 5167 {
5071 5168 mSelectedTickLabelColor = color;
5072 mLabelCache.clear();
5073 5169 }
5074 5170 }
5075 5171
@@ -5125,7 +5221,7 void QCPAxis::setSelectedSubTickPen(cons
5125 5221 */
5126 5222 void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
5127 5223 {
5128 mLowerEnding = ending;
5224 mAxisPainter->lowerEnding = ending;
5129 5225 }
5130 5226
5131 5227 /*!
@@ -5140,7 +5236,7 void QCPAxis::setLowerEnding(const QCPLi
5140 5236 */
5141 5237 void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
5142 5238 {
5143 mUpperEnding = ending;
5239 mAxisPainter->upperEnding = ending;
5144 5240 }
5145 5241
5146 5242 /*!
@@ -5247,15 +5343,15 void QCPAxis::rescale(bool onlyVisiblePl
5247 5343 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5248 5344 continue;
5249 5345 QCPRange plottableRange;
5250 bool validRange;
5346 bool currentFoundRange;
5251 5347 QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5252 5348 if (mScaleType == stLogarithmic)
5253 5349 signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5254 5350 if (p.at(i)->keyAxis() == this)
5255 plottableRange = p.at(i)->getKeyRange(validRange, signDomain);
5351 plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5256 5352 else
5257 plottableRange = p.at(i)->getValueRange(validRange, signDomain);
5258 if (validRange)
5353 plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5354 if (currentFoundRange)
5259 5355 {
5260 5356 if (!haveRange)
5261 5357 newRange = plottableRange;
@@ -5357,7 +5453,7 double QCPAxis::coordToPixel(double valu
5357 5453 else
5358 5454 return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5359 5455 } else // mScaleType == stLogarithmic
5360 {
5456 {
5361 5457 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5362 5458 return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5363 5459 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
@@ -5387,11 +5483,11 QCPAxis::SelectablePart QCPAxis::getPart
5387 5483 if (!mVisible)
5388 5484 return spNone;
5389 5485
5390 if (mAxisSelectionBox.contains(pos.toPoint()))
5486 if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5391 5487 return spAxis;
5392 else if (mTickLabelsSelectionBox.contains(pos.toPoint()))
5488 else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5393 5489 return spTickLabels;
5394 else if (mLabelSelectionBox.contains(pos.toPoint()))
5490 else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5395 5491 return spAxisLabel;
5396 5492 else
5397 5493 return spNone;
@@ -5492,6 +5588,21 QCPAxis::AxisType QCPAxis::marginSideToA
5492 5588 return atLeft;
5493 5589 }
5494 5590
5591 /*!
5592 Returns the axis type that describes the opposite axis of an axis with the specified \a type.
5593 */
5594 QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
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
5495 5606 /*! \internal
5496 5607
5497 5608 This function is called to prepare the tick vector, sub tick vector and tick label vector. If
@@ -5552,8 +5663,6 void QCPAxis::setupTickVectors()
5552 5663 }
5553 5664
5554 5665 // generate tick labels according to tick positions:
5555 mExponentialChar = mParentPlot->locale().exponential(); // will be needed when drawing the numbers generated here, in getTickLabelData()
5556 mPositiveSignChar = mParentPlot->locale().positiveSign(); // will be needed when drawing the numbers generated here, in getTickLabelData()
5557 5666 if (mAutoTickLabels)
5558 5667 {
5559 5668 int vecsize = mTickVector.size();
@@ -5726,515 +5835,6 int QCPAxis::calculateAutoSubTickCount(d
5726 5835 return result;
5727 5836 }
5728 5837
5729 /*! \internal
5730
5731 Draws the axis with the specified \a painter.
5732
5733 The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
5734 here, too.
5735 */
5736 void QCPAxis::draw(QCPPainter *painter)
5737 {
5738 if (!mParentPlot) return;
5739 QPoint origin;
5740 if (mAxisType == atLeft)
5741 origin = mAxisRect->bottomLeft()+QPoint(-mOffset, 0);
5742 else if (mAxisType == atRight)
5743 origin = mAxisRect->bottomRight()+QPoint(+mOffset, 0);
5744 else if (mAxisType == atTop)
5745 origin = mAxisRect->topLeft()+QPoint(0, -mOffset);
5746 else if (mAxisType == atBottom)
5747 origin = mAxisRect->bottomLeft()+QPoint(0, +mOffset);
5748
5749 double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
5750 switch (mAxisType)
5751 {
5752 case atTop: yCor = -1; break;
5753 case atRight: xCor = 1; break;
5754 default: break;
5755 }
5756
5757 int margin = 0;
5758 int lowTick = mLowestVisibleTick;
5759 int highTick = mHighestVisibleTick;
5760 double t; // helper variable, result of coordinate-to-pixel transforms
5761
5762 // draw baseline:
5763 QLineF baseLine;
5764 painter->setPen(getBasePen());
5765 if (orientation() == Qt::Horizontal)
5766 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(mAxisRect->width()+xCor, yCor));
5767 else
5768 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -mAxisRect->height()+yCor));
5769 if (mRangeReversed)
5770 baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
5771 painter->drawLine(baseLine);
5772
5773 // draw ticks:
5774 if (mTicks)
5775 {
5776 painter->setPen(getTickPen());
5777 // direction of ticks ("inward" is right for left axis and left for right axis)
5778 int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
5779 if (orientation() == Qt::Horizontal)
5780 {
5781 for (int i=lowTick; i <= highTick; ++i)
5782 {
5783 t = coordToPixel(mTickVector.at(i)); // x
5784 painter->drawLine(QLineF(t+xCor, origin.y()-mTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mTickLengthIn*tickDir+yCor));
5785 }
5786 } else
5787 {
5788 for (int i=lowTick; i <= highTick; ++i)
5789 {
5790 t = coordToPixel(mTickVector.at(i)); // y
5791 painter->drawLine(QLineF(origin.x()-mTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mTickLengthIn*tickDir+xCor, t+yCor));
5792 }
5793 }
5794 }
5795
5796 // draw subticks:
5797 if (mTicks && mSubTickCount > 0)
5798 {
5799 painter->setPen(getSubTickPen());
5800 // direction of ticks ("inward" is right for left axis and left for right axis)
5801 int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
5802 if (orientation() == Qt::Horizontal)
5803 {
5804 for (int i=0; i<mSubTickVector.size(); ++i) // no need to check bounds because subticks are always only created inside current mRange
5805 {
5806 t = coordToPixel(mSubTickVector.at(i));
5807 painter->drawLine(QLineF(t+xCor, origin.y()-mSubTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mSubTickLengthIn*tickDir+yCor));
5808 }
5809 } else
5810 {
5811 for (int i=0; i<mSubTickVector.size(); ++i)
5812 {
5813 t = coordToPixel(mSubTickVector.at(i));
5814 painter->drawLine(QLineF(origin.x()-mSubTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mSubTickLengthIn*tickDir+xCor, t+yCor));
5815 }
5816 }
5817 }
5818 margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
5819
5820 // draw axis base endings:
5821 bool antialiasingBackup = painter->antialiasing();
5822 painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
5823 painter->setBrush(QBrush(basePen().color()));
5824 QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
5825 if (mLowerEnding.style() != QCPLineEnding::esNone)
5826 mLowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*mLowerEnding.realLength()*(mLowerEnding.inverted()?-1:1), -baseLineVector);
5827 if (mUpperEnding.style() != QCPLineEnding::esNone)
5828 mUpperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*mUpperEnding.realLength()*(mUpperEnding.inverted()?-1:1), baseLineVector);
5829 painter->setAntialiasing(antialiasingBackup);
5830
5831 // tick labels:
5832 QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
5833 if (mTickLabels)
5834 {
5835 margin += mTickLabelPadding;
5836 painter->setFont(getTickLabelFont());
5837 painter->setPen(QPen(getTickLabelColor()));
5838 for (int i=lowTick; i <= highTick; ++i)
5839 {
5840 t = coordToPixel(mTickVector.at(i));
5841 placeTickLabel(painter, t, margin, mTickVectorLabels.at(i), &tickLabelsSize);
5842 }
5843 }
5844 if (orientation() == Qt::Horizontal)
5845 margin += tickLabelsSize.height();
5846 else
5847 margin += tickLabelsSize.width();
5848
5849 // axis label:
5850 QRect labelBounds;
5851 if (!mLabel.isEmpty())
5852 {
5853 margin += mLabelPadding;
5854 painter->setFont(getLabelFont());
5855 painter->setPen(QPen(getLabelColor()));
5856 labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, mLabel);
5857 if (mAxisType == atLeft)
5858 {
5859 QTransform oldTransform = painter->transform();
5860 painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
5861 painter->rotate(-90);
5862 painter->drawText(0, 0, mAxisRect->height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
5863 painter->setTransform(oldTransform);
5864 }
5865 else if (mAxisType == atRight)
5866 {
5867 QTransform oldTransform = painter->transform();
5868 painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-mAxisRect->height());
5869 painter->rotate(90);
5870 painter->drawText(0, 0, mAxisRect->height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
5871 painter->setTransform(oldTransform);
5872 }
5873 else if (mAxisType == atTop)
5874 painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), mAxisRect->width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
5875 else if (mAxisType == atBottom)
5876 painter->drawText(origin.x(), origin.y()+margin, mAxisRect->width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
5877 }
5878
5879 // set selection boxes:
5880 int selAxisOutSize = qMax(qMax(mTickLengthOut, mSubTickLengthOut), mParentPlot->selectionTolerance());
5881 int selAxisInSize = mParentPlot->selectionTolerance();
5882 int selTickLabelSize = (orientation()==Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
5883 int selTickLabelOffset = qMax(mTickLengthOut, mSubTickLengthOut)+mTickLabelPadding;
5884 int selLabelSize = labelBounds.height();
5885 int selLabelOffset = selTickLabelOffset+selTickLabelSize+mLabelPadding;
5886 if (mAxisType == atLeft)
5887 {
5888 mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, mAxisRect->top(), origin.x()+selAxisInSize, mAxisRect->bottom());
5889 mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, mAxisRect->top(), origin.x()-selTickLabelOffset, mAxisRect->bottom());
5890 mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, mAxisRect->top(), origin.x()-selLabelOffset, mAxisRect->bottom());
5891 } else if (mAxisType == atRight)
5892 {
5893 mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, mAxisRect->top(), origin.x()+selAxisOutSize, mAxisRect->bottom());
5894 mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, mAxisRect->top(), origin.x()+selTickLabelOffset, mAxisRect->bottom());
5895 mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, mAxisRect->top(), origin.x()+selLabelOffset, mAxisRect->bottom());
5896 } else if (mAxisType == atTop)
5897 {
5898 mAxisSelectionBox.setCoords(mAxisRect->left(), origin.y()-selAxisOutSize, mAxisRect->right(), origin.y()+selAxisInSize);
5899 mTickLabelsSelectionBox.setCoords(mAxisRect->left(), origin.y()-selTickLabelOffset-selTickLabelSize, mAxisRect->right(), origin.y()-selTickLabelOffset);
5900 mLabelSelectionBox.setCoords(mAxisRect->left(), origin.y()-selLabelOffset-selLabelSize, mAxisRect->right(), origin.y()-selLabelOffset);
5901 } else if (mAxisType == atBottom)
5902 {
5903 mAxisSelectionBox.setCoords(mAxisRect->left(), origin.y()-selAxisInSize, mAxisRect->right(), origin.y()+selAxisOutSize);
5904 mTickLabelsSelectionBox.setCoords(mAxisRect->left(), origin.y()+selTickLabelOffset+selTickLabelSize, mAxisRect->right(), origin.y()+selTickLabelOffset);
5905 mLabelSelectionBox.setCoords(mAxisRect->left(), origin.y()+selLabelOffset+selLabelSize, mAxisRect->right(), origin.y()+selLabelOffset);
5906 }
5907 // draw hitboxes for debug purposes:
5908 //painter->setBrush(Qt::NoBrush);
5909 //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
5910 }
5911
5912 /*! \internal
5913
5914 Draws a single tick label with the provided \a painter, utilizing the internal label cache to
5915 significantly speed up drawing of labels that were drawn in previous calls. The tick label is
5916 always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
5917 pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
5918 for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
5919 at which the label should be drawn.
5920
5921 In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
5922 largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
5923 drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
5924 tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
5925 holds.
5926
5927 The label is drawn with the font and pen that are currently set on the \a painter. To draw
5928 superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
5929 getTickLabelData).
5930 */
5931 void QCPAxis::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
5932 {
5933 // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
5934 if (!mParentPlot) return;
5935 if (text.isEmpty()) return;
5936 QSize finalSize;
5937 QPointF labelAnchor;
5938 switch (mAxisType)
5939 {
5940 case atLeft: labelAnchor = QPointF(mAxisRect->left()-distanceToAxis-mOffset, position); break;
5941 case atRight: labelAnchor = QPointF(mAxisRect->right()+distanceToAxis+mOffset, position); break;
5942 case atTop: labelAnchor = QPointF(position, mAxisRect->top()-distanceToAxis-mOffset); break;
5943 case atBottom: labelAnchor = QPointF(position, mAxisRect->bottom()+distanceToAxis+mOffset); break;
5944 }
5945 if (parentPlot()->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
5946 {
5947 if (!mLabelCache.contains(text)) // no cached label exists, create it
5948 {
5949 CachedLabel *newCachedLabel = new CachedLabel;
5950 TickLabelData labelData = getTickLabelData(painter->font(), text);
5951 QPointF drawOffset = getTickLabelDrawOffset(labelData);
5952 newCachedLabel->offset = drawOffset+labelData.rotatedTotalBounds.topLeft();
5953 newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
5954 newCachedLabel->pixmap.fill(Qt::transparent);
5955 QCPPainter cachePainter(&newCachedLabel->pixmap);
5956 cachePainter.setPen(painter->pen());
5957 drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
5958 mLabelCache.insert(text, newCachedLabel, 1);
5959 }
5960 // draw cached label:
5961 const CachedLabel *cachedLabel = mLabelCache.object(text);
5962 // if label would be partly clipped by widget border on sides, don't draw it:
5963 if (orientation() == Qt::Horizontal)
5964 {
5965 if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > mParentPlot->viewport().right() ||
5966 labelAnchor.x()+cachedLabel->offset.x() < mParentPlot->viewport().left())
5967 return;
5968 } else
5969 {
5970 if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > mParentPlot->viewport().bottom() ||
5971 labelAnchor.y()+cachedLabel->offset.y() < mParentPlot->viewport().top())
5972 return;
5973 }
5974 painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
5975 finalSize = cachedLabel->pixmap.size();
5976 } else // label caching disabled, draw text directly on surface:
5977 {
5978 TickLabelData labelData = getTickLabelData(painter->font(), text);
5979 QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
5980 // if label would be partly clipped by widget border on sides, don't draw it:
5981 if (orientation() == Qt::Horizontal)
5982 {
5983 if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > mParentPlot->viewport().right() ||
5984 finalPosition.x()+labelData.rotatedTotalBounds.left() < mParentPlot->viewport().left())
5985 return;
5986 } else
5987 {
5988 if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > mParentPlot->viewport().bottom() ||
5989 finalPosition.y()+labelData.rotatedTotalBounds.top() < mParentPlot->viewport().top())
5990 return;
5991 }
5992 drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
5993 finalSize = labelData.rotatedTotalBounds.size();
5994 }
5995
5996 // expand passed tickLabelsSize if current tick label is larger:
5997 if (finalSize.width() > tickLabelsSize->width())
5998 tickLabelsSize->setWidth(finalSize.width());
5999 if (finalSize.height() > tickLabelsSize->height())
6000 tickLabelsSize->setHeight(finalSize.height());
6001 }
6002
6003 /*! \internal
6004
6005 This is a \ref placeTickLabel helper function.
6006
6007 Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
6008 y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
6009 directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
6010 QCP::phCacheLabels plotting hint is not set.
6011 */
6012 void QCPAxis::drawTickLabel(QCPPainter *painter, double x, double y, const QCPAxis::TickLabelData &labelData) const
6013 {
6014 // backup painter settings that we're about to change:
6015 QTransform oldTransform = painter->transform();
6016 QFont oldFont = painter->font();
6017
6018 // transform painter to position/rotation:
6019 painter->translate(x, y);
6020 if (!qFuzzyIsNull(mTickLabelRotation))
6021 painter->rotate(mTickLabelRotation);
6022
6023 // draw text:
6024 if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6025 {
6026 painter->setFont(labelData.baseFont);
6027 painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6028 painter->setFont(labelData.expFont);
6029 painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6030 } else
6031 {
6032 painter->setFont(labelData.baseFont);
6033 painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6034 }
6035
6036 // reset painter settings to what it was before:
6037 painter->setTransform(oldTransform);
6038 painter->setFont(oldFont);
6039 }
6040
6041 /*! \internal
6042
6043 This is a \ref placeTickLabel helper function.
6044
6045 Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
6046 processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
6047 exponent if necessary (see \ref setNumberFormat) and calculates appropriate bounding boxes.
6048 */
6049 QCPAxis::TickLabelData QCPAxis::getTickLabelData(const QFont &font, const QString &text) const
6050 {
6051 TickLabelData result;
6052
6053 // determine whether beautiful decimal powers should be used
6054 bool useBeautifulPowers = false;
6055 int ePos = -1;
6056 if (mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber)
6057 {
6058 ePos = text.indexOf('e');
6059 if (ePos > -1)
6060 useBeautifulPowers = true;
6061 }
6062
6063 // calculate text bounding rects and do string preparation for beautiful decimal powers:
6064 result.baseFont = font;
6065 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
6066 if (useBeautifulPowers)
6067 {
6068 // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6069 result.basePart = text.left(ePos);
6070 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6071 if (mScaleType == stLogarithmic && result.basePart == "1")
6072 result.basePart = "10";
6073 else
6074 result.basePart += (mNumberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
6075 result.expPart = text.mid(ePos+1);
6076 // clip "+" and leading zeros off expPart:
6077 while (result.expPart.at(1) == '0' && result.expPart.length() > 2) // length > 2 so we leave one zero when numberFormatChar is 'e'
6078 result.expPart.remove(1, 1);
6079 if (result.expPart.at(0) == mPositiveSignChar)
6080 result.expPart.remove(0, 1);
6081 // prepare smaller font for exponent:
6082 result.expFont = font;
6083 result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6084 // calculate bounding rects of base part, exponent part and total one:
6085 result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6086 result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6087 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
6088 } else // useBeautifulPowers == false
6089 {
6090 result.basePart = text;
6091 result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6092 }
6093 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
6094
6095 // calculate possibly different bounding rect after rotation:
6096 result.rotatedTotalBounds = result.totalBounds;
6097 if (!qFuzzyIsNull(mTickLabelRotation))
6098 {
6099 QTransform transform;
6100 transform.rotate(mTickLabelRotation);
6101 result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6102 }
6103
6104 return result;
6105 }
6106
6107 /*! \internal
6108
6109 This is a \ref placeTickLabel helper function.
6110
6111 Calculates the offset at which the top left corner of the specified tick label shall be drawn.
6112 The offset is relative to a point right next to the tick the label belongs to.
6113
6114 This function is thus responsible for e.g. centering tick labels under ticks and positioning them
6115 appropriately when they are rotated.
6116 */
6117 QPointF QCPAxis::getTickLabelDrawOffset(const QCPAxis::TickLabelData &labelData) const
6118 {
6119 /*
6120 calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6121 explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6122 horizontally under the corresponding tick is always on the label side that is closer to the
6123 axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6124 is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6125 will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6126 time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6127 labels.
6128 */
6129 bool doRotation = !qFuzzyIsNull(mTickLabelRotation);
6130 bool flip = qFuzzyCompare(qAbs(mTickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6131 double radians = mTickLabelRotation/180.0*M_PI;
6132 int x=0, y=0;
6133 if (mAxisType == atLeft)
6134 {
6135 if (doRotation)
6136 {
6137 if (mTickLabelRotation > 0)
6138 {
6139 x = -qCos(radians)*labelData.totalBounds.width();
6140 y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6141 } else
6142 {
6143 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6144 y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6145 }
6146 } else
6147 {
6148 x = -labelData.totalBounds.width();
6149 y = -labelData.totalBounds.height()/2.0;
6150 }
6151 } else if (mAxisType == atRight)
6152 {
6153 if (doRotation)
6154 {
6155 if (mTickLabelRotation > 0)
6156 {
6157 x = +qSin(radians)*labelData.totalBounds.height();
6158 y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6159 } else
6160 {
6161 x = 0;
6162 y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6163 }
6164 } else
6165 {
6166 x = 0;
6167 y = -labelData.totalBounds.height()/2.0;
6168 }
6169 } else if (mAxisType == atTop)
6170 {
6171 if (doRotation)
6172 {
6173 if (mTickLabelRotation > 0)
6174 {
6175 x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6176 y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6177 } else
6178 {
6179 x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6180 y = -qCos(-radians)*labelData.totalBounds.height();
6181 }
6182 } else
6183 {
6184 x = -labelData.totalBounds.width()/2.0;
6185 y = -labelData.totalBounds.height();
6186 }
6187 } else if (mAxisType == atBottom)
6188 {
6189 if (doRotation)
6190 {
6191 if (mTickLabelRotation > 0)
6192 {
6193 x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6194 y = 0;
6195 } else
6196 {
6197 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6198 y = +qSin(-radians)*labelData.totalBounds.width();
6199 }
6200 } else
6201 {
6202 x = -labelData.totalBounds.width()/2.0;
6203 y = 0;
6204 }
6205 }
6206
6207 return QPointF(x, y);
6208 }
6209
6210 /*! \internal
6211
6212 Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
6213 to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
6214 margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
6215 smaller width/height.
6216 */
6217 void QCPAxis::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6218 {
6219 // note: this function must return the same tick label sizes as the placeTickLabel function.
6220 QSize finalSize;
6221 if (parentPlot()->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6222 {
6223 const CachedLabel *cachedLabel = mLabelCache.object(text);
6224 finalSize = cachedLabel->pixmap.size();
6225 } else // label caching disabled or no label with this text cached:
6226 {
6227 TickLabelData labelData = getTickLabelData(font, text);
6228 finalSize = labelData.rotatedTotalBounds.size();
6229 }
6230
6231 // expand passed tickLabelsSize if current tick label is larger:
6232 if (finalSize.width() > tickLabelsSize->width())
6233 tickLabelsSize->setWidth(finalSize.width());
6234 if (finalSize.height() > tickLabelsSize->height())
6235 tickLabelsSize->setHeight(finalSize.height());
6236 }
6237
6238 5838 /* inherits documentation from base class */
6239 5839 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
6240 5840 {
@@ -6278,6 +5878,60 void QCPAxis::applyDefaultAntialiasingHi
6278 5878
6279 5879 /*! \internal
6280 5880
5881 Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
5882
5883 */
5884 void QCPAxis::draw(QCPPainter *painter)
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
5913 mAxisPainter->type = mAxisType;
5914 mAxisPainter->basePen = getBasePen();
5915 mAxisPainter->labelFont = getLabelFont();
5916 mAxisPainter->labelColor = getLabelColor();
5917 mAxisPainter->label = mLabel;
5918 mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers;
5919 mAxisPainter->tickPen = getTickPen();
5920 mAxisPainter->subTickPen = getSubTickPen();
5921 mAxisPainter->tickLabelFont = getTickLabelFont();
5922 mAxisPainter->tickLabelColor = getTickLabelColor();
5923 mAxisPainter->alignmentRect = mAxisRect->rect();
5924 mAxisPainter->viewportRect = mParentPlot->viewport();
5925 mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5926 mAxisPainter->reversedEndings = mRangeReversed;
5927 mAxisPainter->tickPositions = tickPositions;
5928 mAxisPainter->tickLabels = tickLabels;
5929 mAxisPainter->subTickPositions = subTickPositions;
5930 mAxisPainter->draw(painter);
5931 }
5932
5933 /*! \internal
5934
6281 5935 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
6282 5936 the current range. The return values are indices of the tick vector, not the positions of the
6283 5937 ticks themselves.
@@ -6435,37 +6089,41 QColor QCPAxis::getLabelColor() const
6435 6089 */
6436 6090 int QCPAxis::calculateMargin()
6437 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
6438 6095 if (mCachedMarginValid)
6439 6096 return mCachedMargin;
6440 6097
6441 6098 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6442 6099 int margin = 0;
6443 6100
6444 if (mVisible)
6445 {
6446 int lowTick, highTick;
6447 visibleTickBounds(lowTick, highTick);
6448 // get length of tick marks pointing outwards:
6449 if (mTicks)
6450 margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
6451 // calculate size of tick labels:
6452 QSize tickLabelsSize(0, 0);
6453 if (mTickLabels)
6454 {
6455 for (int i=lowTick; i<=highTick; ++i)
6456 getMaxTickLabelSize(mTickLabelFont, mTickVectorLabels.at(i), &tickLabelsSize); // don't use getTickLabelFont() because we don't want margin to possibly change on selection
6457 margin += orientation() == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6458 margin += mTickLabelPadding;
6459 }
6460 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6461 if (!mLabel.isEmpty())
6462 {
6463 QFontMetrics fontMetrics(mLabelFont); // don't use getLabelFont() because we don't want margin to possibly change on selection
6464 QRect bounds;
6465 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, mLabel);
6466 margin += bounds.height() + mLabelPadding;
6467 }
6468 }
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
6118 mAxisPainter->type = mAxisType;
6119 mAxisPainter->labelFont = getLabelFont();
6120 mAxisPainter->label = mLabel;
6121 mAxisPainter->tickLabelFont = mTickLabelFont;
6122 mAxisPainter->alignmentRect = mAxisRect->rect();
6123 mAxisPainter->viewportRect = mParentPlot->viewport();
6124 mAxisPainter->tickPositions = tickPositions;
6125 mAxisPainter->tickLabels = tickLabels;
6126 margin += mAxisPainter->size();
6469 6127 margin += mPadding;
6470 6128
6471 6129 mCachedMargin = margin;
@@ -6481,6 +6139,619 QCP::Interaction QCPAxis::selectionCateg
6481 6139
6482 6140
6483 6141 ////////////////////////////////////////////////////////////////////////////////////////////////////
6142 //////////////////// QCPAxisPainterPrivate
6143 ////////////////////////////////////////////////////////////////////////////////////////////////////
6144
6145 /*! \class QCPAxisPainterPrivate
6146
6147 \internal
6148 \brief (Private)
6149
6150 This is a private class and not part of the public QCustomPlot interface.
6151
6152 It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
6153 axis label. It also buffers the labels to reduce replot times. The parameters are configured by
6154 directly accessing the public member variables.
6155 */
6156
6157 /*!
6158 Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
6159 redraw, to utilize the caching mechanisms.
6160 */
6161 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
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
6185 QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
6186 {
6187 }
6188
6189 /*! \internal
6190
6191 Draws the axis with the specified \a painter.
6192
6193 The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
6194 here, too.
6195 */
6196 void QCPAxisPainterPrivate::draw(QCPPainter *painter)
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());
6273 if (lowerEnding.style() != QCPLineEnding::esNone)
6274 lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6275 if (upperEnding.style() != QCPLineEnding::esNone)
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
6363 /*! \internal
6364
6365 Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
6366 direction) needed to fit the axis.
6367 */
6368 int QCPAxisPainterPrivate::size() const
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
6398 /*! \internal
6399
6400 Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
6401 method is called automatically in \ref draw, if any parameters have changed that invalidate the
6402 cached labels, such as font, color, etc.
6403 */
6404 void QCPAxisPainterPrivate::clearCache()
6405 {
6406 mLabelCache.clear();
6407 }
6408
6409 /*! \internal
6410
6411 Returns a hash that allows uniquely identifying whether the label parameters have changed such
6412 that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
6413 return value of this method hasn't changed since the last redraw, the respective label parameters
6414 haven't changed and cached labels may be used.
6415 */
6416 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
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
6427 /*! \internal
6428
6429 Draws a single tick label with the provided \a painter, utilizing the internal label cache to
6430 significantly speed up drawing of labels that were drawn in previous calls. The tick label is
6431 always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
6432 pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
6433 for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
6434 at which the label should be drawn.
6435
6436 In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
6437 largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
6438 drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
6439 tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
6440 holds.
6441
6442 The label is drawn with the font and pen that are currently set on the \a painter. To draw
6443 superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
6444 getTickLabelData).
6445 */
6446 void 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
6517 /*! \internal
6518
6519 This is a \ref placeTickLabel helper function.
6520
6521 Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
6522 y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
6523 directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
6524 QCP::phCacheLabels plotting hint is not set.
6525 */
6526 void 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
6555 /*! \internal
6556
6557 This is a \ref placeTickLabel helper function.
6558
6559 Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
6560 processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
6561 exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
6562 */
6563 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
6564 {
6565 TickLabelData result;
6566
6567 // determine whether beautiful decimal powers should be used
6568 bool useBeautifulPowers = false;
6569 int ePos = -1;
6570 if (substituteExponent)
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
6622 /*! \internal
6623
6624 This is a \ref placeTickLabel helper function.
6625
6626 Calculates the offset at which the top left corner of the specified tick label shall be drawn.
6627 The offset is relative to a point right next to the tick the label belongs to.
6628
6629 This function is thus responsible for e.g. centering tick labels under ticks and positioning them
6630 appropriately when they are rotated.
6631 */
6632 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
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
6725 /*! \internal
6726
6727 Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
6728 to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
6729 margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
6730 smaller width/height.
6731 */
6732 void 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
6754 ////////////////////////////////////////////////////////////////////////////////////////////////////
6484 6755 //////////////////// QCPAbstractPlottable
6485 6756 ////////////////////////////////////////////////////////////////////////////////////////////////////
6486 6757
@@ -6563,30 +6834,36 QCP::Interaction QCPAxis::selectionCateg
6563 6834 of this plottable inside \a rect, next to the plottable name.
6564 6835 */
6565 6836
6566 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &validRange, SignDomain inSignDomain) const = 0
6837 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
6567 6838 \internal
6568 6839
6569 6840 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6570 6841 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6571 6842 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6572 6843 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6573 restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
6574 parameter that indicates whether a proper range could be found or not. If this is false, you
6575 shouldn't use the returned range (e.g. no points in data).
6844 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6845 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6846 use the returned range (e.g. no points in data).
6847
6848 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6849 this function may have size zero, which wouldn't count as a valid range.
6576 6850
6577 6851 \see rescaleAxes, getValueRange
6578 6852 */
6579 6853
6580 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &validRange, SignDomain inSignDomain) const = 0
6854 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
6581 6855 \internal
6582 6856
6583 6857 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6584 6858 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6585 6859 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6586 6860 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6587 restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
6588 parameter that indicates whether a proper range could be found or not. If this is false, you
6589 shouldn't use the returned range (e.g. no points in data).
6861 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6862 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6863 use the returned range (e.g. no points in data).
6864
6865 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6866 this function may have size zero, which wouldn't count as a valid range.
6590 6867
6591 6868 \see rescaleAxes, getKeyRange
6592 6869 */
@@ -6596,8 +6873,15 QCP::Interaction QCPAxis::selectionCateg
6596 6873
6597 6874 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
6598 6875
6599 This signal is emitted when the selection state of this plottable has changed to \a selected,
6600 either by user interaction or by a direct call to \ref setSelected.
6876 This signal is emitted when the selection state of this plottable has changed, either by user
6877 interaction or by a direct call to \ref setSelected.
6878 */
6879
6880 /*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
6881
6882 This signal is emitted when the selectability of this plottable has changed.
6883
6884 \see setSelectable
6601 6885 */
6602 6886
6603 6887 /* end of documentation of signals */
@@ -6770,7 +7054,11 void QCPAbstractPlottable::setValueAxis(
6770 7054 */
6771 7055 void QCPAbstractPlottable::setSelectable(bool selectable)
6772 7056 {
6773 mSelectable = selectable;
7057 if (mSelectable != selectable)
7058 {
7059 mSelectable = selectable;
7060 emit selectableChanged(mSelectable);
7061 }
6774 7062 }
6775 7063
6776 7064 /*!
@@ -6829,9 +7117,9 void QCPAbstractPlottable::rescaleKeyAxi
6829 7117 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
6830 7118 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
6831 7119
6832 bool rangeValid;
6833 QCPRange newRange = getKeyRange(rangeValid, signDomain);
6834 if (rangeValid)
7120 bool foundRange;
7121 QCPRange newRange = getKeyRange(foundRange, signDomain);
7122 if (foundRange)
6835 7123 {
6836 7124 if (onlyEnlarge)
6837 7125 newRange.expand(keyAxis->range());
@@ -6869,9 +7157,9 void QCPAbstractPlottable::rescaleValueA
6869 7157 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
6870 7158 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
6871 7159
6872 bool rangeValid;
6873 QCPRange newRange = getValueRange(rangeValid, signDomain);
6874 if (rangeValid)
7160 bool foundRange;
7161 QCPRange newRange = getValueRange(foundRange, signDomain);
7162 if (foundRange)
6875 7163 {
6876 7164 if (onlyEnlarge)
6877 7165 newRange.expand(valueAxis->range());
@@ -6917,7 +7205,7 bool QCPAbstractPlottable::addToLegend()
6917 7205 return false;
6918 7206 }
6919 7207
6920 /*!
7208 /*!
6921 7209 Removes the plottable from the legend of the parent QCustomPlot. This means the
6922 7210 QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
6923 7211 is removed.
@@ -6980,7 +7268,7 void QCPAbstractPlottable::coordsToPixel
6980 7268 }
6981 7269 }
6982 7270
6983 /*! \internal
7271 /*! \internal
6984 7272 \overload
6985 7273
6986 7274 Returns the input as pixel coordinates in a QPointF.
@@ -7976,7 +8264,11 void QCPAbstractItem::setClipAxisRect(QC
7976 8264 */
7977 8265 void QCPAbstractItem::setSelectable(bool selectable)
7978 8266 {
7979 mSelectable = selectable;
8267 if (mSelectable != selectable)
8268 {
8269 mSelectable = selectable;
8270 emit selectableChanged(mSelectable);
8271 }
7980 8272 }
7981 8273
7982 8274 /*!
@@ -8010,7 +8302,7 void QCPAbstractItem::setSelected(bool s
8010 8302 positions direcly by their member pointers (which typically have the same variable name as \a
8011 8303 name).
8012 8304
8013 \see positions, anchor
8305 \see positions, anchor
8014 8306 */
8015 8307 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8016 8308 {
@@ -8031,7 +8323,7 QCPItemPosition *QCPAbstractItem::positi
8031 8323 anchors direcly by their member pointers (which typically have the same variable name as \a
8032 8324 name).
8033 8325
8034 \see anchors, position
8326 \see anchors, position
8035 8327 */
8036 8328 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8037 8329 {
@@ -8050,7 +8342,7 QCPItemAnchor *QCPAbstractItem::anchor(c
8050 8342 Note that you can check for positions with this function, too. This is because every position is
8051 8343 also an anchor (QCPItemPosition inherits from QCPItemAnchor).
8052 8344
8053 \see anchor, position
8345 \see anchor, position
8054 8346 */
8055 8347 bool QCPAbstractItem::hasAnchor(const QString &name) const
8056 8348 {
@@ -8283,7 +8575,7 QCP::Interaction QCPAbstractItem::select
8283 8575
8284 8576
8285 8577
8286 /*! \mainpage %QCustomPlot 1.1.1 Documentation
8578 /*! \mainpage %QCustomPlot 1.2.0 Documentation
8287 8579
8288 8580 \image html qcp-doc-logo.png
8289 8581
@@ -8329,7 +8621,8 QCP::Interaction QCPAbstractItem::select
8329 8621 usual, if the cast returns zero, the plottable wasn't of that specific subclass.)
8330 8622
8331 8623 All further interfacing with plottables (e.g how to set data) is specific to the plottable type.
8332 See the documentations of the subclasses: QCPGraph, QCPCurve, QCPBars, QCPStatisticalBox.
8624 See the documentations of the subclasses: QCPGraph, QCPCurve, QCPBars, QCPStatisticalBox,
8625 QCPColorMap.
8333 8626
8334 8627 \section mainpage-axes Controlling the Axes
8335 8628
@@ -8356,20 +8649,20 QCP::Interaction QCPAbstractItem::select
8356 8649
8357 8650 \section mainpage-legend Plot Legend
8358 8651
8359 Every QCustomPlot owns one QCPLegend (as \a legend) by default. A legend is a small layout
8360 element inside the plot which lists the plottables with an icon of the plottable line/symbol and
8361 a description. The Description is retrieved from the plottable name
8362 (QCPAbstractPlottable::setName). Plottables can be added and removed from the legend via \ref
8363 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend. By default,
8364 adding a plottable to QCustomPlot automatically adds it to the legend, too. This behaviour can be
8365 modified with the QCustomPlot::setAutoAddPlottableToLegend property.
8652 Every QCustomPlot has one QCPLegend (as \ref QCustomPlot::legend) by default. A legend is a small
8653 layout element inside the plot which lists the plottables with an icon of the plottable
8654 line/symbol and a name (QCPAbstractPlottable::setName). Plottables can be added and removed from
8655 the main legend via \ref QCPAbstractPlottable::addToLegend and \ref
8656 QCPAbstractPlottable::removeFromLegend. By default, adding a plottable to QCustomPlot
8657 automatically adds it to the legend, too. This behaviour can be modified with the
8658 QCustomPlot::setAutoAddPlottableToLegend property.
8366 8659
8367 8660 The QCPLegend provides an interface to access, add and remove legend items directly, too. See
8368 8661 QCPLegend::item, QCPLegend::itemWithPlottable, QCPLegend::addItem, QCPLegend::removeItem for
8369 8662 example.
8370 8663
8371 Multiple legends are supported via the layout system (as a QCPLegend simply is a normal layout
8372 element).
8664 Multiple legends are supported via the \link thelayoutsystem layout system\endlink (as a
8665 QCPLegend simply is a normal layout element).
8373 8666
8374 8667 \section mainpage-userinteraction User Interactions
8375 8668
@@ -8820,6 +9113,7 QCustomPlot::QCustomPlot(QWidget *parent
8820 9113 mPlotLayout = new QCPLayoutGrid;
8821 9114 mPlotLayout->initializeParentPlot(this);
8822 9115 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9116 mPlotLayout->setLayer("main");
8823 9117 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
8824 9118 mPlotLayout->addElement(0, 0, defaultAxisRect);
8825 9119 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
@@ -9354,10 +9648,10 int QCustomPlot::plottableCount() const
9354 9648 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9355 9649 {
9356 9650 QList<QCPAbstractPlottable*> result;
9357 for (int i=0; i<mPlottables.size(); ++i)
9358 {
9359 if (mPlottables.at(i)->selected())
9360 result.append(mPlottables.at(i));
9651 foreach (QCPAbstractPlottable *plottable, mPlottables)
9652 {
9653 if (plottable->selected())
9654 result.append(plottable);
9361 9655 }
9362 9656 return result;
9363 9657 }
@@ -9379,17 +9673,16 QCPAbstractPlottable *QCustomPlot::plott
9379 9673 QCPAbstractPlottable *resultPlottable = 0;
9380 9674 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9381 9675
9382 for (int i=0; i<mPlottables.size(); ++i)
9383 {
9384 QCPAbstractPlottable *currentPlottable = mPlottables.at(i);
9385 if (onlySelectable && !currentPlottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
9676 foreach (QCPAbstractPlottable *plottable, mPlottables)
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
9386 9679 continue;
9387 if ((currentPlottable->keyAxis()->axisRect()->rect() & currentPlottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
9388 {
9389 double currentDistance = currentPlottable->selectTest(pos, false);
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);
9390 9683 if (currentDistance >= 0 && currentDistance < resultDistance)
9391 9684 {
9392 resultPlottable = currentPlottable;
9685 resultPlottable = plottable;
9393 9686 resultDistance = currentDistance;
9394 9687 }
9395 9688 }
@@ -9545,10 +9838,10 int QCustomPlot::graphCount() const
9545 9838 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9546 9839 {
9547 9840 QList<QCPGraph*> result;
9548 for (int i=0; i<mGraphs.size(); ++i)
9549 {
9550 if (mGraphs.at(i)->selected())
9551 result.append(mGraphs.at(i));
9841 foreach (QCPGraph *graph, mGraphs)
9842 {
9843 if (graph->selected())
9844 result.append(graph);
9552 9845 }
9553 9846 return result;
9554 9847 }
@@ -9679,10 +9972,10 int QCustomPlot::itemCount() const
9679 9972 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9680 9973 {
9681 9974 QList<QCPAbstractItem*> result;
9682 for (int i=0; i<mItems.size(); ++i)
9683 {
9684 if (mItems.at(i)->selected())
9685 result.append(mItems.at(i));
9975 foreach (QCPAbstractItem *item, mItems)
9976 {
9977 if (item->selected())
9978 result.append(item);
9686 9979 }
9687 9980 return result;
9688 9981 }
@@ -9705,17 +9998,16 QCPAbstractItem *QCustomPlot::itemAt(con
9705 9998 QCPAbstractItem *resultItem = 0;
9706 9999 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9707 10000
9708 for (int i=0; i<mItems.size(); ++i)
9709 {
9710 QCPAbstractItem *currentItem = mItems[i];
9711 if (onlySelectable && !currentItem->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
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
9712 10004 continue;
9713 if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9714 {
9715 double currentDistance = currentItem->selectTest(pos, false);
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);
9716 10008 if (currentDistance >= 0 && currentDistance < resultDistance)
9717 10009 {
9718 resultItem = currentItem;
10010 resultItem = item;
9719 10011 resultDistance = currentDistance;
9720 10012 }
9721 10013 }
@@ -9744,10 +10036,10 bool QCustomPlot::hasItem(QCPAbstractIte
9744 10036 */
9745 10037 QCPLayer *QCustomPlot::layer(const QString &name) const
9746 10038 {
9747 for (int i=0; i<mLayers.size(); ++i)
9748 {
9749 if (mLayers.at(i)->name() == name)
9750 return mLayers.at(i);
10039 foreach (QCPLayer *layer, mLayers)
10040 {
10041 if (layer->name() == name)
10042 return layer;
9751 10043 }
9752 10044 return 0;
9753 10045 }
@@ -9775,7 +10067,7 QCPLayer *QCustomPlot::layer(int index)
9775 10067 */
9776 10068 QCPLayer *QCustomPlot::currentLayer() const
9777 10069 {
9778 return mCurrentLayer;
10070 return mCurrentLayer;
9779 10071 }
9780 10072
9781 10073 /*!
@@ -9992,10 +10284,9 QList<QCPAxisRect*> QCustomPlot::axisRec
9992 10284
9993 10285 while (!elementStack.isEmpty())
9994 10286 {
9995 QList<QCPLayoutElement*> subElements = elementStack.pop()->elements(false);
9996 for (int i=0; i<subElements.size(); ++i)
9997 {
9998 if (QCPLayoutElement *element = subElements.at(i))
10287 foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10288 {
10289 if (element)
9999 10290 {
10000 10291 elementStack.push(element);
10001 10292 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
@@ -10018,23 +10309,22 QList<QCPAxisRect*> QCustomPlot::axisRec
10018 10309 */
10019 10310 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10020 10311 {
10021 QCPLayoutElement *current = mPlotLayout;
10312 QCPLayoutElement *currentElement = mPlotLayout;
10022 10313 bool searchSubElements = true;
10023 while (searchSubElements && current)
10314 while (searchSubElements && currentElement)
10024 10315 {
10025 10316 searchSubElements = false;
10026 const QList<QCPLayoutElement*> elements = current->elements(false);
10027 for (int i=0; i<elements.size(); ++i)
10028 {
10029 if (elements.at(i) && elements.at(i)->realVisibility() && elements.at(i)->selectTest(pos, false) >= 0)
10030 {
10031 current = elements.at(i);
10317 foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10318 {
10319 if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10320 {
10321 currentElement = subElement;
10032 10322 searchSubElements = true;
10033 10323 break;
10034 10324 }
10035 10325 }
10036 10326 }
10037 return current;
10327 return currentElement;
10038 10328 }
10039 10329
10040 10330 /*!
@@ -10047,14 +10337,13 QCPLayoutElement *QCustomPlot::layoutEle
10047 10337 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10048 10338 {
10049 10339 QList<QCPAxis*> result, allAxes;
10050 QList<QCPAxisRect*> rects = axisRects();
10051 for (int i=0; i<rects.size(); ++i)
10052 allAxes << rects.at(i)->axes();
10053
10054 for (int i=0; i<allAxes.size(); ++i)
10055 {
10056 if (allAxes.at(i)->selectedParts() != QCPAxis::spNone)
10057 result.append(allAxes.at(i));
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);
10058 10347 }
10059 10348
10060 10349 return result;
@@ -10077,13 +10366,12 QList<QCPLegend*> QCustomPlot::selectedL
10077 10366
10078 10367 while (!elementStack.isEmpty())
10079 10368 {
10080 QList<QCPLayoutElement*> subElements = elementStack.pop()->elements(false);
10081 for (int i=0; i<subElements.size(); ++i)
10082 {
10083 if (QCPLayoutElement *element = subElements.at(i))
10084 {
10085 elementStack.push(element);
10086 if (QCPLegend *leg = qobject_cast<QCPLegend*>(element))
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))
10087 10375 {
10088 10376 if (leg->selectedParts() != QCPLegend::spNone)
10089 10377 result.append(leg);
@@ -10106,11 +10394,10 QList<QCPLegend*> QCustomPlot::selectedL
10106 10394 */
10107 10395 void QCustomPlot::deselectAll()
10108 10396 {
10109 for (int i=0; i<mLayers.size(); ++i)
10110 {
10111 QList<QCPLayerable*> layerables = mLayers.at(i)->children();
10112 for (int k=0; k<layerables.size(); ++k)
10113 layerables.at(k)->deselectEvent(0);
10397 foreach (QCPLayer *layer, mLayers)
10398 {
10399 foreach (QCPLayerable *layerable, layer->children())
10400 layerable->deselectEvent(0);
10114 10401 }
10115 10402 }
10116 10403
@@ -10127,28 +10414,30 void QCustomPlot::deselectAll()
10127 10414 signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10128 10415 recursion.
10129 10416 */
10130 void QCustomPlot::replot()
10417 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
10131 10418 {
10132 10419 if (mReplotting) // incase signals loop back to replot slot
10133 10420 return;
10134 10421 mReplotting = true;
10135 10422 emit beforeReplot();
10423
10136 10424 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10137 10425 QCPPainter painter;
10138 10426 painter.begin(&mPaintBuffer);
10139 if (painter.isActive())
10427 if (painter.isActive())
10140 10428 {
10141 10429 painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10142 10430 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10143 10431 painter.fillRect(mViewport, mBackgroundBrush);
10144 10432 draw(&painter);
10145 10433 painter.end();
10146 if (mPlottingHints.testFlag(QCP::phForceRepaint))
10434 if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10147 10435 repaint();
10148 10436 else
10149 10437 update();
10150 10438 } else // might happen if QCustomPlot has width or height zero
10151 10439 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
10440
10152 10441 emit afterReplot();
10153 10442 mReplotting = false;
10154 10443 }
@@ -10163,15 +10452,12 void QCustomPlot::replot()
10163 10452 */
10164 10453 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10165 10454 {
10166 // get a list of all axes in the plot:
10167 QList<QCPAxis*> axes;
10168 QList<QCPAxisRect*> rects = axisRects();
10169 for (int i=0; i<rects.size(); ++i)
10170 axes << rects.at(i)->axes();
10171
10172 // call rescale on all axes:
10173 for (int i=0; i<axes.size(); ++i)
10174 axes.at(i)->rescale(onlyVisiblePlottables);
10455 QList<QCPAxis*> allAxes;
10456 foreach (QCPAxisRect *rect, axisRects())
10457 allAxes << rect->axes();
10458
10459 foreach (QCPAxis *axis, allAxes)
10460 axis->rescale(onlyVisiblePlottables);
10175 10461 }
10176 10462
10177 10463 /*!
@@ -10203,12 +10489,15 void QCustomPlot::rescaleAxes(bool onlyV
10203 10489 aren't defined yet inside the constructor, so you would get an image that has strange
10204 10490 widths/heights.
10205 10491
10492 \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
10493 PDF file.
10494
10206 10495 \note On Android systems, this method does nothing and issues an according qDebug warning
10207 10496 message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10208 10497
10209 10498 \see savePng, saveBmp, saveJpg, saveRastered
10210 10499 */
10211 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height)
10500 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10212 10501 {
10213 10502 bool success = false;
10214 10503 #ifdef QT_NO_PRINTER
@@ -10234,6 +10523,8 bool QCustomPlot::savePdf(const QString
10234 10523 printer.setOutputFormat(QPrinter::PdfFormat);
10235 10524 printer.setFullPage(true);
10236 10525 printer.setColorMode(QPrinter::Color);
10526 printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10527 printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10237 10528 QRect oldViewport = viewport();
10238 10529 setViewport(QRect(0, 0, newWidth, newHeight));
10239 10530 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
@@ -10296,7 +10587,7 bool QCustomPlot::savePdf(const QString
10296 10587 \see savePdf, saveBmp, saveJpg, saveRastered
10297 10588 */
10298 10589 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10299 {
10590 {
10300 10591 return saveRastered(fileName, width, height, scale, "PNG", quality);
10301 10592 }
10302 10593
@@ -10421,7 +10712,7 void QCustomPlot::resizeEvent(QResizeEve
10421 10712 // resize and repaint the buffer:
10422 10713 mPaintBuffer = QPixmap(event->size());
10423 10714 setViewport(rect());
10424 replot();
10715 replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10425 10716 }
10426 10717
10427 10718 /*! \internal
@@ -10536,6 +10827,22 void QCustomPlot::mouseReleaseEvent(QMou
10536 10827 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10537 10828 bool selectionStateChanged = false;
10538 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 }
10539 10846 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10540 10847 {
10541 10848 // a layerable was actually clicked, call its selectEvent:
@@ -10543,23 +10850,6 void QCustomPlot::mouseReleaseEvent(QMou
10543 10850 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10544 10851 selectionStateChanged |= selChanged;
10545 10852 }
10546 // deselect all other layerables if not additive selection:
10547 if (!additive)
10548 {
10549 for (int i=0; i<mLayers.size(); ++i)
10550 {
10551 QList<QCPLayerable*> layerables = mLayers.at(i)->children();
10552 for (int k=0; k<layerables.size(); ++k)
10553 {
10554 if (layerables.at(k) != clickedLayerable && mInteractions.testFlag(layerables.at(k)->selectionCategory()))
10555 {
10556 bool selChanged = false;
10557 layerables.at(k)->deselectEvent(&selChanged);
10558 selectionStateChanged |= selChanged;
10559 }
10560 }
10561 }
10562 }
10563 10853 doReplot = true;
10564 10854 if (selectionStateChanged)
10565 10855 emit selectionChangedByUser();
@@ -10621,28 +10911,19 void QCustomPlot::wheelEvent(QWheelEvent
10621 10911 */
10622 10912 void QCustomPlot::draw(QCPPainter *painter)
10623 10913 {
10624 // update all axis tick vectors:
10625 QList<QCPAxisRect*> rects = axisRects();
10626 for (int i=0; i<rects.size(); ++i)
10627 {
10628 QList<QCPAxis*> axes = rects.at(i)->axes();
10629 for (int k=0; k<axes.size(); ++k)
10630 axes.at(k)->setupTickVectors();
10631 }
10632
10633 // recalculate layout:
10634 mPlotLayout->update();
10914 // run through layout phases:
10915 mPlotLayout->update(QCPLayoutElement::upPreparation);
10916 mPlotLayout->update(QCPLayoutElement::upMargins);
10917 mPlotLayout->update(QCPLayoutElement::upLayout);
10635 10918
10636 10919 // draw viewport background pixmap:
10637 10920 drawBackground(painter);
10638 10921
10639 10922 // draw all layered objects (grid, axes, plottables, items, legend,...):
10640 for (int layerIndex=0; layerIndex < mLayers.size(); ++layerIndex)
10641 {
10642 QList<QCPLayerable*> layerChildren = mLayers.at(layerIndex)->children();
10643 for (int k=0; k < layerChildren.size(); ++k)
10644 {
10645 QCPLayerable *child = layerChildren.at(k);
10923 foreach (QCPLayer *layer, mLayers)
10924 {
10925 foreach (QCPLayerable *child, layer->children())
10926 {
10646 10927 if (child->realVisibility())
10647 10928 {
10648 10929 painter->save();
@@ -10653,6 +10934,17 void QCustomPlot::draw(QCPPainter *paint
10653 10934 }
10654 10935 }
10655 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 */
10656 10948 }
10657 10949
10658 10950 /*! \internal
@@ -10664,7 +10956,7 void QCustomPlot::draw(QCPPainter *paint
10664 10956 the viewport with the provided \a painter. The scaled version is buffered in
10665 10957 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10666 10958 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
10667 dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
10959 dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
10668 10960 set.
10669 10961
10670 10962 Note that this function does not draw a fill with the background brush (\ref setBackground(const
@@ -10805,7 +11097,7 bool QCustomPlot::saveRastered(const QSt
10805 11097 */
10806 11098 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
10807 11099 {
10808 // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11100 // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
10809 11101 int newWidth, newHeight;
10810 11102 if (width == 0 || height == 0)
10811 11103 {
@@ -10861,7 +11153,7 QPixmap QCustomPlot::toPixmap(int width,
10861 11153 */
10862 11154 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
10863 11155 {
10864 // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11156 // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
10865 11157 int newWidth, newHeight;
10866 11158 if (width == 0 || height == 0)
10867 11159 {
@@ -10890,6 +11182,3215 void QCustomPlot::toPainter(QCPPainter *
10890 11182
10891 11183
10892 11184 ////////////////////////////////////////////////////////////////////////////////////////////////////
11185 //////////////////// QCPColorGradient
11186 ////////////////////////////////////////////////////////////////////////////////////////////////////
11187
11188 /*! \class QCPColorGradient
11189 \brief Defines a color gradient for use with e.g. \ref QCPColorMap
11190
11191 This class describes a color gradient which can be used to encode data with color. For example,
11192 QCPColorMap and QCPColorScale have a \ref QCPColorMap::setGradient "setGradient" method which
11193 takes an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
11194 with a \a position from 0 to 1. In between these defined color positions, the
11195 color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
11196
11197 Alternatively, load one of the preset color gradients shown in the image below, with \ref
11198 loadPreset, or by directly specifying the preset in the constructor.
11199
11200 \image html QCPColorGradient.png
11201
11202 The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
11203 converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
11204 GradientPreset to all the \a setGradient methods, e.g.:
11205 \code
11206 colorMap->setGradient(QCPColorGradient::gpHot);
11207 \endcode
11208
11209 The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
11210 color gradient shall be applied periodically (wrapping around) to data values that lie outside
11211 the data range specified on the plottable instance can be controlled with \ref setPeriodic.
11212 */
11213
11214 /*!
11215 Constructs a new QCPColorGradient initialized with the colors and color interpolation according
11216 to \a preset.
11217
11218 The color level count is initialized to 350.
11219 */
11220 QCPColorGradient::QCPColorGradient(GradientPreset preset) :
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 */
11231 bool QCPColorGradient::operator==(const QCPColorGradient &other) const
11232 {
11233 return ((other.mLevelCount == this->mLevelCount) &&
11234 (other.mColorInterpolation == this->mColorInterpolation) &&
11235 (other.mPeriodic == this->mPeriodic) &&
11236 (other.mColorStops == this->mColorStops));
11237 }
11238
11239 /*!
11240 Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
11241 is typically enough to create a smooth appearance.
11242
11243 \image html QCPColorGradient-levelcount.png
11244 */
11245 void QCPColorGradient::setLevelCount(int n)
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;
11255 mColorBufferInvalidated = true;
11256 }
11257 }
11258
11259 /*!
11260 Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
11261 colors are the values of the passed QMap \a colorStops. In between these color stops, the color
11262 is interpolated according to \ref setColorInterpolation.
11263
11264 A more convenient way to create a custom gradient may be to clear all color stops with \ref
11265 clearColorStops and then adding them one by one with \ref setColorStopAt.
11266
11267 \see clearColorStops
11268 */
11269 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11270 {
11271 mColorStops = colorStops;
11272 mColorBufferInvalidated = true;
11273 }
11274
11275 /*!
11276 Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
11277 these color stops, the color is interpolated according to \ref setColorInterpolation.
11278
11279 \see setColorStops, clearColorStops
11280 */
11281 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11282 {
11283 mColorStops.insert(position, color);
11284 mColorBufferInvalidated = true;
11285 }
11286
11287 /*!
11288 Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
11289 interpolated linearly in RGB or in HSV color space.
11290
11291 For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
11292 whereas in HSV space the intermediate color is yellow.
11293 */
11294 void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
11295 {
11296 if (interpolation != mColorInterpolation)
11297 {
11298 mColorInterpolation = interpolation;
11299 mColorBufferInvalidated = true;
11300 }
11301 }
11302
11303 /*!
11304 Sets whether data points that are outside the configured data range (e.g. \ref
11305 QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
11306 they all have the same color, corresponding to the respective gradient boundary color.
11307
11308 \image html QCPColorGradient-periodic.png
11309
11310 As shown in the image above, gradients that have the same start and end color are especially
11311 suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
11312 the color map. A preset that has this property is \ref gpHues.
11313
11314 In practice, using periodic color gradients makes sense when the data corresponds to a periodic
11315 dimension, such as an angle or a phase. If this is not the case, the color encoding might become
11316 ambiguous, because multiple different data values are shown as the same color.
11317 */
11318 void QCPColorGradient::setPeriodic(bool enabled)
11319 {
11320 mPeriodic = enabled;
11321 }
11322
11323 /*!
11324 This method is used to quickly convert a \a data array to colors. The colors will be output in
11325 the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
11326 function. The data range that shall be used for mapping the data value to the gradient is passed
11327 in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
11328 logarithmically.
11329
11330 if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
11331 set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
11332 array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
11333 is addressed <tt>data[i*dataIndexFactor]</tt>.
11334 */
11335 void 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 }
11348 if (mColorBufferInvalidated)
11349 updateColorBuffer();
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
11401 /*! \internal
11402
11403 This method is used to colorize a single data value given in \a position, to colors. The data
11404 range that shall be used for mapping the data value to the gradient is passed in \a range. \a
11405 logarithmic indicates whether the data value shall be mapped to a color logarithmically.
11406
11407 If an entire array of data values shall be converted, rather use \ref colorize, for better
11408 performance.
11409 */
11410 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11411 {
11412 // If you change something here, make sure to also adapt ::colorize()
11413 if (mColorBufferInvalidated)
11414 updateColorBuffer();
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
11435 /*!
11436 Clears the current color stops and loads the specified \a preset. A preset consists of predefined
11437 color stops and the corresponding color interpolation method.
11438
11439 The available presets are:
11440 \image html QCPColorGradient.png
11441 */
11442 void QCPColorGradient::loadPreset(GradientPreset preset)
11443 {
11444 clearColorStops();
11445 switch (preset)
11446 {
11447 case gpGrayscale:
11448 setColorInterpolation(ciRGB);
11449 setColorStopAt(0, Qt::black);
11450 setColorStopAt(1, Qt::white);
11451 break;
11452 case gpHot:
11453 setColorInterpolation(ciRGB);
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:
11462 setColorInterpolation(ciRGB);
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:
11471 setColorInterpolation(ciHSV);
11472 setColorStopAt(0, QColor(10, 20, 30));
11473 setColorStopAt(1, QColor(250, 255, 250));
11474 break;
11475 case gpCandy:
11476 setColorInterpolation(ciHSV);
11477 setColorStopAt(0, QColor(0, 0, 255));
11478 setColorStopAt(1, QColor(255, 250, 250));
11479 break;
11480 case gpGeography:
11481 setColorInterpolation(ciRGB);
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:
11493 setColorInterpolation(ciHSV);
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:
11500 setColorInterpolation(ciRGB);
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:
11509 setColorInterpolation(ciRGB);
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:
11519 setColorInterpolation(ciHSV);
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:
11528 setColorInterpolation(ciRGB);
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:
11537 setColorInterpolation(ciHSV);
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
11546 /*!
11547 Clears all color stops.
11548
11549 \see setColorStops, setColorStopAt
11550 */
11551 void QCPColorGradient::clearColorStops()
11552 {
11553 mColorStops.clear();
11554 mColorBufferInvalidated = true;
11555 }
11556
11557 /*!
11558 Returns an inverted gradient. The inverted gradient has all properties as this \ref
11559 QCPColorGradient, but the order of the color stops is inverted.
11560
11561 \see setColorStops, setColorStopAt
11562 */
11563 QCPColorGradient QCPColorGradient::inverted() const
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
11572 /*! \internal
11573
11574 Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
11575 convert positions to colors. This is where the interpolation between color stops is calculated.
11576 */
11577 void QCPColorGradient::updateColorBuffer()
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 }
11635 mColorBufferInvalidated = false;
11636 }
11637
11638
11639 ////////////////////////////////////////////////////////////////////////////////////////////////////
11640 //////////////////// QCPAxisRect
11641 ////////////////////////////////////////////////////////////////////////////////////////////////////
11642
11643 /*! \class QCPAxisRect
11644 \brief Holds multiple axes and arranges them in a rectangular shape.
11645
11646 This class represents an axis rect, a rectangular area that is bounded on all sides with an
11647 arbitrary number of axes.
11648
11649 Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
11650 layout system allows to have multiple axis rects, e.g. arranged in a grid layout
11651 (QCustomPlot::plotLayout).
11652
11653 By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
11654 accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
11655 If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
11656 invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
11657 addAxes. To remove an axis, use \ref removeAxis.
11658
11659 The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
11660 setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
11661 explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
11662 placed on other layers, independently of the axis rect.
11663
11664 Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
11665 insetLayout and can be used to have other layout elements (or even other layouts with multiple
11666 elements) hovering inside the axis rect.
11667
11668 If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
11669 behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
11670 is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
11671 via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
11672 only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
11673 QCP::iRangeZoom.
11674
11675 \image html AxisRectSpacingOverview.png
11676 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
11677 line on the far left indicates the viewport/widget border.</center>
11678 */
11679
11680 /* start documentation of inline functions */
11681
11682 /*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
11683
11684 Returns the inset layout of this axis rect. It can be used to place other layout elements (or
11685 even layouts with multiple other elements) inside/on top of an axis rect.
11686
11687 \see QCPLayoutInset
11688 */
11689
11690 /*! \fn int QCPAxisRect::left() const
11691
11692 Returns the pixel position of the left border of this axis rect. Margins are not taken into
11693 account here, so the returned value is with respect to the inner \ref rect.
11694 */
11695
11696 /*! \fn int QCPAxisRect::right() const
11697
11698 Returns the pixel position of the right border of this axis rect. Margins are not taken into
11699 account here, so the returned value is with respect to the inner \ref rect.
11700 */
11701
11702 /*! \fn int QCPAxisRect::top() const
11703
11704 Returns the pixel position of the top border of this axis rect. Margins are not taken into
11705 account here, so the returned value is with respect to the inner \ref rect.
11706 */
11707
11708 /*! \fn int QCPAxisRect::bottom() const
11709
11710 Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
11711 account here, so the returned value is with respect to the inner \ref rect.
11712 */
11713
11714 /*! \fn int QCPAxisRect::width() const
11715
11716 Returns the pixel width of this axis rect. Margins are not taken into account here, so the
11717 returned value is with respect to the inner \ref rect.
11718 */
11719
11720 /*! \fn int QCPAxisRect::height() const
11721
11722 Returns the pixel height of this axis rect. Margins are not taken into account here, so the
11723 returned value is with respect to the inner \ref rect.
11724 */
11725
11726 /*! \fn QSize QCPAxisRect::size() const
11727
11728 Returns the pixel size of this axis rect. Margins are not taken into account here, so the
11729 returned value is with respect to the inner \ref rect.
11730 */
11731
11732 /*! \fn QPoint QCPAxisRect::topLeft() const
11733
11734 Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
11735 so the returned value is with respect to the inner \ref rect.
11736 */
11737
11738 /*! \fn QPoint QCPAxisRect::topRight() const
11739
11740 Returns the top right corner of this axis rect in pixels. Margins are not taken into account
11741 here, so the returned value is with respect to the inner \ref rect.
11742 */
11743
11744 /*! \fn QPoint QCPAxisRect::bottomLeft() const
11745
11746 Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
11747 here, so the returned value is with respect to the inner \ref rect.
11748 */
11749
11750 /*! \fn QPoint QCPAxisRect::bottomRight() const
11751
11752 Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
11753 here, so the returned value is with respect to the inner \ref rect.
11754 */
11755
11756 /*! \fn QPoint QCPAxisRect::center() const
11757
11758 Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
11759 returned value is with respect to the inner \ref rect.
11760 */
11761
11762 /* end documentation of inline functions */
11763
11764 /*!
11765 Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
11766 sides, the top and right axes are set invisible initially.
11767 */
11768 QCPAxisRect::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 {
11780 mInsetLayout->initializeParentPlot(mParentPlot);
11781 mInsetLayout->setParentLayerable(this);
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 {
11793 QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11794 QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
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
11812 QCPAxisRect::~QCPAxisRect()
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
11822 /*!
11823 Returns the number of axes on the axis rect side specified with \a type.
11824
11825 \see axis
11826 */
11827 int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
11828 {
11829 return mAxes.value(type).size();
11830 }
11831
11832 /*!
11833 Returns the axis with the given \a index on the axis rect side specified with \a type.
11834
11835 \see axisCount, axes
11836 */
11837 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
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
11850 /*!
11851 Returns all axes on the axis rect sides specified with \a types.
11852
11853 \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
11854 multiple sides.
11855
11856 \see axis
11857 */
11858 QList<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
11872 /*! \overload
11873
11874 Returns all axes of this axis rect.
11875 */
11876 QList<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
11888 /*!
11889 Adds a new axis to the axis rect side specified with \a type, and returns it.
11890
11891 If an axis rect side already contains one or more axes, the lower and upper endings of the new
11892 axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are initialized to \ref
11893 QCPLineEnding::esHalfBar.
11894
11895 \see addAxes, setupFullAxesBox
11896 */
11897 QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type)
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
11910 /*!
11911 Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
11912 <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
11913
11914 Returns a list of the added axes.
11915
11916 \see addAxis, setupFullAxesBox
11917 */
11918 QList<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
11932 /*!
11933 Removes the specified \a axis from the axis rect and deletes it.
11934
11935 Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
11936
11937 \see addAxis
11938 */
11939 bool QCPAxisRect::removeAxis(QCPAxis *axis)
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)
11950 parentPlot()->axisRemoved(axis);
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
11959 /*!
11960 Convenience function to create an axis on each side that doesn't have any axes yet and set their
11961 visibility to true. Further, the top/right axes are assigned the following properties of the
11962 bottom/left axes:
11963
11964 \li range (\ref QCPAxis::setRange)
11965 \li range reversed (\ref QCPAxis::setRangeReversed)
11966 \li scale type (\ref QCPAxis::setScaleType)
11967 \li scale log base (\ref QCPAxis::setScaleLogBase)
11968 \li ticks (\ref QCPAxis::setTicks)
11969 \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
11970 \li sub tick count (\ref QCPAxis::setSubTickCount)
11971 \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
11972 \li tick step (\ref QCPAxis::setTickStep)
11973 \li auto tick step (\ref QCPAxis::setAutoTickStep)
11974 \li number format (\ref QCPAxis::setNumberFormat)
11975 \li number precision (\ref QCPAxis::setNumberPrecision)
11976 \li tick label type (\ref QCPAxis::setTickLabelType)
11977 \li date time format (\ref QCPAxis::setDateTimeFormat)
11978 \li date time spec (\ref QCPAxis::setDateTimeSpec)
11979
11980 Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
11981
11982 If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
11983 and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
11984 */
11985 void 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
12054 /*!
12055 Returns a list of all the plottables that are associated with this axis rect.
12056
12057 A plottable is considered associated with an axis rect if its key or value axis (or both) is in
12058 this axis rect.
12059
12060 \see graphs, items
12061 */
12062 QList<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
12074 /*!
12075 Returns a list of all the graphs that are associated with this axis rect.
12076
12077 A graph is considered associated with an axis rect if its key or value axis (or both) is in
12078 this axis rect.
12079
12080 \see plottables, items
12081 */
12082 QList<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
12094 /*!
12095 Returns a list of all the items that are associated with this axis rect.
12096
12097 An item is considered associated with an axis rect if any of its positions has key or value axis
12098 set to an axis that is in this axis rect, or if any of its positions has \ref
12099 QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
12100 QCPAbstractItem::setClipAxisRect) is set to this axis rect.
12101
12102 \see plottables, graphs
12103 */
12104 QList<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
12131 /*!
12132 This method is called automatically upon replot and doesn't need to be called by users of
12133 QCPAxisRect.
12134
12135 Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
12136 and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
12137 QCPInsetLayout::update function.
12138 */
12139 void QCPAxisRect::update(UpdatePhase phase)
12140 {
12141 QCPLayoutElement::update(phase);
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 {
12154 mInsetLayout->setOuterRect(rect());
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 */
12165 QList<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 */
12178 void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
12179 {
12180 painter->setAntialiasing(false);
12181 }
12182
12183 /* inherits documentation from base class */
12184 void QCPAxisRect::draw(QCPPainter *painter)
12185 {
12186 drawBackground(painter);
12187 }
12188
12189 /*!
12190 Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
12191 axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
12192 backgrounds are usually drawn below everything else.
12193
12194 For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
12195 enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
12196 is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
12197 consider using the overloaded version of this function.
12198
12199 Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
12200 setBackground(const QBrush &brush).
12201
12202 \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
12203 */
12204 void QCPAxisRect::setBackground(const QPixmap &pm)
12205 {
12206 mBackgroundPixmap = pm;
12207 mScaledBackgroundPixmap = QPixmap();
12208 }
12209
12210 /*! \overload
12211
12212 Sets \a brush as the background brush. The axis rect background will be filled with this brush.
12213 Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
12214 are usually drawn below everything else.
12215
12216 The brush will be drawn before (under) any background pixmap, which may be specified with \ref
12217 setBackground(const QPixmap &pm).
12218
12219 To disable drawing of a background brush, set \a brush to Qt::NoBrush.
12220
12221 \see setBackground(const QPixmap &pm)
12222 */
12223 void QCPAxisRect::setBackground(const QBrush &brush)
12224 {
12225 mBackgroundBrush = brush;
12226 }
12227
12228 /*! \overload
12229
12230 Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
12231 shall be scaled in one call.
12232
12233 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
12234 */
12235 void 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
12243 /*!
12244 Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
12245 is set to true, you may control whether and how the aspect ratio of the original pixmap is
12246 preserved with \ref setBackgroundScaledMode.
12247
12248 Note that the scaled version of the original pixmap is buffered, so there is no performance
12249 penalty on replots. (Except when the axis rect dimensions are changed continuously.)
12250
12251 \see setBackground, setBackgroundScaledMode
12252 */
12253 void QCPAxisRect::setBackgroundScaled(bool scaled)
12254 {
12255 mBackgroundScaled = scaled;
12256 }
12257
12258 /*!
12259 If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
12260 define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
12261 \see setBackground, setBackgroundScaled
12262 */
12263 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12264 {
12265 mBackgroundScaledMode = mode;
12266 }
12267
12268 /*!
12269 Returns the range drag axis of the \a orientation provided.
12270
12271 \see setRangeDragAxes
12272 */
12273 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12274 {
12275 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12276 }
12277
12278 /*!
12279 Returns the range zoom axis of the \a orientation provided.
12280
12281 \see setRangeZoomAxes
12282 */
12283 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12284 {
12285 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12286 }
12287
12288 /*!
12289 Returns the range zoom factor of the \a orientation provided.
12290
12291 \see setRangeZoomFactor
12292 */
12293 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12294 {
12295 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12296 }
12297
12298 /*!
12299 Sets which axis orientation may be range dragged by the user with mouse interaction.
12300 What orientation corresponds to which specific axis can be set with
12301 \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
12302 default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
12303 is the left axis (yAxis).
12304
12305 To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
12306 QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
12307 Qt::Vertical</tt> as \a orientations.
12308
12309 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12310 contains \ref QCP::iRangeDrag to enable the range dragging interaction.
12311
12312 \see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
12313 */
12314 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12315 {
12316 mRangeDrag = orientations;
12317 }
12318
12319 /*!
12320 Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
12321 corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
12322 QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
12323 axis is the left axis (yAxis).
12324
12325 To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
12326 QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
12327 Qt::Vertical</tt> as \a orientations.
12328
12329 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12330 contains \ref QCP::iRangeZoom to enable the range zooming interaction.
12331
12332 \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
12333 */
12334 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12335 {
12336 mRangeZoom = orientations;
12337 }
12338
12339 /*!
12340 Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
12341 on the QCustomPlot widget.
12342
12343 \see setRangeZoomAxes
12344 */
12345 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12346 {
12347 mRangeDragHorzAxis = horizontal;
12348 mRangeDragVertAxis = vertical;
12349 }
12350
12351 /*!
12352 Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
12353 QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
12354 are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
12355
12356 \see setRangeDragAxes
12357 */
12358 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12359 {
12360 mRangeZoomHorzAxis = horizontal;
12361 mRangeZoomVertAxis = vertical;
12362 }
12363
12364 /*!
12365 Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
12366 \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
12367 let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
12368 and which is vertical, can be set with \ref setRangeZoomAxes.
12369
12370 When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
12371 will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
12372 same scrolling direction will zoom out.
12373 */
12374 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12375 {
12376 mRangeZoomFactorHorz = horizontalFactor;
12377 mRangeZoomFactorVert = verticalFactor;
12378 }
12379
12380 /*! \overload
12381
12382 Sets both the horizontal and vertical zoom \a factor.
12383 */
12384 void QCPAxisRect::setRangeZoomFactor(double factor)
12385 {
12386 mRangeZoomFactorHorz = factor;
12387 mRangeZoomFactorVert = factor;
12388 }
12389
12390 /*! \internal
12391
12392 Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
12393 pixmap.
12394
12395 If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
12396 according filling inside the axis rect with the provided \a painter.
12397
12398 Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
12399 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
12400 the axis rect with the provided \a painter. The scaled version is buffered in
12401 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
12402 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
12403 dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
12404 set.
12405
12406 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
12407 */
12408 void QCPAxisRect::drawBackground(QCPPainter *painter)
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 {
12417 if (mBackgroundScaled)
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
12432 /*! \internal
12433
12434 This function makes sure multiple axes on the side specified with \a type don't collide, but are
12435 distributed according to their respective space requirement (QCPAxis::calculateMargin).
12436
12437 It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
12438 one with index zero.
12439
12440 This function is called by \ref calculateAutoMargin.
12441 */
12442 void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
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 */
12463 int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
12464 {
12465 if (!mAutoMargins.testFlag(side))
12466 qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12467
12468 updateAxesOffset(QCPAxis::marginSideToAxisType(side));
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
12478 /*! \internal
12479
12480 Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
12481 pressed, the range dragging interaction is initialized (the actual range manipulation happens in
12482 the \ref mouseMoveEvent).
12483
12484 The mDragging flag is set to true and some anchor points are set that are needed to determine the
12485 distance the mouse was dragged in the mouse move/release events later.
12486
12487 \see mouseMoveEvent, mouseReleaseEvent
12488 */
12489 void 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:
12496 if (mParentPlot->noAntialiasingOnDrag())
12497 {
12498 mAADragBackup = mParentPlot->antialiasedElements();
12499 mNotAADragBackup = mParentPlot->notAntialiasedElements();
12500 }
12501 // Mouse range dragging interaction:
12502 if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12503 {
12504 if (mRangeDragHorzAxis)
12505 mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12506 if (mRangeDragVertAxis)
12507 mDragStartVertRange = mRangeDragVertAxis.data()->range();
12508 }
12509 }
12510 }
12511
12512 /*! \internal
12513
12514 Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
12515 preceding \ref mousePressEvent, the range is moved accordingly.
12516
12517 \see mousePressEvent, mouseReleaseEvent
12518 */
12519 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12520 {
12521 // Mouse range dragging interaction:
12522 if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
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 {
12556 if (mParentPlot->noAntialiasingOnDrag())
12557 mParentPlot->setNotAntialiasedElements(QCP::aeAll);
12558 mParentPlot->replot();
12559 }
12560 }
12561 }
12562
12563 /* inherits documentation from base class */
12564 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12565 {
12566 Q_UNUSED(event)
12567 mDragging = false;
12568 if (mParentPlot->noAntialiasingOnDrag())
12569 {
12570 mParentPlot->setAntialiasedElements(mAADragBackup);
12571 mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
12572 }
12573 }
12574
12575 /*! \internal
12576
12577 Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
12578 ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
12579 the scaling operation is the current cursor position inside the axis rect. The scaling factor is
12580 dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
12581 zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
12582
12583 Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
12584 wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
12585 multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
12586 exponent of the range zoom factor. This takes care of the wheel direction automatically, by
12587 inverting the factor, when the wheel step is negative (f^-1 = 1/f).
12588 */
12589 void 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 }
12610 mParentPlot->replot();
12611 }
12612 }
12613 }
12614
12615
12616 ////////////////////////////////////////////////////////////////////////////////////////////////////
12617 //////////////////// QCPAbstractLegendItem
12618 ////////////////////////////////////////////////////////////////////////////////////////////////////
12619
12620 /*! \class QCPAbstractLegendItem
12621 \brief The abstract base class for all entries in a QCPLegend.
12622
12623 It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
12624 legend, the subclass \ref QCPPlottableLegendItem is more suitable.
12625
12626 Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
12627 that's not even associated with a plottable).
12628
12629 You must implement the following pure virtual functions:
12630 \li \ref draw (from QCPLayerable)
12631
12632 You inherit the following members you may use:
12633 <table>
12634 <tr>
12635 <td>QCPLegend *\b mParentLegend</td>
12636 <td>A pointer to the parent QCPLegend.</td>
12637 </tr><tr>
12638 <td>QFont \b mFont</td>
12639 <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
12640 </tr>
12641 </table>
12642 */
12643
12644 /* start of documentation of signals */
12645
12646 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
12647
12648 This signal is emitted when the selection state of this legend item has changed, either by user
12649 interaction or by a direct call to \ref setSelected.
12650 */
12651
12652 /* end of documentation of signals */
12653
12654 /*!
12655 Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
12656 cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
12657 */
12658 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
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
12672 /*!
12673 Sets the default font of this specific legend item to \a font.
12674
12675 \see setTextColor, QCPLegend::setFont
12676 */
12677 void QCPAbstractLegendItem::setFont(const QFont &font)
12678 {
12679 mFont = font;
12680 }
12681
12682 /*!
12683 Sets the default text color of this specific legend item to \a color.
12684
12685 \see setFont, QCPLegend::setTextColor
12686 */
12687 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12688 {
12689 mTextColor = color;
12690 }
12691
12692 /*!
12693 When this legend item is selected, \a font is used to draw generic text, instead of the normal
12694 font set with \ref setFont.
12695
12696 \see setFont, QCPLegend::setSelectedFont
12697 */
12698 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
12699 {
12700 mSelectedFont = font;
12701 }
12702
12703 /*!
12704 When this legend item is selected, \a color is used to draw generic text, instead of the normal
12705 color set with \ref setTextColor.
12706
12707 \see setTextColor, QCPLegend::setSelectedTextColor
12708 */
12709 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
12710 {
12711 mSelectedTextColor = color;
12712 }
12713
12714 /*!
12715 Sets whether this specific legend item is selectable.
12716
12717 \see setSelectedParts, QCustomPlot::setInteractions
12718 */
12719 void QCPAbstractLegendItem::setSelectable(bool selectable)
12720 {
12721 if (mSelectable != selectable)
12722 {
12723 mSelectable = selectable;
12724 emit selectableChanged(mSelectable);
12725 }
12726 }
12727
12728 /*!
12729 Sets whether this specific legend item is selected.
12730
12731 It is possible to set the selection state of this item by calling this function directly, even if
12732 setSelectable is set to false.
12733
12734 \see setSelectableParts, QCustomPlot::setInteractions
12735 */
12736 void QCPAbstractLegendItem::setSelected(bool selected)
12737 {
12738 if (mSelected != selected)
12739 {
12740 mSelected = selected;
12741 emit selectionChanged(mSelected);
12742 }
12743 }
12744
12745 /* inherits documentation from base class */
12746 double 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 */
12760 void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
12761 {
12762 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
12763 }
12764
12765 /* inherits documentation from base class */
12766 QRect QCPAbstractLegendItem::clipRect() const
12767 {
12768 return mOuterRect;
12769 }
12770
12771 /* inherits documentation from base class */
12772 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12773 {
12774 Q_UNUSED(event)
12775 Q_UNUSED(details)
12776 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
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 */
12786 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12787 {
12788 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12789 {
12790 bool selBefore = mSelected;
12791 setSelected(false);
12792 if (selectionStateChanged)
12793 *selectionStateChanged = mSelected != selBefore;
12794 }
12795 }
12796
12797 ////////////////////////////////////////////////////////////////////////////////////////////////////
12798 //////////////////// QCPPlottableLegendItem
12799 ////////////////////////////////////////////////////////////////////////////////////////////////////
12800
12801 /*! \class QCPPlottableLegendItem
12802 \brief A legend item representing a plottable with an icon and the plottable name.
12803
12804 This is the standard legend item for plottables. It displays an icon of the plottable next to the
12805 plottable name. The icon is drawn by the respective plottable itself (\ref
12806 QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
12807 For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
12808 middle.
12809
12810 Legend items of this type are always associated with one plottable (retrievable via the
12811 plottable() function and settable with the constructor). You may change the font of the plottable
12812 name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
12813 QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
12814
12815 The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
12816 creates/removes legend items of this type in the default implementation. However, these functions
12817 may be reimplemented such that a different kind of legend item (e.g a direct subclass of
12818 QCPAbstractLegendItem) is used for that plottable.
12819
12820 Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
12821 QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
12822 interface, QCPLegend has specialized functions for handling legend items conveniently, see the
12823 documentation of \ref QCPLegend.
12824 */
12825
12826 /*!
12827 Creates a new legend item associated with \a plottable.
12828
12829 Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
12830
12831 A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
12832 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
12833 */
12834 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
12835 QCPAbstractLegendItem(parent),
12836 mPlottable(plottable)
12837 {
12838 }
12839
12840 /*! \internal
12841
12842 Returns the pen that shall be used to draw the icon border, taking into account the selection
12843 state of this item.
12844 */
12845 QPen QCPPlottableLegendItem::getIconBorderPen() const
12846 {
12847 return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
12848 }
12849
12850 /*! \internal
12851
12852 Returns the text color that shall be used to draw text, taking into account the selection state
12853 of this item.
12854 */
12855 QColor QCPPlottableLegendItem::getTextColor() const
12856 {
12857 return mSelected ? mSelectedTextColor : mTextColor;
12858 }
12859
12860 /*! \internal
12861
12862 Returns the font that shall be used to draw text, taking into account the selection state of this
12863 item.
12864 */
12865 QFont QCPPlottableLegendItem::getFont() const
12866 {
12867 return mSelected ? mSelectedFont : mFont;
12868 }
12869
12870 /*! \internal
12871
12872 Draws the item with \a painter. The size and position of the drawn legend item is defined by the
12873 parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
12874 of this legend item.
12875 */
12876 void QCPPlottableLegendItem::draw(QCPPainter *painter)
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
12900 /*! \internal
12901
12902 Calculates and returns the size of this item. This includes the icon, the text and the padding in
12903 between.
12904 */
12905 QSize QCPPlottableLegendItem::minimumSizeHint() const
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
12919 ////////////////////////////////////////////////////////////////////////////////////////////////////
12920 //////////////////// QCPLegend
12921 ////////////////////////////////////////////////////////////////////////////////////////////////////
12922
12923 /*! \class QCPLegend
12924 \brief Manages a legend inside a QCustomPlot.
12925
12926 A legend is a small box somewhere in the plot which lists plottables with their name and icon.
12927
12928 Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
12929 respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
12930 QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
12931 itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
12932
12933 The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
12934 QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
12935 placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
12936 handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
12937 other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
12938 However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
12939 only return the number of items with QCPAbstractLegendItems type).
12940
12941 By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
12942 layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
12943 position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
12944 outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
12945 */
12946
12947 /* start of documentation of signals */
12948
12949 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
12950
12951 This signal is emitted when the selection state of this legend has changed.
12952
12953 \see setSelectedParts, setSelectableParts
12954 */
12955
12956 /* end of documentation of signals */
12957
12958 /*!
12959 Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
12960
12961 Note that by default, QCustomPlot already contains a legend ready to be used as
12962 QCustomPlot::legend
12963 */
12964 QCPLegend::QCPLegend()
12965 {
12966 setRowSpacing(0);
12967 setColumnSpacing(10);
12968 setMargins(QMargins(2, 3, 2, 2));
12969 setAntialiased(false);
12970 setIconSize(32, 18);
12971
12972 setIconTextPadding(7);
12973
12974 setSelectableParts(spLegendBox | spItems);
12975 setSelectedParts(spNone);
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
12987 QCPLegend::~QCPLegend()
12988 {
12989 clearItems();
12990 if (mParentPlot)
12991 mParentPlot->legendRemoved(this);
12992 }
12993
12994 /* no doc for getter, see setSelectedParts */
12995 QCPLegend::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
13013 /*!
13014 Sets the pen, the border of the entire legend is drawn with.
13015 */
13016 void QCPLegend::setBorderPen(const QPen &pen)
13017 {
13018 mBorderPen = pen;
13019 }
13020
13021 /*!
13022 Sets the brush of the legend background.
13023 */
13024 void QCPLegend::setBrush(const QBrush &brush)
13025 {
13026 mBrush = brush;
13027 }
13028
13029 /*!
13030 Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
13031 use this font by default. However, a different font can be specified on a per-item-basis by
13032 accessing the specific legend item.
13033
13034 This function will also set \a font on all already existing legend items.
13035
13036 \see QCPAbstractLegendItem::setFont
13037 */
13038 void 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
13048 /*!
13049 Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
13050 will use this color by default. However, a different colors can be specified on a per-item-basis
13051 by accessing the specific legend item.
13052
13053 This function will also set \a color on all already existing legend items.
13054
13055 \see QCPAbstractLegendItem::setTextColor
13056 */
13057 void 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
13067 /*!
13068 Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
13069 representation of the graph) will use this size by default.
13070 */
13071 void QCPLegend::setIconSize(const QSize &size)
13072 {
13073 mIconSize = size;
13074 }
13075
13076 /*! \overload
13077 */
13078 void QCPLegend::setIconSize(int width, int height)
13079 {
13080 mIconSize.setWidth(width);
13081 mIconSize.setHeight(height);
13082 }
13083
13084 /*!
13085 Sets the horizontal space in pixels between the legend icon and the text next to it.
13086 Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
13087 name of the graph) will use this space by default.
13088 */
13089 void QCPLegend::setIconTextPadding(int padding)
13090 {
13091 mIconTextPadding = padding;
13092 }
13093
13094 /*!
13095 Sets the pen used to draw a border around each legend icon. Legend items that draw an
13096 icon (e.g. a visual representation of the graph) will use this pen by default.
13097
13098 If no border is wanted, set this to \a Qt::NoPen.
13099 */
13100 void QCPLegend::setIconBorderPen(const QPen &pen)
13101 {
13102 mIconBorderPen = pen;
13103 }
13104
13105 /*!
13106 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
13107 (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
13108
13109 However, even when \a selectable is set to a value not allowing the selection of a specific part,
13110 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
13111 directly.
13112
13113 \see SelectablePart, setSelectedParts
13114 */
13115 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13116 {
13117 if (mSelectableParts != selectable)
13118 {
13119 mSelectableParts = selectable;
13120 emit selectableChanged(mSelectableParts);
13121 }
13122 }
13123
13124 /*!
13125 Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
13126 is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
13127 doesn't contain \ref spItems, those items become deselected.
13128
13129 The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
13130 contains iSelectLegend. You only need to call this function when you wish to change the selection
13131 state manually.
13132
13133 This function can change the selection state of a part even when \ref setSelectableParts was set to a
13134 value that actually excludes the part.
13135
13136 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
13137
13138 Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
13139 before, because there's no way to specify which exact items to newly select. Do this by calling
13140 \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
13141
13142 \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
13143 setSelectedFont
13144 */
13145 void 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;
13166 emit selectionChanged(mSelectedParts);
13167 }
13168 }
13169
13170 /*!
13171 When the legend box is selected, this pen is used to draw the border instead of the normal pen
13172 set via \ref setBorderPen.
13173
13174 \see setSelectedParts, setSelectableParts, setSelectedBrush
13175 */
13176 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13177 {
13178 mSelectedBorderPen = pen;
13179 }
13180
13181 /*!
13182 Sets the pen legend items will use to draw their icon borders, when they are selected.
13183
13184 \see setSelectedParts, setSelectableParts, setSelectedFont
13185 */
13186 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
13187 {
13188 mSelectedIconBorderPen = pen;
13189 }
13190
13191 /*!
13192 When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
13193 set via \ref setBrush.
13194
13195 \see setSelectedParts, setSelectableParts, setSelectedBorderPen
13196 */
13197 void QCPLegend::setSelectedBrush(const QBrush &brush)
13198 {
13199 mSelectedBrush = brush;
13200 }
13201
13202 /*!
13203 Sets the default font that is used by legend items when they are selected.
13204
13205 This function will also set \a font on all already existing legend items.
13206
13207 \see setFont, QCPAbstractLegendItem::setSelectedFont
13208 */
13209 void QCPLegend::setSelectedFont(const QFont &font)
13210 {
13211 mSelectedFont = font;
13212 for (int i=0; i<itemCount(); ++i)
13213 {
13214 if (item(i))
13215 item(i)->setSelectedFont(font);
13216 }
13217 }
13218
13219 /*!
13220 Sets the default text color that is used by legend items when they are selected.
13221
13222 This function will also set \a color on all already existing legend items.
13223
13224 \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
13225 */
13226 void 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
13236 /*!
13237 Returns the item with index \a i.
13238
13239 \see itemCount
13240 */
13241 QCPAbstractLegendItem *QCPLegend::item(int index) const
13242 {
13243 return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13244 }
13245
13246 /*!
13247 Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13248 If such an item isn't in the legend, returns 0.
13249
13250 \see hasItemWithPlottable
13251 */
13252 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
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
13265 /*!
13266 Returns the number of items currently in the legend.
13267 \see item
13268 */
13269 int QCPLegend::itemCount() const
13270 {
13271 return elementCount();
13272 }
13273
13274 /*!
13275 Returns whether the legend contains \a itm.
13276 */
13277 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
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
13287 /*!
13288 Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13289 If such an item isn't in the legend, returns false.
13290
13291 \see itemWithPlottable
13292 */
13293 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
13294 {
13295 return itemWithPlottable(plottable);
13296 }
13297
13298 /*!
13299 Adds \a item to the legend, if it's not present already.
13300
13301 Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
13302
13303 The legend takes ownership of the item.
13304 */
13305 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
13306 {
13307 if (!hasItem(item))
13308 {
13309 return addElement(rowCount(), 0, item);
13310 } else
13311 return false;
13312 }
13313
13314 /*!
13315 Removes the item with index \a index from the legend.
13316
13317 Returns true, if successful.
13318
13319 \see itemCount, clearItems
13320 */
13321 bool QCPLegend::removeItem(int index)
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
13332 /*! \overload
13333
13334 Removes \a item from the legend.
13335
13336 Returns true, if successful.
13337
13338 \see clearItems
13339 */
13340 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
13341 {
13342 bool success = remove(item);
13343 simplify();
13344 return success;
13345 }
13346
13347 /*!
13348 Removes all items from the legend.
13349 */
13350 void QCPLegend::clearItems()
13351 {
13352 for (int i=itemCount()-1; i>=0; --i)
13353 removeItem(i);
13354 }
13355
13356 /*!
13357 Returns the legend items that are currently selected. If no items are selected,
13358 the list is empty.
13359
13360 \see QCPAbstractLegendItem::setSelected, setSelectable
13361 */
13362 QList<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
13376 /*! \internal
13377
13378 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
13379 before drawing main legend elements.
13380
13381 This is the antialiasing state the painter passed to the \ref draw method is in by default.
13382
13383 This function takes into account the local setting of the antialiasing flag as well as the
13384 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
13385 QCustomPlot::setNotAntialiasedElements.
13386
13387 \see setAntialiased
13388 */
13389 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
13390 {
13391 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
13392 }
13393
13394 /*! \internal
13395
13396 Returns the pen used to paint the border of the legend, taking into account the selection state
13397 of the legend box.
13398 */
13399 QPen QCPLegend::getBorderPen() const
13400 {
13401 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13402 }
13403
13404 /*! \internal
13405
13406 Returns the brush used to paint the background of the legend, taking into account the selection
13407 state of the legend box.
13408 */
13409 QBrush QCPLegend::getBrush() const
13410 {
13411 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13412 }
13413
13414 /*! \internal
13415
13416 Draws the legend box with the provided \a painter. The individual legend items are layerables
13417 themselves, thus are drawn independently.
13418 */
13419 void QCPLegend::draw(QCPPainter *painter)
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 */
13428 double 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 */
13443 void 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 */
13457 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13458 {
13459 mSelectedParts = selectedParts(); // in case item selection has changed
13460 if (mSelectableParts.testFlag(spLegendBox))
13461 {
13462 SelectableParts selBefore = mSelectedParts;
13463 setSelectedParts(selectedParts() & ~spLegendBox);
13464 if (selectionStateChanged)
13465 *selectionStateChanged = mSelectedParts != selBefore;
13466 }
13467 }
13468
13469 /* inherits documentation from base class */
13470 QCP::Interaction QCPLegend::selectionCategory() const
13471 {
13472 return QCP::iSelectLegend;
13473 }
13474
13475 /* inherits documentation from base class */
13476 QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
13477 {
13478 return QCP::iSelectLegend;
13479 }
13480
13481 /* inherits documentation from base class */
13482 void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
13483 {
13484 Q_UNUSED(parentPlot)
13485 }
13486
13487
13488 ////////////////////////////////////////////////////////////////////////////////////////////////////
13489 //////////////////// QCPPlotTitle
13490 ////////////////////////////////////////////////////////////////////////////////////////////////////
13491
13492 /*! \class QCPPlotTitle
13493 \brief A layout element displaying a plot title text
13494
13495 The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
13496 and \ref setTextColor.
13497
13498 A plot title can be added as follows:
13499 \code
13500 customPlot->plotLayout()->insertRow(0); // inserts an empty row above the default axis rect
13501 customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Your Plot Title"));
13502 \endcode
13503
13504 Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
13505 easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
13506 signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
13507 QCustomPlot::titleDoubleClick signal.
13508 */
13509
13510 /* start documentation of signals */
13511
13512 /*! \fn void QCPPlotTitle::selectionChanged(bool selected)
13513
13514 This signal is emitted when the selection state has changed to \a selected, either by user
13515 interaction or by a direct call to \ref setSelected.
13516
13517 \see setSelected, setSelectable
13518 */
13519
13520 /* end documentation of signals */
13521
13522 /*!
13523 Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
13524
13525 To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
13526 */
13527 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
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 {
13538 setLayer(parentPlot->currentLayer());
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
13545 /*! \overload
13546
13547 Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
13548 */
13549 QCPPlotTitle::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
13563 /*!
13564 Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
13565
13566 \see setFont, setTextColor
13567 */
13568 void QCPPlotTitle::setText(const QString &text)
13569 {
13570 mText = text;
13571 }
13572
13573 /*!
13574 Sets the \a font of the title text.
13575
13576 \see setTextColor, setSelectedFont
13577 */
13578 void QCPPlotTitle::setFont(const QFont &font)
13579 {
13580 mFont = font;
13581 }
13582
13583 /*!
13584 Sets the \a color of the title text.
13585
13586 \see setFont, setSelectedTextColor
13587 */
13588 void QCPPlotTitle::setTextColor(const QColor &color)
13589 {
13590 mTextColor = color;
13591 }
13592
13593 /*!
13594 Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
13595
13596 \see setFont
13597 */
13598 void QCPPlotTitle::setSelectedFont(const QFont &font)
13599 {
13600 mSelectedFont = font;
13601 }
13602
13603 /*!
13604 Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
13605
13606 \see setTextColor
13607 */
13608 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13609 {
13610 mSelectedTextColor = color;
13611 }
13612
13613 /*!
13614 Sets whether the user may select this plot title to \a selectable.
13615
13616 Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
13617 programmatically via \ref setSelected.
13618 */
13619 void QCPPlotTitle::setSelectable(bool selectable)
13620 {
13621 if (mSelectable != selectable)
13622 {
13623 mSelectable = selectable;
13624 emit selectableChanged(mSelectable);
13625 }
13626 }
13627
13628 /*!
13629 Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
13630 selectionChanged is emitted.
13631
13632 Note that this function can change the selection state independently of the current \ref
13633 setSelectable state.
13634 */
13635 void QCPPlotTitle::setSelected(bool selected)
13636 {
13637 if (mSelected != selected)
13638 {
13639 mSelected = selected;
13640 emit selectionChanged(mSelected);
13641 }
13642 }
13643
13644 /* inherits documentation from base class */
13645 void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
13646 {
13647 applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
13648 }
13649
13650 /* inherits documentation from base class */
13651 void QCPPlotTitle::draw(QCPPainter *painter)
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 */
13659 QSize QCPPlotTitle::minimumSizeHint() const
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 */
13669 QSize QCPPlotTitle::maximumSizeHint() const
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 */
13679 void 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 */
13693 void 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 */
13705 double 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
13717 /*! \internal
13718
13719 Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
13720 <tt>true</tt>, else mFont is returned.
13721 */
13722 QFont QCPPlotTitle::mainFont() const
13723 {
13724 return mSelected ? mSelectedFont : mFont;
13725 }
13726
13727 /*! \internal
13728
13729 Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
13730 <tt>true</tt>, else mTextColor is returned.
13731 */
13732 QColor QCPPlotTitle::mainTextColor() const
13733 {
13734 return mSelected ? mSelectedTextColor : mTextColor;
13735 }
13736
13737
13738 ////////////////////////////////////////////////////////////////////////////////////////////////////
13739 //////////////////// QCPColorScale
13740 ////////////////////////////////////////////////////////////////////////////////////////////////////
13741
13742 /*! \class QCPColorScale
13743 \brief A color scale for use with color coding data such as QCPColorMap
13744
13745 This layout element can be placed on the plot to correlate a color gradient with data values. It
13746 is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
13747
13748 \image html QCPColorScale.png
13749
13750 The color scale can be either horizontal or vertical, as shown in the image above. The
13751 orientation and the side where the numbers appear is controlled with \ref setType.
13752
13753 Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
13754 connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
13755 setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
13756 scale, to make them all synchronize these properties.
13757
13758 To have finer control over the number display and axis behaviour, you can directly access the
13759 \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
13760 you want to change the number of automatically generated ticks, call
13761 \code
13762 colorScale->axis()->setAutoTickCount(3);
13763 \endcode
13764
13765 Placing a color scale next to the main axis rect works like with any other layout element:
13766 \code
13767 QCPColorScale *colorScale = new QCPColorScale(customPlot);
13768 customPlot->plotLayout()->addElement(0, 1, colorScale);
13769 colorScale->setLabel("Some Label Text");
13770 \endcode
13771 In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
13772 call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
13773 scale can be set with \ref setLabel.
13774
13775 For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
13776 the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
13777 \code
13778 QCPMarginGroup *group = new QCPMarginGroup(customPlot);
13779 colorScale->setMarginGroup(QCP::msTop|QCP::msBottom, group);
13780 customPlot->axisRect()->setMarginGroup(QCP::msTop|QCP::msBottom, group);
13781 \endcode
13782
13783 Color scales are initialized with a non-zero minimum top and bottom margin (\ref
13784 setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
13785 margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
13786 horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
13787 might want to also change the minimum margins accordingly, e.g. \ref
13788 setMinimumMargins(QMargins(6, 0, 6, 0)).
13789 */
13790
13791 /* start documentation of inline functions */
13792
13793 /*! \fn QCPAxis *QCPColorScale::axis() const
13794
13795 Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
13796 appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
13797 interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
13798 setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
13799 QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
13800 the QCPColorScale or on its QCPAxis.
13801
13802 If the type of the color scale is changed with \ref setType, the axis returned by this method
13803 will change, too, to either the left, right, bottom or top axis, depending on which type was set.
13804 */
13805
13806 /* end documentation of signals */
13807 /* start documentation of signals */
13808
13809 /*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
13810
13811 This signal is emitted when the data range changes.
13812
13813 \see setDataRange
13814 */
13815
13816 /*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
13817
13818 This signal is emitted when the data scale type changes.
13819
13820 \see setDataScaleType
13821 */
13822
13823 /*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
13824
13825 This signal is emitted when the gradient changes.
13826
13827 \see setGradient
13828 */
13829
13830 /* end documentation of signals */
13831
13832 /*!
13833 Constructs a new QCPColorScale.
13834 */
13835 QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
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)
13843 setType(QCPAxis::atRight);
13844 setDataRange(QCPRange(0, 6));
13845 }
13846
13847 QCPColorScale::~QCPColorScale()
13848 {
13849 delete mAxisRect;
13850 }
13851
13852 /* undocumented getter */
13853 QString QCPColorScale::label() const
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 */
13865 bool QCPColorScale::rangeDrag() const
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 */
13879 bool QCPColorScale::rangeZoom() const
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
13892 /*!
13893 Sets at which side of the color scale the axis is placed, and thus also its orientation.
13894
13895 Note that after setting \a type to a different value, the axis returned by \ref axis() will
13896 be a different one. The new axis will adopt the following properties from the previous axis: The
13897 range, scale type, log base and label.
13898 */
13899 void QCPColorScale::setType(QCPAxis::AxisType type)
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
13940 /*!
13941 Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
13942
13943 It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
13944 also equivalent to directly accessing the \ref axis and setting its range with \ref
13945 QCPAxis::setRange.
13946
13947 \see setDataScaleType, setGradient, rescaleDataRange
13948 */
13949 void QCPColorScale::setDataRange(const QCPRange &dataRange)
13950 {
13951 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13952 {
13953 mDataRange = dataRange;
13954 if (mColorAxis)
13955 mColorAxis.data()->setRange(mDataRange);
13956 emit dataRangeChanged(mDataRange);
13957 }
13958 }
13959
13960 /*!
13961 Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
13962 or logarithmically.
13963
13964 It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
13965 also equivalent to directly accessing the \ref axis and setting its scale type with \ref
13966 QCPAxis::setScaleType.
13967
13968 \see setDataRange, setGradient
13969 */
13970 void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
13971 {
13972 if (mDataScaleType != scaleType)
13973 {
13974 mDataScaleType = scaleType;
13975 if (mColorAxis)
13976 mColorAxis.data()->setScaleType(mDataScaleType);
13977 if (mDataScaleType == QCPAxis::stLogarithmic)
13978 setDataRange(mDataRange.sanitizedForLogScale());
13979 emit dataScaleTypeChanged(mDataScaleType);
13980 }
13981 }
13982
13983 /*!
13984 Sets the color gradient that will be used to represent data values.
13985
13986 It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
13987
13988 \see setDataRange, setDataScaleType
13989 */
13990 void QCPColorScale::setGradient(const QCPColorGradient &gradient)
13991 {
13992 if (mGradient != gradient)
13993 {
13994 mGradient = gradient;
13995 if (mAxisRect)
13996 mAxisRect.data()->mGradientImageInvalidated = true;
13997 emit gradientChanged(mGradient);
13998 }
13999 }
14000
14001 /*!
14002 Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
14003 the internal \ref axis.
14004 */
14005 void 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
14016 /*!
14017 Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
14018 will have.
14019 */
14020 void QCPColorScale::setBarWidth(int width)
14021 {
14022 mBarWidth = width;
14023 }
14024
14025 /*!
14026 Sets whether the user can drag the data range (\ref setDataRange).
14027
14028 Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
14029 QCustomPlot::setInteractions) to allow range dragging.
14030 */
14031 void QCPColorScale::setRangeDrag(bool enabled)
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
14045 /*!
14046 Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
14047
14048 Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
14049 QCustomPlot::setInteractions) to allow range dragging.
14050 */
14051 void QCPColorScale::setRangeZoom(bool enabled)
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
14065 /*!
14066 Returns a list of all the color maps associated with this color scale.
14067 */
14068 QList<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
14080 /*!
14081 Changes the data range such that all color maps associated with this color scale are fully mapped
14082 to the gradient in the data dimension.
14083
14084 \see setDataRange
14085 */
14086 void 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)
14092 if (mDataScaleType == QCPAxis::stLogarithmic)
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
14131 if (mDataScaleType == QCPAxis::stLinear)
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 */
14146 void QCPColorScale::update(UpdatePhase phase)
14147 {
14148 QCPLayoutElement::update(phase);
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 {
14161 if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
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 */
14182 void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
14183 {
14184 painter->setAntialiasing(false);
14185 }
14186
14187 /* inherits documentation from base class */
14188 void 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 */
14199 void 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 */
14210 void 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 */
14221 void 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
14231 ////////////////////////////////////////////////////////////////////////////////////////////////////
14232 //////////////////// QCPColorScaleAxisRectPrivate
14233 ////////////////////////////////////////////////////////////////////////////////////////////////////
14234
14235 /*! \class QCPColorScaleAxisRectPrivate
14236
14237 \internal
14238 \brief An axis rect subclass for use in a QCPColorScale
14239
14240 This is a private class and not part of the public QCustomPlot interface.
14241
14242 It provides the axis rect functionality for the QCPColorScale class.
14243 */
14244
14245
14246 /*!
14247 Creates a new instance, as a child of \a parentColorScale.
14248 */
14249 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
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
14281 /*! \internal
14282 Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
14283 it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
14284 */
14285 void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
14286 {
14287 if (mGradientImageInvalidated)
14288 updateGradientImage();
14289
14290 bool mirrorHorz = false;
14291 bool mirrorVert = false;
14292 if (mParentColorScale->mColorAxis)
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
14302 /*! \internal
14303
14304 Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
14305 generate a gradient image. This gradient image will be used in the \ref draw method.
14306 */
14307 void QCPColorScaleAxisRectPrivate::updateGradientImage()
14308 {
14309 if (rect().isEmpty())
14310 return;
14311
14312 int n = mParentColorScale->mGradient.levelCount();
14313 int w, h;
14314 QVector<double> data(n);
14315 for (int i=0; i<n; ++i)
14316 data[i] = i;
14317 if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
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 }
14341 mGradientImageInvalidated = false;
14342 }
14343
14344 /*! \internal
14345
14346 This slot is connected to the selectionChanged signals of the four axes in the constructor. It
14347 synchronizes the selection state of the axes.
14348 */
14349 void 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
14368 /*! \internal
14369
14370 This slot is connected to the selectableChanged signals of the four axes in the constructor. It
14371 synchronizes the selectability of the axes.
14372 */
14373 void 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
14393 ////////////////////////////////////////////////////////////////////////////////////////////////////
10893 14394 //////////////////// QCPData
10894 14395 ////////////////////////////////////////////////////////////////////////////////////////////////////
10895 14396
@@ -10948,7 +14449,9 QCPData::QCPData(double key, double valu
10948 14449 Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
10949 14450 instance is accessed via QCustomPlot::graph.
10950 14451
10951 To plot data, assign it with the \ref setData or \ref addData functions.
14452 To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
14453 also access and modify the graph's data via the \ref data method, which returns a pointer to the
14454 internal \ref QCPDataMap.
10952 14455
10953 14456 Graphs are used to display single-valued data. Single-valued means that there should only be one
10954 14457 data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
@@ -10972,6 +14475,17 QCPData::QCPData(double key, double valu
10972 14475 \see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph
10973 14476 */
10974 14477
14478 /* start of documentation of inline functions */
14479
14480 /*! \fn QCPDataMap *QCPGraph::data() const
14481
14482 Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
14483 directly manipulate the data, which may be more convenient and faster than using the regular \ref
14484 setData or \ref addData methods, in certain situations.
14485 */
14486
14487 /* end of documentation of inline functions */
14488
10975 14489 /*!
10976 14490 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
10977 14491 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
@@ -10999,6 +14513,7 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCP
10999 14513 setErrorBarSize(6);
11000 14514 setErrorBarSkipSymbol(true);
11001 14515 setChannelFillGraph(0);
14516 setAdaptiveSampling(true);
11002 14517 }
11003 14518
11004 14519 QCPGraph::~QCPGraph()
@@ -11012,6 +14527,9 QCPGraph::~QCPGraph()
11012 14527 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
11013 14528 takes ownership of the passed data and replaces the internal data pointer with it. This is
11014 14529 significantly faster than copying for large datasets.
14530
14531 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14532 returns a pointer to the internal \ref QCPDataMap.
11015 14533 */
11016 14534 void QCPGraph::setData(QCPDataMap *data, bool copy)
11017 14535 {
@@ -11223,7 +14741,7 void QCPGraph::setLineStyle(LineStyle ls
11223 14741 mLineStyle = ls;
11224 14742 }
11225 14743
11226 /*!
14744 /*!
11227 14745 Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
11228 14746 are drawn (e.g. for line-only-plots with appropriate line style).
11229 14747
@@ -11256,7 +14774,7 void QCPGraph::setErrorPen(const QPen &p
11256 14774 mErrorPen = pen;
11257 14775 }
11258 14776
11259 /*!
14777 /*!
11260 14778 Sets the width of the handles at both ends of an error bar in pixels.
11261 14779 */
11262 14780 void QCPGraph::setErrorBarSize(double size)
@@ -11264,7 +14782,7 void QCPGraph::setErrorBarSize(double si
11264 14782 mErrorBarSize = size;
11265 14783 }
11266 14784
11267 /*!
14785 /*!
11268 14786 If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
11269 14787 leave some free space around the symbol.
11270 14788
@@ -11280,7 +14798,7 void QCPGraph::setErrorBarSkipSymbol(boo
11280 14798 mErrorBarSkipSymbol = enabled;
11281 14799 }
11282 14800
11283 /*!
14801 /*!
11284 14802 Sets the target graph for filling the area between this graph and \a targetGraph with the current
11285 14803 brush (\ref setBrush).
11286 14804
@@ -11310,7 +14828,47 void QCPGraph::setChannelFillGraph(QCPGr
11310 14828 }
11311 14829
11312 14830 /*!
14831 Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
14832 sampling technique can drastically improve the replot performance for graphs with a larger number
14833 of points (e.g. above 10,000), without notably changing the appearance of the graph.
14834
14835 By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
14836 sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
14837 disadvantage in almost all cases.
14838
14839 \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
14840
14841 As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
14842 reproduced reliably, as well as the overall shape of the data set. The replot time reduces
14843 dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
14844
14845 \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
14846
14847 Care must be taken when using high-density scatter plots in combination with adaptive sampling.
14848 The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
14849 gives a significant reduction of replot times, but not quite as much as for line plots. This is
14850 because scatter plots inherently need more data points to be preserved in order to still resemble
14851 the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
14852 identical, as banding occurs for the outer data points. This is in fact intentional, such that
14853 the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
14854 depends on the point density, i.e. the number of points in the plot.
14855
14856 For some situations with scatter plots it might thus be desirable to manually turn adaptive
14857 sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
14858 enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
14859 back to true afterwards.
14860 */
14861 void QCPGraph::setAdaptiveSampling(bool enabled)
14862 {
14863 mAdaptiveSampling = enabled;
14864 }
14865
14866 /*!
11313 14867 Adds the provided data points in \a dataMap to the current data.
14868
14869 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14870 returns a pointer to the internal \ref QCPDataMap.
14871
11314 14872 \see removeData
11315 14873 */
11316 14874 void QCPGraph::addData(const QCPDataMap &dataMap)
@@ -11320,6 +14878,10 void QCPGraph::addData(const QCPDataMap
11320 14878
11321 14879 /*! \overload
11322 14880 Adds the provided single data point in \a data to the current data.
14881
14882 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14883 returns a pointer to the internal \ref QCPDataMap.
14884
11323 14885 \see removeData
11324 14886 */
11325 14887 void QCPGraph::addData(const QCPData &data)
@@ -11329,6 +14891,10 void QCPGraph::addData(const QCPData &da
11329 14891
11330 14892 /*! \overload
11331 14893 Adds the provided single data point as \a key and \a value pair to the current data.
14894
14895 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14896 returns a pointer to the internal \ref QCPDataMap.
14897
11332 14898 \see removeData
11333 14899 */
11334 14900 void QCPGraph::addData(double key, double value)
@@ -11341,6 +14907,10 void QCPGraph::addData(double key, doubl
11341 14907
11342 14908 /*! \overload
11343 14909 Adds the provided data points as \a key and \a value pairs to the current data.
14910
14911 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14912 returns a pointer to the internal \ref QCPDataMap.
14913
11344 14914 \see removeData
11345 14915 */
11346 14916 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
@@ -11422,8 +14992,12 double QCPGraph::selectTest(const QPoint
11422 14992 Q_UNUSED(details)
11423 14993 if ((onlySelectable && !mSelectable) || mData->isEmpty())
11424 14994 return -1;
11425
11426 return pointDistance(pos);
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;
11427 15001 }
11428 15002
11429 15003 /*! \overload
@@ -11459,10 +15033,10 void QCPGraph::rescaleKeyAxis(bool onlyE
11459 15033 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
11460 15034 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
11461 15035
11462 bool validRange;
11463 QCPRange newRange = getKeyRange(validRange, signDomain, includeErrorBars);
11464
11465 if (validRange)
15036 bool foundRange;
15037 QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15038
15039 if (foundRange)
11466 15040 {
11467 15041 if (onlyEnlarge)
11468 15042 {
@@ -11495,10 +15069,10 void QCPGraph::rescaleValueAxis(bool onl
11495 15069 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
11496 15070 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
11497 15071
11498 bool validRange;
11499 QCPRange newRange = getValueRange(validRange, signDomain, includeErrorBars);
11500
11501 if (validRange)
15072 bool foundRange;
15073 QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15074
15075 if (foundRange)
11502 15076 {
11503 15077 if (onlyEnlarge)
11504 15078 {
@@ -11520,12 +15094,12 void QCPGraph::draw(QCPPainter *painter)
11520 15094
11521 15095 // allocate line and (if necessary) point vectors:
11522 15096 QVector<QPointF> *lineData = new QVector<QPointF>;
11523 QVector<QCPData> *pointData = 0;
15097 QVector<QCPData> *scatterData = 0;
11524 15098 if (!mScatterStyle.isNone())
11525 pointData = new QVector<QCPData>;
15099 scatterData = new QVector<QCPData>;
11526 15100
11527 15101 // fill vectors with data appropriate to plot style:
11528 getPlotData(lineData, pointData);
15102 getPlotData(lineData, scatterData);
11529 15103
11530 15104 // check data validity if flag set:
11531 15105 #ifdef QCUSTOMPLOT_CHECK_DATA
@@ -11549,13 +15123,13 void QCPGraph::draw(QCPPainter *painter)
11549 15123 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
11550 15124
11551 15125 // draw scatters:
11552 if (pointData)
11553 drawScatterPlot(painter, pointData);
15126 if (scatterData)
15127 drawScatterPlot(painter, scatterData);
11554 15128
11555 15129 // free allocated line and point vectors:
11556 15130 delete lineData;
11557 if (pointData)
11558 delete pointData;
15131 if (scatterData)
15132 delete scatterData;
11559 15133 }
11560 15134
11561 15135 /* inherits documentation from base class */
@@ -11604,30 +15178,30 void QCPGraph::drawLegendIcon(QCPPainter
11604 15178 make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
11605 15179 untouched.
11606 15180
11607 \a pointData will be filled with the original data points so \ref drawScatterPlot can draw the
15181 \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
11608 15182 scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
11609 \ref QCPScatterStyle::ssNone, pass 0 as \a pointData, and this step will be skipped.
15183 \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
11610 15184
11611 15185 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
11612 15186 getStepCenterPlotData, getImpulsePlotData
11613 15187 */
11614 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
15188 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
11615 15189 {
11616 15190 switch(mLineStyle)
11617 15191 {
11618 case lsNone: getScatterPlotData(pointData); break;
11619 case lsLine: getLinePlotData(lineData, pointData); break;
11620 case lsStepLeft: getStepLeftPlotData(lineData, pointData); break;
11621 case lsStepRight: getStepRightPlotData(lineData, pointData); break;
11622 case lsStepCenter: getStepCenterPlotData(lineData, pointData); break;
11623 case lsImpulse: getImpulsePlotData(lineData, pointData); break;
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;
11624 15198 }
11625 15199 }
11626 15200
11627 15201 /*! \internal
11628 15202
11629 15203 If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
11630 this function serves at providing the visible data points in \a pointData, so the \ref
15204 this function serves at providing the visible data points in \a scatterData, so the \ref
11631 15205 drawScatterPlot function can draw the scatter points accordingly.
11632 15206
11633 15207 If line style is not \ref lsNone, this function is not called and the data for the scatter points
@@ -11635,434 +15209,269 void QCPGraph::getPlotData(QVector<QPoin
11635 15209
11636 15210 \see drawScatterPlot
11637 15211 */
11638 void QCPGraph::getScatterPlotData(QVector<QCPData> *pointData) const
11639 {
11640 if (!pointData) return;
11641 QCPAxis *keyAxis = mKeyAxis.data();
11642 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
11643
11644 // get visible data range:
11645 QCPDataMap::const_iterator lower, upper;
11646 int dataCount = 0;
11647 getVisibleDataBounds(lower, upper, dataCount);
11648 if (dataCount > 0)
11649 {
11650 // prepare vectors:
11651 pointData->resize(dataCount);
11652
11653 // position data points:
11654 QCPDataMap::const_iterator it = lower;
11655 QCPDataMap::const_iterator upperEnd = upper+1;
11656 int i = 0;
11657 while (it != upperEnd)
11658 {
11659 (*pointData)[i] = it.value();
11660 ++i;
11661 ++it;
11662 }
11663 }
11664 }
11665
11666 /*! \internal
11667
11668 Places the raw data points needed for a normal linearly connected graph in \a lineData.
11669
11670 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
15212 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15213 {
15214 getPreparedData(0, scatterData);
15215 }
15216
15217 /*! \internal
15218
15219 Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
15220
15221 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
11671 15222 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11672 15223 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
11673 pointData, and the function will skip filling the vector.
15224 scatterData, and the function will skip filling the vector.
11674 15225
11675 15226 \see drawLinePlot
11676 15227 */
11677 void QCPGraph::getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
15228 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
11678 15229 {
11679 15230 QCPAxis *keyAxis = mKeyAxis.data();
11680 15231 QCPAxis *valueAxis = mValueAxis.data();
11681 15232 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
11682 if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11683
11684 // get visible data range:
11685 QCPDataMap::const_iterator lower, upper;
11686 int dataCount = 0;
11687 getVisibleDataBounds(lower, upper, dataCount);
11688 if (dataCount > 0)
11689 {
11690 lineData->reserve(dataCount+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11691 lineData->resize(dataCount);
11692 if (pointData)
11693 pointData->resize(dataCount);
11694
11695 // position data points:
11696 QCPDataMap::const_iterator it = lower;
11697 QCPDataMap::const_iterator upperEnd = upper+1;
11698 int i = 0;
11699 if (keyAxis->orientation() == Qt::Vertical)
11700 {
11701 while (it != upperEnd)
11702 {
11703 if (pointData)
11704 (*pointData)[i] = it.value();
11705 (*lineData)[i].setX(valueAxis->coordToPixel(it.value().value));
11706 (*lineData)[i].setY(keyAxis->coordToPixel(it.key()));
11707 ++i;
11708 ++it;
11709 }
11710 } else // key axis is horizontal
11711 {
11712 while (it != upperEnd)
11713 {
11714 if (pointData)
11715 (*pointData)[i] = it.value();
11716 (*lineData)[i].setX(keyAxis->coordToPixel(it.key()));
11717 (*lineData)[i].setY(valueAxis->coordToPixel(it.value().value));
11718 ++i;
11719 ++it;
11720 }
11721 }
11722 }
11723 }
11724
11725 /*!
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
15258 /*!
11726 15259 \internal
11727 15260 Places the raw data points needed for a step plot with left oriented steps in \a lineData.
11728 15261
11729 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
15262 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
11730 15263 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11731 15264 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
11732 pointData, and the function will skip filling the vector.
15265 scatterData, and the function will skip filling the vector.
11733 15266
11734 15267 \see drawLinePlot
11735 15268 */
11736 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
15269 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
11737 15270 {
11738 15271 QCPAxis *keyAxis = mKeyAxis.data();
11739 15272 QCPAxis *valueAxis = mValueAxis.data();
11740 15273 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
11741 if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11742
11743 // get visible data range:
11744 QCPDataMap::const_iterator lower, upper;
11745 int dataCount = 0;
11746 getVisibleDataBounds(lower, upper, dataCount);
11747 if (dataCount > 0)
11748 {
11749 lineData->reserve(dataCount*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11750 lineData->resize(dataCount*2); // multiplied by 2 because step plot needs two polyline points per one actual data point
11751 if (pointData)
11752 pointData->resize(dataCount);
11753
11754 // position data points:
11755 QCPDataMap::const_iterator it = lower;
11756 QCPDataMap::const_iterator upperEnd = upper+1;
11757 int i = 0;
11758 int ipoint = 0;
11759 if (keyAxis->orientation() == Qt::Vertical)
11760 {
11761 double lastValue = valueAxis->coordToPixel(it.value().value);
11762 double key;
11763 while (it != upperEnd)
11764 {
11765 if (pointData)
11766 {
11767 (*pointData)[ipoint] = it.value();
11768 ++ipoint;
11769 }
11770 key = keyAxis->coordToPixel(it.key());
11771 (*lineData)[i].setX(lastValue);
11772 (*lineData)[i].setY(key);
11773 ++i;
11774 lastValue = valueAxis->coordToPixel(it.value().value);
11775 (*lineData)[i].setX(lastValue);
11776 (*lineData)[i].setY(key);
11777 ++i;
11778 ++it;
11779 }
11780 } else // key axis is horizontal
11781 {
11782 double lastValue = valueAxis->coordToPixel(it.value().value);
11783 double key;
11784 while (it != upperEnd)
11785 {
11786 if (pointData)
11787 {
11788 (*pointData)[ipoint] = it.value();
11789 ++ipoint;
11790 }
11791 key = keyAxis->coordToPixel(it.key());
11792 (*lineData)[i].setX(key);
11793 (*lineData)[i].setY(lastValue);
11794 ++i;
11795 lastValue = valueAxis->coordToPixel(it.value().value);
11796 (*lineData)[i].setX(key);
11797 (*lineData)[i].setY(lastValue);
11798 ++i;
11799 ++it;
11800 }
11801 }
11802 }
11803 }
11804
11805 /*!
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
15311 /*!
11806 15312 \internal
11807 15313 Places the raw data points needed for a step plot with right oriented steps in \a lineData.
11808 15314
11809 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
15315 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
11810 15316 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11811 15317 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
11812 pointData, and the function will skip filling the vector.
15318 scatterData, and the function will skip filling the vector.
11813 15319
11814 15320 \see drawLinePlot
11815 15321 */
11816 void QCPGraph::getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
15322 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
11817 15323 {
11818 15324 QCPAxis *keyAxis = mKeyAxis.data();
11819 15325 QCPAxis *valueAxis = mValueAxis.data();
11820 15326 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
11821 if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11822
11823 // get visible data range:
11824 QCPDataMap::const_iterator lower, upper;
11825 int dataCount = 0;
11826 getVisibleDataBounds(lower, upper, dataCount);
11827 if (dataCount > 0)
11828 {
11829 lineData->reserve(dataCount*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11830 lineData->resize(dataCount*2); // multiplied by 2 because step plot needs two polyline points per one actual data point
11831 if (pointData)
11832 pointData->resize(dataCount);
11833
11834 // position points:
11835 QCPDataMap::const_iterator it = lower;
11836 QCPDataMap::const_iterator upperEnd = upper+1;
11837 int i = 0;
11838 int ipoint = 0;
11839 if (keyAxis->orientation() == Qt::Vertical)
11840 {
11841 double lastKey = keyAxis->coordToPixel(it.key());
11842 double value;
11843 while (it != upperEnd)
11844 {
11845 if (pointData)
11846 {
11847 (*pointData)[ipoint] = it.value();
11848 ++ipoint;
11849 }
11850 value = valueAxis->coordToPixel(it.value().value);
11851 (*lineData)[i].setX(value);
11852 (*lineData)[i].setY(lastKey);
11853 ++i;
11854 lastKey = keyAxis->coordToPixel(it.key());
11855 (*lineData)[i].setX(value);
11856 (*lineData)[i].setY(lastKey);
11857 ++i;
11858 ++it;
11859 }
11860 } else // key axis is horizontal
11861 {
11862 double lastKey = keyAxis->coordToPixel(it.key());
11863 double value;
11864 while (it != upperEnd)
11865 {
11866 if (pointData)
11867 {
11868 (*pointData)[ipoint] = it.value();
11869 ++ipoint;
11870 }
11871 value = valueAxis->coordToPixel(it.value().value);
11872 (*lineData)[i].setX(lastKey);
11873 (*lineData)[i].setY(value);
11874 ++i;
11875 lastKey = keyAxis->coordToPixel(it.key());
11876 (*lineData)[i].setX(lastKey);
11877 (*lineData)[i].setY(value);
11878 ++i;
11879 ++it;
11880 }
11881 }
11882 }
11883 }
11884
11885 /*!
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
15364 /*!
11886 15365 \internal
11887 15366 Places the raw data points needed for a step plot with centered steps in \a lineData.
11888 15367
11889 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
15368 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
11890 15369 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11891 15370 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
11892 pointData, and the function will skip filling the vector.
15371 scatterData, and the function will skip filling the vector.
11893 15372
11894 15373 \see drawLinePlot
11895 15374 */
11896 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
15375 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
11897 15376 {
11898 15377 QCPAxis *keyAxis = mKeyAxis.data();
11899 15378 QCPAxis *valueAxis = mValueAxis.data();
11900 15379 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
11901 if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11902
11903 // get visible data range:
11904 QCPDataMap::const_iterator lower, upper;
11905 int dataCount = 0;
11906 getVisibleDataBounds(lower, upper, dataCount);
11907 if (dataCount > 0)
11908 {
11909 // added 2 to reserve memory for lower/upper fill base points that might be needed for base fill
11910 // multiplied by 2 because step plot needs two polyline points per one actual data point
11911 lineData->reserve(dataCount*2+2);
11912 lineData->resize(dataCount*2);
11913 if (pointData)
11914 pointData->resize(dataCount);
11915
11916 // position points:
11917 QCPDataMap::const_iterator it = lower;
11918 QCPDataMap::const_iterator upperEnd = upper+1;
11919 int i = 0;
11920 int ipoint = 0;
11921 if (keyAxis->orientation() == Qt::Vertical)
11922 {
11923 double lastKey = keyAxis->coordToPixel(it.key());
11924 double lastValue = valueAxis->coordToPixel(it.value().value);
11925 double key;
11926 if (pointData)
11927 {
11928 (*pointData)[ipoint] = it.value();
11929 ++ipoint;
11930 }
11931 (*lineData)[i].setX(lastValue);
11932 (*lineData)[i].setY(lastKey);
11933 ++it;
11934 ++i;
11935 while (it != upperEnd)
11936 {
11937 if (pointData)
11938 {
11939 (*pointData)[ipoint] = it.value();
11940 ++ipoint;
11941 }
11942 key = (keyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
11943 (*lineData)[i].setX(lastValue);
11944 (*lineData)[i].setY(key);
11945 ++i;
11946 lastValue = valueAxis->coordToPixel(it.value().value);
11947 lastKey = keyAxis->coordToPixel(it.key());
11948 (*lineData)[i].setX(lastValue);
11949 (*lineData)[i].setY(key);
11950 ++it;
11951 ++i;
11952 }
11953 (*lineData)[i].setX(lastValue);
11954 (*lineData)[i].setY(lastKey);
11955 } else // key axis is horizontal
11956 {
11957 double lastKey = keyAxis->coordToPixel(it.key());
11958 double lastValue = valueAxis->coordToPixel(it.value().value);
11959 double key;
11960 if (pointData)
11961 {
11962 (*pointData)[ipoint] = it.value();
11963 ++ipoint;
11964 }
11965 (*lineData)[i].setX(lastKey);
11966 (*lineData)[i].setY(lastValue);
11967 ++it;
11968 ++i;
11969 while (it != upperEnd)
11970 {
11971 if (pointData)
11972 {
11973 (*pointData)[ipoint] = it.value();
11974 ++ipoint;
11975 }
11976 key = (keyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
11977 (*lineData)[i].setX(key);
11978 (*lineData)[i].setY(lastValue);
11979 ++i;
11980 lastValue = valueAxis->coordToPixel(it.value().value);
11981 lastKey = keyAxis->coordToPixel(it.key());
11982 (*lineData)[i].setX(key);
11983 (*lineData)[i].setY(lastValue);
11984 ++it;
11985 ++i;
11986 }
11987 (*lineData)[i].setX(lastKey);
11988 (*lineData)[i].setY(lastValue);
11989 }
11990 }
11991 }
11992
11993 /*!
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
15429 /*!
11994 15430 \internal
11995 15431 Places the raw data points needed for an impulse plot in \a lineData.
11996 15432
11997 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
15433 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
11998 15434 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11999 15435 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
12000 pointData, and the function will skip filling the vector.
15436 scatterData, and the function will skip filling the vector.
12001 15437
12002 15438 \see drawImpulsePlot
12003 15439 */
12004 void QCPGraph::getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
15440 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
12005 15441 {
12006 15442 QCPAxis *keyAxis = mKeyAxis.data();
12007 15443 QCPAxis *valueAxis = mValueAxis.data();
12008 15444 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
12009 if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
12010
12011 // get visible data range:
12012 QCPDataMap::const_iterator lower, upper;
12013 int dataCount = 0;
12014 getVisibleDataBounds(lower, upper, dataCount);
12015 if (dataCount > 0)
12016 {
12017 lineData->resize(dataCount*2); // no need to reserve 2 extra points, because there is no fill for impulse plot
12018 if (pointData)
12019 pointData->resize(dataCount);
12020
12021 // position data points:
12022 QCPDataMap::const_iterator it = lower;
12023 QCPDataMap::const_iterator upperEnd = upper+1;
12024 int i = 0;
12025 int ipoint = 0;
12026 if (keyAxis->orientation() == Qt::Vertical)
12027 {
12028 double zeroPointX = valueAxis->coordToPixel(0);
12029 double key;
12030 while (it != upperEnd)
12031 {
12032 if (pointData)
12033 {
12034 (*pointData)[ipoint] = it.value();
12035 ++ipoint;
12036 }
12037 key = keyAxis->coordToPixel(it.key());
12038 (*lineData)[i].setX(zeroPointX);
12039 (*lineData)[i].setY(key);
12040 ++i;
12041 (*lineData)[i].setX(valueAxis->coordToPixel(it.value().value));
12042 (*lineData)[i].setY(key);
12043 ++i;
12044 ++it;
12045 }
12046 } else // key axis is horizontal
12047 {
12048 double zeroPointY = valueAxis->coordToPixel(0);
12049 double key;
12050 while (it != upperEnd)
12051 {
12052 if (pointData)
12053 {
12054 (*pointData)[ipoint] = it.value();
12055 ++ipoint;
12056 }
12057 key = keyAxis->coordToPixel(it.key());
12058 (*lineData)[i].setX(key);
12059 (*lineData)[i].setY(zeroPointY);
12060 ++i;
12061 (*lineData)[i].setX(key);
12062 (*lineData)[i].setY(valueAxis->coordToPixel(it.value().value));
12063 ++i;
12064 ++it;
12065 }
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));
12066 15475 }
12067 15476 }
12068 15477 }
@@ -12105,14 +15514,14 void QCPGraph::drawFill(QCPPainter *pain
12105 15514
12106 15515 /*! \internal
12107 15516
12108 Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent
15517 Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
12109 15518 of the line style and are always drawn if the scatter style's shape is not \ref
12110 QCPScatterStyle::ssNone. Hence, the \a pointData vector is outputted by all "get(...)PlotData"
15519 QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
12111 15520 functions, together with the (line style dependent) line data.
12112 15521
12113 15522 \see drawLinePlot, drawImpulsePlot
12114 15523 */
12115 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const
15524 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
12116 15525 {
12117 15526 QCPAxis *keyAxis = mKeyAxis.data();
12118 15527 QCPAxis *valueAxis = mValueAxis.data();
@@ -12125,12 +15534,12 void QCPGraph::drawScatterPlot(QCPPainte
12125 15534 painter->setPen(mErrorPen);
12126 15535 if (keyAxis->orientation() == Qt::Vertical)
12127 15536 {
12128 for (int i=0; i<pointData->size(); ++i)
12129 drawError(painter, valueAxis->coordToPixel(pointData->at(i).value), keyAxis->coordToPixel(pointData->at(i).key), pointData->at(i));
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));
12130 15539 } else
12131 15540 {
12132 for (int i=0; i<pointData->size(); ++i)
12133 drawError(painter, keyAxis->coordToPixel(pointData->at(i).key), valueAxis->coordToPixel(pointData->at(i).value), pointData->at(i));
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));
12134 15543 }
12135 15544 }
12136 15545
@@ -12139,12 +15548,12 void QCPGraph::drawScatterPlot(QCPPainte
12139 15548 mScatterStyle.applyTo(painter, mPen);
12140 15549 if (keyAxis->orientation() == Qt::Vertical)
12141 15550 {
12142 for (int i=0; i<pointData->size(); ++i)
12143 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(pointData->at(i).value), keyAxis->coordToPixel(pointData->at(i).key));
12144 } else
12145 {
12146 for (int i=0; i<pointData->size(); ++i)
12147 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(pointData->at(i).key), valueAxis->coordToPixel(pointData->at(i).value));
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));
12148 15557 }
12149 15558 }
12150 15559
@@ -12190,7 +15599,7 void QCPGraph::drawLinePlot(QCPPainter *
12190 15599 for (int i=1; i<lineData->size(); ++i)
12191 15600 painter->drawLine(lineData->at(i-1), lineData->at(i));
12192 15601 } else
12193 {
15602 {
12194 15603 painter->drawPolyline(QPolygonF(*lineData));
12195 15604 }
12196 15605 }
@@ -12217,6 +15626,199 void QCPGraph::drawImpulsePlot(QCPPainte
12217 15626 }
12218 15627 }
12219 15628
15629 /*! \internal
15630
15631 Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into
15632 consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point
15633 densities.
15634
15635 0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't
15636 needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a
15637 scatterData should be 0 to prevent unnecessary calculations.
15638
15639 This method is used by the various "get(...)PlotData" methods to get the basic working set of data.
15640 */
15641 void 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();
15654 if (mAdaptiveSampling)
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
12220 15822 /*! \internal
12221 15823
12222 15824 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
@@ -12248,7 +15850,7 void QCPGraph::drawError(QCPPainter *pai
12248 15850 {
12249 15851 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
12250 15852 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
12251 if (y-b > skipSymbolMargin)
15853 if (y-b > skipSymbolMargin)
12252 15854 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
12253 15855 } else
12254 15856 painter->drawLine(QLineF(x, a, x, b));
@@ -12321,30 +15923,25 void QCPGraph::drawError(QCPPainter *pai
12321 15923
12322 15924 /*! \internal
12323 15925
12324 called by the specific plot data generating functions "get(...)PlotData" to determine which data
12325 range is visible, so only that needs to be processed.
15926 called by \ref getPreparedData to determine which data (key) range is visible at the current key
15927 axis range setting, so only that needs to be processed.
12326 15928
12327 15929 \a lower returns an iterator to the lowest data point that needs to be taken into account when
12328 plotting. Note that in order to get a clean plot all the way to the edge of the axes, \a lower
12329 may still be outside the visible range.
15930 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
15931 lower may still be just outside the visible range.
12330 15932
12331 15933 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
12332 outside of the visible range.
12333
12334 \a count number of data points that need plotting, i.e. points between \a lower and \a upper,
12335 including them. This is useful for allocating the array of <tt>QPointF</tt>s in the specific
12336 drawing functions.
12337
12338 if the graph contains no data, \a count is zero and both \a lower and \a upper point to constEnd.
12339 */
12340 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const
15934 just outside of the visible range.
15935
15936 if the graph contains no data, both \a lower and \a upper point to constEnd.
15937 */
15938 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
12341 15939 {
12342 15940 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
12343 15941 if (mData->isEmpty())
12344 15942 {
12345 15943 lower = mData->constEnd();
12346 15944 upper = mData->constEnd();
12347 count = 0;
12348 15945 return;
12349 15946 }
12350 15947
@@ -12356,15 +15953,30 void QCPGraph::getVisibleDataBounds(QCPD
12356 15953
12357 15954 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
12358 15955 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
12359
12360 // count number of points in range lower to upper (including them), so we can allocate array for them in draw functions:
15956 }
15957
15958 /*! \internal
15959
15960 Counts the number of data points between \a lower and \a upper (including them), up to a maximum
15961 of \a maxCount.
15962
15963 This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
15964 used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
15965 only needs to be done until \a maxCount is reached, which should be set to the number of data
15966 points at which adaptive sampling sets in.
15967 */
15968 int 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;
12361 15972 QCPDataMap::const_iterator it = lower;
12362 count = 1;
12363 while (it != upper)
15973 int count = 1;
15974 while (it != upper && count < maxCount)
12364 15975 {
12365 15976 ++it;
12366 15977 ++count;
12367 15978 }
15979 return count;
12368 15980 }
12369 15981
12370 15982 /*! \internal
@@ -12749,7 +16361,7 int QCPGraph::findIndexAboveY(const QVec
12749 16361 return -1;
12750 16362 }
12751 16363
12752 /*! \internal
16364 /*! \internal
12753 16365
12754 16366 Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
12755 16367 pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
@@ -12779,20 +16391,20 double QCPGraph::pointDistance(const QPo
12779 16391 if (mLineStyle == lsNone)
12780 16392 {
12781 16393 // no line displayed, only calculate distance to scatter points:
12782 QVector<QCPData> *pointData = new QVector<QCPData>;
12783 getScatterPlotData(pointData);
16394 QVector<QCPData> *scatterData = new QVector<QCPData>;
16395 getScatterPlotData(scatterData);
12784 16396 double minDistSqr = std::numeric_limits<double>::max();
12785 16397 QPointF ptA;
12786 QPointF ptB = coordsToPixels(pointData->at(0).key, pointData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
12787 for (int i=1; i<pointData->size(); ++i)
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)
12788 16400 {
12789 16401 ptA = ptB;
12790 ptB = coordsToPixels(pointData->at(i).key, pointData->at(i).value);
16402 ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value);
12791 16403 double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
12792 16404 if (currentDistSqr < minDistSqr)
12793 16405 minDistSqr = currentDistSqr;
12794 16406 }
12795 delete pointData;
16407 delete scatterData;
12796 16408 return sqrt(minDistSqr);
12797 16409 } else
12798 16410 {
@@ -12809,7 +16421,7 double QCPGraph::pointDistance(const QPo
12809 16421 if (currentDistSqr < minDistSqr)
12810 16422 minDistSqr = currentDistSqr;
12811 16423 }
12812 } else
16424 } else
12813 16425 {
12814 16426 // all other line plots (line and step) connect points directly:
12815 16427 for (int i=0; i<lineData->size()-1; ++i)
@@ -12848,28 +16460,28 int QCPGraph::findIndexBelowY(const QVec
12848 16460 }
12849 16461
12850 16462 /* inherits documentation from base class */
12851 QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain) const
16463 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
12852 16464 {
12853 16465 // just call the specialized version which takes an additional argument whether error bars
12854 16466 // should also be taken into consideration for range calculation. We set this to true here.
12855 return getKeyRange(validRange, inSignDomain, true);
12856 }
12857
12858 /* inherits documentation from base class */
12859 QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain) const
16467 return getKeyRange(foundRange, inSignDomain, true);
16468 }
16469
16470 /* inherits documentation from base class */
16471 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
12860 16472 {
12861 16473 // just call the specialized version which takes an additional argument whether error bars
12862 16474 // should also be taken into consideration for range calculation. We set this to true here.
12863 return getValueRange(validRange, inSignDomain, true);
16475 return getValueRange(foundRange, inSignDomain, true);
12864 16476 }
12865 16477
12866 16478 /*! \overload
12867 16479
12868 16480 Allows to specify whether the error bars should be included in the range calculation.
12869 16481
12870 \see getKeyRange(bool &validRange, SignDomain inSignDomain)
12871 */
12872 QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
16482 \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
16483 */
16484 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
12873 16485 {
12874 16486 QCPRange range;
12875 16487 bool haveLower = false;
@@ -12965,7 +16577,7 QCPRange QCPGraph::getKeyRange(bool &val
12965 16577 }
12966 16578 }
12967 16579
12968 validRange = haveLower && haveUpper;
16580 foundRange = haveLower && haveUpper;
12969 16581 return range;
12970 16582 }
12971 16583
@@ -12973,9 +16585,9 QCPRange QCPGraph::getKeyRange(bool &val
12973 16585
12974 16586 Allows to specify whether the error bars should be included in the range calculation.
12975 16587
12976 \see getValueRange(bool &validRange, SignDomain inSignDomain)
12977 */
12978 QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
16588 \see getValueRange(bool &foundRange, SignDomain inSignDomain)
16589 */
16590 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
12979 16591 {
12980 16592 QCPRange range;
12981 16593 bool haveLower = false;
@@ -13071,7 +16683,7 QCPRange QCPGraph::getValueRange(bool &v
13071 16683 }
13072 16684 }
13073 16685
13074 validRange = haveLower && haveUpper;
16686 foundRange = haveLower && haveUpper;
13075 16687 return range;
13076 16688 }
13077 16689
@@ -13136,7 +16748,7 QCPCurveData::QCPCurveData(double t, dou
13136 16748 \section usage Usage
13137 16749
13138 16750 Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
13139 the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
16751 the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
13140 16752
13141 16753 Usually, you first create an instance:
13142 16754 \code
@@ -13242,7 +16854,7 void QCPCurve::setData(const QVector<dou
13242 16854 }
13243 16855 }
13244 16856
13245 /*!
16857 /*!
13246 16858 Sets the visual appearance of single data points in the plot. If set to \ref
13247 16859 QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate
13248 16860 line style).
@@ -13404,8 +17016,12 double QCPCurve::selectTest(const QPoint
13404 17016 Q_UNUSED(details)
13405 17017 if ((onlySelectable && !mSelectable) || mData->isEmpty())
13406 17018 return -1;
13407
13408 return pointDistance(pos);
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;
13409 17025 }
13410 17026
13411 17027 /* inherits documentation from base class */
@@ -13454,7 +17070,7 void QCPCurve::draw(QCPPainter *painter)
13454 17070 for (int i=1; i<lineData->size(); ++i)
13455 17071 painter->drawLine(lineData->at(i-1), lineData->at(i));
13456 17072 } else
13457 {
17073 {
13458 17074 painter->drawPolyline(QPolygonF(*lineData));
13459 17075 }
13460 17076 }
@@ -13525,9 +17141,9 void QCPCurve::drawScatterPlot(QCPPainte
13525 17141 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
13526 17142 {
13527 17143 /* Extended sides of axis rect R divide space into 9 regions:
13528 1__|_4_|__7
17144 1__|_4_|__7
13529 17145 2__|_R_|__8
13530 3 | 6 | 9
17146 3 | 6 | 9
13531 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.
13532 17148 Curves outside R become straight lines closely outside of R which greatly reduces drawing time, yet keeps the look of lines and
13533 17149 fills inside R consistent.
@@ -13636,7 +17252,7 void QCPCurve::getCurveData(QVector<QPoi
13636 17252 lineData->append(coordsToPixels((mData->constEnd()-1).value().key, (mData->constEnd()-1).value().value));
13637 17253 }
13638 17254
13639 /*! \internal
17255 /*! \internal
13640 17256
13641 17257 Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
13642 17258 pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
@@ -13703,7 +17319,7 QPointF QCPCurve::outsideCoordsToPixels(
13703 17319 }
13704 17320
13705 17321 /* inherits documentation from base class */
13706 QCPRange QCPCurve::getKeyRange(bool &validRange, SignDomain inSignDomain) const
17322 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
13707 17323 {
13708 17324 QCPRange range;
13709 17325 bool haveLower = false;
@@ -13731,12 +17347,12 QCPRange QCPCurve::getKeyRange(bool &val
13731 17347 ++it;
13732 17348 }
13733 17349
13734 validRange = haveLower && haveUpper;
17350 foundRange = haveLower && haveUpper;
13735 17351 return range;
13736 17352 }
13737 17353
13738 17354 /* inherits documentation from base class */
13739 QCPRange QCPCurve::getValueRange(bool &validRange, SignDomain inSignDomain) const
17355 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
13740 17356 {
13741 17357 QCPRange range;
13742 17358 bool haveLower = false;
@@ -13764,7 +17380,7 QCPRange QCPCurve::getValueRange(bool &v
13764 17380 ++it;
13765 17381 }
13766 17382
13767 validRange = haveLower && haveUpper;
17383 foundRange = haveLower && haveUpper;
13768 17384 return range;
13769 17385 }
13770 17386
@@ -14114,17 +17730,21 double QCPBars::selectTest(const QPointF
14114 17730 Q_UNUSED(details)
14115 17731 if (onlySelectable && !mSelectable)
14116 17732 return -1;
14117
14118 QCPBarDataMap::ConstIterator it;
14119 double posKey, posValue;
14120 pixelsToCoords(pos, posKey, posValue);
14121 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
14122 {
14123 double baseValue = getBaseValue(it.key(), it.value().value >=0);
14124 QCPRange keyRange(it.key()-mWidth*0.5, it.key()+mWidth*0.5);
14125 QCPRange valueRange(baseValue, baseValue+it.value().value);
14126 if (keyRange.contains(posKey) && valueRange.contains(posValue))
14127 return mParentPlot->selectionTolerance()*0.99;
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 }
14128 17748 }
14129 17749 return -1;
14130 17750 }
@@ -14262,7 +17882,7 void QCPBars::connectBars(QCPBars *lower
14262 17882 }
14263 17883
14264 17884 /* inherits documentation from base class */
14265 QCPRange QCPBars::getKeyRange(bool &validRange, SignDomain inSignDomain) const
17885 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
14266 17886 {
14267 17887 QCPRange range;
14268 17888 bool haveLower = false;
@@ -14290,12 +17910,12 QCPRange QCPBars::getKeyRange(bool &vali
14290 17910 ++it;
14291 17911 }
14292 17912
14293 validRange = haveLower && haveUpper;
17913 foundRange = haveLower && haveUpper;
14294 17914 return range;
14295 17915 }
14296 17916
14297 17917 /* inherits documentation from base class */
14298 QCPRange QCPBars::getValueRange(bool &validRange, SignDomain inSignDomain) const
17918 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
14299 17919 {
14300 17920 QCPRange range;
14301 17921 bool haveLower = true; // set to true, because 0 should always be visible in bar charts
@@ -14323,7 +17943,7 QCPRange QCPBars::getValueRange(bool &va
14323 17943 ++it;
14324 17944 }
14325 17945
14326 validRange = range.lower < range.upper;
17946 foundRange = true; // return true because bar charts always have the 0-line visible
14327 17947 return range;
14328 17948 }
14329 17949
@@ -14338,14 +17958,14 QCPRange QCPBars::getValueRange(bool &va
14338 17958 \image html QCPStatisticalBox.png
14339 17959
14340 17960 To plot data, assign it with the individual parameter functions or use \ref setData to set all
14341 parameters at once. The individual funcions are:
17961 parameters at once. The individual functions are:
14342 17962 \li \ref setMinimum
14343 17963 \li \ref setLowerQuartile
14344 17964 \li \ref setMedian
14345 17965 \li \ref setUpperQuartile
14346 17966 \li \ref setMaximum
14347 17967
14348 Additionally you can define a list of outliers, drawn as circle datapoints:
17968 Additionally you can define a list of outliers, drawn as scatter datapoints:
14349 17969 \li \ref setOutliers
14350 17970
14351 17971 \section appearance Changing the appearance
@@ -14483,9 +18103,9 void QCPStatisticalBox::setMaximum(doubl
14483 18103 }
14484 18104
14485 18105 /*!
14486 Sets a vector of outlier values that will be drawn as circles. Any data points in the sample that
14487 are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers and
14488 displayed as such.
18106 Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
18107 that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
18108 and displayed as such.
14489 18109
14490 18110 \see setOutlierStyle
14491 18111 */
@@ -14591,18 +18211,20 double QCPStatisticalBox::selectTest(con
14591 18211 return -1;
14592 18212 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14593 18213
14594 double posKey, posValue;
14595 pixelsToCoords(pos, posKey, posValue);
14596 // quartile box:
14597 QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
14598 QCPRange valueRange(mLowerQuartile, mUpperQuartile);
14599 if (keyRange.contains(posKey) && valueRange.contains(posValue))
14600 return mParentPlot->selectionTolerance()*0.99;
14601
14602 // min/max whiskers:
14603 if (QCPRange(mMinimum, mMaximum).contains(posValue))
14604 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
14605
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);
18220 QCPRange valueRange(mLowerQuartile, mUpperQuartile);
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 }
14606 18228 return -1;
14607 18229 }
14608 18230
@@ -14712,9 +18334,9 void QCPStatisticalBox::drawOutliers(QCP
14712 18334 }
14713 18335
14714 18336 /* inherits documentation from base class */
14715 QCPRange QCPStatisticalBox::getKeyRange(bool &validRange, SignDomain inSignDomain) const
14716 {
14717 validRange = mWidth > 0;
18337 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
18338 {
18339 foundRange = true;
14718 18340 if (inSignDomain == sdBoth)
14719 18341 {
14720 18342 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
@@ -14726,7 +18348,7 QCPRange QCPStatisticalBox::getKeyRange(
14726 18348 return QCPRange(mKey-mWidth*0.5, mKey);
14727 18349 else
14728 18350 {
14729 validRange = false;
18351 foundRange = false;
14730 18352 return QCPRange();
14731 18353 }
14732 18354 } else if (inSignDomain == sdPositive)
@@ -14737,69 +18359,938 QCPRange QCPStatisticalBox::getKeyRange(
14737 18359 return QCPRange(mKey, mKey+mWidth*0.5);
14738 18360 else
14739 18361 {
14740 validRange = false;
18362 foundRange = false;
14741 18363 return QCPRange();
14742 18364 }
14743 18365 }
14744 validRange = false;
18366 foundRange = false;
14745 18367 return QCPRange();
14746 18368 }
14747 18369
14748 18370 /* inherits documentation from base class */
14749 QCPRange QCPStatisticalBox::getValueRange(bool &validRange, SignDomain inSignDomain) const
14750 {
14751 if (inSignDomain == sdBoth)
14752 {
14753 double lower = qMin(mMinimum, qMin(mMedian, mLowerQuartile));
14754 double upper = qMax(mMaximum, qMax(mMedian, mUpperQuartile));
14755 for (int i=0; i<mOutliers.size(); ++i)
14756 {
14757 if (mOutliers.at(i) < lower)
14758 lower = mOutliers.at(i);
14759 if (mOutliers.at(i) > upper)
14760 upper = mOutliers.at(i);
14761 }
14762 validRange = upper > lower;
18371 QCPRange 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);
18375 values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
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;
14763 18404 return QCPRange(lower, upper);
14764 } else
14765 {
14766 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
14767 values.reserve(mOutliers.size() + 5);
14768 values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
14769 values << mOutliers;
14770 // go through values and find the ones in legal range:
14771 bool haveUpper = false;
14772 bool haveLower = false;
14773 double upper = 0;
14774 double lower = 0;
14775 for (int i=0; i<values.size(); ++i)
14776 {
14777 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
14778 (inSignDomain == sdPositive && values.at(i) > 0))
14779 {
14780 if (values.at(i) > upper || !haveUpper)
14781 {
14782 upper = values.at(i);
14783 haveUpper = true;
14784 }
14785 if (values.at(i) < lower || !haveLower)
14786 {
14787 lower = values.at(i);
14788 haveLower = true;
14789 }
14790 }
14791 }
14792 // return the bounds if we found some sensible values:
14793 if (haveLower && haveUpper && lower < upper)
14794 {
14795 validRange = true;
14796 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
18413 ////////////////////////////////////////////////////////////////////////////////////////////////////
18414 //////////////////// QCPColorMapData
18415 ////////////////////////////////////////////////////////////////////////////////////////////////////
18416
18417 /*! \class QCPColorMapData
18418 \brief Holds the two-dimensional data of a QCPColorMap plottable.
18419
18420 This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref
18421 QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a
18422 color, depending on the value.
18423
18424 The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize).
18425 Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref
18426 setKeyRange, \ref setValueRange).
18427
18428 The data cells can be accessed in two ways: They can be directly addressed by an integer index
18429 with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot
18430 coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are
18431 provided by the functions \ref coordToCell and \ref cellToCoord.
18432
18433 This class also buffers the minimum and maximum values that are in the data set, to provide
18434 QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value
18435 that is greater than the current maximum increases this maximum to the new value. However,
18436 setting the cell that currently holds the maximum value to a smaller value doesn't decrease the
18437 maximum again, because finding the true new maximum would require going through the entire data
18438 array, which might be time consuming. The same holds for the data minimum. This functionality is
18439 given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the
18440 true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience
18441 parameter \a recalculateDataBounds which may be set to true to automatically call \ref
18442 recalculateDataBounds internally.
18443 */
18444
18445 /* start of documentation of inline functions */
18446
18447 /*! \fn bool QCPColorMapData::isEmpty() const
18448
18449 Returns whether this instance carries no data. This is equivalent to having a size where at least
18450 one of the dimensions is 0 (see \ref setSize).
18451 */
18452
18453 /* end of documentation of inline functions */
18454
18455 /*!
18456 Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction
18457 and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap
18458 at the coordinates \a keyRange and \a valueRange.
18459
18460 \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
18461 */
18462 QCPColorMapData::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 {
18471 setSize(keySize, valueSize);
18472 fill(0);
18473 }
18474
18475 QCPColorMapData::~QCPColorMapData()
18476 {
18477 if (mData)
18478 delete[] mData;
18479 }
18480
18481 /*!
18482 Constructs a new QCPColorMapData instance copying the data and range of \a other.
18483 */
18484 QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
18485 mKeySize(0),
18486 mValueSize(0),
18487 mIsEmpty(true),
18488 mData(0),
18489 mDataModified(true)
18490 {
18491 *this = other;
18492 }
18493
18494 /*!
18495 Overwrites this color map data instance with the data stored in \a other.
18496 */
18497 QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
18498 {
18499 if (&other != this)
18500 {
18501 const int keySize = other.keySize();
18502 const int valueSize = other.valueSize();
18503 setSize(keySize, 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 */
18514 double 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 */
18525 double 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
18533 /*!
18534 Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in
18535 the value dimension.
18536
18537 The current data is discarded and the map cells are set to 0, unless the map had already the
18538 requested size.
18539
18540 Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref
18541 isEmpty returns true.
18542
18543 \see setRange, setKeySize, setValueSize
18544 */
18545 void QCPColorMapData::setSize(int keySize, int valueSize)
18546 {
18547 if (keySize != mKeySize || valueSize != mValueSize)
18548 {
18549 mKeySize = keySize;
18550 mValueSize = valueSize;
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;
14797 18567 } else
14798 {
14799 validRange = false;
14800 return QCPRange();
14801 }
14802 }
18568 mData = 0;
18569 mDataModified = true;
18570 }
18571 }
18572
18573 /*!
18574 Resizes the data array to have \a keySize cells in the key dimension.
18575
18576 The current data is discarded and the map cells are set to 0, unless the map had already the
18577 requested size.
18578
18579 Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true.
18580
18581 \see setKeyRange, setSize, setValueSize
18582 */
18583 void QCPColorMapData::setKeySize(int keySize)
18584 {
18585 setSize(keySize, mValueSize);
18586 }
18587
18588 /*!
18589 Resizes the data array to have \a valueSize cells in the value dimension.
18590
18591 The current data is discarded and the map cells are set to 0, unless the map had already the
18592 requested size.
18593
18594 Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true.
18595
18596 \see setValueRange, setSize, setKeySize
18597 */
18598 void QCPColorMapData::setValueSize(int valueSize)
18599 {
18600 setSize(mKeySize, valueSize);
18601 }
18602
18603 /*!
18604 Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area
18605 covered by the color map in plot coordinates.
18606
18607 The outer cells will be centered on the range boundaries given to this function. For example, if
18608 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
18609 be cells centered on the key coordinates 2, 2.5 and 3.
18610
18611 \see setSize
18612 */
18613 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
18614 {
18615 setKeyRange(keyRange);
18616 setValueRange(valueRange);
18617 }
18618
18619 /*!
18620 Sets the coordinate range the data shall be distributed over in the key dimension. Together with
18621 the value range, This defines the rectangular area covered by the color map in plot coordinates.
18622
18623 The outer cells will be centered on the range boundaries given to this function. For example, if
18624 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
18625 be cells centered on the key coordinates 2, 2.5 and 3.
18626
18627 \see setRange, setValueRange, setSize
18628 */
18629 void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
18630 {
18631 mKeyRange = keyRange;
18632 }
18633
18634 /*!
18635 Sets the coordinate range the data shall be distributed over in the value dimension. Together with
18636 the key range, This defines the rectangular area covered by the color map in plot coordinates.
18637
18638 The outer cells will be centered on the range boundaries given to this function. For example, if
18639 the value size (\ref setValueSize) is 3 and \a valueRange is set to <tt>QCPRange(2, 3)</tt> there
18640 will be cells centered on the value coordinates 2, 2.5 and 3.
18641
18642 \see setRange, setKeyRange, setSize
18643 */
18644 void QCPColorMapData::setValueRange(const QCPRange &valueRange)
18645 {
18646 mValueRange = valueRange;
18647 }
18648
18649 /*!
18650 Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a
18651 z.
18652
18653 \see setCell, setRange
18654 */
18655 void 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)
18663 mDataBounds.lower = z;
18664 if (z > mDataBounds.upper)
18665 mDataBounds.upper = z;
18666 mDataModified = true;
18667 }
18668 }
18669
18670 /*!
18671 Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices
18672 enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see
18673 \ref setSize).
18674
18675 In the standard plot configuration (horizontal key axis and vertical value axis, both not
18676 range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with
18677 indices (keySize-1, valueSize-1) is in the top right corner of the color map.
18678
18679 \see setData, setSize
18680 */
18681 void 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)
18687 mDataBounds.lower = z;
18688 if (z > mDataBounds.upper)
18689 mDataBounds.upper = z;
18690 mDataModified = true;
18691 }
18692 }
18693
18694 /*!
18695 Goes through the data and updates the buffered minimum and maximum data values.
18696
18697 Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange
18698 and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten
18699 with a smaller or larger value respectively, since the buffered maximum/minimum values have been
18700 updated the last time. Why this is the case is explained in the class description (\ref
18701 QCPColorMapData).
18702
18703 Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a
18704 recalculateDataBounds for convenience. Setting this to true will call this method for you, before
18705 doing the rescale.
18706 */
18707 void QCPColorMapData::recalculateDataBounds()
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
18726 /*!
18727 Frees the internal data memory.
18728
18729 This is equivalent to calling \ref setSize "setSize(0, 0)".
18730 */
18731 void QCPColorMapData::clear()
18732 {
18733 setSize(0, 0);
18734 }
18735
18736 /*!
18737 Sets all cells to the value \a z.
18738 */
18739 void QCPColorMapData::fill(double z)
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
18747 /*!
18748 Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData
18749 instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a
18750 valueIndex.
18751
18752 The retrieved key/value cell indices can then be used for example with \ref setCell.
18753
18754 If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
18755 keyIndex.
18756
18757 \see cellToCoord, QCPAxis::coordToPixel
18758 */
18759 void 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
18767 /*!
18768 Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData
18769 instance. The resulting coordinates are returned via the output parameters \a key and \a
18770 value.
18771
18772 If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
18773 value.
18774
18775 \see coordToCell, QCPAxis::pixelToCoord
18776 */
18777 void 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
18786 ////////////////////////////////////////////////////////////////////////////////////////////////////
18787 //////////////////// QCPColorMap
18788 ////////////////////////////////////////////////////////////////////////////////////////////////////
18789
18790 /*! \class QCPColorMap
18791 \brief A plottable representing a two-dimensional color map in a plot.
18792
18793 \image html QCPColorMap.png
18794
18795 The data is stored in the class \ref QCPColorMapData, which can be accessed via the data()
18796 method.
18797
18798 A color map has three dimensions to represent a data point: The \a key dimension, the \a value
18799 dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value
18800 correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QColorMap
18801 constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a
18802 value).
18803
18804 Set the number of points (or \a cells) in the key/value dimension via \ref
18805 QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is
18806 specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range
18807 boundary and the last cell will be centered on the upper range boundary. The data can be set by
18808 either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via
18809 their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer
18810 setCell, since it doesn't need to do any coordinate transformation and thus performs a bit
18811 better.
18812
18813 The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)
18814 key and value axes.
18815
18816 To show the user which colors correspond to which \a data values, a \ref QCPColorScale is
18817 typically placed to the right of the axis rect. See the documentation there for details on how to
18818 add and use a color scale.
18819
18820 \section appearance Changing the appearance
18821
18822 The central part of the appearance is the color gradient, which can be specified via \ref
18823 setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
18824 gradient.
18825
18826 The \a data range that is mapped to the colors of the gradient can be specified with \ref
18827 setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
18828 rescaleDataRange.
18829
18830 \section usage Usage
18831
18832 Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable
18833 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
18834 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
18835
18836 Usually, you first create an instance:
18837 \code
18838 QCPColorMap *colorMap = new QCPColorMap(customPlot->xAxis, customPlot->yAxis);\endcode
18839 add it to the customPlot with QCustomPlot::addPlottable:
18840 \code
18841 customPlot->addPlottable(colorMap);\endcode
18842 and then modify the properties of the newly created color map, e.g.:
18843 \code
18844 colorMap->data()->setSize(50, 50);
18845 colorMap->data()->setRange(QCPRange(0, 2), QCPRange(0, 2));
18846 for (int x=0; x<50; ++x)
18847 for (int y=0; y<50; ++y)
18848 colorMap->data()->setCell(x, y, qCos(x/10.0)+qSin(y/10.0));
18849 colorMap->setGradient(QCPColorGradient::gpPolar);
18850 colorMap->rescaleDataRange(true);
18851 customPlot->rescaleAxes();
18852 customPlot->replot();
18853 \endcode
18854
18855 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
18856 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
18857 you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
18858 determine the cell index. Rather directly access the cell index with \ref
18859 QCPColorMapData::setCell.
18860 */
18861
18862 /* start documentation of inline functions */
18863
18864 /*! \fn QCPColorMapData *QCPColorMap::data() const
18865
18866 Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to
18867 modify data points (cells) and the color map key/value range.
18868
18869 \see setData
18870 */
18871
18872 /* end documentation of inline functions */
18873
18874 /* start documentation of signals */
18875
18876 /*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
18877
18878 This signal is emitted when the data range changes.
18879
18880 \see setDataRange
18881 */
18882
18883 /*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
18884
18885 This signal is emitted when the data scale type changes.
18886
18887 \see setDataScaleType
18888 */
18889
18890 /*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
18891
18892 This signal is emitted when the gradient changes.
18893
18894 \see setGradient
18895 */
18896
18897 /* end documentation of signals */
18898
18899 /*!
18900 Constructs a color map with the specified \a keyAxis and \a valueAxis.
18901
18902 The constructed QCPColorMap can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
18903 then takes ownership of the color map.
18904 */
18905 QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
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
18915 QCPColorMap::~QCPColorMap()
18916 {
18917 delete mMapData;
18918 }
18919
18920 /*!
18921 Replaces the current \ref data with the provided \a data.
18922
18923 If \a copy is set to true, the \a data object will only be copied. if false, the color map
18924 takes ownership of the passed data and replaces the internal data pointer with it. This is
18925 significantly faster than copying for large datasets.
18926 */
18927 void QCPColorMap::setData(QCPColorMapData *data, bool copy)
18928 {
18929 if (copy)
18930 {
18931 *mMapData = *data;
18932 } else
18933 {
18934 delete mMapData;
18935 mMapData = data;
18936 }
18937 mMapImageInvalidated = true;
18938 }
18939
18940 /*!
18941 Sets the data range of this color map to \a dataRange. The data range defines which data values
18942 are mapped to the color gradient.
18943
18944 To make the data range span the full range of the data set, use \ref rescaleDataRange.
18945
18946 \see QCPColorScale::setDataRange
18947 */
18948 void QCPColorMap::setDataRange(const QCPRange &dataRange)
18949 {
18950 if (!QCPRange::validRange(dataRange)) return;
18951 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
18952 {
18953 if (mDataScaleType == QCPAxis::stLogarithmic)
18954 mDataRange = dataRange.sanitizedForLogScale();
18955 else
18956 mDataRange = dataRange.sanitizedForLinScale();
18957 mMapImageInvalidated = true;
18958 emit dataRangeChanged(mDataRange);
18959 }
18960 }
18961
18962 /*!
18963 Sets whether the data is correlated with the color gradient linearly or logarithmically.
18964
18965 \see QCPColorScale::setDataScaleType
18966 */
18967 void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
18968 {
18969 if (mDataScaleType != scaleType)
18970 {
18971 mDataScaleType = scaleType;
18972 mMapImageInvalidated = true;
18973 emit dataScaleTypeChanged(mDataScaleType);
18974 if (mDataScaleType == QCPAxis::stLogarithmic)
18975 setDataRange(mDataRange.sanitizedForLogScale());
18976 }
18977 }
18978
18979 /*!
18980 Sets the color gradient that is used to represent the data. For more details on how to create an
18981 own gradient or use one of the preset gradients, see \ref QCPColorGradient.
18982
18983 The colors defined by the gradient will be used to represent data values in the currently set
18984 data range, see \ref setDataRange. Data points that are outside this data range will either be
18985 colored uniformly with the respective gradient boundary color, or the gradient will repeat,
18986 depending on \ref QCPColorGradient::setPeriodic.
18987
18988 \see QCPColorScale::setGradient
18989 */
18990 void QCPColorMap::setGradient(const QCPColorGradient &gradient)
18991 {
18992 if (mGradient != gradient)
18993 {
18994 mGradient = gradient;
18995 mMapImageInvalidated = true;
18996 emit gradientChanged(mGradient);
18997 }
18998 }
18999
19000 /*!
19001 Sets whether the color map image shall use bicubic interpolation when displaying the color map
19002 shrinked or expanded, and not at a 1:1 pixel-to-data scale.
19003
19004 \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled"
19005 */
19006 void QCPColorMap::setInterpolate(bool enabled)
19007 {
19008 mInterpolate = enabled;
19009 }
19010
19011 /*!
19012 Sets whether the outer most data rows and columns are clipped to the specified key and value
19013 range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange).
19014
19015 if \a enabled is set to false, the data points at the border of the color map are drawn with the
19016 same width and height as all other data points. Since the data points are represented by
19017 rectangles of one color centered on the data coordinate, this means that the shown color map
19018 extends by half a data point over the specified key/value range in each direction.
19019
19020 \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled"
19021 */
19022 void QCPColorMap::setTightBoundary(bool enabled)
19023 {
19024 mTightBoundary = enabled;
19025 }
19026
19027 /*!
19028 Associates the color scale \a colorScale with this color map.
19029
19030 This means that both the color scale and the color map synchronize their gradient, data range and
19031 data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps
19032 can be associated with one single color scale. This causes the color maps to also synchronize
19033 those properties, via the mutual color scale.
19034
19035 This function causes the color map to adopt the current color gradient, data range and data scale
19036 type of \a colorScale. After this call, you may change these properties at either the color map
19037 or the color scale, and the setting will be applied to both.
19038
19039 Pass 0 as \a colorScale to disconnect the color scale from this color map again.
19040 */
19041 void QCPColorMap::setColorScale(QCPColorScale *colorScale)
19042 {
19043 if (mColorScale) // unconnect signals from old color scale
19044 {
19045 disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
19046 disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
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)));
19050 disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19051 }
19052 mColorScale = colorScale;
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)));
19059 connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
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)));
19063 connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19064 }
19065 }
19066
19067 /*!
19068 Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the
19069 current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods,
19070 only for the third data dimension of the color map.
19071
19072 The minimum and maximum values of the data set are buffered in the internal QCPColorMapData
19073 instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref
19074 QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For
19075 performance reasons, however, they are only updated in an expanding fashion. So the buffered
19076 maximum can only increase and the buffered minimum can only decrease. In consequence, changes to
19077 the data that actually lower the maximum of the data set (by overwriting the cell holding the
19078 current maximum with a smaller value), aren't recognized and the buffered maximum overestimates
19079 the true maximum of the data set. The same happens for the buffered minimum. To recalculate the
19080 true minimum and maximum by explicitly looking at each cell, the method
19081 QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a
19082 recalculateDataBounds calls this method before setting the data range to the buffered minimum and
19083 maximum.
19084
19085 \see setDataRange
19086 */
19087 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
19088 {
19089 if (recalculateDataBounds)
19090 mMapData->recalculateDataBounds();
19091 setDataRange(mMapData->dataBounds());
19092 }
19093
19094 /*!
19095 Takes the current appearance of the color map and updates the legend icon, which is used to
19096 represent this color map in the legend (see \ref QCPLegend).
19097
19098 The \a transformMode specifies whether the rescaling is done by a faster, low quality image
19099 scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm
19100 (Qt::SmoothTransformation).
19101
19102 The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to
19103 the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured
19104 legend icon size, the thumb will be rescaled during drawing of the legend item.
19105
19106 \see setDataRange
19107 */
19108 void 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
19121 /*!
19122 Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also
19123 resizes the map to 0x0 cells.
19124 */
19125 void QCPColorMap::clearData()
19126 {
19127 mMapData->clear();
19128 }
19129
19130 /* inherits documentation from base class */
19131 double 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
19148 /*! \internal
19149
19150 Updates the internal map image buffer by going through the internal \ref QCPColorMapData and
19151 turning the data values into color pixels with \ref QCPColorGradient::colorize.
19152
19153 This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image
19154 has been invalidated for a different reason (e.g. a change of the data range with \ref
19155 setDataRange).
19156 */
19157 void QCPColorMap::updateMapImage()
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 */
19197 void QCPColorMap::draw(QCPPainter *painter)
19198 {
19199 if (mMapData->isEmpty()) return;
19200 if (!mKeyAxis || !mValueAxis) return;
19201 applyDefaultAntialiasingHint(painter);
19202
19203 if (mMapData->mDataModified || mMapImageInvalidated)
19204 updateMapImage();
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 */
19233 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19234 {
19235 applyDefaultAntialiasingHint(painter);
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 */
19253 QCPRange 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 */
19275 QCPRange 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;
14803 19294 }
14804 19295
14805 19296
@@ -15342,7 +19833,7 void QCPItemCurve::draw(QCPPainter *pain
15342 19833 QPointF startDirVec(startDir->pixelPoint());
15343 19834 QPointF endDirVec(endDir->pixelPoint());
15344 19835 QPointF endVec(end->pixelPoint());
15345 if (QVector2D(endVec-startVec).length() > 1e10) // too large curves cause crash
19836 if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
15346 19837 return;
15347 19838
15348 19839 QPainterPath cubicPath(startVec);
@@ -16768,10 +21259,10 double QCPItemBracket::selectTest(const
16768 21259 if (leftVec.toPoint() == rightVec.toPoint())
16769 21260 return -1;
16770 21261
16771 QVector2D widthVec = (rightVec-leftVec)*0.5;
21262 QVector2D widthVec = (rightVec-leftVec)*0.5f;
16772 21263 QVector2D lengthVec(-widthVec.y(), widthVec.x());
16773 21264 lengthVec = lengthVec.normalized()*mLength;
16774 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
21265 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
16775 21266
16776 21267 return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
16777 21268 }
@@ -16784,10 +21275,10 void QCPItemBracket::draw(QCPPainter *pa
16784 21275 if (leftVec.toPoint() == rightVec.toPoint())
16785 21276 return;
16786 21277
16787 QVector2D widthVec = (rightVec-leftVec)*0.5;
21278 QVector2D widthVec = (rightVec-leftVec)*0.5f;
16788 21279 QVector2D lengthVec(-widthVec.y(), widthVec.x());
16789 21280 lengthVec = lengthVec.normalized()*mLength;
16790 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
21281 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
16791 21282
16792 21283 QPolygon boundingPoly;
16793 21284 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
@@ -16820,8 +21311,8 void QCPItemBracket::draw(QCPPainter *pa
16820 21311 painter->setBrush(Qt::NoBrush);
16821 21312 QPainterPath path;
16822 21313 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
16823 path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+1*lengthVec).toPointF(), centerVec.toPointF());
16824 path.cubicTo((centerVec-0.4*widthVec+1*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (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());
16825 21316 painter->drawPath(path);
16826 21317 break;
16827 21318 }
@@ -16832,11 +21323,11 void QCPItemBracket::draw(QCPPainter *pa
16832 21323 QPainterPath path;
16833 21324 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
16834 21325
16835 path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
16836 path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
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());
16837 21328
16838 path.cubicTo((centerVec-widthVec*1-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
16839 path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec*1-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
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());
16840 21331
16841 21332 painter->drawPath(path);
16842 21333 break;
@@ -16853,10 +21344,10 QPointF QCPItemBracket::anchorPixelPoint
16853 21344 if (leftVec.toPoint() == rightVec.toPoint())
16854 21345 return leftVec.toPointF();
16855 21346
16856 QVector2D widthVec = (rightVec-leftVec)*0.5;
21347 QVector2D widthVec = (rightVec-leftVec)*0.5f;
16857 21348 QVector2D lengthVec(-widthVec.y(), widthVec.x());
16858 21349 lengthVec = lengthVec.normalized()*mLength;
16859 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
21350 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
16860 21351
16861 21352 switch (anchorId)
16862 21353 {
@@ -16877,2061 +21368,3 QPen QCPItemBracket::mainPen() const
16877 21368 return mSelected ? mSelectedPen : mPen;
16878 21369 }
16879 21370
16880
16881 ////////////////////////////////////////////////////////////////////////////////////////////////////
16882 //////////////////// QCPAxisRect
16883 ////////////////////////////////////////////////////////////////////////////////////////////////////
16884
16885 /*! \class QCPAxisRect
16886 \brief Holds multiple axes and arranges them in a rectangular shape.
16887
16888 This class represents an axis rect, a rectangular area that is bounded on all sides with an
16889 arbitrary number of axes.
16890
16891 Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
16892 layout system allows to have multiple axis rects, e.g. arranged in a grid layout
16893 (QCustomPlot::plotLayout).
16894
16895 By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
16896 accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
16897 If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
16898 invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
16899 addAxes. To remove an axis, use \ref removeAxis.
16900
16901 The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
16902 setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
16903 explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
16904 placed on other layers, independently of the axis rect.
16905
16906 Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
16907 insetLayout and can be used to have other layout elements (or even other layouts with multiple
16908 elements) hovering inside the axis rect.
16909
16910 If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
16911 behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
16912 is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
16913 via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
16914 only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
16915 QCP::iRangeZoom.
16916
16917 \image html AxisRectSpacingOverview.png
16918 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
16919 line on the far left indicates the viewport/widget border.</center>
16920 */
16921
16922 /* start documentation of inline functions */
16923
16924 /*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
16925
16926 Returns the inset layout of this axis rect. It can be used to place other layout elements (or
16927 even layouts with multiple other elements) inside/on top of an axis rect.
16928
16929 \see QCPLayoutInset
16930 */
16931
16932 /*! \fn int QCPAxisRect::left() const
16933
16934 Returns the pixel position of the left border of this axis rect. Margins are not taken into
16935 account here, so the returned value is with respect to the inner \ref rect.
16936 */
16937
16938 /*! \fn int QCPAxisRect::right() const
16939
16940 Returns the pixel position of the right border of this axis rect. Margins are not taken into
16941 account here, so the returned value is with respect to the inner \ref rect.
16942 */
16943
16944 /*! \fn int QCPAxisRect::top() const
16945
16946 Returns the pixel position of the top border of this axis rect. Margins are not taken into
16947 account here, so the returned value is with respect to the inner \ref rect.
16948 */
16949
16950 /*! \fn int QCPAxisRect::bottom() const
16951
16952 Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
16953 account here, so the returned value is with respect to the inner \ref rect.
16954 */
16955
16956 /*! \fn int QCPAxisRect::width() const
16957
16958 Returns the pixel width of this axis rect. Margins are not taken into account here, so the
16959 returned value is with respect to the inner \ref rect.
16960 */
16961
16962 /*! \fn int QCPAxisRect::height() const
16963
16964 Returns the pixel height of this axis rect. Margins are not taken into account here, so the
16965 returned value is with respect to the inner \ref rect.
16966 */
16967
16968 /*! \fn QSize QCPAxisRect::size() const
16969
16970 Returns the pixel size of this axis rect. Margins are not taken into account here, so the
16971 returned value is with respect to the inner \ref rect.
16972 */
16973
16974 /*! \fn QPoint QCPAxisRect::topLeft() const
16975
16976 Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
16977 so the returned value is with respect to the inner \ref rect.
16978 */
16979
16980 /*! \fn QPoint QCPAxisRect::topRight() const
16981
16982 Returns the top right corner of this axis rect in pixels. Margins are not taken into account
16983 here, so the returned value is with respect to the inner \ref rect.
16984 */
16985
16986 /*! \fn QPoint QCPAxisRect::bottomLeft() const
16987
16988 Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
16989 here, so the returned value is with respect to the inner \ref rect.
16990 */
16991
16992 /*! \fn QPoint QCPAxisRect::bottomRight() const
16993
16994 Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
16995 here, so the returned value is with respect to the inner \ref rect.
16996 */
16997
16998 /*! \fn QPoint QCPAxisRect::center() const
16999
17000 Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
17001 returned value is with respect to the inner \ref rect.
17002 */
17003
17004 /* end documentation of inline functions */
17005
17006 /*!
17007 Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
17008 sides, the top and right axes are set invisible initially.
17009 */
17010 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
17011 QCPLayoutElement(parentPlot),
17012 mBackgroundBrush(Qt::NoBrush),
17013 mBackgroundScaled(true),
17014 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
17015 mInsetLayout(new QCPLayoutInset),
17016 mRangeDrag(Qt::Horizontal|Qt::Vertical),
17017 mRangeZoom(Qt::Horizontal|Qt::Vertical),
17018 mRangeZoomFactorHorz(0.85),
17019 mRangeZoomFactorVert(0.85),
17020 mDragging(false)
17021 {
17022 mInsetLayout->initializeParentPlot(mParentPlot);
17023 mInsetLayout->setParentLayerable(this);
17024 mInsetLayout->setParent(this);
17025
17026 setMinimumSize(50, 50);
17027 setMinimumMargins(QMargins(15, 15, 15, 15));
17028 mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
17029 mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
17030 mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
17031 mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
17032
17033 if (setupDefaultAxes)
17034 {
17035 QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
17036 QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
17037 QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
17038 QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
17039 setRangeDragAxes(xAxis, yAxis);
17040 setRangeZoomAxes(xAxis, yAxis);
17041 xAxis2->setVisible(false);
17042 yAxis2->setVisible(false);
17043 xAxis->grid()->setVisible(true);
17044 yAxis->grid()->setVisible(true);
17045 xAxis2->grid()->setVisible(false);
17046 yAxis2->grid()->setVisible(false);
17047 xAxis2->grid()->setZeroLinePen(Qt::NoPen);
17048 yAxis2->grid()->setZeroLinePen(Qt::NoPen);
17049 xAxis2->grid()->setVisible(false);
17050 yAxis2->grid()->setVisible(false);
17051 }
17052 }
17053
17054 QCPAxisRect::~QCPAxisRect()
17055 {
17056 delete mInsetLayout;
17057 mInsetLayout = 0;
17058
17059 QList<QCPAxis*> axesList = axes();
17060 for (int i=0; i<axesList.size(); ++i)
17061 removeAxis(axesList.at(i));
17062 }
17063
17064 /*!
17065 Returns the number of axes on the axis rect side specified with \a type.
17066
17067 \see axis
17068 */
17069 int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
17070 {
17071 return mAxes.value(type).size();
17072 }
17073
17074 /*!
17075 Returns the axis with the given \a index on the axis rect side specified with \a type.
17076
17077 \see axisCount, axes
17078 */
17079 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
17080 {
17081 QList<QCPAxis*> ax(mAxes.value(type));
17082 if (index >= 0 && index < ax.size())
17083 {
17084 return ax.at(index);
17085 } else
17086 {
17087 qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
17088 return 0;
17089 }
17090 }
17091
17092 /*!
17093 Returns all axes on the axis rect sides specified with \a types.
17094
17095 \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
17096 multiple sides.
17097
17098 \see axis
17099 */
17100 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
17101 {
17102 QList<QCPAxis*> result;
17103 if (types.testFlag(QCPAxis::atLeft))
17104 result << mAxes.value(QCPAxis::atLeft);
17105 if (types.testFlag(QCPAxis::atRight))
17106 result << mAxes.value(QCPAxis::atRight);
17107 if (types.testFlag(QCPAxis::atTop))
17108 result << mAxes.value(QCPAxis::atTop);
17109 if (types.testFlag(QCPAxis::atBottom))
17110 result << mAxes.value(QCPAxis::atBottom);
17111 return result;
17112 }
17113
17114 /*! \overload
17115
17116 Returns all axes of this axis rect.
17117 */
17118 QList<QCPAxis*> QCPAxisRect::axes() const
17119 {
17120 QList<QCPAxis*> result;
17121 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
17122 while (it.hasNext())
17123 {
17124 it.next();
17125 result << it.value();
17126 }
17127 return result;
17128 }
17129
17130 /*!
17131 Adds a new axis to the axis rect side specified with \a type, and returns it.
17132
17133 If an axis rect side already contains one or more axes, the lower and upper endings of the new
17134 axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are initialized to \ref
17135 QCPLineEnding::esHalfBar.
17136
17137 \see addAxes, setupFullAxesBox
17138 */
17139 QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type)
17140 {
17141 QCPAxis *newAxis = new QCPAxis(this, type);
17142 if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
17143 {
17144 bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
17145 newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
17146 newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
17147 }
17148 mAxes[type].append(newAxis);
17149 return newAxis;
17150 }
17151
17152 /*!
17153 Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
17154 <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
17155
17156 Returns a list of the added axes.
17157
17158 \see addAxis, setupFullAxesBox
17159 */
17160 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
17161 {
17162 QList<QCPAxis*> result;
17163 if (types.testFlag(QCPAxis::atLeft))
17164 result << addAxis(QCPAxis::atLeft);
17165 if (types.testFlag(QCPAxis::atRight))
17166 result << addAxis(QCPAxis::atRight);
17167 if (types.testFlag(QCPAxis::atTop))
17168 result << addAxis(QCPAxis::atTop);
17169 if (types.testFlag(QCPAxis::atBottom))
17170 result << addAxis(QCPAxis::atBottom);
17171 return result;
17172 }
17173
17174 /*!
17175 Removes the specified \a axis from the axis rect and deletes it.
17176
17177 Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
17178
17179 \see addAxis
17180 */
17181 bool QCPAxisRect::removeAxis(QCPAxis *axis)
17182 {
17183 // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
17184 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
17185 while (it.hasNext())
17186 {
17187 it.next();
17188 if (it.value().contains(axis))
17189 {
17190 mAxes[it.key()].removeOne(axis);
17191 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)
17192 parentPlot()->axisRemoved(axis);
17193 delete axis;
17194 return true;
17195 }
17196 }
17197 qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
17198 return false;
17199 }
17200
17201 /*!
17202 Convenience function to create an axis on each side that doesn't have any axes yet and set their
17203 visibility to true. Further, the top/right axes are assigned the following properties of the
17204 bottom/left axes:
17205
17206 \li range (\ref QCPAxis::setRange)
17207 \li range reversed (\ref QCPAxis::setRangeReversed)
17208 \li scale type (\ref QCPAxis::setScaleType)
17209 \li scale log base (\ref QCPAxis::setScaleLogBase)
17210 \li ticks (\ref QCPAxis::setTicks)
17211 \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
17212 \li sub tick count (\ref QCPAxis::setSubTickCount)
17213 \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
17214 \li tick step (\ref QCPAxis::setTickStep)
17215 \li auto tick step (\ref QCPAxis::setAutoTickStep)
17216 \li number format (\ref QCPAxis::setNumberFormat)
17217 \li number precision (\ref QCPAxis::setNumberPrecision)
17218 \li tick label type (\ref QCPAxis::setTickLabelType)
17219 \li date time format (\ref QCPAxis::setDateTimeFormat)
17220 \li date time spec (\ref QCPAxis::setDateTimeSpec)
17221
17222 Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
17223
17224 If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
17225 and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
17226 */
17227 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
17228 {
17229 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
17230 if (axisCount(QCPAxis::atBottom) == 0)
17231 xAxis = addAxis(QCPAxis::atBottom);
17232 else
17233 xAxis = axis(QCPAxis::atBottom);
17234
17235 if (axisCount(QCPAxis::atLeft) == 0)
17236 yAxis = addAxis(QCPAxis::atLeft);
17237 else
17238 yAxis = axis(QCPAxis::atLeft);
17239
17240 if (axisCount(QCPAxis::atTop) == 0)
17241 xAxis2 = addAxis(QCPAxis::atTop);
17242 else
17243 xAxis2 = axis(QCPAxis::atTop);
17244
17245 if (axisCount(QCPAxis::atRight) == 0)
17246 yAxis2 = addAxis(QCPAxis::atRight);
17247 else
17248 yAxis2 = axis(QCPAxis::atRight);
17249
17250 xAxis->setVisible(true);
17251 yAxis->setVisible(true);
17252 xAxis2->setVisible(true);
17253 yAxis2->setVisible(true);
17254 xAxis2->setTickLabels(false);
17255 yAxis2->setTickLabels(false);
17256
17257 xAxis2->setRange(xAxis->range());
17258 xAxis2->setRangeReversed(xAxis->rangeReversed());
17259 xAxis2->setScaleType(xAxis->scaleType());
17260 xAxis2->setScaleLogBase(xAxis->scaleLogBase());
17261 xAxis2->setTicks(xAxis->ticks());
17262 xAxis2->setAutoTickCount(xAxis->autoTickCount());
17263 xAxis2->setSubTickCount(xAxis->subTickCount());
17264 xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
17265 xAxis2->setTickStep(xAxis->tickStep());
17266 xAxis2->setAutoTickStep(xAxis->autoTickStep());
17267 xAxis2->setNumberFormat(xAxis->numberFormat());
17268 xAxis2->setNumberPrecision(xAxis->numberPrecision());
17269 xAxis2->setTickLabelType(xAxis->tickLabelType());
17270 xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
17271 xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
17272
17273 yAxis2->setRange(yAxis->range());
17274 yAxis2->setRangeReversed(yAxis->rangeReversed());
17275 yAxis2->setScaleType(yAxis->scaleType());
17276 yAxis2->setScaleLogBase(yAxis->scaleLogBase());
17277 yAxis2->setTicks(yAxis->ticks());
17278 yAxis2->setAutoTickCount(yAxis->autoTickCount());
17279 yAxis2->setSubTickCount(yAxis->subTickCount());
17280 yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
17281 yAxis2->setTickStep(yAxis->tickStep());
17282 yAxis2->setAutoTickStep(yAxis->autoTickStep());
17283 yAxis2->setNumberFormat(yAxis->numberFormat());
17284 yAxis2->setNumberPrecision(yAxis->numberPrecision());
17285 yAxis2->setTickLabelType(yAxis->tickLabelType());
17286 yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
17287 yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
17288
17289 if (connectRanges)
17290 {
17291 connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
17292 connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
17293 }
17294 }
17295
17296 /*!
17297 Returns a list of all the plottables that are associated with this axis rect.
17298
17299 A plottable is considered associated with an axis rect if its key or value axis (or both) is in
17300 this axis rect.
17301
17302 \see graphs, items
17303 */
17304 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
17305 {
17306 // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
17307 QList<QCPAbstractPlottable*> result;
17308 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
17309 {
17310 if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
17311 result.append(mParentPlot->mPlottables.at(i));
17312 }
17313 return result;
17314 }
17315
17316 /*!
17317 Returns a list of all the graphs that are associated with this axis rect.
17318
17319 A graph is considered associated with an axis rect if its key or value axis (or both) is in
17320 this axis rect.
17321
17322 \see plottables, items
17323 */
17324 QList<QCPGraph*> QCPAxisRect::graphs() const
17325 {
17326 // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
17327 QList<QCPGraph*> result;
17328 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
17329 {
17330 if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
17331 result.append(mParentPlot->mGraphs.at(i));
17332 }
17333 return result;
17334 }
17335
17336 /*!
17337 Returns a list of all the items that are associated with this axis rect.
17338
17339 An item is considered associated with an axis rect if any of its positions has key or value axis
17340 set to an axis that is in this axis rect, or if any of its positions has \ref
17341 QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
17342 QCPAbstractItem::setClipAxisRect) is set to this axis rect.
17343
17344 \see plottables, graphs
17345 */
17346 QList<QCPAbstractItem *> QCPAxisRect::items() const
17347 {
17348 // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
17349 // and miss those items that have this axis rect as clipAxisRect.
17350 QList<QCPAbstractItem*> result;
17351 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
17352 {
17353 if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
17354 {
17355 result.append(mParentPlot->mItems.at(itemId));
17356 continue;
17357 }
17358 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
17359 for (int posId=0; posId<positions.size(); ++posId)
17360 {
17361 if (positions.at(posId)->axisRect() == this ||
17362 positions.at(posId)->keyAxis()->axisRect() == this ||
17363 positions.at(posId)->valueAxis()->axisRect() == this)
17364 {
17365 result.append(mParentPlot->mItems.at(itemId));
17366 break;
17367 }
17368 }
17369 }
17370 return result;
17371 }
17372
17373 /*!
17374 This method is called automatically upon replot and doesn't need to be called by users of
17375 QCPAxisRect.
17376
17377 Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
17378 and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
17379 QCPInsetLayout::update function.
17380 */
17381 void QCPAxisRect::update()
17382 {
17383 QCPLayoutElement::update();
17384
17385 // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
17386 mInsetLayout->setOuterRect(rect());
17387 mInsetLayout->update();
17388 }
17389
17390 /* inherits documentation from base class */
17391 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
17392 {
17393 QList<QCPLayoutElement*> result;
17394 if (mInsetLayout)
17395 {
17396 result << mInsetLayout;
17397 if (recursive)
17398 result << mInsetLayout->elements(recursive);
17399 }
17400 return result;
17401 }
17402
17403 /* inherits documentation from base class */
17404 void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
17405 {
17406 painter->setAntialiasing(false);
17407 }
17408
17409 /* inherits documentation from base class */
17410 void QCPAxisRect::draw(QCPPainter *painter)
17411 {
17412 drawBackground(painter);
17413 }
17414
17415 /*!
17416 Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
17417 axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
17418 backgrounds are usually drawn below everything else.
17419
17420 For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
17421 enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
17422 is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
17423 consider using the overloaded version of this function.
17424
17425 Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
17426 setBackground(const QBrush &brush).
17427
17428 \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
17429 */
17430 void QCPAxisRect::setBackground(const QPixmap &pm)
17431 {
17432 mBackgroundPixmap = pm;
17433 mScaledBackgroundPixmap = QPixmap();
17434 }
17435
17436 /*! \overload
17437
17438 Sets \a brush as the background brush. The axis rect background will be filled with this brush.
17439 Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
17440 are usually drawn below everything else.
17441
17442 The brush will be drawn before (under) any background pixmap, which may be specified with \ref
17443 setBackground(const QPixmap &pm).
17444
17445 To disable drawing of a background brush, set \a brush to Qt::NoBrush.
17446
17447 \see setBackground(const QPixmap &pm)
17448 */
17449 void QCPAxisRect::setBackground(const QBrush &brush)
17450 {
17451 mBackgroundBrush = brush;
17452 }
17453
17454 /*! \overload
17455
17456 Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
17457 shall be scaled in one call.
17458
17459 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
17460 */
17461 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
17462 {
17463 mBackgroundPixmap = pm;
17464 mScaledBackgroundPixmap = QPixmap();
17465 mBackgroundScaled = scaled;
17466 mBackgroundScaledMode = mode;
17467 }
17468
17469 /*!
17470 Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
17471 is set to true, you may control whether and how the aspect ratio of the original pixmap is
17472 preserved with \ref setBackgroundScaledMode.
17473
17474 Note that the scaled version of the original pixmap is buffered, so there is no performance
17475 penalty on replots. (Except when the axis rect dimensions are changed continuously.)
17476
17477 \see setBackground, setBackgroundScaledMode
17478 */
17479 void QCPAxisRect::setBackgroundScaled(bool scaled)
17480 {
17481 mBackgroundScaled = scaled;
17482 }
17483
17484 /*!
17485 If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
17486 define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
17487 \see setBackground, setBackgroundScaled
17488 */
17489 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
17490 {
17491 mBackgroundScaledMode = mode;
17492 }
17493
17494 /*!
17495 Returns the range drag axis of the \a orientation provided.
17496
17497 \see setRangeDragAxes
17498 */
17499 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
17500 {
17501 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
17502 }
17503
17504 /*!
17505 Returns the range zoom axis of the \a orientation provided.
17506
17507 \see setRangeZoomAxes
17508 */
17509 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
17510 {
17511 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
17512 }
17513
17514 /*!
17515 Returns the range zoom factor of the \a orientation provided.
17516
17517 \see setRangeZoomFactor
17518 */
17519 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
17520 {
17521 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
17522 }
17523
17524 /*!
17525 Sets which axis orientation may be range dragged by the user with mouse interaction.
17526 What orientation corresponds to which specific axis can be set with
17527 \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
17528 default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
17529 is the left axis (yAxis).
17530
17531 To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
17532 QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
17533 Qt::Vertical</tt> as \a orientations.
17534
17535 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
17536 contains \ref QCP::iRangeDrag to enable the range dragging interaction.
17537
17538 \see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
17539 */
17540 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
17541 {
17542 mRangeDrag = orientations;
17543 }
17544
17545 /*!
17546 Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
17547 corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
17548 QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
17549 axis is the left axis (yAxis).
17550
17551 To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
17552 QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
17553 Qt::Vertical</tt> as \a orientations.
17554
17555 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
17556 contains \ref QCP::iRangeZoom to enable the range zooming interaction.
17557
17558 \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
17559 */
17560 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
17561 {
17562 mRangeZoom = orientations;
17563 }
17564
17565 /*!
17566 Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
17567 on the QCustomPlot widget.
17568
17569 \see setRangeZoomAxes
17570 */
17571 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
17572 {
17573 mRangeDragHorzAxis = horizontal;
17574 mRangeDragVertAxis = vertical;
17575 }
17576
17577 /*!
17578 Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
17579 QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
17580 are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
17581
17582 \see setRangeDragAxes
17583 */
17584 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
17585 {
17586 mRangeZoomHorzAxis = horizontal;
17587 mRangeZoomVertAxis = vertical;
17588 }
17589
17590 /*!
17591 Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
17592 \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
17593 let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
17594 and which is vertical, can be set with \ref setRangeZoomAxes.
17595
17596 When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
17597 will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
17598 same scrolling direction will zoom out.
17599 */
17600 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
17601 {
17602 mRangeZoomFactorHorz = horizontalFactor;
17603 mRangeZoomFactorVert = verticalFactor;
17604 }
17605
17606 /*! \overload
17607
17608 Sets both the horizontal and vertical zoom \a factor.
17609 */
17610 void QCPAxisRect::setRangeZoomFactor(double factor)
17611 {
17612 mRangeZoomFactorHorz = factor;
17613 mRangeZoomFactorVert = factor;
17614 }
17615
17616 /*! \internal
17617
17618 Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
17619 pixmap.
17620
17621 If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
17622 according filling inside the axis rect with the provided \a painter.
17623
17624 Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
17625 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
17626 the axis rect with the provided \a painter. The scaled version is buffered in
17627 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
17628 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
17629 dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
17630 set.
17631
17632 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
17633 */
17634 void QCPAxisRect::drawBackground(QCPPainter *painter)
17635 {
17636 // draw background fill:
17637 if (mBackgroundBrush != Qt::NoBrush)
17638 painter->fillRect(mRect, mBackgroundBrush);
17639
17640 // draw background pixmap (on top of fill, if brush specified):
17641 if (!mBackgroundPixmap.isNull())
17642 {
17643 if (mBackgroundScaled)
17644 {
17645 // check whether mScaledBackground needs to be updated:
17646 QSize scaledSize(mBackgroundPixmap.size());
17647 scaledSize.scale(mRect.size(), mBackgroundScaledMode);
17648 if (mScaledBackgroundPixmap.size() != scaledSize)
17649 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
17650 painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
17651 } else
17652 {
17653 painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
17654 }
17655 }
17656 }
17657
17658 /*! \internal
17659
17660 This function makes sure multiple axes on the side specified with \a type don't collide, but are
17661 distributed according to their respective space requirement (QCPAxis::calculateMargin).
17662
17663 It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
17664 one with index zero.
17665
17666 This function is called by \ref calculateAutoMargin.
17667 */
17668 void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
17669 {
17670 const QList<QCPAxis*> axesList = mAxes.value(type);
17671 for (int i=1; i<axesList.size(); ++i)
17672 axesList.at(i)->setOffset(axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin() + axesList.at(i)->tickLengthIn());
17673 }
17674
17675 /* inherits documentation from base class */
17676 int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
17677 {
17678 if (!mAutoMargins.testFlag(side))
17679 qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
17680
17681 updateAxesOffset(QCPAxis::marginSideToAxisType(side));
17682
17683 // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
17684 const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
17685 if (axesList.size() > 0)
17686 return axesList.last()->offset() + axesList.last()->calculateMargin();
17687 else
17688 return 0;
17689 }
17690
17691 /*! \internal
17692
17693 Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
17694 pressed, the range dragging interaction is initialized (the actual range manipulation happens in
17695 the \ref mouseMoveEvent).
17696
17697 The mDragging flag is set to true and some anchor points are set that are needed to determine the
17698 distance the mouse was dragged in the mouse move/release events later.
17699
17700 \see mouseMoveEvent, mouseReleaseEvent
17701 */
17702 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
17703 {
17704 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)
17705 if (event->buttons() & Qt::LeftButton)
17706 {
17707 mDragging = true;
17708 // initialize antialiasing backup in case we start dragging:
17709 if (mParentPlot->noAntialiasingOnDrag())
17710 {
17711 mAADragBackup = mParentPlot->antialiasedElements();
17712 mNotAADragBackup = mParentPlot->notAntialiasedElements();
17713 }
17714 // Mouse range dragging interaction:
17715 if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17716 {
17717 if (mRangeDragHorzAxis)
17718 mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
17719 if (mRangeDragVertAxis)
17720 mDragStartVertRange = mRangeDragVertAxis.data()->range();
17721 }
17722 }
17723 }
17724
17725 /*! \internal
17726
17727 Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
17728 preceding \ref mousePressEvent, the range is moved accordingly.
17729
17730 \see mousePressEvent, mouseReleaseEvent
17731 */
17732 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
17733 {
17734 // Mouse range dragging interaction:
17735 if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17736 {
17737 if (mRangeDrag.testFlag(Qt::Horizontal))
17738 {
17739 if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
17740 {
17741 if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
17742 {
17743 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
17744 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
17745 } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
17746 {
17747 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
17748 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
17749 }
17750 }
17751 }
17752 if (mRangeDrag.testFlag(Qt::Vertical))
17753 {
17754 if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
17755 {
17756 if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
17757 {
17758 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
17759 rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
17760 } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
17761 {
17762 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
17763 rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
17764 }
17765 }
17766 }
17767 if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
17768 {
17769 if (mParentPlot->noAntialiasingOnDrag())
17770 mParentPlot->setNotAntialiasedElements(QCP::aeAll);
17771 mParentPlot->replot();
17772 }
17773 }
17774 }
17775
17776 /* inherits documentation from base class */
17777 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
17778 {
17779 Q_UNUSED(event)
17780 mDragging = false;
17781 if (mParentPlot->noAntialiasingOnDrag())
17782 {
17783 mParentPlot->setAntialiasedElements(mAADragBackup);
17784 mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
17785 }
17786 }
17787
17788 /*! \internal
17789
17790 Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
17791 ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
17792 the scaling operation is the current cursor position inside the axis rect. The scaling factor is
17793 dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
17794 zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
17795
17796 Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
17797 wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
17798 multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
17799 exponent of the range zoom factor. This takes care of the wheel direction automatically, by
17800 inverting the factor, when the wheel step is negative (f^-1 = 1/f).
17801 */
17802 void QCPAxisRect::wheelEvent(QWheelEvent *event)
17803 {
17804 // Mouse range zooming interaction:
17805 if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
17806 {
17807 if (mRangeZoom != 0)
17808 {
17809 double factor;
17810 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
17811 if (mRangeZoom.testFlag(Qt::Horizontal))
17812 {
17813 factor = pow(mRangeZoomFactorHorz, wheelSteps);
17814 if (mRangeZoomHorzAxis.data())
17815 mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
17816 }
17817 if (mRangeZoom.testFlag(Qt::Vertical))
17818 {
17819 factor = pow(mRangeZoomFactorVert, wheelSteps);
17820 if (mRangeZoomVertAxis.data())
17821 mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
17822 }
17823 mParentPlot->replot();
17824 }
17825 }
17826 }
17827
17828
17829 ////////////////////////////////////////////////////////////////////////////////////////////////////
17830 //////////////////// QCPAbstractLegendItem
17831 ////////////////////////////////////////////////////////////////////////////////////////////////////
17832
17833 /*! \class QCPAbstractLegendItem
17834 \brief The abstract base class for all entries in a QCPLegend.
17835
17836 It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
17837 legend, the subclass \ref QCPPlottableLegendItem is more suitable.
17838
17839 Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
17840 that's not even associated with a plottable).
17841
17842 You must implement the following pure virtual functions:
17843 \li \ref draw (from QCPLayerable)
17844
17845 You inherit the following members you may use:
17846 <table>
17847 <tr>
17848 <td>QCPLegend *\b mParentLegend</td>
17849 <td>A pointer to the parent QCPLegend.</td>
17850 </tr><tr>
17851 <td>QFont \b mFont</td>
17852 <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
17853 </tr>
17854 </table>
17855 */
17856
17857 /* start of documentation of signals */
17858
17859 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
17860
17861 This signal is emitted when the selection state of this legend item has changed, either by user
17862 interaction or by a direct call to \ref setSelected.
17863 */
17864
17865 /* end of documentation of signals */
17866
17867 /*!
17868 Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
17869 cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
17870 */
17871 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
17872 QCPLayoutElement(parent->parentPlot()),
17873 mParentLegend(parent),
17874 mFont(parent->font()),
17875 mTextColor(parent->textColor()),
17876 mSelectedFont(parent->selectedFont()),
17877 mSelectedTextColor(parent->selectedTextColor()),
17878 mSelectable(true),
17879 mSelected(false)
17880 {
17881 setLayer("legend");
17882 setMargins(QMargins(8, 2, 8, 2));
17883 }
17884
17885 /*!
17886 Sets the default font of this specific legend item to \a font.
17887
17888 \see setTextColor, QCPLegend::setFont
17889 */
17890 void QCPAbstractLegendItem::setFont(const QFont &font)
17891 {
17892 mFont = font;
17893 }
17894
17895 /*!
17896 Sets the default text color of this specific legend item to \a color.
17897
17898 \see setFont, QCPLegend::setTextColor
17899 */
17900 void QCPAbstractLegendItem::setTextColor(const QColor &color)
17901 {
17902 mTextColor = color;
17903 }
17904
17905 /*!
17906 When this legend item is selected, \a font is used to draw generic text, instead of the normal
17907 font set with \ref setFont.
17908
17909 \see setFont, QCPLegend::setSelectedFont
17910 */
17911 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
17912 {
17913 mSelectedFont = font;
17914 }
17915
17916 /*!
17917 When this legend item is selected, \a color is used to draw generic text, instead of the normal
17918 color set with \ref setTextColor.
17919
17920 \see setTextColor, QCPLegend::setSelectedTextColor
17921 */
17922 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
17923 {
17924 mSelectedTextColor = color;
17925 }
17926
17927 /*!
17928 Sets whether this specific legend item is selectable.
17929
17930 \see setSelectedParts, QCustomPlot::setInteractions
17931 */
17932 void QCPAbstractLegendItem::setSelectable(bool selectable)
17933 {
17934 mSelectable = selectable;
17935 }
17936
17937 /*!
17938 Sets whether this specific legend item is selected.
17939
17940 It is possible to set the selection state of this item by calling this function directly, even if
17941 setSelectable is set to false.
17942
17943 \see setSelectableParts, QCustomPlot::setInteractions
17944 */
17945 void QCPAbstractLegendItem::setSelected(bool selected)
17946 {
17947 if (mSelected != selected)
17948 {
17949 mSelected = selected;
17950 emit selectionChanged(mSelected);
17951 }
17952 }
17953
17954 /* inherits documentation from base class */
17955 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17956 {
17957 Q_UNUSED(details)
17958 if (!mParentPlot) return -1;
17959 if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
17960 return -1;
17961
17962 if (mRect.contains(pos.toPoint()))
17963 return mParentPlot->selectionTolerance()*0.99;
17964 else
17965 return -1;
17966 }
17967
17968 /* inherits documentation from base class */
17969 void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
17970 {
17971 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
17972 }
17973
17974 /* inherits documentation from base class */
17975 QRect QCPAbstractLegendItem::clipRect() const
17976 {
17977 return mOuterRect;
17978 }
17979
17980 /* inherits documentation from base class */
17981 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
17982 {
17983 Q_UNUSED(event)
17984 Q_UNUSED(details)
17985 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
17986 {
17987 bool selBefore = mSelected;
17988 setSelected(additive ? !mSelected : true);
17989 if (selectionStateChanged)
17990 *selectionStateChanged = mSelected != selBefore;
17991 }
17992 }
17993
17994 /* inherits documentation from base class */
17995 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
17996 {
17997 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
17998 {
17999 bool selBefore = mSelected;
18000 setSelected(false);
18001 if (selectionStateChanged)
18002 *selectionStateChanged = mSelected != selBefore;
18003 }
18004 }
18005
18006 ////////////////////////////////////////////////////////////////////////////////////////////////////
18007 //////////////////// QCPPlottableLegendItem
18008 ////////////////////////////////////////////////////////////////////////////////////////////////////
18009
18010 /*! \class QCPPlottableLegendItem
18011 \brief A legend item representing a plottable with an icon and the plottable name.
18012
18013 This is the standard legend item for plottables. It displays an icon of the plottable next to the
18014 plottable name. The icon is drawn by the respective plottable itself (\ref
18015 QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
18016 For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
18017 middle.
18018
18019 Legend items of this type are always associated with one plottable (retrievable via the
18020 plottable() function and settable with the constructor). You may change the font of the plottable
18021 name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
18022 QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
18023
18024 The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
18025 creates/removes legend items of this type in the default implementation. However, these functions
18026 may be reimplemented such that a different kind of legend item (e.g a direct subclass of
18027 QCPAbstractLegendItem) is used for that plottable.
18028
18029 Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
18030 QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
18031 interface, QCPLegend has specialized functions for handling legend items conveniently, see the
18032 documentation of \ref QCPLegend.
18033 */
18034
18035 /*!
18036 Creates a new legend item associated with \a plottable.
18037
18038 Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
18039
18040 A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
18041 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
18042 */
18043 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
18044 QCPAbstractLegendItem(parent),
18045 mPlottable(plottable)
18046 {
18047 }
18048
18049 /*! \internal
18050
18051 Returns the pen that shall be used to draw the icon border, taking into account the selection
18052 state of this item.
18053 */
18054 QPen QCPPlottableLegendItem::getIconBorderPen() const
18055 {
18056 return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
18057 }
18058
18059 /*! \internal
18060
18061 Returns the text color that shall be used to draw text, taking into account the selection state
18062 of this item.
18063 */
18064 QColor QCPPlottableLegendItem::getTextColor() const
18065 {
18066 return mSelected ? mSelectedTextColor : mTextColor;
18067 }
18068
18069 /*! \internal
18070
18071 Returns the font that shall be used to draw text, taking into account the selection state of this
18072 item.
18073 */
18074 QFont QCPPlottableLegendItem::getFont() const
18075 {
18076 return mSelected ? mSelectedFont : mFont;
18077 }
18078
18079 /*! \internal
18080
18081 Draws the item with \a painter. The size and position of the drawn legend item is defined by the
18082 parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
18083 of this legend item.
18084 */
18085 void QCPPlottableLegendItem::draw(QCPPainter *painter)
18086 {
18087 if (!mPlottable) return;
18088 painter->setFont(getFont());
18089 painter->setPen(QPen(getTextColor()));
18090 QSizeF iconSize = mParentLegend->iconSize();
18091 QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18092 QRectF iconRect(mRect.topLeft(), iconSize);
18093 int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
18094 painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
18095 // draw icon:
18096 painter->save();
18097 painter->setClipRect(iconRect, Qt::IntersectClip);
18098 mPlottable->drawLegendIcon(painter, iconRect);
18099 painter->restore();
18100 // draw icon border:
18101 if (getIconBorderPen().style() != Qt::NoPen)
18102 {
18103 painter->setPen(getIconBorderPen());
18104 painter->setBrush(Qt::NoBrush);
18105 painter->drawRect(iconRect);
18106 }
18107 }
18108
18109 /*! \internal
18110
18111 Calculates and returns the size of this item. This includes the icon, the text and the padding in
18112 between.
18113 */
18114 QSize QCPPlottableLegendItem::minimumSizeHint() const
18115 {
18116 if (!mPlottable) return QSize();
18117 QSize result(0, 0);
18118 QRect textRect;
18119 QFontMetrics fontMetrics(getFont());
18120 QSize iconSize = mParentLegend->iconSize();
18121 textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18122 result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
18123 result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
18124 return result;
18125 }
18126
18127
18128 ////////////////////////////////////////////////////////////////////////////////////////////////////
18129 //////////////////// QCPLegend
18130 ////////////////////////////////////////////////////////////////////////////////////////////////////
18131
18132 /*! \class QCPLegend
18133 \brief Manages a legend inside a QCustomPlot.
18134
18135 A legend is a small box somewhere in the plot which lists plottables with their name and icon.
18136
18137 Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
18138 respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
18139 QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
18140 itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
18141
18142 The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
18143 QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
18144 placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
18145 handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
18146 other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
18147 However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
18148 only return the number of items with QCPAbstractLegendItems type).
18149
18150 By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
18151 layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
18152 position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
18153 outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
18154 */
18155
18156 /* start of documentation of signals */
18157
18158 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
18159
18160 This signal is emitted when the selection state of this legend has changed.
18161
18162 \see setSelectedParts, setSelectableParts
18163 */
18164
18165 /* end of documentation of signals */
18166
18167 /*!
18168 Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
18169
18170 Note that by default, QCustomPlot already contains a legend ready to be used as
18171 QCustomPlot::legend
18172 */
18173 QCPLegend::QCPLegend()
18174 {
18175 setRowSpacing(0);
18176 setColumnSpacing(10);
18177 setMargins(QMargins(2, 3, 2, 2));
18178 setAntialiased(false);
18179 setIconSize(32, 18);
18180
18181 setIconTextPadding(7);
18182
18183 setSelectableParts(spLegendBox | spItems);
18184 setSelectedParts(spNone);
18185
18186 setBorderPen(QPen(Qt::black));
18187 setSelectedBorderPen(QPen(Qt::blue, 2));
18188 setIconBorderPen(Qt::NoPen);
18189 setSelectedIconBorderPen(QPen(Qt::blue, 2));
18190 setBrush(Qt::white);
18191 setSelectedBrush(Qt::white);
18192 setTextColor(Qt::black);
18193 setSelectedTextColor(Qt::blue);
18194 }
18195
18196 QCPLegend::~QCPLegend()
18197 {
18198 clearItems();
18199 if (mParentPlot)
18200 mParentPlot->legendRemoved(this);
18201 }
18202
18203 /* no doc for getter, see setSelectedParts */
18204 QCPLegend::SelectableParts QCPLegend::selectedParts() const
18205 {
18206 // check whether any legend elements selected, if yes, add spItems to return value
18207 bool hasSelectedItems = false;
18208 for (int i=0; i<itemCount(); ++i)
18209 {
18210 if (item(i) && item(i)->selected())
18211 {
18212 hasSelectedItems = true;
18213 break;
18214 }
18215 }
18216 if (hasSelectedItems)
18217 return mSelectedParts | spItems;
18218 else
18219 return mSelectedParts & ~spItems;
18220 }
18221
18222 /*!
18223 Sets the pen, the border of the entire legend is drawn with.
18224 */
18225 void QCPLegend::setBorderPen(const QPen &pen)
18226 {
18227 mBorderPen = pen;
18228 }
18229
18230 /*!
18231 Sets the brush of the legend background.
18232 */
18233 void QCPLegend::setBrush(const QBrush &brush)
18234 {
18235 mBrush = brush;
18236 }
18237
18238 /*!
18239 Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
18240 use this font by default. However, a different font can be specified on a per-item-basis by
18241 accessing the specific legend item.
18242
18243 This function will also set \a font on all already existing legend items.
18244
18245 \see QCPAbstractLegendItem::setFont
18246 */
18247 void QCPLegend::setFont(const QFont &font)
18248 {
18249 mFont = font;
18250 for (int i=0; i<itemCount(); ++i)
18251 {
18252 if (item(i))
18253 item(i)->setFont(mFont);
18254 }
18255 }
18256
18257 /*!
18258 Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
18259 will use this color by default. However, a different colors can be specified on a per-item-basis
18260 by accessing the specific legend item.
18261
18262 This function will also set \a color on all already existing legend items.
18263
18264 \see QCPAbstractLegendItem::setTextColor
18265 */
18266 void QCPLegend::setTextColor(const QColor &color)
18267 {
18268 mTextColor = color;
18269 for (int i=0; i<itemCount(); ++i)
18270 {
18271 if (item(i))
18272 item(i)->setTextColor(color);
18273 }
18274 }
18275
18276 /*!
18277 Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
18278 representation of the graph) will use this size by default.
18279 */
18280 void QCPLegend::setIconSize(const QSize &size)
18281 {
18282 mIconSize = size;
18283 }
18284
18285 /*! \overload
18286 */
18287 void QCPLegend::setIconSize(int width, int height)
18288 {
18289 mIconSize.setWidth(width);
18290 mIconSize.setHeight(height);
18291 }
18292
18293 /*!
18294 Sets the horizontal space in pixels between the legend icon and the text next to it.
18295 Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
18296 name of the graph) will use this space by default.
18297 */
18298 void QCPLegend::setIconTextPadding(int padding)
18299 {
18300 mIconTextPadding = padding;
18301 }
18302
18303 /*!
18304 Sets the pen used to draw a border around each legend icon. Legend items that draw an
18305 icon (e.g. a visual representation of the graph) will use this pen by default.
18306
18307 If no border is wanted, set this to \a Qt::NoPen.
18308 */
18309 void QCPLegend::setIconBorderPen(const QPen &pen)
18310 {
18311 mIconBorderPen = pen;
18312 }
18313
18314 /*!
18315 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
18316 (When \ref QCustomPlot::setInteractions contains iSelectLegend.)
18317
18318 However, even when \a selectable is set to a value not allowing the selection of a specific part,
18319 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
18320 directly.
18321
18322 \see SelectablePart, setSelectedParts
18323 */
18324 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
18325 {
18326 mSelectableParts = selectable;
18327 }
18328
18329 /*!
18330 Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
18331 is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
18332 doesn't contain \ref spItems, those items become deselected.
18333
18334 The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
18335 contains iSelectLegend. You only need to call this function when you wish to change the selection
18336 state manually.
18337
18338 This function can change the selection state of a part even when \ref setSelectableParts was set to a
18339 value that actually excludes the part.
18340
18341 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
18342
18343 Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
18344 before, because there's no way to specify which exact items to newly select. Do this by calling
18345 \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
18346
18347 \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
18348 setSelectedFont
18349 */
18350 void QCPLegend::setSelectedParts(const SelectableParts &selected)
18351 {
18352 SelectableParts newSelected = selected;
18353 mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
18354
18355 if (mSelectedParts != newSelected)
18356 {
18357 if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
18358 {
18359 qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
18360 newSelected &= ~spItems;
18361 }
18362 if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
18363 {
18364 for (int i=0; i<itemCount(); ++i)
18365 {
18366 if (item(i))
18367 item(i)->setSelected(false);
18368 }
18369 }
18370 mSelectedParts = newSelected;
18371 emit selectionChanged(mSelectedParts);
18372 }
18373 }
18374
18375 /*!
18376 When the legend box is selected, this pen is used to draw the border instead of the normal pen
18377 set via \ref setBorderPen.
18378
18379 \see setSelectedParts, setSelectableParts, setSelectedBrush
18380 */
18381 void QCPLegend::setSelectedBorderPen(const QPen &pen)
18382 {
18383 mSelectedBorderPen = pen;
18384 }
18385
18386 /*!
18387 Sets the pen legend items will use to draw their icon borders, when they are selected.
18388
18389 \see setSelectedParts, setSelectableParts, setSelectedFont
18390 */
18391 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
18392 {
18393 mSelectedIconBorderPen = pen;
18394 }
18395
18396 /*!
18397 When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
18398 set via \ref setBrush.
18399
18400 \see setSelectedParts, setSelectableParts, setSelectedBorderPen
18401 */
18402 void QCPLegend::setSelectedBrush(const QBrush &brush)
18403 {
18404 mSelectedBrush = brush;
18405 }
18406
18407 /*!
18408 Sets the default font that is used by legend items when they are selected.
18409
18410 This function will also set \a font on all already existing legend items.
18411
18412 \see setFont, QCPAbstractLegendItem::setSelectedFont
18413 */
18414 void QCPLegend::setSelectedFont(const QFont &font)
18415 {
18416 mSelectedFont = font;
18417 for (int i=0; i<itemCount(); ++i)
18418 {
18419 if (item(i))
18420 item(i)->setSelectedFont(font);
18421 }
18422 }
18423
18424 /*!
18425 Sets the default text color that is used by legend items when they are selected.
18426
18427 This function will also set \a color on all already existing legend items.
18428
18429 \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
18430 */
18431 void QCPLegend::setSelectedTextColor(const QColor &color)
18432 {
18433 mSelectedTextColor = color;
18434 for (int i=0; i<itemCount(); ++i)
18435 {
18436 if (item(i))
18437 item(i)->setSelectedTextColor(color);
18438 }
18439 }
18440
18441 /*!
18442 Returns the item with index \a i.
18443
18444 \see itemCount
18445 */
18446 QCPAbstractLegendItem *QCPLegend::item(int index) const
18447 {
18448 return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
18449 }
18450
18451 /*!
18452 Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
18453 If such an item isn't in the legend, returns 0.
18454
18455 \see hasItemWithPlottable
18456 */
18457 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
18458 {
18459 for (int i=0; i<itemCount(); ++i)
18460 {
18461 if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
18462 {
18463 if (pli->plottable() == plottable)
18464 return pli;
18465 }
18466 }
18467 return 0;
18468 }
18469
18470 /*!
18471 Returns the number of items currently in the legend.
18472 \see item
18473 */
18474 int QCPLegend::itemCount() const
18475 {
18476 return elementCount();
18477 }
18478
18479 /*!
18480 Returns whether the legend contains \a itm.
18481 */
18482 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
18483 {
18484 for (int i=0; i<itemCount(); ++i)
18485 {
18486 if (item == this->item(i))
18487 return true;
18488 }
18489 return false;
18490 }
18491
18492 /*!
18493 Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
18494 If such an item isn't in the legend, returns false.
18495
18496 \see itemWithPlottable
18497 */
18498 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
18499 {
18500 return itemWithPlottable(plottable);
18501 }
18502
18503 /*!
18504 Adds \a item to the legend, if it's not present already.
18505
18506 Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
18507
18508 The legend takes ownership of the item.
18509 */
18510 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
18511 {
18512 if (!hasItem(item))
18513 {
18514 return addElement(rowCount(), 0, item);
18515 } else
18516 return false;
18517 }
18518
18519 /*!
18520 Removes the item with index \a index from the legend.
18521
18522 Returns true, if successful.
18523
18524 \see itemCount, clearItems
18525 */
18526 bool QCPLegend::removeItem(int index)
18527 {
18528 if (QCPAbstractLegendItem *ali = item(index))
18529 {
18530 bool success = remove(ali);
18531 simplify();
18532 return success;
18533 } else
18534 return false;
18535 }
18536
18537 /*! \overload
18538
18539 Removes \a item from the legend.
18540
18541 Returns true, if successful.
18542
18543 \see clearItems
18544 */
18545 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
18546 {
18547 bool success = remove(item);
18548 simplify();
18549 return success;
18550 }
18551
18552 /*!
18553 Removes all items from the legend.
18554 */
18555 void QCPLegend::clearItems()
18556 {
18557 for (int i=itemCount()-1; i>=0; --i)
18558 removeItem(i);
18559 }
18560
18561 /*!
18562 Returns the legend items that are currently selected. If no items are selected,
18563 the list is empty.
18564
18565 \see QCPAbstractLegendItem::setSelected, setSelectable
18566 */
18567 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
18568 {
18569 QList<QCPAbstractLegendItem*> result;
18570 for (int i=0; i<itemCount(); ++i)
18571 {
18572 if (QCPAbstractLegendItem *ali = item(i))
18573 {
18574 if (ali->selected())
18575 result.append(ali);
18576 }
18577 }
18578 return result;
18579 }
18580
18581 /*! \internal
18582
18583 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
18584 before drawing main legend elements.
18585
18586 This is the antialiasing state the painter passed to the \ref draw method is in by default.
18587
18588 This function takes into account the local setting of the antialiasing flag as well as the
18589 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
18590 QCustomPlot::setNotAntialiasedElements.
18591
18592 \see setAntialiased
18593 */
18594 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
18595 {
18596 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
18597 }
18598
18599 /*! \internal
18600
18601 Returns the pen used to paint the border of the legend, taking into account the selection state
18602 of the legend box.
18603 */
18604 QPen QCPLegend::getBorderPen() const
18605 {
18606 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
18607 }
18608
18609 /*! \internal
18610
18611 Returns the brush used to paint the background of the legend, taking into account the selection
18612 state of the legend box.
18613 */
18614 QBrush QCPLegend::getBrush() const
18615 {
18616 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
18617 }
18618
18619 /*! \internal
18620
18621 Draws the legend box with the provided \a painter. The individual legend items are layerables
18622 themselves, thus are drawn independently.
18623 */
18624 void QCPLegend::draw(QCPPainter *painter)
18625 {
18626 // draw background rect:
18627 painter->setBrush(getBrush());
18628 painter->setPen(getBorderPen());
18629 painter->drawRect(mOuterRect);
18630 }
18631
18632 /* inherits documentation from base class */
18633 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18634 {
18635 if (!mParentPlot) return -1;
18636 if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
18637 return -1;
18638
18639 if (mOuterRect.contains(pos.toPoint()))
18640 {
18641 if (details) details->setValue(spLegendBox);
18642 return mParentPlot->selectionTolerance()*0.99;
18643 }
18644 return -1;
18645 }
18646
18647 /* inherits documentation from base class */
18648 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18649 {
18650 Q_UNUSED(event)
18651 mSelectedParts = selectedParts(); // in case item selection has changed
18652 if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
18653 {
18654 SelectableParts selBefore = mSelectedParts;
18655 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)
18656 if (selectionStateChanged)
18657 *selectionStateChanged = mSelectedParts != selBefore;
18658 }
18659 }
18660
18661 /* inherits documentation from base class */
18662 void QCPLegend::deselectEvent(bool *selectionStateChanged)
18663 {
18664 mSelectedParts = selectedParts(); // in case item selection has changed
18665 if (mSelectableParts.testFlag(spLegendBox))
18666 {
18667 SelectableParts selBefore = mSelectedParts;
18668 setSelectedParts(selectedParts() & ~spLegendBox);
18669 if (selectionStateChanged)
18670 *selectionStateChanged = mSelectedParts != selBefore;
18671 }
18672 }
18673
18674 /* inherits documentation from base class */
18675 QCP::Interaction QCPLegend::selectionCategory() const
18676 {
18677 return QCP::iSelectLegend;
18678 }
18679
18680 /* inherits documentation from base class */
18681 QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
18682 {
18683 return QCP::iSelectLegend;
18684 }
18685
18686 /* inherits documentation from base class */
18687 void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
18688 {
18689 Q_UNUSED(parentPlot)
18690 }
18691
18692
18693 ////////////////////////////////////////////////////////////////////////////////////////////////////
18694 //////////////////// QCPPlotTitle
18695 ////////////////////////////////////////////////////////////////////////////////////////////////////
18696
18697 /*! \class QCPPlotTitle
18698 \brief A layout element displaying a plot title text
18699
18700 The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
18701 and \ref setTextColor.
18702
18703 A plot title can be added as follows:
18704 \code
18705 customPlot->plotLayout()->insertRow(0); // inserts an empty row above the default axis rect
18706 customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Your Plot Title"));
18707 \endcode
18708
18709 Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
18710 easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
18711 signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
18712 QCustomPlot::titleDoubleClick signal.
18713 */
18714
18715 /* start documentation of signals */
18716
18717 /*! \fn void QCPPlotTitle::selectionChanged(bool selected)
18718
18719 This signal is emitted when the selection state has changed to \a selected, either by user
18720 interaction or by a direct call to \ref setSelected.
18721
18722 \see setSelected, setSelectable
18723 */
18724
18725 /* end documentation of signals */
18726
18727 /*!
18728 Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
18729
18730 To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
18731 */
18732 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
18733 QCPLayoutElement(parentPlot),
18734 mFont(QFont("sans serif", 13*1.5, QFont::Bold)),
18735 mTextColor(Qt::black),
18736 mSelectedFont(QFont("sans serif", 13*1.6, QFont::Bold)),
18737 mSelectedTextColor(Qt::blue),
18738 mSelectable(false),
18739 mSelected(false)
18740 {
18741 if (parentPlot)
18742 {
18743 setLayer(parentPlot->currentLayer());
18744 mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
18745 mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
18746 }
18747 setMargins(QMargins(5, 5, 5, 0));
18748 }
18749
18750 /*! \overload
18751
18752 Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
18753 */
18754 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
18755 QCPLayoutElement(parentPlot),
18756 mText(text),
18757 mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
18758 mTextColor(Qt::black),
18759 mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
18760 mSelectedTextColor(Qt::blue),
18761 mSelectable(false),
18762 mSelected(false)
18763 {
18764 setLayer("axes");
18765 setMargins(QMargins(5, 5, 5, 0));
18766 }
18767
18768 /*!
18769 Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
18770
18771 \see setFont, setTextColor
18772 */
18773 void QCPPlotTitle::setText(const QString &text)
18774 {
18775 mText = text;
18776 }
18777
18778 /*!
18779 Sets the \a font of the title text.
18780
18781 \see setTextColor, setSelectedFont
18782 */
18783 void QCPPlotTitle::setFont(const QFont &font)
18784 {
18785 mFont = font;
18786 }
18787
18788 /*!
18789 Sets the \a color of the title text.
18790
18791 \see setFont, setSelectedTextColor
18792 */
18793 void QCPPlotTitle::setTextColor(const QColor &color)
18794 {
18795 mTextColor = color;
18796 }
18797
18798 /*!
18799 Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
18800
18801 \see setFont
18802 */
18803 void QCPPlotTitle::setSelectedFont(const QFont &font)
18804 {
18805 mSelectedFont = font;
18806 }
18807
18808 /*!
18809 Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
18810
18811 \see setTextColor
18812 */
18813 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
18814 {
18815 mSelectedTextColor = color;
18816 }
18817
18818 /*!
18819 Sets whether the user may select this plot title to \a selectable.
18820
18821 Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
18822 programmatically via \ref setSelected.
18823 */
18824 void QCPPlotTitle::setSelectable(bool selectable)
18825 {
18826 mSelectable = selectable;
18827 }
18828
18829 /*!
18830 Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
18831 selectionChanged is emitted.
18832
18833 Note that this function can change the selection state independently of the current \ref
18834 setSelectable state.
18835 */
18836 void QCPPlotTitle::setSelected(bool selected)
18837 {
18838 if (mSelected != selected)
18839 {
18840 mSelected = selected;
18841 emit selectionChanged(mSelected);
18842 }
18843 }
18844
18845 /* inherits documentation from base class */
18846 void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
18847 {
18848 applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
18849 }
18850
18851 /* inherits documentation from base class */
18852 void QCPPlotTitle::draw(QCPPainter *painter)
18853 {
18854 painter->setFont(mainFont());
18855 painter->setPen(QPen(mainTextColor()));
18856 painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
18857 }
18858
18859 /* inherits documentation from base class */
18860 QSize QCPPlotTitle::minimumSizeHint() const
18861 {
18862 QFontMetrics metrics(mFont);
18863 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
18864 result.rwidth() += mMargins.left() + mMargins.right();
18865 result.rheight() += mMargins.top() + mMargins.bottom();
18866 return result;
18867 }
18868
18869 /* inherits documentation from base class */
18870 QSize QCPPlotTitle::maximumSizeHint() const
18871 {
18872 QFontMetrics metrics(mFont);
18873 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
18874 result.rheight() += mMargins.top() + mMargins.bottom();
18875 result.setWidth(QWIDGETSIZE_MAX);
18876 return result;
18877 }
18878
18879 /* inherits documentation from base class */
18880 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18881 {
18882 Q_UNUSED(event)
18883 Q_UNUSED(details)
18884 if (mSelectable)
18885 {
18886 bool selBefore = mSelected;
18887 setSelected(additive ? !mSelected : true);
18888 if (selectionStateChanged)
18889 *selectionStateChanged = mSelected != selBefore;
18890 }
18891 }
18892
18893 /* inherits documentation from base class */
18894 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
18895 {
18896 if (mSelectable)
18897 {
18898 bool selBefore = mSelected;
18899 setSelected(false);
18900 if (selectionStateChanged)
18901 *selectionStateChanged = mSelected != selBefore;
18902 }
18903 }
18904
18905 /* inherits documentation from base class */
18906 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18907 {
18908 Q_UNUSED(details)
18909 if (onlySelectable && !mSelectable)
18910 return -1;
18911
18912 if (mTextBoundingRect.contains(pos.toPoint()))
18913 return mParentPlot->selectionTolerance()*0.99;
18914 else
18915 return -1;
18916 }
18917
18918 /*! \internal
18919
18920 Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
18921 <tt>true</tt>, else mFont is returned.
18922 */
18923 QFont QCPPlotTitle::mainFont() const
18924 {
18925 return mSelected ? mSelectedFont : mFont;
18926 }
18927
18928 /*! \internal
18929
18930 Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
18931 <tt>true</tt>, else mTextColor is returned.
18932 */
18933 QColor QCPPlotTitle::mainTextColor() const
18934 {
18935 return mSelected ? mSelectedTextColor : mTextColor;
18936 }
18937
This diff has been collapsed as it changes many lines, (1481 lines changed) Show them Hide them
@@ -1,7 +1,7
1 1 /***************************************************************************
2 2 ** **
3 3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011, 2012, 2013 Emanuel Eichhammer **
4 ** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer **
5 5 ** **
6 6 ** This program is free software: you can redistribute it and/or modify **
7 7 ** it under the terms of the GNU General Public License as published by **
@@ -19,8 +19,8
19 19 ****************************************************************************
20 20 ** Author: Emanuel Eichhammer **
21 21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 09.12.13 **
23 ** Version: 1.1.1 **
22 ** Date: 14.03.14 **
23 ** Version: 1.2.0 **
24 24 ****************************************************************************/
25 25
26 26 #ifndef QCUSTOMPLOT_H
@@ -48,6 +48,7
48 48 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
49 49 # include <qnumeric.h>
50 50 # include <QPrinter>
51 # include <QPrintEngine>
51 52 #else
52 53 # include <QtNumeric>
53 54 # include <QtPrintSupport>
@@ -60,6 +61,7 class QCPLayoutElement;
60 61 class QCPLayout;
61 62 class QCPAxis;
62 63 class QCPAxisRect;
64 class QCPAxisPainterPrivate;
63 65 class QCPAbstractPlottable;
64 66 class QCPGraph;
65 67 class QCPAbstractItem;
@@ -68,6 +70,8 class QCPLayer;
68 70 class QCPPlotTitle;
69 71 class QCPLegend;
70 72 class QCPAbstractLegendItem;
73 class QCPColorMap;
74 class QCPColorScale;
71 75
72 76
73 77 /*! \file */
@@ -134,8 +138,8 Q_DECLARE_FLAGS(AntialiasedElements, Ant
134 138 enum PlottingHint { phNone = 0x000 ///< <tt>0x000</tt> No hints are set
135 139 ,phFastPolylines = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
136 140 ///< especially of the line segment joins. (Only relevant for solid line pens.)
137 ,phForceRepaint = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called. This is set by default
138 ///< on Windows-Systems to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
141 ,phForceRepaint = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called with parameter \ref QCustomPlot::rpHint.
142 ///< This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
139 143 ,phCacheLabels = 0x004 ///< <tt>0x004</tt> axis (tick) labels will be cached as pixmaps, increasing replot performance.
140 144 };
141 145 Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
@@ -312,10 +316,10 public:
312 316 Defines special modes the painter can operate in. They disable or enable certain subsets of features/fixes/workarounds,
313 317 depending on whether they are wanted on the respective output device.
314 318 */
315 enum PainterMode {pmDefault = 0x00 ///< <tt>0x00</tt> Default mode for painting on screen devices
316 ,pmVectorized = 0x01 ///< <tt>0x01</tt> Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes.
317 ,pmNoCaching = 0x02 ///< <tt>0x02</tt> Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels
318 ,pmNonCosmetic = 0x04 ///< <tt>0x04</tt> Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.)
319 enum PainterMode { pmDefault = 0x00 ///< <tt>0x00</tt> Default mode for painting on screen devices
320 ,pmVectorized = 0x01 ///< <tt>0x01</tt> Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes.
321 ,pmNoCaching = 0x02 ///< <tt>0x02</tt> Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels
322 ,pmNonCosmetic = 0x04 ///< <tt>0x04</tt> Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.)
319 323 };
320 324 Q_FLAGS(PainterMode PainterModes)
321 325 Q_DECLARE_FLAGS(PainterModes, PainterMode)
@@ -365,6 +369,7 class QCP_LIB_DECL QCPLayer : public QOb
365 369 Q_PROPERTY(QString name READ name)
366 370 Q_PROPERTY(int index READ index)
367 371 Q_PROPERTY(QList<QCPLayerable*> children READ children)
372 Q_PROPERTY(bool visible READ visible WRITE setVisible)
368 373 /// \endcond
369 374 public:
370 375 QCPLayer(QCustomPlot* parentPlot, const QString &layerName);
@@ -375,6 +380,10 public:
375 380 QString name() const { return mName; }
376 381 int index() const { return mIndex; }
377 382 QList<QCPLayerable*> children() const { return mChildren; }
383 bool visible() const { return mVisible; }
384
385 // setters:
386 void setVisible(bool visible);
378 387
379 388 protected:
380 389 // property members:
@@ -382,6 +391,7 protected:
382 391 QString mName;
383 392 int mIndex;
384 393 QList<QCPLayerable*> mChildren;
394 bool mVisible;
385 395
386 396 // non-virtual methods:
387 397 void addChild(QCPLayerable *layerable, bool prepend);
@@ -401,7 +411,7 class QCP_LIB_DECL QCPLayerable : public
401 411 Q_PROPERTY(bool visible READ visible WRITE setVisible)
402 412 Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)
403 413 Q_PROPERTY(QCPLayerable* parentLayerable READ parentLayerable)
404 Q_PROPERTY(QCPLayer* layer READ layer WRITE setLayer)
414 Q_PROPERTY(QCPLayer* layer READ layer WRITE setLayer NOTIFY layerChanged)
405 415 Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
406 416 /// \endcond
407 417 public:
@@ -417,7 +427,7 public:
417 427
418 428 // setters:
419 429 void setVisible(bool on);
420 bool setLayer(QCPLayer *layer);
430 Q_SLOT bool setLayer(QCPLayer *layer);
421 431 bool setLayer(const QString &layerName);
422 432 void setAntialiased(bool enabled);
423 433
@@ -427,6 +437,9 public:
427 437 // non-property methods:
428 438 bool realVisibility() const;
429 439
440 signals:
441 void layerChanged(QCPLayer *newLayer);
442
430 443 protected:
431 444 // property members:
432 445 bool mVisible;
@@ -467,6 +480,20 public:
467 480 QCPRange();
468 481 QCPRange(double lower, double upper);
469 482
483 bool operator==(const QCPRange& other) { return lower == other.lower && upper == other.upper; }
484 bool operator!=(const QCPRange& other) { return !(*this == other); }
485
486 QCPRange &operator+=(const double& value) { lower+=value; upper+=value; return *this; }
487 QCPRange &operator-=(const double& value) { lower-=value; upper-=value; return *this; }
488 QCPRange &operator*=(const double& value) { lower*=value; upper*=value; return *this; }
489 QCPRange &operator/=(const double& value) { lower/=value; upper/=value; return *this; }
490 friend inline const QCPRange operator+(const QCPRange&, double);
491 friend inline const QCPRange operator+(double, const QCPRange&);
492 friend inline const QCPRange operator-(const QCPRange& range, double value);
493 friend inline const QCPRange operator*(const QCPRange& range, double value);
494 friend inline const QCPRange operator*(double value, const QCPRange& range);
495 friend inline const QCPRange operator/(const QCPRange& range, double value);
496
470 497 double size() const;
471 498 double center() const;
472 499 void normalize();
@@ -480,9 +507,94 public:
480 507 static bool validRange(const QCPRange &range);
481 508 static const double minRange; //1e-280;
482 509 static const double maxRange; //1e280;
510
483 511 };
484 512 Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE);
485 513
514 /* documentation of inline functions */
515
516 /*! \fn QCPRange &QCPRange::operator+=(const double& value)
517
518 Adds \a value to both boundaries of the range.
519 */
520
521 /*! \fn QCPRange &QCPRange::operator-=(const double& value)
522
523 Subtracts \a value from both boundaries of the range.
524 */
525
526 /*! \fn QCPRange &QCPRange::operator*=(const double& value)
527
528 Multiplies both boundaries of the range by \a value.
529 */
530
531 /*! \fn QCPRange &QCPRange::operator/=(const double& value)
532
533 Divides both boundaries of the range by \a value.
534 */
535
536 /* end documentation of inline functions */
537
538 /*!
539 Adds \a value to both boundaries of the range.
540 */
541 inline const QCPRange operator+(const QCPRange& range, double value)
542 {
543 QCPRange result(range);
544 result += value;
545 return result;
546 }
547
548 /*!
549 Adds \a value to both boundaries of the range.
550 */
551 inline const QCPRange operator+(double value, const QCPRange& range)
552 {
553 QCPRange result(range);
554 result += value;
555 return result;
556 }
557
558 /*!
559 Subtracts \a value from both boundaries of the range.
560 */
561 inline const QCPRange operator-(const QCPRange& range, double value)
562 {
563 QCPRange result(range);
564 result -= value;
565 return result;
566 }
567
568 /*!
569 Multiplies both boundaries of the range by \a value.
570 */
571 inline const QCPRange operator*(const QCPRange& range, double value)
572 {
573 QCPRange result(range);
574 result *= value;
575 return result;
576 }
577
578 /*!
579 Multiplies both boundaries of the range by \a value.
580 */
581 inline const QCPRange operator*(double value, const QCPRange& range)
582 {
583 QCPRange result(range);
584 result *= value;
585 return result;
586 }
587
588 /*!
589 Divides both boundaries of the range by \a value.
590 */
591 inline const QCPRange operator/(const QCPRange& range, double value)
592 {
593 QCPRange result(range);
594 result /= value;
595 return result;
596 }
597
486 598
487 599 class QCP_LIB_DECL QCPMarginGroup : public QObject
488 600 {
@@ -526,6 +638,16 class QCP_LIB_DECL QCPLayoutElement : pu
526 638 Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
527 639 /// \endcond
528 640 public:
641 /*!
642 Defines the phases of the update process, that happens just before a replot. At each phase,
643 \ref update is called with the according UpdatePhase value.
644 */
645 enum UpdatePhase { upPreparation ///< Phase used for any type of preparation that needs to be done before margin calculation and layout
646 ,upMargins ///< Phase in which the margins are calculated and set
647 ,upLayout ///< Final phase in which the layout system places the rects of the elements
648 };
649 Q_ENUMS(UpdatePhase)
650
529 651 explicit QCPLayoutElement(QCustomPlot *parentPlot=0);
530 652 virtual ~QCPLayoutElement();
531 653
@@ -553,7 +675,7 public:
553 675 void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group);
554 676
555 677 // introduced virtual methods:
556 virtual void update();
678 virtual void update(UpdatePhase phase);
557 679 virtual QSize minimumSizeHint() const;
558 680 virtual QSize maximumSizeHint() const;
559 681 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
@@ -600,7 +722,7 public:
600 722 explicit QCPLayout();
601 723
602 724 // reimplemented virtual methods:
603 virtual void update();
725 virtual void update(UpdatePhase phase);
604 726 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
605 727
606 728 // introduced virtual methods:
@@ -704,8 +826,8 public:
704 826 /*!
705 827 Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset.
706 828 */
707 enum InsetPlacement {ipFree ///< The element may be positioned/sized arbitrarily, see \ref setInsetRect
708 ,ipBorderAligned ///< The element is aligned to one of the layout sides, see \ref setInsetAlignment
829 enum InsetPlacement { ipFree ///< The element may be positioned/sized arbitrarily, see \ref setInsetRect
830 ,ipBorderAligned ///< The element is aligned to one of the layout sides, see \ref setInsetAlignment
709 831 };
710 832
711 833 explicit QCPLayoutInset();
@@ -759,7 +881,7 public:
759 881 and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only
760 882 support a width, the length property is ignored.
761 883
762 \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail
884 \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail, QCPAxis::setLowerEnding, QCPAxis::setUpperEnding
763 885 */
764 886 Q_ENUMS(EndingStyle)
765 887 enum EndingStyle { esNone ///< No ending decoration
@@ -770,7 +892,7 public:
770 892 ,esSquare ///< A filled square
771 893 ,esDiamond ///< A filled diamond (45° rotated square)
772 894 ,esBar ///< A bar perpendicular to the line
773 ,esHalfBar ///< A bar perpendicular to the line sticking out to one side
895 ,esHalfBar ///< A bar perpendicular to the line, pointing out to only one side (to which side can be changed with \ref setInverted)
774 896 ,esSkewedBar ///< A bar that is skewed (skew controllable via \ref setLength)
775 897 };
776 898
@@ -860,9 +982,9 class QCP_LIB_DECL QCPAxis : public QCPL
860 982 /// \cond INCLUDE_QPROPERTIES
861 983 Q_PROPERTY(AxisType axisType READ axisType)
862 984 Q_PROPERTY(QCPAxisRect* axisRect READ axisRect)
863 Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType)
985 Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY scaleTypeChanged)
864 986 Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase)
865 Q_PROPERTY(QCPRange range READ range WRITE setRange)
987 Q_PROPERTY(QCPRange range READ range WRITE setRange NOTIFY rangeChanged)
866 988 Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)
867 989 Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
868 990 Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
@@ -897,8 +1019,8 class QCP_LIB_DECL QCPAxis : public QCPL
897 1019 Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
898 1020 Q_PROPERTY(int padding READ padding WRITE setPadding)
899 1021 Q_PROPERTY(int offset READ offset WRITE setOffset)
900 Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts)
901 Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts)
1022 Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged)
1023 Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged)
902 1024 Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont)
903 1025 Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)
904 1026 Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor)
@@ -953,7 +1075,8 public:
953 1075 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
954 1076
955 1077 explicit QCPAxis(QCPAxisRect *parent, AxisType type);
956
1078 virtual ~QCPAxis();
1079
957 1080 // getters:
958 1081 AxisType axisType() const { return mAxisType; }
959 1082 QCPAxisRect *axisRect() const { return mAxisRect; }
@@ -968,11 +1091,11 public:
968 1091 bool autoSubTicks() const { return mAutoSubTicks; }
969 1092 bool ticks() const { return mTicks; }
970 1093 bool tickLabels() const { return mTickLabels; }
971 int tickLabelPadding() const { return mTickLabelPadding; }
1094 int tickLabelPadding() const;
972 1095 LabelType tickLabelType() const { return mTickLabelType; }
973 1096 QFont tickLabelFont() const { return mTickLabelFont; }
974 1097 QColor tickLabelColor() const { return mTickLabelColor; }
975 double tickLabelRotation() const { return mTickLabelRotation; }
1098 double tickLabelRotation() const;
976 1099 QString dateTimeFormat() const { return mDateTimeFormat; }
977 1100 Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
978 1101 QString numberFormat() const;
@@ -980,20 +1103,20 public:
980 1103 double tickStep() const { return mTickStep; }
981 1104 QVector<double> tickVector() const { return mTickVector; }
982 1105 QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
983 int tickLengthIn() const { return mTickLengthIn; }
984 int tickLengthOut() const { return mTickLengthOut; }
1106 int tickLengthIn() const;
1107 int tickLengthOut() const;
985 1108 int subTickCount() const { return mSubTickCount; }
986 int subTickLengthIn() const { return mSubTickLengthIn; }
987 int subTickLengthOut() const { return mSubTickLengthOut; }
1109 int subTickLengthIn() const;
1110 int subTickLengthOut() const;
988 1111 QPen basePen() const { return mBasePen; }
989 1112 QPen tickPen() const { return mTickPen; }
990 1113 QPen subTickPen() const { return mSubTickPen; }
991 1114 QFont labelFont() const { return mLabelFont; }
992 1115 QColor labelColor() const { return mLabelColor; }
993 1116 QString label() const { return mLabel; }
994 int labelPadding() const { return mLabelPadding; }
1117 int labelPadding() const;
995 1118 int padding() const { return mPadding; }
996 int offset() const { return mOffset; }
1119 int offset() const;
997 1120 SelectableParts selectedParts() const { return mSelectedParts; }
998 1121 SelectableParts selectableParts() const { return mSelectableParts; }
999 1122 QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
@@ -1003,12 +1126,12 public:
1003 1126 QPen selectedBasePen() const { return mSelectedBasePen; }
1004 1127 QPen selectedTickPen() const { return mSelectedTickPen; }
1005 1128 QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
1006 QCPLineEnding lowerEnding() const { return mLowerEnding; }
1007 QCPLineEnding upperEnding() const { return mUpperEnding; }
1129 QCPLineEnding lowerEnding() const;
1130 QCPLineEnding upperEnding() const;
1008 1131 QCPGrid *grid() const { return mGrid; }
1009 1132
1010 1133 // setters:
1011 void setScaleType(ScaleType type);
1134 Q_SLOT void setScaleType(QCPAxis::ScaleType type);
1012 1135 void setScaleLogBase(double base);
1013 1136 Q_SLOT void setRange(const QCPRange &range);
1014 1137 void setRange(double lower, double upper);
@@ -1066,7 +1189,7 public:
1066 1189 // reimplemented virtual methods:
1067 1190 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
1068 1191
1069 // non-virtual methods:
1192 // non-property methods:
1070 1193 Qt::Orientation orientation() const { return mOrientation; }
1071 1194 void moveRange(double diff);
1072 1195 void scaleRange(double factor, double center);
@@ -1080,44 +1203,37 public:
1080 1203 QList<QCPAbstractItem*> items() const;
1081 1204
1082 1205 static AxisType marginSideToAxisType(QCP::MarginSide side);
1206 static Qt::Orientation orientation(AxisType type) { return type==atBottom||type==atTop ? Qt::Horizontal : Qt::Vertical; }
1207 static AxisType opposite(AxisType type);
1083 1208
1084 1209 signals:
1085 1210 void ticksRequest();
1086 1211 void rangeChanged(const QCPRange &newRange);
1087 1212 void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
1213 void scaleTypeChanged(QCPAxis::ScaleType scaleType);
1088 1214 void selectionChanged(const QCPAxis::SelectableParts &parts);
1215 void selectableChanged(const QCPAxis::SelectableParts &parts);
1089 1216
1090 1217 protected:
1091 struct CachedLabel
1092 {
1093 QPointF offset;
1094 QPixmap pixmap;
1095 };
1096 struct TickLabelData
1097 {
1098 QString basePart, expPart;
1099 QRect baseBounds, expBounds, totalBounds, rotatedTotalBounds;
1100 QFont baseFont, expFont;
1101 };
1102
1103 1218 // property members:
1104 1219 // axis base:
1105 1220 AxisType mAxisType;
1106 1221 QCPAxisRect *mAxisRect;
1107 int mOffset, mPadding;
1222 //int mOffset; // in QCPAxisPainter
1223 int mPadding;
1108 1224 Qt::Orientation mOrientation;
1109 1225 SelectableParts mSelectableParts, mSelectedParts;
1110 1226 QPen mBasePen, mSelectedBasePen;
1111 QCPLineEnding mLowerEnding, mUpperEnding;
1227 //QCPLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter
1112 1228 // axis label:
1113 int mLabelPadding;
1229 //int mLabelPadding; // in QCPAxisPainter
1114 1230 QString mLabel;
1115 1231 QFont mLabelFont, mSelectedLabelFont;
1116 1232 QColor mLabelColor, mSelectedLabelColor;
1117 1233 // tick labels:
1118 int mTickLabelPadding;
1234 //int mTickLabelPadding; // in QCPAxisPainter
1119 1235 bool mTickLabels, mAutoTickLabels;
1120 double mTickLabelRotation;
1236 //double mTickLabelRotation; // in QCPAxisPainter
1121 1237 LabelType mTickLabelType;
1122 1238 QFont mTickLabelFont, mSelectedTickLabelFont;
1123 1239 QColor mTickLabelColor, mSelectedTickLabelColor;
@@ -1126,13 +1242,13 protected:
1126 1242 int mNumberPrecision;
1127 1243 char mNumberFormatChar;
1128 1244 bool mNumberBeautifulPowers;
1129 bool mNumberMultiplyCross;
1245 //bool mNumberMultiplyCross; // QCPAxisPainter
1130 1246 // ticks and subticks:
1131 1247 bool mTicks;
1132 1248 double mTickStep;
1133 1249 int mSubTickCount, mAutoTickCount;
1134 1250 bool mAutoTicks, mAutoTickStep, mAutoSubTicks;
1135 int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;
1251 //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // QCPAxisPainter
1136 1252 QPen mTickPen, mSelectedTickPen;
1137 1253 QPen mSubTickPen, mSelectedSubTickPen;
1138 1254 // scale and range:
@@ -1143,13 +1259,11 protected:
1143 1259
1144 1260 // non-property members:
1145 1261 QCPGrid *mGrid;
1146 QCache<QString, CachedLabel> mLabelCache;
1262 QCPAxisPainterPrivate *mAxisPainter;
1147 1263 int mLowestVisibleTick, mHighestVisibleTick;
1148 QChar mExponentialChar, mPositiveSignChar;
1149 1264 QVector<double> mTickVector;
1150 1265 QVector<QString> mTickVectorLabels;
1151 1266 QVector<double> mSubTickVector;
1152 QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
1153 1267 bool mCachedMarginValid;
1154 1268 int mCachedMargin;
1155 1269
@@ -1158,16 +1272,10 protected:
1158 1272 virtual void generateAutoTicks();
1159 1273 virtual int calculateAutoSubTickCount(double tickStep) const;
1160 1274 virtual int calculateMargin();
1161 // tick label drawing/caching:
1162 virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);
1163 virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const;
1164 virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const;
1165 virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const;
1166 virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
1167 1275
1168 1276 // reimplemented virtual methods:
1169 1277 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
1170 virtual void draw(QCPPainter *painter);
1278 virtual void draw(QCPPainter *painter);
1171 1279 virtual QCP::Interaction selectionCategory() const;
1172 1280 // events:
1173 1281 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
@@ -1197,6 +1305,72 Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::A
1197 1305 Q_DECLARE_METATYPE(QCPAxis::SelectablePart)
1198 1306
1199 1307
1308 class QCPAxisPainterPrivate
1309 {
1310 public:
1311 explicit QCPAxisPainterPrivate(QCustomPlot *parentPlot);
1312 virtual ~QCPAxisPainterPrivate();
1313
1314 virtual void draw(QCPPainter *painter);
1315 virtual int size() const;
1316 void clearCache();
1317
1318 QRect axisSelectionBox() const { return mAxisSelectionBox; }
1319 QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; }
1320 QRect labelSelectionBox() const { return mLabelSelectionBox; }
1321
1322 // public property members:
1323 QCPAxis::AxisType type;
1324 QPen basePen;
1325 QCPLineEnding lowerEnding, upperEnding; // directly accessed by QCPAxis setters/getters
1326 int labelPadding; // directly accessed by QCPAxis setters/getters
1327 QFont labelFont;
1328 QColor labelColor;
1329 QString label;
1330 int tickLabelPadding; // directly accessed by QCPAxis setters/getters
1331 double tickLabelRotation; // directly accessed by QCPAxis setters/getters
1332 bool substituteExponent;
1333 bool numberMultiplyCross; // directly accessed by QCPAxis setters/getters
1334 int tickLengthIn, tickLengthOut, subTickLengthIn, subTickLengthOut; // directly accessed by QCPAxis setters/getters
1335 QPen tickPen, subTickPen;
1336 QFont tickLabelFont;
1337 QColor tickLabelColor;
1338 QRect alignmentRect, viewportRect;
1339 double offset; // directly accessed by QCPAxis setters/getters
1340 bool abbreviateDecimalPowers;
1341 bool reversedEndings;
1342
1343 QVector<double> subTickPositions;
1344 QVector<double> tickPositions;
1345 QVector<QString> tickLabels;
1346
1347 protected:
1348 struct CachedLabel
1349 {
1350 QPointF offset;
1351 QPixmap pixmap;
1352 };
1353 struct TickLabelData
1354 {
1355 QString basePart, expPart;
1356 QRect baseBounds, expBounds, totalBounds, rotatedTotalBounds;
1357 QFont baseFont, expFont;
1358 };
1359 QCustomPlot *mParentPlot;
1360 QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters
1361 QCache<QString, CachedLabel> mLabelCache;
1362 QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
1363
1364 virtual QByteArray generateLabelParameterHash() const;
1365
1366 virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);
1367 virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const;
1368 virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const;
1369 virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const;
1370 virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
1371 };
1372
1373
1200 1374 class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
1201 1375 {
1202 1376 Q_OBJECT
@@ -1211,8 +1385,8 class QCP_LIB_DECL QCPAbstractPlottable
1211 1385 Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
1212 1386 Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis)
1213 1387 Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis)
1214 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable)
1215 Q_PROPERTY(bool selected READ selected WRITE setSelected)
1388 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
1389 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
1216 1390 /// \endcond
1217 1391 public:
1218 1392 QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
@@ -1258,6 +1432,7 public:
1258 1432
1259 1433 signals:
1260 1434 void selectionChanged(bool selected);
1435 void selectableChanged(bool selectable);
1261 1436
1262 1437 protected:
1263 1438 /*!
@@ -1287,8 +1462,8 protected:
1287 1462
1288 1463 // introduced virtual methods:
1289 1464 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0;
1290 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
1291 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
1465 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
1466 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
1292 1467
1293 1468 // non-virtual methods:
1294 1469 void coordsToPixels(double key, double value, double &x, double &y) const;
@@ -1356,8 +1531,12 public:
1356 1531 \see setType
1357 1532 */
1358 1533 enum PositionType { ptAbsolute ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.
1359 ,ptViewportRatio ///< Static positioning given by a fraction of the viewport size.
1360 ,ptAxisRectRatio ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect).
1534 ,ptViewportRatio ///< Static positioning given by a fraction of the viewport size. For example, if you call setCoords(0, 0), the position will be at the top
1535 ///< left corner of the viewport/widget. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and
1536 ///< vertically at the top of the viewport/widget, etc.
1537 ,ptAxisRectRatio ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect). For example, if you call setCoords(0, 0), the position will be at the top
1538 ///< left corner of the axis rect. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and
1539 ///< vertically at the top of the axis rect, etc. You can also go beyond the axis rect by providing negative coordinates or coordinates larger than 1.
1361 1540 ,ptPlotCoords ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes).
1362 1541 };
1363 1542
@@ -1407,8 +1586,8 class QCP_LIB_DECL QCPAbstractItem : pub
1407 1586 /// \cond INCLUDE_QPROPERTIES
1408 1587 Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)
1409 1588 Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)
1410 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable)
1411 Q_PROPERTY(bool selected READ selected WRITE setSelected)
1589 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
1590 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
1412 1591 /// \endcond
1413 1592 public:
1414 1593 QCPAbstractItem(QCustomPlot *parentPlot);
@@ -1423,8 +1602,8 public:
1423 1602 // setters:
1424 1603 void setClipToAxisRect(bool clip);
1425 1604 void setClipAxisRect(QCPAxisRect *rect);
1426 void setSelectable(bool selectable);
1427 void setSelected(bool selected);
1605 Q_SLOT void setSelectable(bool selectable);
1606 Q_SLOT void setSelected(bool selected);
1428 1607
1429 1608 // reimplemented virtual methods:
1430 1609 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
@@ -1438,6 +1617,7 public:
1438 1617
1439 1618 signals:
1440 1619 void selectionChanged(bool selected);
1620 void selectableChanged(bool selectable);
1441 1621
1442 1622 protected:
1443 1623 // property members:
@@ -1498,6 +1678,16 public:
1498 1678 };
1499 1679 Q_ENUMS(LayerInsertMode)
1500 1680
1681 /*!
1682 Defines with what timing the QCustomPlot surface is refreshed after a replot.
1683
1684 \see replot
1685 */
1686 enum RefreshPriority { rpImmediate ///< The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot
1687 ,rpQueued ///< Queues the refresh such that it is performed at a slightly delayed point in time after the replot, by calling QWidget::update() after the replot
1688 ,rpHint ///< Whether to use immediate repaint or queued update depends on whether the plotting hint \ref QCP::phForceRepaint is set, see \ref setPlottingHints.
1689 };
1690
1501 1691 explicit QCustomPlot(QWidget *parent = 0);
1502 1692 virtual ~QCustomPlot();
1503 1693
@@ -1593,14 +1783,14 public:
1593 1783 QList<QCPLegend*> selectedLegends() const;
1594 1784 Q_SLOT void deselectAll();
1595 1785
1596 bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0);
1786 bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator="", const QString &pdfTitle="");
1597 1787 bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
1598 1788 bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
1599 1789 bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
1600 1790 bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
1601 1791 QPixmap toPixmap(int width=0, int height=0, double scale=1.0);
1602 1792 void toPainter(QCPPainter *painter, int width=0, int height=0);
1603 Q_SLOT void replot();
1793 Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint);
1604 1794
1605 1795 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
1606 1796 QCPLegend *legend;
@@ -1652,7 +1842,7 protected:
1652 1842 // non-property members:
1653 1843 QPixmap mPaintBuffer;
1654 1844 QPoint mMousePressPos;
1655 QCPLayoutElement *mMouseEventElement;
1845 QPointer<QCPLayoutElement> mMouseEventElement;
1656 1846 bool mReplotting;
1657 1847
1658 1848 // reimplemented virtual methods:
@@ -1683,6 +1873,569 protected:
1683 1873 };
1684 1874
1685 1875
1876 class QCP_LIB_DECL QCPColorGradient
1877 {
1878 Q_GADGET
1879 public:
1880 /*!
1881 Defines the color spaces in which color interpolation between gradient stops can be performed.
1882
1883 \see setColorInterpolation
1884 */
1885 enum ColorInterpolation { ciRGB ///< Color channels red, green and blue are linearly interpolated
1886 ,ciHSV ///< Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the shortest angle distance)
1887 };
1888 Q_ENUMS(ColorInterpolation)
1889
1890 /*!
1891 Defines the available presets that can be loaded with \ref loadPreset. See the documentation
1892 there for an image of the presets.
1893 */
1894 enum GradientPreset { gpGrayscale ///< Continuous lightness from black to white (suited for non-biased data representation)
1895 ,gpHot ///< Continuous lightness from black over firey colors to white (suited for non-biased data representation)
1896 ,gpCold ///< Continuous lightness from black over icey colors to white (suited for non-biased data representation)
1897 ,gpNight ///< Continuous lightness from black over weak blueish colors to white (suited for non-biased data representation)
1898 ,gpCandy ///< Blue over pink to white
1899 ,gpGeography ///< Colors suitable to represent different elevations on geographical maps
1900 ,gpIon ///< Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allows more precise magnitude estimates)
1901 ,gpThermal ///< Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white
1902 ,gpPolar ///< Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle and red for positive values
1903 ,gpSpectrum ///< An approximation of the visible light spectrum (creates banding illusion but allows more precise magnitude estimates)
1904 ,gpJet ///< Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion but allows more precise magnitude estimates)
1905 ,gpHues ///< Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and phases, see \ref setPeriodic)
1906 };
1907 Q_ENUMS(GradientPreset)
1908
1909 QCPColorGradient(GradientPreset preset=gpCold);
1910 bool operator==(const QCPColorGradient &other) const;
1911 bool operator!=(const QCPColorGradient &other) const { return !(*this == other); }
1912
1913 // getters:
1914 int levelCount() const { return mLevelCount; }
1915 QMap<double, QColor> colorStops() const { return mColorStops; }
1916 ColorInterpolation colorInterpolation() const { return mColorInterpolation; }
1917 bool periodic() const { return mPeriodic; }
1918
1919 // setters:
1920 void setLevelCount(int n);
1921 void setColorStops(const QMap<double, QColor> &colorStops);
1922 void setColorStopAt(double position, const QColor &color);
1923 void setColorInterpolation(ColorInterpolation interpolation);
1924 void setPeriodic(bool enabled);
1925
1926 // non-property methods:
1927 void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false);
1928 QRgb color(double position, const QCPRange &range, bool logarithmic=false);
1929 void loadPreset(GradientPreset preset);
1930 void clearColorStops();
1931 QCPColorGradient inverted() const;
1932
1933 protected:
1934 void updateColorBuffer();
1935
1936 // property members:
1937 int mLevelCount;
1938 QMap<double, QColor> mColorStops;
1939 ColorInterpolation mColorInterpolation;
1940 bool mPeriodic;
1941
1942 // non-property members:
1943 QVector<QRgb> mColorBuffer;
1944 bool mColorBufferInvalidated;
1945 };
1946
1947
1948 class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement
1949 {
1950 Q_OBJECT
1951 /// \cond INCLUDE_QPROPERTIES
1952 Q_PROPERTY(QPixmap background READ background WRITE setBackground)
1953 Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
1954 Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)
1955 Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
1956 Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
1957 /// \endcond
1958 public:
1959 explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true);
1960 virtual ~QCPAxisRect();
1961
1962 // getters:
1963 QPixmap background() const { return mBackgroundPixmap; }
1964 bool backgroundScaled() const { return mBackgroundScaled; }
1965 Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
1966 Qt::Orientations rangeDrag() const { return mRangeDrag; }
1967 Qt::Orientations rangeZoom() const { return mRangeZoom; }
1968 QCPAxis *rangeDragAxis(Qt::Orientation orientation);
1969 QCPAxis *rangeZoomAxis(Qt::Orientation orientation);
1970 double rangeZoomFactor(Qt::Orientation orientation);
1971
1972 // setters:
1973 void setBackground(const QPixmap &pm);
1974 void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
1975 void setBackground(const QBrush &brush);
1976 void setBackgroundScaled(bool scaled);
1977 void setBackgroundScaledMode(Qt::AspectRatioMode mode);
1978 void setRangeDrag(Qt::Orientations orientations);
1979 void setRangeZoom(Qt::Orientations orientations);
1980 void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical);
1981 void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical);
1982 void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
1983 void setRangeZoomFactor(double factor);
1984
1985 // non-property methods:
1986 int axisCount(QCPAxis::AxisType type) const;
1987 QCPAxis *axis(QCPAxis::AxisType type, int index=0) const;
1988 QList<QCPAxis*> axes(QCPAxis::AxisTypes types) const;
1989 QList<QCPAxis*> axes() const;
1990 QCPAxis *addAxis(QCPAxis::AxisType type);
1991 QList<QCPAxis*> addAxes(QCPAxis::AxisTypes types);
1992 bool removeAxis(QCPAxis *axis);
1993 QCPLayoutInset *insetLayout() const { return mInsetLayout; }
1994
1995 void setupFullAxesBox(bool connectRanges=false);
1996 QList<QCPAbstractPlottable*> plottables() const;
1997 QList<QCPGraph*> graphs() const;
1998 QList<QCPAbstractItem*> items() const;
1999
2000 // read-only interface imitating a QRect:
2001 int left() const { return mRect.left(); }
2002 int right() const { return mRect.right(); }
2003 int top() const { return mRect.top(); }
2004 int bottom() const { return mRect.bottom(); }
2005 int width() const { return mRect.width(); }
2006 int height() const { return mRect.height(); }
2007 QSize size() const { return mRect.size(); }
2008 QPoint topLeft() const { return mRect.topLeft(); }
2009 QPoint topRight() const { return mRect.topRight(); }
2010 QPoint bottomLeft() const { return mRect.bottomLeft(); }
2011 QPoint bottomRight() const { return mRect.bottomRight(); }
2012 QPoint center() const { return mRect.center(); }
2013
2014 // reimplemented virtual methods:
2015 virtual void update(UpdatePhase phase);
2016 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
2017
2018 protected:
2019 // property members:
2020 QBrush mBackgroundBrush;
2021 QPixmap mBackgroundPixmap;
2022 QPixmap mScaledBackgroundPixmap;
2023 bool mBackgroundScaled;
2024 Qt::AspectRatioMode mBackgroundScaledMode;
2025 QCPLayoutInset *mInsetLayout;
2026 Qt::Orientations mRangeDrag, mRangeZoom;
2027 QPointer<QCPAxis> mRangeDragHorzAxis, mRangeDragVertAxis, mRangeZoomHorzAxis, mRangeZoomVertAxis;
2028 double mRangeZoomFactorHorz, mRangeZoomFactorVert;
2029 // non-property members:
2030 QCPRange mDragStartHorzRange, mDragStartVertRange;
2031 QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
2032 QPoint mDragStart;
2033 bool mDragging;
2034 QHash<QCPAxis::AxisType, QList<QCPAxis*> > mAxes;
2035
2036 // reimplemented virtual methods:
2037 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2038 virtual void draw(QCPPainter *painter);
2039 virtual int calculateAutoMargin(QCP::MarginSide side);
2040 // events:
2041 virtual void mousePressEvent(QMouseEvent *event);
2042 virtual void mouseMoveEvent(QMouseEvent *event);
2043 virtual void mouseReleaseEvent(QMouseEvent *event);
2044 virtual void wheelEvent(QWheelEvent *event);
2045
2046 // non-property methods:
2047 void drawBackground(QCPPainter *painter);
2048 void updateAxesOffset(QCPAxis::AxisType type);
2049
2050 private:
2051 Q_DISABLE_COPY(QCPAxisRect)
2052
2053 friend class QCustomPlot;
2054 };
2055
2056
2057 class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement
2058 {
2059 Q_OBJECT
2060 /// \cond INCLUDE_QPROPERTIES
2061 Q_PROPERTY(QCPLegend* parentLegend READ parentLegend)
2062 Q_PROPERTY(QFont font READ font WRITE setFont)
2063 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
2064 Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
2065 Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
2066 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectionChanged)
2067 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectableChanged)
2068 /// \endcond
2069 public:
2070 explicit QCPAbstractLegendItem(QCPLegend *parent);
2071
2072 // getters:
2073 QCPLegend *parentLegend() const { return mParentLegend; }
2074 QFont font() const { return mFont; }
2075 QColor textColor() const { return mTextColor; }
2076 QFont selectedFont() const { return mSelectedFont; }
2077 QColor selectedTextColor() const { return mSelectedTextColor; }
2078 bool selectable() const { return mSelectable; }
2079 bool selected() const { return mSelected; }
2080
2081 // setters:
2082 void setFont(const QFont &font);
2083 void setTextColor(const QColor &color);
2084 void setSelectedFont(const QFont &font);
2085 void setSelectedTextColor(const QColor &color);
2086 Q_SLOT void setSelectable(bool selectable);
2087 Q_SLOT void setSelected(bool selected);
2088
2089 // reimplemented virtual methods:
2090 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
2091
2092 signals:
2093 void selectionChanged(bool selected);
2094 void selectableChanged(bool selectable);
2095
2096 protected:
2097 // property members:
2098 QCPLegend *mParentLegend;
2099 QFont mFont;
2100 QColor mTextColor;
2101 QFont mSelectedFont;
2102 QColor mSelectedTextColor;
2103 bool mSelectable, mSelected;
2104
2105 // reimplemented virtual methods:
2106 virtual QCP::Interaction selectionCategory() const;
2107 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2108 virtual QRect clipRect() const;
2109 virtual void draw(QCPPainter *painter) = 0;
2110 // events:
2111 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
2112 virtual void deselectEvent(bool *selectionStateChanged);
2113
2114 private:
2115 Q_DISABLE_COPY(QCPAbstractLegendItem)
2116
2117 friend class QCPLegend;
2118 };
2119
2120
2121 class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem
2122 {
2123 Q_OBJECT
2124 public:
2125 QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable);
2126
2127 // getters:
2128 QCPAbstractPlottable *plottable() { return mPlottable; }
2129
2130 protected:
2131 // property members:
2132 QCPAbstractPlottable *mPlottable;
2133
2134 // reimplemented virtual methods:
2135 virtual void draw(QCPPainter *painter);
2136 virtual QSize minimumSizeHint() const;
2137
2138 // non-virtual methods:
2139 QPen getIconBorderPen() const;
2140 QColor getTextColor() const;
2141 QFont getFont() const;
2142 };
2143
2144
2145 class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid
2146 {
2147 Q_OBJECT
2148 /// \cond INCLUDE_QPROPERTIES
2149 Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen)
2150 Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
2151 Q_PROPERTY(QFont font READ font WRITE setFont)
2152 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
2153 Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
2154 Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding)
2155 Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen)
2156 Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectionChanged)
2157 Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectableChanged)
2158 Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen)
2159 Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen)
2160 Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
2161 Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
2162 Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
2163 /// \endcond
2164 public:
2165 /*!
2166 Defines the selectable parts of a legend
2167
2168 \see setSelectedParts, setSelectableParts
2169 */
2170 enum SelectablePart { spNone = 0x000 ///< <tt>0x000</tt> None
2171 ,spLegendBox = 0x001 ///< <tt>0x001</tt> The legend box (frame)
2172 ,spItems = 0x002 ///< <tt>0x002</tt> Legend items individually (see \ref selectedItems)
2173 };
2174 Q_FLAGS(SelectablePart SelectableParts)
2175 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
2176
2177 explicit QCPLegend();
2178 virtual ~QCPLegend();
2179
2180 // getters:
2181 QPen borderPen() const { return mBorderPen; }
2182 QBrush brush() const { return mBrush; }
2183 QFont font() const { return mFont; }
2184 QColor textColor() const { return mTextColor; }
2185 QSize iconSize() const { return mIconSize; }
2186 int iconTextPadding() const { return mIconTextPadding; }
2187 QPen iconBorderPen() const { return mIconBorderPen; }
2188 SelectableParts selectableParts() const { return mSelectableParts; }
2189 SelectableParts selectedParts() const;
2190 QPen selectedBorderPen() const { return mSelectedBorderPen; }
2191 QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }
2192 QBrush selectedBrush() const { return mSelectedBrush; }
2193 QFont selectedFont() const { return mSelectedFont; }
2194 QColor selectedTextColor() const { return mSelectedTextColor; }
2195
2196 // setters:
2197 void setBorderPen(const QPen &pen);
2198 void setBrush(const QBrush &brush);
2199 void setFont(const QFont &font);
2200 void setTextColor(const QColor &color);
2201 void setIconSize(const QSize &size);
2202 void setIconSize(int width, int height);
2203 void setIconTextPadding(int padding);
2204 void setIconBorderPen(const QPen &pen);
2205 Q_SLOT void setSelectableParts(const SelectableParts &selectableParts);
2206 Q_SLOT void setSelectedParts(const SelectableParts &selectedParts);
2207 void setSelectedBorderPen(const QPen &pen);
2208 void setSelectedIconBorderPen(const QPen &pen);
2209 void setSelectedBrush(const QBrush &brush);
2210 void setSelectedFont(const QFont &font);
2211 void setSelectedTextColor(const QColor &color);
2212
2213 // reimplemented virtual methods:
2214 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
2215
2216 // non-virtual methods:
2217 QCPAbstractLegendItem *item(int index) const;
2218 QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const;
2219 int itemCount() const;
2220 bool hasItem(QCPAbstractLegendItem *item) const;
2221 bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const;
2222 bool addItem(QCPAbstractLegendItem *item);
2223 bool removeItem(int index);
2224 bool removeItem(QCPAbstractLegendItem *item);
2225 void clearItems();
2226 QList<QCPAbstractLegendItem*> selectedItems() const;
2227
2228 signals:
2229 void selectionChanged(QCPLegend::SelectableParts parts);
2230 void selectableChanged(QCPLegend::SelectableParts parts);
2231
2232 protected:
2233 // property members:
2234 QPen mBorderPen, mIconBorderPen;
2235 QBrush mBrush;
2236 QFont mFont;
2237 QColor mTextColor;
2238 QSize mIconSize;
2239 int mIconTextPadding;
2240 SelectableParts mSelectedParts, mSelectableParts;
2241 QPen mSelectedBorderPen, mSelectedIconBorderPen;
2242 QBrush mSelectedBrush;
2243 QFont mSelectedFont;
2244 QColor mSelectedTextColor;
2245
2246 // reimplemented virtual methods:
2247 virtual void parentPlotInitialized(QCustomPlot *parentPlot);
2248 virtual QCP::Interaction selectionCategory() const;
2249 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2250 virtual void draw(QCPPainter *painter);
2251 // events:
2252 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
2253 virtual void deselectEvent(bool *selectionStateChanged);
2254
2255 // non-virtual methods:
2256 QPen getBorderPen() const;
2257 QBrush getBrush() const;
2258
2259 private:
2260 Q_DISABLE_COPY(QCPLegend)
2261
2262 friend class QCustomPlot;
2263 friend class QCPAbstractLegendItem;
2264 };
2265 Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts)
2266 Q_DECLARE_METATYPE(QCPLegend::SelectablePart)
2267
2268
2269 class QCP_LIB_DECL QCPPlotTitle : public QCPLayoutElement
2270 {
2271 Q_OBJECT
2272 /// \cond INCLUDE_QPROPERTIES
2273 Q_PROPERTY(QString text READ text WRITE setText)
2274 Q_PROPERTY(QFont font READ font WRITE setFont)
2275 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
2276 Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
2277 Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
2278 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
2279 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
2280 /// \endcond
2281 public:
2282 explicit QCPPlotTitle(QCustomPlot *parentPlot);
2283 explicit QCPPlotTitle(QCustomPlot *parentPlot, const QString &text);
2284
2285 // getters:
2286 QString text() const { return mText; }
2287 QFont font() const { return mFont; }
2288 QColor textColor() const { return mTextColor; }
2289 QFont selectedFont() const { return mSelectedFont; }
2290 QColor selectedTextColor() const { return mSelectedTextColor; }
2291 bool selectable() const { return mSelectable; }
2292 bool selected() const { return mSelected; }
2293
2294 // setters:
2295 void setText(const QString &text);
2296 void setFont(const QFont &font);
2297 void setTextColor(const QColor &color);
2298 void setSelectedFont(const QFont &font);
2299 void setSelectedTextColor(const QColor &color);
2300 Q_SLOT void setSelectable(bool selectable);
2301 Q_SLOT void setSelected(bool selected);
2302
2303 // reimplemented virtual methods:
2304 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
2305
2306 signals:
2307 void selectionChanged(bool selected);
2308 void selectableChanged(bool selectable);
2309
2310 protected:
2311 // property members:
2312 QString mText;
2313 QFont mFont;
2314 QColor mTextColor;
2315 QFont mSelectedFont;
2316 QColor mSelectedTextColor;
2317 QRect mTextBoundingRect;
2318 bool mSelectable, mSelected;
2319
2320 // reimplemented virtual methods:
2321 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2322 virtual void draw(QCPPainter *painter);
2323 virtual QSize minimumSizeHint() const;
2324 virtual QSize maximumSizeHint() const;
2325 // events:
2326 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
2327 virtual void deselectEvent(bool *selectionStateChanged);
2328
2329 // non-virtual methods:
2330 QFont mainFont() const;
2331 QColor mainTextColor() const;
2332
2333 private:
2334 Q_DISABLE_COPY(QCPPlotTitle)
2335 };
2336
2337
2338 class QCPColorScaleAxisRectPrivate : public QCPAxisRect
2339 {
2340 Q_OBJECT
2341 public:
2342 explicit QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale);
2343 protected:
2344 QCPColorScale *mParentColorScale;
2345 QImage mGradientImage;
2346 bool mGradientImageInvalidated;
2347 // re-using some methods of QCPAxisRect to make them available to friend class QCPColorScale
2348 using QCPAxisRect::calculateAutoMargin;
2349 using QCPAxisRect::mousePressEvent;
2350 using QCPAxisRect::mouseMoveEvent;
2351 using QCPAxisRect::mouseReleaseEvent;
2352 using QCPAxisRect::wheelEvent;
2353 using QCPAxisRect::update;
2354 virtual void draw(QCPPainter *painter);
2355 void updateGradientImage();
2356 Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts);
2357 Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts);
2358 friend class QCPColorScale;
2359 };
2360
2361
2362 class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement
2363 {
2364 Q_OBJECT
2365 /// \cond INCLUDE_QPROPERTIES
2366 Q_PROPERTY(QCPAxis::AxisType type READ type WRITE setType)
2367 Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)
2368 Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)
2369 Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)
2370 Q_PROPERTY(QString label READ label WRITE setLabel)
2371 Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth)
2372 Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag)
2373 Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom)
2374 /// \endcond
2375 public:
2376 explicit QCPColorScale(QCustomPlot *parentPlot);
2377 virtual ~QCPColorScale();
2378
2379 // getters:
2380 QCPAxis *axis() const { return mColorAxis.data(); }
2381 QCPAxis::AxisType type() const { return mType; }
2382 QCPRange dataRange() const { return mDataRange; }
2383 QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; }
2384 QCPColorGradient gradient() const { return mGradient; }
2385 QString label() const;
2386 int barWidth () const { return mBarWidth; }
2387 bool rangeDrag() const;
2388 bool rangeZoom() const;
2389
2390 // setters:
2391 void setType(QCPAxis::AxisType type);
2392 Q_SLOT void setDataRange(const QCPRange &dataRange);
2393 Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType);
2394 Q_SLOT void setGradient(const QCPColorGradient &gradient);
2395 void setLabel(const QString &str);
2396 void setBarWidth(int width);
2397 void setRangeDrag(bool enabled);
2398 void setRangeZoom(bool enabled);
2399
2400 // non-property methods:
2401 QList<QCPColorMap*> colorMaps() const;
2402 void rescaleDataRange(bool onlyVisibleMaps);
2403
2404 // reimplemented virtual methods:
2405 virtual void update(UpdatePhase phase);
2406
2407 signals:
2408 void dataRangeChanged(QCPRange newRange);
2409 void dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
2410 void gradientChanged(QCPColorGradient newGradient);
2411
2412 protected:
2413 // property members:
2414 QCPAxis::AxisType mType;
2415 QCPRange mDataRange;
2416 QCPAxis::ScaleType mDataScaleType;
2417 QCPColorGradient mGradient;
2418 int mBarWidth;
2419
2420 // non-property members:
2421 QPointer<QCPColorScaleAxisRectPrivate> mAxisRect;
2422 QPointer<QCPAxis> mColorAxis;
2423
2424 // reimplemented virtual methods:
2425 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2426 // events:
2427 virtual void mousePressEvent(QMouseEvent *event);
2428 virtual void mouseMoveEvent(QMouseEvent *event);
2429 virtual void mouseReleaseEvent(QMouseEvent *event);
2430 virtual void wheelEvent(QWheelEvent *event);
2431
2432 private:
2433 Q_DISABLE_COPY(QCPColorScale)
2434
2435 friend class QCPColorScaleAxisRectPrivate;
2436 };
2437
2438
1686 2439 /*! \file */
1687 2440
1688 2441
@@ -1721,6 +2474,7 class QCP_LIB_DECL QCPGraph : public QCP
1721 2474 Q_PROPERTY(double errorBarSize READ errorBarSize WRITE setErrorBarSize)
1722 2475 Q_PROPERTY(bool errorBarSkipSymbol READ errorBarSkipSymbol WRITE setErrorBarSkipSymbol)
1723 2476 Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph)
2477 Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling)
1724 2478 /// \endcond
1725 2479 public:
1726 2480 /*!
@@ -1730,20 +2484,20 public:
1730 2484 */
1731 2485 enum LineStyle { lsNone ///< data points are not connected with any lines (e.g. data only represented
1732 2486 ///< with symbols according to the scatter style, see \ref setScatterStyle)
1733 ,lsLine ///< data points are connected by a straight line
1734 ,lsStepLeft ///< line is drawn as steps where the step height is the value of the left data point
1735 ,lsStepRight ///< line is drawn as steps where the step height is the value of the right data point
1736 ,lsStepCenter ///< line is drawn as steps where the step is in between two data points
1737 ,lsImpulse ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line
2487 ,lsLine ///< data points are connected by a straight line
2488 ,lsStepLeft ///< line is drawn as steps where the step height is the value of the left data point
2489 ,lsStepRight ///< line is drawn as steps where the step height is the value of the right data point
2490 ,lsStepCenter ///< line is drawn as steps where the step is in between two data points
2491 ,lsImpulse ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line
1738 2492 };
1739 2493 Q_ENUMS(LineStyle)
1740 2494 /*!
1741 2495 Defines what kind of error bars are drawn for each data point
1742 2496 */
1743 2497 enum ErrorType { etNone ///< No error bars are shown
1744 ,etKey ///< Error bars for the key dimension of the data point are shown
1745 ,etValue ///< Error bars for the value dimension of the data point are shown
1746 ,etBoth ///< Error bars for both key and value dimensions of the data point are shown
2498 ,etKey ///< Error bars for the key dimension of the data point are shown
2499 ,etValue ///< Error bars for the value dimension of the data point are shown
2500 ,etBoth ///< Error bars for both key and value dimensions of the data point are shown
1747 2501 };
1748 2502 Q_ENUMS(ErrorType)
1749 2503
@@ -1751,7 +2505,7 public:
1751 2505 virtual ~QCPGraph();
1752 2506
1753 2507 // getters:
1754 const QCPDataMap *data() const { return mData; }
2508 QCPDataMap *data() const { return mData; }
1755 2509 LineStyle lineStyle() const { return mLineStyle; }
1756 2510 QCPScatterStyle scatterStyle() const { return mScatterStyle; }
1757 2511 ErrorType errorType() const { return mErrorType; }
@@ -1759,6 +2513,7 public:
1759 2513 double errorBarSize() const { return mErrorBarSize; }
1760 2514 bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
1761 2515 QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); }
2516 bool adaptiveSampling() const { return mAdaptiveSampling; }
1762 2517
1763 2518 // setters:
1764 2519 void setData(QCPDataMap *data, bool copy=false);
@@ -1776,6 +2531,7 public:
1776 2531 void setErrorBarSize(double size);
1777 2532 void setErrorBarSkipSymbol(bool enabled);
1778 2533 void setChannelFillGraph(QCPGraph *targetGraph);
2534 void setAdaptiveSampling(bool enabled);
1779 2535
1780 2536 // non-property methods:
1781 2537 void addData(const QCPDataMap &dataMap);
@@ -1807,31 +2563,34 protected:
1807 2563 double mErrorBarSize;
1808 2564 bool mErrorBarSkipSymbol;
1809 2565 QPointer<QCPGraph> mChannelFillGraph;
2566 bool mAdaptiveSampling;
1810 2567
1811 2568 // reimplemented virtual methods:
1812 2569 virtual void draw(QCPPainter *painter);
1813 2570 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
1814 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
1815 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
1816 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
1817 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
2571 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2572 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2573 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
2574 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
1818 2575
1819 2576 // introduced virtual methods:
1820 2577 virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const;
1821 virtual void drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const;
2578 virtual void drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const;
1822 2579 virtual void drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
1823 2580 virtual void drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
1824 2581
1825 2582 // non-virtual methods:
1826 void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
1827 void getScatterPlotData(QVector<QCPData> *pointData) const;
1828 void getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
1829 void getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
1830 void getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
1831 void getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
1832 void getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2583 void getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const;
2584 void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const;
2585 void getScatterPlotData(QVector<QCPData> *scatterData) const;
2586 void getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
2587 void getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
2588 void getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
2589 void getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
2590 void getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
1833 2591 void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const;
1834 void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const;
2592 void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const;
2593 int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const;
1835 2594 void addFillBasePoints(QVector<QPointF> *lineData) const;
1836 2595 void removeFillBasePoints(QVector<QPointF> *lineData) const;
1837 2596 QPointF lowerFillBasePoint(double lowerKey) const;
@@ -1887,8 +2646,8 public:
1887 2646 current pen of the curve (\ref setPen).
1888 2647 \see setLineStyle
1889 2648 */
1890 enum LineStyle { lsNone, ///< No line is drawn between data points (e.g. only scatters)
1891 lsLine ///< Data points are connected with a straight line
2649 enum LineStyle { lsNone ///< No line is drawn between data points (e.g. only scatters)
2650 ,lsLine ///< Data points are connected with a straight line
1892 2651 };
1893 2652 explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
1894 2653 virtual ~QCPCurve();
@@ -1929,8 +2688,8 protected:
1929 2688 // reimplemented virtual methods:
1930 2689 virtual void draw(QCPPainter *painter);
1931 2690 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
1932 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
1933 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2691 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2692 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
1934 2693
1935 2694 // introduced virtual methods:
1936 2695 virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const;
@@ -2018,8 +2777,8 protected:
2018 2777 // reimplemented virtual methods:
2019 2778 virtual void draw(QCPPainter *painter);
2020 2779 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2021 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2022 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2780 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2781 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2023 2782
2024 2783 // non-virtual methods:
2025 2784 QPolygonF getBarPolygon(double key, double value) const;
@@ -2103,8 +2862,8 protected:
2103 2862 // reimplemented virtual methods:
2104 2863 virtual void draw(QCPPainter *painter);
2105 2864 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2106 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2107 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2865 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2866 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2108 2867
2109 2868 // introduced virtual methods:
2110 2869 virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const;
@@ -2117,6 +2876,129 protected:
2117 2876 };
2118 2877
2119 2878
2879 class QCP_LIB_DECL QCPColorMapData
2880 {
2881 public:
2882 QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange);
2883 ~QCPColorMapData();
2884 QCPColorMapData(const QCPColorMapData &other);
2885 QCPColorMapData &operator=(const QCPColorMapData &other);
2886
2887 // getters:
2888 int keySize() const { return mKeySize; }
2889 int valueSize() const { return mValueSize; }
2890 QCPRange keyRange() const { return mKeyRange; }
2891 QCPRange valueRange() const { return mValueRange; }
2892 QCPRange dataBounds() const { return mDataBounds; }
2893 double data(double key, double value);
2894 double cell(int keyIndex, int valueIndex);
2895
2896 // setters:
2897 void setSize(int keySize, int valueSize);
2898 void setKeySize(int keySize);
2899 void setValueSize(int valueSize);
2900 void setRange(const QCPRange &keyRange, const QCPRange &valueRange);
2901 void setKeyRange(const QCPRange &keyRange);
2902 void setValueRange(const QCPRange &valueRange);
2903 void setData(double key, double value, double z);
2904 void setCell(int keyIndex, int valueIndex, double z);
2905
2906 // non-property methods:
2907 void recalculateDataBounds();
2908 void clear();
2909 void fill(double z);
2910 bool isEmpty() const { return mIsEmpty; }
2911 void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const;
2912 void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const;
2913
2914 protected:
2915 // property members:
2916 int mKeySize, mValueSize;
2917 QCPRange mKeyRange, mValueRange;
2918 bool mIsEmpty;
2919 // non-property members:
2920 double *mData;
2921 QCPRange mDataBounds;
2922 bool mDataModified;
2923
2924 friend class QCPColorMap;
2925 };
2926
2927
2928 class QCP_LIB_DECL QCPColorMap : public QCPAbstractPlottable
2929 {
2930 Q_OBJECT
2931 /// \cond INCLUDE_QPROPERTIES
2932 Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)
2933 Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)
2934 Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)
2935 Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate)
2936 Q_PROPERTY(bool tightBoundary READ tightBoundary WRITE setTightBoundary)
2937 Q_PROPERTY(QCPColorScale* colorScale READ colorScale WRITE setColorScale)
2938 /// \endcond
2939 public:
2940 explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis);
2941 virtual ~QCPColorMap();
2942
2943 // getters:
2944 QCPColorMapData *data() const { return mMapData; }
2945 QCPRange dataRange() const { return mDataRange; }
2946 QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; }
2947 bool interpolate() const { return mInterpolate; }
2948 bool tightBoundary() const { return mTightBoundary; }
2949 QCPColorGradient gradient() const { return mGradient; }
2950 QCPColorScale *colorScale() const { return mColorScale.data(); }
2951
2952 // setters:
2953 void setData(QCPColorMapData *data, bool copy=false);
2954 Q_SLOT void setDataRange(const QCPRange &dataRange);
2955 Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType);
2956 Q_SLOT void setGradient(const QCPColorGradient &gradient);
2957 void setInterpolate(bool enabled);
2958 void setTightBoundary(bool enabled);
2959 void setColorScale(QCPColorScale *colorScale);
2960
2961 // non-property methods:
2962 void rescaleDataRange(bool recalculateDataBounds=false);
2963 Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18));
2964
2965 // reimplemented virtual methods:
2966 virtual void clearData();
2967 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
2968
2969 signals:
2970 void dataRangeChanged(QCPRange newRange);
2971 void dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
2972 void gradientChanged(QCPColorGradient newGradient);
2973
2974 protected:
2975 // property members:
2976 QCPRange mDataRange;
2977 QCPAxis::ScaleType mDataScaleType;
2978 QCPColorMapData *mMapData;
2979 QCPColorGradient mGradient;
2980 bool mInterpolate;
2981 bool mTightBoundary;
2982 QPointer<QCPColorScale> mColorScale;
2983 // non-property members:
2984 QImage mMapImage;
2985 QPixmap mLegendIcon;
2986 bool mMapImageInvalidated;
2987
2988 // introduced virtual methods:
2989 virtual void updateMapImage();
2990
2991 // reimplemented virtual methods:
2992 virtual void draw(QCPPainter *painter);
2993 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2994 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2995 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2996
2997 friend class QCustomPlot;
2998 friend class QCPLegend;
2999 };
3000
3001
2120 3002 class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
2121 3003 {
2122 3004 Q_OBJECT
@@ -2643,392 +3525,5 protected:
2643 3525 QPen mainPen() const;
2644 3526 };
2645 3527
2646
2647 class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement
2648 {
2649 Q_OBJECT
2650 /// \cond INCLUDE_QPROPERTIES
2651 Q_PROPERTY(QPixmap background READ background WRITE setBackground)
2652 Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
2653 Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)
2654 Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
2655 Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
2656 /// \endcond
2657 public:
2658 explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true);
2659 virtual ~QCPAxisRect();
2660
2661 // getters:
2662 QPixmap background() const { return mBackgroundPixmap; }
2663 bool backgroundScaled() const { return mBackgroundScaled; }
2664 Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
2665 Qt::Orientations rangeDrag() const { return mRangeDrag; }
2666 Qt::Orientations rangeZoom() const { return mRangeZoom; }
2667 QCPAxis *rangeDragAxis(Qt::Orientation orientation);
2668 QCPAxis *rangeZoomAxis(Qt::Orientation orientation);
2669 double rangeZoomFactor(Qt::Orientation orientation);
2670
2671 // setters:
2672 void setBackground(const QPixmap &pm);
2673 void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
2674 void setBackground(const QBrush &brush);
2675 void setBackgroundScaled(bool scaled);
2676 void setBackgroundScaledMode(Qt::AspectRatioMode mode);
2677 void setRangeDrag(Qt::Orientations orientations);
2678 void setRangeZoom(Qt::Orientations orientations);
2679 void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical);
2680 void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical);
2681 void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
2682 void setRangeZoomFactor(double factor);
2683
2684 // non-property methods:
2685 int axisCount(QCPAxis::AxisType type) const;
2686 QCPAxis *axis(QCPAxis::AxisType type, int index=0) const;
2687 QList<QCPAxis*> axes(QCPAxis::AxisTypes types) const;
2688 QList<QCPAxis*> axes() const;
2689 QCPAxis *addAxis(QCPAxis::AxisType type);
2690 QList<QCPAxis*> addAxes(QCPAxis::AxisTypes types);
2691 bool removeAxis(QCPAxis *axis);
2692 QCPLayoutInset *insetLayout() const { return mInsetLayout; }
2693
2694 void setupFullAxesBox(bool connectRanges=false);
2695 QList<QCPAbstractPlottable*> plottables() const;
2696 QList<QCPGraph*> graphs() const;
2697 QList<QCPAbstractItem*> items() const;
2698
2699 // read-only interface imitating a QRect:
2700 int left() const { return mRect.left(); }
2701 int right() const { return mRect.right(); }
2702 int top() const { return mRect.top(); }
2703 int bottom() const { return mRect.bottom(); }
2704 int width() const { return mRect.width(); }
2705 int height() const { return mRect.height(); }
2706 QSize size() const { return mRect.size(); }
2707 QPoint topLeft() const { return mRect.topLeft(); }
2708 QPoint topRight() const { return mRect.topRight(); }
2709 QPoint bottomLeft() const { return mRect.bottomLeft(); }
2710 QPoint bottomRight() const { return mRect.bottomRight(); }
2711 QPoint center() const { return mRect.center(); }
2712
2713 // reimplemented virtual methods:
2714 virtual void update();
2715 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
2716
2717 protected:
2718 // property members:
2719 QBrush mBackgroundBrush;
2720 QPixmap mBackgroundPixmap;
2721 QPixmap mScaledBackgroundPixmap;
2722 bool mBackgroundScaled;
2723 Qt::AspectRatioMode mBackgroundScaledMode;
2724 QCPLayoutInset *mInsetLayout;
2725 Qt::Orientations mRangeDrag, mRangeZoom;
2726 QPointer<QCPAxis> mRangeDragHorzAxis, mRangeDragVertAxis, mRangeZoomHorzAxis, mRangeZoomVertAxis;
2727 double mRangeZoomFactorHorz, mRangeZoomFactorVert;
2728 // non-property members:
2729 QCPRange mDragStartHorzRange, mDragStartVertRange;
2730 QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
2731 QPoint mDragStart;
2732 bool mDragging;
2733 QHash<QCPAxis::AxisType, QList<QCPAxis*> > mAxes;
2734
2735 // reimplemented virtual methods:
2736 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2737 virtual void draw(QCPPainter *painter);
2738 virtual int calculateAutoMargin(QCP::MarginSide side);
2739 // events:
2740 virtual void mousePressEvent(QMouseEvent *event);
2741 virtual void mouseMoveEvent(QMouseEvent *event);
2742 virtual void mouseReleaseEvent(QMouseEvent *event);
2743 virtual void wheelEvent(QWheelEvent *event);
2744
2745 // non-property methods:
2746 void drawBackground(QCPPainter *painter);
2747 void updateAxesOffset(QCPAxis::AxisType type);
2748
2749 private:
2750 Q_DISABLE_COPY(QCPAxisRect)
2751
2752 friend class QCustomPlot;
2753 };
2754
2755
2756 class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement
2757 {
2758 Q_OBJECT
2759 /// \cond INCLUDE_QPROPERTIES
2760 Q_PROPERTY(QCPLegend* parentLegend READ parentLegend)
2761 Q_PROPERTY(QFont font READ font WRITE setFont)
2762 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
2763 Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
2764 Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
2765 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable)
2766 Q_PROPERTY(bool selected READ selected WRITE setSelected)
2767 /// \endcond
2768 public:
2769 explicit QCPAbstractLegendItem(QCPLegend *parent);
2770
2771 // getters:
2772 QCPLegend *parentLegend() const { return mParentLegend; }
2773 QFont font() const { return mFont; }
2774 QColor textColor() const { return mTextColor; }
2775 QFont selectedFont() const { return mSelectedFont; }
2776 QColor selectedTextColor() const { return mSelectedTextColor; }
2777 bool selectable() const { return mSelectable; }
2778 bool selected() const { return mSelected; }
2779
2780 // setters:
2781 void setFont(const QFont &font);
2782 void setTextColor(const QColor &color);
2783 void setSelectedFont(const QFont &font);
2784 void setSelectedTextColor(const QColor &color);
2785 void setSelectable(bool selectable);
2786 void setSelected(bool selected);
2787
2788 // reimplemented virtual methods:
2789 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
2790
2791 signals:
2792 void selectionChanged(bool selected);
2793
2794 protected:
2795 // property members:
2796 QCPLegend *mParentLegend;
2797 QFont mFont;
2798 QColor mTextColor;
2799 QFont mSelectedFont;
2800 QColor mSelectedTextColor;
2801 bool mSelectable, mSelected;
2802
2803 // reimplemented virtual methods:
2804 virtual QCP::Interaction selectionCategory() const;
2805 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2806 virtual QRect clipRect() const;
2807 virtual void draw(QCPPainter *painter) = 0;
2808 // events:
2809 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
2810 virtual void deselectEvent(bool *selectionStateChanged);
2811
2812 private:
2813 Q_DISABLE_COPY(QCPAbstractLegendItem)
2814
2815 friend class QCPLegend;
2816 };
2817
2818
2819 class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem
2820 {
2821 Q_OBJECT
2822 public:
2823 QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable);
2824
2825 // getters:
2826 QCPAbstractPlottable *plottable() { return mPlottable; }
2827
2828 protected:
2829 // property members:
2830 QCPAbstractPlottable *mPlottable;
2831
2832 // reimplemented virtual methods:
2833 virtual void draw(QCPPainter *painter);
2834 virtual QSize minimumSizeHint() const;
2835
2836 // non-virtual methods:
2837 QPen getIconBorderPen() const;
2838 QColor getTextColor() const;
2839 QFont getFont() const;
2840 };
2841
2842
2843 class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid
2844 {
2845 Q_OBJECT
2846 /// \cond INCLUDE_QPROPERTIES
2847 Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen)
2848 Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
2849 Q_PROPERTY(QFont font READ font WRITE setFont)
2850 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
2851 Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
2852 Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding)
2853 Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen)
2854 Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts)
2855 Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts)
2856 Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen)
2857 Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen)
2858 Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
2859 Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
2860 Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
2861 /// \endcond
2862 public:
2863 /*!
2864 Defines the selectable parts of a legend
2865
2866 \see setSelectedParts, setSelectableParts
2867 */
2868 enum SelectablePart { spNone = 0x000 ///< <tt>0x000</tt> None
2869 ,spLegendBox = 0x001 ///< <tt>0x001</tt> The legend box (frame)
2870 ,spItems = 0x002 ///< <tt>0x002</tt> Legend items individually (see \ref selectedItems)
2871 };
2872 Q_FLAGS(SelectablePart SelectableParts)
2873 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
2874
2875 explicit QCPLegend();
2876 virtual ~QCPLegend();
2877
2878 // getters:
2879 QPen borderPen() const { return mBorderPen; }
2880 QBrush brush() const { return mBrush; }
2881 QFont font() const { return mFont; }
2882 QColor textColor() const { return mTextColor; }
2883 QSize iconSize() const { return mIconSize; }
2884 int iconTextPadding() const { return mIconTextPadding; }
2885 QPen iconBorderPen() const { return mIconBorderPen; }
2886 SelectableParts selectableParts() const { return mSelectableParts; }
2887 SelectableParts selectedParts() const;
2888 QPen selectedBorderPen() const { return mSelectedBorderPen; }
2889 QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }
2890 QBrush selectedBrush() const { return mSelectedBrush; }
2891 QFont selectedFont() const { return mSelectedFont; }
2892 QColor selectedTextColor() const { return mSelectedTextColor; }
2893
2894 // setters:
2895 void setBorderPen(const QPen &pen);
2896 void setBrush(const QBrush &brush);
2897 void setFont(const QFont &font);
2898 void setTextColor(const QColor &color);
2899 void setIconSize(const QSize &size);
2900 void setIconSize(int width, int height);
2901 void setIconTextPadding(int padding);
2902 void setIconBorderPen(const QPen &pen);
2903 void setSelectableParts(const SelectableParts &selectableParts);
2904 void setSelectedParts(const SelectableParts &selectedParts);
2905 void setSelectedBorderPen(const QPen &pen);
2906 void setSelectedIconBorderPen(const QPen &pen);
2907 void setSelectedBrush(const QBrush &brush);
2908 void setSelectedFont(const QFont &font);
2909 void setSelectedTextColor(const QColor &color);
2910
2911 // reimplemented virtual methods:
2912 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
2913
2914 // non-virtual methods:
2915 QCPAbstractLegendItem *item(int index) const;
2916 QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const;
2917 int itemCount() const;
2918 bool hasItem(QCPAbstractLegendItem *item) const;
2919 bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const;
2920 bool addItem(QCPAbstractLegendItem *item);
2921 bool removeItem(int index);
2922 bool removeItem(QCPAbstractLegendItem *item);
2923 void clearItems();
2924 QList<QCPAbstractLegendItem*> selectedItems() const;
2925
2926 signals:
2927 void selectionChanged(QCPLegend::SelectableParts selection);
2928
2929 protected:
2930 // property members:
2931 QPen mBorderPen, mIconBorderPen;
2932 QBrush mBrush;
2933 QFont mFont;
2934 QColor mTextColor;
2935 QSize mIconSize;
2936 int mIconTextPadding;
2937 SelectableParts mSelectedParts, mSelectableParts;
2938 QPen mSelectedBorderPen, mSelectedIconBorderPen;
2939 QBrush mSelectedBrush;
2940 QFont mSelectedFont;
2941 QColor mSelectedTextColor;
2942
2943 // reimplemented virtual methods:
2944 virtual void parentPlotInitialized(QCustomPlot *parentPlot);
2945 virtual QCP::Interaction selectionCategory() const;
2946 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
2947 virtual void draw(QCPPainter *painter);
2948 // events:
2949 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
2950 virtual void deselectEvent(bool *selectionStateChanged);
2951
2952 // non-virtual methods:
2953 QPen getBorderPen() const;
2954 QBrush getBrush() const;
2955
2956 private:
2957 Q_DISABLE_COPY(QCPLegend)
2958
2959 friend class QCustomPlot;
2960 friend class QCPAbstractLegendItem;
2961 };
2962 Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts)
2963 Q_DECLARE_METATYPE(QCPLegend::SelectablePart)
2964
2965
2966 class QCP_LIB_DECL QCPPlotTitle : public QCPLayoutElement
2967 {
2968 Q_OBJECT
2969 /// \cond INCLUDE_QPROPERTIES
2970 Q_PROPERTY(QString text READ text WRITE setText)
2971 Q_PROPERTY(QFont font READ font WRITE setFont)
2972 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
2973 Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
2974 Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
2975 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable)
2976 Q_PROPERTY(bool selected READ selected WRITE setSelected)
2977 /// \endcond
2978 public:
2979 explicit QCPPlotTitle(QCustomPlot *parentPlot);
2980 explicit QCPPlotTitle(QCustomPlot *parentPlot, const QString &text);
2981
2982 // getters:
2983 QString text() const { return mText; }
2984 QFont font() const { return mFont; }
2985 QColor textColor() const { return mTextColor; }
2986 QFont selectedFont() const { return mSelectedFont; }
2987 QColor selectedTextColor() const { return mSelectedTextColor; }
2988 bool selectable() const { return mSelectable; }
2989 bool selected() const { return mSelected; }
2990
2991 // setters:
2992 void setText(const QString &text);
2993 void setFont(const QFont &font);
2994 void setTextColor(const QColor &color);
2995 void setSelectedFont(const QFont &font);
2996 void setSelectedTextColor(const QColor &color);
2997 void setSelectable(bool selectable);
2998 void setSelected(bool selected);
2999
3000 // reimplemented virtual methods:
3001 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
3002
3003 signals:
3004 void selectionChanged(bool selected);
3005
3006 protected:
3007 // property members:
3008 QString mText;
3009 QFont mFont;
3010 QColor mTextColor;
3011 QFont mSelectedFont;
3012 QColor mSelectedTextColor;
3013 QRect mTextBoundingRect;
3014 bool mSelectable, mSelected;
3015
3016 // reimplemented virtual methods:
3017 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
3018 virtual void draw(QCPPainter *painter);
3019 virtual QSize minimumSizeHint() const;
3020 virtual QSize maximumSizeHint() const;
3021 // events:
3022 virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
3023 virtual void deselectEvent(bool *selectionStateChanged);
3024
3025 // non-virtual methods:
3026 QFont mainFont() const;
3027 QColor mainTextColor() const;
3028
3029 private:
3030 Q_DISABLE_COPY(QCPPlotTitle)
3031 };
3032
3033 3528 #endif // QCUSTOMPLOT_H
3034 3529
@@ -80,6 +80,11 void SocExplorerPlot::setLegendSelectedF
80 80 this->repaint();
81 81 }
82 82
83 void SocExplorerPlot::setAdaptativeSampling(int graphIndex, bool enable)
84 {
85 this->m_plot->graph(graphIndex)->setAdaptiveSampling(enable);
86 }
87
83 88 int SocExplorerPlot::addGraph()
84 89 {
85 90 this->m_plot->addGraph();
@@ -18,6 +18,7 public:
18 18 void rescaleAxis();
19 19 void setLegendFont(QFont font);
20 20 void setLegendSelectedFont(QFont font);
21 void setAdaptativeSampling(int graphIndex,bool enable);
21 22 int addGraph();
22 23 void setGraphName(int graphIndex,QString name);
23 24 void setGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y);
@@ -233,6 +233,11 void PythonQtWrapper_SocExplorerPlot::re
233 233 ( theWrappedObject->rescaleAxis());
234 234 }
235 235
236 void PythonQtWrapper_SocExplorerPlot::setAdaptativeSampling(SocExplorerPlot* theWrappedObject, int graphIndex, bool enable)
237 {
238 ( theWrappedObject->setAdaptativeSampling(graphIndex, enable));
239 }
240
236 241 void PythonQtWrapper_SocExplorerPlot::setGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QList<QVariant > x, QList<QVariant > y)
237 242 {
238 243 ( theWrappedObject->setGraphData(graphIndex, x, y));
@@ -115,6 +115,7 void delete_SocExplorerPlot(SocExplorerP
115 115 void addGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QVariant x, QVariant y);
116 116 QPen getGraphPen(SocExplorerPlot* theWrappedObject, int graphIndex);
117 117 void rescaleAxis(SocExplorerPlot* theWrappedObject);
118 void setAdaptativeSampling(SocExplorerPlot* theWrappedObject, int graphIndex, bool enable);
118 119 void setGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QList<QVariant > x, QList<QVariant > y);
119 120 void setGraphLineStyle(SocExplorerPlot* theWrappedObject, int graphIndex, QString lineStyle);
120 121 void setGraphName(SocExplorerPlot* theWrappedObject, int graphIndex, QString name);
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now