##// 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, (9107 lines changed) Show them Hide them
@@ -1,7 +1,7
1 /***************************************************************************
1 /***************************************************************************
2 ** **
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
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 ** This program is free software: you can redistribute it and/or modify **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
7 ** it under the terms of the GNU General Public License as published by **
@@ -19,8 +19,8
19 ****************************************************************************
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 09.12.13 **
22 ** Date: 14.03.14 **
23 ** Version: 1.1.1 **
23 ** Version: 1.2.0 **
24 ****************************************************************************/
24 ****************************************************************************/
25
25
26 #include "qcustomplot.h"
26 #include "qcustomplot.h"
@@ -35,7 +35,7
35 /*! \class QCPPainter
35 /*! \class QCPPainter
36 \brief QPainter subclass used internally
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 consistency between antialiased and non-antialiased painting. Further it provides workarounds
39 consistency between antialiased and non-antialiased painting. Further it provides workarounds
40 for QPainter quirks.
40 for QPainter quirks.
41
41
@@ -752,7 +752,8 QCPLayer::QCPLayer(QCustomPlot *parentPl
752 QObject(parentPlot),
752 QObject(parentPlot),
753 mParentPlot(parentPlot),
753 mParentPlot(parentPlot),
754 mName(layerName),
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 // Note: no need to make sure layerName is unique, because layer
758 // Note: no need to make sure layerName is unique, because layer
758 // management is done with QCustomPlot functions.
759 // management is done with QCustomPlot functions.
@@ -772,6 +773,19 QCPLayer::~QCPLayer()
772 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 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 /*! \internal
789 /*! \internal
776
790
777 Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
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 /* end documentation of pure virtual functions */
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 Creates a new QCPLayerable instance.
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
1013 Returns whether this layerable is visible, taking the visibility of the layerable parent and the
989 into account. This is the method that is consulted to decide whether a layerable shall be drawn
1014 visibility of the layer this layerable is on into account. This is the method that is consulted
990 or not.
1015 to decide whether a layerable shall be drawn or not.
991
1016
992 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
1017 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
993 subclasses, like in the case of QCPLayoutElement), this function returns true only if this
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 bool QCPLayerable::realVisibility() const
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 return false;
1138 return false;
1114 }
1139 }
1115
1140
1141 QCPLayer *oldLayer = mLayer;
1116 if (mLayer)
1142 if (mLayer)
1117 mLayer->removeChild(this);
1143 mLayer->removeChild(this);
1118 mLayer = layer;
1144 mLayer = layer;
1119 if (mLayer)
1145 if (mLayer)
1120 mLayer->addChild(this, prepend);
1146 mLayer->addChild(this, prepend);
1147 if (mLayer != oldLayer)
1148 emit layerChanged(mLayer);
1121 return true;
1149 return true;
1122 }
1150 }
1123
1151
@@ -1532,17 +1560,11 bool QCPRange::validRange(const QCPRange
1532 Since a QCPLayout is a layout element itself, it may be placed inside other layouts. This way,
1560 Since a QCPLayout is a layout element itself, it may be placed inside other layouts. This way,
1533 complex hierarchies may be created, offering very flexible arrangements.
1561 complex hierarchies may be created, offering very flexible arrangements.
1534
1562
1535 <div style="text-align:center">
1563 \image html LayoutsystemSketch.png
1536 <div style="display:inline-block; margin-left:auto; margin-right:auto">\image html LayoutsystemSketch0.png ""</div>
1564
1537 <div style="display:inline-block; margin-left:auto; margin-right:auto">\image html LayoutsystemSketch1.png ""</div>
1565 Above is a sketch of the default \ref QCPLayoutGrid accessible via \ref QCustomPlot::plotLayout.
1538 <div style="clear:both"></div>
1566 It shows how two child layout elements are placed inside the grid layout next to each other in
1539 <div style="display:inline-block; max-width:1000px; text-align:justify">
1567 cells (0, 0) and (0, 1).
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>
1546
1568
1547 \subsection layoutsystem-plotlayout The top level plot layout
1569 \subsection layoutsystem-plotlayout The top level plot layout
1548
1570
@@ -1561,7 +1583,7 bool QCPRange::validRange(const QCPRange
1561 title->setFont(QFont("sans", 12, QFont::Bold));
1583 title->setFont(QFont("sans", 12, QFont::Bold));
1562 // then we add it to the main plot layout:
1584 // then we add it to the main plot layout:
1563 customPlot->plotLayout()->insertRow(0); // insert an empty row above the axis rect
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 \endcode
1587 \endcode
1566 \image html layoutsystem-addingplottitle.png
1588 \image html layoutsystem-addingplottitle.png
1567
1589
@@ -1569,15 +1591,28 bool QCPRange::validRange(const QCPRange
1569 \code
1591 \code
1570 customPlot->plotLayout()->clear(); // let's start from scratch and remove the default axis rect
1592 customPlot->plotLayout()->clear(); // let's start from scratch and remove the default axis rect
1571 // add the first axis rect in second row (row index 1):
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 // create a sub layout that we'll place in first row:
1596 // create a sub layout that we'll place in first row:
1574 QCPLayoutGrid *subLayout = new QCPLayoutGrid;
1597 QCPLayoutGrid *subLayout = new QCPLayoutGrid;
1575 customPlot->plotLayout()->addElement(0, 0, subLayout);
1598 customPlot->plotLayout()->addElement(0, 0, subLayout);
1576 // add two axis rects in the sub layout next to eachother:
1599 // add two axis rects in the sub layout next to eachother:
1577 subLayout->addElement(0, 0, new QCPAxisRect(customPlot));
1600 QCPAxisRect *leftAxisRect = new QCPAxisRect(customPlot);
1578 subLayout->addElement(0, 1, new QCPAxisRect(customPlot));
1601 QCPAxisRect *rightAxisRect = new QCPAxisRect(customPlot);
1602 subLayout->addElement(0, 0, leftAxisRect);
1603 subLayout->addElement(0, 1, rightAxisRect);
1579 subLayout->setColumnStretchFactor(0, 3); // left axis rect shall have 60% of width
1604 subLayout->setColumnStretchFactor(0, 3); // left axis rect shall have 60% of width
1580 subLayout->setColumnStretchFactor(1, 2); // right one only 40% (3:2 = 60:40)
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 \endcode
1616 \endcode
1582 \image html layoutsystem-multipleaxisrects.png
1617 \image html layoutsystem-multipleaxisrects.png
1583
1618
@@ -2007,25 +2042,27 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
2045 Updates the layout element and sub-elements. This function is automatically called before every
2011 the parent layout element.
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 Layout elements that have child elements should call the \ref update method of their child
2050 Layout elements that have child elements should call the \ref update method of their child
2014 elements.
2051 elements, and pass the current \a phase unchanged.
2015
2052
2016 The default implementation executes the automatic margin mechanism, so subclasses should make
2053 The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
2017 sure to call the base class implementation.
2054 Subclasses should make sure to call the base class implementation.
2018 */
2055 */
2019 void QCPLayoutElement::update()
2056 void QCPLayoutElement::update(UpdatePhase phase)
2057 {
2058 if (phase == upMargins)
2020 {
2059 {
2021 if (mAutoMargins != QCP::msNone)
2060 if (mAutoMargins != QCP::msNone)
2022 {
2061 {
2023 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
2062 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
2024 QMargins newMargins = mMargins;
2063 QMargins newMargins = mMargins;
2025 QVector<QCP::MarginSide> marginSides = QVector<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
2064 foreach (QCP::MarginSide side, QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom)
2026 for (int i=0; i<marginSides.size(); ++i)
2065 {
2027 {
2028 QCP::MarginSide side = marginSides.at(i);
2029 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
2066 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
2030 {
2067 {
2031 if (mMarginGroups.contains(side))
2068 if (mMarginGroups.contains(side))
@@ -2040,6 +2077,7 void QCPLayoutElement::update()
2040 setMargins(newMargins);
2077 setMargins(newMargins);
2041 }
2078 }
2042 }
2079 }
2080 }
2043
2081
2044 /*!
2082 /*!
2045 Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
2083 Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
@@ -2069,7 +2107,7 QSize QCPLayoutElement::maximumSizeHint(
2069 Returns a list of all child elements in this layout element. If \a recursive is true, all
2107 Returns a list of all child elements in this layout element. If \a recursive is true, all
2070 sub-child elements are included in the list, too.
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 empty cells which yield 0 at the respective index.)
2111 empty cells which yield 0 at the respective index.)
2074 */
2112 */
2075 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
2113 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
@@ -2116,11 +2154,10 double QCPLayoutElement::selectTest(cons
2116 */
2154 */
2117 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2155 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2118 {
2156 {
2119 QList<QCPLayoutElement*> els = elements(false);
2157 foreach (QCPLayoutElement* el, elements(false))
2120 for (int i=0; i<els.size(); ++i)
2158 {
2121 {
2159 if (!el->parentPlot())
2122 if (!els.at(i)->parentPlot())
2160 el->initializeParentPlot(parentPlot);
2123 els.at(i)->initializeParentPlot(parentPlot);
2124 }
2161 }
2125 }
2162 }
2126
2163
@@ -2227,25 +2264,27 QCPLayout::QCPLayout()
2227
2264
2228 Finally, \ref update is called on all child elements.
2265 Finally, \ref update is called on all child elements.
2229 */
2266 */
2230 void QCPLayout::update()
2267 void QCPLayout::update(UpdatePhase phase)
2231 {
2268 {
2232 QCPLayoutElement::update(); // recalculates (auto-)margins
2269 QCPLayoutElement::update(phase);
2233
2270
2234 // set child element rects according to layout:
2271 // set child element rects according to layout:
2272 if (phase == upLayout)
2235 updateLayout();
2273 updateLayout();
2236
2274
2237 // propagate update call to child elements:
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 if (QCPLayoutElement *el = elementAt(i))
2279 if (QCPLayoutElement *el = elementAt(i))
2241 el->update();
2280 el->update(phase);
2242 }
2281 }
2243 }
2282 }
2244
2283
2245 /* inherits documentation from base class */
2284 /* inherits documentation from base class */
2246 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2285 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2247 {
2286 {
2248 int c = elementCount();
2287 const int c = elementCount();
2249 QList<QCPLayoutElement*> result;
2288 QList<QCPLayoutElement*> result;
2250 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2289 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2251 result.reserve(c);
2290 result.reserve(c);
@@ -3616,8 +3655,8 void QCPLineEnding::draw(QCPPainter *pai
3616 if (lengthVec.isNull())
3655 if (lengthVec.isNull())
3617 lengthVec = QVector2D(1, 0);
3656 lengthVec = QVector2D(1, 0);
3618 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3657 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3619 lengthVec *= mLength*(mInverted ? -1 : 1);
3658 lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3620 widthVec *= mWidth*0.5*(mInverted ? -1 : 1);
3659 widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3621
3660
3622 QPen penBackup = painter->pen();
3661 QPen penBackup = painter->pen();
3623 QBrush brushBackup = painter->brush();
3662 QBrush brushBackup = painter->brush();
@@ -3644,7 +3683,7 void QCPLineEnding::draw(QCPPainter *pai
3644 {
3683 {
3645 QPointF points[4] = {pos.toPointF(),
3684 QPointF points[4] = {pos.toPointF(),
3646 (pos-lengthVec+widthVec).toPointF(),
3685 (pos-lengthVec+widthVec).toPointF(),
3647 (pos-lengthVec*0.8).toPointF(),
3686 (pos-lengthVec*0.8f).toPointF(),
3648 (pos-lengthVec-widthVec).toPointF()
3687 (pos-lengthVec-widthVec).toPointF()
3649 };
3688 };
3650 painter->setPen(miterPen);
3689 painter->setPen(miterPen);
@@ -3717,13 +3756,13 void QCPLineEnding::draw(QCPPainter *pai
3717 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3756 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3718 {
3757 {
3719 // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
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(),
3759 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3721 (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
3760 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3722 } else
3761 } else
3723 {
3762 {
3724 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
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(),
3764 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3726 (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0, (double)painter->pen().widthF())*0.5).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 break;
3767 break;
3729 }
3768 }
@@ -3994,8 +4033,10 void QCPGrid::drawSubGridLines(QCPPainte
3994
4033
3995 /*! \fn Qt::Orientation QCPAxis::orientation() const
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 from the axis type (left, top, right or bottom).
4037 from the axis type (left, top, right or bottom).
4038
4039 \see orientation(AxisType type)
3999 */
4040 */
4000
4041
4001 /*! \fn QCPGrid *QCPAxis::grid() const
4042 /*! \fn QCPGrid *QCPAxis::grid() const
@@ -4004,6 +4045,13 void QCPGrid::drawSubGridLines(QCPPainte
4004 grid is displayed.
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 /* end of documentation of inline functions */
4055 /* end of documentation of inline functions */
4008 /* start of documentation of signals */
4056 /* start of documentation of signals */
4009
4057
@@ -4036,12 +4084,22 void QCPGrid::drawSubGridLines(QCPPainte
4036 \a oldRange.
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 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
4092 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
4040
4093
4041 This signal is emitted when the selection state of this axis has changed, either by user interaction
4094 This signal is emitted when the selection state of this axis has changed, either by user interaction
4042 or by a direct call to \ref setSelectedParts.
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 /* end of documentation of signals */
4103 /* end of documentation of signals */
4046
4104
4047 /*!
4105 /*!
@@ -4053,27 +4111,21 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4053 // axis base:
4111 // axis base:
4054 mAxisType(type),
4112 mAxisType(type),
4055 mAxisRect(parent),
4113 mAxisRect(parent),
4056 mOffset(0),
4057 mPadding(5),
4114 mPadding(5),
4058 mOrientation((type == atBottom || type == atTop) ? Qt::Horizontal : Qt::Vertical),
4115 mOrientation(orientation(type)),
4059 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4116 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4060 mSelectedParts(spNone),
4117 mSelectedParts(spNone),
4061 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4118 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4062 mSelectedBasePen(QPen(Qt::blue, 2)),
4119 mSelectedBasePen(QPen(Qt::blue, 2)),
4063 mLowerEnding(QCPLineEnding::esNone),
4064 mUpperEnding(QCPLineEnding::esNone),
4065 // axis label:
4120 // axis label:
4066 mLabelPadding(0),
4067 mLabel(""),
4121 mLabel(""),
4068 mLabelFont(mParentPlot->font()),
4122 mLabelFont(mParentPlot->font()),
4069 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4123 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4070 mLabelColor(Qt::black),
4124 mLabelColor(Qt::black),
4071 mSelectedLabelColor(Qt::blue),
4125 mSelectedLabelColor(Qt::blue),
4072 // tick labels:
4126 // tick labels:
4073 mTickLabelPadding(0),
4074 mTickLabels(true),
4127 mTickLabels(true),
4075 mAutoTickLabels(true),
4128 mAutoTickLabels(true),
4076 mTickLabelRotation(0),
4077 mTickLabelType(ltNumber),
4129 mTickLabelType(ltNumber),
4078 mTickLabelFont(mParentPlot->font()),
4130 mTickLabelFont(mParentPlot->font()),
4079 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4131 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
@@ -4084,7 +4136,6 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4084 mNumberPrecision(6),
4136 mNumberPrecision(6),
4085 mNumberFormatChar('g'),
4137 mNumberFormatChar('g'),
4086 mNumberBeautifulPowers(true),
4138 mNumberBeautifulPowers(true),
4087 mNumberMultiplyCross(false),
4088 // ticks and subticks:
4139 // ticks and subticks:
4089 mTicks(true),
4140 mTicks(true),
4090 mTickStep(1),
4141 mTickStep(1),
@@ -4093,10 +4144,6 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4093 mAutoTicks(true),
4144 mAutoTicks(true),
4094 mAutoTickStep(true),
4145 mAutoTickStep(true),
4095 mAutoSubTicks(true),
4146 mAutoSubTicks(true),
4096 mTickLengthIn(5),
4097 mTickLengthOut(0),
4098 mSubTickLengthIn(2),
4099 mSubTickLengthOut(0),
4100 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4147 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4101 mSelectedTickPen(QPen(Qt::blue, 2)),
4148 mSelectedTickPen(QPen(Qt::blue, 2)),
4102 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4149 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
@@ -4109,11 +4156,9 QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
4109 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4156 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4110 // internal members:
4157 // internal members:
4111 mGrid(new QCPGrid(this)),
4158 mGrid(new QCPGrid(this)),
4112 mLabelCache(16), // cache at most 16 (tick) labels
4159 mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4113 mLowestVisibleTick(0),
4160 mLowestVisibleTick(0),
4114 mHighestVisibleTick(-1),
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 mCachedMarginValid(false),
4162 mCachedMarginValid(false),
4118 mCachedMargin(0)
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 /* No documentation as it is a property getter */
4205 /* No documentation as it is a property getter */
4144 QString QCPAxis::numberFormat() const
4206 QString QCPAxis::numberFormat() const
4145 {
4207 {
@@ -4148,12 +4210,60 QString QCPAxis::numberFormat() const
4148 if (mNumberBeautifulPowers)
4210 if (mNumberBeautifulPowers)
4149 {
4211 {
4150 result.append("b");
4212 result.append("b");
4151 if (mNumberMultiplyCross)
4213 if (mAxisPainter->numberMultiplyCross)
4152 result.append("c");
4214 result.append("c");
4153 }
4215 }
4154 return result;
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 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4268 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4159 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
4269 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
@@ -4167,14 +4277,15 QString QCPAxis::numberFormat() const
4167 part). To only display the decimal power, set the number precision to zero with
4277 part). To only display the decimal power, set the number precision to zero with
4168 \ref setNumberPrecision.
4278 \ref setNumberPrecision.
4169 */
4279 */
4170 void QCPAxis::setScaleType(ScaleType type)
4280 void QCPAxis::setScaleType(QCPAxis::ScaleType type)
4171 {
4281 {
4172 if (mScaleType != type)
4282 if (mScaleType != type)
4173 {
4283 {
4174 mScaleType = type;
4284 mScaleType = type;
4175 if (mScaleType == stLogarithmic)
4285 if (mScaleType == stLogarithmic)
4176 mRange = mRange.sanitizedForLogScale();
4286 setRange(mRange.sanitizedForLogScale());
4177 mCachedMarginValid = false;
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 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4347 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4237 {
4348 {
4349 if (mSelectableParts != selectable)
4350 {
4238 mSelectableParts = selectable;
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 if (mSelectedParts != selected)
4373 if (mSelectedParts != selected)
4259 {
4374 {
4260 if (mSelectedParts.testFlag(spTickLabels) != selected.testFlag(spTickLabels))
4261 mLabelCache.clear();
4262 mSelectedParts = selected;
4375 mSelectedParts = selected;
4263 emit selectionChanged(mSelectedParts);
4376 emit selectionChanged(mSelectedParts);
4264 }
4377 }
@@ -4523,9 +4636,9 void QCPAxis::setTickLabels(bool show)
4523 */
4636 */
4524 void QCPAxis::setTickLabelPadding(int padding)
4637 void QCPAxis::setTickLabelPadding(int padding)
4525 {
4638 {
4526 if (mTickLabelPadding != padding)
4639 if (mAxisPainter->tickLabelPadding != padding)
4527 {
4640 {
4528 mTickLabelPadding = padding;
4641 mAxisPainter->tickLabelPadding = padding;
4529 mCachedMarginValid = false;
4642 mCachedMarginValid = false;
4530 }
4643 }
4531 }
4644 }
@@ -4571,7 +4684,6 void QCPAxis::setTickLabelFont(const QFo
4571 {
4684 {
4572 mTickLabelFont = font;
4685 mTickLabelFont = font;
4573 mCachedMarginValid = false;
4686 mCachedMarginValid = false;
4574 mLabelCache.clear();
4575 }
4687 }
4576 }
4688 }
4577
4689
@@ -4586,7 +4698,6 void QCPAxis::setTickLabelColor(const QC
4586 {
4698 {
4587 mTickLabelColor = color;
4699 mTickLabelColor = color;
4588 mCachedMarginValid = false;
4700 mCachedMarginValid = false;
4589 mLabelCache.clear();
4590 }
4701 }
4591 }
4702 }
4592
4703
@@ -4601,11 +4712,10 void QCPAxis::setTickLabelColor(const QC
4601 */
4712 */
4602 void QCPAxis::setTickLabelRotation(double degrees)
4713 void QCPAxis::setTickLabelRotation(double degrees)
4603 {
4714 {
4604 if (!qFuzzyIsNull(degrees-mTickLabelRotation))
4715 if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4605 {
4716 {
4606 mTickLabelRotation = qBound(-90.0, degrees, 90.0);
4717 mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4607 mCachedMarginValid = false;
4718 mCachedMarginValid = false;
4608 mLabelCache.clear();
4609 }
4719 }
4610 }
4720 }
4611
4721
@@ -4623,7 +4733,6 void QCPAxis::setDateTimeFormat(const QS
4623 {
4733 {
4624 mDateTimeFormat = format;
4734 mDateTimeFormat = format;
4625 mCachedMarginValid = false;
4735 mCachedMarginValid = false;
4626 mLabelCache.clear();
4627 }
4736 }
4628 }
4737 }
4629
4738
@@ -4685,7 +4794,6 void QCPAxis::setNumberFormat(const QStr
4685 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4794 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4686 return;
4795 return;
4687 }
4796 }
4688 mLabelCache.clear();
4689 mCachedMarginValid = false;
4797 mCachedMarginValid = false;
4690
4798
4691 // interpret first char as number format char:
4799 // interpret first char as number format char:
@@ -4701,7 +4809,7 void QCPAxis::setNumberFormat(const QStr
4701 if (formatCode.length() < 2)
4809 if (formatCode.length() < 2)
4702 {
4810 {
4703 mNumberBeautifulPowers = false;
4811 mNumberBeautifulPowers = false;
4704 mNumberMultiplyCross = false;
4812 mAxisPainter->numberMultiplyCross = false;
4705 return;
4813 return;
4706 }
4814 }
4707
4815
@@ -4716,17 +4824,17 void QCPAxis::setNumberFormat(const QStr
4716 }
4824 }
4717 if (formatCode.length() < 3)
4825 if (formatCode.length() < 3)
4718 {
4826 {
4719 mNumberMultiplyCross = false;
4827 mAxisPainter->numberMultiplyCross = false;
4720 return;
4828 return;
4721 }
4829 }
4722
4830
4723 // interpret third char as indicator for dot or cross multiplication symbol:
4831 // interpret third char as indicator for dot or cross multiplication symbol:
4724 if (formatCode.at(2) == 'c')
4832 if (formatCode.at(2) == 'c')
4725 {
4833 {
4726 mNumberMultiplyCross = true;
4834 mAxisPainter->numberMultiplyCross = true;
4727 } else if (formatCode.at(2) == 'd')
4835 } else if (formatCode.at(2) == 'd')
4728 {
4836 {
4729 mNumberMultiplyCross = false;
4837 mAxisPainter->numberMultiplyCross = false;
4730 } else
4838 } else
4731 {
4839 {
4732 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
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 zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4920 zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4813 they won't collide with the ticks.
4921 they won't collide with the ticks.
4814
4922
4815 \see setSubTickLength
4923 \see setSubTickLength, setTickLengthIn, setTickLengthOut
4816 */
4924 */
4817 void QCPAxis::setTickLength(int inside, int outside)
4925 void QCPAxis::setTickLength(int inside, int outside)
4818 {
4926 {
4819 if (mTickLengthIn != inside)
4927 setTickLengthIn(inside);
4820 {
4928 setTickLengthOut(outside);
4821 mTickLengthIn = inside;
4822 }
4823 if (mTickLengthOut != outside)
4824 {
4825 mTickLengthOut = outside;
4826 mCachedMarginValid = false; // only outside tick length can change margin
4827 }
4828 }
4929 }
4829
4930
4830 /*!
4931 /*!
4831 Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4932 Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4832 inside the plot.
4933 inside the plot.
4833
4934
4834 \see setTickLengthOut, setSubTickLength
4935 \see setTickLengthOut, setTickLength, setSubTickLength
4835 */
4936 */
4836 void QCPAxis::setTickLengthIn(int inside)
4937 void QCPAxis::setTickLengthIn(int inside)
4837 {
4938 {
4838 if (mTickLengthIn != inside)
4939 if (mAxisPainter->tickLengthIn != inside)
4839 {
4940 {
4840 mTickLengthIn = inside;
4941 mAxisPainter->tickLengthIn = inside;
4841 }
4942 }
4842 }
4943 }
4843
4944
@@ -4846,13 +4947,13 void QCPAxis::setTickLengthIn(int inside
4846 outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4947 outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4847 increase their distance to the axis accordingly, so they won't collide with the ticks.
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 void QCPAxis::setTickLengthOut(int outside)
4952 void QCPAxis::setTickLengthOut(int outside)
4852 {
4953 {
4853 if (mTickLengthOut != outside)
4954 if (mAxisPainter->tickLengthOut != outside)
4854 {
4955 {
4855 mTickLengthOut = outside;
4956 mAxisPainter->tickLengthOut = outside;
4856 mCachedMarginValid = false; // only outside tick length can change margin
4957 mCachedMarginValid = false; // only outside tick length can change margin
4857 }
4958 }
4858 }
4959 }
@@ -4878,31 +4979,26 void QCPAxis::setSubTickCount(int count)
4878 the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4979 the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4879 than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4980 than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4880 so they won't collide with the ticks.
4981 so they won't collide with the ticks.
4982
4983 \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4881 */
4984 */
4882 void QCPAxis::setSubTickLength(int inside, int outside)
4985 void QCPAxis::setSubTickLength(int inside, int outside)
4883 {
4986 {
4884 if (mSubTickLengthIn != inside)
4987 setSubTickLengthIn(inside);
4885 {
4988 setSubTickLengthOut(outside);
4886 mSubTickLengthIn = inside;
4887 }
4888 if (mSubTickLengthOut != outside)
4889 {
4890 mSubTickLengthOut = outside;
4891 mCachedMarginValid = false; // only outside tick length can change margin
4892 }
4893 }
4989 }
4894
4990
4895 /*!
4991 /*!
4896 Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4992 Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4897 the plot.
4993 the plot.
4898
4994
4899 \see setSubTickLengthOut, setTickLength
4995 \see setSubTickLengthOut, setSubTickLength, setTickLength
4900 */
4996 */
4901 void QCPAxis::setSubTickLengthIn(int inside)
4997 void QCPAxis::setSubTickLengthIn(int inside)
4902 {
4998 {
4903 if (mSubTickLengthIn != inside)
4999 if (mAxisPainter->subTickLengthIn != inside)
4904 {
5000 {
4905 mSubTickLengthIn = inside;
5001 mAxisPainter->subTickLengthIn = inside;
4906 }
5002 }
4907 }
5003 }
4908
5004
@@ -4911,13 +5007,13 void QCPAxis::setSubTickLengthIn(int ins
4911 outside the plot. If \a outside is greater than zero, the tick labels will increase their
5007 outside the plot. If \a outside is greater than zero, the tick labels will increase their
4912 distance to the axis accordingly, so they won't collide with the ticks.
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 void QCPAxis::setSubTickLengthOut(int outside)
5012 void QCPAxis::setSubTickLengthOut(int outside)
4917 {
5013 {
4918 if (mSubTickLengthOut != outside)
5014 if (mAxisPainter->subTickLengthOut != outside)
4919 {
5015 {
4920 mSubTickLengthOut = outside;
5016 mAxisPainter->subTickLengthOut = outside;
4921 mCachedMarginValid = false; // only outside tick length can change margin
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 void QCPAxis::setLabelPadding(int padding)
5093 void QCPAxis::setLabelPadding(int padding)
4998 {
5094 {
4999 if (mLabelPadding != padding)
5095 if (mAxisPainter->labelPadding != padding)
5000 {
5096 {
5001 mLabelPadding = padding;
5097 mAxisPainter->labelPadding = padding;
5002 mCachedMarginValid = false;
5098 mCachedMarginValid = false;
5003 }
5099 }
5004 }
5100 }
@@ -5025,12 +5121,14 void QCPAxis::setPadding(int padding)
5025 /*!
5121 /*!
5026 Sets the offset the axis has to its axis rect side.
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
5124 If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
5029 is controlled automatically, to place the axes at appropriate positions to prevent them from overlapping.
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 void QCPAxis::setOffset(int offset)
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 if (font != mSelectedTickLabelFont)
5141 if (font != mSelectedTickLabelFont)
5044 {
5142 {
5045 mSelectedTickLabelFont = font;
5143 mSelectedTickLabelFont = font;
5046 mLabelCache.clear();
5047 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
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 if (color != mSelectedTickLabelColor)
5166 if (color != mSelectedTickLabelColor)
5070 {
5167 {
5071 mSelectedTickLabelColor = color;
5168 mSelectedTickLabelColor = color;
5072 mLabelCache.clear();
5073 }
5169 }
5074 }
5170 }
5075
5171
@@ -5125,7 +5221,7 void QCPAxis::setSelectedSubTickPen(cons
5125 */
5221 */
5126 void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
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 void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
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 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5343 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5248 continue;
5344 continue;
5249 QCPRange plottableRange;
5345 QCPRange plottableRange;
5250 bool validRange;
5346 bool currentFoundRange;
5251 QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5347 QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5252 if (mScaleType == stLogarithmic)
5348 if (mScaleType == stLogarithmic)
5253 signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5349 signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5254 if (p.at(i)->keyAxis() == this)
5350 if (p.at(i)->keyAxis() == this)
5255 plottableRange = p.at(i)->getKeyRange(validRange, signDomain);
5351 plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5256 else
5352 else
5257 plottableRange = p.at(i)->getValueRange(validRange, signDomain);
5353 plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5258 if (validRange)
5354 if (currentFoundRange)
5259 {
5355 {
5260 if (!haveRange)
5356 if (!haveRange)
5261 newRange = plottableRange;
5357 newRange = plottableRange;
@@ -5387,11 +5483,11 QCPAxis::SelectablePart QCPAxis::getPart
5387 if (!mVisible)
5483 if (!mVisible)
5388 return spNone;
5484 return spNone;
5389
5485
5390 if (mAxisSelectionBox.contains(pos.toPoint()))
5486 if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5391 return spAxis;
5487 return spAxis;
5392 else if (mTickLabelsSelectionBox.contains(pos.toPoint()))
5488 else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5393 return spTickLabels;
5489 return spTickLabels;
5394 else if (mLabelSelectionBox.contains(pos.toPoint()))
5490 else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5395 return spAxisLabel;
5491 return spAxisLabel;
5396 else
5492 else
5397 return spNone;
5493 return spNone;
@@ -5492,6 +5588,21 QCPAxis::AxisType QCPAxis::marginSideToA
5492 return atLeft;
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 /*! \internal
5606 /*! \internal
5496
5607
5497 This function is called to prepare the tick vector, sub tick vector and tick label vector. If
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 // generate tick labels according to tick positions:
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 if (mAutoTickLabels)
5666 if (mAutoTickLabels)
5558 {
5667 {
5559 int vecsize = mTickVector.size();
5668 int vecsize = mTickVector.size();
@@ -5726,515 +5835,6 int QCPAxis::calculateAutoSubTickCount(d
5726 return result;
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 /* inherits documentation from base class */
5838 /* inherits documentation from base class */
6239 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
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 /*! \internal
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 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
5935 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
6282 the current range. The return values are indices of the tick vector, not the positions of the
5936 the current range. The return values are indices of the tick vector, not the positions of the
6283 ticks themselves.
5937 ticks themselves.
@@ -6435,37 +6089,41 QColor QCPAxis::getLabelColor() const
6435 */
6089 */
6436 int QCPAxis::calculateMargin()
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 if (mCachedMarginValid)
6095 if (mCachedMarginValid)
6439 return mCachedMargin;
6096 return mCachedMargin;
6440
6097
6441 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6098 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
6442 int margin = 0;
6099 int margin = 0;
6443
6100
6444 if (mVisible)
6445 {
6446 int lowTick, highTick;
6101 int lowTick, highTick;
6447 visibleTickBounds(lowTick, highTick);
6102 visibleTickBounds(lowTick, highTick);
6448 // get length of tick marks pointing outwards:
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);
6449 if (mTicks)
6107 if (mTicks)
6450 margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
6451 // calculate size of tick labels:
6452 QSize tickLabelsSize(0, 0);
6453 if (mTickLabels)
6454 {
6108 {
6455 for (int i=lowTick; i<=highTick; ++i)
6109 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
6110 {
6457 margin += orientation() == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6111 tickPositions.append(coordToPixel(mTickVector.at(i)));
6458 margin += mTickLabelPadding;
6112 if (mTickLabels)
6459 }
6113 tickLabels.append(mTickVectorLabels.at(i));
6460 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6114 }
6461 if (!mLabel.isEmpty())
6115 }
6462 {
6116 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6463 QFontMetrics fontMetrics(mLabelFont); // don't use getLabelFont() because we don't want margin to possibly change on selection
6117 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6464 QRect bounds;
6118 mAxisPainter->type = mAxisType;
6465 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, mLabel);
6119 mAxisPainter->labelFont = getLabelFont();
6466 margin += bounds.height() + mLabelPadding;
6120 mAxisPainter->label = mLabel;
6467 }
6121 mAxisPainter->tickLabelFont = mTickLabelFont;
6468 }
6122 mAxisPainter->alignmentRect = mAxisRect->rect();
6123 mAxisPainter->viewportRect = mParentPlot->viewport();
6124 mAxisPainter->tickPositions = tickPositions;
6125 mAxisPainter->tickLabels = tickLabels;
6126 margin += mAxisPainter->size();
6469 margin += mPadding;
6127 margin += mPadding;
6470
6128
6471 mCachedMargin = margin;
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 //////////////////// QCPAbstractPlottable
6755 //////////////////// QCPAbstractPlottable
6485 ////////////////////////////////////////////////////////////////////////////////////////////////////
6756 ////////////////////////////////////////////////////////////////////////////////////////////////////
6486
6757
@@ -6563,30 +6834,36 QCP::Interaction QCPAxis::selectionCateg
6563 of this plottable inside \a rect, next to the plottable name.
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 \internal
6838 \internal
6568
6839
6569 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6840 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6570 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6841 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6571 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6842 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6572 to \ref sdNegative and all positive points will be ignored for range calculation. For no
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
6844 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6574 parameter that indicates whether a proper range could be found or not. If this is false, you
6845 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6575 shouldn't use the returned range (e.g. no points in data).
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 \see rescaleAxes, getValueRange
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 \internal
6855 \internal
6582
6856
6583 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6857 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6584 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6858 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6585 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6859 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6586 to \ref sdNegative and all positive points will be ignored for range calculation. For no
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
6861 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6588 parameter that indicates whether a proper range could be found or not. If this is false, you
6862 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6589 shouldn't use the returned range (e.g. no points in data).
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 \see rescaleAxes, getKeyRange
6868 \see rescaleAxes, getKeyRange
6592 */
6869 */
@@ -6596,8 +6873,15 QCP::Interaction QCPAxis::selectionCateg
6596
6873
6597 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
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,
6876 This signal is emitted when the selection state of this plottable has changed, either by user
6600 either by user interaction or by a direct call to \ref setSelected.
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 /* end of documentation of signals */
6887 /* end of documentation of signals */
@@ -6770,7 +7054,11 void QCPAbstractPlottable::setValueAxis(
6770 */
7054 */
6771 void QCPAbstractPlottable::setSelectable(bool selectable)
7055 void QCPAbstractPlottable::setSelectable(bool selectable)
6772 {
7056 {
7057 if (mSelectable != selectable)
7058 {
6773 mSelectable = selectable;
7059 mSelectable = selectable;
7060 emit selectableChanged(mSelectable);
7061 }
6774 }
7062 }
6775
7063
6776 /*!
7064 /*!
@@ -6829,9 +7117,9 void QCPAbstractPlottable::rescaleKeyAxi
6829 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7117 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
6830 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7118 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
6831
7119
6832 bool rangeValid;
7120 bool foundRange;
6833 QCPRange newRange = getKeyRange(rangeValid, signDomain);
7121 QCPRange newRange = getKeyRange(foundRange, signDomain);
6834 if (rangeValid)
7122 if (foundRange)
6835 {
7123 {
6836 if (onlyEnlarge)
7124 if (onlyEnlarge)
6837 newRange.expand(keyAxis->range());
7125 newRange.expand(keyAxis->range());
@@ -6869,9 +7157,9 void QCPAbstractPlottable::rescaleValueA
6869 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7157 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
6870 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7158 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
6871
7159
6872 bool rangeValid;
7160 bool foundRange;
6873 QCPRange newRange = getValueRange(rangeValid, signDomain);
7161 QCPRange newRange = getValueRange(foundRange, signDomain);
6874 if (rangeValid)
7162 if (foundRange)
6875 {
7163 {
6876 if (onlyEnlarge)
7164 if (onlyEnlarge)
6877 newRange.expand(valueAxis->range());
7165 newRange.expand(valueAxis->range());
@@ -7976,7 +8264,11 void QCPAbstractItem::setClipAxisRect(QC
7976 */
8264 */
7977 void QCPAbstractItem::setSelectable(bool selectable)
8265 void QCPAbstractItem::setSelectable(bool selectable)
7978 {
8266 {
8267 if (mSelectable != selectable)
8268 {
7979 mSelectable = selectable;
8269 mSelectable = selectable;
8270 emit selectableChanged(mSelectable);
8271 }
7980 }
8272 }
7981
8273
7982 /*!
8274 /*!
@@ -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 \image html qcp-doc-logo.png
8580 \image html qcp-doc-logo.png
8289
8581
@@ -8329,7 +8621,8 QCP::Interaction QCPAbstractItem::select
8329 usual, if the cast returns zero, the plottable wasn't of that specific subclass.)
8621 usual, if the cast returns zero, the plottable wasn't of that specific subclass.)
8330
8622
8331 All further interfacing with plottables (e.g how to set data) is specific to the plottable type.
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 \section mainpage-axes Controlling the Axes
8627 \section mainpage-axes Controlling the Axes
8335
8628
@@ -8356,20 +8649,20 QCP::Interaction QCPAbstractItem::select
8356
8649
8357 \section mainpage-legend Plot Legend
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
8652 Every QCustomPlot has one QCPLegend (as \ref QCustomPlot::legend) by default. A legend is a small
8360 element inside the plot which lists the plottables with an icon of the plottable line/symbol and
8653 layout element inside the plot which lists the plottables with an icon of the plottable
8361 a description. The Description is retrieved from the plottable name
8654 line/symbol and a name (QCPAbstractPlottable::setName). Plottables can be added and removed from
8362 (QCPAbstractPlottable::setName). Plottables can be added and removed from the legend via \ref
8655 the main legend via \ref QCPAbstractPlottable::addToLegend and \ref
8363 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend. By default,
8656 QCPAbstractPlottable::removeFromLegend. By default, adding a plottable to QCustomPlot
8364 adding a plottable to QCustomPlot automatically adds it to the legend, too. This behaviour can be
8657 automatically adds it to the legend, too. This behaviour can be modified with the
8365 modified with the QCustomPlot::setAutoAddPlottableToLegend property.
8658 QCustomPlot::setAutoAddPlottableToLegend property.
8366
8659
8367 The QCPLegend provides an interface to access, add and remove legend items directly, too. See
8660 The QCPLegend provides an interface to access, add and remove legend items directly, too. See
8368 QCPLegend::item, QCPLegend::itemWithPlottable, QCPLegend::addItem, QCPLegend::removeItem for
8661 QCPLegend::item, QCPLegend::itemWithPlottable, QCPLegend::addItem, QCPLegend::removeItem for
8369 example.
8662 example.
8370
8663
8371 Multiple legends are supported via the layout system (as a QCPLegend simply is a normal layout
8664 Multiple legends are supported via the \link thelayoutsystem layout system\endlink (as a
8372 element).
8665 QCPLegend simply is a normal layout element).
8373
8666
8374 \section mainpage-userinteraction User Interactions
8667 \section mainpage-userinteraction User Interactions
8375
8668
@@ -8820,6 +9113,7 QCustomPlot::QCustomPlot(QWidget *parent
8820 mPlotLayout = new QCPLayoutGrid;
9113 mPlotLayout = new QCPLayoutGrid;
8821 mPlotLayout->initializeParentPlot(this);
9114 mPlotLayout->initializeParentPlot(this);
8822 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9115 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9116 mPlotLayout->setLayer("main");
8823 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9117 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
8824 mPlotLayout->addElement(0, 0, defaultAxisRect);
9118 mPlotLayout->addElement(0, 0, defaultAxisRect);
8825 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9119 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
@@ -9354,10 +9648,10 int QCustomPlot::plottableCount() const
9354 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9648 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9355 {
9649 {
9356 QList<QCPAbstractPlottable*> result;
9650 QList<QCPAbstractPlottable*> result;
9357 for (int i=0; i<mPlottables.size(); ++i)
9651 foreach (QCPAbstractPlottable *plottable, mPlottables)
9358 {
9652 {
9359 if (mPlottables.at(i)->selected())
9653 if (plottable->selected())
9360 result.append(mPlottables.at(i));
9654 result.append(plottable);
9361 }
9655 }
9362 return result;
9656 return result;
9363 }
9657 }
@@ -9379,17 +9673,16 QCPAbstractPlottable *QCustomPlot::plott
9379 QCPAbstractPlottable *resultPlottable = 0;
9673 QCPAbstractPlottable *resultPlottable = 0;
9380 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
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)
9676 foreach (QCPAbstractPlottable *plottable, mPlottables)
9383 {
9677 {
9384 QCPAbstractPlottable *currentPlottable = mPlottables.at(i);
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
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
9386 continue;
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
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
9388 {
9681 {
9389 double currentDistance = currentPlottable->selectTest(pos, false);
9682 double currentDistance = plottable->selectTest(pos, false);
9390 if (currentDistance >= 0 && currentDistance < resultDistance)
9683 if (currentDistance >= 0 && currentDistance < resultDistance)
9391 {
9684 {
9392 resultPlottable = currentPlottable;
9685 resultPlottable = plottable;
9393 resultDistance = currentDistance;
9686 resultDistance = currentDistance;
9394 }
9687 }
9395 }
9688 }
@@ -9545,10 +9838,10 int QCustomPlot::graphCount() const
9545 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9838 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9546 {
9839 {
9547 QList<QCPGraph*> result;
9840 QList<QCPGraph*> result;
9548 for (int i=0; i<mGraphs.size(); ++i)
9841 foreach (QCPGraph *graph, mGraphs)
9549 {
9842 {
9550 if (mGraphs.at(i)->selected())
9843 if (graph->selected())
9551 result.append(mGraphs.at(i));
9844 result.append(graph);
9552 }
9845 }
9553 return result;
9846 return result;
9554 }
9847 }
@@ -9679,10 +9972,10 int QCustomPlot::itemCount() const
9679 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9972 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9680 {
9973 {
9681 QList<QCPAbstractItem*> result;
9974 QList<QCPAbstractItem*> result;
9682 for (int i=0; i<mItems.size(); ++i)
9975 foreach (QCPAbstractItem *item, mItems)
9683 {
9976 {
9684 if (mItems.at(i)->selected())
9977 if (item->selected())
9685 result.append(mItems.at(i));
9978 result.append(item);
9686 }
9979 }
9687 return result;
9980 return result;
9688 }
9981 }
@@ -9705,17 +9998,16 QCPAbstractItem *QCustomPlot::itemAt(con
9705 QCPAbstractItem *resultItem = 0;
9998 QCPAbstractItem *resultItem = 0;
9706 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
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)
10001 foreach (QCPAbstractItem *item, mItems)
9709 {
10002 {
9710 QCPAbstractItem *currentItem = mItems[i];
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
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
9712 continue;
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
10005 if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9714 {
10006 {
9715 double currentDistance = currentItem->selectTest(pos, false);
10007 double currentDistance = item->selectTest(pos, false);
9716 if (currentDistance >= 0 && currentDistance < resultDistance)
10008 if (currentDistance >= 0 && currentDistance < resultDistance)
9717 {
10009 {
9718 resultItem = currentItem;
10010 resultItem = item;
9719 resultDistance = currentDistance;
10011 resultDistance = currentDistance;
9720 }
10012 }
9721 }
10013 }
@@ -9744,10 +10036,10 bool QCustomPlot::hasItem(QCPAbstractIte
9744 */
10036 */
9745 QCPLayer *QCustomPlot::layer(const QString &name) const
10037 QCPLayer *QCustomPlot::layer(const QString &name) const
9746 {
10038 {
9747 for (int i=0; i<mLayers.size(); ++i)
10039 foreach (QCPLayer *layer, mLayers)
9748 {
10040 {
9749 if (mLayers.at(i)->name() == name)
10041 if (layer->name() == name)
9750 return mLayers.at(i);
10042 return layer;
9751 }
10043 }
9752 return 0;
10044 return 0;
9753 }
10045 }
@@ -9992,10 +10284,9 QList<QCPAxisRect*> QCustomPlot::axisRec
9992
10284
9993 while (!elementStack.isEmpty())
10285 while (!elementStack.isEmpty())
9994 {
10286 {
9995 QList<QCPLayoutElement*> subElements = elementStack.pop()->elements(false);
10287 foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
9996 for (int i=0; i<subElements.size(); ++i)
10288 {
9997 {
10289 if (element)
9998 if (QCPLayoutElement *element = subElements.at(i))
9999 {
10290 {
10000 elementStack.push(element);
10291 elementStack.push(element);
10001 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10292 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
@@ -10018,23 +10309,22 QList<QCPAxisRect*> QCustomPlot::axisRec
10018 */
10309 */
10019 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10310 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10020 {
10311 {
10021 QCPLayoutElement *current = mPlotLayout;
10312 QCPLayoutElement *currentElement = mPlotLayout;
10022 bool searchSubElements = true;
10313 bool searchSubElements = true;
10023 while (searchSubElements && current)
10314 while (searchSubElements && currentElement)
10024 {
10315 {
10025 searchSubElements = false;
10316 searchSubElements = false;
10026 const QList<QCPLayoutElement*> elements = current->elements(false);
10317 foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10027 for (int i=0; i<elements.size(); ++i)
10318 {
10028 {
10319 if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10029 if (elements.at(i) && elements.at(i)->realVisibility() && elements.at(i)->selectTest(pos, false) >= 0)
10320 {
10030 {
10321 currentElement = subElement;
10031 current = elements.at(i);
10032 searchSubElements = true;
10322 searchSubElements = true;
10033 break;
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 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10337 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10048 {
10338 {
10049 QList<QCPAxis*> result, allAxes;
10339 QList<QCPAxis*> result, allAxes;
10050 QList<QCPAxisRect*> rects = axisRects();
10340 foreach (QCPAxisRect *rect, axisRects())
10051 for (int i=0; i<rects.size(); ++i)
10341 allAxes << rect->axes();
10052 allAxes << rects.at(i)->axes();
10342
10053
10343 foreach (QCPAxis *axis, allAxes)
10054 for (int i=0; i<allAxes.size(); ++i)
10344 {
10055 {
10345 if (axis->selectedParts() != QCPAxis::spNone)
10056 if (allAxes.at(i)->selectedParts() != QCPAxis::spNone)
10346 result.append(axis);
10057 result.append(allAxes.at(i));
10058 }
10347 }
10059
10348
10060 return result;
10349 return result;
@@ -10077,13 +10366,12 QList<QCPLegend*> QCustomPlot::selectedL
10077
10366
10078 while (!elementStack.isEmpty())
10367 while (!elementStack.isEmpty())
10079 {
10368 {
10080 QList<QCPLayoutElement*> subElements = elementStack.pop()->elements(false);
10369 foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10081 for (int i=0; i<subElements.size(); ++i)
10370 {
10082 {
10371 if (subElement)
10083 if (QCPLayoutElement *element = subElements.at(i))
10372 {
10084 {
10373 elementStack.push(subElement);
10085 elementStack.push(element);
10374 if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10086 if (QCPLegend *leg = qobject_cast<QCPLegend*>(element))
10087 {
10375 {
10088 if (leg->selectedParts() != QCPLegend::spNone)
10376 if (leg->selectedParts() != QCPLegend::spNone)
10089 result.append(leg);
10377 result.append(leg);
@@ -10106,11 +10394,10 QList<QCPLegend*> QCustomPlot::selectedL
10106 */
10394 */
10107 void QCustomPlot::deselectAll()
10395 void QCustomPlot::deselectAll()
10108 {
10396 {
10109 for (int i=0; i<mLayers.size(); ++i)
10397 foreach (QCPLayer *layer, mLayers)
10110 {
10398 {
10111 QList<QCPLayerable*> layerables = mLayers.at(i)->children();
10399 foreach (QCPLayerable *layerable, layer->children())
10112 for (int k=0; k<layerables.size(); ++k)
10400 layerable->deselectEvent(0);
10113 layerables.at(k)->deselectEvent(0);
10114 }
10401 }
10115 }
10402 }
10116
10403
@@ -10127,12 +10414,13 void QCustomPlot::deselectAll()
10127 signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10414 signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10128 recursion.
10415 recursion.
10129 */
10416 */
10130 void QCustomPlot::replot()
10417 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
10131 {
10418 {
10132 if (mReplotting) // incase signals loop back to replot slot
10419 if (mReplotting) // incase signals loop back to replot slot
10133 return;
10420 return;
10134 mReplotting = true;
10421 mReplotting = true;
10135 emit beforeReplot();
10422 emit beforeReplot();
10423
10136 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10424 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10137 QCPPainter painter;
10425 QCPPainter painter;
10138 painter.begin(&mPaintBuffer);
10426 painter.begin(&mPaintBuffer);
@@ -10143,12 +10431,13 void QCustomPlot::replot()
10143 painter.fillRect(mViewport, mBackgroundBrush);
10431 painter.fillRect(mViewport, mBackgroundBrush);
10144 draw(&painter);
10432 draw(&painter);
10145 painter.end();
10433 painter.end();
10146 if (mPlottingHints.testFlag(QCP::phForceRepaint))
10434 if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10147 repaint();
10435 repaint();
10148 else
10436 else
10149 update();
10437 update();
10150 } else // might happen if QCustomPlot has width or height zero
10438 } else // might happen if QCustomPlot has width or height zero
10151 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
10439 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
10440
10152 emit afterReplot();
10441 emit afterReplot();
10153 mReplotting = false;
10442 mReplotting = false;
10154 }
10443 }
@@ -10163,15 +10452,12 void QCustomPlot::replot()
10163 */
10452 */
10164 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10453 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10165 {
10454 {
10166 // get a list of all axes in the plot:
10455 QList<QCPAxis*> allAxes;
10167 QList<QCPAxis*> axes;
10456 foreach (QCPAxisRect *rect, axisRects())
10168 QList<QCPAxisRect*> rects = axisRects();
10457 allAxes << rect->axes();
10169 for (int i=0; i<rects.size(); ++i)
10458
10170 axes << rects.at(i)->axes();
10459 foreach (QCPAxis *axis, allAxes)
10171
10460 axis->rescale(onlyVisiblePlottables);
10172 // call rescale on all axes:
10173 for (int i=0; i<axes.size(); ++i)
10174 axes.at(i)->rescale(onlyVisiblePlottables);
10175 }
10461 }
10176
10462
10177 /*!
10463 /*!
@@ -10203,12 +10489,15 void QCustomPlot::rescaleAxes(bool onlyV
10203 aren't defined yet inside the constructor, so you would get an image that has strange
10489 aren't defined yet inside the constructor, so you would get an image that has strange
10204 widths/heights.
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 \note On Android systems, this method does nothing and issues an according qDebug warning
10495 \note On Android systems, this method does nothing and issues an according qDebug warning
10207 message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10496 message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10208
10497
10209 \see savePng, saveBmp, saveJpg, saveRastered
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 bool success = false;
10502 bool success = false;
10214 #ifdef QT_NO_PRINTER
10503 #ifdef QT_NO_PRINTER
@@ -10234,6 +10523,8 bool QCustomPlot::savePdf(const QString
10234 printer.setOutputFormat(QPrinter::PdfFormat);
10523 printer.setOutputFormat(QPrinter::PdfFormat);
10235 printer.setFullPage(true);
10524 printer.setFullPage(true);
10236 printer.setColorMode(QPrinter::Color);
10525 printer.setColorMode(QPrinter::Color);
10526 printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10527 printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10237 QRect oldViewport = viewport();
10528 QRect oldViewport = viewport();
10238 setViewport(QRect(0, 0, newWidth, newHeight));
10529 setViewport(QRect(0, 0, newWidth, newHeight));
10239 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10530 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
@@ -10421,7 +10712,7 void QCustomPlot::resizeEvent(QResizeEve
10421 // resize and repaint the buffer:
10712 // resize and repaint the buffer:
10422 mPaintBuffer = QPixmap(event->size());
10713 mPaintBuffer = QPixmap(event->size());
10423 setViewport(rect());
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 /*! \internal
10718 /*! \internal
@@ -10536,6 +10827,22 void QCustomPlot::mouseReleaseEvent(QMou
10536 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10827 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10537 bool selectionStateChanged = false;
10828 bool selectionStateChanged = false;
10538 bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
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 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10846 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10540 {
10847 {
10541 // a layerable was actually clicked, call its selectEvent:
10848 // a layerable was actually clicked, call its selectEvent:
@@ -10543,23 +10850,6 void QCustomPlot::mouseReleaseEvent(QMou
10543 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10850 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10544 selectionStateChanged |= selChanged;
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 doReplot = true;
10853 doReplot = true;
10564 if (selectionStateChanged)
10854 if (selectionStateChanged)
10565 emit selectionChangedByUser();
10855 emit selectionChangedByUser();
@@ -10621,28 +10911,19 void QCustomPlot::wheelEvent(QWheelEvent
10621 */
10911 */
10622 void QCustomPlot::draw(QCPPainter *painter)
10912 void QCustomPlot::draw(QCPPainter *painter)
10623 {
10913 {
10624 // update all axis tick vectors:
10914 // run through layout phases:
10625 QList<QCPAxisRect*> rects = axisRects();
10915 mPlotLayout->update(QCPLayoutElement::upPreparation);
10626 for (int i=0; i<rects.size(); ++i)
10916 mPlotLayout->update(QCPLayoutElement::upMargins);
10627 {
10917 mPlotLayout->update(QCPLayoutElement::upLayout);
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();
10635
10918
10636 // draw viewport background pixmap:
10919 // draw viewport background pixmap:
10637 drawBackground(painter);
10920 drawBackground(painter);
10638
10921
10639 // draw all layered objects (grid, axes, plottables, items, legend,...):
10922 // draw all layered objects (grid, axes, plottables, items, legend,...):
10640 for (int layerIndex=0; layerIndex < mLayers.size(); ++layerIndex)
10923 foreach (QCPLayer *layer, mLayers)
10641 {
10924 {
10642 QList<QCPLayerable*> layerChildren = mLayers.at(layerIndex)->children();
10925 foreach (QCPLayerable *child, layer->children())
10643 for (int k=0; k < layerChildren.size(); ++k)
10926 {
10644 {
10645 QCPLayerable *child = layerChildren.at(k);
10646 if (child->realVisibility())
10927 if (child->realVisibility())
10647 {
10928 {
10648 painter->save();
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 /*! \internal
10950 /*! \internal
@@ -10664,7 +10956,7 void QCustomPlot::draw(QCPPainter *paint
10664 the viewport with the provided \a painter. The scaled version is buffered in
10956 the viewport with the provided \a painter. The scaled version is buffered in
10665 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10957 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10666 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
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 set.
10960 set.
10669
10961
10670 Note that this function does not draw a fill with the background brush (\ref setBackground(const
10962 Note that this function does not draw a fill with the background brush (\ref setBackground(const
@@ -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 //////////////////// QCPData
14394 //////////////////// QCPData
10894 ////////////////////////////////////////////////////////////////////////////////////////////////////
14395 ////////////////////////////////////////////////////////////////////////////////////////////////////
10895
14396
@@ -10948,7 +14449,9 QCPData::QCPData(double key, double valu
10948 Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
14449 Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
10949 instance is accessed via QCustomPlot::graph.
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 Graphs are used to display single-valued data. Single-valued means that there should only be one
14456 Graphs are used to display single-valued data. Single-valued means that there should only be one
10954 data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
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 \see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph
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 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
14490 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
10977 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
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 setErrorBarSize(6);
14513 setErrorBarSize(6);
11000 setErrorBarSkipSymbol(true);
14514 setErrorBarSkipSymbol(true);
11001 setChannelFillGraph(0);
14515 setChannelFillGraph(0);
14516 setAdaptiveSampling(true);
11002 }
14517 }
11003
14518
11004 QCPGraph::~QCPGraph()
14519 QCPGraph::~QCPGraph()
@@ -11012,6 +14527,9 QCPGraph::~QCPGraph()
11012 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
14527 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
11013 takes ownership of the passed data and replaces the internal data pointer with it. This is
14528 takes ownership of the passed data and replaces the internal data pointer with it. This is
11014 significantly faster than copying for large datasets.
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 void QCPGraph::setData(QCPDataMap *data, bool copy)
14534 void QCPGraph::setData(QCPDataMap *data, bool copy)
11017 {
14535 {
@@ -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 Adds the provided data points in \a dataMap to the current data.
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 \see removeData
14872 \see removeData
11315 */
14873 */
11316 void QCPGraph::addData(const QCPDataMap &dataMap)
14874 void QCPGraph::addData(const QCPDataMap &dataMap)
@@ -11320,6 +14878,10 void QCPGraph::addData(const QCPDataMap
11320
14878
11321 /*! \overload
14879 /*! \overload
11322 Adds the provided single data point in \a data to the current data.
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 \see removeData
14885 \see removeData
11324 */
14886 */
11325 void QCPGraph::addData(const QCPData &data)
14887 void QCPGraph::addData(const QCPData &data)
@@ -11329,6 +14891,10 void QCPGraph::addData(const QCPData &da
11329
14891
11330 /*! \overload
14892 /*! \overload
11331 Adds the provided single data point as \a key and \a value pair to the current data.
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 \see removeData
14898 \see removeData
11333 */
14899 */
11334 void QCPGraph::addData(double key, double value)
14900 void QCPGraph::addData(double key, double value)
@@ -11341,6 +14907,10 void QCPGraph::addData(double key, doubl
11341
14907
11342 /*! \overload
14908 /*! \overload
11343 Adds the provided data points as \a key and \a value pairs to the current data.
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 \see removeData
14914 \see removeData
11345 */
14915 */
11346 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14916 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
@@ -11422,8 +14992,12 double QCPGraph::selectTest(const QPoint
11422 Q_UNUSED(details)
14992 Q_UNUSED(details)
11423 if ((onlySelectable && !mSelectable) || mData->isEmpty())
14993 if ((onlySelectable && !mSelectable) || mData->isEmpty())
11424 return -1;
14994 return -1;
11425
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()))
11426 return pointDistance(pos);
14998 return pointDistance(pos);
14999 else
15000 return -1;
11427 }
15001 }
11428
15002
11429 /*! \overload
15003 /*! \overload
@@ -11459,10 +15033,10 void QCPGraph::rescaleKeyAxis(bool onlyE
11459 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15033 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
11460 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15034 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
11461
15035
11462 bool validRange;
15036 bool foundRange;
11463 QCPRange newRange = getKeyRange(validRange, signDomain, includeErrorBars);
15037 QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
11464
15038
11465 if (validRange)
15039 if (foundRange)
11466 {
15040 {
11467 if (onlyEnlarge)
15041 if (onlyEnlarge)
11468 {
15042 {
@@ -11495,10 +15069,10 void QCPGraph::rescaleValueAxis(bool onl
11495 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15069 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
11496 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15070 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
11497
15071
11498 bool validRange;
15072 bool foundRange;
11499 QCPRange newRange = getValueRange(validRange, signDomain, includeErrorBars);
15073 QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
11500
15074
11501 if (validRange)
15075 if (foundRange)
11502 {
15076 {
11503 if (onlyEnlarge)
15077 if (onlyEnlarge)
11504 {
15078 {
@@ -11520,12 +15094,12 void QCPGraph::draw(QCPPainter *painter)
11520
15094
11521 // allocate line and (if necessary) point vectors:
15095 // allocate line and (if necessary) point vectors:
11522 QVector<QPointF> *lineData = new QVector<QPointF>;
15096 QVector<QPointF> *lineData = new QVector<QPointF>;
11523 QVector<QCPData> *pointData = 0;
15097 QVector<QCPData> *scatterData = 0;
11524 if (!mScatterStyle.isNone())
15098 if (!mScatterStyle.isNone())
11525 pointData = new QVector<QCPData>;
15099 scatterData = new QVector<QCPData>;
11526
15100
11527 // fill vectors with data appropriate to plot style:
15101 // fill vectors with data appropriate to plot style:
11528 getPlotData(lineData, pointData);
15102 getPlotData(lineData, scatterData);
11529
15103
11530 // check data validity if flag set:
15104 // check data validity if flag set:
11531 #ifdef QCUSTOMPLOT_CHECK_DATA
15105 #ifdef QCUSTOMPLOT_CHECK_DATA
@@ -11549,13 +15123,13 void QCPGraph::draw(QCPPainter *painter)
11549 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15123 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
11550
15124
11551 // draw scatters:
15125 // draw scatters:
11552 if (pointData)
15126 if (scatterData)
11553 drawScatterPlot(painter, pointData);
15127 drawScatterPlot(painter, scatterData);
11554
15128
11555 // free allocated line and point vectors:
15129 // free allocated line and point vectors:
11556 delete lineData;
15130 delete lineData;
11557 if (pointData)
15131 if (scatterData)
11558 delete pointData;
15132 delete scatterData;
11559 }
15133 }
11560
15134
11561 /* inherits documentation from base class */
15135 /* inherits documentation from base class */
@@ -11604,30 +15178,30 void QCPGraph::drawLegendIcon(QCPPainter
11604 make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
15178 make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
11605 untouched.
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 scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
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 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
15185 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
11612 getStepCenterPlotData, getImpulsePlotData
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 switch(mLineStyle)
15190 switch(mLineStyle)
11617 {
15191 {
11618 case lsNone: getScatterPlotData(pointData); break;
15192 case lsNone: getScatterPlotData(scatterData); break;
11619 case lsLine: getLinePlotData(lineData, pointData); break;
15193 case lsLine: getLinePlotData(lineData, scatterData); break;
11620 case lsStepLeft: getStepLeftPlotData(lineData, pointData); break;
15194 case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
11621 case lsStepRight: getStepRightPlotData(lineData, pointData); break;
15195 case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
11622 case lsStepCenter: getStepCenterPlotData(lineData, pointData); break;
15196 case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
11623 case lsImpulse: getImpulsePlotData(lineData, pointData); break;
15197 case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
11624 }
15198 }
11625 }
15199 }
11626
15200
11627 /*! \internal
15201 /*! \internal
11628
15202
11629 If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
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 drawScatterPlot function can draw the scatter points accordingly.
15205 drawScatterPlot function can draw the scatter points accordingly.
11632
15206
11633 If line style is not \ref lsNone, this function is not called and the data for the scatter points
15207 If line style is not \ref lsNone, this function is not called and the data for the scatter points
@@ -11635,89 +15209,48 void QCPGraph::getPlotData(QVector<QPoin
11635
15209
11636 \see drawScatterPlot
15210 \see drawScatterPlot
11637 */
15211 */
11638 void QCPGraph::getScatterPlotData(QVector<QCPData> *pointData) const
15212 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
11639 {
15213 {
11640 if (!pointData) return;
15214 getPreparedData(0, scatterData);
11641 QCPAxis *keyAxis = mKeyAxis.data();
15215 }
11642 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15216
11643
15217 /*! \internal
11644 // get visible data range:
15218
11645 QCPDataMap::const_iterator lower, upper;
15219 Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
11646 int dataCount = 0;
15220
11647 getVisibleDataBounds(lower, upper, dataCount);
15221 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
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)
11671 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15222 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11672 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
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 \see drawLinePlot
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 QCPAxis *keyAxis = mKeyAxis.data();
15230 QCPAxis *keyAxis = mKeyAxis.data();
11680 QCPAxis *valueAxis = mValueAxis.data();
15231 QCPAxis *valueAxis = mValueAxis.data();
11681 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
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; }
15233 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
11683
15234
11684 // get visible data range:
15235 QVector<QCPData> lineData;
11685 QCPDataMap::const_iterator lower, upper;
15236 getPreparedData(&lineData, scatterData);
11686 int dataCount = 0;
15237 linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11687 getVisibleDataBounds(lower, upper, dataCount);
15238 linePixelData->resize(lineData.size());
11688 if (dataCount > 0)
15239
11689 {
15240 // transform lineData points to pixels:
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)
15241 if (keyAxis->orientation() == Qt::Vertical)
11700 {
15242 {
11701 while (it != upperEnd)
15243 for (int i=0; i<lineData.size(); ++i)
11702 {
15244 {
11703 if (pointData)
15245 (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
11704 (*pointData)[i] = it.value();
15246 (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
11705 (*lineData)[i].setX(valueAxis->coordToPixel(it.value().value));
11706 (*lineData)[i].setY(keyAxis->coordToPixel(it.key()));
11707 ++i;
11708 ++it;
11709 }
15247 }
11710 } else // key axis is horizontal
15248 } else // key axis is horizontal
11711 {
15249 {
11712 while (it != upperEnd)
15250 for (int i=0; i<lineData.size(); ++i)
11713 {
15251 {
11714 if (pointData)
15252 (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
11715 (*pointData)[i] = it.value();
15253 (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
11716 (*lineData)[i].setX(keyAxis->coordToPixel(it.key()));
11717 (*lineData)[i].setY(valueAxis->coordToPixel(it.value().value));
11718 ++i;
11719 ++it;
11720 }
11721 }
15254 }
11722 }
15255 }
11723 }
15256 }
@@ -11726,78 +15259,51 void QCPGraph::getLinePlotData(QVector<Q
11726 \internal
15259 \internal
11727 Places the raw data points needed for a step plot with left oriented steps in \a lineData.
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 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15263 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11731 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
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 \see drawLinePlot
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 QCPAxis *keyAxis = mKeyAxis.data();
15271 QCPAxis *keyAxis = mKeyAxis.data();
11739 QCPAxis *valueAxis = mValueAxis.data();
15272 QCPAxis *valueAxis = mValueAxis.data();
11740 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
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; }
15274 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11742
15275
11743 // get visible data range:
15276 QVector<QCPData> lineData;
11744 QCPDataMap::const_iterator lower, upper;
15277 getPreparedData(&lineData, scatterData);
11745 int dataCount = 0;
15278 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11746 getVisibleDataBounds(lower, upper, dataCount);
15279 linePixelData->resize(lineData.size()*2);
11747 if (dataCount > 0)
15280
11748 {
15281 // calculate steps from lineData and transform to pixel coordinates:
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)
15282 if (keyAxis->orientation() == Qt::Vertical)
11760 {
15283 {
11761 double lastValue = valueAxis->coordToPixel(it.value().value);
15284 double lastValue = valueAxis->coordToPixel(lineData.first().value);
11762 double key;
15285 double key;
11763 while (it != upperEnd)
15286 for (int i=0; i<lineData.size(); ++i)
11764 {
15287 {
11765 if (pointData)
15288 key = keyAxis->coordToPixel(lineData.at(i).key);
11766 {
15289 (*linePixelData)[i*2+0].setX(lastValue);
11767 (*pointData)[ipoint] = it.value();
15290 (*linePixelData)[i*2+0].setY(key);
11768 ++ipoint;
15291 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
11769 }
15292 (*linePixelData)[i*2+1].setX(lastValue);
11770 key = keyAxis->coordToPixel(it.key());
15293 (*linePixelData)[i*2+1].setY(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 }
15294 }
11780 } else // key axis is horizontal
15295 } else // key axis is horizontal
11781 {
15296 {
11782 double lastValue = valueAxis->coordToPixel(it.value().value);
15297 double lastValue = valueAxis->coordToPixel(lineData.first().value);
11783 double key;
15298 double key;
11784 while (it != upperEnd)
15299 for (int i=0; i<lineData.size(); ++i)
11785 {
15300 {
11786 if (pointData)
15301 key = keyAxis->coordToPixel(lineData.at(i).key);
11787 {
15302 (*linePixelData)[i*2+0].setX(key);
11788 (*pointData)[ipoint] = it.value();
15303 (*linePixelData)[i*2+0].setY(lastValue);
11789 ++ipoint;
15304 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
11790 }
15305 (*linePixelData)[i*2+1].setX(key);
11791 key = keyAxis->coordToPixel(it.key());
15306 (*linePixelData)[i*2+1].setY(lastValue);
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 }
15307 }
11802 }
15308 }
11803 }
15309 }
@@ -11806,78 +15312,51 void QCPGraph::getStepLeftPlotData(QVect
11806 \internal
15312 \internal
11807 Places the raw data points needed for a step plot with right oriented steps in \a lineData.
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 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15316 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11811 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
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 \see drawLinePlot
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 QCPAxis *keyAxis = mKeyAxis.data();
15324 QCPAxis *keyAxis = mKeyAxis.data();
11819 QCPAxis *valueAxis = mValueAxis.data();
15325 QCPAxis *valueAxis = mValueAxis.data();
11820 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
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; }
15327 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11822
15328
11823 // get visible data range:
15329 QVector<QCPData> lineData;
11824 QCPDataMap::const_iterator lower, upper;
15330 getPreparedData(&lineData, scatterData);
11825 int dataCount = 0;
15331 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11826 getVisibleDataBounds(lower, upper, dataCount);
15332 linePixelData->resize(lineData.size()*2);
11827 if (dataCount > 0)
15333
11828 {
15334 // calculate steps from lineData and transform to pixel coordinates:
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)
15335 if (keyAxis->orientation() == Qt::Vertical)
11840 {
15336 {
11841 double lastKey = keyAxis->coordToPixel(it.key());
15337 double lastKey = keyAxis->coordToPixel(lineData.first().key);
11842 double value;
15338 double value;
11843 while (it != upperEnd)
15339 for (int i=0; i<lineData.size(); ++i)
11844 {
15340 {
11845 if (pointData)
15341 value = valueAxis->coordToPixel(lineData.at(i).value);
11846 {
15342 (*linePixelData)[i*2+0].setX(value);
11847 (*pointData)[ipoint] = it.value();
15343 (*linePixelData)[i*2+0].setY(lastKey);
11848 ++ipoint;
15344 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
11849 }
15345 (*linePixelData)[i*2+1].setX(value);
11850 value = valueAxis->coordToPixel(it.value().value);
15346 (*linePixelData)[i*2+1].setY(lastKey);
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 }
15347 }
11860 } else // key axis is horizontal
15348 } else // key axis is horizontal
11861 {
15349 {
11862 double lastKey = keyAxis->coordToPixel(it.key());
15350 double lastKey = keyAxis->coordToPixel(lineData.first().key);
11863 double value;
15351 double value;
11864 while (it != upperEnd)
15352 for (int i=0; i<lineData.size(); ++i)
11865 {
15353 {
11866 if (pointData)
15354 value = valueAxis->coordToPixel(lineData.at(i).value);
11867 {
15355 (*linePixelData)[i*2+0].setX(lastKey);
11868 (*pointData)[ipoint] = it.value();
15356 (*linePixelData)[i*2+0].setY(value);
11869 ++ipoint;
15357 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
11870 }
15358 (*linePixelData)[i*2+1].setX(lastKey);
11871 value = valueAxis->coordToPixel(it.value().value);
15359 (*linePixelData)[i*2+1].setY(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 }
15360 }
11882 }
15361 }
11883 }
15362 }
@@ -11886,183 +15365,113 void QCPGraph::getStepRightPlotData(QVec
11886 \internal
15365 \internal
11887 Places the raw data points needed for a step plot with centered steps in \a lineData.
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 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15369 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11891 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
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 \see drawLinePlot
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 QCPAxis *keyAxis = mKeyAxis.data();
15377 QCPAxis *keyAxis = mKeyAxis.data();
11899 QCPAxis *valueAxis = mValueAxis.data();
15378 QCPAxis *valueAxis = mValueAxis.data();
11900 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
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; }
15380 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
11902
15381
11903 // get visible data range:
15382 QVector<QCPData> lineData;
11904 QCPDataMap::const_iterator lower, upper;
15383 getPreparedData(&lineData, scatterData);
11905 int dataCount = 0;
15384 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
11906 getVisibleDataBounds(lower, upper, dataCount);
15385 linePixelData->resize(lineData.size()*2);
11907 if (dataCount > 0)
15386 // calculate steps from lineData and transform to pixel coordinates:
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)
15387 if (keyAxis->orientation() == Qt::Vertical)
11922 {
15388 {
11923 double lastKey = keyAxis->coordToPixel(it.key());
15389 double lastKey = keyAxis->coordToPixel(lineData.first().key);
11924 double lastValue = valueAxis->coordToPixel(it.value().value);
15390 double lastValue = valueAxis->coordToPixel(lineData.first().value);
11925 double key;
15391 double key;
11926 if (pointData)
15392 (*linePixelData)[0].setX(lastValue);
11927 {
15393 (*linePixelData)[0].setY(lastKey);
11928 (*pointData)[ipoint] = it.value();
15394 for (int i=1; i<lineData.size(); ++i)
11929 ++ipoint;
15395 {
11930 }
15396 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
11931 (*lineData)[i].setX(lastValue);
15397 (*linePixelData)[i*2-1].setX(lastValue);
11932 (*lineData)[i].setY(lastKey);
15398 (*linePixelData)[i*2-1].setY(key);
11933 ++it;
15399 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
11934 ++i;
15400 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
11935 while (it != upperEnd)
15401 (*linePixelData)[i*2+0].setX(lastValue);
11936 {
15402 (*linePixelData)[i*2+0].setY(key);
11937 if (pointData)
15403 }
11938 {
15404 (*linePixelData)[lineData.size()*2-1].setX(lastValue);
11939 (*pointData)[ipoint] = it.value();
15405 (*linePixelData)[lineData.size()*2-1].setY(lastKey);
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
15406 } else // key axis is horizontal
11956 {
15407 {
11957 double lastKey = keyAxis->coordToPixel(it.key());
15408 double lastKey = keyAxis->coordToPixel(lineData.first().key);
11958 double lastValue = valueAxis->coordToPixel(it.value().value);
15409 double lastValue = valueAxis->coordToPixel(lineData.first().value);
11959 double key;
15410 double key;
11960 if (pointData)
15411 (*linePixelData)[0].setX(lastKey);
11961 {
15412 (*linePixelData)[0].setY(lastValue);
11962 (*pointData)[ipoint] = it.value();
15413 for (int i=1; i<lineData.size(); ++i)
11963 ++ipoint;
15414 {
11964 }
15415 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
11965 (*lineData)[i].setX(lastKey);
15416 (*linePixelData)[i*2-1].setX(key);
11966 (*lineData)[i].setY(lastValue);
15417 (*linePixelData)[i*2-1].setY(lastValue);
11967 ++it;
15418 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
11968 ++i;
15419 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
11969 while (it != upperEnd)
15420 (*linePixelData)[i*2+0].setX(key);
11970 {
15421 (*linePixelData)[i*2+0].setY(lastValue);
11971 if (pointData)
15422 }
11972 {
15423 (*linePixelData)[lineData.size()*2-1].setX(lastKey);
11973 (*pointData)[ipoint] = it.value();
15424 (*linePixelData)[lineData.size()*2-1].setY(lastValue);
11974 ++ipoint;
15425 }
11975 }
15426
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 }
15427 }
11992
15428
11993 /*!
15429 /*!
11994 \internal
15430 \internal
11995 Places the raw data points needed for an impulse plot in \a lineData.
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 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15434 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
11999 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
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 \see drawImpulsePlot
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 QCPAxis *keyAxis = mKeyAxis.data();
15442 QCPAxis *keyAxis = mKeyAxis.data();
12007 QCPAxis *valueAxis = mValueAxis.data();
15443 QCPAxis *valueAxis = mValueAxis.data();
12008 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
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; }
15445 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
12010
15446
12011 // get visible data range:
15447 QVector<QCPData> lineData;
12012 QCPDataMap::const_iterator lower, upper;
15448 getPreparedData(&lineData, scatterData);
12013 int dataCount = 0;
15449 linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
12014 getVisibleDataBounds(lower, upper, dataCount);
15450
12015 if (dataCount > 0)
15451 // transform lineData points to pixels:
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)
15452 if (keyAxis->orientation() == Qt::Vertical)
12027 {
15453 {
12028 double zeroPointX = valueAxis->coordToPixel(0);
15454 double zeroPointX = valueAxis->coordToPixel(0);
12029 double key;
15455 double key;
12030 while (it != upperEnd)
15456 for (int i=0; i<lineData.size(); ++i)
12031 {
15457 {
12032 if (pointData)
15458 key = keyAxis->coordToPixel(lineData.at(i).key);
12033 {
15459 (*linePixelData)[i*2+0].setX(zeroPointX);
12034 (*pointData)[ipoint] = it.value();
15460 (*linePixelData)[i*2+0].setY(key);
12035 ++ipoint;
15461 (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
12036 }
15462 (*linePixelData)[i*2+1].setY(key);
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 }
15463 }
12046 } else // key axis is horizontal
15464 } else // key axis is horizontal
12047 {
15465 {
12048 double zeroPointY = valueAxis->coordToPixel(0);
15466 double zeroPointY = valueAxis->coordToPixel(0);
12049 double key;
15467 double key;
12050 while (it != upperEnd)
15468 for (int i=0; i<lineData.size(); ++i)
12051 {
15469 {
12052 if (pointData)
15470 key = keyAxis->coordToPixel(lineData.at(i).key);
12053 {
15471 (*linePixelData)[i*2+0].setX(key);
12054 (*pointData)[ipoint] = it.value();
15472 (*linePixelData)[i*2+0].setY(zeroPointY);
12055 ++ipoint;
15473 (*linePixelData)[i*2+1].setX(key);
12056 }
15474 (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
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 }
12066 }
15475 }
12067 }
15476 }
12068 }
15477 }
@@ -12105,14 +15514,14 void QCPGraph::drawFill(QCPPainter *pain
12105
15514
12106 /*! \internal
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 of the line style and are always drawn if the scatter style's shape is not \ref
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 functions, together with the (line style dependent) line data.
15520 functions, together with the (line style dependent) line data.
12112
15521
12113 \see drawLinePlot, drawImpulsePlot
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 QCPAxis *keyAxis = mKeyAxis.data();
15526 QCPAxis *keyAxis = mKeyAxis.data();
12118 QCPAxis *valueAxis = mValueAxis.data();
15527 QCPAxis *valueAxis = mValueAxis.data();
@@ -12125,12 +15534,12 void QCPGraph::drawScatterPlot(QCPPainte
12125 painter->setPen(mErrorPen);
15534 painter->setPen(mErrorPen);
12126 if (keyAxis->orientation() == Qt::Vertical)
15535 if (keyAxis->orientation() == Qt::Vertical)
12127 {
15536 {
12128 for (int i=0; i<pointData->size(); ++i)
15537 for (int i=0; i<scatterData->size(); ++i)
12129 drawError(painter, valueAxis->coordToPixel(pointData->at(i).value), keyAxis->coordToPixel(pointData->at(i).key), pointData->at(i));
15538 drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
12130 } else
15539 } else
12131 {
15540 {
12132 for (int i=0; i<pointData->size(); ++i)
15541 for (int i=0; i<scatterData->size(); ++i)
12133 drawError(painter, keyAxis->coordToPixel(pointData->at(i).key), valueAxis->coordToPixel(pointData->at(i).value), pointData->at(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 mScatterStyle.applyTo(painter, mPen);
15548 mScatterStyle.applyTo(painter, mPen);
12140 if (keyAxis->orientation() == Qt::Vertical)
15549 if (keyAxis->orientation() == Qt::Vertical)
12141 {
15550 {
12142 for (int i=0; i<pointData->size(); ++i)
15551 for (int i=0; i<scatterData->size(); ++i)
12143 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(pointData->at(i).value), keyAxis->coordToPixel(pointData->at(i).key));
15552 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
12144 } else
15553 } else
12145 {
15554 {
12146 for (int i=0; i<pointData->size(); ++i)
15555 for (int i=0; i<scatterData->size(); ++i)
12147 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(pointData->at(i).key), valueAxis->coordToPixel(pointData->at(i).value));
15556 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
12148 }
15557 }
12149 }
15558 }
12150
15559
@@ -12219,6 +15628,199 void QCPGraph::drawImpulsePlot(QCPPainte
12219
15628
12220 /*! \internal
15629 /*! \internal
12221
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
15822 /*! \internal
15823
12222 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
15824 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
12223 point. \a x and \a y pixel positions of the data point are passed since they are already known in
15825 point. \a x and \a y pixel positions of the data point are passed since they are already known in
12224 pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
15826 pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
@@ -12321,30 +15923,25 void QCPGraph::drawError(QCPPainter *pai
12321
15923
12322 /*! \internal
15924 /*! \internal
12323
15925
12324 called by the specific plot data generating functions "get(...)PlotData" to determine which data
15926 called by \ref getPreparedData to determine which data (key) range is visible at the current key
12325 range is visible, so only that needs to be processed.
15927 axis range setting, so only that needs to be processed.
12326
15928
12327 \a lower returns an iterator to the lowest data point that needs to be taken into account when
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
15930 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
12329 may still be outside the visible range.
15931 lower may still be just outside the visible range.
12330
15932
12331 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
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.
15934 just outside of the visible range.
12333
15935
12334 \a count number of data points that need plotting, i.e. points between \a lower and \a upper,
15936 if the graph contains no data, both \a lower and \a upper point to constEnd.
12335 including them. This is useful for allocating the array of <tt>QPointF</tt>s in the specific
15937 */
12336 drawing functions.
15938 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
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
12341 {
15939 {
12342 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15940 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
12343 if (mData->isEmpty())
15941 if (mData->isEmpty())
12344 {
15942 {
12345 lower = mData->constEnd();
15943 lower = mData->constEnd();
12346 upper = mData->constEnd();
15944 upper = mData->constEnd();
12347 count = 0;
12348 return;
15945 return;
12349 }
15946 }
12350
15947
@@ -12356,15 +15953,30 void QCPGraph::getVisibleDataBounds(QCPD
12356
15953
12357 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
15954 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
12358 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
15955 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
12359
15956 }
12360 // count number of points in range lower to upper (including them), so we can allocate array for them in draw functions:
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 QCPDataMap::const_iterator it = lower;
15972 QCPDataMap::const_iterator it = lower;
12362 count = 1;
15973 int count = 1;
12363 while (it != upper)
15974 while (it != upper && count < maxCount)
12364 {
15975 {
12365 ++it;
15976 ++it;
12366 ++count;
15977 ++count;
12367 }
15978 }
15979 return count;
12368 }
15980 }
12369
15981
12370 /*! \internal
15982 /*! \internal
@@ -12779,20 +16391,20 double QCPGraph::pointDistance(const QPo
12779 if (mLineStyle == lsNone)
16391 if (mLineStyle == lsNone)
12780 {
16392 {
12781 // no line displayed, only calculate distance to scatter points:
16393 // no line displayed, only calculate distance to scatter points:
12782 QVector<QCPData> *pointData = new QVector<QCPData>;
16394 QVector<QCPData> *scatterData = new QVector<QCPData>;
12783 getScatterPlotData(pointData);
16395 getScatterPlotData(scatterData);
12784 double minDistSqr = std::numeric_limits<double>::max();
16396 double minDistSqr = std::numeric_limits<double>::max();
12785 QPointF ptA;
16397 QPointF ptA;
12786 QPointF ptB = coordsToPixels(pointData->at(0).key, pointData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
16398 QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
12787 for (int i=1; i<pointData->size(); ++i)
16399 for (int i=1; i<scatterData->size(); ++i)
12788 {
16400 {
12789 ptA = ptB;
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 double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
16403 double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
12792 if (currentDistSqr < minDistSqr)
16404 if (currentDistSqr < minDistSqr)
12793 minDistSqr = currentDistSqr;
16405 minDistSqr = currentDistSqr;
12794 }
16406 }
12795 delete pointData;
16407 delete scatterData;
12796 return sqrt(minDistSqr);
16408 return sqrt(minDistSqr);
12797 } else
16409 } else
12798 {
16410 {
@@ -12848,28 +16460,28 int QCPGraph::findIndexBelowY(const QVec
12848 }
16460 }
12849
16461
12850 /* inherits documentation from base class */
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 // just call the specialized version which takes an additional argument whether error bars
16465 // just call the specialized version which takes an additional argument whether error bars
12854 // should also be taken into consideration for range calculation. We set this to true here.
16466 // should also be taken into consideration for range calculation. We set this to true here.
12855 return getKeyRange(validRange, inSignDomain, true);
16467 return getKeyRange(foundRange, inSignDomain, true);
12856 }
16468 }
12857
16469
12858 /* inherits documentation from base class */
16470 /* inherits documentation from base class */
12859 QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain) const
16471 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
12860 {
16472 {
12861 // just call the specialized version which takes an additional argument whether error bars
16473 // just call the specialized version which takes an additional argument whether error bars
12862 // should also be taken into consideration for range calculation. We set this to true here.
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 /*! \overload
16478 /*! \overload
12867
16479
12868 Allows to specify whether the error bars should be included in the range calculation.
16480 Allows to specify whether the error bars should be included in the range calculation.
12869
16481
12870 \see getKeyRange(bool &validRange, SignDomain inSignDomain)
16482 \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
12871 */
16483 */
12872 QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
16484 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
12873 {
16485 {
12874 QCPRange range;
16486 QCPRange range;
12875 bool haveLower = false;
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 return range;
16581 return range;
12970 }
16582 }
12971
16583
@@ -12973,9 +16585,9 QCPRange QCPGraph::getKeyRange(bool &val
12973
16585
12974 Allows to specify whether the error bars should be included in the range calculation.
16586 Allows to specify whether the error bars should be included in the range calculation.
12975
16587
12976 \see getValueRange(bool &validRange, SignDomain inSignDomain)
16588 \see getValueRange(bool &foundRange, SignDomain inSignDomain)
12977 */
16589 */
12978 QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
16590 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
12979 {
16591 {
12980 QCPRange range;
16592 QCPRange range;
12981 bool haveLower = false;
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 return range;
16687 return range;
13076 }
16688 }
13077
16689
@@ -13404,8 +17016,12 double QCPCurve::selectTest(const QPoint
13404 Q_UNUSED(details)
17016 Q_UNUSED(details)
13405 if ((onlySelectable && !mSelectable) || mData->isEmpty())
17017 if ((onlySelectable && !mSelectable) || mData->isEmpty())
13406 return -1;
17018 return -1;
13407
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()))
13408 return pointDistance(pos);
17022 return pointDistance(pos);
17023 else
17024 return -1;
13409 }
17025 }
13410
17026
13411 /* inherits documentation from base class */
17027 /* inherits documentation from base class */
@@ -13703,7 +17319,7 QPointF QCPCurve::outsideCoordsToPixels(
13703 }
17319 }
13704
17320
13705 /* inherits documentation from base class */
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 QCPRange range;
17324 QCPRange range;
13709 bool haveLower = false;
17325 bool haveLower = false;
@@ -13731,12 +17347,12 QCPRange QCPCurve::getKeyRange(bool &val
13731 ++it;
17347 ++it;
13732 }
17348 }
13733
17349
13734 validRange = haveLower && haveUpper;
17350 foundRange = haveLower && haveUpper;
13735 return range;
17351 return range;
13736 }
17352 }
13737
17353
13738 /* inherits documentation from base class */
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 QCPRange range;
17357 QCPRange range;
13742 bool haveLower = false;
17358 bool haveLower = false;
@@ -13764,7 +17380,7 QCPRange QCPCurve::getValueRange(bool &v
13764 ++it;
17380 ++it;
13765 }
17381 }
13766
17382
13767 validRange = haveLower && haveUpper;
17383 foundRange = haveLower && haveUpper;
13768 return range;
17384 return range;
13769 }
17385 }
13770
17386
@@ -14114,7 +17730,10 double QCPBars::selectTest(const QPointF
14114 Q_UNUSED(details)
17730 Q_UNUSED(details)
14115 if (onlySelectable && !mSelectable)
17731 if (onlySelectable && !mSelectable)
14116 return -1;
17732 return -1;
14117
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 {
14118 QCPBarDataMap::ConstIterator it;
17737 QCPBarDataMap::ConstIterator it;
14119 double posKey, posValue;
17738 double posKey, posValue;
14120 pixelsToCoords(pos, posKey, posValue);
17739 pixelsToCoords(pos, posKey, posValue);
@@ -14126,6 +17745,7 double QCPBars::selectTest(const QPointF
14126 if (keyRange.contains(posKey) && valueRange.contains(posValue))
17745 if (keyRange.contains(posKey) && valueRange.contains(posValue))
14127 return mParentPlot->selectionTolerance()*0.99;
17746 return mParentPlot->selectionTolerance()*0.99;
14128 }
17747 }
17748 }
14129 return -1;
17749 return -1;
14130 }
17750 }
14131
17751
@@ -14262,7 +17882,7 void QCPBars::connectBars(QCPBars *lower
14262 }
17882 }
14263
17883
14264 /* inherits documentation from base class */
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 QCPRange range;
17887 QCPRange range;
14268 bool haveLower = false;
17888 bool haveLower = false;
@@ -14290,12 +17910,12 QCPRange QCPBars::getKeyRange(bool &vali
14290 ++it;
17910 ++it;
14291 }
17911 }
14292
17912
14293 validRange = haveLower && haveUpper;
17913 foundRange = haveLower && haveUpper;
14294 return range;
17914 return range;
14295 }
17915 }
14296
17916
14297 /* inherits documentation from base class */
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 QCPRange range;
17920 QCPRange range;
14301 bool haveLower = true; // set to true, because 0 should always be visible in bar charts
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 ++it;
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 return range;
17947 return range;
14328 }
17948 }
14329
17949
@@ -14338,14 +17958,14 QCPRange QCPBars::getValueRange(bool &va
14338 \image html QCPStatisticalBox.png
17958 \image html QCPStatisticalBox.png
14339
17959
14340 To plot data, assign it with the individual parameter functions or use \ref setData to set all
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 \li \ref setMinimum
17962 \li \ref setMinimum
14343 \li \ref setLowerQuartile
17963 \li \ref setLowerQuartile
14344 \li \ref setMedian
17964 \li \ref setMedian
14345 \li \ref setUpperQuartile
17965 \li \ref setUpperQuartile
14346 \li \ref setMaximum
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 \li \ref setOutliers
17969 \li \ref setOutliers
14350
17970
14351 \section appearance Changing the appearance
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
18106 Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
14487 are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers and
18107 that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
14488 displayed as such.
18108 and displayed as such.
14489
18109
14490 \see setOutlierStyle
18110 \see setOutlierStyle
14491 */
18111 */
@@ -14591,6 +18211,8 double QCPStatisticalBox::selectTest(con
14591 return -1;
18211 return -1;
14592 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18212 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
14593
18213
18214 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18215 {
14594 double posKey, posValue;
18216 double posKey, posValue;
14595 pixelsToCoords(pos, posKey, posValue);
18217 pixelsToCoords(pos, posKey, posValue);
14596 // quartile box:
18218 // quartile box:
@@ -14602,7 +18224,7 double QCPStatisticalBox::selectTest(con
14602 // min/max whiskers:
18224 // min/max whiskers:
14603 if (QCPRange(mMinimum, mMaximum).contains(posValue))
18225 if (QCPRange(mMinimum, mMaximum).contains(posValue))
14604 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
18226 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
14605
18227 }
14606 return -1;
18228 return -1;
14607 }
18229 }
14608
18230
@@ -14712,9 +18334,9 void QCPStatisticalBox::drawOutliers(QCP
14712 }
18334 }
14713
18335
14714 /* inherits documentation from base class */
18336 /* inherits documentation from base class */
14715 QCPRange QCPStatisticalBox::getKeyRange(bool &validRange, SignDomain inSignDomain) const
18337 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
14716 {
18338 {
14717 validRange = mWidth > 0;
18339 foundRange = true;
14718 if (inSignDomain == sdBoth)
18340 if (inSignDomain == sdBoth)
14719 {
18341 {
14720 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
18342 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
@@ -14726,7 +18348,7 QCPRange QCPStatisticalBox::getKeyRange(
14726 return QCPRange(mKey-mWidth*0.5, mKey);
18348 return QCPRange(mKey-mWidth*0.5, mKey);
14727 else
18349 else
14728 {
18350 {
14729 validRange = false;
18351 foundRange = false;
14730 return QCPRange();
18352 return QCPRange();
14731 }
18353 }
14732 } else if (inSignDomain == sdPositive)
18354 } else if (inSignDomain == sdPositive)
@@ -14737,31 +18359,16 QCPRange QCPStatisticalBox::getKeyRange(
14737 return QCPRange(mKey, mKey+mWidth*0.5);
18359 return QCPRange(mKey, mKey+mWidth*0.5);
14738 else
18360 else
14739 {
18361 {
14740 validRange = false;
18362 foundRange = false;
14741 return QCPRange();
14742 }
14743 }
14744 validRange = false;
14745 return QCPRange();
18363 return QCPRange();
14746 }
18364 }
14747
18365 }
14748 /* inherits documentation from base class */
18366 foundRange = false;
14749 QCPRange QCPStatisticalBox::getValueRange(bool &validRange, SignDomain inSignDomain) const
18367 return QCPRange();
14750 {
18368 }
14751 if (inSignDomain == sdBoth)
18369
14752 {
18370 /* inherits documentation from base class */
14753 double lower = qMin(mMinimum, qMin(mMedian, mLowerQuartile));
18371 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
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;
14763 return QCPRange(lower, upper);
14764 } else
14765 {
18372 {
14766 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
18373 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
14767 values.reserve(mOutliers.size() + 5);
18374 values.reserve(mOutliers.size() + 5);
@@ -14775,7 +18382,8 QCPRange QCPStatisticalBox::getValueRang
14775 for (int i=0; i<values.size(); ++i)
18382 for (int i=0; i<values.size(); ++i)
14776 {
18383 {
14777 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
18384 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
14778 (inSignDomain == sdPositive && values.at(i) > 0))
18385 (inSignDomain == sdPositive && values.at(i) > 0) ||
18386 (inSignDomain == sdBoth))
14779 {
18387 {
14780 if (values.at(i) > upper || !haveUpper)
18388 if (values.at(i) > upper || !haveUpper)
14781 {
18389 {
@@ -14790,16 +18398,899 QCPRange QCPStatisticalBox::getValueRang
14790 }
18398 }
14791 }
18399 }
14792 // return the bounds if we found some sensible values:
18400 // return the bounds if we found some sensible values:
14793 if (haveLower && haveUpper && lower < upper)
18401 if (haveLower && haveUpper)
14794 {
18402 {
14795 validRange = true;
18403 foundRange = true;
14796 return QCPRange(lower, upper);
18404 return QCPRange(lower, upper);
14797 } else
18405 } else // might happen if all values are in other sign domain
14798 {
18406 {
14799 validRange = false;
18407 foundRange = false;
14800 return QCPRange();
18408 return QCPRange();
14801 }
18409 }
14802 }
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;
18567 } else
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 QPointF startDirVec(startDir->pixelPoint());
19833 QPointF startDirVec(startDir->pixelPoint());
15343 QPointF endDirVec(endDir->pixelPoint());
19834 QPointF endDirVec(endDir->pixelPoint());
15344 QPointF endVec(end->pixelPoint());
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 return;
19837 return;
15347
19838
15348 QPainterPath cubicPath(startVec);
19839 QPainterPath cubicPath(startVec);
@@ -16768,10 +21259,10 double QCPItemBracket::selectTest(const
16768 if (leftVec.toPoint() == rightVec.toPoint())
21259 if (leftVec.toPoint() == rightVec.toPoint())
16769 return -1;
21260 return -1;
16770
21261
16771 QVector2D widthVec = (rightVec-leftVec)*0.5;
21262 QVector2D widthVec = (rightVec-leftVec)*0.5f;
16772 QVector2D lengthVec(-widthVec.y(), widthVec.x());
21263 QVector2D lengthVec(-widthVec.y(), widthVec.x());
16773 lengthVec = lengthVec.normalized()*mLength;
21264 lengthVec = lengthVec.normalized()*mLength;
16774 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
21265 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
16775
21266
16776 return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
21267 return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
16777 }
21268 }
@@ -16784,10 +21275,10 void QCPItemBracket::draw(QCPPainter *pa
16784 if (leftVec.toPoint() == rightVec.toPoint())
21275 if (leftVec.toPoint() == rightVec.toPoint())
16785 return;
21276 return;
16786
21277
16787 QVector2D widthVec = (rightVec-leftVec)*0.5;
21278 QVector2D widthVec = (rightVec-leftVec)*0.5f;
16788 QVector2D lengthVec(-widthVec.y(), widthVec.x());
21279 QVector2D lengthVec(-widthVec.y(), widthVec.x());
16789 lengthVec = lengthVec.normalized()*mLength;
21280 lengthVec = lengthVec.normalized()*mLength;
16790 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
21281 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
16791
21282
16792 QPolygon boundingPoly;
21283 QPolygon boundingPoly;
16793 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
21284 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
@@ -16820,8 +21311,8 void QCPItemBracket::draw(QCPPainter *pa
16820 painter->setBrush(Qt::NoBrush);
21311 painter->setBrush(Qt::NoBrush);
16821 QPainterPath path;
21312 QPainterPath path;
16822 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
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());
21314 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+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());
21315 path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
16825 painter->drawPath(path);
21316 painter->drawPath(path);
16826 break;
21317 break;
16827 }
21318 }
@@ -16832,11 +21323,11 void QCPItemBracket::draw(QCPPainter *pa
16832 QPainterPath path;
21323 QPainterPath path;
16833 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
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());
21326 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*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());
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());
21329 path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
16839 path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec*1-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).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 painter->drawPath(path);
21332 painter->drawPath(path);
16842 break;
21333 break;
@@ -16853,10 +21344,10 QPointF QCPItemBracket::anchorPixelPoint
16853 if (leftVec.toPoint() == rightVec.toPoint())
21344 if (leftVec.toPoint() == rightVec.toPoint())
16854 return leftVec.toPointF();
21345 return leftVec.toPointF();
16855
21346
16856 QVector2D widthVec = (rightVec-leftVec)*0.5;
21347 QVector2D widthVec = (rightVec-leftVec)*0.5f;
16857 QVector2D lengthVec(-widthVec.y(), widthVec.x());
21348 QVector2D lengthVec(-widthVec.y(), widthVec.x());
16858 lengthVec = lengthVec.normalized()*mLength;
21349 lengthVec = lengthVec.normalized()*mLength;
16859 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
21350 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
16860
21351
16861 switch (anchorId)
21352 switch (anchorId)
16862 {
21353 {
@@ -16877,2061 +21368,3 QPen QCPItemBracket::mainPen() const
16877 return mSelected ? mSelectedPen : mPen;
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, (1449 lines changed) Show them Hide them
@@ -1,7 +1,7
1 /***************************************************************************
1 /***************************************************************************
2 ** **
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
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 ** This program is free software: you can redistribute it and/or modify **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
7 ** it under the terms of the GNU General Public License as published by **
@@ -19,8 +19,8
19 ****************************************************************************
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 09.12.13 **
22 ** Date: 14.03.14 **
23 ** Version: 1.1.1 **
23 ** Version: 1.2.0 **
24 ****************************************************************************/
24 ****************************************************************************/
25
25
26 #ifndef QCUSTOMPLOT_H
26 #ifndef QCUSTOMPLOT_H
@@ -48,6 +48,7
48 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
48 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
49 # include <qnumeric.h>
49 # include <qnumeric.h>
50 # include <QPrinter>
50 # include <QPrinter>
51 # include <QPrintEngine>
51 #else
52 #else
52 # include <QtNumeric>
53 # include <QtNumeric>
53 # include <QtPrintSupport>
54 # include <QtPrintSupport>
@@ -60,6 +61,7 class QCPLayoutElement;
60 class QCPLayout;
61 class QCPLayout;
61 class QCPAxis;
62 class QCPAxis;
62 class QCPAxisRect;
63 class QCPAxisRect;
64 class QCPAxisPainterPrivate;
63 class QCPAbstractPlottable;
65 class QCPAbstractPlottable;
64 class QCPGraph;
66 class QCPGraph;
65 class QCPAbstractItem;
67 class QCPAbstractItem;
@@ -68,6 +70,8 class QCPLayer;
68 class QCPPlotTitle;
70 class QCPPlotTitle;
69 class QCPLegend;
71 class QCPLegend;
70 class QCPAbstractLegendItem;
72 class QCPAbstractLegendItem;
73 class QCPColorMap;
74 class QCPColorScale;
71
75
72
76
73 /*! \file */
77 /*! \file */
@@ -134,8 +138,8 Q_DECLARE_FLAGS(AntialiasedElements, Ant
134 enum PlottingHint { phNone = 0x000 ///< <tt>0x000</tt> No hints are set
138 enum PlottingHint { phNone = 0x000 ///< <tt>0x000</tt> No hints are set
135 ,phFastPolylines = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
139 ,phFastPolylines = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
136 ///< especially of the line segment joins. (Only relevant for solid line pens.)
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
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.
138 ///< on Windows-Systems to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
142 ///< This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
139 ,phCacheLabels = 0x004 ///< <tt>0x004</tt> axis (tick) labels will be cached as pixmaps, increasing replot performance.
143 ,phCacheLabels = 0x004 ///< <tt>0x004</tt> axis (tick) labels will be cached as pixmaps, increasing replot performance.
140 };
144 };
141 Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
145 Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
@@ -365,6 +369,7 class QCP_LIB_DECL QCPLayer : public QOb
365 Q_PROPERTY(QString name READ name)
369 Q_PROPERTY(QString name READ name)
366 Q_PROPERTY(int index READ index)
370 Q_PROPERTY(int index READ index)
367 Q_PROPERTY(QList<QCPLayerable*> children READ children)
371 Q_PROPERTY(QList<QCPLayerable*> children READ children)
372 Q_PROPERTY(bool visible READ visible WRITE setVisible)
368 /// \endcond
373 /// \endcond
369 public:
374 public:
370 QCPLayer(QCustomPlot* parentPlot, const QString &layerName);
375 QCPLayer(QCustomPlot* parentPlot, const QString &layerName);
@@ -375,6 +380,10 public:
375 QString name() const { return mName; }
380 QString name() const { return mName; }
376 int index() const { return mIndex; }
381 int index() const { return mIndex; }
377 QList<QCPLayerable*> children() const { return mChildren; }
382 QList<QCPLayerable*> children() const { return mChildren; }
383 bool visible() const { return mVisible; }
384
385 // setters:
386 void setVisible(bool visible);
378
387
379 protected:
388 protected:
380 // property members:
389 // property members:
@@ -382,6 +391,7 protected:
382 QString mName;
391 QString mName;
383 int mIndex;
392 int mIndex;
384 QList<QCPLayerable*> mChildren;
393 QList<QCPLayerable*> mChildren;
394 bool mVisible;
385
395
386 // non-virtual methods:
396 // non-virtual methods:
387 void addChild(QCPLayerable *layerable, bool prepend);
397 void addChild(QCPLayerable *layerable, bool prepend);
@@ -401,7 +411,7 class QCP_LIB_DECL QCPLayerable : public
401 Q_PROPERTY(bool visible READ visible WRITE setVisible)
411 Q_PROPERTY(bool visible READ visible WRITE setVisible)
402 Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)
412 Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)
403 Q_PROPERTY(QCPLayerable* parentLayerable READ parentLayerable)
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 Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
415 Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
406 /// \endcond
416 /// \endcond
407 public:
417 public:
@@ -417,7 +427,7 public:
417
427
418 // setters:
428 // setters:
419 void setVisible(bool on);
429 void setVisible(bool on);
420 bool setLayer(QCPLayer *layer);
430 Q_SLOT bool setLayer(QCPLayer *layer);
421 bool setLayer(const QString &layerName);
431 bool setLayer(const QString &layerName);
422 void setAntialiased(bool enabled);
432 void setAntialiased(bool enabled);
423
433
@@ -427,6 +437,9 public:
427 // non-property methods:
437 // non-property methods:
428 bool realVisibility() const;
438 bool realVisibility() const;
429
439
440 signals:
441 void layerChanged(QCPLayer *newLayer);
442
430 protected:
443 protected:
431 // property members:
444 // property members:
432 bool mVisible;
445 bool mVisible;
@@ -467,6 +480,20 public:
467 QCPRange();
480 QCPRange();
468 QCPRange(double lower, double upper);
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 double size() const;
497 double size() const;
471 double center() const;
498 double center() const;
472 void normalize();
499 void normalize();
@@ -480,9 +507,94 public:
480 static bool validRange(const QCPRange &range);
507 static bool validRange(const QCPRange &range);
481 static const double minRange; //1e-280;
508 static const double minRange; //1e-280;
482 static const double maxRange; //1e280;
509 static const double maxRange; //1e280;
510
483 };
511 };
484 Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE);
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 class QCP_LIB_DECL QCPMarginGroup : public QObject
599 class QCP_LIB_DECL QCPMarginGroup : public QObject
488 {
600 {
@@ -526,6 +638,16 class QCP_LIB_DECL QCPLayoutElement : pu
526 Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
638 Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
527 /// \endcond
639 /// \endcond
528 public:
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 explicit QCPLayoutElement(QCustomPlot *parentPlot=0);
651 explicit QCPLayoutElement(QCustomPlot *parentPlot=0);
530 virtual ~QCPLayoutElement();
652 virtual ~QCPLayoutElement();
531
653
@@ -553,7 +675,7 public:
553 void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group);
675 void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group);
554
676
555 // introduced virtual methods:
677 // introduced virtual methods:
556 virtual void update();
678 virtual void update(UpdatePhase phase);
557 virtual QSize minimumSizeHint() const;
679 virtual QSize minimumSizeHint() const;
558 virtual QSize maximumSizeHint() const;
680 virtual QSize maximumSizeHint() const;
559 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
681 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
@@ -600,7 +722,7 public:
600 explicit QCPLayout();
722 explicit QCPLayout();
601
723
602 // reimplemented virtual methods:
724 // reimplemented virtual methods:
603 virtual void update();
725 virtual void update(UpdatePhase phase);
604 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
726 virtual QList<QCPLayoutElement*> elements(bool recursive) const;
605
727
606 // introduced virtual methods:
728 // introduced virtual methods:
@@ -759,7 +881,7 public:
759 and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only
881 and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only
760 support a width, the length property is ignored.
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 Q_ENUMS(EndingStyle)
886 Q_ENUMS(EndingStyle)
765 enum EndingStyle { esNone ///< No ending decoration
887 enum EndingStyle { esNone ///< No ending decoration
@@ -770,7 +892,7 public:
770 ,esSquare ///< A filled square
892 ,esSquare ///< A filled square
771 ,esDiamond ///< A filled diamond (45° rotated square)
893 ,esDiamond ///< A filled diamond (45° rotated square)
772 ,esBar ///< A bar perpendicular to the line
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 ,esSkewedBar ///< A bar that is skewed (skew controllable via \ref setLength)
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 /// \cond INCLUDE_QPROPERTIES
982 /// \cond INCLUDE_QPROPERTIES
861 Q_PROPERTY(AxisType axisType READ axisType)
983 Q_PROPERTY(AxisType axisType READ axisType)
862 Q_PROPERTY(QCPAxisRect* axisRect READ axisRect)
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 Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase)
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 Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)
988 Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)
867 Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
989 Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
868 Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
990 Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
@@ -897,8 +1019,8 class QCP_LIB_DECL QCPAxis : public QCPL
897 Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
1019 Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
898 Q_PROPERTY(int padding READ padding WRITE setPadding)
1020 Q_PROPERTY(int padding READ padding WRITE setPadding)
899 Q_PROPERTY(int offset READ offset WRITE setOffset)
1021 Q_PROPERTY(int offset READ offset WRITE setOffset)
900 Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts)
1022 Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged)
901 Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts)
1023 Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged)
902 Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont)
1024 Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont)
903 Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)
1025 Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)
904 Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor)
1026 Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor)
@@ -953,6 +1075,7 public:
953 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
1075 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
954
1076
955 explicit QCPAxis(QCPAxisRect *parent, AxisType type);
1077 explicit QCPAxis(QCPAxisRect *parent, AxisType type);
1078 virtual ~QCPAxis();
956
1079
957 // getters:
1080 // getters:
958 AxisType axisType() const { return mAxisType; }
1081 AxisType axisType() const { return mAxisType; }
@@ -968,11 +1091,11 public:
968 bool autoSubTicks() const { return mAutoSubTicks; }
1091 bool autoSubTicks() const { return mAutoSubTicks; }
969 bool ticks() const { return mTicks; }
1092 bool ticks() const { return mTicks; }
970 bool tickLabels() const { return mTickLabels; }
1093 bool tickLabels() const { return mTickLabels; }
971 int tickLabelPadding() const { return mTickLabelPadding; }
1094 int tickLabelPadding() const;
972 LabelType tickLabelType() const { return mTickLabelType; }
1095 LabelType tickLabelType() const { return mTickLabelType; }
973 QFont tickLabelFont() const { return mTickLabelFont; }
1096 QFont tickLabelFont() const { return mTickLabelFont; }
974 QColor tickLabelColor() const { return mTickLabelColor; }
1097 QColor tickLabelColor() const { return mTickLabelColor; }
975 double tickLabelRotation() const { return mTickLabelRotation; }
1098 double tickLabelRotation() const;
976 QString dateTimeFormat() const { return mDateTimeFormat; }
1099 QString dateTimeFormat() const { return mDateTimeFormat; }
977 Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
1100 Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
978 QString numberFormat() const;
1101 QString numberFormat() const;
@@ -980,20 +1103,20 public:
980 double tickStep() const { return mTickStep; }
1103 double tickStep() const { return mTickStep; }
981 QVector<double> tickVector() const { return mTickVector; }
1104 QVector<double> tickVector() const { return mTickVector; }
982 QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
1105 QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
983 int tickLengthIn() const { return mTickLengthIn; }
1106 int tickLengthIn() const;
984 int tickLengthOut() const { return mTickLengthOut; }
1107 int tickLengthOut() const;
985 int subTickCount() const { return mSubTickCount; }
1108 int subTickCount() const { return mSubTickCount; }
986 int subTickLengthIn() const { return mSubTickLengthIn; }
1109 int subTickLengthIn() const;
987 int subTickLengthOut() const { return mSubTickLengthOut; }
1110 int subTickLengthOut() const;
988 QPen basePen() const { return mBasePen; }
1111 QPen basePen() const { return mBasePen; }
989 QPen tickPen() const { return mTickPen; }
1112 QPen tickPen() const { return mTickPen; }
990 QPen subTickPen() const { return mSubTickPen; }
1113 QPen subTickPen() const { return mSubTickPen; }
991 QFont labelFont() const { return mLabelFont; }
1114 QFont labelFont() const { return mLabelFont; }
992 QColor labelColor() const { return mLabelColor; }
1115 QColor labelColor() const { return mLabelColor; }
993 QString label() const { return mLabel; }
1116 QString label() const { return mLabel; }
994 int labelPadding() const { return mLabelPadding; }
1117 int labelPadding() const;
995 int padding() const { return mPadding; }
1118 int padding() const { return mPadding; }
996 int offset() const { return mOffset; }
1119 int offset() const;
997 SelectableParts selectedParts() const { return mSelectedParts; }
1120 SelectableParts selectedParts() const { return mSelectedParts; }
998 SelectableParts selectableParts() const { return mSelectableParts; }
1121 SelectableParts selectableParts() const { return mSelectableParts; }
999 QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
1122 QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
@@ -1003,12 +1126,12 public:
1003 QPen selectedBasePen() const { return mSelectedBasePen; }
1126 QPen selectedBasePen() const { return mSelectedBasePen; }
1004 QPen selectedTickPen() const { return mSelectedTickPen; }
1127 QPen selectedTickPen() const { return mSelectedTickPen; }
1005 QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
1128 QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
1006 QCPLineEnding lowerEnding() const { return mLowerEnding; }
1129 QCPLineEnding lowerEnding() const;
1007 QCPLineEnding upperEnding() const { return mUpperEnding; }
1130 QCPLineEnding upperEnding() const;
1008 QCPGrid *grid() const { return mGrid; }
1131 QCPGrid *grid() const { return mGrid; }
1009
1132
1010 // setters:
1133 // setters:
1011 void setScaleType(ScaleType type);
1134 Q_SLOT void setScaleType(QCPAxis::ScaleType type);
1012 void setScaleLogBase(double base);
1135 void setScaleLogBase(double base);
1013 Q_SLOT void setRange(const QCPRange &range);
1136 Q_SLOT void setRange(const QCPRange &range);
1014 void setRange(double lower, double upper);
1137 void setRange(double lower, double upper);
@@ -1066,7 +1189,7 public:
1066 // reimplemented virtual methods:
1189 // reimplemented virtual methods:
1067 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
1190 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
1068
1191
1069 // non-virtual methods:
1192 // non-property methods:
1070 Qt::Orientation orientation() const { return mOrientation; }
1193 Qt::Orientation orientation() const { return mOrientation; }
1071 void moveRange(double diff);
1194 void moveRange(double diff);
1072 void scaleRange(double factor, double center);
1195 void scaleRange(double factor, double center);
@@ -1080,44 +1203,37 public:
1080 QList<QCPAbstractItem*> items() const;
1203 QList<QCPAbstractItem*> items() const;
1081
1204
1082 static AxisType marginSideToAxisType(QCP::MarginSide side);
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 signals:
1209 signals:
1085 void ticksRequest();
1210 void ticksRequest();
1086 void rangeChanged(const QCPRange &newRange);
1211 void rangeChanged(const QCPRange &newRange);
1087 void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
1212 void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
1213 void scaleTypeChanged(QCPAxis::ScaleType scaleType);
1088 void selectionChanged(const QCPAxis::SelectableParts &parts);
1214 void selectionChanged(const QCPAxis::SelectableParts &parts);
1215 void selectableChanged(const QCPAxis::SelectableParts &parts);
1089
1216
1090 protected:
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 // property members:
1218 // property members:
1104 // axis base:
1219 // axis base:
1105 AxisType mAxisType;
1220 AxisType mAxisType;
1106 QCPAxisRect *mAxisRect;
1221 QCPAxisRect *mAxisRect;
1107 int mOffset, mPadding;
1222 //int mOffset; // in QCPAxisPainter
1223 int mPadding;
1108 Qt::Orientation mOrientation;
1224 Qt::Orientation mOrientation;
1109 SelectableParts mSelectableParts, mSelectedParts;
1225 SelectableParts mSelectableParts, mSelectedParts;
1110 QPen mBasePen, mSelectedBasePen;
1226 QPen mBasePen, mSelectedBasePen;
1111 QCPLineEnding mLowerEnding, mUpperEnding;
1227 //QCPLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter
1112 // axis label:
1228 // axis label:
1113 int mLabelPadding;
1229 //int mLabelPadding; // in QCPAxisPainter
1114 QString mLabel;
1230 QString mLabel;
1115 QFont mLabelFont, mSelectedLabelFont;
1231 QFont mLabelFont, mSelectedLabelFont;
1116 QColor mLabelColor, mSelectedLabelColor;
1232 QColor mLabelColor, mSelectedLabelColor;
1117 // tick labels:
1233 // tick labels:
1118 int mTickLabelPadding;
1234 //int mTickLabelPadding; // in QCPAxisPainter
1119 bool mTickLabels, mAutoTickLabels;
1235 bool mTickLabels, mAutoTickLabels;
1120 double mTickLabelRotation;
1236 //double mTickLabelRotation; // in QCPAxisPainter
1121 LabelType mTickLabelType;
1237 LabelType mTickLabelType;
1122 QFont mTickLabelFont, mSelectedTickLabelFont;
1238 QFont mTickLabelFont, mSelectedTickLabelFont;
1123 QColor mTickLabelColor, mSelectedTickLabelColor;
1239 QColor mTickLabelColor, mSelectedTickLabelColor;
@@ -1126,13 +1242,13 protected:
1126 int mNumberPrecision;
1242 int mNumberPrecision;
1127 char mNumberFormatChar;
1243 char mNumberFormatChar;
1128 bool mNumberBeautifulPowers;
1244 bool mNumberBeautifulPowers;
1129 bool mNumberMultiplyCross;
1245 //bool mNumberMultiplyCross; // QCPAxisPainter
1130 // ticks and subticks:
1246 // ticks and subticks:
1131 bool mTicks;
1247 bool mTicks;
1132 double mTickStep;
1248 double mTickStep;
1133 int mSubTickCount, mAutoTickCount;
1249 int mSubTickCount, mAutoTickCount;
1134 bool mAutoTicks, mAutoTickStep, mAutoSubTicks;
1250 bool mAutoTicks, mAutoTickStep, mAutoSubTicks;
1135 int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;
1251 //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // QCPAxisPainter
1136 QPen mTickPen, mSelectedTickPen;
1252 QPen mTickPen, mSelectedTickPen;
1137 QPen mSubTickPen, mSelectedSubTickPen;
1253 QPen mSubTickPen, mSelectedSubTickPen;
1138 // scale and range:
1254 // scale and range:
@@ -1143,13 +1259,11 protected:
1143
1259
1144 // non-property members:
1260 // non-property members:
1145 QCPGrid *mGrid;
1261 QCPGrid *mGrid;
1146 QCache<QString, CachedLabel> mLabelCache;
1262 QCPAxisPainterPrivate *mAxisPainter;
1147 int mLowestVisibleTick, mHighestVisibleTick;
1263 int mLowestVisibleTick, mHighestVisibleTick;
1148 QChar mExponentialChar, mPositiveSignChar;
1149 QVector<double> mTickVector;
1264 QVector<double> mTickVector;
1150 QVector<QString> mTickVectorLabels;
1265 QVector<QString> mTickVectorLabels;
1151 QVector<double> mSubTickVector;
1266 QVector<double> mSubTickVector;
1152 QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
1153 bool mCachedMarginValid;
1267 bool mCachedMarginValid;
1154 int mCachedMargin;
1268 int mCachedMargin;
1155
1269
@@ -1158,12 +1272,6 protected:
1158 virtual void generateAutoTicks();
1272 virtual void generateAutoTicks();
1159 virtual int calculateAutoSubTickCount(double tickStep) const;
1273 virtual int calculateAutoSubTickCount(double tickStep) const;
1160 virtual int calculateMargin();
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 // reimplemented virtual methods:
1276 // reimplemented virtual methods:
1169 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
1277 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
@@ -1197,6 +1305,72 Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::A
1197 Q_DECLARE_METATYPE(QCPAxis::SelectablePart)
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 class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
1374 class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
1201 {
1375 {
1202 Q_OBJECT
1376 Q_OBJECT
@@ -1211,8 +1385,8 class QCP_LIB_DECL QCPAbstractPlottable
1211 Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
1385 Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
1212 Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis)
1386 Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis)
1213 Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis)
1387 Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis)
1214 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable)
1388 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
1215 Q_PROPERTY(bool selected READ selected WRITE setSelected)
1389 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
1216 /// \endcond
1390 /// \endcond
1217 public:
1391 public:
1218 QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
1392 QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
@@ -1258,6 +1432,7 public:
1258
1432
1259 signals:
1433 signals:
1260 void selectionChanged(bool selected);
1434 void selectionChanged(bool selected);
1435 void selectableChanged(bool selectable);
1261
1436
1262 protected:
1437 protected:
1263 /*!
1438 /*!
@@ -1287,8 +1462,8 protected:
1287
1462
1288 // introduced virtual methods:
1463 // introduced virtual methods:
1289 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0;
1464 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0;
1290 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
1465 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
1291 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
1466 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
1292
1467
1293 // non-virtual methods:
1468 // non-virtual methods:
1294 void coordsToPixels(double key, double value, double &x, double &y) const;
1469 void coordsToPixels(double key, double value, double &x, double &y) const;
@@ -1356,8 +1531,12 public:
1356 \see setType
1531 \see setType
1357 */
1532 */
1358 enum PositionType { ptAbsolute ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.
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.
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
1360 ,ptAxisRectRatio ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect).
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 ,ptPlotCoords ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes).
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 /// \cond INCLUDE_QPROPERTIES
1586 /// \cond INCLUDE_QPROPERTIES
1408 Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)
1587 Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)
1409 Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)
1588 Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)
1410 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable)
1589 Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
1411 Q_PROPERTY(bool selected READ selected WRITE setSelected)
1590 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
1412 /// \endcond
1591 /// \endcond
1413 public:
1592 public:
1414 QCPAbstractItem(QCustomPlot *parentPlot);
1593 QCPAbstractItem(QCustomPlot *parentPlot);
@@ -1423,8 +1602,8 public:
1423 // setters:
1602 // setters:
1424 void setClipToAxisRect(bool clip);
1603 void setClipToAxisRect(bool clip);
1425 void setClipAxisRect(QCPAxisRect *rect);
1604 void setClipAxisRect(QCPAxisRect *rect);
1426 void setSelectable(bool selectable);
1605 Q_SLOT void setSelectable(bool selectable);
1427 void setSelected(bool selected);
1606 Q_SLOT void setSelected(bool selected);
1428
1607
1429 // reimplemented virtual methods:
1608 // reimplemented virtual methods:
1430 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
1609 virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
@@ -1438,6 +1617,7 public:
1438
1617
1439 signals:
1618 signals:
1440 void selectionChanged(bool selected);
1619 void selectionChanged(bool selected);
1620 void selectableChanged(bool selectable);
1441
1621
1442 protected:
1622 protected:
1443 // property members:
1623 // property members:
@@ -1498,6 +1678,16 public:
1498 };
1678 };
1499 Q_ENUMS(LayerInsertMode)
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 explicit QCustomPlot(QWidget *parent = 0);
1691 explicit QCustomPlot(QWidget *parent = 0);
1502 virtual ~QCustomPlot();
1692 virtual ~QCustomPlot();
1503
1693
@@ -1593,14 +1783,14 public:
1593 QList<QCPLegend*> selectedLegends() const;
1783 QList<QCPLegend*> selectedLegends() const;
1594 Q_SLOT void deselectAll();
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 bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
1787 bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
1598 bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
1788 bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
1599 bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
1789 bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
1600 bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
1790 bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
1601 QPixmap toPixmap(int width=0, int height=0, double scale=1.0);
1791 QPixmap toPixmap(int width=0, int height=0, double scale=1.0);
1602 void toPainter(QCPPainter *painter, int width=0, int height=0);
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 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
1795 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
1606 QCPLegend *legend;
1796 QCPLegend *legend;
@@ -1652,7 +1842,7 protected:
1652 // non-property members:
1842 // non-property members:
1653 QPixmap mPaintBuffer;
1843 QPixmap mPaintBuffer;
1654 QPoint mMousePressPos;
1844 QPoint mMousePressPos;
1655 QCPLayoutElement *mMouseEventElement;
1845 QPointer<QCPLayoutElement> mMouseEventElement;
1656 bool mReplotting;
1846 bool mReplotting;
1657
1847
1658 // reimplemented virtual methods:
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 /*! \file */
2439 /*! \file */
1687
2440
1688
2441
@@ -1721,6 +2474,7 class QCP_LIB_DECL QCPGraph : public QCP
1721 Q_PROPERTY(double errorBarSize READ errorBarSize WRITE setErrorBarSize)
2474 Q_PROPERTY(double errorBarSize READ errorBarSize WRITE setErrorBarSize)
1722 Q_PROPERTY(bool errorBarSkipSymbol READ errorBarSkipSymbol WRITE setErrorBarSkipSymbol)
2475 Q_PROPERTY(bool errorBarSkipSymbol READ errorBarSkipSymbol WRITE setErrorBarSkipSymbol)
1723 Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph)
2476 Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph)
2477 Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling)
1724 /// \endcond
2478 /// \endcond
1725 public:
2479 public:
1726 /*!
2480 /*!
@@ -1751,7 +2505,7 public:
1751 virtual ~QCPGraph();
2505 virtual ~QCPGraph();
1752
2506
1753 // getters:
2507 // getters:
1754 const QCPDataMap *data() const { return mData; }
2508 QCPDataMap *data() const { return mData; }
1755 LineStyle lineStyle() const { return mLineStyle; }
2509 LineStyle lineStyle() const { return mLineStyle; }
1756 QCPScatterStyle scatterStyle() const { return mScatterStyle; }
2510 QCPScatterStyle scatterStyle() const { return mScatterStyle; }
1757 ErrorType errorType() const { return mErrorType; }
2511 ErrorType errorType() const { return mErrorType; }
@@ -1759,6 +2513,7 public:
1759 double errorBarSize() const { return mErrorBarSize; }
2513 double errorBarSize() const { return mErrorBarSize; }
1760 bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
2514 bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
1761 QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); }
2515 QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); }
2516 bool adaptiveSampling() const { return mAdaptiveSampling; }
1762
2517
1763 // setters:
2518 // setters:
1764 void setData(QCPDataMap *data, bool copy=false);
2519 void setData(QCPDataMap *data, bool copy=false);
@@ -1776,6 +2531,7 public:
1776 void setErrorBarSize(double size);
2531 void setErrorBarSize(double size);
1777 void setErrorBarSkipSymbol(bool enabled);
2532 void setErrorBarSkipSymbol(bool enabled);
1778 void setChannelFillGraph(QCPGraph *targetGraph);
2533 void setChannelFillGraph(QCPGraph *targetGraph);
2534 void setAdaptiveSampling(bool enabled);
1779
2535
1780 // non-property methods:
2536 // non-property methods:
1781 void addData(const QCPDataMap &dataMap);
2537 void addData(const QCPDataMap &dataMap);
@@ -1807,31 +2563,34 protected:
1807 double mErrorBarSize;
2563 double mErrorBarSize;
1808 bool mErrorBarSkipSymbol;
2564 bool mErrorBarSkipSymbol;
1809 QPointer<QCPGraph> mChannelFillGraph;
2565 QPointer<QCPGraph> mChannelFillGraph;
2566 bool mAdaptiveSampling;
1810
2567
1811 // reimplemented virtual methods:
2568 // reimplemented virtual methods:
1812 virtual void draw(QCPPainter *painter);
2569 virtual void draw(QCPPainter *painter);
1813 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2570 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
1814 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2571 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
1815 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2572 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
1816 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
2573 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
1817 virtual QCPRange getValueRange(bool &validRange, 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 // introduced virtual methods:
2576 // introduced virtual methods:
1820 virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const;
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 virtual void drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
2579 virtual void drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
1823 virtual void drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
2580 virtual void drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
1824
2581
1825 // non-virtual methods:
2582 // non-virtual methods:
1826 void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2583 void getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const;
1827 void getScatterPlotData(QVector<QCPData> *pointData) const;
2584 void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const;
1828 void getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2585 void getScatterPlotData(QVector<QCPData> *scatterData) const;
1829 void getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2586 void getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
1830 void getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2587 void getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
1831 void getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2588 void getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
1832 void getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
2589 void getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
2590 void getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
1833 void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const;
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 void addFillBasePoints(QVector<QPointF> *lineData) const;
2594 void addFillBasePoints(QVector<QPointF> *lineData) const;
1836 void removeFillBasePoints(QVector<QPointF> *lineData) const;
2595 void removeFillBasePoints(QVector<QPointF> *lineData) const;
1837 QPointF lowerFillBasePoint(double lowerKey) const;
2596 QPointF lowerFillBasePoint(double lowerKey) const;
@@ -1887,8 +2646,8 public:
1887 current pen of the curve (\ref setPen).
2646 current pen of the curve (\ref setPen).
1888 \see setLineStyle
2647 \see setLineStyle
1889 */
2648 */
1890 enum LineStyle { lsNone, ///< No line is drawn between data points (e.g. only scatters)
2649 enum LineStyle { lsNone ///< No line is drawn between data points (e.g. only scatters)
1891 lsLine ///< Data points are connected with a straight line
2650 ,lsLine ///< Data points are connected with a straight line
1892 };
2651 };
1893 explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
2652 explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
1894 virtual ~QCPCurve();
2653 virtual ~QCPCurve();
@@ -1929,8 +2688,8 protected:
1929 // reimplemented virtual methods:
2688 // reimplemented virtual methods:
1930 virtual void draw(QCPPainter *painter);
2689 virtual void draw(QCPPainter *painter);
1931 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2690 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
1932 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2691 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
1933 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2692 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
1934
2693
1935 // introduced virtual methods:
2694 // introduced virtual methods:
1936 virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const;
2695 virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const;
@@ -2018,8 +2777,8 protected:
2018 // reimplemented virtual methods:
2777 // reimplemented virtual methods:
2019 virtual void draw(QCPPainter *painter);
2778 virtual void draw(QCPPainter *painter);
2020 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2779 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2021 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2780 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2022 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2781 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2023
2782
2024 // non-virtual methods:
2783 // non-virtual methods:
2025 QPolygonF getBarPolygon(double key, double value) const;
2784 QPolygonF getBarPolygon(double key, double value) const;
@@ -2103,8 +2862,8 protected:
2103 // reimplemented virtual methods:
2862 // reimplemented virtual methods:
2104 virtual void draw(QCPPainter *painter);
2863 virtual void draw(QCPPainter *painter);
2105 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2864 virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
2106 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2865 virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2107 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
2866 virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
2108
2867
2109 // introduced virtual methods:
2868 // introduced virtual methods:
2110 virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const;
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 class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
3002 class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
2121 {
3003 {
2122 Q_OBJECT
3004 Q_OBJECT
@@ -2643,392 +3525,5 protected:
2643 QPen mainPen() const;
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 #endif // QCUSTOMPLOT_H
3528 #endif // QCUSTOMPLOT_H
3034
3529
@@ -80,6 +80,11 void SocExplorerPlot::setLegendSelectedF
80 this->repaint();
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 int SocExplorerPlot::addGraph()
88 int SocExplorerPlot::addGraph()
84 {
89 {
85 this->m_plot->addGraph();
90 this->m_plot->addGraph();
@@ -18,6 +18,7 public:
18 void rescaleAxis();
18 void rescaleAxis();
19 void setLegendFont(QFont font);
19 void setLegendFont(QFont font);
20 void setLegendSelectedFont(QFont font);
20 void setLegendSelectedFont(QFont font);
21 void setAdaptativeSampling(int graphIndex,bool enable);
21 int addGraph();
22 int addGraph();
22 void setGraphName(int graphIndex,QString name);
23 void setGraphName(int graphIndex,QString name);
23 void setGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y);
24 void setGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y);
@@ -233,6 +233,11 void PythonQtWrapper_SocExplorerPlot::re
233 ( theWrappedObject->rescaleAxis());
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 void PythonQtWrapper_SocExplorerPlot::setGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QList<QVariant > x, QList<QVariant > y)
241 void PythonQtWrapper_SocExplorerPlot::setGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QList<QVariant > x, QList<QVariant > y)
237 {
242 {
238 ( theWrappedObject->setGraphData(graphIndex, x, y));
243 ( theWrappedObject->setGraphData(graphIndex, x, y));
@@ -115,6 +115,7 void delete_SocExplorerPlot(SocExplorerP
115 void addGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QVariant x, QVariant y);
115 void addGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QVariant x, QVariant y);
116 QPen getGraphPen(SocExplorerPlot* theWrappedObject, int graphIndex);
116 QPen getGraphPen(SocExplorerPlot* theWrappedObject, int graphIndex);
117 void rescaleAxis(SocExplorerPlot* theWrappedObject);
117 void rescaleAxis(SocExplorerPlot* theWrappedObject);
118 void setAdaptativeSampling(SocExplorerPlot* theWrappedObject, int graphIndex, bool enable);
118 void setGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QList<QVariant > x, QList<QVariant > y);
119 void setGraphData(SocExplorerPlot* theWrappedObject, int graphIndex, QList<QVariant > x, QList<QVariant > y);
119 void setGraphLineStyle(SocExplorerPlot* theWrappedObject, int graphIndex, QString lineStyle);
120 void setGraphLineStyle(SocExplorerPlot* theWrappedObject, int graphIndex, QString lineStyle);
120 void setGraphName(SocExplorerPlot* theWrappedObject, int graphIndex, QString name);
121 void setGraphName(SocExplorerPlot* theWrappedObject, int graphIndex, QString name);
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now