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
|
3
|
** QCustomPlot, an easy to use, modern plotting widget for Qt **
|
|
4
|
|
** Copyright (C) 2011, 2012, 2013 Emanuel Eichhammer **
|
|
|
4
|
** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer **
|
|
5
|
5
|
** **
|
|
6
|
6
|
** This program is free software: you can redistribute it and/or modify **
|
|
7
|
7
|
** it under the terms of the GNU General Public License as published by **
|
|
@@
-19,8
+19,8
|
|
19
|
19
|
****************************************************************************
|
|
20
|
20
|
** Author: Emanuel Eichhammer **
|
|
21
|
21
|
** Website/Contact: http://www.qcustomplot.com/ **
|
|
22
|
|
** Date: 09.12.13 **
|
|
23
|
|
** Version: 1.1.1 **
|
|
|
22
|
** Date: 14.03.14 **
|
|
|
23
|
** Version: 1.2.0 **
|
|
24
|
24
|
****************************************************************************/
|
|
25
|
25
|
|
|
26
|
26
|
#include "qcustomplot.h"
|
|
@@
-35,7
+35,7
|
|
35
|
35
|
/*! \class QCPPainter
|
|
36
|
36
|
\brief QPainter subclass used internally
|
|
37
|
37
|
|
|
38
|
|
This internal class is used to provide some extended functionality e.g. for tweaking position
|
|
|
38
|
This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
|
|
39
|
39
|
consistency between antialiased and non-antialiased painting. Further it provides workarounds
|
|
40
|
40
|
for QPainter quirks.
|
|
41
|
41
|
|
|
@@
-752,7
+752,8
QCPLayer::QCPLayer(QCustomPlot *parentPl
|
|
752
|
752
|
QObject(parentPlot),
|
|
753
|
753
|
mParentPlot(parentPlot),
|
|
754
|
754
|
mName(layerName),
|
|
755
|
|
mIndex(-1) // will be set to a proper value by the QCustomPlot layer creation function
|
|
|
755
|
mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
|
|
|
756
|
mVisible(true)
|
|
756
|
757
|
{
|
|
757
|
758
|
// Note: no need to make sure layerName is unique, because layer
|
|
758
|
759
|
// management is done with QCustomPlot functions.
|
|
@@
-772,6
+773,19
QCPLayer::~QCPLayer()
|
|
772
|
773
|
qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
|
|
773
|
774
|
}
|
|
774
|
775
|
|
|
|
776
|
/*!
|
|
|
777
|
Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
|
|
|
778
|
layer will be invisible.
|
|
|
779
|
|
|
|
780
|
This function doesn't change the visibility property of the layerables (\ref
|
|
|
781
|
QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
|
|
|
782
|
visibility of the parent layer into account.
|
|
|
783
|
*/
|
|
|
784
|
void QCPLayer::setVisible(bool visible)
|
|
|
785
|
{
|
|
|
786
|
mVisible = visible;
|
|
|
787
|
}
|
|
|
788
|
|
|
775
|
789
|
/*! \internal
|
|
776
|
790
|
|
|
777
|
791
|
Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
|
|
@@
-885,6
+899,17
void QCPLayer::removeChild(QCPLayerable
|
|
885
|
899
|
*/
|
|
886
|
900
|
|
|
887
|
901
|
/* end documentation of pure virtual functions */
|
|
|
902
|
/* start documentation of signals */
|
|
|
903
|
|
|
|
904
|
/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
|
|
|
905
|
|
|
|
906
|
This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
|
|
|
907
|
a different layer.
|
|
|
908
|
|
|
|
909
|
\see setLayer
|
|
|
910
|
*/
|
|
|
911
|
|
|
|
912
|
/* end documentation of signals */
|
|
888
|
913
|
|
|
889
|
914
|
/*!
|
|
890
|
915
|
Creates a new QCPLayerable instance.
|
|
@@
-985,9
+1010,9
void QCPLayerable::setAntialiased(bool e
|
|
985
|
1010
|
}
|
|
986
|
1011
|
|
|
987
|
1012
|
/*!
|
|
988
|
|
Returns whether this layerable is visible, taking possible direct layerable parent visibility
|
|
989
|
|
into account. This is the method that is consulted to decide whether a layerable shall be drawn
|
|
990
|
|
or not.
|
|
|
1013
|
Returns whether this layerable is visible, taking the visibility of the layerable parent and the
|
|
|
1014
|
visibility of the layer this layerable is on into account. This is the method that is consulted
|
|
|
1015
|
to decide whether a layerable shall be drawn or not.
|
|
991
|
1016
|
|
|
992
|
1017
|
If this layerable has a direct layerable parent (usually set via hierarchies implemented in
|
|
993
|
1018
|
subclasses, like in the case of QCPLayoutElement), this function returns true only if this
|
|
@@
-999,7
+1024,7
void QCPLayerable::setAntialiased(bool e
|
|
999
|
1024
|
*/
|
|
1000
|
1025
|
bool QCPLayerable::realVisibility() const
|
|
1001
|
1026
|
{
|
|
1002
|
|
return mVisible && (!mParentLayerable || mParentLayerable.data()->realVisibility());
|
|
|
1027
|
return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
|
|
1003
|
1028
|
}
|
|
1004
|
1029
|
|
|
1005
|
1030
|
/*!
|
|
@@
-1113,11
+1138,14
bool QCPLayerable::moveToLayer(QCPLayer
|
|
1113
|
1138
|
return false;
|
|
1114
|
1139
|
}
|
|
1115
|
1140
|
|
|
|
1141
|
QCPLayer *oldLayer = mLayer;
|
|
1116
|
1142
|
if (mLayer)
|
|
1117
|
1143
|
mLayer->removeChild(this);
|
|
1118
|
1144
|
mLayer = layer;
|
|
1119
|
1145
|
if (mLayer)
|
|
1120
|
1146
|
mLayer->addChild(this, prepend);
|
|
|
1147
|
if (mLayer != oldLayer)
|
|
|
1148
|
emit layerChanged(mLayer);
|
|
1121
|
1149
|
return true;
|
|
1122
|
1150
|
}
|
|
1123
|
1151
|
|
|
@@
-1532,17
+1560,11
bool QCPRange::validRange(const QCPRange
|
|
1532
|
1560
|
Since a QCPLayout is a layout element itself, it may be placed inside other layouts. This way,
|
|
1533
|
1561
|
complex hierarchies may be created, offering very flexible arrangements.
|
|
1534
|
1562
|
|
|
1535
|
|
<div style="text-align:center">
|
|
1536
|
|
<div style="display:inline-block; margin-left:auto; margin-right:auto">\image html LayoutsystemSketch0.png ""</div>
|
|
1537
|
|
<div style="display:inline-block; margin-left:auto; margin-right:auto">\image html LayoutsystemSketch1.png ""</div>
|
|
1538
|
|
<div style="clear:both"></div>
|
|
1539
|
|
<div style="display:inline-block; max-width:1000px; text-align:justify">
|
|
1540
|
|
Sketch of the default QCPLayoutGrid accessible via \ref QCustomPlot::plotLayout. The left image
|
|
1541
|
|
shows the outer and inner rect of the grid layout itself while the right image shows how two
|
|
1542
|
|
child layout elements are placed inside the grid layout next to each other in cells (0, 0) and
|
|
1543
|
|
(0, 1).
|
|
1544
|
|
</div>
|
|
1545
|
|
</div>
|
|
|
1563
|
\image html LayoutsystemSketch.png
|
|
|
1564
|
|
|
|
1565
|
Above is a sketch of the default \ref QCPLayoutGrid accessible via \ref QCustomPlot::plotLayout.
|
|
|
1566
|
It shows how two child layout elements are placed inside the grid layout next to each other in
|
|
|
1567
|
cells (0, 0) and (0, 1).
|
|
1546
|
1568
|
|
|
1547
|
1569
|
\subsection layoutsystem-plotlayout The top level plot layout
|
|
1548
|
1570
|
|
|
@@
-1561,7
+1583,7
bool QCPRange::validRange(const QCPRange
|
|
1561
|
1583
|
title->setFont(QFont("sans", 12, QFont::Bold));
|
|
1562
|
1584
|
// then we add it to the main plot layout:
|
|
1563
|
1585
|
customPlot->plotLayout()->insertRow(0); // insert an empty row above the axis rect
|
|
1564
|
|
customPlot->plotLayout()->addElement(0, 0, title); // insert the title in the empty cell we just created
|
|
|
1586
|
customPlot->plotLayout()->addElement(0, 0, title); // place the title in the empty cell we've just created
|
|
1565
|
1587
|
\endcode
|
|
1566
|
1588
|
\image html layoutsystem-addingplottitle.png
|
|
1567
|
1589
|
|
|
@@
-1569,15
+1591,28
bool QCPRange::validRange(const QCPRange
|
|
1569
|
1591
|
\code
|
|
1570
|
1592
|
customPlot->plotLayout()->clear(); // let's start from scratch and remove the default axis rect
|
|
1571
|
1593
|
// add the first axis rect in second row (row index 1):
|
|
1572
|
|
customPlot->plotLayout()->addElement(1, 0, new QCPAxisRect(customPlot));
|
|
|
1594
|
QCPAxisRect *topAxisRect = new QCPAxisRect(customPlot);
|
|
|
1595
|
customPlot->plotLayout()->addElement(1, 0, topAxisRect);
|
|
1573
|
1596
|
// create a sub layout that we'll place in first row:
|
|
1574
|
1597
|
QCPLayoutGrid *subLayout = new QCPLayoutGrid;
|
|
1575
|
1598
|
customPlot->plotLayout()->addElement(0, 0, subLayout);
|
|
1576
|
1599
|
// add two axis rects in the sub layout next to eachother:
|
|
1577
|
|
subLayout->addElement(0, 0, new QCPAxisRect(customPlot));
|
|
1578
|
|
subLayout->addElement(0, 1, new QCPAxisRect(customPlot));
|
|
|
1600
|
QCPAxisRect *leftAxisRect = new QCPAxisRect(customPlot);
|
|
|
1601
|
QCPAxisRect *rightAxisRect = new QCPAxisRect(customPlot);
|
|
|
1602
|
subLayout->addElement(0, 0, leftAxisRect);
|
|
|
1603
|
subLayout->addElement(0, 1, rightAxisRect);
|
|
1579
|
1604
|
subLayout->setColumnStretchFactor(0, 3); // left axis rect shall have 60% of width
|
|
1580
|
1605
|
subLayout->setColumnStretchFactor(1, 2); // right one only 40% (3:2 = 60:40)
|
|
|
1606
|
// since we've created the axis rects and axes from scratch, we need to place them on
|
|
|
1607
|
// according layers, if we don't want the grid to be drawn above the axes etc.
|
|
|
1608
|
// place the axis on "axes" layer and grids on the "grid" layer, which is below "axes":
|
|
|
1609
|
QList<QCPAxis*> allAxes;
|
|
|
1610
|
allAxes << topAxisRect->axes() << leftAxisRect->axes() << rightAxisRect->axes();
|
|
|
1611
|
foreach (QCPAxis *axis, allAxes)
|
|
|
1612
|
{
|
|
|
1613
|
axis->setLayer("axes");
|
|
|
1614
|
axis->grid()->setLayer("grid");
|
|
|
1615
|
}
|
|
1581
|
1616
|
\endcode
|
|
1582
|
1617
|
\image html layoutsystem-multipleaxisrects.png
|
|
1583
|
1618
|
|
|
@@
-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
|
|
2011
|
|
the parent layout element.
|
|
|
2045
|
Updates the layout element and sub-elements. This function is automatically called before every
|
|
|
2046
|
replot by the parent layout element. It is called multiple times, once for every \ref
|
|
|
2047
|
UpdatePhase. The phases are run through in the order of the enum values. For details about what
|
|
|
2048
|
happens at the different phases, see the documentation of \ref UpdatePhase.
|
|
2012
|
2049
|
|
|
2013
|
2050
|
Layout elements that have child elements should call the \ref update method of their child
|
|
2014
|
|
elements.
|
|
2015
|
|
|
|
2016
|
|
The default implementation executes the automatic margin mechanism, so subclasses should make
|
|
2017
|
|
sure to call the base class implementation.
|
|
2018
|
|
*/
|
|
2019
|
|
void QCPLayoutElement::update()
|
|
|
2051
|
elements, and pass the current \a phase unchanged.
|
|
|
2052
|
|
|
|
2053
|
The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
|
|
|
2054
|
Subclasses should make sure to call the base class implementation.
|
|
|
2055
|
*/
|
|
|
2056
|
void QCPLayoutElement::update(UpdatePhase phase)
|
|
|
2057
|
{
|
|
|
2058
|
if (phase == upMargins)
|
|
2020
|
2059
|
{
|
|
2021
|
2060
|
if (mAutoMargins != QCP::msNone)
|
|
2022
|
2061
|
{
|
|
2023
|
2062
|
// set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
|
|
2024
|
2063
|
QMargins newMargins = mMargins;
|
|
2025
|
|
QVector<QCP::MarginSide> marginSides = QVector<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
|
|
2026
|
|
for (int i=0; i<marginSides.size(); ++i)
|
|
2027
|
|
{
|
|
2028
|
|
QCP::MarginSide side = marginSides.at(i);
|
|
|
2064
|
foreach (QCP::MarginSide side, QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom)
|
|
|
2065
|
{
|
|
2029
|
2066
|
if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
|
|
2030
|
2067
|
{
|
|
2031
|
2068
|
if (mMarginGroups.contains(side))
|
|
@@
-2040,6
+2077,7
void QCPLayoutElement::update()
|
|
2040
|
2077
|
setMargins(newMargins);
|
|
2041
|
2078
|
}
|
|
2042
|
2079
|
}
|
|
|
2080
|
}
|
|
2043
|
2081
|
|
|
2044
|
2082
|
/*!
|
|
2045
|
2083
|
Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
|
|
@@
-2069,7
+2107,7
QSize QCPLayoutElement::maximumSizeHint(
|
|
2069
|
2107
|
Returns a list of all child elements in this layout element. If \a recursive is true, all
|
|
2070
|
2108
|
sub-child elements are included in the list, too.
|
|
2071
|
2109
|
|
|
2072
|
|
Note that there may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
|
|
|
2110
|
\warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
|
|
2073
|
2111
|
empty cells which yield 0 at the respective index.)
|
|
2074
|
2112
|
*/
|
|
2075
|
2113
|
QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
|
|
@@
-2116,11
+2154,10
double QCPLayoutElement::selectTest(cons
|
|
2116
|
2154
|
*/
|
|
2117
|
2155
|
void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
|
|
2118
|
2156
|
{
|
|
2119
|
|
QList<QCPLayoutElement*> els = elements(false);
|
|
2120
|
|
for (int i=0; i<els.size(); ++i)
|
|
2121
|
|
{
|
|
2122
|
|
if (!els.at(i)->parentPlot())
|
|
2123
|
|
els.at(i)->initializeParentPlot(parentPlot);
|
|
|
2157
|
foreach (QCPLayoutElement* el, elements(false))
|
|
|
2158
|
{
|
|
|
2159
|
if (!el->parentPlot())
|
|
|
2160
|
el->initializeParentPlot(parentPlot);
|
|
2124
|
2161
|
}
|
|
2125
|
2162
|
}
|
|
2126
|
2163
|
|
|
@@
-2227,25
+2264,27
QCPLayout::QCPLayout()
|
|
2227
|
2264
|
|
|
2228
|
2265
|
Finally, \ref update is called on all child elements.
|
|
2229
|
2266
|
*/
|
|
2230
|
|
void QCPLayout::update()
|
|
2231
|
|
{
|
|
2232
|
|
QCPLayoutElement::update(); // recalculates (auto-)margins
|
|
|
2267
|
void QCPLayout::update(UpdatePhase phase)
|
|
|
2268
|
{
|
|
|
2269
|
QCPLayoutElement::update(phase);
|
|
2233
|
2270
|
|
|
2234
|
2271
|
// set child element rects according to layout:
|
|
|
2272
|
if (phase == upLayout)
|
|
2235
|
2273
|
updateLayout();
|
|
2236
|
2274
|
|
|
2237
|
2275
|
// propagate update call to child elements:
|
|
2238
|
|
for (int i=0; i<elementCount(); ++i)
|
|
|
2276
|
const int elCount = elementCount();
|
|
|
2277
|
for (int i=0; i<elCount; ++i)
|
|
2239
|
2278
|
{
|
|
2240
|
2279
|
if (QCPLayoutElement *el = elementAt(i))
|
|
2241
|
|
el->update();
|
|
|
2280
|
el->update(phase);
|
|
2242
|
2281
|
}
|
|
2243
|
2282
|
}
|
|
2244
|
2283
|
|
|
2245
|
2284
|
/* inherits documentation from base class */
|
|
2246
|
2285
|
QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
|
|
2247
|
2286
|
{
|
|
2248
|
|
int c = elementCount();
|
|
|
2287
|
const int c = elementCount();
|
|
2249
|
2288
|
QList<QCPLayoutElement*> result;
|
|
2250
|
2289
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
|
2251
|
2290
|
result.reserve(c);
|
|
@@
-3616,8
+3655,8
void QCPLineEnding::draw(QCPPainter *pai
|
|
3616
|
3655
|
if (lengthVec.isNull())
|
|
3617
|
3656
|
lengthVec = QVector2D(1, 0);
|
|
3618
|
3657
|
QVector2D widthVec(-lengthVec.y(), lengthVec.x());
|
|
3619
|
|
lengthVec *= mLength*(mInverted ? -1 : 1);
|
|
3620
|
|
widthVec *= mWidth*0.5*(mInverted ? -1 : 1);
|
|
|
3658
|
lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
|
|
|
3659
|
widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
|
|
3621
|
3660
|
|
|
3622
|
3661
|
QPen penBackup = painter->pen();
|
|
3623
|
3662
|
QBrush brushBackup = painter->brush();
|
|
@@
-3644,7
+3683,7
void QCPLineEnding::draw(QCPPainter *pai
|
|
3644
|
3683
|
{
|
|
3645
|
3684
|
QPointF points[4] = {pos.toPointF(),
|
|
3646
|
3685
|
(pos-lengthVec+widthVec).toPointF(),
|
|
3647
|
|
(pos-lengthVec*0.8).toPointF(),
|
|
|
3686
|
(pos-lengthVec*0.8f).toPointF(),
|
|
3648
|
3687
|
(pos-lengthVec-widthVec).toPointF()
|
|
3649
|
3688
|
};
|
|
3650
|
3689
|
painter->setPen(miterPen);
|
|
@@
-3717,13
+3756,13
void QCPLineEnding::draw(QCPPainter *pai
|
|
3717
|
3756
|
if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
|
|
3718
|
3757
|
{
|
|
3719
|
3758
|
// if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
|
|
3720
|
|
painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(),
|
|
3721
|
|
(pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
|
|
|
3759
|
painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
|
|
|
3760
|
(pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
|
|
3722
|
3761
|
} else
|
|
3723
|
3762
|
{
|
|
3724
|
3763
|
// if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
|
|
3725
|
|
painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0, (double)painter->pen().widthF())*0.5).toPointF(),
|
|
3726
|
|
(pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0, (double)painter->pen().widthF())*0.5).toPointF());
|
|
|
3764
|
painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
|
|
|
3765
|
(pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
|
|
3727
|
3766
|
}
|
|
3728
|
3767
|
break;
|
|
3729
|
3768
|
}
|
|
@@
-3994,8
+4033,10
void QCPGrid::drawSubGridLines(QCPPainte
|
|
3994
|
4033
|
|
|
3995
|
4034
|
/*! \fn Qt::Orientation QCPAxis::orientation() const
|
|
3996
|
4035
|
|
|
3997
|
|
Returns the orientation of the axis. The axis orientation (horizontal or vertical) is deduced
|
|
|
4036
|
Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
|
|
3998
|
4037
|
from the axis type (left, top, right or bottom).
|
|
|
4038
|
|
|
|
4039
|
\see orientation(AxisType type)
|
|
3999
|
4040
|
*/
|
|
4000
|
4041
|
|
|
4001
|
4042
|
/*! \fn QCPGrid *QCPAxis::grid() const
|
|
@@
-4004,6
+4045,13
void QCPGrid::drawSubGridLines(QCPPainte
|
|
4004
|
4045
|
grid is displayed.
|
|
4005
|
4046
|
*/
|
|
4006
|
4047
|
|
|
|
4048
|
/*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
|
|
|
4049
|
|
|
|
4050
|
Returns the orientation of the specified axis type
|
|
|
4051
|
|
|
|
4052
|
\see orientation()
|
|
|
4053
|
*/
|
|
|
4054
|
|
|
4007
|
4055
|
/* end of documentation of inline functions */
|
|
4008
|
4056
|
/* start of documentation of signals */
|
|
4009
|
4057
|
|
|
@@
-4036,12
+4084,22
void QCPGrid::drawSubGridLines(QCPPainte
|
|
4036
|
4084
|
\a oldRange.
|
|
4037
|
4085
|
*/
|
|
4038
|
4086
|
|
|
|
4087
|
/*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
|
|
|
4088
|
|
|
|
4089
|
This signal is emitted when the scale type changes, by calls to \ref setScaleType
|
|
|
4090
|
*/
|
|
|
4091
|
|
|
4039
|
4092
|
/*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
|
|
4040
|
4093
|
|
|
4041
|
4094
|
This signal is emitted when the selection state of this axis has changed, either by user interaction
|
|
4042
|
4095
|
or by a direct call to \ref setSelectedParts.
|
|
4043
|
4096
|
*/
|
|
4044
|
4097
|
|
|
|
4098
|
/*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
|
|
|
4099
|
|
|
|
4100
|
This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
|
|
|
4101
|
*/
|
|
|
4102
|
|
|
4045
|
4103
|
/* end of documentation of signals */
|
|
4046
|
4104
|
|
|
4047
|
4105
|
/*!
|
|
@@
-4053,27
+4111,21
QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
|
|
4053
|
4111
|
// axis base:
|
|
4054
|
4112
|
mAxisType(type),
|
|
4055
|
4113
|
mAxisRect(parent),
|
|
4056
|
|
mOffset(0),
|
|
4057
|
4114
|
mPadding(5),
|
|
4058
|
|
mOrientation((type == atBottom || type == atTop) ? Qt::Horizontal : Qt::Vertical),
|
|
|
4115
|
mOrientation(orientation(type)),
|
|
4059
|
4116
|
mSelectableParts(spAxis | spTickLabels | spAxisLabel),
|
|
4060
|
4117
|
mSelectedParts(spNone),
|
|
4061
|
4118
|
mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
|
|
4062
|
4119
|
mSelectedBasePen(QPen(Qt::blue, 2)),
|
|
4063
|
|
mLowerEnding(QCPLineEnding::esNone),
|
|
4064
|
|
mUpperEnding(QCPLineEnding::esNone),
|
|
4065
|
4120
|
// axis label:
|
|
4066
|
|
mLabelPadding(0),
|
|
4067
|
4121
|
mLabel(""),
|
|
4068
|
4122
|
mLabelFont(mParentPlot->font()),
|
|
4069
|
4123
|
mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
|
|
4070
|
4124
|
mLabelColor(Qt::black),
|
|
4071
|
4125
|
mSelectedLabelColor(Qt::blue),
|
|
4072
|
4126
|
// tick labels:
|
|
4073
|
|
mTickLabelPadding(0),
|
|
4074
|
4127
|
mTickLabels(true),
|
|
4075
|
4128
|
mAutoTickLabels(true),
|
|
4076
|
|
mTickLabelRotation(0),
|
|
4077
|
4129
|
mTickLabelType(ltNumber),
|
|
4078
|
4130
|
mTickLabelFont(mParentPlot->font()),
|
|
4079
|
4131
|
mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
|
|
@@
-4084,7
+4136,6
QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
|
|
4084
|
4136
|
mNumberPrecision(6),
|
|
4085
|
4137
|
mNumberFormatChar('g'),
|
|
4086
|
4138
|
mNumberBeautifulPowers(true),
|
|
4087
|
|
mNumberMultiplyCross(false),
|
|
4088
|
4139
|
// ticks and subticks:
|
|
4089
|
4140
|
mTicks(true),
|
|
4090
|
4141
|
mTickStep(1),
|
|
@@
-4093,10
+4144,6
QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
|
|
4093
|
4144
|
mAutoTicks(true),
|
|
4094
|
4145
|
mAutoTickStep(true),
|
|
4095
|
4146
|
mAutoSubTicks(true),
|
|
4096
|
|
mTickLengthIn(5),
|
|
4097
|
|
mTickLengthOut(0),
|
|
4098
|
|
mSubTickLengthIn(2),
|
|
4099
|
|
mSubTickLengthOut(0),
|
|
4100
|
4147
|
mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
|
|
4101
|
4148
|
mSelectedTickPen(QPen(Qt::blue, 2)),
|
|
4102
|
4149
|
mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
|
|
@@
-4109,11
+4156,9
QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
|
|
4109
|
4156
|
mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
|
|
4110
|
4157
|
// internal members:
|
|
4111
|
4158
|
mGrid(new QCPGrid(this)),
|
|
4112
|
|
mLabelCache(16), // cache at most 16 (tick) labels
|
|
|
4159
|
mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
|
|
4113
|
4160
|
mLowestVisibleTick(0),
|
|
4114
|
4161
|
mHighestVisibleTick(-1),
|
|
4115
|
|
mExponentialChar('e'), // will be updated with locale sensitive values in setupTickVector
|
|
4116
|
|
mPositiveSignChar('+'), // will be updated with locale sensitive values in setupTickVector
|
|
4117
|
4162
|
mCachedMarginValid(false),
|
|
4118
|
4163
|
mCachedMargin(0)
|
|
4119
|
4164
|
{
|
|
@@
-4140,6
+4185,23
QCPAxis::QCPAxis(QCPAxisRect *parent, Ax
|
|
4140
|
4185
|
}
|
|
4141
|
4186
|
}
|
|
4142
|
4187
|
|
|
|
4188
|
QCPAxis::~QCPAxis()
|
|
|
4189
|
{
|
|
|
4190
|
delete mAxisPainter;
|
|
|
4191
|
}
|
|
|
4192
|
|
|
|
4193
|
/* No documentation as it is a property getter */
|
|
|
4194
|
int QCPAxis::tickLabelPadding() const
|
|
|
4195
|
{
|
|
|
4196
|
return mAxisPainter->tickLabelPadding;
|
|
|
4197
|
}
|
|
|
4198
|
|
|
|
4199
|
/* No documentation as it is a property getter */
|
|
|
4200
|
double QCPAxis::tickLabelRotation() const
|
|
|
4201
|
{
|
|
|
4202
|
return mAxisPainter->tickLabelRotation;
|
|
|
4203
|
}
|
|
|
4204
|
|
|
4143
|
4205
|
/* No documentation as it is a property getter */
|
|
4144
|
4206
|
QString QCPAxis::numberFormat() const
|
|
4145
|
4207
|
{
|
|
@@
-4148,12
+4210,60
QString QCPAxis::numberFormat() const
|
|
4148
|
4210
|
if (mNumberBeautifulPowers)
|
|
4149
|
4211
|
{
|
|
4150
|
4212
|
result.append("b");
|
|
4151
|
|
if (mNumberMultiplyCross)
|
|
|
4213
|
if (mAxisPainter->numberMultiplyCross)
|
|
4152
|
4214
|
result.append("c");
|
|
4153
|
4215
|
}
|
|
4154
|
4216
|
return result;
|
|
4155
|
4217
|
}
|
|
4156
|
4218
|
|
|
|
4219
|
/* No documentation as it is a property getter */
|
|
|
4220
|
int QCPAxis::tickLengthIn() const
|
|
|
4221
|
{
|
|
|
4222
|
return mAxisPainter->tickLengthIn;
|
|
|
4223
|
}
|
|
|
4224
|
|
|
|
4225
|
/* No documentation as it is a property getter */
|
|
|
4226
|
int QCPAxis::tickLengthOut() const
|
|
|
4227
|
{
|
|
|
4228
|
return mAxisPainter->tickLengthOut;
|
|
|
4229
|
}
|
|
|
4230
|
|
|
|
4231
|
/* No documentation as it is a property getter */
|
|
|
4232
|
int QCPAxis::subTickLengthIn() const
|
|
|
4233
|
{
|
|
|
4234
|
return mAxisPainter->subTickLengthIn;
|
|
|
4235
|
}
|
|
|
4236
|
|
|
|
4237
|
/* No documentation as it is a property getter */
|
|
|
4238
|
int QCPAxis::subTickLengthOut() const
|
|
|
4239
|
{
|
|
|
4240
|
return mAxisPainter->subTickLengthOut;
|
|
|
4241
|
}
|
|
|
4242
|
|
|
|
4243
|
/* No documentation as it is a property getter */
|
|
|
4244
|
int QCPAxis::labelPadding() const
|
|
|
4245
|
{
|
|
|
4246
|
return mAxisPainter->labelPadding;
|
|
|
4247
|
}
|
|
|
4248
|
|
|
|
4249
|
/* No documentation as it is a property getter */
|
|
|
4250
|
int QCPAxis::offset() const
|
|
|
4251
|
{
|
|
|
4252
|
return mAxisPainter->offset;
|
|
|
4253
|
}
|
|
|
4254
|
|
|
|
4255
|
/* No documentation as it is a property getter */
|
|
|
4256
|
QCPLineEnding QCPAxis::lowerEnding() const
|
|
|
4257
|
{
|
|
|
4258
|
return mAxisPainter->lowerEnding;
|
|
|
4259
|
}
|
|
|
4260
|
|
|
|
4261
|
/* No documentation as it is a property getter */
|
|
|
4262
|
QCPLineEnding QCPAxis::upperEnding() const
|
|
|
4263
|
{
|
|
|
4264
|
return mAxisPainter->upperEnding;
|
|
|
4265
|
}
|
|
|
4266
|
|
|
4157
|
4267
|
/*!
|
|
4158
|
4268
|
Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
|
|
4159
|
4269
|
stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
|
|
@@
-4167,14
+4277,15
QString QCPAxis::numberFormat() const
|
|
4167
|
4277
|
part). To only display the decimal power, set the number precision to zero with
|
|
4168
|
4278
|
\ref setNumberPrecision.
|
|
4169
|
4279
|
*/
|
|
4170
|
|
void QCPAxis::setScaleType(ScaleType type)
|
|
|
4280
|
void QCPAxis::setScaleType(QCPAxis::ScaleType type)
|
|
4171
|
4281
|
{
|
|
4172
|
4282
|
if (mScaleType != type)
|
|
4173
|
4283
|
{
|
|
4174
|
4284
|
mScaleType = type;
|
|
4175
|
4285
|
if (mScaleType == stLogarithmic)
|
|
4176
|
|
mRange = mRange.sanitizedForLogScale();
|
|
|
4286
|
setRange(mRange.sanitizedForLogScale());
|
|
4177
|
4287
|
mCachedMarginValid = false;
|
|
|
4288
|
emit scaleTypeChanged(mScaleType);
|
|
4178
|
4289
|
}
|
|
4179
|
4290
|
}
|
|
4180
|
4291
|
|
|
@@
-4235,7
+4346,11
void QCPAxis::setRange(const QCPRange &r
|
|
4235
|
4346
|
*/
|
|
4236
|
4347
|
void QCPAxis::setSelectableParts(const SelectableParts &selectable)
|
|
4237
|
4348
|
{
|
|
|
4349
|
if (mSelectableParts != selectable)
|
|
|
4350
|
{
|
|
4238
|
4351
|
mSelectableParts = selectable;
|
|
|
4352
|
emit selectableChanged(mSelectableParts);
|
|
|
4353
|
}
|
|
4239
|
4354
|
}
|
|
4240
|
4355
|
|
|
4241
|
4356
|
/*!
|
|
@@
-4257,8
+4372,6
void QCPAxis::setSelectedParts(const Sel
|
|
4257
|
4372
|
{
|
|
4258
|
4373
|
if (mSelectedParts != selected)
|
|
4259
|
4374
|
{
|
|
4260
|
|
if (mSelectedParts.testFlag(spTickLabels) != selected.testFlag(spTickLabels))
|
|
4261
|
|
mLabelCache.clear();
|
|
4262
|
4375
|
mSelectedParts = selected;
|
|
4263
|
4376
|
emit selectionChanged(mSelectedParts);
|
|
4264
|
4377
|
}
|
|
@@
-4523,9
+4636,9
void QCPAxis::setTickLabels(bool show)
|
|
4523
|
4636
|
*/
|
|
4524
|
4637
|
void QCPAxis::setTickLabelPadding(int padding)
|
|
4525
|
4638
|
{
|
|
4526
|
|
if (mTickLabelPadding != padding)
|
|
4527
|
|
{
|
|
4528
|
|
mTickLabelPadding = padding;
|
|
|
4639
|
if (mAxisPainter->tickLabelPadding != padding)
|
|
|
4640
|
{
|
|
|
4641
|
mAxisPainter->tickLabelPadding = padding;
|
|
4529
|
4642
|
mCachedMarginValid = false;
|
|
4530
|
4643
|
}
|
|
4531
|
4644
|
}
|
|
@@
-4571,7
+4684,6
void QCPAxis::setTickLabelFont(const QFo
|
|
4571
|
4684
|
{
|
|
4572
|
4685
|
mTickLabelFont = font;
|
|
4573
|
4686
|
mCachedMarginValid = false;
|
|
4574
|
|
mLabelCache.clear();
|
|
4575
|
4687
|
}
|
|
4576
|
4688
|
}
|
|
4577
|
4689
|
|
|
@@
-4586,7
+4698,6
void QCPAxis::setTickLabelColor(const QC
|
|
4586
|
4698
|
{
|
|
4587
|
4699
|
mTickLabelColor = color;
|
|
4588
|
4700
|
mCachedMarginValid = false;
|
|
4589
|
|
mLabelCache.clear();
|
|
4590
|
4701
|
}
|
|
4591
|
4702
|
}
|
|
4592
|
4703
|
|
|
@@
-4601,11
+4712,10
void QCPAxis::setTickLabelColor(const QC
|
|
4601
|
4712
|
*/
|
|
4602
|
4713
|
void QCPAxis::setTickLabelRotation(double degrees)
|
|
4603
|
4714
|
{
|
|
4604
|
|
if (!qFuzzyIsNull(degrees-mTickLabelRotation))
|
|
4605
|
|
{
|
|
4606
|
|
mTickLabelRotation = qBound(-90.0, degrees, 90.0);
|
|
|
4715
|
if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
|
|
|
4716
|
{
|
|
|
4717
|
mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
|
|
4607
|
4718
|
mCachedMarginValid = false;
|
|
4608
|
|
mLabelCache.clear();
|
|
4609
|
4719
|
}
|
|
4610
|
4720
|
}
|
|
4611
|
4721
|
|
|
@@
-4623,7
+4733,6
void QCPAxis::setDateTimeFormat(const QS
|
|
4623
|
4733
|
{
|
|
4624
|
4734
|
mDateTimeFormat = format;
|
|
4625
|
4735
|
mCachedMarginValid = false;
|
|
4626
|
|
mLabelCache.clear();
|
|
4627
|
4736
|
}
|
|
4628
|
4737
|
}
|
|
4629
|
4738
|
|
|
@@
-4685,7
+4794,6
void QCPAxis::setNumberFormat(const QStr
|
|
4685
|
4794
|
qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
|
|
4686
|
4795
|
return;
|
|
4687
|
4796
|
}
|
|
4688
|
|
mLabelCache.clear();
|
|
4689
|
4797
|
mCachedMarginValid = false;
|
|
4690
|
4798
|
|
|
4691
|
4799
|
// interpret first char as number format char:
|
|
@@
-4701,7
+4809,7
void QCPAxis::setNumberFormat(const QStr
|
|
4701
|
4809
|
if (formatCode.length() < 2)
|
|
4702
|
4810
|
{
|
|
4703
|
4811
|
mNumberBeautifulPowers = false;
|
|
4704
|
|
mNumberMultiplyCross = false;
|
|
|
4812
|
mAxisPainter->numberMultiplyCross = false;
|
|
4705
|
4813
|
return;
|
|
4706
|
4814
|
}
|
|
4707
|
4815
|
|
|
@@
-4716,17
+4824,17
void QCPAxis::setNumberFormat(const QStr
|
|
4716
|
4824
|
}
|
|
4717
|
4825
|
if (formatCode.length() < 3)
|
|
4718
|
4826
|
{
|
|
4719
|
|
mNumberMultiplyCross = false;
|
|
|
4827
|
mAxisPainter->numberMultiplyCross = false;
|
|
4720
|
4828
|
return;
|
|
4721
|
4829
|
}
|
|
4722
|
4830
|
|
|
4723
|
4831
|
// interpret third char as indicator for dot or cross multiplication symbol:
|
|
4724
|
4832
|
if (formatCode.at(2) == 'c')
|
|
4725
|
4833
|
{
|
|
4726
|
|
mNumberMultiplyCross = true;
|
|
|
4834
|
mAxisPainter->numberMultiplyCross = true;
|
|
4727
|
4835
|
} else if (formatCode.at(2) == 'd')
|
|
4728
|
4836
|
{
|
|
4729
|
|
mNumberMultiplyCross = false;
|
|
|
4837
|
mAxisPainter->numberMultiplyCross = false;
|
|
4730
|
4838
|
} else
|
|
4731
|
4839
|
{
|
|
4732
|
4840
|
qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
|
|
@@
-4812,32
+4920,25
void QCPAxis::setTickVectorLabels(const
|
|
4812
|
4920
|
zero, the tick labels and axis label will increase their distance to the axis accordingly, so
|
|
4813
|
4921
|
they won't collide with the ticks.
|
|
4814
|
4922
|
|
|
4815
|
|
\see setSubTickLength
|
|
|
4923
|
\see setSubTickLength, setTickLengthIn, setTickLengthOut
|
|
4816
|
4924
|
*/
|
|
4817
|
4925
|
void QCPAxis::setTickLength(int inside, int outside)
|
|
4818
|
4926
|
{
|
|
4819
|
|
if (mTickLengthIn != inside)
|
|
4820
|
|
{
|
|
4821
|
|
mTickLengthIn = inside;
|
|
4822
|
|
}
|
|
4823
|
|
if (mTickLengthOut != outside)
|
|
4824
|
|
{
|
|
4825
|
|
mTickLengthOut = outside;
|
|
4826
|
|
mCachedMarginValid = false; // only outside tick length can change margin
|
|
4827
|
|
}
|
|
|
4927
|
setTickLengthIn(inside);
|
|
|
4928
|
setTickLengthOut(outside);
|
|
4828
|
4929
|
}
|
|
4829
|
4930
|
|
|
4830
|
4931
|
/*!
|
|
4831
|
4932
|
Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
|
|
4832
|
4933
|
inside the plot.
|
|
4833
|
4934
|
|
|
4834
|
|
\see setTickLengthOut, setSubTickLength
|
|
|
4935
|
\see setTickLengthOut, setTickLength, setSubTickLength
|
|
4835
|
4936
|
*/
|
|
4836
|
4937
|
void QCPAxis::setTickLengthIn(int inside)
|
|
4837
|
4938
|
{
|
|
4838
|
|
if (mTickLengthIn != inside)
|
|
4839
|
|
{
|
|
4840
|
|
mTickLengthIn = inside;
|
|
|
4939
|
if (mAxisPainter->tickLengthIn != inside)
|
|
|
4940
|
{
|
|
|
4941
|
mAxisPainter->tickLengthIn = inside;
|
|
4841
|
4942
|
}
|
|
4842
|
4943
|
}
|
|
4843
|
4944
|
|
|
@@
-4846,13
+4947,13
void QCPAxis::setTickLengthIn(int inside
|
|
4846
|
4947
|
outside the plot. If \a outside is greater than zero, the tick labels and axis label will
|
|
4847
|
4948
|
increase their distance to the axis accordingly, so they won't collide with the ticks.
|
|
4848
|
4949
|
|
|
4849
|
|
\see setTickLengthIn, setSubTickLength
|
|
|
4950
|
\see setTickLengthIn, setTickLength, setSubTickLength
|
|
4850
|
4951
|
*/
|
|
4851
|
4952
|
void QCPAxis::setTickLengthOut(int outside)
|
|
4852
|
4953
|
{
|
|
4853
|
|
if (mTickLengthOut != outside)
|
|
4854
|
|
{
|
|
4855
|
|
mTickLengthOut = outside;
|
|
|
4954
|
if (mAxisPainter->tickLengthOut != outside)
|
|
|
4955
|
{
|
|
|
4956
|
mAxisPainter->tickLengthOut = outside;
|
|
4856
|
4957
|
mCachedMarginValid = false; // only outside tick length can change margin
|
|
4857
|
4958
|
}
|
|
4858
|
4959
|
}
|
|
@@
-4878,31
+4979,26
void QCPAxis::setSubTickCount(int count)
|
|
4878
|
4979
|
the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
|
|
4879
|
4980
|
than zero, the tick labels and axis label will increase their distance to the axis accordingly,
|
|
4880
|
4981
|
so they won't collide with the ticks.
|
|
|
4982
|
|
|
|
4983
|
\see setTickLength, setSubTickLengthIn, setSubTickLengthOut
|
|
4881
|
4984
|
*/
|
|
4882
|
4985
|
void QCPAxis::setSubTickLength(int inside, int outside)
|
|
4883
|
4986
|
{
|
|
4884
|
|
if (mSubTickLengthIn != inside)
|
|
4885
|
|
{
|
|
4886
|
|
mSubTickLengthIn = inside;
|
|
4887
|
|
}
|
|
4888
|
|
if (mSubTickLengthOut != outside)
|
|
4889
|
|
{
|
|
4890
|
|
mSubTickLengthOut = outside;
|
|
4891
|
|
mCachedMarginValid = false; // only outside tick length can change margin
|
|
4892
|
|
}
|
|
|
4987
|
setSubTickLengthIn(inside);
|
|
|
4988
|
setSubTickLengthOut(outside);
|
|
4893
|
4989
|
}
|
|
4894
|
4990
|
|
|
4895
|
4991
|
/*!
|
|
4896
|
4992
|
Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
|
|
4897
|
4993
|
the plot.
|
|
4898
|
4994
|
|
|
4899
|
|
\see setSubTickLengthOut, setTickLength
|
|
|
4995
|
\see setSubTickLengthOut, setSubTickLength, setTickLength
|
|
4900
|
4996
|
*/
|
|
4901
|
4997
|
void QCPAxis::setSubTickLengthIn(int inside)
|
|
4902
|
4998
|
{
|
|
4903
|
|
if (mSubTickLengthIn != inside)
|
|
4904
|
|
{
|
|
4905
|
|
mSubTickLengthIn = inside;
|
|
|
4999
|
if (mAxisPainter->subTickLengthIn != inside)
|
|
|
5000
|
{
|
|
|
5001
|
mAxisPainter->subTickLengthIn = inside;
|
|
4906
|
5002
|
}
|
|
4907
|
5003
|
}
|
|
4908
|
5004
|
|
|
@@
-4911,13
+5007,13
void QCPAxis::setSubTickLengthIn(int ins
|
|
4911
|
5007
|
outside the plot. If \a outside is greater than zero, the tick labels will increase their
|
|
4912
|
5008
|
distance to the axis accordingly, so they won't collide with the ticks.
|
|
4913
|
5009
|
|
|
4914
|
|
\see setSubTickLengthIn, setTickLength
|
|
|
5010
|
\see setSubTickLengthIn, setSubTickLength, setTickLength
|
|
4915
|
5011
|
*/
|
|
4916
|
5012
|
void QCPAxis::setSubTickLengthOut(int outside)
|
|
4917
|
5013
|
{
|
|
4918
|
|
if (mSubTickLengthOut != outside)
|
|
4919
|
|
{
|
|
4920
|
|
mSubTickLengthOut = outside;
|
|
|
5014
|
if (mAxisPainter->subTickLengthOut != outside)
|
|
|
5015
|
{
|
|
|
5016
|
mAxisPainter->subTickLengthOut = outside;
|
|
4921
|
5017
|
mCachedMarginValid = false; // only outside tick length can change margin
|
|
4922
|
5018
|
}
|
|
4923
|
5019
|
}
|
|
@@
-4996,9
+5092,9
void QCPAxis::setLabel(const QString &st
|
|
4996
|
5092
|
*/
|
|
4997
|
5093
|
void QCPAxis::setLabelPadding(int padding)
|
|
4998
|
5094
|
{
|
|
4999
|
|
if (mLabelPadding != padding)
|
|
5000
|
|
{
|
|
5001
|
|
mLabelPadding = padding;
|
|
|
5095
|
if (mAxisPainter->labelPadding != padding)
|
|
|
5096
|
{
|
|
|
5097
|
mAxisPainter->labelPadding = padding;
|
|
5002
|
5098
|
mCachedMarginValid = false;
|
|
5003
|
5099
|
}
|
|
5004
|
5100
|
}
|
|
@@
-5025,12
+5121,14
void QCPAxis::setPadding(int padding)
|
|
5025
|
5121
|
/*!
|
|
5026
|
5122
|
Sets the offset the axis has to its axis rect side.
|
|
5027
|
5123
|
|
|
5028
|
|
If an axis rect side has multiple axes, only the offset of the inner most axis has meaning. The offset of the other axes
|
|
5029
|
|
is controlled automatically, to place the axes at appropriate positions to prevent them from overlapping.
|
|
|
5124
|
If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
|
|
|
5125
|
only the offset of the inner most axis has meaning (even if it is set to be invisible). The
|
|
|
5126
|
offset of the other, outer axes is controlled automatically, to place them at appropriate
|
|
|
5127
|
positions.
|
|
5030
|
5128
|
*/
|
|
5031
|
5129
|
void QCPAxis::setOffset(int offset)
|
|
5032
|
5130
|
{
|
|
5033
|
|
mOffset = offset;
|
|
|
5131
|
mAxisPainter->offset = offset;
|
|
5034
|
5132
|
}
|
|
5035
|
5133
|
|
|
5036
|
5134
|
/*!
|
|
@@
-5043,7
+5141,6
void QCPAxis::setSelectedTickLabelFont(c
|
|
5043
|
5141
|
if (font != mSelectedTickLabelFont)
|
|
5044
|
5142
|
{
|
|
5045
|
5143
|
mSelectedTickLabelFont = font;
|
|
5046
|
|
mLabelCache.clear();
|
|
5047
|
5144
|
// don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
|
|
5048
|
5145
|
}
|
|
5049
|
5146
|
}
|
|
@@
-5069,7
+5166,6
void QCPAxis::setSelectedTickLabelColor(
|
|
5069
|
5166
|
if (color != mSelectedTickLabelColor)
|
|
5070
|
5167
|
{
|
|
5071
|
5168
|
mSelectedTickLabelColor = color;
|
|
5072
|
|
mLabelCache.clear();
|
|
5073
|
5169
|
}
|
|
5074
|
5170
|
}
|
|
5075
|
5171
|
|
|
@@
-5125,7
+5221,7
void QCPAxis::setSelectedSubTickPen(cons
|
|
5125
|
5221
|
*/
|
|
5126
|
5222
|
void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
|
|
5127
|
5223
|
{
|
|
5128
|
|
mLowerEnding = ending;
|
|
|
5224
|
mAxisPainter->lowerEnding = ending;
|
|
5129
|
5225
|
}
|
|
5130
|
5226
|
|
|
5131
|
5227
|
/*!
|
|
@@
-5140,7
+5236,7
void QCPAxis::setLowerEnding(const QCPLi
|
|
5140
|
5236
|
*/
|
|
5141
|
5237
|
void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
|
|
5142
|
5238
|
{
|
|
5143
|
|
mUpperEnding = ending;
|
|
|
5239
|
mAxisPainter->upperEnding = ending;
|
|
5144
|
5240
|
}
|
|
5145
|
5241
|
|
|
5146
|
5242
|
/*!
|
|
@@
-5247,15
+5343,15
void QCPAxis::rescale(bool onlyVisiblePl
|
|
5247
|
5343
|
if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
|
|
5248
|
5344
|
continue;
|
|
5249
|
5345
|
QCPRange plottableRange;
|
|
5250
|
|
bool validRange;
|
|
|
5346
|
bool currentFoundRange;
|
|
5251
|
5347
|
QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
|
|
5252
|
5348
|
if (mScaleType == stLogarithmic)
|
|
5253
|
5349
|
signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
|
|
5254
|
5350
|
if (p.at(i)->keyAxis() == this)
|
|
5255
|
|
plottableRange = p.at(i)->getKeyRange(validRange, signDomain);
|
|
5256
|
|
else
|
|
5257
|
|
plottableRange = p.at(i)->getValueRange(validRange, signDomain);
|
|
5258
|
|
if (validRange)
|
|
|
5351
|
plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
|
|
|
5352
|
else
|
|
|
5353
|
plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
|
|
|
5354
|
if (currentFoundRange)
|
|
5259
|
5355
|
{
|
|
5260
|
5356
|
if (!haveRange)
|
|
5261
|
5357
|
newRange = plottableRange;
|
|
@@
-5387,11
+5483,11
QCPAxis::SelectablePart QCPAxis::getPart
|
|
5387
|
5483
|
if (!mVisible)
|
|
5388
|
5484
|
return spNone;
|
|
5389
|
5485
|
|
|
5390
|
|
if (mAxisSelectionBox.contains(pos.toPoint()))
|
|
|
5486
|
if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
|
|
5391
|
5487
|
return spAxis;
|
|
5392
|
|
else if (mTickLabelsSelectionBox.contains(pos.toPoint()))
|
|
|
5488
|
else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
|
|
5393
|
5489
|
return spTickLabels;
|
|
5394
|
|
else if (mLabelSelectionBox.contains(pos.toPoint()))
|
|
|
5490
|
else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
|
|
5395
|
5491
|
return spAxisLabel;
|
|
5396
|
5492
|
else
|
|
5397
|
5493
|
return spNone;
|
|
@@
-5492,6
+5588,21
QCPAxis::AxisType QCPAxis::marginSideToA
|
|
5492
|
5588
|
return atLeft;
|
|
5493
|
5589
|
}
|
|
5494
|
5590
|
|
|
|
5591
|
/*!
|
|
|
5592
|
Returns the axis type that describes the opposite axis of an axis with the specified \a type.
|
|
|
5593
|
*/
|
|
|
5594
|
QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
|
|
|
5595
|
{
|
|
|
5596
|
switch (type)
|
|
|
5597
|
{
|
|
|
5598
|
case atLeft: return atRight; break;
|
|
|
5599
|
case atRight: return atLeft; break;
|
|
|
5600
|
case atBottom: return atTop; break;
|
|
|
5601
|
case atTop: return atBottom; break;
|
|
|
5602
|
default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
|
|
|
5603
|
}
|
|
|
5604
|
}
|
|
|
5605
|
|
|
5495
|
5606
|
/*! \internal
|
|
5496
|
5607
|
|
|
5497
|
5608
|
This function is called to prepare the tick vector, sub tick vector and tick label vector. If
|
|
@@
-5552,8
+5663,6
void QCPAxis::setupTickVectors()
|
|
5552
|
5663
|
}
|
|
5553
|
5664
|
|
|
5554
|
5665
|
// generate tick labels according to tick positions:
|
|
5555
|
|
mExponentialChar = mParentPlot->locale().exponential(); // will be needed when drawing the numbers generated here, in getTickLabelData()
|
|
5556
|
|
mPositiveSignChar = mParentPlot->locale().positiveSign(); // will be needed when drawing the numbers generated here, in getTickLabelData()
|
|
5557
|
5666
|
if (mAutoTickLabels)
|
|
5558
|
5667
|
{
|
|
5559
|
5668
|
int vecsize = mTickVector.size();
|
|
@@
-5726,515
+5835,6
int QCPAxis::calculateAutoSubTickCount(d
|
|
5726
|
5835
|
return result;
|
|
5727
|
5836
|
}
|
|
5728
|
5837
|
|
|
5729
|
|
/*! \internal
|
|
5730
|
|
|
|
5731
|
|
Draws the axis with the specified \a painter.
|
|
5732
|
|
|
|
5733
|
|
The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
|
|
5734
|
|
here, too.
|
|
5735
|
|
*/
|
|
5736
|
|
void QCPAxis::draw(QCPPainter *painter)
|
|
5737
|
|
{
|
|
5738
|
|
if (!mParentPlot) return;
|
|
5739
|
|
QPoint origin;
|
|
5740
|
|
if (mAxisType == atLeft)
|
|
5741
|
|
origin = mAxisRect->bottomLeft()+QPoint(-mOffset, 0);
|
|
5742
|
|
else if (mAxisType == atRight)
|
|
5743
|
|
origin = mAxisRect->bottomRight()+QPoint(+mOffset, 0);
|
|
5744
|
|
else if (mAxisType == atTop)
|
|
5745
|
|
origin = mAxisRect->topLeft()+QPoint(0, -mOffset);
|
|
5746
|
|
else if (mAxisType == atBottom)
|
|
5747
|
|
origin = mAxisRect->bottomLeft()+QPoint(0, +mOffset);
|
|
5748
|
|
|
|
5749
|
|
double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
|
|
5750
|
|
switch (mAxisType)
|
|
5751
|
|
{
|
|
5752
|
|
case atTop: yCor = -1; break;
|
|
5753
|
|
case atRight: xCor = 1; break;
|
|
5754
|
|
default: break;
|
|
5755
|
|
}
|
|
5756
|
|
|
|
5757
|
|
int margin = 0;
|
|
5758
|
|
int lowTick = mLowestVisibleTick;
|
|
5759
|
|
int highTick = mHighestVisibleTick;
|
|
5760
|
|
double t; // helper variable, result of coordinate-to-pixel transforms
|
|
5761
|
|
|
|
5762
|
|
// draw baseline:
|
|
5763
|
|
QLineF baseLine;
|
|
5764
|
|
painter->setPen(getBasePen());
|
|
5765
|
|
if (orientation() == Qt::Horizontal)
|
|
5766
|
|
baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(mAxisRect->width()+xCor, yCor));
|
|
5767
|
|
else
|
|
5768
|
|
baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -mAxisRect->height()+yCor));
|
|
5769
|
|
if (mRangeReversed)
|
|
5770
|
|
baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
|
|
5771
|
|
painter->drawLine(baseLine);
|
|
5772
|
|
|
|
5773
|
|
// draw ticks:
|
|
5774
|
|
if (mTicks)
|
|
5775
|
|
{
|
|
5776
|
|
painter->setPen(getTickPen());
|
|
5777
|
|
// direction of ticks ("inward" is right for left axis and left for right axis)
|
|
5778
|
|
int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
|
|
5779
|
|
if (orientation() == Qt::Horizontal)
|
|
5780
|
|
{
|
|
5781
|
|
for (int i=lowTick; i <= highTick; ++i)
|
|
5782
|
|
{
|
|
5783
|
|
t = coordToPixel(mTickVector.at(i)); // x
|
|
5784
|
|
painter->drawLine(QLineF(t+xCor, origin.y()-mTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mTickLengthIn*tickDir+yCor));
|
|
5785
|
|
}
|
|
5786
|
|
} else
|
|
5787
|
|
{
|
|
5788
|
|
for (int i=lowTick; i <= highTick; ++i)
|
|
5789
|
|
{
|
|
5790
|
|
t = coordToPixel(mTickVector.at(i)); // y
|
|
5791
|
|
painter->drawLine(QLineF(origin.x()-mTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mTickLengthIn*tickDir+xCor, t+yCor));
|
|
5792
|
|
}
|
|
5793
|
|
}
|
|
5794
|
|
}
|
|
5795
|
|
|
|
5796
|
|
// draw subticks:
|
|
5797
|
|
if (mTicks && mSubTickCount > 0)
|
|
5798
|
|
{
|
|
5799
|
|
painter->setPen(getSubTickPen());
|
|
5800
|
|
// direction of ticks ("inward" is right for left axis and left for right axis)
|
|
5801
|
|
int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
|
|
5802
|
|
if (orientation() == Qt::Horizontal)
|
|
5803
|
|
{
|
|
5804
|
|
for (int i=0; i<mSubTickVector.size(); ++i) // no need to check bounds because subticks are always only created inside current mRange
|
|
5805
|
|
{
|
|
5806
|
|
t = coordToPixel(mSubTickVector.at(i));
|
|
5807
|
|
painter->drawLine(QLineF(t+xCor, origin.y()-mSubTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mSubTickLengthIn*tickDir+yCor));
|
|
5808
|
|
}
|
|
5809
|
|
} else
|
|
5810
|
|
{
|
|
5811
|
|
for (int i=0; i<mSubTickVector.size(); ++i)
|
|
5812
|
|
{
|
|
5813
|
|
t = coordToPixel(mSubTickVector.at(i));
|
|
5814
|
|
painter->drawLine(QLineF(origin.x()-mSubTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mSubTickLengthIn*tickDir+xCor, t+yCor));
|
|
5815
|
|
}
|
|
5816
|
|
}
|
|
5817
|
|
}
|
|
5818
|
|
margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
|
|
5819
|
|
|
|
5820
|
|
// draw axis base endings:
|
|
5821
|
|
bool antialiasingBackup = painter->antialiasing();
|
|
5822
|
|
painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
|
|
5823
|
|
painter->setBrush(QBrush(basePen().color()));
|
|
5824
|
|
QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
|
|
5825
|
|
if (mLowerEnding.style() != QCPLineEnding::esNone)
|
|
5826
|
|
mLowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*mLowerEnding.realLength()*(mLowerEnding.inverted()?-1:1), -baseLineVector);
|
|
5827
|
|
if (mUpperEnding.style() != QCPLineEnding::esNone)
|
|
5828
|
|
mUpperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*mUpperEnding.realLength()*(mUpperEnding.inverted()?-1:1), baseLineVector);
|
|
5829
|
|
painter->setAntialiasing(antialiasingBackup);
|
|
5830
|
|
|
|
5831
|
|
// tick labels:
|
|
5832
|
|
QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
|
|
5833
|
|
if (mTickLabels)
|
|
5834
|
|
{
|
|
5835
|
|
margin += mTickLabelPadding;
|
|
5836
|
|
painter->setFont(getTickLabelFont());
|
|
5837
|
|
painter->setPen(QPen(getTickLabelColor()));
|
|
5838
|
|
for (int i=lowTick; i <= highTick; ++i)
|
|
5839
|
|
{
|
|
5840
|
|
t = coordToPixel(mTickVector.at(i));
|
|
5841
|
|
placeTickLabel(painter, t, margin, mTickVectorLabels.at(i), &tickLabelsSize);
|
|
5842
|
|
}
|
|
5843
|
|
}
|
|
5844
|
|
if (orientation() == Qt::Horizontal)
|
|
5845
|
|
margin += tickLabelsSize.height();
|
|
5846
|
|
else
|
|
5847
|
|
margin += tickLabelsSize.width();
|
|
5848
|
|
|
|
5849
|
|
// axis label:
|
|
5850
|
|
QRect labelBounds;
|
|
5851
|
|
if (!mLabel.isEmpty())
|
|
5852
|
|
{
|
|
5853
|
|
margin += mLabelPadding;
|
|
5854
|
|
painter->setFont(getLabelFont());
|
|
5855
|
|
painter->setPen(QPen(getLabelColor()));
|
|
5856
|
|
labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, mLabel);
|
|
5857
|
|
if (mAxisType == atLeft)
|
|
5858
|
|
{
|
|
5859
|
|
QTransform oldTransform = painter->transform();
|
|
5860
|
|
painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
|
|
5861
|
|
painter->rotate(-90);
|
|
5862
|
|
painter->drawText(0, 0, mAxisRect->height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
|
|
5863
|
|
painter->setTransform(oldTransform);
|
|
5864
|
|
}
|
|
5865
|
|
else if (mAxisType == atRight)
|
|
5866
|
|
{
|
|
5867
|
|
QTransform oldTransform = painter->transform();
|
|
5868
|
|
painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-mAxisRect->height());
|
|
5869
|
|
painter->rotate(90);
|
|
5870
|
|
painter->drawText(0, 0, mAxisRect->height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
|
|
5871
|
|
painter->setTransform(oldTransform);
|
|
5872
|
|
}
|
|
5873
|
|
else if (mAxisType == atTop)
|
|
5874
|
|
painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), mAxisRect->width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
|
|
5875
|
|
else if (mAxisType == atBottom)
|
|
5876
|
|
painter->drawText(origin.x(), origin.y()+margin, mAxisRect->width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
|
|
5877
|
|
}
|
|
5878
|
|
|
|
5879
|
|
// set selection boxes:
|
|
5880
|
|
int selAxisOutSize = qMax(qMax(mTickLengthOut, mSubTickLengthOut), mParentPlot->selectionTolerance());
|
|
5881
|
|
int selAxisInSize = mParentPlot->selectionTolerance();
|
|
5882
|
|
int selTickLabelSize = (orientation()==Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
|
|
5883
|
|
int selTickLabelOffset = qMax(mTickLengthOut, mSubTickLengthOut)+mTickLabelPadding;
|
|
5884
|
|
int selLabelSize = labelBounds.height();
|
|
5885
|
|
int selLabelOffset = selTickLabelOffset+selTickLabelSize+mLabelPadding;
|
|
5886
|
|
if (mAxisType == atLeft)
|
|
5887
|
|
{
|
|
5888
|
|
mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, mAxisRect->top(), origin.x()+selAxisInSize, mAxisRect->bottom());
|
|
5889
|
|
mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, mAxisRect->top(), origin.x()-selTickLabelOffset, mAxisRect->bottom());
|
|
5890
|
|
mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, mAxisRect->top(), origin.x()-selLabelOffset, mAxisRect->bottom());
|
|
5891
|
|
} else if (mAxisType == atRight)
|
|
5892
|
|
{
|
|
5893
|
|
mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, mAxisRect->top(), origin.x()+selAxisOutSize, mAxisRect->bottom());
|
|
5894
|
|
mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, mAxisRect->top(), origin.x()+selTickLabelOffset, mAxisRect->bottom());
|
|
5895
|
|
mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, mAxisRect->top(), origin.x()+selLabelOffset, mAxisRect->bottom());
|
|
5896
|
|
} else if (mAxisType == atTop)
|
|
5897
|
|
{
|
|
5898
|
|
mAxisSelectionBox.setCoords(mAxisRect->left(), origin.y()-selAxisOutSize, mAxisRect->right(), origin.y()+selAxisInSize);
|
|
5899
|
|
mTickLabelsSelectionBox.setCoords(mAxisRect->left(), origin.y()-selTickLabelOffset-selTickLabelSize, mAxisRect->right(), origin.y()-selTickLabelOffset);
|
|
5900
|
|
mLabelSelectionBox.setCoords(mAxisRect->left(), origin.y()-selLabelOffset-selLabelSize, mAxisRect->right(), origin.y()-selLabelOffset);
|
|
5901
|
|
} else if (mAxisType == atBottom)
|
|
5902
|
|
{
|
|
5903
|
|
mAxisSelectionBox.setCoords(mAxisRect->left(), origin.y()-selAxisInSize, mAxisRect->right(), origin.y()+selAxisOutSize);
|
|
5904
|
|
mTickLabelsSelectionBox.setCoords(mAxisRect->left(), origin.y()+selTickLabelOffset+selTickLabelSize, mAxisRect->right(), origin.y()+selTickLabelOffset);
|
|
5905
|
|
mLabelSelectionBox.setCoords(mAxisRect->left(), origin.y()+selLabelOffset+selLabelSize, mAxisRect->right(), origin.y()+selLabelOffset);
|
|
5906
|
|
}
|
|
5907
|
|
// draw hitboxes for debug purposes:
|
|
5908
|
|
//painter->setBrush(Qt::NoBrush);
|
|
5909
|
|
//painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
|
|
5910
|
|
}
|
|
5911
|
|
|
|
5912
|
|
/*! \internal
|
|
5913
|
|
|
|
5914
|
|
Draws a single tick label with the provided \a painter, utilizing the internal label cache to
|
|
5915
|
|
significantly speed up drawing of labels that were drawn in previous calls. The tick label is
|
|
5916
|
|
always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
|
|
5917
|
|
pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
|
|
5918
|
|
for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
|
|
5919
|
|
at which the label should be drawn.
|
|
5920
|
|
|
|
5921
|
|
In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
|
|
5922
|
|
largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
|
|
5923
|
|
drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
|
|
5924
|
|
tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
|
|
5925
|
|
holds.
|
|
5926
|
|
|
|
5927
|
|
The label is drawn with the font and pen that are currently set on the \a painter. To draw
|
|
5928
|
|
superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
|
|
5929
|
|
getTickLabelData).
|
|
5930
|
|
*/
|
|
5931
|
|
void QCPAxis::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
|
|
5932
|
|
{
|
|
5933
|
|
// warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
|
|
5934
|
|
if (!mParentPlot) return;
|
|
5935
|
|
if (text.isEmpty()) return;
|
|
5936
|
|
QSize finalSize;
|
|
5937
|
|
QPointF labelAnchor;
|
|
5938
|
|
switch (mAxisType)
|
|
5939
|
|
{
|
|
5940
|
|
case atLeft: labelAnchor = QPointF(mAxisRect->left()-distanceToAxis-mOffset, position); break;
|
|
5941
|
|
case atRight: labelAnchor = QPointF(mAxisRect->right()+distanceToAxis+mOffset, position); break;
|
|
5942
|
|
case atTop: labelAnchor = QPointF(position, mAxisRect->top()-distanceToAxis-mOffset); break;
|
|
5943
|
|
case atBottom: labelAnchor = QPointF(position, mAxisRect->bottom()+distanceToAxis+mOffset); break;
|
|
5944
|
|
}
|
|
5945
|
|
if (parentPlot()->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
|
|
5946
|
|
{
|
|
5947
|
|
if (!mLabelCache.contains(text)) // no cached label exists, create it
|
|
5948
|
|
{
|
|
5949
|
|
CachedLabel *newCachedLabel = new CachedLabel;
|
|
5950
|
|
TickLabelData labelData = getTickLabelData(painter->font(), text);
|
|
5951
|
|
QPointF drawOffset = getTickLabelDrawOffset(labelData);
|
|
5952
|
|
newCachedLabel->offset = drawOffset+labelData.rotatedTotalBounds.topLeft();
|
|
5953
|
|
newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
|
|
5954
|
|
newCachedLabel->pixmap.fill(Qt::transparent);
|
|
5955
|
|
QCPPainter cachePainter(&newCachedLabel->pixmap);
|
|
5956
|
|
cachePainter.setPen(painter->pen());
|
|
5957
|
|
drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
|
|
5958
|
|
mLabelCache.insert(text, newCachedLabel, 1);
|
|
5959
|
|
}
|
|
5960
|
|
// draw cached label:
|
|
5961
|
|
const CachedLabel *cachedLabel = mLabelCache.object(text);
|
|
5962
|
|
// if label would be partly clipped by widget border on sides, don't draw it:
|
|
5963
|
|
if (orientation() == Qt::Horizontal)
|
|
5964
|
|
{
|
|
5965
|
|
if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > mParentPlot->viewport().right() ||
|
|
5966
|
|
labelAnchor.x()+cachedLabel->offset.x() < mParentPlot->viewport().left())
|
|
5967
|
|
return;
|
|
5968
|
|
} else
|
|
5969
|
|
{
|
|
5970
|
|
if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > mParentPlot->viewport().bottom() ||
|
|
5971
|
|
labelAnchor.y()+cachedLabel->offset.y() < mParentPlot->viewport().top())
|
|
5972
|
|
return;
|
|
5973
|
|
}
|
|
5974
|
|
painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
|
|
5975
|
|
finalSize = cachedLabel->pixmap.size();
|
|
5976
|
|
} else // label caching disabled, draw text directly on surface:
|
|
5977
|
|
{
|
|
5978
|
|
TickLabelData labelData = getTickLabelData(painter->font(), text);
|
|
5979
|
|
QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
|
|
5980
|
|
// if label would be partly clipped by widget border on sides, don't draw it:
|
|
5981
|
|
if (orientation() == Qt::Horizontal)
|
|
5982
|
|
{
|
|
5983
|
|
if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > mParentPlot->viewport().right() ||
|
|
5984
|
|
finalPosition.x()+labelData.rotatedTotalBounds.left() < mParentPlot->viewport().left())
|
|
5985
|
|
return;
|
|
5986
|
|
} else
|
|
5987
|
|
{
|
|
5988
|
|
if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > mParentPlot->viewport().bottom() ||
|
|
5989
|
|
finalPosition.y()+labelData.rotatedTotalBounds.top() < mParentPlot->viewport().top())
|
|
5990
|
|
return;
|
|
5991
|
|
}
|
|
5992
|
|
drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
|
|
5993
|
|
finalSize = labelData.rotatedTotalBounds.size();
|
|
5994
|
|
}
|
|
5995
|
|
|
|
5996
|
|
// expand passed tickLabelsSize if current tick label is larger:
|
|
5997
|
|
if (finalSize.width() > tickLabelsSize->width())
|
|
5998
|
|
tickLabelsSize->setWidth(finalSize.width());
|
|
5999
|
|
if (finalSize.height() > tickLabelsSize->height())
|
|
6000
|
|
tickLabelsSize->setHeight(finalSize.height());
|
|
6001
|
|
}
|
|
6002
|
|
|
|
6003
|
|
/*! \internal
|
|
6004
|
|
|
|
6005
|
|
This is a \ref placeTickLabel helper function.
|
|
6006
|
|
|
|
6007
|
|
Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
|
|
6008
|
|
y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
|
|
6009
|
|
directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
|
|
6010
|
|
QCP::phCacheLabels plotting hint is not set.
|
|
6011
|
|
*/
|
|
6012
|
|
void QCPAxis::drawTickLabel(QCPPainter *painter, double x, double y, const QCPAxis::TickLabelData &labelData) const
|
|
6013
|
|
{
|
|
6014
|
|
// backup painter settings that we're about to change:
|
|
6015
|
|
QTransform oldTransform = painter->transform();
|
|
6016
|
|
QFont oldFont = painter->font();
|
|
6017
|
|
|
|
6018
|
|
// transform painter to position/rotation:
|
|
6019
|
|
painter->translate(x, y);
|
|
6020
|
|
if (!qFuzzyIsNull(mTickLabelRotation))
|
|
6021
|
|
painter->rotate(mTickLabelRotation);
|
|
6022
|
|
|
|
6023
|
|
// draw text:
|
|
6024
|
|
if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
|
|
6025
|
|
{
|
|
6026
|
|
painter->setFont(labelData.baseFont);
|
|
6027
|
|
painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
|
|
6028
|
|
painter->setFont(labelData.expFont);
|
|
6029
|
|
painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
|
|
6030
|
|
} else
|
|
6031
|
|
{
|
|
6032
|
|
painter->setFont(labelData.baseFont);
|
|
6033
|
|
painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
|
|
6034
|
|
}
|
|
6035
|
|
|
|
6036
|
|
// reset painter settings to what it was before:
|
|
6037
|
|
painter->setTransform(oldTransform);
|
|
6038
|
|
painter->setFont(oldFont);
|
|
6039
|
|
}
|
|
6040
|
|
|
|
6041
|
|
/*! \internal
|
|
6042
|
|
|
|
6043
|
|
This is a \ref placeTickLabel helper function.
|
|
6044
|
|
|
|
6045
|
|
Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
|
|
6046
|
|
processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
|
|
6047
|
|
exponent if necessary (see \ref setNumberFormat) and calculates appropriate bounding boxes.
|
|
6048
|
|
*/
|
|
6049
|
|
QCPAxis::TickLabelData QCPAxis::getTickLabelData(const QFont &font, const QString &text) const
|
|
6050
|
|
{
|
|
6051
|
|
TickLabelData result;
|
|
6052
|
|
|
|
6053
|
|
// determine whether beautiful decimal powers should be used
|
|
6054
|
|
bool useBeautifulPowers = false;
|
|
6055
|
|
int ePos = -1;
|
|
6056
|
|
if (mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber)
|
|
6057
|
|
{
|
|
6058
|
|
ePos = text.indexOf('e');
|
|
6059
|
|
if (ePos > -1)
|
|
6060
|
|
useBeautifulPowers = true;
|
|
6061
|
|
}
|
|
6062
|
|
|
|
6063
|
|
// calculate text bounding rects and do string preparation for beautiful decimal powers:
|
|
6064
|
|
result.baseFont = font;
|
|
6065
|
|
result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
|
|
6066
|
|
if (useBeautifulPowers)
|
|
6067
|
|
{
|
|
6068
|
|
// split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
|
|
6069
|
|
result.basePart = text.left(ePos);
|
|
6070
|
|
// in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
|
|
6071
|
|
if (mScaleType == stLogarithmic && result.basePart == "1")
|
|
6072
|
|
result.basePart = "10";
|
|
6073
|
|
else
|
|
6074
|
|
result.basePart += (mNumberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
|
|
6075
|
|
result.expPart = text.mid(ePos+1);
|
|
6076
|
|
// clip "+" and leading zeros off expPart:
|
|
6077
|
|
while (result.expPart.at(1) == '0' && result.expPart.length() > 2) // length > 2 so we leave one zero when numberFormatChar is 'e'
|
|
6078
|
|
result.expPart.remove(1, 1);
|
|
6079
|
|
if (result.expPart.at(0) == mPositiveSignChar)
|
|
6080
|
|
result.expPart.remove(0, 1);
|
|
6081
|
|
// prepare smaller font for exponent:
|
|
6082
|
|
result.expFont = font;
|
|
6083
|
|
result.expFont.setPointSize(result.expFont.pointSize()*0.75);
|
|
6084
|
|
// calculate bounding rects of base part, exponent part and total one:
|
|
6085
|
|
result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
|
|
6086
|
|
result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
|
|
6087
|
|
result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
|
|
6088
|
|
} else // useBeautifulPowers == false
|
|
6089
|
|
{
|
|
6090
|
|
result.basePart = text;
|
|
6091
|
|
result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
|
|
6092
|
|
}
|
|
6093
|
|
result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
|
|
6094
|
|
|
|
6095
|
|
// calculate possibly different bounding rect after rotation:
|
|
6096
|
|
result.rotatedTotalBounds = result.totalBounds;
|
|
6097
|
|
if (!qFuzzyIsNull(mTickLabelRotation))
|
|
6098
|
|
{
|
|
6099
|
|
QTransform transform;
|
|
6100
|
|
transform.rotate(mTickLabelRotation);
|
|
6101
|
|
result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
|
|
6102
|
|
}
|
|
6103
|
|
|
|
6104
|
|
return result;
|
|
6105
|
|
}
|
|
6106
|
|
|
|
6107
|
|
/*! \internal
|
|
6108
|
|
|
|
6109
|
|
This is a \ref placeTickLabel helper function.
|
|
6110
|
|
|
|
6111
|
|
Calculates the offset at which the top left corner of the specified tick label shall be drawn.
|
|
6112
|
|
The offset is relative to a point right next to the tick the label belongs to.
|
|
6113
|
|
|
|
6114
|
|
This function is thus responsible for e.g. centering tick labels under ticks and positioning them
|
|
6115
|
|
appropriately when they are rotated.
|
|
6116
|
|
*/
|
|
6117
|
|
QPointF QCPAxis::getTickLabelDrawOffset(const QCPAxis::TickLabelData &labelData) const
|
|
6118
|
|
{
|
|
6119
|
|
/*
|
|
6120
|
|
calculate label offset from base point at tick (non-trivial, for best visual appearance): short
|
|
6121
|
|
explanation for bottom axis: The anchor, i.e. the point in the label that is placed
|
|
6122
|
|
horizontally under the corresponding tick is always on the label side that is closer to the
|
|
6123
|
|
axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
|
|
6124
|
|
is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
|
|
6125
|
|
will be centered under the tick (i.e. displaced horizontally by half its height). At the same
|
|
6126
|
|
time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
|
|
6127
|
|
labels.
|
|
6128
|
|
*/
|
|
6129
|
|
bool doRotation = !qFuzzyIsNull(mTickLabelRotation);
|
|
6130
|
|
bool flip = qFuzzyCompare(qAbs(mTickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
|
|
6131
|
|
double radians = mTickLabelRotation/180.0*M_PI;
|
|
6132
|
|
int x=0, y=0;
|
|
6133
|
|
if (mAxisType == atLeft)
|
|
6134
|
|
{
|
|
6135
|
|
if (doRotation)
|
|
6136
|
|
{
|
|
6137
|
|
if (mTickLabelRotation > 0)
|
|
6138
|
|
{
|
|
6139
|
|
x = -qCos(radians)*labelData.totalBounds.width();
|
|
6140
|
|
y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
|
|
6141
|
|
} else
|
|
6142
|
|
{
|
|
6143
|
|
x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
|
|
6144
|
|
y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
|
|
6145
|
|
}
|
|
6146
|
|
} else
|
|
6147
|
|
{
|
|
6148
|
|
x = -labelData.totalBounds.width();
|
|
6149
|
|
y = -labelData.totalBounds.height()/2.0;
|
|
6150
|
|
}
|
|
6151
|
|
} else if (mAxisType == atRight)
|
|
6152
|
|
{
|
|
6153
|
|
if (doRotation)
|
|
6154
|
|
{
|
|
6155
|
|
if (mTickLabelRotation > 0)
|
|
6156
|
|
{
|
|
6157
|
|
x = +qSin(radians)*labelData.totalBounds.height();
|
|
6158
|
|
y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
|
|
6159
|
|
} else
|
|
6160
|
|
{
|
|
6161
|
|
x = 0;
|
|
6162
|
|
y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
|
|
6163
|
|
}
|
|
6164
|
|
} else
|
|
6165
|
|
{
|
|
6166
|
|
x = 0;
|
|
6167
|
|
y = -labelData.totalBounds.height()/2.0;
|
|
6168
|
|
}
|
|
6169
|
|
} else if (mAxisType == atTop)
|
|
6170
|
|
{
|
|
6171
|
|
if (doRotation)
|
|
6172
|
|
{
|
|
6173
|
|
if (mTickLabelRotation > 0)
|
|
6174
|
|
{
|
|
6175
|
|
x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
|
|
6176
|
|
y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
|
|
6177
|
|
} else
|
|
6178
|
|
{
|
|
6179
|
|
x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
|
|
6180
|
|
y = -qCos(-radians)*labelData.totalBounds.height();
|
|
6181
|
|
}
|
|
6182
|
|
} else
|
|
6183
|
|
{
|
|
6184
|
|
x = -labelData.totalBounds.width()/2.0;
|
|
6185
|
|
y = -labelData.totalBounds.height();
|
|
6186
|
|
}
|
|
6187
|
|
} else if (mAxisType == atBottom)
|
|
6188
|
|
{
|
|
6189
|
|
if (doRotation)
|
|
6190
|
|
{
|
|
6191
|
|
if (mTickLabelRotation > 0)
|
|
6192
|
|
{
|
|
6193
|
|
x = +qSin(radians)*labelData.totalBounds.height()/2.0;
|
|
6194
|
|
y = 0;
|
|
6195
|
|
} else
|
|
6196
|
|
{
|
|
6197
|
|
x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
|
|
6198
|
|
y = +qSin(-radians)*labelData.totalBounds.width();
|
|
6199
|
|
}
|
|
6200
|
|
} else
|
|
6201
|
|
{
|
|
6202
|
|
x = -labelData.totalBounds.width()/2.0;
|
|
6203
|
|
y = 0;
|
|
6204
|
|
}
|
|
6205
|
|
}
|
|
6206
|
|
|
|
6207
|
|
return QPointF(x, y);
|
|
6208
|
|
}
|
|
6209
|
|
|
|
6210
|
|
/*! \internal
|
|
6211
|
|
|
|
6212
|
|
Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
|
|
6213
|
|
to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
|
|
6214
|
|
margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
|
|
6215
|
|
smaller width/height.
|
|
6216
|
|
*/
|
|
6217
|
|
void QCPAxis::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
|
|
6218
|
|
{
|
|
6219
|
|
// note: this function must return the same tick label sizes as the placeTickLabel function.
|
|
6220
|
|
QSize finalSize;
|
|
6221
|
|
if (parentPlot()->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
|
|
6222
|
|
{
|
|
6223
|
|
const CachedLabel *cachedLabel = mLabelCache.object(text);
|
|
6224
|
|
finalSize = cachedLabel->pixmap.size();
|
|
6225
|
|
} else // label caching disabled or no label with this text cached:
|
|
6226
|
|
{
|
|
6227
|
|
TickLabelData labelData = getTickLabelData(font, text);
|
|
6228
|
|
finalSize = labelData.rotatedTotalBounds.size();
|
|
6229
|
|
}
|
|
6230
|
|
|
|
6231
|
|
// expand passed tickLabelsSize if current tick label is larger:
|
|
6232
|
|
if (finalSize.width() > tickLabelsSize->width())
|
|
6233
|
|
tickLabelsSize->setWidth(finalSize.width());
|
|
6234
|
|
if (finalSize.height() > tickLabelsSize->height())
|
|
6235
|
|
tickLabelsSize->setHeight(finalSize.height());
|
|
6236
|
|
}
|
|
6237
|
|
|
|
6238
|
5838
|
/* inherits documentation from base class */
|
|
6239
|
5839
|
void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
6240
|
5840
|
{
|
|
@@
-6278,6
+5878,60
void QCPAxis::applyDefaultAntialiasingHi
|
|
6278
|
5878
|
|
|
6279
|
5879
|
/*! \internal
|
|
6280
|
5880
|
|
|
|
5881
|
Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
|
|
|
5882
|
|
|
|
5883
|
*/
|
|
|
5884
|
void QCPAxis::draw(QCPPainter *painter)
|
|
|
5885
|
{
|
|
|
5886
|
const int lowTick = mLowestVisibleTick;
|
|
|
5887
|
const int highTick = mHighestVisibleTick;
|
|
|
5888
|
QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
|
|
|
5889
|
QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
|
|
|
5890
|
QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
|
|
|
5891
|
tickPositions.reserve(highTick-lowTick+1);
|
|
|
5892
|
tickLabels.reserve(highTick-lowTick+1);
|
|
|
5893
|
subTickPositions.reserve(mSubTickVector.size());
|
|
|
5894
|
|
|
|
5895
|
if (mTicks)
|
|
|
5896
|
{
|
|
|
5897
|
for (int i=lowTick; i<=highTick; ++i)
|
|
|
5898
|
{
|
|
|
5899
|
tickPositions.append(coordToPixel(mTickVector.at(i)));
|
|
|
5900
|
if (mTickLabels)
|
|
|
5901
|
tickLabels.append(mTickVectorLabels.at(i));
|
|
|
5902
|
}
|
|
|
5903
|
|
|
|
5904
|
if (mSubTickCount > 0)
|
|
|
5905
|
{
|
|
|
5906
|
const int subTickCount = mSubTickVector.size();
|
|
|
5907
|
for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
|
|
|
5908
|
subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
|
|
|
5909
|
}
|
|
|
5910
|
}
|
|
|
5911
|
// transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
|
|
|
5912
|
// Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
|
|
|
5913
|
mAxisPainter->type = mAxisType;
|
|
|
5914
|
mAxisPainter->basePen = getBasePen();
|
|
|
5915
|
mAxisPainter->labelFont = getLabelFont();
|
|
|
5916
|
mAxisPainter->labelColor = getLabelColor();
|
|
|
5917
|
mAxisPainter->label = mLabel;
|
|
|
5918
|
mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers;
|
|
|
5919
|
mAxisPainter->tickPen = getTickPen();
|
|
|
5920
|
mAxisPainter->subTickPen = getSubTickPen();
|
|
|
5921
|
mAxisPainter->tickLabelFont = getTickLabelFont();
|
|
|
5922
|
mAxisPainter->tickLabelColor = getTickLabelColor();
|
|
|
5923
|
mAxisPainter->alignmentRect = mAxisRect->rect();
|
|
|
5924
|
mAxisPainter->viewportRect = mParentPlot->viewport();
|
|
|
5925
|
mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
|
|
|
5926
|
mAxisPainter->reversedEndings = mRangeReversed;
|
|
|
5927
|
mAxisPainter->tickPositions = tickPositions;
|
|
|
5928
|
mAxisPainter->tickLabels = tickLabels;
|
|
|
5929
|
mAxisPainter->subTickPositions = subTickPositions;
|
|
|
5930
|
mAxisPainter->draw(painter);
|
|
|
5931
|
}
|
|
|
5932
|
|
|
|
5933
|
/*! \internal
|
|
|
5934
|
|
|
6281
|
5935
|
Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
|
|
6282
|
5936
|
the current range. The return values are indices of the tick vector, not the positions of the
|
|
6283
|
5937
|
ticks themselves.
|
|
@@
-6435,37
+6089,41
QColor QCPAxis::getLabelColor() const
|
|
6435
|
6089
|
*/
|
|
6436
|
6090
|
int QCPAxis::calculateMargin()
|
|
6437
|
6091
|
{
|
|
|
6092
|
if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
|
|
|
6093
|
return 0;
|
|
|
6094
|
|
|
6438
|
6095
|
if (mCachedMarginValid)
|
|
6439
|
6096
|
return mCachedMargin;
|
|
6440
|
6097
|
|
|
6441
|
6098
|
// run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
|
|
6442
|
6099
|
int margin = 0;
|
|
6443
|
6100
|
|
|
6444
|
|
if (mVisible)
|
|
6445
|
|
{
|
|
6446
|
6101
|
int lowTick, highTick;
|
|
6447
|
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
|
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
|
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
|
|
6457
|
|
margin += orientation() == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
|
|
6458
|
|
margin += mTickLabelPadding;
|
|
6459
|
|
}
|
|
6460
|
|
// calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
|
|
6461
|
|
if (!mLabel.isEmpty())
|
|
6462
|
|
{
|
|
6463
|
|
QFontMetrics fontMetrics(mLabelFont); // don't use getLabelFont() because we don't want margin to possibly change on selection
|
|
6464
|
|
QRect bounds;
|
|
6465
|
|
bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, mLabel);
|
|
6466
|
|
margin += bounds.height() + mLabelPadding;
|
|
6467
|
|
}
|
|
6468
|
|
}
|
|
|
6110
|
{
|
|
|
6111
|
tickPositions.append(coordToPixel(mTickVector.at(i)));
|
|
|
6112
|
if (mTickLabels)
|
|
|
6113
|
tickLabels.append(mTickVectorLabels.at(i));
|
|
|
6114
|
}
|
|
|
6115
|
}
|
|
|
6116
|
// transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
|
|
|
6117
|
// Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
|
|
|
6118
|
mAxisPainter->type = mAxisType;
|
|
|
6119
|
mAxisPainter->labelFont = getLabelFont();
|
|
|
6120
|
mAxisPainter->label = mLabel;
|
|
|
6121
|
mAxisPainter->tickLabelFont = mTickLabelFont;
|
|
|
6122
|
mAxisPainter->alignmentRect = mAxisRect->rect();
|
|
|
6123
|
mAxisPainter->viewportRect = mParentPlot->viewport();
|
|
|
6124
|
mAxisPainter->tickPositions = tickPositions;
|
|
|
6125
|
mAxisPainter->tickLabels = tickLabels;
|
|
|
6126
|
margin += mAxisPainter->size();
|
|
6469
|
6127
|
margin += mPadding;
|
|
6470
|
6128
|
|
|
6471
|
6129
|
mCachedMargin = margin;
|
|
@@
-6481,6
+6139,619
QCP::Interaction QCPAxis::selectionCateg
|
|
6481
|
6139
|
|
|
6482
|
6140
|
|
|
6483
|
6141
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
6142
|
//////////////////// QCPAxisPainterPrivate
|
|
|
6143
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
6144
|
|
|
|
6145
|
/*! \class QCPAxisPainterPrivate
|
|
|
6146
|
|
|
|
6147
|
\internal
|
|
|
6148
|
\brief (Private)
|
|
|
6149
|
|
|
|
6150
|
This is a private class and not part of the public QCustomPlot interface.
|
|
|
6151
|
|
|
|
6152
|
It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
|
|
|
6153
|
axis label. It also buffers the labels to reduce replot times. The parameters are configured by
|
|
|
6154
|
directly accessing the public member variables.
|
|
|
6155
|
*/
|
|
|
6156
|
|
|
|
6157
|
/*!
|
|
|
6158
|
Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
|
|
|
6159
|
redraw, to utilize the caching mechanisms.
|
|
|
6160
|
*/
|
|
|
6161
|
QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
|
|
|
6162
|
type(QCPAxis::atLeft),
|
|
|
6163
|
basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
|
|
|
6164
|
lowerEnding(QCPLineEnding::esNone),
|
|
|
6165
|
upperEnding(QCPLineEnding::esNone),
|
|
|
6166
|
labelPadding(0),
|
|
|
6167
|
tickLabelPadding(0),
|
|
|
6168
|
tickLabelRotation(0),
|
|
|
6169
|
substituteExponent(true),
|
|
|
6170
|
numberMultiplyCross(false),
|
|
|
6171
|
tickLengthIn(5),
|
|
|
6172
|
tickLengthOut(0),
|
|
|
6173
|
subTickLengthIn(2),
|
|
|
6174
|
subTickLengthOut(0),
|
|
|
6175
|
tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
|
|
|
6176
|
subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
|
|
|
6177
|
offset(0),
|
|
|
6178
|
abbreviateDecimalPowers(false),
|
|
|
6179
|
reversedEndings(false),
|
|
|
6180
|
mParentPlot(parentPlot),
|
|
|
6181
|
mLabelCache(16) // cache at most 16 (tick) labels
|
|
|
6182
|
{
|
|
|
6183
|
}
|
|
|
6184
|
|
|
|
6185
|
QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
|
|
|
6186
|
{
|
|
|
6187
|
}
|
|
|
6188
|
|
|
|
6189
|
/*! \internal
|
|
|
6190
|
|
|
|
6191
|
Draws the axis with the specified \a painter.
|
|
|
6192
|
|
|
|
6193
|
The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
|
|
|
6194
|
here, too.
|
|
|
6195
|
*/
|
|
|
6196
|
void QCPAxisPainterPrivate::draw(QCPPainter *painter)
|
|
|
6197
|
{
|
|
|
6198
|
QByteArray newHash = generateLabelParameterHash();
|
|
|
6199
|
if (newHash != mLabelParameterHash)
|
|
|
6200
|
{
|
|
|
6201
|
mLabelCache.clear();
|
|
|
6202
|
mLabelParameterHash = newHash;
|
|
|
6203
|
}
|
|
|
6204
|
|
|
|
6205
|
QPoint origin;
|
|
|
6206
|
switch (type)
|
|
|
6207
|
{
|
|
|
6208
|
case QCPAxis::atLeft: origin = alignmentRect.bottomLeft() +QPoint(-offset, 0); break;
|
|
|
6209
|
case QCPAxis::atRight: origin = alignmentRect.bottomRight()+QPoint(+offset, 0); break;
|
|
|
6210
|
case QCPAxis::atTop: origin = alignmentRect.topLeft() +QPoint(0, -offset); break;
|
|
|
6211
|
case QCPAxis::atBottom: origin = alignmentRect.bottomLeft() +QPoint(0, +offset); break;
|
|
|
6212
|
}
|
|
|
6213
|
|
|
|
6214
|
double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
|
|
|
6215
|
switch (type)
|
|
|
6216
|
{
|
|
|
6217
|
case QCPAxis::atTop: yCor = -1; break;
|
|
|
6218
|
case QCPAxis::atRight: xCor = 1; break;
|
|
|
6219
|
default: break;
|
|
|
6220
|
}
|
|
|
6221
|
|
|
|
6222
|
int margin = 0;
|
|
|
6223
|
// draw baseline:
|
|
|
6224
|
QLineF baseLine;
|
|
|
6225
|
painter->setPen(basePen);
|
|
|
6226
|
if (QCPAxis::orientation(type) == Qt::Horizontal)
|
|
|
6227
|
baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(alignmentRect.width()+xCor, yCor));
|
|
|
6228
|
else
|
|
|
6229
|
baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -alignmentRect.height()+yCor));
|
|
|
6230
|
if (reversedEndings)
|
|
|
6231
|
baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
|
|
|
6232
|
painter->drawLine(baseLine);
|
|
|
6233
|
|
|
|
6234
|
// draw ticks:
|
|
|
6235
|
if (!tickPositions.isEmpty())
|
|
|
6236
|
{
|
|
|
6237
|
painter->setPen(tickPen);
|
|
|
6238
|
int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
|
|
|
6239
|
if (QCPAxis::orientation(type) == Qt::Horizontal)
|
|
|
6240
|
{
|
|
|
6241
|
for (int i=0; i<tickPositions.size(); ++i)
|
|
|
6242
|
painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
|
|
|
6243
|
} else
|
|
|
6244
|
{
|
|
|
6245
|
for (int i=0; i<tickPositions.size(); ++i)
|
|
|
6246
|
painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
|
|
|
6247
|
}
|
|
|
6248
|
}
|
|
|
6249
|
|
|
|
6250
|
// draw subticks:
|
|
|
6251
|
if (!subTickPositions.isEmpty())
|
|
|
6252
|
{
|
|
|
6253
|
painter->setPen(subTickPen);
|
|
|
6254
|
// direction of ticks ("inward" is right for left axis and left for right axis)
|
|
|
6255
|
int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
|
|
|
6256
|
if (QCPAxis::orientation(type) == Qt::Horizontal)
|
|
|
6257
|
{
|
|
|
6258
|
for (int i=0; i<subTickPositions.size(); ++i)
|
|
|
6259
|
painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
|
|
|
6260
|
} else
|
|
|
6261
|
{
|
|
|
6262
|
for (int i=0; i<subTickPositions.size(); ++i)
|
|
|
6263
|
painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
|
|
|
6264
|
}
|
|
|
6265
|
}
|
|
|
6266
|
margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
|
|
|
6267
|
|
|
|
6268
|
// draw axis base endings:
|
|
|
6269
|
bool antialiasingBackup = painter->antialiasing();
|
|
|
6270
|
painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
|
|
|
6271
|
painter->setBrush(QBrush(basePen.color()));
|
|
|
6272
|
QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
|
|
|
6273
|
if (lowerEnding.style() != QCPLineEnding::esNone)
|
|
|
6274
|
lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
|
|
|
6275
|
if (upperEnding.style() != QCPLineEnding::esNone)
|
|
|
6276
|
upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
|
|
|
6277
|
painter->setAntialiasing(antialiasingBackup);
|
|
|
6278
|
|
|
|
6279
|
// tick labels:
|
|
|
6280
|
QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
|
|
|
6281
|
if (!tickLabels.isEmpty())
|
|
|
6282
|
{
|
|
|
6283
|
margin += tickLabelPadding;
|
|
|
6284
|
painter->setFont(tickLabelFont);
|
|
|
6285
|
painter->setPen(QPen(tickLabelColor));
|
|
|
6286
|
const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
|
|
|
6287
|
for (int i=0; i<maxLabelIndex; ++i)
|
|
|
6288
|
placeTickLabel(painter, tickPositions.at(i), margin, tickLabels.at(i), &tickLabelsSize);
|
|
|
6289
|
if (QCPAxis::orientation(type) == Qt::Horizontal)
|
|
|
6290
|
margin += tickLabelsSize.height();
|
|
|
6291
|
else
|
|
|
6292
|
margin += tickLabelsSize.width();
|
|
|
6293
|
}
|
|
|
6294
|
|
|
|
6295
|
// axis label:
|
|
|
6296
|
QRect labelBounds;
|
|
|
6297
|
if (!label.isEmpty())
|
|
|
6298
|
{
|
|
|
6299
|
margin += labelPadding;
|
|
|
6300
|
painter->setFont(labelFont);
|
|
|
6301
|
painter->setPen(QPen(labelColor));
|
|
|
6302
|
labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
|
|
|
6303
|
if (type == QCPAxis::atLeft)
|
|
|
6304
|
{
|
|
|
6305
|
QTransform oldTransform = painter->transform();
|
|
|
6306
|
painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
|
|
|
6307
|
painter->rotate(-90);
|
|
|
6308
|
painter->drawText(0, 0, alignmentRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
|
|
|
6309
|
painter->setTransform(oldTransform);
|
|
|
6310
|
}
|
|
|
6311
|
else if (type == QCPAxis::atRight)
|
|
|
6312
|
{
|
|
|
6313
|
QTransform oldTransform = painter->transform();
|
|
|
6314
|
painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-alignmentRect.height());
|
|
|
6315
|
painter->rotate(90);
|
|
|
6316
|
painter->drawText(0, 0, alignmentRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
|
|
|
6317
|
painter->setTransform(oldTransform);
|
|
|
6318
|
}
|
|
|
6319
|
else if (type == QCPAxis::atTop)
|
|
|
6320
|
painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), alignmentRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
|
|
|
6321
|
else if (type == QCPAxis::atBottom)
|
|
|
6322
|
painter->drawText(origin.x(), origin.y()+margin, alignmentRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
|
|
|
6323
|
}
|
|
|
6324
|
|
|
|
6325
|
// set selection boxes:
|
|
|
6326
|
int selectionTolerance = 0;
|
|
|
6327
|
if (mParentPlot)
|
|
|
6328
|
selectionTolerance = mParentPlot->selectionTolerance();
|
|
|
6329
|
else
|
|
|
6330
|
qDebug() << Q_FUNC_INFO << "mParentPlot is null";
|
|
|
6331
|
int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
|
|
|
6332
|
int selAxisInSize = selectionTolerance;
|
|
|
6333
|
int selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
|
|
|
6334
|
int selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
|
|
|
6335
|
int selLabelSize = labelBounds.height();
|
|
|
6336
|
int selLabelOffset = selTickLabelOffset+selTickLabelSize+labelPadding;
|
|
|
6337
|
if (type == QCPAxis::atLeft)
|
|
|
6338
|
{
|
|
|
6339
|
mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, alignmentRect.top(), origin.x()+selAxisInSize, alignmentRect.bottom());
|
|
|
6340
|
mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, alignmentRect.top(), origin.x()-selTickLabelOffset, alignmentRect.bottom());
|
|
|
6341
|
mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, alignmentRect.top(), origin.x()-selLabelOffset, alignmentRect.bottom());
|
|
|
6342
|
} else if (type == QCPAxis::atRight)
|
|
|
6343
|
{
|
|
|
6344
|
mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, alignmentRect.top(), origin.x()+selAxisOutSize, alignmentRect.bottom());
|
|
|
6345
|
mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, alignmentRect.top(), origin.x()+selTickLabelOffset, alignmentRect.bottom());
|
|
|
6346
|
mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, alignmentRect.top(), origin.x()+selLabelOffset, alignmentRect.bottom());
|
|
|
6347
|
} else if (type == QCPAxis::atTop)
|
|
|
6348
|
{
|
|
|
6349
|
mAxisSelectionBox.setCoords(alignmentRect.left(), origin.y()-selAxisOutSize, alignmentRect.right(), origin.y()+selAxisInSize);
|
|
|
6350
|
mTickLabelsSelectionBox.setCoords(alignmentRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, alignmentRect.right(), origin.y()-selTickLabelOffset);
|
|
|
6351
|
mLabelSelectionBox.setCoords(alignmentRect.left(), origin.y()-selLabelOffset-selLabelSize, alignmentRect.right(), origin.y()-selLabelOffset);
|
|
|
6352
|
} else if (type == QCPAxis::atBottom)
|
|
|
6353
|
{
|
|
|
6354
|
mAxisSelectionBox.setCoords(alignmentRect.left(), origin.y()-selAxisInSize, alignmentRect.right(), origin.y()+selAxisOutSize);
|
|
|
6355
|
mTickLabelsSelectionBox.setCoords(alignmentRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, alignmentRect.right(), origin.y()+selTickLabelOffset);
|
|
|
6356
|
mLabelSelectionBox.setCoords(alignmentRect.left(), origin.y()+selLabelOffset+selLabelSize, alignmentRect.right(), origin.y()+selLabelOffset);
|
|
|
6357
|
}
|
|
|
6358
|
// draw hitboxes for debug purposes:
|
|
|
6359
|
//painter->setBrush(Qt::NoBrush);
|
|
|
6360
|
//painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
|
|
|
6361
|
}
|
|
|
6362
|
|
|
|
6363
|
/*! \internal
|
|
|
6364
|
|
|
|
6365
|
Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
|
|
|
6366
|
direction) needed to fit the axis.
|
|
|
6367
|
*/
|
|
|
6368
|
int QCPAxisPainterPrivate::size() const
|
|
|
6369
|
{
|
|
|
6370
|
int result = 0;
|
|
|
6371
|
|
|
|
6372
|
// get length of tick marks pointing outwards:
|
|
|
6373
|
if (!tickPositions.isEmpty())
|
|
|
6374
|
result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
|
|
|
6375
|
|
|
|
6376
|
// calculate size of tick labels:
|
|
|
6377
|
QSize tickLabelsSize(0, 0);
|
|
|
6378
|
if (!tickLabels.isEmpty())
|
|
|
6379
|
{
|
|
|
6380
|
for (int i=0; i<tickLabels.size(); ++i)
|
|
|
6381
|
getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
|
|
|
6382
|
result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
|
|
|
6383
|
result += tickLabelPadding;
|
|
|
6384
|
}
|
|
|
6385
|
|
|
|
6386
|
// calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
|
|
|
6387
|
if (!label.isEmpty())
|
|
|
6388
|
{
|
|
|
6389
|
QFontMetrics fontMetrics(labelFont);
|
|
|
6390
|
QRect bounds;
|
|
|
6391
|
bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
|
|
|
6392
|
result += bounds.height() + labelPadding;
|
|
|
6393
|
}
|
|
|
6394
|
|
|
|
6395
|
return result;
|
|
|
6396
|
}
|
|
|
6397
|
|
|
|
6398
|
/*! \internal
|
|
|
6399
|
|
|
|
6400
|
Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
|
|
|
6401
|
method is called automatically in \ref draw, if any parameters have changed that invalidate the
|
|
|
6402
|
cached labels, such as font, color, etc.
|
|
|
6403
|
*/
|
|
|
6404
|
void QCPAxisPainterPrivate::clearCache()
|
|
|
6405
|
{
|
|
|
6406
|
mLabelCache.clear();
|
|
|
6407
|
}
|
|
|
6408
|
|
|
|
6409
|
/*! \internal
|
|
|
6410
|
|
|
|
6411
|
Returns a hash that allows uniquely identifying whether the label parameters have changed such
|
|
|
6412
|
that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
|
|
|
6413
|
return value of this method hasn't changed since the last redraw, the respective label parameters
|
|
|
6414
|
haven't changed and cached labels may be used.
|
|
|
6415
|
*/
|
|
|
6416
|
QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
|
|
|
6417
|
{
|
|
|
6418
|
QByteArray result;
|
|
|
6419
|
result.append(QByteArray::number(tickLabelRotation));
|
|
|
6420
|
result.append(QByteArray::number((int)substituteExponent));
|
|
|
6421
|
result.append(QByteArray::number((int)numberMultiplyCross));
|
|
|
6422
|
result.append(tickLabelColor.name()+QByteArray::number(tickLabelColor.alpha(), 16));
|
|
|
6423
|
result.append(tickLabelFont.toString());
|
|
|
6424
|
return result;
|
|
|
6425
|
}
|
|
|
6426
|
|
|
|
6427
|
/*! \internal
|
|
|
6428
|
|
|
|
6429
|
Draws a single tick label with the provided \a painter, utilizing the internal label cache to
|
|
|
6430
|
significantly speed up drawing of labels that were drawn in previous calls. The tick label is
|
|
|
6431
|
always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
|
|
|
6432
|
pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
|
|
|
6433
|
for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
|
|
|
6434
|
at which the label should be drawn.
|
|
|
6435
|
|
|
|
6436
|
In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
|
|
|
6437
|
largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
|
|
|
6438
|
drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
|
|
|
6439
|
tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
|
|
|
6440
|
holds.
|
|
|
6441
|
|
|
|
6442
|
The label is drawn with the font and pen that are currently set on the \a painter. To draw
|
|
|
6443
|
superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
|
|
|
6444
|
getTickLabelData).
|
|
|
6445
|
*/
|
|
|
6446
|
void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
|
|
|
6447
|
{
|
|
|
6448
|
// warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
|
|
|
6449
|
if (text.isEmpty()) return;
|
|
|
6450
|
QSize finalSize;
|
|
|
6451
|
QPointF labelAnchor;
|
|
|
6452
|
switch (type)
|
|
|
6453
|
{
|
|
|
6454
|
case QCPAxis::atLeft: labelAnchor = QPointF(alignmentRect.left()-distanceToAxis-offset, position); break;
|
|
|
6455
|
case QCPAxis::atRight: labelAnchor = QPointF(alignmentRect.right()+distanceToAxis+offset, position); break;
|
|
|
6456
|
case QCPAxis::atTop: labelAnchor = QPointF(position, alignmentRect.top()-distanceToAxis-offset); break;
|
|
|
6457
|
case QCPAxis::atBottom: labelAnchor = QPointF(position, alignmentRect.bottom()+distanceToAxis+offset); break;
|
|
|
6458
|
}
|
|
|
6459
|
if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
|
|
|
6460
|
{
|
|
|
6461
|
if (!mLabelCache.contains(text)) // no cached label exists, create it
|
|
|
6462
|
{
|
|
|
6463
|
CachedLabel *newCachedLabel = new CachedLabel;
|
|
|
6464
|
TickLabelData labelData = getTickLabelData(painter->font(), text);
|
|
|
6465
|
QPointF drawOffset = getTickLabelDrawOffset(labelData);
|
|
|
6466
|
newCachedLabel->offset = drawOffset+labelData.rotatedTotalBounds.topLeft();
|
|
|
6467
|
newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
|
|
|
6468
|
newCachedLabel->pixmap.fill(Qt::transparent);
|
|
|
6469
|
QCPPainter cachePainter(&newCachedLabel->pixmap);
|
|
|
6470
|
cachePainter.setPen(painter->pen());
|
|
|
6471
|
drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
|
|
|
6472
|
mLabelCache.insert(text, newCachedLabel, 1);
|
|
|
6473
|
}
|
|
|
6474
|
// draw cached label:
|
|
|
6475
|
const CachedLabel *cachedLabel = mLabelCache.object(text);
|
|
|
6476
|
// if label would be partly clipped by widget border on sides, don't draw it:
|
|
|
6477
|
if (QCPAxis::orientation(type) == Qt::Horizontal)
|
|
|
6478
|
{
|
|
|
6479
|
if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() ||
|
|
|
6480
|
labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left())
|
|
|
6481
|
return;
|
|
|
6482
|
} else
|
|
|
6483
|
{
|
|
|
6484
|
if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() >viewportRect.bottom() ||
|
|
|
6485
|
labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top())
|
|
|
6486
|
return;
|
|
|
6487
|
}
|
|
|
6488
|
painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
|
|
|
6489
|
finalSize = cachedLabel->pixmap.size();
|
|
|
6490
|
} else // label caching disabled, draw text directly on surface:
|
|
|
6491
|
{
|
|
|
6492
|
TickLabelData labelData = getTickLabelData(painter->font(), text);
|
|
|
6493
|
QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
|
|
|
6494
|
// if label would be partly clipped by widget border on sides, don't draw it:
|
|
|
6495
|
if (QCPAxis::orientation(type) == Qt::Horizontal)
|
|
|
6496
|
{
|
|
|
6497
|
if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() ||
|
|
|
6498
|
finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left())
|
|
|
6499
|
return;
|
|
|
6500
|
} else
|
|
|
6501
|
{
|
|
|
6502
|
if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() ||
|
|
|
6503
|
finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top())
|
|
|
6504
|
return;
|
|
|
6505
|
}
|
|
|
6506
|
drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
|
|
|
6507
|
finalSize = labelData.rotatedTotalBounds.size();
|
|
|
6508
|
}
|
|
|
6509
|
|
|
|
6510
|
// expand passed tickLabelsSize if current tick label is larger:
|
|
|
6511
|
if (finalSize.width() > tickLabelsSize->width())
|
|
|
6512
|
tickLabelsSize->setWidth(finalSize.width());
|
|
|
6513
|
if (finalSize.height() > tickLabelsSize->height())
|
|
|
6514
|
tickLabelsSize->setHeight(finalSize.height());
|
|
|
6515
|
}
|
|
|
6516
|
|
|
|
6517
|
/*! \internal
|
|
|
6518
|
|
|
|
6519
|
This is a \ref placeTickLabel helper function.
|
|
|
6520
|
|
|
|
6521
|
Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
|
|
|
6522
|
y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
|
|
|
6523
|
directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
|
|
|
6524
|
QCP::phCacheLabels plotting hint is not set.
|
|
|
6525
|
*/
|
|
|
6526
|
void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
|
|
|
6527
|
{
|
|
|
6528
|
// backup painter settings that we're about to change:
|
|
|
6529
|
QTransform oldTransform = painter->transform();
|
|
|
6530
|
QFont oldFont = painter->font();
|
|
|
6531
|
|
|
|
6532
|
// transform painter to position/rotation:
|
|
|
6533
|
painter->translate(x, y);
|
|
|
6534
|
if (!qFuzzyIsNull(tickLabelRotation))
|
|
|
6535
|
painter->rotate(tickLabelRotation);
|
|
|
6536
|
|
|
|
6537
|
// draw text:
|
|
|
6538
|
if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
|
|
|
6539
|
{
|
|
|
6540
|
painter->setFont(labelData.baseFont);
|
|
|
6541
|
painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
|
|
|
6542
|
painter->setFont(labelData.expFont);
|
|
|
6543
|
painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
|
|
|
6544
|
} else
|
|
|
6545
|
{
|
|
|
6546
|
painter->setFont(labelData.baseFont);
|
|
|
6547
|
painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
|
|
|
6548
|
}
|
|
|
6549
|
|
|
|
6550
|
// reset painter settings to what it was before:
|
|
|
6551
|
painter->setTransform(oldTransform);
|
|
|
6552
|
painter->setFont(oldFont);
|
|
|
6553
|
}
|
|
|
6554
|
|
|
|
6555
|
/*! \internal
|
|
|
6556
|
|
|
|
6557
|
This is a \ref placeTickLabel helper function.
|
|
|
6558
|
|
|
|
6559
|
Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
|
|
|
6560
|
processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
|
|
|
6561
|
exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
|
|
|
6562
|
*/
|
|
|
6563
|
QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
|
|
|
6564
|
{
|
|
|
6565
|
TickLabelData result;
|
|
|
6566
|
|
|
|
6567
|
// determine whether beautiful decimal powers should be used
|
|
|
6568
|
bool useBeautifulPowers = false;
|
|
|
6569
|
int ePos = -1;
|
|
|
6570
|
if (substituteExponent)
|
|
|
6571
|
{
|
|
|
6572
|
ePos = text.indexOf('e');
|
|
|
6573
|
if (ePos > -1)
|
|
|
6574
|
useBeautifulPowers = true;
|
|
|
6575
|
}
|
|
|
6576
|
|
|
|
6577
|
// calculate text bounding rects and do string preparation for beautiful decimal powers:
|
|
|
6578
|
result.baseFont = font;
|
|
|
6579
|
if (result.baseFont.pointSizeF() > 0) // On some rare systems, this sometimes is initialized with -1 (Qt bug?), so we check here before possibly setting a negative value in the next line
|
|
|
6580
|
result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
|
|
|
6581
|
if (useBeautifulPowers)
|
|
|
6582
|
{
|
|
|
6583
|
// split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
|
|
|
6584
|
result.basePart = text.left(ePos);
|
|
|
6585
|
// in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
|
|
|
6586
|
if (abbreviateDecimalPowers && result.basePart == "1")
|
|
|
6587
|
result.basePart = "10";
|
|
|
6588
|
else
|
|
|
6589
|
result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
|
|
|
6590
|
result.expPart = text.mid(ePos+1);
|
|
|
6591
|
// clip "+" and leading zeros off expPart:
|
|
|
6592
|
while (result.expPart.length() > 2 && result.expPart.at(1) == '0') // length > 2 so we leave one zero when numberFormatChar is 'e'
|
|
|
6593
|
result.expPart.remove(1, 1);
|
|
|
6594
|
if (!result.expPart.isEmpty() && result.expPart.at(0) == '+')
|
|
|
6595
|
result.expPart.remove(0, 1);
|
|
|
6596
|
// prepare smaller font for exponent:
|
|
|
6597
|
result.expFont = font;
|
|
|
6598
|
result.expFont.setPointSize(result.expFont.pointSize()*0.75);
|
|
|
6599
|
// calculate bounding rects of base part, exponent part and total one:
|
|
|
6600
|
result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
|
|
|
6601
|
result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
|
|
|
6602
|
result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
|
|
|
6603
|
} else // useBeautifulPowers == false
|
|
|
6604
|
{
|
|
|
6605
|
result.basePart = text;
|
|
|
6606
|
result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
|
|
|
6607
|
}
|
|
|
6608
|
result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
|
|
|
6609
|
|
|
|
6610
|
// calculate possibly different bounding rect after rotation:
|
|
|
6611
|
result.rotatedTotalBounds = result.totalBounds;
|
|
|
6612
|
if (!qFuzzyIsNull(tickLabelRotation))
|
|
|
6613
|
{
|
|
|
6614
|
QTransform transform;
|
|
|
6615
|
transform.rotate(tickLabelRotation);
|
|
|
6616
|
result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
|
|
|
6617
|
}
|
|
|
6618
|
|
|
|
6619
|
return result;
|
|
|
6620
|
}
|
|
|
6621
|
|
|
|
6622
|
/*! \internal
|
|
|
6623
|
|
|
|
6624
|
This is a \ref placeTickLabel helper function.
|
|
|
6625
|
|
|
|
6626
|
Calculates the offset at which the top left corner of the specified tick label shall be drawn.
|
|
|
6627
|
The offset is relative to a point right next to the tick the label belongs to.
|
|
|
6628
|
|
|
|
6629
|
This function is thus responsible for e.g. centering tick labels under ticks and positioning them
|
|
|
6630
|
appropriately when they are rotated.
|
|
|
6631
|
*/
|
|
|
6632
|
QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
|
|
|
6633
|
{
|
|
|
6634
|
/*
|
|
|
6635
|
calculate label offset from base point at tick (non-trivial, for best visual appearance): short
|
|
|
6636
|
explanation for bottom axis: The anchor, i.e. the point in the label that is placed
|
|
|
6637
|
horizontally under the corresponding tick is always on the label side that is closer to the
|
|
|
6638
|
axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
|
|
|
6639
|
is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
|
|
|
6640
|
will be centered under the tick (i.e. displaced horizontally by half its height). At the same
|
|
|
6641
|
time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
|
|
|
6642
|
labels.
|
|
|
6643
|
*/
|
|
|
6644
|
bool doRotation = !qFuzzyIsNull(tickLabelRotation);
|
|
|
6645
|
bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
|
|
|
6646
|
double radians = tickLabelRotation/180.0*M_PI;
|
|
|
6647
|
int x=0, y=0;
|
|
|
6648
|
if (type == QCPAxis::atLeft)
|
|
|
6649
|
{
|
|
|
6650
|
if (doRotation)
|
|
|
6651
|
{
|
|
|
6652
|
if (tickLabelRotation > 0)
|
|
|
6653
|
{
|
|
|
6654
|
x = -qCos(radians)*labelData.totalBounds.width();
|
|
|
6655
|
y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
|
|
|
6656
|
} else
|
|
|
6657
|
{
|
|
|
6658
|
x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
|
|
|
6659
|
y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
|
|
|
6660
|
}
|
|
|
6661
|
} else
|
|
|
6662
|
{
|
|
|
6663
|
x = -labelData.totalBounds.width();
|
|
|
6664
|
y = -labelData.totalBounds.height()/2.0;
|
|
|
6665
|
}
|
|
|
6666
|
} else if (type == QCPAxis::atRight)
|
|
|
6667
|
{
|
|
|
6668
|
if (doRotation)
|
|
|
6669
|
{
|
|
|
6670
|
if (tickLabelRotation > 0)
|
|
|
6671
|
{
|
|
|
6672
|
x = +qSin(radians)*labelData.totalBounds.height();
|
|
|
6673
|
y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
|
|
|
6674
|
} else
|
|
|
6675
|
{
|
|
|
6676
|
x = 0;
|
|
|
6677
|
y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
|
|
|
6678
|
}
|
|
|
6679
|
} else
|
|
|
6680
|
{
|
|
|
6681
|
x = 0;
|
|
|
6682
|
y = -labelData.totalBounds.height()/2.0;
|
|
|
6683
|
}
|
|
|
6684
|
} else if (type == QCPAxis::atTop)
|
|
|
6685
|
{
|
|
|
6686
|
if (doRotation)
|
|
|
6687
|
{
|
|
|
6688
|
if (tickLabelRotation > 0)
|
|
|
6689
|
{
|
|
|
6690
|
x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
|
|
|
6691
|
y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
|
|
|
6692
|
} else
|
|
|
6693
|
{
|
|
|
6694
|
x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
|
|
|
6695
|
y = -qCos(-radians)*labelData.totalBounds.height();
|
|
|
6696
|
}
|
|
|
6697
|
} else
|
|
|
6698
|
{
|
|
|
6699
|
x = -labelData.totalBounds.width()/2.0;
|
|
|
6700
|
y = -labelData.totalBounds.height();
|
|
|
6701
|
}
|
|
|
6702
|
} else if (type == QCPAxis::atBottom)
|
|
|
6703
|
{
|
|
|
6704
|
if (doRotation)
|
|
|
6705
|
{
|
|
|
6706
|
if (tickLabelRotation > 0)
|
|
|
6707
|
{
|
|
|
6708
|
x = +qSin(radians)*labelData.totalBounds.height()/2.0;
|
|
|
6709
|
y = 0;
|
|
|
6710
|
} else
|
|
|
6711
|
{
|
|
|
6712
|
x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
|
|
|
6713
|
y = +qSin(-radians)*labelData.totalBounds.width();
|
|
|
6714
|
}
|
|
|
6715
|
} else
|
|
|
6716
|
{
|
|
|
6717
|
x = -labelData.totalBounds.width()/2.0;
|
|
|
6718
|
y = 0;
|
|
|
6719
|
}
|
|
|
6720
|
}
|
|
|
6721
|
|
|
|
6722
|
return QPointF(x, y);
|
|
|
6723
|
}
|
|
|
6724
|
|
|
|
6725
|
/*! \internal
|
|
|
6726
|
|
|
|
6727
|
Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
|
|
|
6728
|
to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
|
|
|
6729
|
margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
|
|
|
6730
|
smaller width/height.
|
|
|
6731
|
*/
|
|
|
6732
|
void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
|
|
|
6733
|
{
|
|
|
6734
|
// note: this function must return the same tick label sizes as the placeTickLabel function.
|
|
|
6735
|
QSize finalSize;
|
|
|
6736
|
if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
|
|
|
6737
|
{
|
|
|
6738
|
const CachedLabel *cachedLabel = mLabelCache.object(text);
|
|
|
6739
|
finalSize = cachedLabel->pixmap.size();
|
|
|
6740
|
} else // label caching disabled or no label with this text cached:
|
|
|
6741
|
{
|
|
|
6742
|
TickLabelData labelData = getTickLabelData(font, text);
|
|
|
6743
|
finalSize = labelData.rotatedTotalBounds.size();
|
|
|
6744
|
}
|
|
|
6745
|
|
|
|
6746
|
// expand passed tickLabelsSize if current tick label is larger:
|
|
|
6747
|
if (finalSize.width() > tickLabelsSize->width())
|
|
|
6748
|
tickLabelsSize->setWidth(finalSize.width());
|
|
|
6749
|
if (finalSize.height() > tickLabelsSize->height())
|
|
|
6750
|
tickLabelsSize->setHeight(finalSize.height());
|
|
|
6751
|
}
|
|
|
6752
|
|
|
|
6753
|
|
|
|
6754
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
6484
|
6755
|
//////////////////// QCPAbstractPlottable
|
|
6485
|
6756
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
6486
|
6757
|
|
|
@@
-6563,30
+6834,36
QCP::Interaction QCPAxis::selectionCateg
|
|
6563
|
6834
|
of this plottable inside \a rect, next to the plottable name.
|
|
6564
|
6835
|
*/
|
|
6565
|
6836
|
|
|
6566
|
|
/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &validRange, SignDomain inSignDomain) const = 0
|
|
|
6837
|
/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
|
|
6567
|
6838
|
\internal
|
|
6568
|
6839
|
|
|
6569
|
6840
|
called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
|
|
6570
|
6841
|
set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
|
|
6571
|
6842
|
returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
|
|
6572
|
6843
|
to \ref sdNegative and all positive points will be ignored for range calculation. For no
|
|
6573
|
|
restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
|
|
6574
|
|
parameter that indicates whether a proper range could be found or not. If this is false, you
|
|
6575
|
|
shouldn't use the returned range (e.g. no points in data).
|
|
|
6844
|
restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
|
|
|
6845
|
parameter that indicates whether a range could be found or not. If this is false, you shouldn't
|
|
|
6846
|
use the returned range (e.g. no points in data).
|
|
|
6847
|
|
|
|
6848
|
Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
|
|
|
6849
|
this function may have size zero, which wouldn't count as a valid range.
|
|
6576
|
6850
|
|
|
6577
|
6851
|
\see rescaleAxes, getValueRange
|
|
6578
|
6852
|
*/
|
|
6579
|
6853
|
|
|
6580
|
|
/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &validRange, SignDomain inSignDomain) const = 0
|
|
|
6854
|
/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
|
|
6581
|
6855
|
\internal
|
|
6582
|
6856
|
|
|
6583
|
6857
|
called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
|
|
6584
|
6858
|
set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
|
|
6585
|
6859
|
returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
|
|
6586
|
6860
|
to \ref sdNegative and all positive points will be ignored for range calculation. For no
|
|
6587
|
|
restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
|
|
6588
|
|
parameter that indicates whether a proper range could be found or not. If this is false, you
|
|
6589
|
|
shouldn't use the returned range (e.g. no points in data).
|
|
|
6861
|
restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
|
|
|
6862
|
parameter that indicates whether a range could be found or not. If this is false, you shouldn't
|
|
|
6863
|
use the returned range (e.g. no points in data).
|
|
|
6864
|
|
|
|
6865
|
Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
|
|
|
6866
|
this function may have size zero, which wouldn't count as a valid range.
|
|
6590
|
6867
|
|
|
6591
|
6868
|
\see rescaleAxes, getKeyRange
|
|
6592
|
6869
|
*/
|
|
@@
-6596,8
+6873,15
QCP::Interaction QCPAxis::selectionCateg
|
|
6596
|
6873
|
|
|
6597
|
6874
|
/*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
|
|
6598
|
6875
|
|
|
6599
|
|
This signal is emitted when the selection state of this plottable has changed to \a selected,
|
|
6600
|
|
either by user interaction or by a direct call to \ref setSelected.
|
|
|
6876
|
This signal is emitted when the selection state of this plottable has changed, either by user
|
|
|
6877
|
interaction or by a direct call to \ref setSelected.
|
|
|
6878
|
*/
|
|
|
6879
|
|
|
|
6880
|
/*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
|
|
|
6881
|
|
|
|
6882
|
This signal is emitted when the selectability of this plottable has changed.
|
|
|
6883
|
|
|
|
6884
|
\see setSelectable
|
|
6601
|
6885
|
*/
|
|
6602
|
6886
|
|
|
6603
|
6887
|
/* end of documentation of signals */
|
|
@@
-6770,7
+7054,11
void QCPAbstractPlottable::setValueAxis(
|
|
6770
|
7054
|
*/
|
|
6771
|
7055
|
void QCPAbstractPlottable::setSelectable(bool selectable)
|
|
6772
|
7056
|
{
|
|
|
7057
|
if (mSelectable != selectable)
|
|
|
7058
|
{
|
|
6773
|
7059
|
mSelectable = selectable;
|
|
|
7060
|
emit selectableChanged(mSelectable);
|
|
|
7061
|
}
|
|
6774
|
7062
|
}
|
|
6775
|
7063
|
|
|
6776
|
7064
|
/*!
|
|
@@
-6829,9
+7117,9
void QCPAbstractPlottable::rescaleKeyAxi
|
|
6829
|
7117
|
if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
|
|
6830
|
7118
|
signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
|
|
6831
|
7119
|
|
|
6832
|
|
bool rangeValid;
|
|
6833
|
|
QCPRange newRange = getKeyRange(rangeValid, signDomain);
|
|
6834
|
|
if (rangeValid)
|
|
|
7120
|
bool foundRange;
|
|
|
7121
|
QCPRange newRange = getKeyRange(foundRange, signDomain);
|
|
|
7122
|
if (foundRange)
|
|
6835
|
7123
|
{
|
|
6836
|
7124
|
if (onlyEnlarge)
|
|
6837
|
7125
|
newRange.expand(keyAxis->range());
|
|
@@
-6869,9
+7157,9
void QCPAbstractPlottable::rescaleValueA
|
|
6869
|
7157
|
if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
|
|
6870
|
7158
|
signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
|
|
6871
|
7159
|
|
|
6872
|
|
bool rangeValid;
|
|
6873
|
|
QCPRange newRange = getValueRange(rangeValid, signDomain);
|
|
6874
|
|
if (rangeValid)
|
|
|
7160
|
bool foundRange;
|
|
|
7161
|
QCPRange newRange = getValueRange(foundRange, signDomain);
|
|
|
7162
|
if (foundRange)
|
|
6875
|
7163
|
{
|
|
6876
|
7164
|
if (onlyEnlarge)
|
|
6877
|
7165
|
newRange.expand(valueAxis->range());
|
|
@@
-7976,7
+8264,11
void QCPAbstractItem::setClipAxisRect(QC
|
|
7976
|
8264
|
*/
|
|
7977
|
8265
|
void QCPAbstractItem::setSelectable(bool selectable)
|
|
7978
|
8266
|
{
|
|
|
8267
|
if (mSelectable != selectable)
|
|
|
8268
|
{
|
|
7979
|
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
|
8580
|
\image html qcp-doc-logo.png
|
|
8289
|
8581
|
|
|
@@
-8329,7
+8621,8
QCP::Interaction QCPAbstractItem::select
|
|
8329
|
8621
|
usual, if the cast returns zero, the plottable wasn't of that specific subclass.)
|
|
8330
|
8622
|
|
|
8331
|
8623
|
All further interfacing with plottables (e.g how to set data) is specific to the plottable type.
|
|
8332
|
|
See the documentations of the subclasses: QCPGraph, QCPCurve, QCPBars, QCPStatisticalBox.
|
|
|
8624
|
See the documentations of the subclasses: QCPGraph, QCPCurve, QCPBars, QCPStatisticalBox,
|
|
|
8625
|
QCPColorMap.
|
|
8333
|
8626
|
|
|
8334
|
8627
|
\section mainpage-axes Controlling the Axes
|
|
8335
|
8628
|
|
|
@@
-8356,20
+8649,20
QCP::Interaction QCPAbstractItem::select
|
|
8356
|
8649
|
|
|
8357
|
8650
|
\section mainpage-legend Plot Legend
|
|
8358
|
8651
|
|
|
8359
|
|
Every QCustomPlot owns one QCPLegend (as \a legend) by default. A legend is a small layout
|
|
8360
|
|
element inside the plot which lists the plottables with an icon of the plottable line/symbol and
|
|
8361
|
|
a description. The Description is retrieved from the plottable name
|
|
8362
|
|
(QCPAbstractPlottable::setName). Plottables can be added and removed from the legend via \ref
|
|
8363
|
|
QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend. By default,
|
|
8364
|
|
adding a plottable to QCustomPlot automatically adds it to the legend, too. This behaviour can be
|
|
8365
|
|
modified with the QCustomPlot::setAutoAddPlottableToLegend property.
|
|
|
8652
|
Every QCustomPlot has one QCPLegend (as \ref QCustomPlot::legend) by default. A legend is a small
|
|
|
8653
|
layout element inside the plot which lists the plottables with an icon of the plottable
|
|
|
8654
|
line/symbol and a name (QCPAbstractPlottable::setName). Plottables can be added and removed from
|
|
|
8655
|
the main legend via \ref QCPAbstractPlottable::addToLegend and \ref
|
|
|
8656
|
QCPAbstractPlottable::removeFromLegend. By default, adding a plottable to QCustomPlot
|
|
|
8657
|
automatically adds it to the legend, too. This behaviour can be modified with the
|
|
|
8658
|
QCustomPlot::setAutoAddPlottableToLegend property.
|
|
8366
|
8659
|
|
|
8367
|
8660
|
The QCPLegend provides an interface to access, add and remove legend items directly, too. See
|
|
8368
|
8661
|
QCPLegend::item, QCPLegend::itemWithPlottable, QCPLegend::addItem, QCPLegend::removeItem for
|
|
8369
|
8662
|
example.
|
|
8370
|
8663
|
|
|
8371
|
|
Multiple legends are supported via the layout system (as a QCPLegend simply is a normal layout
|
|
8372
|
|
element).
|
|
|
8664
|
Multiple legends are supported via the \link thelayoutsystem layout system\endlink (as a
|
|
|
8665
|
QCPLegend simply is a normal layout element).
|
|
8373
|
8666
|
|
|
8374
|
8667
|
\section mainpage-userinteraction User Interactions
|
|
8375
|
8668
|
|
|
@@
-8820,6
+9113,7
QCustomPlot::QCustomPlot(QWidget *parent
|
|
8820
|
9113
|
mPlotLayout = new QCPLayoutGrid;
|
|
8821
|
9114
|
mPlotLayout->initializeParentPlot(this);
|
|
8822
|
9115
|
mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
|
|
|
9116
|
mPlotLayout->setLayer("main");
|
|
8823
|
9117
|
QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
|
|
8824
|
9118
|
mPlotLayout->addElement(0, 0, defaultAxisRect);
|
|
8825
|
9119
|
xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
|
|
@@
-9354,10
+9648,10
int QCustomPlot::plottableCount() const
|
|
9354
|
9648
|
QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
|
|
9355
|
9649
|
{
|
|
9356
|
9650
|
QList<QCPAbstractPlottable*> result;
|
|
9357
|
|
for (int i=0; i<mPlottables.size(); ++i)
|
|
9358
|
|
{
|
|
9359
|
|
if (mPlottables.at(i)->selected())
|
|
9360
|
|
result.append(mPlottables.at(i));
|
|
|
9651
|
foreach (QCPAbstractPlottable *plottable, mPlottables)
|
|
|
9652
|
{
|
|
|
9653
|
if (plottable->selected())
|
|
|
9654
|
result.append(plottable);
|
|
9361
|
9655
|
}
|
|
9362
|
9656
|
return result;
|
|
9363
|
9657
|
}
|
|
@@
-9379,17
+9673,16
QCPAbstractPlottable *QCustomPlot::plott
|
|
9379
|
9673
|
QCPAbstractPlottable *resultPlottable = 0;
|
|
9380
|
9674
|
double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
|
|
9381
|
9675
|
|
|
9382
|
|
for (int i=0; i<mPlottables.size(); ++i)
|
|
9383
|
|
{
|
|
9384
|
|
QCPAbstractPlottable *currentPlottable = mPlottables.at(i);
|
|
9385
|
|
if (onlySelectable && !currentPlottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
|
|
|
9676
|
foreach (QCPAbstractPlottable *plottable, mPlottables)
|
|
|
9677
|
{
|
|
|
9678
|
if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
|
|
9386
|
9679
|
continue;
|
|
9387
|
|
if ((currentPlottable->keyAxis()->axisRect()->rect() & currentPlottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
|
|
9388
|
|
{
|
|
9389
|
|
double currentDistance = currentPlottable->selectTest(pos, false);
|
|
|
9680
|
if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
|
|
|
9681
|
{
|
|
|
9682
|
double currentDistance = plottable->selectTest(pos, false);
|
|
9390
|
9683
|
if (currentDistance >= 0 && currentDistance < resultDistance)
|
|
9391
|
9684
|
{
|
|
9392
|
|
resultPlottable = currentPlottable;
|
|
|
9685
|
resultPlottable = plottable;
|
|
9393
|
9686
|
resultDistance = currentDistance;
|
|
9394
|
9687
|
}
|
|
9395
|
9688
|
}
|
|
@@
-9545,10
+9838,10
int QCustomPlot::graphCount() const
|
|
9545
|
9838
|
QList<QCPGraph*> QCustomPlot::selectedGraphs() const
|
|
9546
|
9839
|
{
|
|
9547
|
9840
|
QList<QCPGraph*> result;
|
|
9548
|
|
for (int i=0; i<mGraphs.size(); ++i)
|
|
9549
|
|
{
|
|
9550
|
|
if (mGraphs.at(i)->selected())
|
|
9551
|
|
result.append(mGraphs.at(i));
|
|
|
9841
|
foreach (QCPGraph *graph, mGraphs)
|
|
|
9842
|
{
|
|
|
9843
|
if (graph->selected())
|
|
|
9844
|
result.append(graph);
|
|
9552
|
9845
|
}
|
|
9553
|
9846
|
return result;
|
|
9554
|
9847
|
}
|
|
@@
-9679,10
+9972,10
int QCustomPlot::itemCount() const
|
|
9679
|
9972
|
QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
|
|
9680
|
9973
|
{
|
|
9681
|
9974
|
QList<QCPAbstractItem*> result;
|
|
9682
|
|
for (int i=0; i<mItems.size(); ++i)
|
|
9683
|
|
{
|
|
9684
|
|
if (mItems.at(i)->selected())
|
|
9685
|
|
result.append(mItems.at(i));
|
|
|
9975
|
foreach (QCPAbstractItem *item, mItems)
|
|
|
9976
|
{
|
|
|
9977
|
if (item->selected())
|
|
|
9978
|
result.append(item);
|
|
9686
|
9979
|
}
|
|
9687
|
9980
|
return result;
|
|
9688
|
9981
|
}
|
|
@@
-9705,17
+9998,16
QCPAbstractItem *QCustomPlot::itemAt(con
|
|
9705
|
9998
|
QCPAbstractItem *resultItem = 0;
|
|
9706
|
9999
|
double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
|
|
9707
|
10000
|
|
|
9708
|
|
for (int i=0; i<mItems.size(); ++i)
|
|
9709
|
|
{
|
|
9710
|
|
QCPAbstractItem *currentItem = mItems[i];
|
|
9711
|
|
if (onlySelectable && !currentItem->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
|
|
|
10001
|
foreach (QCPAbstractItem *item, mItems)
|
|
|
10002
|
{
|
|
|
10003
|
if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
|
|
9712
|
10004
|
continue;
|
|
9713
|
|
if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
|
|
9714
|
|
{
|
|
9715
|
|
double currentDistance = currentItem->selectTest(pos, false);
|
|
|
10005
|
if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
|
|
|
10006
|
{
|
|
|
10007
|
double currentDistance = item->selectTest(pos, false);
|
|
9716
|
10008
|
if (currentDistance >= 0 && currentDistance < resultDistance)
|
|
9717
|
10009
|
{
|
|
9718
|
|
resultItem = currentItem;
|
|
|
10010
|
resultItem = item;
|
|
9719
|
10011
|
resultDistance = currentDistance;
|
|
9720
|
10012
|
}
|
|
9721
|
10013
|
}
|
|
@@
-9744,10
+10036,10
bool QCustomPlot::hasItem(QCPAbstractIte
|
|
9744
|
10036
|
*/
|
|
9745
|
10037
|
QCPLayer *QCustomPlot::layer(const QString &name) const
|
|
9746
|
10038
|
{
|
|
9747
|
|
for (int i=0; i<mLayers.size(); ++i)
|
|
9748
|
|
{
|
|
9749
|
|
if (mLayers.at(i)->name() == name)
|
|
9750
|
|
return mLayers.at(i);
|
|
|
10039
|
foreach (QCPLayer *layer, mLayers)
|
|
|
10040
|
{
|
|
|
10041
|
if (layer->name() == name)
|
|
|
10042
|
return layer;
|
|
9751
|
10043
|
}
|
|
9752
|
10044
|
return 0;
|
|
9753
|
10045
|
}
|
|
@@
-9992,10
+10284,9
QList<QCPAxisRect*> QCustomPlot::axisRec
|
|
9992
|
10284
|
|
|
9993
|
10285
|
while (!elementStack.isEmpty())
|
|
9994
|
10286
|
{
|
|
9995
|
|
QList<QCPLayoutElement*> subElements = elementStack.pop()->elements(false);
|
|
9996
|
|
for (int i=0; i<subElements.size(); ++i)
|
|
9997
|
|
{
|
|
9998
|
|
if (QCPLayoutElement *element = subElements.at(i))
|
|
|
10287
|
foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
|
|
|
10288
|
{
|
|
|
10289
|
if (element)
|
|
9999
|
10290
|
{
|
|
10000
|
10291
|
elementStack.push(element);
|
|
10001
|
10292
|
if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
|
|
@@
-10018,23
+10309,22
QList<QCPAxisRect*> QCustomPlot::axisRec
|
|
10018
|
10309
|
*/
|
|
10019
|
10310
|
QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
|
|
10020
|
10311
|
{
|
|
10021
|
|
QCPLayoutElement *current = mPlotLayout;
|
|
|
10312
|
QCPLayoutElement *currentElement = mPlotLayout;
|
|
10022
|
10313
|
bool searchSubElements = true;
|
|
10023
|
|
while (searchSubElements && current)
|
|
|
10314
|
while (searchSubElements && currentElement)
|
|
10024
|
10315
|
{
|
|
10025
|
10316
|
searchSubElements = false;
|
|
10026
|
|
const QList<QCPLayoutElement*> elements = current->elements(false);
|
|
10027
|
|
for (int i=0; i<elements.size(); ++i)
|
|
10028
|
|
{
|
|
10029
|
|
if (elements.at(i) && elements.at(i)->realVisibility() && elements.at(i)->selectTest(pos, false) >= 0)
|
|
10030
|
|
{
|
|
10031
|
|
current = elements.at(i);
|
|
|
10317
|
foreach (QCPLayoutElement *subElement, currentElement->elements(false))
|
|
|
10318
|
{
|
|
|
10319
|
if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
|
|
|
10320
|
{
|
|
|
10321
|
currentElement = subElement;
|
|
10032
|
10322
|
searchSubElements = true;
|
|
10033
|
10323
|
break;
|
|
10034
|
10324
|
}
|
|
10035
|
10325
|
}
|
|
10036
|
10326
|
}
|
|
10037
|
|
return current;
|
|
|
10327
|
return currentElement;
|
|
10038
|
10328
|
}
|
|
10039
|
10329
|
|
|
10040
|
10330
|
/*!
|
|
@@
-10047,14
+10337,13
QCPLayoutElement *QCustomPlot::layoutEle
|
|
10047
|
10337
|
QList<QCPAxis*> QCustomPlot::selectedAxes() const
|
|
10048
|
10338
|
{
|
|
10049
|
10339
|
QList<QCPAxis*> result, allAxes;
|
|
10050
|
|
QList<QCPAxisRect*> rects = axisRects();
|
|
10051
|
|
for (int i=0; i<rects.size(); ++i)
|
|
10052
|
|
allAxes << rects.at(i)->axes();
|
|
10053
|
|
|
|
10054
|
|
for (int i=0; i<allAxes.size(); ++i)
|
|
10055
|
|
{
|
|
10056
|
|
if (allAxes.at(i)->selectedParts() != QCPAxis::spNone)
|
|
10057
|
|
result.append(allAxes.at(i));
|
|
|
10340
|
foreach (QCPAxisRect *rect, axisRects())
|
|
|
10341
|
allAxes << rect->axes();
|
|
|
10342
|
|
|
|
10343
|
foreach (QCPAxis *axis, allAxes)
|
|
|
10344
|
{
|
|
|
10345
|
if (axis->selectedParts() != QCPAxis::spNone)
|
|
|
10346
|
result.append(axis);
|
|
10058
|
10347
|
}
|
|
10059
|
10348
|
|
|
10060
|
10349
|
return result;
|
|
@@
-10077,13
+10366,12
QList<QCPLegend*> QCustomPlot::selectedL
|
|
10077
|
10366
|
|
|
10078
|
10367
|
while (!elementStack.isEmpty())
|
|
10079
|
10368
|
{
|
|
10080
|
|
QList<QCPLayoutElement*> subElements = elementStack.pop()->elements(false);
|
|
10081
|
|
for (int i=0; i<subElements.size(); ++i)
|
|
10082
|
|
{
|
|
10083
|
|
if (QCPLayoutElement *element = subElements.at(i))
|
|
10084
|
|
{
|
|
10085
|
|
elementStack.push(element);
|
|
10086
|
|
if (QCPLegend *leg = qobject_cast<QCPLegend*>(element))
|
|
|
10369
|
foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
|
|
|
10370
|
{
|
|
|
10371
|
if (subElement)
|
|
|
10372
|
{
|
|
|
10373
|
elementStack.push(subElement);
|
|
|
10374
|
if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
|
|
10087
|
10375
|
{
|
|
10088
|
10376
|
if (leg->selectedParts() != QCPLegend::spNone)
|
|
10089
|
10377
|
result.append(leg);
|
|
@@
-10106,11
+10394,10
QList<QCPLegend*> QCustomPlot::selectedL
|
|
10106
|
10394
|
*/
|
|
10107
|
10395
|
void QCustomPlot::deselectAll()
|
|
10108
|
10396
|
{
|
|
10109
|
|
for (int i=0; i<mLayers.size(); ++i)
|
|
10110
|
|
{
|
|
10111
|
|
QList<QCPLayerable*> layerables = mLayers.at(i)->children();
|
|
10112
|
|
for (int k=0; k<layerables.size(); ++k)
|
|
10113
|
|
layerables.at(k)->deselectEvent(0);
|
|
|
10397
|
foreach (QCPLayer *layer, mLayers)
|
|
|
10398
|
{
|
|
|
10399
|
foreach (QCPLayerable *layerable, layer->children())
|
|
|
10400
|
layerable->deselectEvent(0);
|
|
10114
|
10401
|
}
|
|
10115
|
10402
|
}
|
|
10116
|
10403
|
|
|
@@
-10127,12
+10414,13
void QCustomPlot::deselectAll()
|
|
10127
|
10414
|
signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
|
|
10128
|
10415
|
recursion.
|
|
10129
|
10416
|
*/
|
|
10130
|
|
void QCustomPlot::replot()
|
|
|
10417
|
void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
|
|
10131
|
10418
|
{
|
|
10132
|
10419
|
if (mReplotting) // incase signals loop back to replot slot
|
|
10133
|
10420
|
return;
|
|
10134
|
10421
|
mReplotting = true;
|
|
10135
|
10422
|
emit beforeReplot();
|
|
|
10423
|
|
|
10136
|
10424
|
mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
|
|
10137
|
10425
|
QCPPainter painter;
|
|
10138
|
10426
|
painter.begin(&mPaintBuffer);
|
|
@@
-10143,12
+10431,13
void QCustomPlot::replot()
|
|
10143
|
10431
|
painter.fillRect(mViewport, mBackgroundBrush);
|
|
10144
|
10432
|
draw(&painter);
|
|
10145
|
10433
|
painter.end();
|
|
10146
|
|
if (mPlottingHints.testFlag(QCP::phForceRepaint))
|
|
|
10434
|
if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
|
|
10147
|
10435
|
repaint();
|
|
10148
|
10436
|
else
|
|
10149
|
10437
|
update();
|
|
10150
|
10438
|
} else // might happen if QCustomPlot has width or height zero
|
|
10151
|
10439
|
qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
|
|
|
10440
|
|
|
10152
|
10441
|
emit afterReplot();
|
|
10153
|
10442
|
mReplotting = false;
|
|
10154
|
10443
|
}
|
|
@@
-10163,15
+10452,12
void QCustomPlot::replot()
|
|
10163
|
10452
|
*/
|
|
10164
|
10453
|
void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
|
|
10165
|
10454
|
{
|
|
10166
|
|
// get a list of all axes in the plot:
|
|
10167
|
|
QList<QCPAxis*> axes;
|
|
10168
|
|
QList<QCPAxisRect*> rects = axisRects();
|
|
10169
|
|
for (int i=0; i<rects.size(); ++i)
|
|
10170
|
|
axes << rects.at(i)->axes();
|
|
10171
|
|
|
|
10172
|
|
// call rescale on all axes:
|
|
10173
|
|
for (int i=0; i<axes.size(); ++i)
|
|
10174
|
|
axes.at(i)->rescale(onlyVisiblePlottables);
|
|
|
10455
|
QList<QCPAxis*> allAxes;
|
|
|
10456
|
foreach (QCPAxisRect *rect, axisRects())
|
|
|
10457
|
allAxes << rect->axes();
|
|
|
10458
|
|
|
|
10459
|
foreach (QCPAxis *axis, allAxes)
|
|
|
10460
|
axis->rescale(onlyVisiblePlottables);
|
|
10175
|
10461
|
}
|
|
10176
|
10462
|
|
|
10177
|
10463
|
/*!
|
|
@@
-10203,12
+10489,15
void QCustomPlot::rescaleAxes(bool onlyV
|
|
10203
|
10489
|
aren't defined yet inside the constructor, so you would get an image that has strange
|
|
10204
|
10490
|
widths/heights.
|
|
10205
|
10491
|
|
|
|
10492
|
\a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
|
|
|
10493
|
PDF file.
|
|
|
10494
|
|
|
10206
|
10495
|
\note On Android systems, this method does nothing and issues an according qDebug warning
|
|
10207
|
10496
|
message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
|
|
10208
|
10497
|
|
|
10209
|
10498
|
\see savePng, saveBmp, saveJpg, saveRastered
|
|
10210
|
10499
|
*/
|
|
10211
|
|
bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height)
|
|
|
10500
|
bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
|
|
10212
|
10501
|
{
|
|
10213
|
10502
|
bool success = false;
|
|
10214
|
10503
|
#ifdef QT_NO_PRINTER
|
|
@@
-10234,6
+10523,8
bool QCustomPlot::savePdf(const QString
|
|
10234
|
10523
|
printer.setOutputFormat(QPrinter::PdfFormat);
|
|
10235
|
10524
|
printer.setFullPage(true);
|
|
10236
|
10525
|
printer.setColorMode(QPrinter::Color);
|
|
|
10526
|
printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
|
|
|
10527
|
printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
|
|
10237
|
10528
|
QRect oldViewport = viewport();
|
|
10238
|
10529
|
setViewport(QRect(0, 0, newWidth, newHeight));
|
|
10239
|
10530
|
printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
|
|
@@
-10421,7
+10712,7
void QCustomPlot::resizeEvent(QResizeEve
|
|
10421
|
10712
|
// resize and repaint the buffer:
|
|
10422
|
10713
|
mPaintBuffer = QPixmap(event->size());
|
|
10423
|
10714
|
setViewport(rect());
|
|
10424
|
|
replot();
|
|
|
10715
|
replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
|
|
10425
|
10716
|
}
|
|
10426
|
10717
|
|
|
10427
|
10718
|
/*! \internal
|
|
@@
-10536,6
+10827,22
void QCustomPlot::mouseReleaseEvent(QMou
|
|
10536
|
10827
|
QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
|
|
10537
|
10828
|
bool selectionStateChanged = false;
|
|
10538
|
10829
|
bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
|
|
|
10830
|
// deselect all other layerables if not additive selection:
|
|
|
10831
|
if (!additive)
|
|
|
10832
|
{
|
|
|
10833
|
foreach (QCPLayer *layer, mLayers)
|
|
|
10834
|
{
|
|
|
10835
|
foreach (QCPLayerable *layerable, layer->children())
|
|
|
10836
|
{
|
|
|
10837
|
if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
|
|
|
10838
|
{
|
|
|
10839
|
bool selChanged = false;
|
|
|
10840
|
layerable->deselectEvent(&selChanged);
|
|
|
10841
|
selectionStateChanged |= selChanged;
|
|
|
10842
|
}
|
|
|
10843
|
}
|
|
|
10844
|
}
|
|
|
10845
|
}
|
|
10539
|
10846
|
if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
|
|
10540
|
10847
|
{
|
|
10541
|
10848
|
// a layerable was actually clicked, call its selectEvent:
|
|
@@
-10543,23
+10850,6
void QCustomPlot::mouseReleaseEvent(QMou
|
|
10543
|
10850
|
clickedLayerable->selectEvent(event, additive, details, &selChanged);
|
|
10544
|
10851
|
selectionStateChanged |= selChanged;
|
|
10545
|
10852
|
}
|
|
10546
|
|
// deselect all other layerables if not additive selection:
|
|
10547
|
|
if (!additive)
|
|
10548
|
|
{
|
|
10549
|
|
for (int i=0; i<mLayers.size(); ++i)
|
|
10550
|
|
{
|
|
10551
|
|
QList<QCPLayerable*> layerables = mLayers.at(i)->children();
|
|
10552
|
|
for (int k=0; k<layerables.size(); ++k)
|
|
10553
|
|
{
|
|
10554
|
|
if (layerables.at(k) != clickedLayerable && mInteractions.testFlag(layerables.at(k)->selectionCategory()))
|
|
10555
|
|
{
|
|
10556
|
|
bool selChanged = false;
|
|
10557
|
|
layerables.at(k)->deselectEvent(&selChanged);
|
|
10558
|
|
selectionStateChanged |= selChanged;
|
|
10559
|
|
}
|
|
10560
|
|
}
|
|
10561
|
|
}
|
|
10562
|
|
}
|
|
10563
|
10853
|
doReplot = true;
|
|
10564
|
10854
|
if (selectionStateChanged)
|
|
10565
|
10855
|
emit selectionChangedByUser();
|
|
@@
-10621,28
+10911,19
void QCustomPlot::wheelEvent(QWheelEvent
|
|
10621
|
10911
|
*/
|
|
10622
|
10912
|
void QCustomPlot::draw(QCPPainter *painter)
|
|
10623
|
10913
|
{
|
|
10624
|
|
// update all axis tick vectors:
|
|
10625
|
|
QList<QCPAxisRect*> rects = axisRects();
|
|
10626
|
|
for (int i=0; i<rects.size(); ++i)
|
|
10627
|
|
{
|
|
10628
|
|
QList<QCPAxis*> axes = rects.at(i)->axes();
|
|
10629
|
|
for (int k=0; k<axes.size(); ++k)
|
|
10630
|
|
axes.at(k)->setupTickVectors();
|
|
10631
|
|
}
|
|
10632
|
|
|
|
10633
|
|
// recalculate layout:
|
|
10634
|
|
mPlotLayout->update();
|
|
|
10914
|
// run through layout phases:
|
|
|
10915
|
mPlotLayout->update(QCPLayoutElement::upPreparation);
|
|
|
10916
|
mPlotLayout->update(QCPLayoutElement::upMargins);
|
|
|
10917
|
mPlotLayout->update(QCPLayoutElement::upLayout);
|
|
10635
|
10918
|
|
|
10636
|
10919
|
// draw viewport background pixmap:
|
|
10637
|
10920
|
drawBackground(painter);
|
|
10638
|
10921
|
|
|
10639
|
10922
|
// draw all layered objects (grid, axes, plottables, items, legend,...):
|
|
10640
|
|
for (int layerIndex=0; layerIndex < mLayers.size(); ++layerIndex)
|
|
10641
|
|
{
|
|
10642
|
|
QList<QCPLayerable*> layerChildren = mLayers.at(layerIndex)->children();
|
|
10643
|
|
for (int k=0; k < layerChildren.size(); ++k)
|
|
10644
|
|
{
|
|
10645
|
|
QCPLayerable *child = layerChildren.at(k);
|
|
|
10923
|
foreach (QCPLayer *layer, mLayers)
|
|
|
10924
|
{
|
|
|
10925
|
foreach (QCPLayerable *child, layer->children())
|
|
|
10926
|
{
|
|
10646
|
10927
|
if (child->realVisibility())
|
|
10647
|
10928
|
{
|
|
10648
|
10929
|
painter->save();
|
|
@@
-10653,6
+10934,17
void QCustomPlot::draw(QCPPainter *paint
|
|
10653
|
10934
|
}
|
|
10654
|
10935
|
}
|
|
10655
|
10936
|
}
|
|
|
10937
|
|
|
|
10938
|
/* Debug code to draw all layout element rects
|
|
|
10939
|
foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
|
|
|
10940
|
{
|
|
|
10941
|
painter->setBrush(Qt::NoBrush);
|
|
|
10942
|
painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
|
|
|
10943
|
painter->drawRect(el->rect());
|
|
|
10944
|
painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
|
|
|
10945
|
painter->drawRect(el->outerRect());
|
|
|
10946
|
}
|
|
|
10947
|
*/
|
|
10656
|
10948
|
}
|
|
10657
|
10949
|
|
|
10658
|
10950
|
/*! \internal
|
|
@@
-10664,7
+10956,7
void QCustomPlot::draw(QCPPainter *paint
|
|
10664
|
10956
|
the viewport with the provided \a painter. The scaled version is buffered in
|
|
10665
|
10957
|
mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
|
|
10666
|
10958
|
the axis rect has changed in a way that requires a rescale of the background pixmap (this is
|
|
10667
|
|
dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
|
|
|
10959
|
dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
|
|
10668
|
10960
|
set.
|
|
10669
|
10961
|
|
|
10670
|
10962
|
Note that this function does not draw a fill with the background brush (\ref setBackground(const
|
|
@@
-10890,6
+11182,3215
void QCustomPlot::toPainter(QCPPainter *
|
|
10890
|
11182
|
|
|
10891
|
11183
|
|
|
10892
|
11184
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
11185
|
//////////////////// QCPColorGradient
|
|
|
11186
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
11187
|
|
|
|
11188
|
/*! \class QCPColorGradient
|
|
|
11189
|
\brief Defines a color gradient for use with e.g. \ref QCPColorMap
|
|
|
11190
|
|
|
|
11191
|
This class describes a color gradient which can be used to encode data with color. For example,
|
|
|
11192
|
QCPColorMap and QCPColorScale have a \ref QCPColorMap::setGradient "setGradient" method which
|
|
|
11193
|
takes an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
|
|
|
11194
|
with a \a position from 0 to 1. In between these defined color positions, the
|
|
|
11195
|
color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
|
|
|
11196
|
|
|
|
11197
|
Alternatively, load one of the preset color gradients shown in the image below, with \ref
|
|
|
11198
|
loadPreset, or by directly specifying the preset in the constructor.
|
|
|
11199
|
|
|
|
11200
|
\image html QCPColorGradient.png
|
|
|
11201
|
|
|
|
11202
|
The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
|
|
|
11203
|
converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
|
|
|
11204
|
GradientPreset to all the \a setGradient methods, e.g.:
|
|
|
11205
|
\code
|
|
|
11206
|
colorMap->setGradient(QCPColorGradient::gpHot);
|
|
|
11207
|
\endcode
|
|
|
11208
|
|
|
|
11209
|
The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
|
|
|
11210
|
color gradient shall be applied periodically (wrapping around) to data values that lie outside
|
|
|
11211
|
the data range specified on the plottable instance can be controlled with \ref setPeriodic.
|
|
|
11212
|
*/
|
|
|
11213
|
|
|
|
11214
|
/*!
|
|
|
11215
|
Constructs a new QCPColorGradient initialized with the colors and color interpolation according
|
|
|
11216
|
to \a preset.
|
|
|
11217
|
|
|
|
11218
|
The color level count is initialized to 350.
|
|
|
11219
|
*/
|
|
|
11220
|
QCPColorGradient::QCPColorGradient(GradientPreset preset) :
|
|
|
11221
|
mLevelCount(350),
|
|
|
11222
|
mColorInterpolation(ciRGB),
|
|
|
11223
|
mPeriodic(false),
|
|
|
11224
|
mColorBufferInvalidated(true)
|
|
|
11225
|
{
|
|
|
11226
|
mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
|
|
|
11227
|
loadPreset(preset);
|
|
|
11228
|
}
|
|
|
11229
|
|
|
|
11230
|
/* undocumented operator */
|
|
|
11231
|
bool QCPColorGradient::operator==(const QCPColorGradient &other) const
|
|
|
11232
|
{
|
|
|
11233
|
return ((other.mLevelCount == this->mLevelCount) &&
|
|
|
11234
|
(other.mColorInterpolation == this->mColorInterpolation) &&
|
|
|
11235
|
(other.mPeriodic == this->mPeriodic) &&
|
|
|
11236
|
(other.mColorStops == this->mColorStops));
|
|
|
11237
|
}
|
|
|
11238
|
|
|
|
11239
|
/*!
|
|
|
11240
|
Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
|
|
|
11241
|
is typically enough to create a smooth appearance.
|
|
|
11242
|
|
|
|
11243
|
\image html QCPColorGradient-levelcount.png
|
|
|
11244
|
*/
|
|
|
11245
|
void QCPColorGradient::setLevelCount(int n)
|
|
|
11246
|
{
|
|
|
11247
|
if (n < 2)
|
|
|
11248
|
{
|
|
|
11249
|
qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
|
|
|
11250
|
n = 2;
|
|
|
11251
|
}
|
|
|
11252
|
if (n != mLevelCount)
|
|
|
11253
|
{
|
|
|
11254
|
mLevelCount = n;
|
|
|
11255
|
mColorBufferInvalidated = true;
|
|
|
11256
|
}
|
|
|
11257
|
}
|
|
|
11258
|
|
|
|
11259
|
/*!
|
|
|
11260
|
Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
|
|
|
11261
|
colors are the values of the passed QMap \a colorStops. In between these color stops, the color
|
|
|
11262
|
is interpolated according to \ref setColorInterpolation.
|
|
|
11263
|
|
|
|
11264
|
A more convenient way to create a custom gradient may be to clear all color stops with \ref
|
|
|
11265
|
clearColorStops and then adding them one by one with \ref setColorStopAt.
|
|
|
11266
|
|
|
|
11267
|
\see clearColorStops
|
|
|
11268
|
*/
|
|
|
11269
|
void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
|
|
|
11270
|
{
|
|
|
11271
|
mColorStops = colorStops;
|
|
|
11272
|
mColorBufferInvalidated = true;
|
|
|
11273
|
}
|
|
|
11274
|
|
|
|
11275
|
/*!
|
|
|
11276
|
Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
|
|
|
11277
|
these color stops, the color is interpolated according to \ref setColorInterpolation.
|
|
|
11278
|
|
|
|
11279
|
\see setColorStops, clearColorStops
|
|
|
11280
|
*/
|
|
|
11281
|
void QCPColorGradient::setColorStopAt(double position, const QColor &color)
|
|
|
11282
|
{
|
|
|
11283
|
mColorStops.insert(position, color);
|
|
|
11284
|
mColorBufferInvalidated = true;
|
|
|
11285
|
}
|
|
|
11286
|
|
|
|
11287
|
/*!
|
|
|
11288
|
Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
|
|
|
11289
|
interpolated linearly in RGB or in HSV color space.
|
|
|
11290
|
|
|
|
11291
|
For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
|
|
|
11292
|
whereas in HSV space the intermediate color is yellow.
|
|
|
11293
|
*/
|
|
|
11294
|
void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
|
|
|
11295
|
{
|
|
|
11296
|
if (interpolation != mColorInterpolation)
|
|
|
11297
|
{
|
|
|
11298
|
mColorInterpolation = interpolation;
|
|
|
11299
|
mColorBufferInvalidated = true;
|
|
|
11300
|
}
|
|
|
11301
|
}
|
|
|
11302
|
|
|
|
11303
|
/*!
|
|
|
11304
|
Sets whether data points that are outside the configured data range (e.g. \ref
|
|
|
11305
|
QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
|
|
|
11306
|
they all have the same color, corresponding to the respective gradient boundary color.
|
|
|
11307
|
|
|
|
11308
|
\image html QCPColorGradient-periodic.png
|
|
|
11309
|
|
|
|
11310
|
As shown in the image above, gradients that have the same start and end color are especially
|
|
|
11311
|
suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
|
|
|
11312
|
the color map. A preset that has this property is \ref gpHues.
|
|
|
11313
|
|
|
|
11314
|
In practice, using periodic color gradients makes sense when the data corresponds to a periodic
|
|
|
11315
|
dimension, such as an angle or a phase. If this is not the case, the color encoding might become
|
|
|
11316
|
ambiguous, because multiple different data values are shown as the same color.
|
|
|
11317
|
*/
|
|
|
11318
|
void QCPColorGradient::setPeriodic(bool enabled)
|
|
|
11319
|
{
|
|
|
11320
|
mPeriodic = enabled;
|
|
|
11321
|
}
|
|
|
11322
|
|
|
|
11323
|
/*!
|
|
|
11324
|
This method is used to quickly convert a \a data array to colors. The colors will be output in
|
|
|
11325
|
the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
|
|
|
11326
|
function. The data range that shall be used for mapping the data value to the gradient is passed
|
|
|
11327
|
in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
|
|
|
11328
|
logarithmically.
|
|
|
11329
|
|
|
|
11330
|
if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
|
|
|
11331
|
set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
|
|
|
11332
|
array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
|
|
|
11333
|
is addressed <tt>data[i*dataIndexFactor]</tt>.
|
|
|
11334
|
*/
|
|
|
11335
|
void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
|
|
|
11336
|
{
|
|
|
11337
|
// If you change something here, make sure to also adapt ::color()
|
|
|
11338
|
if (!data)
|
|
|
11339
|
{
|
|
|
11340
|
qDebug() << Q_FUNC_INFO << "null pointer given as data";
|
|
|
11341
|
return;
|
|
|
11342
|
}
|
|
|
11343
|
if (!scanLine)
|
|
|
11344
|
{
|
|
|
11345
|
qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
|
|
|
11346
|
return;
|
|
|
11347
|
}
|
|
|
11348
|
if (mColorBufferInvalidated)
|
|
|
11349
|
updateColorBuffer();
|
|
|
11350
|
|
|
|
11351
|
if (!logarithmic)
|
|
|
11352
|
{
|
|
|
11353
|
const double posToIndexFactor = mLevelCount/range.size();
|
|
|
11354
|
if (mPeriodic)
|
|
|
11355
|
{
|
|
|
11356
|
for (int i=0; i<n; ++i)
|
|
|
11357
|
{
|
|
|
11358
|
int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
|
|
|
11359
|
if (index < 0)
|
|
|
11360
|
index += mLevelCount;
|
|
|
11361
|
scanLine[i] = mColorBuffer.at(index);
|
|
|
11362
|
}
|
|
|
11363
|
} else
|
|
|
11364
|
{
|
|
|
11365
|
for (int i=0; i<n; ++i)
|
|
|
11366
|
{
|
|
|
11367
|
int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
|
|
|
11368
|
if (index < 0)
|
|
|
11369
|
index = 0;
|
|
|
11370
|
else if (index >= mLevelCount)
|
|
|
11371
|
index = mLevelCount-1;
|
|
|
11372
|
scanLine[i] = mColorBuffer.at(index);
|
|
|
11373
|
}
|
|
|
11374
|
}
|
|
|
11375
|
} else // logarithmic == true
|
|
|
11376
|
{
|
|
|
11377
|
if (mPeriodic)
|
|
|
11378
|
{
|
|
|
11379
|
for (int i=0; i<n; ++i)
|
|
|
11380
|
{
|
|
|
11381
|
int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*mLevelCount) % mLevelCount;
|
|
|
11382
|
if (index < 0)
|
|
|
11383
|
index += mLevelCount;
|
|
|
11384
|
scanLine[i] = mColorBuffer.at(index);
|
|
|
11385
|
}
|
|
|
11386
|
} else
|
|
|
11387
|
{
|
|
|
11388
|
for (int i=0; i<n; ++i)
|
|
|
11389
|
{
|
|
|
11390
|
int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*mLevelCount;
|
|
|
11391
|
if (index < 0)
|
|
|
11392
|
index = 0;
|
|
|
11393
|
else if (index >= mLevelCount)
|
|
|
11394
|
index = mLevelCount-1;
|
|
|
11395
|
scanLine[i] = mColorBuffer.at(index);
|
|
|
11396
|
}
|
|
|
11397
|
}
|
|
|
11398
|
}
|
|
|
11399
|
}
|
|
|
11400
|
|
|
|
11401
|
/*! \internal
|
|
|
11402
|
|
|
|
11403
|
This method is used to colorize a single data value given in \a position, to colors. The data
|
|
|
11404
|
range that shall be used for mapping the data value to the gradient is passed in \a range. \a
|
|
|
11405
|
logarithmic indicates whether the data value shall be mapped to a color logarithmically.
|
|
|
11406
|
|
|
|
11407
|
If an entire array of data values shall be converted, rather use \ref colorize, for better
|
|
|
11408
|
performance.
|
|
|
11409
|
*/
|
|
|
11410
|
QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
|
|
|
11411
|
{
|
|
|
11412
|
// If you change something here, make sure to also adapt ::colorize()
|
|
|
11413
|
if (mColorBufferInvalidated)
|
|
|
11414
|
updateColorBuffer();
|
|
|
11415
|
int index = 0;
|
|
|
11416
|
if (!logarithmic)
|
|
|
11417
|
index = (position-range.lower)*mLevelCount/range.size();
|
|
|
11418
|
else
|
|
|
11419
|
index = qLn(position/range.lower)/qLn(range.upper/range.lower)*mLevelCount;
|
|
|
11420
|
if (mPeriodic)
|
|
|
11421
|
{
|
|
|
11422
|
index = index % mLevelCount;
|
|
|
11423
|
if (index < 0)
|
|
|
11424
|
index += mLevelCount;
|
|
|
11425
|
} else
|
|
|
11426
|
{
|
|
|
11427
|
if (index < 0)
|
|
|
11428
|
index = 0;
|
|
|
11429
|
else if (index >= mLevelCount)
|
|
|
11430
|
index = mLevelCount-1;
|
|
|
11431
|
}
|
|
|
11432
|
return mColorBuffer.at(index);
|
|
|
11433
|
}
|
|
|
11434
|
|
|
|
11435
|
/*!
|
|
|
11436
|
Clears the current color stops and loads the specified \a preset. A preset consists of predefined
|
|
|
11437
|
color stops and the corresponding color interpolation method.
|
|
|
11438
|
|
|
|
11439
|
The available presets are:
|
|
|
11440
|
\image html QCPColorGradient.png
|
|
|
11441
|
*/
|
|
|
11442
|
void QCPColorGradient::loadPreset(GradientPreset preset)
|
|
|
11443
|
{
|
|
|
11444
|
clearColorStops();
|
|
|
11445
|
switch (preset)
|
|
|
11446
|
{
|
|
|
11447
|
case gpGrayscale:
|
|
|
11448
|
setColorInterpolation(ciRGB);
|
|
|
11449
|
setColorStopAt(0, Qt::black);
|
|
|
11450
|
setColorStopAt(1, Qt::white);
|
|
|
11451
|
break;
|
|
|
11452
|
case gpHot:
|
|
|
11453
|
setColorInterpolation(ciRGB);
|
|
|
11454
|
setColorStopAt(0, QColor(50, 0, 0));
|
|
|
11455
|
setColorStopAt(0.2, QColor(180, 10, 0));
|
|
|
11456
|
setColorStopAt(0.4, QColor(245, 50, 0));
|
|
|
11457
|
setColorStopAt(0.6, QColor(255, 150, 10));
|
|
|
11458
|
setColorStopAt(0.8, QColor(255, 255, 50));
|
|
|
11459
|
setColorStopAt(1, QColor(255, 255, 255));
|
|
|
11460
|
break;
|
|
|
11461
|
case gpCold:
|
|
|
11462
|
setColorInterpolation(ciRGB);
|
|
|
11463
|
setColorStopAt(0, QColor(0, 0, 50));
|
|
|
11464
|
setColorStopAt(0.2, QColor(0, 10, 180));
|
|
|
11465
|
setColorStopAt(0.4, QColor(0, 50, 245));
|
|
|
11466
|
setColorStopAt(0.6, QColor(10, 150, 255));
|
|
|
11467
|
setColorStopAt(0.8, QColor(50, 255, 255));
|
|
|
11468
|
setColorStopAt(1, QColor(255, 255, 255));
|
|
|
11469
|
break;
|
|
|
11470
|
case gpNight:
|
|
|
11471
|
setColorInterpolation(ciHSV);
|
|
|
11472
|
setColorStopAt(0, QColor(10, 20, 30));
|
|
|
11473
|
setColorStopAt(1, QColor(250, 255, 250));
|
|
|
11474
|
break;
|
|
|
11475
|
case gpCandy:
|
|
|
11476
|
setColorInterpolation(ciHSV);
|
|
|
11477
|
setColorStopAt(0, QColor(0, 0, 255));
|
|
|
11478
|
setColorStopAt(1, QColor(255, 250, 250));
|
|
|
11479
|
break;
|
|
|
11480
|
case gpGeography:
|
|
|
11481
|
setColorInterpolation(ciRGB);
|
|
|
11482
|
setColorStopAt(0, QColor(70, 170, 210));
|
|
|
11483
|
setColorStopAt(0.20, QColor(90, 160, 180));
|
|
|
11484
|
setColorStopAt(0.25, QColor(45, 130, 175));
|
|
|
11485
|
setColorStopAt(0.30, QColor(100, 140, 125));
|
|
|
11486
|
setColorStopAt(0.5, QColor(100, 140, 100));
|
|
|
11487
|
setColorStopAt(0.6, QColor(130, 145, 120));
|
|
|
11488
|
setColorStopAt(0.7, QColor(140, 130, 120));
|
|
|
11489
|
setColorStopAt(0.9, QColor(180, 190, 190));
|
|
|
11490
|
setColorStopAt(1, QColor(210, 210, 230));
|
|
|
11491
|
break;
|
|
|
11492
|
case gpIon:
|
|
|
11493
|
setColorInterpolation(ciHSV);
|
|
|
11494
|
setColorStopAt(0, QColor(50, 10, 10));
|
|
|
11495
|
setColorStopAt(0.45, QColor(0, 0, 255));
|
|
|
11496
|
setColorStopAt(0.8, QColor(0, 255, 255));
|
|
|
11497
|
setColorStopAt(1, QColor(0, 255, 0));
|
|
|
11498
|
break;
|
|
|
11499
|
case gpThermal:
|
|
|
11500
|
setColorInterpolation(ciRGB);
|
|
|
11501
|
setColorStopAt(0, QColor(0, 0, 50));
|
|
|
11502
|
setColorStopAt(0.15, QColor(20, 0, 120));
|
|
|
11503
|
setColorStopAt(0.33, QColor(200, 30, 140));
|
|
|
11504
|
setColorStopAt(0.6, QColor(255, 100, 0));
|
|
|
11505
|
setColorStopAt(0.85, QColor(255, 255, 40));
|
|
|
11506
|
setColorStopAt(1, QColor(255, 255, 255));
|
|
|
11507
|
break;
|
|
|
11508
|
case gpPolar:
|
|
|
11509
|
setColorInterpolation(ciRGB);
|
|
|
11510
|
setColorStopAt(0, QColor(50, 255, 255));
|
|
|
11511
|
setColorStopAt(0.18, QColor(10, 70, 255));
|
|
|
11512
|
setColorStopAt(0.28, QColor(10, 10, 190));
|
|
|
11513
|
setColorStopAt(0.5, QColor(0, 0, 0));
|
|
|
11514
|
setColorStopAt(0.72, QColor(190, 10, 10));
|
|
|
11515
|
setColorStopAt(0.82, QColor(255, 70, 10));
|
|
|
11516
|
setColorStopAt(1, QColor(255, 255, 50));
|
|
|
11517
|
break;
|
|
|
11518
|
case gpSpectrum:
|
|
|
11519
|
setColorInterpolation(ciHSV);
|
|
|
11520
|
setColorStopAt(0, QColor(50, 0, 50));
|
|
|
11521
|
setColorStopAt(0.15, QColor(0, 0, 255));
|
|
|
11522
|
setColorStopAt(0.35, QColor(0, 255, 255));
|
|
|
11523
|
setColorStopAt(0.6, QColor(255, 255, 0));
|
|
|
11524
|
setColorStopAt(0.75, QColor(255, 30, 0));
|
|
|
11525
|
setColorStopAt(1, QColor(50, 0, 0));
|
|
|
11526
|
break;
|
|
|
11527
|
case gpJet:
|
|
|
11528
|
setColorInterpolation(ciRGB);
|
|
|
11529
|
setColorStopAt(0, QColor(0, 0, 100));
|
|
|
11530
|
setColorStopAt(0.15, QColor(0, 50, 255));
|
|
|
11531
|
setColorStopAt(0.35, QColor(0, 255, 255));
|
|
|
11532
|
setColorStopAt(0.65, QColor(255, 255, 0));
|
|
|
11533
|
setColorStopAt(0.85, QColor(255, 30, 0));
|
|
|
11534
|
setColorStopAt(1, QColor(100, 0, 0));
|
|
|
11535
|
break;
|
|
|
11536
|
case gpHues:
|
|
|
11537
|
setColorInterpolation(ciHSV);
|
|
|
11538
|
setColorStopAt(0, QColor(255, 0, 0));
|
|
|
11539
|
setColorStopAt(1.0/3.0, QColor(0, 0, 255));
|
|
|
11540
|
setColorStopAt(2.0/3.0, QColor(0, 255, 0));
|
|
|
11541
|
setColorStopAt(1, QColor(255, 0, 0));
|
|
|
11542
|
break;
|
|
|
11543
|
}
|
|
|
11544
|
}
|
|
|
11545
|
|
|
|
11546
|
/*!
|
|
|
11547
|
Clears all color stops.
|
|
|
11548
|
|
|
|
11549
|
\see setColorStops, setColorStopAt
|
|
|
11550
|
*/
|
|
|
11551
|
void QCPColorGradient::clearColorStops()
|
|
|
11552
|
{
|
|
|
11553
|
mColorStops.clear();
|
|
|
11554
|
mColorBufferInvalidated = true;
|
|
|
11555
|
}
|
|
|
11556
|
|
|
|
11557
|
/*!
|
|
|
11558
|
Returns an inverted gradient. The inverted gradient has all properties as this \ref
|
|
|
11559
|
QCPColorGradient, but the order of the color stops is inverted.
|
|
|
11560
|
|
|
|
11561
|
\see setColorStops, setColorStopAt
|
|
|
11562
|
*/
|
|
|
11563
|
QCPColorGradient QCPColorGradient::inverted() const
|
|
|
11564
|
{
|
|
|
11565
|
QCPColorGradient result(*this);
|
|
|
11566
|
result.clearColorStops();
|
|
|
11567
|
for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
|
|
|
11568
|
result.setColorStopAt(1.0-it.key(), it.value());
|
|
|
11569
|
return result;
|
|
|
11570
|
}
|
|
|
11571
|
|
|
|
11572
|
/*! \internal
|
|
|
11573
|
|
|
|
11574
|
Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
|
|
|
11575
|
convert positions to colors. This is where the interpolation between color stops is calculated.
|
|
|
11576
|
*/
|
|
|
11577
|
void QCPColorGradient::updateColorBuffer()
|
|
|
11578
|
{
|
|
|
11579
|
if (mColorBuffer.size() != mLevelCount)
|
|
|
11580
|
mColorBuffer.resize(mLevelCount);
|
|
|
11581
|
if (mColorStops.size() > 1)
|
|
|
11582
|
{
|
|
|
11583
|
double indexToPosFactor = 1.0/(double)(mLevelCount-1);
|
|
|
11584
|
for (int i=0; i<mLevelCount; ++i)
|
|
|
11585
|
{
|
|
|
11586
|
double position = i*indexToPosFactor;
|
|
|
11587
|
QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
|
|
|
11588
|
if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
|
|
|
11589
|
{
|
|
|
11590
|
mColorBuffer[i] = (it-1).value().rgb();
|
|
|
11591
|
} else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
|
|
|
11592
|
{
|
|
|
11593
|
mColorBuffer[i] = it.value().rgb();
|
|
|
11594
|
} else // position is in between stops (or on an intermediate stop), interpolate color
|
|
|
11595
|
{
|
|
|
11596
|
QMap<double, QColor>::const_iterator high = it;
|
|
|
11597
|
QMap<double, QColor>::const_iterator low = it-1;
|
|
|
11598
|
double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
|
|
|
11599
|
switch (mColorInterpolation)
|
|
|
11600
|
{
|
|
|
11601
|
case ciRGB:
|
|
|
11602
|
{
|
|
|
11603
|
mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
|
|
|
11604
|
(1-t)*low.value().green() + t*high.value().green(),
|
|
|
11605
|
(1-t)*low.value().blue() + t*high.value().blue());
|
|
|
11606
|
break;
|
|
|
11607
|
}
|
|
|
11608
|
case ciHSV:
|
|
|
11609
|
{
|
|
|
11610
|
QColor lowHsv = low.value().toHsv();
|
|
|
11611
|
QColor highHsv = high.value().toHsv();
|
|
|
11612
|
double hue = 0;
|
|
|
11613
|
double hueDiff = highHsv.hueF()-lowHsv.hueF();
|
|
|
11614
|
if (hueDiff > 0.5)
|
|
|
11615
|
hue = lowHsv.hueF() - t*(1.0-hueDiff);
|
|
|
11616
|
else if (hueDiff < -0.5)
|
|
|
11617
|
hue = lowHsv.hueF() + t*(1.0+hueDiff);
|
|
|
11618
|
else
|
|
|
11619
|
hue = lowHsv.hueF() + t*hueDiff;
|
|
|
11620
|
if (hue < 0) hue += 1.0;
|
|
|
11621
|
else if (hue >= 1.0) hue -= 1.0;
|
|
|
11622
|
mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
|
|
|
11623
|
break;
|
|
|
11624
|
}
|
|
|
11625
|
}
|
|
|
11626
|
}
|
|
|
11627
|
}
|
|
|
11628
|
} else if (mColorStops.size() == 1)
|
|
|
11629
|
{
|
|
|
11630
|
mColorBuffer.fill(mColorStops.constBegin().value().rgb());
|
|
|
11631
|
} else // mColorStops is empty, fill color buffer with black
|
|
|
11632
|
{
|
|
|
11633
|
mColorBuffer.fill(qRgb(0, 0, 0));
|
|
|
11634
|
}
|
|
|
11635
|
mColorBufferInvalidated = false;
|
|
|
11636
|
}
|
|
|
11637
|
|
|
|
11638
|
|
|
|
11639
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
11640
|
//////////////////// QCPAxisRect
|
|
|
11641
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
11642
|
|
|
|
11643
|
/*! \class QCPAxisRect
|
|
|
11644
|
\brief Holds multiple axes and arranges them in a rectangular shape.
|
|
|
11645
|
|
|
|
11646
|
This class represents an axis rect, a rectangular area that is bounded on all sides with an
|
|
|
11647
|
arbitrary number of axes.
|
|
|
11648
|
|
|
|
11649
|
Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
|
|
|
11650
|
layout system allows to have multiple axis rects, e.g. arranged in a grid layout
|
|
|
11651
|
(QCustomPlot::plotLayout).
|
|
|
11652
|
|
|
|
11653
|
By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
|
|
|
11654
|
accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
|
|
|
11655
|
If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
|
|
|
11656
|
invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
|
|
|
11657
|
addAxes. To remove an axis, use \ref removeAxis.
|
|
|
11658
|
|
|
|
11659
|
The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
|
|
|
11660
|
setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
|
|
|
11661
|
explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
|
|
|
11662
|
placed on other layers, independently of the axis rect.
|
|
|
11663
|
|
|
|
11664
|
Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
|
|
|
11665
|
insetLayout and can be used to have other layout elements (or even other layouts with multiple
|
|
|
11666
|
elements) hovering inside the axis rect.
|
|
|
11667
|
|
|
|
11668
|
If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
|
|
|
11669
|
behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
|
|
|
11670
|
is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
|
|
|
11671
|
via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
|
|
|
11672
|
only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
|
|
|
11673
|
QCP::iRangeZoom.
|
|
|
11674
|
|
|
|
11675
|
\image html AxisRectSpacingOverview.png
|
|
|
11676
|
<center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
|
|
|
11677
|
line on the far left indicates the viewport/widget border.</center>
|
|
|
11678
|
*/
|
|
|
11679
|
|
|
|
11680
|
/* start documentation of inline functions */
|
|
|
11681
|
|
|
|
11682
|
/*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
|
|
|
11683
|
|
|
|
11684
|
Returns the inset layout of this axis rect. It can be used to place other layout elements (or
|
|
|
11685
|
even layouts with multiple other elements) inside/on top of an axis rect.
|
|
|
11686
|
|
|
|
11687
|
\see QCPLayoutInset
|
|
|
11688
|
*/
|
|
|
11689
|
|
|
|
11690
|
/*! \fn int QCPAxisRect::left() const
|
|
|
11691
|
|
|
|
11692
|
Returns the pixel position of the left border of this axis rect. Margins are not taken into
|
|
|
11693
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
|
11694
|
*/
|
|
|
11695
|
|
|
|
11696
|
/*! \fn int QCPAxisRect::right() const
|
|
|
11697
|
|
|
|
11698
|
Returns the pixel position of the right border of this axis rect. Margins are not taken into
|
|
|
11699
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
|
11700
|
*/
|
|
|
11701
|
|
|
|
11702
|
/*! \fn int QCPAxisRect::top() const
|
|
|
11703
|
|
|
|
11704
|
Returns the pixel position of the top border of this axis rect. Margins are not taken into
|
|
|
11705
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
|
11706
|
*/
|
|
|
11707
|
|
|
|
11708
|
/*! \fn int QCPAxisRect::bottom() const
|
|
|
11709
|
|
|
|
11710
|
Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
|
|
|
11711
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
|
11712
|
*/
|
|
|
11713
|
|
|
|
11714
|
/*! \fn int QCPAxisRect::width() const
|
|
|
11715
|
|
|
|
11716
|
Returns the pixel width of this axis rect. Margins are not taken into account here, so the
|
|
|
11717
|
returned value is with respect to the inner \ref rect.
|
|
|
11718
|
*/
|
|
|
11719
|
|
|
|
11720
|
/*! \fn int QCPAxisRect::height() const
|
|
|
11721
|
|
|
|
11722
|
Returns the pixel height of this axis rect. Margins are not taken into account here, so the
|
|
|
11723
|
returned value is with respect to the inner \ref rect.
|
|
|
11724
|
*/
|
|
|
11725
|
|
|
|
11726
|
/*! \fn QSize QCPAxisRect::size() const
|
|
|
11727
|
|
|
|
11728
|
Returns the pixel size of this axis rect. Margins are not taken into account here, so the
|
|
|
11729
|
returned value is with respect to the inner \ref rect.
|
|
|
11730
|
*/
|
|
|
11731
|
|
|
|
11732
|
/*! \fn QPoint QCPAxisRect::topLeft() const
|
|
|
11733
|
|
|
|
11734
|
Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
|
|
|
11735
|
so the returned value is with respect to the inner \ref rect.
|
|
|
11736
|
*/
|
|
|
11737
|
|
|
|
11738
|
/*! \fn QPoint QCPAxisRect::topRight() const
|
|
|
11739
|
|
|
|
11740
|
Returns the top right corner of this axis rect in pixels. Margins are not taken into account
|
|
|
11741
|
here, so the returned value is with respect to the inner \ref rect.
|
|
|
11742
|
*/
|
|
|
11743
|
|
|
|
11744
|
/*! \fn QPoint QCPAxisRect::bottomLeft() const
|
|
|
11745
|
|
|
|
11746
|
Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
|
|
|
11747
|
here, so the returned value is with respect to the inner \ref rect.
|
|
|
11748
|
*/
|
|
|
11749
|
|
|
|
11750
|
/*! \fn QPoint QCPAxisRect::bottomRight() const
|
|
|
11751
|
|
|
|
11752
|
Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
|
|
|
11753
|
here, so the returned value is with respect to the inner \ref rect.
|
|
|
11754
|
*/
|
|
|
11755
|
|
|
|
11756
|
/*! \fn QPoint QCPAxisRect::center() const
|
|
|
11757
|
|
|
|
11758
|
Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
|
|
|
11759
|
returned value is with respect to the inner \ref rect.
|
|
|
11760
|
*/
|
|
|
11761
|
|
|
|
11762
|
/* end documentation of inline functions */
|
|
|
11763
|
|
|
|
11764
|
/*!
|
|
|
11765
|
Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
|
|
|
11766
|
sides, the top and right axes are set invisible initially.
|
|
|
11767
|
*/
|
|
|
11768
|
QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
|
|
|
11769
|
QCPLayoutElement(parentPlot),
|
|
|
11770
|
mBackgroundBrush(Qt::NoBrush),
|
|
|
11771
|
mBackgroundScaled(true),
|
|
|
11772
|
mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
|
|
|
11773
|
mInsetLayout(new QCPLayoutInset),
|
|
|
11774
|
mRangeDrag(Qt::Horizontal|Qt::Vertical),
|
|
|
11775
|
mRangeZoom(Qt::Horizontal|Qt::Vertical),
|
|
|
11776
|
mRangeZoomFactorHorz(0.85),
|
|
|
11777
|
mRangeZoomFactorVert(0.85),
|
|
|
11778
|
mDragging(false)
|
|
|
11779
|
{
|
|
|
11780
|
mInsetLayout->initializeParentPlot(mParentPlot);
|
|
|
11781
|
mInsetLayout->setParentLayerable(this);
|
|
|
11782
|
mInsetLayout->setParent(this);
|
|
|
11783
|
|
|
|
11784
|
setMinimumSize(50, 50);
|
|
|
11785
|
setMinimumMargins(QMargins(15, 15, 15, 15));
|
|
|
11786
|
mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
|
|
|
11787
|
mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
|
|
|
11788
|
mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
|
|
|
11789
|
mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
|
|
|
11790
|
|
|
|
11791
|
if (setupDefaultAxes)
|
|
|
11792
|
{
|
|
|
11793
|
QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
|
|
|
11794
|
QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
|
|
|
11795
|
QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
|
|
|
11796
|
QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
|
|
|
11797
|
setRangeDragAxes(xAxis, yAxis);
|
|
|
11798
|
setRangeZoomAxes(xAxis, yAxis);
|
|
|
11799
|
xAxis2->setVisible(false);
|
|
|
11800
|
yAxis2->setVisible(false);
|
|
|
11801
|
xAxis->grid()->setVisible(true);
|
|
|
11802
|
yAxis->grid()->setVisible(true);
|
|
|
11803
|
xAxis2->grid()->setVisible(false);
|
|
|
11804
|
yAxis2->grid()->setVisible(false);
|
|
|
11805
|
xAxis2->grid()->setZeroLinePen(Qt::NoPen);
|
|
|
11806
|
yAxis2->grid()->setZeroLinePen(Qt::NoPen);
|
|
|
11807
|
xAxis2->grid()->setVisible(false);
|
|
|
11808
|
yAxis2->grid()->setVisible(false);
|
|
|
11809
|
}
|
|
|
11810
|
}
|
|
|
11811
|
|
|
|
11812
|
QCPAxisRect::~QCPAxisRect()
|
|
|
11813
|
{
|
|
|
11814
|
delete mInsetLayout;
|
|
|
11815
|
mInsetLayout = 0;
|
|
|
11816
|
|
|
|
11817
|
QList<QCPAxis*> axesList = axes();
|
|
|
11818
|
for (int i=0; i<axesList.size(); ++i)
|
|
|
11819
|
removeAxis(axesList.at(i));
|
|
|
11820
|
}
|
|
|
11821
|
|
|
|
11822
|
/*!
|
|
|
11823
|
Returns the number of axes on the axis rect side specified with \a type.
|
|
|
11824
|
|
|
|
11825
|
\see axis
|
|
|
11826
|
*/
|
|
|
11827
|
int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
|
|
|
11828
|
{
|
|
|
11829
|
return mAxes.value(type).size();
|
|
|
11830
|
}
|
|
|
11831
|
|
|
|
11832
|
/*!
|
|
|
11833
|
Returns the axis with the given \a index on the axis rect side specified with \a type.
|
|
|
11834
|
|
|
|
11835
|
\see axisCount, axes
|
|
|
11836
|
*/
|
|
|
11837
|
QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
|
|
|
11838
|
{
|
|
|
11839
|
QList<QCPAxis*> ax(mAxes.value(type));
|
|
|
11840
|
if (index >= 0 && index < ax.size())
|
|
|
11841
|
{
|
|
|
11842
|
return ax.at(index);
|
|
|
11843
|
} else
|
|
|
11844
|
{
|
|
|
11845
|
qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
|
|
|
11846
|
return 0;
|
|
|
11847
|
}
|
|
|
11848
|
}
|
|
|
11849
|
|
|
|
11850
|
/*!
|
|
|
11851
|
Returns all axes on the axis rect sides specified with \a types.
|
|
|
11852
|
|
|
|
11853
|
\a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
|
|
|
11854
|
multiple sides.
|
|
|
11855
|
|
|
|
11856
|
\see axis
|
|
|
11857
|
*/
|
|
|
11858
|
QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
|
|
|
11859
|
{
|
|
|
11860
|
QList<QCPAxis*> result;
|
|
|
11861
|
if (types.testFlag(QCPAxis::atLeft))
|
|
|
11862
|
result << mAxes.value(QCPAxis::atLeft);
|
|
|
11863
|
if (types.testFlag(QCPAxis::atRight))
|
|
|
11864
|
result << mAxes.value(QCPAxis::atRight);
|
|
|
11865
|
if (types.testFlag(QCPAxis::atTop))
|
|
|
11866
|
result << mAxes.value(QCPAxis::atTop);
|
|
|
11867
|
if (types.testFlag(QCPAxis::atBottom))
|
|
|
11868
|
result << mAxes.value(QCPAxis::atBottom);
|
|
|
11869
|
return result;
|
|
|
11870
|
}
|
|
|
11871
|
|
|
|
11872
|
/*! \overload
|
|
|
11873
|
|
|
|
11874
|
Returns all axes of this axis rect.
|
|
|
11875
|
*/
|
|
|
11876
|
QList<QCPAxis*> QCPAxisRect::axes() const
|
|
|
11877
|
{
|
|
|
11878
|
QList<QCPAxis*> result;
|
|
|
11879
|
QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
|
|
|
11880
|
while (it.hasNext())
|
|
|
11881
|
{
|
|
|
11882
|
it.next();
|
|
|
11883
|
result << it.value();
|
|
|
11884
|
}
|
|
|
11885
|
return result;
|
|
|
11886
|
}
|
|
|
11887
|
|
|
|
11888
|
/*!
|
|
|
11889
|
Adds a new axis to the axis rect side specified with \a type, and returns it.
|
|
|
11890
|
|
|
|
11891
|
If an axis rect side already contains one or more axes, the lower and upper endings of the new
|
|
|
11892
|
axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are initialized to \ref
|
|
|
11893
|
QCPLineEnding::esHalfBar.
|
|
|
11894
|
|
|
|
11895
|
\see addAxes, setupFullAxesBox
|
|
|
11896
|
*/
|
|
|
11897
|
QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type)
|
|
|
11898
|
{
|
|
|
11899
|
QCPAxis *newAxis = new QCPAxis(this, type);
|
|
|
11900
|
if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
|
|
|
11901
|
{
|
|
|
11902
|
bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
|
|
|
11903
|
newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
|
|
|
11904
|
newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
|
|
|
11905
|
}
|
|
|
11906
|
mAxes[type].append(newAxis);
|
|
|
11907
|
return newAxis;
|
|
|
11908
|
}
|
|
|
11909
|
|
|
|
11910
|
/*!
|
|
|
11911
|
Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
|
|
|
11912
|
<tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
|
|
|
11913
|
|
|
|
11914
|
Returns a list of the added axes.
|
|
|
11915
|
|
|
|
11916
|
\see addAxis, setupFullAxesBox
|
|
|
11917
|
*/
|
|
|
11918
|
QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
|
|
|
11919
|
{
|
|
|
11920
|
QList<QCPAxis*> result;
|
|
|
11921
|
if (types.testFlag(QCPAxis::atLeft))
|
|
|
11922
|
result << addAxis(QCPAxis::atLeft);
|
|
|
11923
|
if (types.testFlag(QCPAxis::atRight))
|
|
|
11924
|
result << addAxis(QCPAxis::atRight);
|
|
|
11925
|
if (types.testFlag(QCPAxis::atTop))
|
|
|
11926
|
result << addAxis(QCPAxis::atTop);
|
|
|
11927
|
if (types.testFlag(QCPAxis::atBottom))
|
|
|
11928
|
result << addAxis(QCPAxis::atBottom);
|
|
|
11929
|
return result;
|
|
|
11930
|
}
|
|
|
11931
|
|
|
|
11932
|
/*!
|
|
|
11933
|
Removes the specified \a axis from the axis rect and deletes it.
|
|
|
11934
|
|
|
|
11935
|
Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
|
|
|
11936
|
|
|
|
11937
|
\see addAxis
|
|
|
11938
|
*/
|
|
|
11939
|
bool QCPAxisRect::removeAxis(QCPAxis *axis)
|
|
|
11940
|
{
|
|
|
11941
|
// don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
|
|
|
11942
|
QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
|
|
|
11943
|
while (it.hasNext())
|
|
|
11944
|
{
|
|
|
11945
|
it.next();
|
|
|
11946
|
if (it.value().contains(axis))
|
|
|
11947
|
{
|
|
|
11948
|
mAxes[it.key()].removeOne(axis);
|
|
|
11949
|
if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
|
|
|
11950
|
parentPlot()->axisRemoved(axis);
|
|
|
11951
|
delete axis;
|
|
|
11952
|
return true;
|
|
|
11953
|
}
|
|
|
11954
|
}
|
|
|
11955
|
qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
|
|
|
11956
|
return false;
|
|
|
11957
|
}
|
|
|
11958
|
|
|
|
11959
|
/*!
|
|
|
11960
|
Convenience function to create an axis on each side that doesn't have any axes yet and set their
|
|
|
11961
|
visibility to true. Further, the top/right axes are assigned the following properties of the
|
|
|
11962
|
bottom/left axes:
|
|
|
11963
|
|
|
|
11964
|
\li range (\ref QCPAxis::setRange)
|
|
|
11965
|
\li range reversed (\ref QCPAxis::setRangeReversed)
|
|
|
11966
|
\li scale type (\ref QCPAxis::setScaleType)
|
|
|
11967
|
\li scale log base (\ref QCPAxis::setScaleLogBase)
|
|
|
11968
|
\li ticks (\ref QCPAxis::setTicks)
|
|
|
11969
|
\li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
|
|
|
11970
|
\li sub tick count (\ref QCPAxis::setSubTickCount)
|
|
|
11971
|
\li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
|
|
|
11972
|
\li tick step (\ref QCPAxis::setTickStep)
|
|
|
11973
|
\li auto tick step (\ref QCPAxis::setAutoTickStep)
|
|
|
11974
|
\li number format (\ref QCPAxis::setNumberFormat)
|
|
|
11975
|
\li number precision (\ref QCPAxis::setNumberPrecision)
|
|
|
11976
|
\li tick label type (\ref QCPAxis::setTickLabelType)
|
|
|
11977
|
\li date time format (\ref QCPAxis::setDateTimeFormat)
|
|
|
11978
|
\li date time spec (\ref QCPAxis::setDateTimeSpec)
|
|
|
11979
|
|
|
|
11980
|
Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
|
|
|
11981
|
|
|
|
11982
|
If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
|
|
|
11983
|
and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
|
|
|
11984
|
*/
|
|
|
11985
|
void QCPAxisRect::setupFullAxesBox(bool connectRanges)
|
|
|
11986
|
{
|
|
|
11987
|
QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
|
|
|
11988
|
if (axisCount(QCPAxis::atBottom) == 0)
|
|
|
11989
|
xAxis = addAxis(QCPAxis::atBottom);
|
|
|
11990
|
else
|
|
|
11991
|
xAxis = axis(QCPAxis::atBottom);
|
|
|
11992
|
|
|
|
11993
|
if (axisCount(QCPAxis::atLeft) == 0)
|
|
|
11994
|
yAxis = addAxis(QCPAxis::atLeft);
|
|
|
11995
|
else
|
|
|
11996
|
yAxis = axis(QCPAxis::atLeft);
|
|
|
11997
|
|
|
|
11998
|
if (axisCount(QCPAxis::atTop) == 0)
|
|
|
11999
|
xAxis2 = addAxis(QCPAxis::atTop);
|
|
|
12000
|
else
|
|
|
12001
|
xAxis2 = axis(QCPAxis::atTop);
|
|
|
12002
|
|
|
|
12003
|
if (axisCount(QCPAxis::atRight) == 0)
|
|
|
12004
|
yAxis2 = addAxis(QCPAxis::atRight);
|
|
|
12005
|
else
|
|
|
12006
|
yAxis2 = axis(QCPAxis::atRight);
|
|
|
12007
|
|
|
|
12008
|
xAxis->setVisible(true);
|
|
|
12009
|
yAxis->setVisible(true);
|
|
|
12010
|
xAxis2->setVisible(true);
|
|
|
12011
|
yAxis2->setVisible(true);
|
|
|
12012
|
xAxis2->setTickLabels(false);
|
|
|
12013
|
yAxis2->setTickLabels(false);
|
|
|
12014
|
|
|
|
12015
|
xAxis2->setRange(xAxis->range());
|
|
|
12016
|
xAxis2->setRangeReversed(xAxis->rangeReversed());
|
|
|
12017
|
xAxis2->setScaleType(xAxis->scaleType());
|
|
|
12018
|
xAxis2->setScaleLogBase(xAxis->scaleLogBase());
|
|
|
12019
|
xAxis2->setTicks(xAxis->ticks());
|
|
|
12020
|
xAxis2->setAutoTickCount(xAxis->autoTickCount());
|
|
|
12021
|
xAxis2->setSubTickCount(xAxis->subTickCount());
|
|
|
12022
|
xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
|
|
|
12023
|
xAxis2->setTickStep(xAxis->tickStep());
|
|
|
12024
|
xAxis2->setAutoTickStep(xAxis->autoTickStep());
|
|
|
12025
|
xAxis2->setNumberFormat(xAxis->numberFormat());
|
|
|
12026
|
xAxis2->setNumberPrecision(xAxis->numberPrecision());
|
|
|
12027
|
xAxis2->setTickLabelType(xAxis->tickLabelType());
|
|
|
12028
|
xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
|
|
|
12029
|
xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
|
|
|
12030
|
|
|
|
12031
|
yAxis2->setRange(yAxis->range());
|
|
|
12032
|
yAxis2->setRangeReversed(yAxis->rangeReversed());
|
|
|
12033
|
yAxis2->setScaleType(yAxis->scaleType());
|
|
|
12034
|
yAxis2->setScaleLogBase(yAxis->scaleLogBase());
|
|
|
12035
|
yAxis2->setTicks(yAxis->ticks());
|
|
|
12036
|
yAxis2->setAutoTickCount(yAxis->autoTickCount());
|
|
|
12037
|
yAxis2->setSubTickCount(yAxis->subTickCount());
|
|
|
12038
|
yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
|
|
|
12039
|
yAxis2->setTickStep(yAxis->tickStep());
|
|
|
12040
|
yAxis2->setAutoTickStep(yAxis->autoTickStep());
|
|
|
12041
|
yAxis2->setNumberFormat(yAxis->numberFormat());
|
|
|
12042
|
yAxis2->setNumberPrecision(yAxis->numberPrecision());
|
|
|
12043
|
yAxis2->setTickLabelType(yAxis->tickLabelType());
|
|
|
12044
|
yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
|
|
|
12045
|
yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
|
|
|
12046
|
|
|
|
12047
|
if (connectRanges)
|
|
|
12048
|
{
|
|
|
12049
|
connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
|
|
|
12050
|
connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
|
|
|
12051
|
}
|
|
|
12052
|
}
|
|
|
12053
|
|
|
|
12054
|
/*!
|
|
|
12055
|
Returns a list of all the plottables that are associated with this axis rect.
|
|
|
12056
|
|
|
|
12057
|
A plottable is considered associated with an axis rect if its key or value axis (or both) is in
|
|
|
12058
|
this axis rect.
|
|
|
12059
|
|
|
|
12060
|
\see graphs, items
|
|
|
12061
|
*/
|
|
|
12062
|
QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
|
|
|
12063
|
{
|
|
|
12064
|
// Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
|
|
|
12065
|
QList<QCPAbstractPlottable*> result;
|
|
|
12066
|
for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
|
|
|
12067
|
{
|
|
|
12068
|
if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
|
|
|
12069
|
result.append(mParentPlot->mPlottables.at(i));
|
|
|
12070
|
}
|
|
|
12071
|
return result;
|
|
|
12072
|
}
|
|
|
12073
|
|
|
|
12074
|
/*!
|
|
|
12075
|
Returns a list of all the graphs that are associated with this axis rect.
|
|
|
12076
|
|
|
|
12077
|
A graph is considered associated with an axis rect if its key or value axis (or both) is in
|
|
|
12078
|
this axis rect.
|
|
|
12079
|
|
|
|
12080
|
\see plottables, items
|
|
|
12081
|
*/
|
|
|
12082
|
QList<QCPGraph*> QCPAxisRect::graphs() const
|
|
|
12083
|
{
|
|
|
12084
|
// Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
|
|
|
12085
|
QList<QCPGraph*> result;
|
|
|
12086
|
for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
|
|
|
12087
|
{
|
|
|
12088
|
if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
|
|
|
12089
|
result.append(mParentPlot->mGraphs.at(i));
|
|
|
12090
|
}
|
|
|
12091
|
return result;
|
|
|
12092
|
}
|
|
|
12093
|
|
|
|
12094
|
/*!
|
|
|
12095
|
Returns a list of all the items that are associated with this axis rect.
|
|
|
12096
|
|
|
|
12097
|
An item is considered associated with an axis rect if any of its positions has key or value axis
|
|
|
12098
|
set to an axis that is in this axis rect, or if any of its positions has \ref
|
|
|
12099
|
QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
|
|
|
12100
|
QCPAbstractItem::setClipAxisRect) is set to this axis rect.
|
|
|
12101
|
|
|
|
12102
|
\see plottables, graphs
|
|
|
12103
|
*/
|
|
|
12104
|
QList<QCPAbstractItem *> QCPAxisRect::items() const
|
|
|
12105
|
{
|
|
|
12106
|
// Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
|
|
|
12107
|
// and miss those items that have this axis rect as clipAxisRect.
|
|
|
12108
|
QList<QCPAbstractItem*> result;
|
|
|
12109
|
for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
|
|
|
12110
|
{
|
|
|
12111
|
if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
|
|
|
12112
|
{
|
|
|
12113
|
result.append(mParentPlot->mItems.at(itemId));
|
|
|
12114
|
continue;
|
|
|
12115
|
}
|
|
|
12116
|
QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
|
|
|
12117
|
for (int posId=0; posId<positions.size(); ++posId)
|
|
|
12118
|
{
|
|
|
12119
|
if (positions.at(posId)->axisRect() == this ||
|
|
|
12120
|
positions.at(posId)->keyAxis()->axisRect() == this ||
|
|
|
12121
|
positions.at(posId)->valueAxis()->axisRect() == this)
|
|
|
12122
|
{
|
|
|
12123
|
result.append(mParentPlot->mItems.at(itemId));
|
|
|
12124
|
break;
|
|
|
12125
|
}
|
|
|
12126
|
}
|
|
|
12127
|
}
|
|
|
12128
|
return result;
|
|
|
12129
|
}
|
|
|
12130
|
|
|
|
12131
|
/*!
|
|
|
12132
|
This method is called automatically upon replot and doesn't need to be called by users of
|
|
|
12133
|
QCPAxisRect.
|
|
|
12134
|
|
|
|
12135
|
Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
|
|
|
12136
|
and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
|
|
|
12137
|
QCPInsetLayout::update function.
|
|
|
12138
|
*/
|
|
|
12139
|
void QCPAxisRect::update(UpdatePhase phase)
|
|
|
12140
|
{
|
|
|
12141
|
QCPLayoutElement::update(phase);
|
|
|
12142
|
|
|
|
12143
|
switch (phase)
|
|
|
12144
|
{
|
|
|
12145
|
case upPreparation:
|
|
|
12146
|
{
|
|
|
12147
|
QList<QCPAxis*> allAxes = axes();
|
|
|
12148
|
for (int i=0; i<allAxes.size(); ++i)
|
|
|
12149
|
allAxes.at(i)->setupTickVectors();
|
|
|
12150
|
break;
|
|
|
12151
|
}
|
|
|
12152
|
case upLayout:
|
|
|
12153
|
{
|
|
|
12154
|
mInsetLayout->setOuterRect(rect());
|
|
|
12155
|
break;
|
|
|
12156
|
}
|
|
|
12157
|
default: break;
|
|
|
12158
|
}
|
|
|
12159
|
|
|
|
12160
|
// pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
|
|
|
12161
|
mInsetLayout->update(phase);
|
|
|
12162
|
}
|
|
|
12163
|
|
|
|
12164
|
/* inherits documentation from base class */
|
|
|
12165
|
QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
|
|
|
12166
|
{
|
|
|
12167
|
QList<QCPLayoutElement*> result;
|
|
|
12168
|
if (mInsetLayout)
|
|
|
12169
|
{
|
|
|
12170
|
result << mInsetLayout;
|
|
|
12171
|
if (recursive)
|
|
|
12172
|
result << mInsetLayout->elements(recursive);
|
|
|
12173
|
}
|
|
|
12174
|
return result;
|
|
|
12175
|
}
|
|
|
12176
|
|
|
|
12177
|
/* inherits documentation from base class */
|
|
|
12178
|
void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
|
12179
|
{
|
|
|
12180
|
painter->setAntialiasing(false);
|
|
|
12181
|
}
|
|
|
12182
|
|
|
|
12183
|
/* inherits documentation from base class */
|
|
|
12184
|
void QCPAxisRect::draw(QCPPainter *painter)
|
|
|
12185
|
{
|
|
|
12186
|
drawBackground(painter);
|
|
|
12187
|
}
|
|
|
12188
|
|
|
|
12189
|
/*!
|
|
|
12190
|
Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
|
|
|
12191
|
axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
|
|
|
12192
|
backgrounds are usually drawn below everything else.
|
|
|
12193
|
|
|
|
12194
|
For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
|
|
|
12195
|
enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
|
|
|
12196
|
is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
|
|
|
12197
|
consider using the overloaded version of this function.
|
|
|
12198
|
|
|
|
12199
|
Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
|
|
|
12200
|
setBackground(const QBrush &brush).
|
|
|
12201
|
|
|
|
12202
|
\see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
|
|
|
12203
|
*/
|
|
|
12204
|
void QCPAxisRect::setBackground(const QPixmap &pm)
|
|
|
12205
|
{
|
|
|
12206
|
mBackgroundPixmap = pm;
|
|
|
12207
|
mScaledBackgroundPixmap = QPixmap();
|
|
|
12208
|
}
|
|
|
12209
|
|
|
|
12210
|
/*! \overload
|
|
|
12211
|
|
|
|
12212
|
Sets \a brush as the background brush. The axis rect background will be filled with this brush.
|
|
|
12213
|
Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
|
|
|
12214
|
are usually drawn below everything else.
|
|
|
12215
|
|
|
|
12216
|
The brush will be drawn before (under) any background pixmap, which may be specified with \ref
|
|
|
12217
|
setBackground(const QPixmap &pm).
|
|
|
12218
|
|
|
|
12219
|
To disable drawing of a background brush, set \a brush to Qt::NoBrush.
|
|
|
12220
|
|
|
|
12221
|
\see setBackground(const QPixmap &pm)
|
|
|
12222
|
*/
|
|
|
12223
|
void QCPAxisRect::setBackground(const QBrush &brush)
|
|
|
12224
|
{
|
|
|
12225
|
mBackgroundBrush = brush;
|
|
|
12226
|
}
|
|
|
12227
|
|
|
|
12228
|
/*! \overload
|
|
|
12229
|
|
|
|
12230
|
Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
|
|
|
12231
|
shall be scaled in one call.
|
|
|
12232
|
|
|
|
12233
|
\see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
|
|
|
12234
|
*/
|
|
|
12235
|
void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
|
|
|
12236
|
{
|
|
|
12237
|
mBackgroundPixmap = pm;
|
|
|
12238
|
mScaledBackgroundPixmap = QPixmap();
|
|
|
12239
|
mBackgroundScaled = scaled;
|
|
|
12240
|
mBackgroundScaledMode = mode;
|
|
|
12241
|
}
|
|
|
12242
|
|
|
|
12243
|
/*!
|
|
|
12244
|
Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
|
|
|
12245
|
is set to true, you may control whether and how the aspect ratio of the original pixmap is
|
|
|
12246
|
preserved with \ref setBackgroundScaledMode.
|
|
|
12247
|
|
|
|
12248
|
Note that the scaled version of the original pixmap is buffered, so there is no performance
|
|
|
12249
|
penalty on replots. (Except when the axis rect dimensions are changed continuously.)
|
|
|
12250
|
|
|
|
12251
|
\see setBackground, setBackgroundScaledMode
|
|
|
12252
|
*/
|
|
|
12253
|
void QCPAxisRect::setBackgroundScaled(bool scaled)
|
|
|
12254
|
{
|
|
|
12255
|
mBackgroundScaled = scaled;
|
|
|
12256
|
}
|
|
|
12257
|
|
|
|
12258
|
/*!
|
|
|
12259
|
If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
|
|
|
12260
|
define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
|
|
|
12261
|
\see setBackground, setBackgroundScaled
|
|
|
12262
|
*/
|
|
|
12263
|
void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
|
|
|
12264
|
{
|
|
|
12265
|
mBackgroundScaledMode = mode;
|
|
|
12266
|
}
|
|
|
12267
|
|
|
|
12268
|
/*!
|
|
|
12269
|
Returns the range drag axis of the \a orientation provided.
|
|
|
12270
|
|
|
|
12271
|
\see setRangeDragAxes
|
|
|
12272
|
*/
|
|
|
12273
|
QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
|
|
|
12274
|
{
|
|
|
12275
|
return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
|
|
|
12276
|
}
|
|
|
12277
|
|
|
|
12278
|
/*!
|
|
|
12279
|
Returns the range zoom axis of the \a orientation provided.
|
|
|
12280
|
|
|
|
12281
|
\see setRangeZoomAxes
|
|
|
12282
|
*/
|
|
|
12283
|
QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
|
|
|
12284
|
{
|
|
|
12285
|
return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
|
|
|
12286
|
}
|
|
|
12287
|
|
|
|
12288
|
/*!
|
|
|
12289
|
Returns the range zoom factor of the \a orientation provided.
|
|
|
12290
|
|
|
|
12291
|
\see setRangeZoomFactor
|
|
|
12292
|
*/
|
|
|
12293
|
double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
|
|
|
12294
|
{
|
|
|
12295
|
return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
|
|
|
12296
|
}
|
|
|
12297
|
|
|
|
12298
|
/*!
|
|
|
12299
|
Sets which axis orientation may be range dragged by the user with mouse interaction.
|
|
|
12300
|
What orientation corresponds to which specific axis can be set with
|
|
|
12301
|
\ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
|
|
|
12302
|
default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
|
|
|
12303
|
is the left axis (yAxis).
|
|
|
12304
|
|
|
|
12305
|
To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
|
|
|
12306
|
QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
|
|
|
12307
|
Qt::Vertical</tt> as \a orientations.
|
|
|
12308
|
|
|
|
12309
|
In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
|
|
|
12310
|
contains \ref QCP::iRangeDrag to enable the range dragging interaction.
|
|
|
12311
|
|
|
|
12312
|
\see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
|
|
|
12313
|
*/
|
|
|
12314
|
void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
|
|
|
12315
|
{
|
|
|
12316
|
mRangeDrag = orientations;
|
|
|
12317
|
}
|
|
|
12318
|
|
|
|
12319
|
/*!
|
|
|
12320
|
Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
|
|
|
12321
|
corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
|
|
|
12322
|
QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
|
|
|
12323
|
axis is the left axis (yAxis).
|
|
|
12324
|
|
|
|
12325
|
To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
|
|
|
12326
|
QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
|
|
|
12327
|
Qt::Vertical</tt> as \a orientations.
|
|
|
12328
|
|
|
|
12329
|
In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
|
|
|
12330
|
contains \ref QCP::iRangeZoom to enable the range zooming interaction.
|
|
|
12331
|
|
|
|
12332
|
\see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
|
|
|
12333
|
*/
|
|
|
12334
|
void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
|
|
|
12335
|
{
|
|
|
12336
|
mRangeZoom = orientations;
|
|
|
12337
|
}
|
|
|
12338
|
|
|
|
12339
|
/*!
|
|
|
12340
|
Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
|
|
|
12341
|
on the QCustomPlot widget.
|
|
|
12342
|
|
|
|
12343
|
\see setRangeZoomAxes
|
|
|
12344
|
*/
|
|
|
12345
|
void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
|
|
|
12346
|
{
|
|
|
12347
|
mRangeDragHorzAxis = horizontal;
|
|
|
12348
|
mRangeDragVertAxis = vertical;
|
|
|
12349
|
}
|
|
|
12350
|
|
|
|
12351
|
/*!
|
|
|
12352
|
Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
|
|
|
12353
|
QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
|
|
|
12354
|
are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
|
|
|
12355
|
|
|
|
12356
|
\see setRangeDragAxes
|
|
|
12357
|
*/
|
|
|
12358
|
void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
|
|
|
12359
|
{
|
|
|
12360
|
mRangeZoomHorzAxis = horizontal;
|
|
|
12361
|
mRangeZoomVertAxis = vertical;
|
|
|
12362
|
}
|
|
|
12363
|
|
|
|
12364
|
/*!
|
|
|
12365
|
Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
|
|
|
12366
|
\ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
|
|
|
12367
|
let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
|
|
|
12368
|
and which is vertical, can be set with \ref setRangeZoomAxes.
|
|
|
12369
|
|
|
|
12370
|
When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
|
|
|
12371
|
will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
|
|
|
12372
|
same scrolling direction will zoom out.
|
|
|
12373
|
*/
|
|
|
12374
|
void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
|
|
|
12375
|
{
|
|
|
12376
|
mRangeZoomFactorHorz = horizontalFactor;
|
|
|
12377
|
mRangeZoomFactorVert = verticalFactor;
|
|
|
12378
|
}
|
|
|
12379
|
|
|
|
12380
|
/*! \overload
|
|
|
12381
|
|
|
|
12382
|
Sets both the horizontal and vertical zoom \a factor.
|
|
|
12383
|
*/
|
|
|
12384
|
void QCPAxisRect::setRangeZoomFactor(double factor)
|
|
|
12385
|
{
|
|
|
12386
|
mRangeZoomFactorHorz = factor;
|
|
|
12387
|
mRangeZoomFactorVert = factor;
|
|
|
12388
|
}
|
|
|
12389
|
|
|
|
12390
|
/*! \internal
|
|
|
12391
|
|
|
|
12392
|
Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
|
|
|
12393
|
pixmap.
|
|
|
12394
|
|
|
|
12395
|
If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
|
|
|
12396
|
according filling inside the axis rect with the provided \a painter.
|
|
|
12397
|
|
|
|
12398
|
Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
|
|
|
12399
|
depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
|
|
|
12400
|
the axis rect with the provided \a painter. The scaled version is buffered in
|
|
|
12401
|
mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
|
|
|
12402
|
the axis rect has changed in a way that requires a rescale of the background pixmap (this is
|
|
|
12403
|
dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
|
|
|
12404
|
set.
|
|
|
12405
|
|
|
|
12406
|
\see setBackground, setBackgroundScaled, setBackgroundScaledMode
|
|
|
12407
|
*/
|
|
|
12408
|
void QCPAxisRect::drawBackground(QCPPainter *painter)
|
|
|
12409
|
{
|
|
|
12410
|
// draw background fill:
|
|
|
12411
|
if (mBackgroundBrush != Qt::NoBrush)
|
|
|
12412
|
painter->fillRect(mRect, mBackgroundBrush);
|
|
|
12413
|
|
|
|
12414
|
// draw background pixmap (on top of fill, if brush specified):
|
|
|
12415
|
if (!mBackgroundPixmap.isNull())
|
|
|
12416
|
{
|
|
|
12417
|
if (mBackgroundScaled)
|
|
|
12418
|
{
|
|
|
12419
|
// check whether mScaledBackground needs to be updated:
|
|
|
12420
|
QSize scaledSize(mBackgroundPixmap.size());
|
|
|
12421
|
scaledSize.scale(mRect.size(), mBackgroundScaledMode);
|
|
|
12422
|
if (mScaledBackgroundPixmap.size() != scaledSize)
|
|
|
12423
|
mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
|
|
|
12424
|
painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
|
|
|
12425
|
} else
|
|
|
12426
|
{
|
|
|
12427
|
painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
|
|
|
12428
|
}
|
|
|
12429
|
}
|
|
|
12430
|
}
|
|
|
12431
|
|
|
|
12432
|
/*! \internal
|
|
|
12433
|
|
|
|
12434
|
This function makes sure multiple axes on the side specified with \a type don't collide, but are
|
|
|
12435
|
distributed according to their respective space requirement (QCPAxis::calculateMargin).
|
|
|
12436
|
|
|
|
12437
|
It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
|
|
|
12438
|
one with index zero.
|
|
|
12439
|
|
|
|
12440
|
This function is called by \ref calculateAutoMargin.
|
|
|
12441
|
*/
|
|
|
12442
|
void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
|
|
|
12443
|
{
|
|
|
12444
|
const QList<QCPAxis*> axesList = mAxes.value(type);
|
|
|
12445
|
if (axesList.isEmpty())
|
|
|
12446
|
return;
|
|
|
12447
|
|
|
|
12448
|
bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
|
|
|
12449
|
for (int i=1; i<axesList.size(); ++i)
|
|
|
12450
|
{
|
|
|
12451
|
int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
|
|
|
12452
|
if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
|
|
|
12453
|
{
|
|
|
12454
|
if (!isFirstVisible)
|
|
|
12455
|
offset += axesList.at(i)->tickLengthIn();
|
|
|
12456
|
isFirstVisible = false;
|
|
|
12457
|
}
|
|
|
12458
|
axesList.at(i)->setOffset(offset);
|
|
|
12459
|
}
|
|
|
12460
|
}
|
|
|
12461
|
|
|
|
12462
|
/* inherits documentation from base class */
|
|
|
12463
|
int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
|
|
|
12464
|
{
|
|
|
12465
|
if (!mAutoMargins.testFlag(side))
|
|
|
12466
|
qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
|
|
|
12467
|
|
|
|
12468
|
updateAxesOffset(QCPAxis::marginSideToAxisType(side));
|
|
|
12469
|
|
|
|
12470
|
// note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
|
|
|
12471
|
const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
|
|
|
12472
|
if (axesList.size() > 0)
|
|
|
12473
|
return axesList.last()->offset() + axesList.last()->calculateMargin();
|
|
|
12474
|
else
|
|
|
12475
|
return 0;
|
|
|
12476
|
}
|
|
|
12477
|
|
|
|
12478
|
/*! \internal
|
|
|
12479
|
|
|
|
12480
|
Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
|
|
|
12481
|
pressed, the range dragging interaction is initialized (the actual range manipulation happens in
|
|
|
12482
|
the \ref mouseMoveEvent).
|
|
|
12483
|
|
|
|
12484
|
The mDragging flag is set to true and some anchor points are set that are needed to determine the
|
|
|
12485
|
distance the mouse was dragged in the mouse move/release events later.
|
|
|
12486
|
|
|
|
12487
|
\see mouseMoveEvent, mouseReleaseEvent
|
|
|
12488
|
*/
|
|
|
12489
|
void QCPAxisRect::mousePressEvent(QMouseEvent *event)
|
|
|
12490
|
{
|
|
|
12491
|
mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
|
|
|
12492
|
if (event->buttons() & Qt::LeftButton)
|
|
|
12493
|
{
|
|
|
12494
|
mDragging = true;
|
|
|
12495
|
// initialize antialiasing backup in case we start dragging:
|
|
|
12496
|
if (mParentPlot->noAntialiasingOnDrag())
|
|
|
12497
|
{
|
|
|
12498
|
mAADragBackup = mParentPlot->antialiasedElements();
|
|
|
12499
|
mNotAADragBackup = mParentPlot->notAntialiasedElements();
|
|
|
12500
|
}
|
|
|
12501
|
// Mouse range dragging interaction:
|
|
|
12502
|
if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
|
|
|
12503
|
{
|
|
|
12504
|
if (mRangeDragHorzAxis)
|
|
|
12505
|
mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
|
|
|
12506
|
if (mRangeDragVertAxis)
|
|
|
12507
|
mDragStartVertRange = mRangeDragVertAxis.data()->range();
|
|
|
12508
|
}
|
|
|
12509
|
}
|
|
|
12510
|
}
|
|
|
12511
|
|
|
|
12512
|
/*! \internal
|
|
|
12513
|
|
|
|
12514
|
Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
|
|
|
12515
|
preceding \ref mousePressEvent, the range is moved accordingly.
|
|
|
12516
|
|
|
|
12517
|
\see mousePressEvent, mouseReleaseEvent
|
|
|
12518
|
*/
|
|
|
12519
|
void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
|
|
|
12520
|
{
|
|
|
12521
|
// Mouse range dragging interaction:
|
|
|
12522
|
if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
|
|
|
12523
|
{
|
|
|
12524
|
if (mRangeDrag.testFlag(Qt::Horizontal))
|
|
|
12525
|
{
|
|
|
12526
|
if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
|
|
|
12527
|
{
|
|
|
12528
|
if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
|
|
|
12529
|
{
|
|
|
12530
|
double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
|
|
|
12531
|
rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
|
|
|
12532
|
} else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
|
|
|
12533
|
{
|
|
|
12534
|
double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
|
|
|
12535
|
rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
|
|
|
12536
|
}
|
|
|
12537
|
}
|
|
|
12538
|
}
|
|
|
12539
|
if (mRangeDrag.testFlag(Qt::Vertical))
|
|
|
12540
|
{
|
|
|
12541
|
if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
|
|
|
12542
|
{
|
|
|
12543
|
if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
|
|
|
12544
|
{
|
|
|
12545
|
double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
|
|
|
12546
|
rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
|
|
|
12547
|
} else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
|
|
|
12548
|
{
|
|
|
12549
|
double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
|
|
|
12550
|
rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
|
|
|
12551
|
}
|
|
|
12552
|
}
|
|
|
12553
|
}
|
|
|
12554
|
if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
|
|
|
12555
|
{
|
|
|
12556
|
if (mParentPlot->noAntialiasingOnDrag())
|
|
|
12557
|
mParentPlot->setNotAntialiasedElements(QCP::aeAll);
|
|
|
12558
|
mParentPlot->replot();
|
|
|
12559
|
}
|
|
|
12560
|
}
|
|
|
12561
|
}
|
|
|
12562
|
|
|
|
12563
|
/* inherits documentation from base class */
|
|
|
12564
|
void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
|
|
|
12565
|
{
|
|
|
12566
|
Q_UNUSED(event)
|
|
|
12567
|
mDragging = false;
|
|
|
12568
|
if (mParentPlot->noAntialiasingOnDrag())
|
|
|
12569
|
{
|
|
|
12570
|
mParentPlot->setAntialiasedElements(mAADragBackup);
|
|
|
12571
|
mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
|
|
|
12572
|
}
|
|
|
12573
|
}
|
|
|
12574
|
|
|
|
12575
|
/*! \internal
|
|
|
12576
|
|
|
|
12577
|
Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
|
|
|
12578
|
ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
|
|
|
12579
|
the scaling operation is the current cursor position inside the axis rect. The scaling factor is
|
|
|
12580
|
dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
|
|
|
12581
|
zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
|
|
|
12582
|
|
|
|
12583
|
Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
|
|
|
12584
|
wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
|
|
|
12585
|
multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
|
|
|
12586
|
exponent of the range zoom factor. This takes care of the wheel direction automatically, by
|
|
|
12587
|
inverting the factor, when the wheel step is negative (f^-1 = 1/f).
|
|
|
12588
|
*/
|
|
|
12589
|
void QCPAxisRect::wheelEvent(QWheelEvent *event)
|
|
|
12590
|
{
|
|
|
12591
|
// Mouse range zooming interaction:
|
|
|
12592
|
if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
|
|
|
12593
|
{
|
|
|
12594
|
if (mRangeZoom != 0)
|
|
|
12595
|
{
|
|
|
12596
|
double factor;
|
|
|
12597
|
double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
|
|
|
12598
|
if (mRangeZoom.testFlag(Qt::Horizontal))
|
|
|
12599
|
{
|
|
|
12600
|
factor = pow(mRangeZoomFactorHorz, wheelSteps);
|
|
|
12601
|
if (mRangeZoomHorzAxis.data())
|
|
|
12602
|
mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
|
|
|
12603
|
}
|
|
|
12604
|
if (mRangeZoom.testFlag(Qt::Vertical))
|
|
|
12605
|
{
|
|
|
12606
|
factor = pow(mRangeZoomFactorVert, wheelSteps);
|
|
|
12607
|
if (mRangeZoomVertAxis.data())
|
|
|
12608
|
mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
|
|
|
12609
|
}
|
|
|
12610
|
mParentPlot->replot();
|
|
|
12611
|
}
|
|
|
12612
|
}
|
|
|
12613
|
}
|
|
|
12614
|
|
|
|
12615
|
|
|
|
12616
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
12617
|
//////////////////// QCPAbstractLegendItem
|
|
|
12618
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
12619
|
|
|
|
12620
|
/*! \class QCPAbstractLegendItem
|
|
|
12621
|
\brief The abstract base class for all entries in a QCPLegend.
|
|
|
12622
|
|
|
|
12623
|
It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
|
|
|
12624
|
legend, the subclass \ref QCPPlottableLegendItem is more suitable.
|
|
|
12625
|
|
|
|
12626
|
Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
|
|
|
12627
|
that's not even associated with a plottable).
|
|
|
12628
|
|
|
|
12629
|
You must implement the following pure virtual functions:
|
|
|
12630
|
\li \ref draw (from QCPLayerable)
|
|
|
12631
|
|
|
|
12632
|
You inherit the following members you may use:
|
|
|
12633
|
<table>
|
|
|
12634
|
<tr>
|
|
|
12635
|
<td>QCPLegend *\b mParentLegend</td>
|
|
|
12636
|
<td>A pointer to the parent QCPLegend.</td>
|
|
|
12637
|
</tr><tr>
|
|
|
12638
|
<td>QFont \b mFont</td>
|
|
|
12639
|
<td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
|
|
|
12640
|
</tr>
|
|
|
12641
|
</table>
|
|
|
12642
|
*/
|
|
|
12643
|
|
|
|
12644
|
/* start of documentation of signals */
|
|
|
12645
|
|
|
|
12646
|
/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
|
|
|
12647
|
|
|
|
12648
|
This signal is emitted when the selection state of this legend item has changed, either by user
|
|
|
12649
|
interaction or by a direct call to \ref setSelected.
|
|
|
12650
|
*/
|
|
|
12651
|
|
|
|
12652
|
/* end of documentation of signals */
|
|
|
12653
|
|
|
|
12654
|
/*!
|
|
|
12655
|
Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
|
|
|
12656
|
cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
|
|
|
12657
|
*/
|
|
|
12658
|
QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
|
|
|
12659
|
QCPLayoutElement(parent->parentPlot()),
|
|
|
12660
|
mParentLegend(parent),
|
|
|
12661
|
mFont(parent->font()),
|
|
|
12662
|
mTextColor(parent->textColor()),
|
|
|
12663
|
mSelectedFont(parent->selectedFont()),
|
|
|
12664
|
mSelectedTextColor(parent->selectedTextColor()),
|
|
|
12665
|
mSelectable(true),
|
|
|
12666
|
mSelected(false)
|
|
|
12667
|
{
|
|
|
12668
|
setLayer("legend");
|
|
|
12669
|
setMargins(QMargins(8, 2, 8, 2));
|
|
|
12670
|
}
|
|
|
12671
|
|
|
|
12672
|
/*!
|
|
|
12673
|
Sets the default font of this specific legend item to \a font.
|
|
|
12674
|
|
|
|
12675
|
\see setTextColor, QCPLegend::setFont
|
|
|
12676
|
*/
|
|
|
12677
|
void QCPAbstractLegendItem::setFont(const QFont &font)
|
|
|
12678
|
{
|
|
|
12679
|
mFont = font;
|
|
|
12680
|
}
|
|
|
12681
|
|
|
|
12682
|
/*!
|
|
|
12683
|
Sets the default text color of this specific legend item to \a color.
|
|
|
12684
|
|
|
|
12685
|
\see setFont, QCPLegend::setTextColor
|
|
|
12686
|
*/
|
|
|
12687
|
void QCPAbstractLegendItem::setTextColor(const QColor &color)
|
|
|
12688
|
{
|
|
|
12689
|
mTextColor = color;
|
|
|
12690
|
}
|
|
|
12691
|
|
|
|
12692
|
/*!
|
|
|
12693
|
When this legend item is selected, \a font is used to draw generic text, instead of the normal
|
|
|
12694
|
font set with \ref setFont.
|
|
|
12695
|
|
|
|
12696
|
\see setFont, QCPLegend::setSelectedFont
|
|
|
12697
|
*/
|
|
|
12698
|
void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
|
|
|
12699
|
{
|
|
|
12700
|
mSelectedFont = font;
|
|
|
12701
|
}
|
|
|
12702
|
|
|
|
12703
|
/*!
|
|
|
12704
|
When this legend item is selected, \a color is used to draw generic text, instead of the normal
|
|
|
12705
|
color set with \ref setTextColor.
|
|
|
12706
|
|
|
|
12707
|
\see setTextColor, QCPLegend::setSelectedTextColor
|
|
|
12708
|
*/
|
|
|
12709
|
void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
|
|
|
12710
|
{
|
|
|
12711
|
mSelectedTextColor = color;
|
|
|
12712
|
}
|
|
|
12713
|
|
|
|
12714
|
/*!
|
|
|
12715
|
Sets whether this specific legend item is selectable.
|
|
|
12716
|
|
|
|
12717
|
\see setSelectedParts, QCustomPlot::setInteractions
|
|
|
12718
|
*/
|
|
|
12719
|
void QCPAbstractLegendItem::setSelectable(bool selectable)
|
|
|
12720
|
{
|
|
|
12721
|
if (mSelectable != selectable)
|
|
|
12722
|
{
|
|
|
12723
|
mSelectable = selectable;
|
|
|
12724
|
emit selectableChanged(mSelectable);
|
|
|
12725
|
}
|
|
|
12726
|
}
|
|
|
12727
|
|
|
|
12728
|
/*!
|
|
|
12729
|
Sets whether this specific legend item is selected.
|
|
|
12730
|
|
|
|
12731
|
It is possible to set the selection state of this item by calling this function directly, even if
|
|
|
12732
|
setSelectable is set to false.
|
|
|
12733
|
|
|
|
12734
|
\see setSelectableParts, QCustomPlot::setInteractions
|
|
|
12735
|
*/
|
|
|
12736
|
void QCPAbstractLegendItem::setSelected(bool selected)
|
|
|
12737
|
{
|
|
|
12738
|
if (mSelected != selected)
|
|
|
12739
|
{
|
|
|
12740
|
mSelected = selected;
|
|
|
12741
|
emit selectionChanged(mSelected);
|
|
|
12742
|
}
|
|
|
12743
|
}
|
|
|
12744
|
|
|
|
12745
|
/* inherits documentation from base class */
|
|
|
12746
|
double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
|
12747
|
{
|
|
|
12748
|
Q_UNUSED(details)
|
|
|
12749
|
if (!mParentPlot) return -1;
|
|
|
12750
|
if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
|
|
|
12751
|
return -1;
|
|
|
12752
|
|
|
|
12753
|
if (mRect.contains(pos.toPoint()))
|
|
|
12754
|
return mParentPlot->selectionTolerance()*0.99;
|
|
|
12755
|
else
|
|
|
12756
|
return -1;
|
|
|
12757
|
}
|
|
|
12758
|
|
|
|
12759
|
/* inherits documentation from base class */
|
|
|
12760
|
void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
|
12761
|
{
|
|
|
12762
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
|
|
|
12763
|
}
|
|
|
12764
|
|
|
|
12765
|
/* inherits documentation from base class */
|
|
|
12766
|
QRect QCPAbstractLegendItem::clipRect() const
|
|
|
12767
|
{
|
|
|
12768
|
return mOuterRect;
|
|
|
12769
|
}
|
|
|
12770
|
|
|
|
12771
|
/* inherits documentation from base class */
|
|
|
12772
|
void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
|
12773
|
{
|
|
|
12774
|
Q_UNUSED(event)
|
|
|
12775
|
Q_UNUSED(details)
|
|
|
12776
|
if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
|
|
|
12777
|
{
|
|
|
12778
|
bool selBefore = mSelected;
|
|
|
12779
|
setSelected(additive ? !mSelected : true);
|
|
|
12780
|
if (selectionStateChanged)
|
|
|
12781
|
*selectionStateChanged = mSelected != selBefore;
|
|
|
12782
|
}
|
|
|
12783
|
}
|
|
|
12784
|
|
|
|
12785
|
/* inherits documentation from base class */
|
|
|
12786
|
void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
|
|
|
12787
|
{
|
|
|
12788
|
if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
|
|
|
12789
|
{
|
|
|
12790
|
bool selBefore = mSelected;
|
|
|
12791
|
setSelected(false);
|
|
|
12792
|
if (selectionStateChanged)
|
|
|
12793
|
*selectionStateChanged = mSelected != selBefore;
|
|
|
12794
|
}
|
|
|
12795
|
}
|
|
|
12796
|
|
|
|
12797
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
12798
|
//////////////////// QCPPlottableLegendItem
|
|
|
12799
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
12800
|
|
|
|
12801
|
/*! \class QCPPlottableLegendItem
|
|
|
12802
|
\brief A legend item representing a plottable with an icon and the plottable name.
|
|
|
12803
|
|
|
|
12804
|
This is the standard legend item for plottables. It displays an icon of the plottable next to the
|
|
|
12805
|
plottable name. The icon is drawn by the respective plottable itself (\ref
|
|
|
12806
|
QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
|
|
|
12807
|
For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
|
|
|
12808
|
middle.
|
|
|
12809
|
|
|
|
12810
|
Legend items of this type are always associated with one plottable (retrievable via the
|
|
|
12811
|
plottable() function and settable with the constructor). You may change the font of the plottable
|
|
|
12812
|
name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
|
|
|
12813
|
QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
|
|
|
12814
|
|
|
|
12815
|
The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
|
|
|
12816
|
creates/removes legend items of this type in the default implementation. However, these functions
|
|
|
12817
|
may be reimplemented such that a different kind of legend item (e.g a direct subclass of
|
|
|
12818
|
QCPAbstractLegendItem) is used for that plottable.
|
|
|
12819
|
|
|
|
12820
|
Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
|
|
|
12821
|
QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
|
|
|
12822
|
interface, QCPLegend has specialized functions for handling legend items conveniently, see the
|
|
|
12823
|
documentation of \ref QCPLegend.
|
|
|
12824
|
*/
|
|
|
12825
|
|
|
|
12826
|
/*!
|
|
|
12827
|
Creates a new legend item associated with \a plottable.
|
|
|
12828
|
|
|
|
12829
|
Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
|
|
|
12830
|
|
|
|
12831
|
A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
|
|
|
12832
|
QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
|
|
|
12833
|
*/
|
|
|
12834
|
QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
|
|
|
12835
|
QCPAbstractLegendItem(parent),
|
|
|
12836
|
mPlottable(plottable)
|
|
|
12837
|
{
|
|
|
12838
|
}
|
|
|
12839
|
|
|
|
12840
|
/*! \internal
|
|
|
12841
|
|
|
|
12842
|
Returns the pen that shall be used to draw the icon border, taking into account the selection
|
|
|
12843
|
state of this item.
|
|
|
12844
|
*/
|
|
|
12845
|
QPen QCPPlottableLegendItem::getIconBorderPen() const
|
|
|
12846
|
{
|
|
|
12847
|
return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
|
|
|
12848
|
}
|
|
|
12849
|
|
|
|
12850
|
/*! \internal
|
|
|
12851
|
|
|
|
12852
|
Returns the text color that shall be used to draw text, taking into account the selection state
|
|
|
12853
|
of this item.
|
|
|
12854
|
*/
|
|
|
12855
|
QColor QCPPlottableLegendItem::getTextColor() const
|
|
|
12856
|
{
|
|
|
12857
|
return mSelected ? mSelectedTextColor : mTextColor;
|
|
|
12858
|
}
|
|
|
12859
|
|
|
|
12860
|
/*! \internal
|
|
|
12861
|
|
|
|
12862
|
Returns the font that shall be used to draw text, taking into account the selection state of this
|
|
|
12863
|
item.
|
|
|
12864
|
*/
|
|
|
12865
|
QFont QCPPlottableLegendItem::getFont() const
|
|
|
12866
|
{
|
|
|
12867
|
return mSelected ? mSelectedFont : mFont;
|
|
|
12868
|
}
|
|
|
12869
|
|
|
|
12870
|
/*! \internal
|
|
|
12871
|
|
|
|
12872
|
Draws the item with \a painter. The size and position of the drawn legend item is defined by the
|
|
|
12873
|
parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
|
|
|
12874
|
of this legend item.
|
|
|
12875
|
*/
|
|
|
12876
|
void QCPPlottableLegendItem::draw(QCPPainter *painter)
|
|
|
12877
|
{
|
|
|
12878
|
if (!mPlottable) return;
|
|
|
12879
|
painter->setFont(getFont());
|
|
|
12880
|
painter->setPen(QPen(getTextColor()));
|
|
|
12881
|
QSizeF iconSize = mParentLegend->iconSize();
|
|
|
12882
|
QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
|
|
|
12883
|
QRectF iconRect(mRect.topLeft(), iconSize);
|
|
|
12884
|
int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
|
|
|
12885
|
painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
|
|
|
12886
|
// draw icon:
|
|
|
12887
|
painter->save();
|
|
|
12888
|
painter->setClipRect(iconRect, Qt::IntersectClip);
|
|
|
12889
|
mPlottable->drawLegendIcon(painter, iconRect);
|
|
|
12890
|
painter->restore();
|
|
|
12891
|
// draw icon border:
|
|
|
12892
|
if (getIconBorderPen().style() != Qt::NoPen)
|
|
|
12893
|
{
|
|
|
12894
|
painter->setPen(getIconBorderPen());
|
|
|
12895
|
painter->setBrush(Qt::NoBrush);
|
|
|
12896
|
painter->drawRect(iconRect);
|
|
|
12897
|
}
|
|
|
12898
|
}
|
|
|
12899
|
|
|
|
12900
|
/*! \internal
|
|
|
12901
|
|
|
|
12902
|
Calculates and returns the size of this item. This includes the icon, the text and the padding in
|
|
|
12903
|
between.
|
|
|
12904
|
*/
|
|
|
12905
|
QSize QCPPlottableLegendItem::minimumSizeHint() const
|
|
|
12906
|
{
|
|
|
12907
|
if (!mPlottable) return QSize();
|
|
|
12908
|
QSize result(0, 0);
|
|
|
12909
|
QRect textRect;
|
|
|
12910
|
QFontMetrics fontMetrics(getFont());
|
|
|
12911
|
QSize iconSize = mParentLegend->iconSize();
|
|
|
12912
|
textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
|
|
|
12913
|
result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
|
|
|
12914
|
result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
|
|
|
12915
|
return result;
|
|
|
12916
|
}
|
|
|
12917
|
|
|
|
12918
|
|
|
|
12919
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
12920
|
//////////////////// QCPLegend
|
|
|
12921
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
12922
|
|
|
|
12923
|
/*! \class QCPLegend
|
|
|
12924
|
\brief Manages a legend inside a QCustomPlot.
|
|
|
12925
|
|
|
|
12926
|
A legend is a small box somewhere in the plot which lists plottables with their name and icon.
|
|
|
12927
|
|
|
|
12928
|
Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
|
|
|
12929
|
respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
|
|
|
12930
|
QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
|
|
|
12931
|
itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
|
|
|
12932
|
|
|
|
12933
|
The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
|
|
|
12934
|
QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
|
|
|
12935
|
placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
|
|
|
12936
|
handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
|
|
|
12937
|
other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
|
|
|
12938
|
However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
|
|
|
12939
|
only return the number of items with QCPAbstractLegendItems type).
|
|
|
12940
|
|
|
|
12941
|
By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
|
|
|
12942
|
layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
|
|
|
12943
|
position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
|
|
|
12944
|
outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
|
|
|
12945
|
*/
|
|
|
12946
|
|
|
|
12947
|
/* start of documentation of signals */
|
|
|
12948
|
|
|
|
12949
|
/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
|
|
|
12950
|
|
|
|
12951
|
This signal is emitted when the selection state of this legend has changed.
|
|
|
12952
|
|
|
|
12953
|
\see setSelectedParts, setSelectableParts
|
|
|
12954
|
*/
|
|
|
12955
|
|
|
|
12956
|
/* end of documentation of signals */
|
|
|
12957
|
|
|
|
12958
|
/*!
|
|
|
12959
|
Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
|
|
|
12960
|
|
|
|
12961
|
Note that by default, QCustomPlot already contains a legend ready to be used as
|
|
|
12962
|
QCustomPlot::legend
|
|
|
12963
|
*/
|
|
|
12964
|
QCPLegend::QCPLegend()
|
|
|
12965
|
{
|
|
|
12966
|
setRowSpacing(0);
|
|
|
12967
|
setColumnSpacing(10);
|
|
|
12968
|
setMargins(QMargins(2, 3, 2, 2));
|
|
|
12969
|
setAntialiased(false);
|
|
|
12970
|
setIconSize(32, 18);
|
|
|
12971
|
|
|
|
12972
|
setIconTextPadding(7);
|
|
|
12973
|
|
|
|
12974
|
setSelectableParts(spLegendBox | spItems);
|
|
|
12975
|
setSelectedParts(spNone);
|
|
|
12976
|
|
|
|
12977
|
setBorderPen(QPen(Qt::black));
|
|
|
12978
|
setSelectedBorderPen(QPen(Qt::blue, 2));
|
|
|
12979
|
setIconBorderPen(Qt::NoPen);
|
|
|
12980
|
setSelectedIconBorderPen(QPen(Qt::blue, 2));
|
|
|
12981
|
setBrush(Qt::white);
|
|
|
12982
|
setSelectedBrush(Qt::white);
|
|
|
12983
|
setTextColor(Qt::black);
|
|
|
12984
|
setSelectedTextColor(Qt::blue);
|
|
|
12985
|
}
|
|
|
12986
|
|
|
|
12987
|
QCPLegend::~QCPLegend()
|
|
|
12988
|
{
|
|
|
12989
|
clearItems();
|
|
|
12990
|
if (mParentPlot)
|
|
|
12991
|
mParentPlot->legendRemoved(this);
|
|
|
12992
|
}
|
|
|
12993
|
|
|
|
12994
|
/* no doc for getter, see setSelectedParts */
|
|
|
12995
|
QCPLegend::SelectableParts QCPLegend::selectedParts() const
|
|
|
12996
|
{
|
|
|
12997
|
// check whether any legend elements selected, if yes, add spItems to return value
|
|
|
12998
|
bool hasSelectedItems = false;
|
|
|
12999
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13000
|
{
|
|
|
13001
|
if (item(i) && item(i)->selected())
|
|
|
13002
|
{
|
|
|
13003
|
hasSelectedItems = true;
|
|
|
13004
|
break;
|
|
|
13005
|
}
|
|
|
13006
|
}
|
|
|
13007
|
if (hasSelectedItems)
|
|
|
13008
|
return mSelectedParts | spItems;
|
|
|
13009
|
else
|
|
|
13010
|
return mSelectedParts & ~spItems;
|
|
|
13011
|
}
|
|
|
13012
|
|
|
|
13013
|
/*!
|
|
|
13014
|
Sets the pen, the border of the entire legend is drawn with.
|
|
|
13015
|
*/
|
|
|
13016
|
void QCPLegend::setBorderPen(const QPen &pen)
|
|
|
13017
|
{
|
|
|
13018
|
mBorderPen = pen;
|
|
|
13019
|
}
|
|
|
13020
|
|
|
|
13021
|
/*!
|
|
|
13022
|
Sets the brush of the legend background.
|
|
|
13023
|
*/
|
|
|
13024
|
void QCPLegend::setBrush(const QBrush &brush)
|
|
|
13025
|
{
|
|
|
13026
|
mBrush = brush;
|
|
|
13027
|
}
|
|
|
13028
|
|
|
|
13029
|
/*!
|
|
|
13030
|
Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
|
|
|
13031
|
use this font by default. However, a different font can be specified on a per-item-basis by
|
|
|
13032
|
accessing the specific legend item.
|
|
|
13033
|
|
|
|
13034
|
This function will also set \a font on all already existing legend items.
|
|
|
13035
|
|
|
|
13036
|
\see QCPAbstractLegendItem::setFont
|
|
|
13037
|
*/
|
|
|
13038
|
void QCPLegend::setFont(const QFont &font)
|
|
|
13039
|
{
|
|
|
13040
|
mFont = font;
|
|
|
13041
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13042
|
{
|
|
|
13043
|
if (item(i))
|
|
|
13044
|
item(i)->setFont(mFont);
|
|
|
13045
|
}
|
|
|
13046
|
}
|
|
|
13047
|
|
|
|
13048
|
/*!
|
|
|
13049
|
Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
|
|
|
13050
|
will use this color by default. However, a different colors can be specified on a per-item-basis
|
|
|
13051
|
by accessing the specific legend item.
|
|
|
13052
|
|
|
|
13053
|
This function will also set \a color on all already existing legend items.
|
|
|
13054
|
|
|
|
13055
|
\see QCPAbstractLegendItem::setTextColor
|
|
|
13056
|
*/
|
|
|
13057
|
void QCPLegend::setTextColor(const QColor &color)
|
|
|
13058
|
{
|
|
|
13059
|
mTextColor = color;
|
|
|
13060
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13061
|
{
|
|
|
13062
|
if (item(i))
|
|
|
13063
|
item(i)->setTextColor(color);
|
|
|
13064
|
}
|
|
|
13065
|
}
|
|
|
13066
|
|
|
|
13067
|
/*!
|
|
|
13068
|
Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
|
|
|
13069
|
representation of the graph) will use this size by default.
|
|
|
13070
|
*/
|
|
|
13071
|
void QCPLegend::setIconSize(const QSize &size)
|
|
|
13072
|
{
|
|
|
13073
|
mIconSize = size;
|
|
|
13074
|
}
|
|
|
13075
|
|
|
|
13076
|
/*! \overload
|
|
|
13077
|
*/
|
|
|
13078
|
void QCPLegend::setIconSize(int width, int height)
|
|
|
13079
|
{
|
|
|
13080
|
mIconSize.setWidth(width);
|
|
|
13081
|
mIconSize.setHeight(height);
|
|
|
13082
|
}
|
|
|
13083
|
|
|
|
13084
|
/*!
|
|
|
13085
|
Sets the horizontal space in pixels between the legend icon and the text next to it.
|
|
|
13086
|
Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
|
|
|
13087
|
name of the graph) will use this space by default.
|
|
|
13088
|
*/
|
|
|
13089
|
void QCPLegend::setIconTextPadding(int padding)
|
|
|
13090
|
{
|
|
|
13091
|
mIconTextPadding = padding;
|
|
|
13092
|
}
|
|
|
13093
|
|
|
|
13094
|
/*!
|
|
|
13095
|
Sets the pen used to draw a border around each legend icon. Legend items that draw an
|
|
|
13096
|
icon (e.g. a visual representation of the graph) will use this pen by default.
|
|
|
13097
|
|
|
|
13098
|
If no border is wanted, set this to \a Qt::NoPen.
|
|
|
13099
|
*/
|
|
|
13100
|
void QCPLegend::setIconBorderPen(const QPen &pen)
|
|
|
13101
|
{
|
|
|
13102
|
mIconBorderPen = pen;
|
|
|
13103
|
}
|
|
|
13104
|
|
|
|
13105
|
/*!
|
|
|
13106
|
Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
|
|
|
13107
|
(When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
|
|
|
13108
|
|
|
|
13109
|
However, even when \a selectable is set to a value not allowing the selection of a specific part,
|
|
|
13110
|
it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
|
|
|
13111
|
directly.
|
|
|
13112
|
|
|
|
13113
|
\see SelectablePart, setSelectedParts
|
|
|
13114
|
*/
|
|
|
13115
|
void QCPLegend::setSelectableParts(const SelectableParts &selectable)
|
|
|
13116
|
{
|
|
|
13117
|
if (mSelectableParts != selectable)
|
|
|
13118
|
{
|
|
|
13119
|
mSelectableParts = selectable;
|
|
|
13120
|
emit selectableChanged(mSelectableParts);
|
|
|
13121
|
}
|
|
|
13122
|
}
|
|
|
13123
|
|
|
|
13124
|
/*!
|
|
|
13125
|
Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
|
|
|
13126
|
is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
|
|
|
13127
|
doesn't contain \ref spItems, those items become deselected.
|
|
|
13128
|
|
|
|
13129
|
The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
|
|
|
13130
|
contains iSelectLegend. You only need to call this function when you wish to change the selection
|
|
|
13131
|
state manually.
|
|
|
13132
|
|
|
|
13133
|
This function can change the selection state of a part even when \ref setSelectableParts was set to a
|
|
|
13134
|
value that actually excludes the part.
|
|
|
13135
|
|
|
|
13136
|
emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
|
|
|
13137
|
|
|
|
13138
|
Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
|
|
|
13139
|
before, because there's no way to specify which exact items to newly select. Do this by calling
|
|
|
13140
|
\ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
|
|
|
13141
|
|
|
|
13142
|
\see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
|
|
|
13143
|
setSelectedFont
|
|
|
13144
|
*/
|
|
|
13145
|
void QCPLegend::setSelectedParts(const SelectableParts &selected)
|
|
|
13146
|
{
|
|
|
13147
|
SelectableParts newSelected = selected;
|
|
|
13148
|
mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
|
|
|
13149
|
|
|
|
13150
|
if (mSelectedParts != newSelected)
|
|
|
13151
|
{
|
|
|
13152
|
if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
|
|
|
13153
|
{
|
|
|
13154
|
qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
|
|
|
13155
|
newSelected &= ~spItems;
|
|
|
13156
|
}
|
|
|
13157
|
if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
|
|
|
13158
|
{
|
|
|
13159
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13160
|
{
|
|
|
13161
|
if (item(i))
|
|
|
13162
|
item(i)->setSelected(false);
|
|
|
13163
|
}
|
|
|
13164
|
}
|
|
|
13165
|
mSelectedParts = newSelected;
|
|
|
13166
|
emit selectionChanged(mSelectedParts);
|
|
|
13167
|
}
|
|
|
13168
|
}
|
|
|
13169
|
|
|
|
13170
|
/*!
|
|
|
13171
|
When the legend box is selected, this pen is used to draw the border instead of the normal pen
|
|
|
13172
|
set via \ref setBorderPen.
|
|
|
13173
|
|
|
|
13174
|
\see setSelectedParts, setSelectableParts, setSelectedBrush
|
|
|
13175
|
*/
|
|
|
13176
|
void QCPLegend::setSelectedBorderPen(const QPen &pen)
|
|
|
13177
|
{
|
|
|
13178
|
mSelectedBorderPen = pen;
|
|
|
13179
|
}
|
|
|
13180
|
|
|
|
13181
|
/*!
|
|
|
13182
|
Sets the pen legend items will use to draw their icon borders, when they are selected.
|
|
|
13183
|
|
|
|
13184
|
\see setSelectedParts, setSelectableParts, setSelectedFont
|
|
|
13185
|
*/
|
|
|
13186
|
void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
|
|
|
13187
|
{
|
|
|
13188
|
mSelectedIconBorderPen = pen;
|
|
|
13189
|
}
|
|
|
13190
|
|
|
|
13191
|
/*!
|
|
|
13192
|
When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
|
|
|
13193
|
set via \ref setBrush.
|
|
|
13194
|
|
|
|
13195
|
\see setSelectedParts, setSelectableParts, setSelectedBorderPen
|
|
|
13196
|
*/
|
|
|
13197
|
void QCPLegend::setSelectedBrush(const QBrush &brush)
|
|
|
13198
|
{
|
|
|
13199
|
mSelectedBrush = brush;
|
|
|
13200
|
}
|
|
|
13201
|
|
|
|
13202
|
/*!
|
|
|
13203
|
Sets the default font that is used by legend items when they are selected.
|
|
|
13204
|
|
|
|
13205
|
This function will also set \a font on all already existing legend items.
|
|
|
13206
|
|
|
|
13207
|
\see setFont, QCPAbstractLegendItem::setSelectedFont
|
|
|
13208
|
*/
|
|
|
13209
|
void QCPLegend::setSelectedFont(const QFont &font)
|
|
|
13210
|
{
|
|
|
13211
|
mSelectedFont = font;
|
|
|
13212
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13213
|
{
|
|
|
13214
|
if (item(i))
|
|
|
13215
|
item(i)->setSelectedFont(font);
|
|
|
13216
|
}
|
|
|
13217
|
}
|
|
|
13218
|
|
|
|
13219
|
/*!
|
|
|
13220
|
Sets the default text color that is used by legend items when they are selected.
|
|
|
13221
|
|
|
|
13222
|
This function will also set \a color on all already existing legend items.
|
|
|
13223
|
|
|
|
13224
|
\see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
|
|
|
13225
|
*/
|
|
|
13226
|
void QCPLegend::setSelectedTextColor(const QColor &color)
|
|
|
13227
|
{
|
|
|
13228
|
mSelectedTextColor = color;
|
|
|
13229
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13230
|
{
|
|
|
13231
|
if (item(i))
|
|
|
13232
|
item(i)->setSelectedTextColor(color);
|
|
|
13233
|
}
|
|
|
13234
|
}
|
|
|
13235
|
|
|
|
13236
|
/*!
|
|
|
13237
|
Returns the item with index \a i.
|
|
|
13238
|
|
|
|
13239
|
\see itemCount
|
|
|
13240
|
*/
|
|
|
13241
|
QCPAbstractLegendItem *QCPLegend::item(int index) const
|
|
|
13242
|
{
|
|
|
13243
|
return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
|
|
|
13244
|
}
|
|
|
13245
|
|
|
|
13246
|
/*!
|
|
|
13247
|
Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
|
|
|
13248
|
If such an item isn't in the legend, returns 0.
|
|
|
13249
|
|
|
|
13250
|
\see hasItemWithPlottable
|
|
|
13251
|
*/
|
|
|
13252
|
QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
|
|
|
13253
|
{
|
|
|
13254
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13255
|
{
|
|
|
13256
|
if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
|
|
|
13257
|
{
|
|
|
13258
|
if (pli->plottable() == plottable)
|
|
|
13259
|
return pli;
|
|
|
13260
|
}
|
|
|
13261
|
}
|
|
|
13262
|
return 0;
|
|
|
13263
|
}
|
|
|
13264
|
|
|
|
13265
|
/*!
|
|
|
13266
|
Returns the number of items currently in the legend.
|
|
|
13267
|
\see item
|
|
|
13268
|
*/
|
|
|
13269
|
int QCPLegend::itemCount() const
|
|
|
13270
|
{
|
|
|
13271
|
return elementCount();
|
|
|
13272
|
}
|
|
|
13273
|
|
|
|
13274
|
/*!
|
|
|
13275
|
Returns whether the legend contains \a itm.
|
|
|
13276
|
*/
|
|
|
13277
|
bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
|
|
|
13278
|
{
|
|
|
13279
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13280
|
{
|
|
|
13281
|
if (item == this->item(i))
|
|
|
13282
|
return true;
|
|
|
13283
|
}
|
|
|
13284
|
return false;
|
|
|
13285
|
}
|
|
|
13286
|
|
|
|
13287
|
/*!
|
|
|
13288
|
Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
|
|
|
13289
|
If such an item isn't in the legend, returns false.
|
|
|
13290
|
|
|
|
13291
|
\see itemWithPlottable
|
|
|
13292
|
*/
|
|
|
13293
|
bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
|
|
|
13294
|
{
|
|
|
13295
|
return itemWithPlottable(plottable);
|
|
|
13296
|
}
|
|
|
13297
|
|
|
|
13298
|
/*!
|
|
|
13299
|
Adds \a item to the legend, if it's not present already.
|
|
|
13300
|
|
|
|
13301
|
Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
|
|
|
13302
|
|
|
|
13303
|
The legend takes ownership of the item.
|
|
|
13304
|
*/
|
|
|
13305
|
bool QCPLegend::addItem(QCPAbstractLegendItem *item)
|
|
|
13306
|
{
|
|
|
13307
|
if (!hasItem(item))
|
|
|
13308
|
{
|
|
|
13309
|
return addElement(rowCount(), 0, item);
|
|
|
13310
|
} else
|
|
|
13311
|
return false;
|
|
|
13312
|
}
|
|
|
13313
|
|
|
|
13314
|
/*!
|
|
|
13315
|
Removes the item with index \a index from the legend.
|
|
|
13316
|
|
|
|
13317
|
Returns true, if successful.
|
|
|
13318
|
|
|
|
13319
|
\see itemCount, clearItems
|
|
|
13320
|
*/
|
|
|
13321
|
bool QCPLegend::removeItem(int index)
|
|
|
13322
|
{
|
|
|
13323
|
if (QCPAbstractLegendItem *ali = item(index))
|
|
|
13324
|
{
|
|
|
13325
|
bool success = remove(ali);
|
|
|
13326
|
simplify();
|
|
|
13327
|
return success;
|
|
|
13328
|
} else
|
|
|
13329
|
return false;
|
|
|
13330
|
}
|
|
|
13331
|
|
|
|
13332
|
/*! \overload
|
|
|
13333
|
|
|
|
13334
|
Removes \a item from the legend.
|
|
|
13335
|
|
|
|
13336
|
Returns true, if successful.
|
|
|
13337
|
|
|
|
13338
|
\see clearItems
|
|
|
13339
|
*/
|
|
|
13340
|
bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
|
|
|
13341
|
{
|
|
|
13342
|
bool success = remove(item);
|
|
|
13343
|
simplify();
|
|
|
13344
|
return success;
|
|
|
13345
|
}
|
|
|
13346
|
|
|
|
13347
|
/*!
|
|
|
13348
|
Removes all items from the legend.
|
|
|
13349
|
*/
|
|
|
13350
|
void QCPLegend::clearItems()
|
|
|
13351
|
{
|
|
|
13352
|
for (int i=itemCount()-1; i>=0; --i)
|
|
|
13353
|
removeItem(i);
|
|
|
13354
|
}
|
|
|
13355
|
|
|
|
13356
|
/*!
|
|
|
13357
|
Returns the legend items that are currently selected. If no items are selected,
|
|
|
13358
|
the list is empty.
|
|
|
13359
|
|
|
|
13360
|
\see QCPAbstractLegendItem::setSelected, setSelectable
|
|
|
13361
|
*/
|
|
|
13362
|
QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
|
|
|
13363
|
{
|
|
|
13364
|
QList<QCPAbstractLegendItem*> result;
|
|
|
13365
|
for (int i=0; i<itemCount(); ++i)
|
|
|
13366
|
{
|
|
|
13367
|
if (QCPAbstractLegendItem *ali = item(i))
|
|
|
13368
|
{
|
|
|
13369
|
if (ali->selected())
|
|
|
13370
|
result.append(ali);
|
|
|
13371
|
}
|
|
|
13372
|
}
|
|
|
13373
|
return result;
|
|
|
13374
|
}
|
|
|
13375
|
|
|
|
13376
|
/*! \internal
|
|
|
13377
|
|
|
|
13378
|
A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
|
|
|
13379
|
before drawing main legend elements.
|
|
|
13380
|
|
|
|
13381
|
This is the antialiasing state the painter passed to the \ref draw method is in by default.
|
|
|
13382
|
|
|
|
13383
|
This function takes into account the local setting of the antialiasing flag as well as the
|
|
|
13384
|
overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
|
|
|
13385
|
QCustomPlot::setNotAntialiasedElements.
|
|
|
13386
|
|
|
|
13387
|
\see setAntialiased
|
|
|
13388
|
*/
|
|
|
13389
|
void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
|
13390
|
{
|
|
|
13391
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
|
|
|
13392
|
}
|
|
|
13393
|
|
|
|
13394
|
/*! \internal
|
|
|
13395
|
|
|
|
13396
|
Returns the pen used to paint the border of the legend, taking into account the selection state
|
|
|
13397
|
of the legend box.
|
|
|
13398
|
*/
|
|
|
13399
|
QPen QCPLegend::getBorderPen() const
|
|
|
13400
|
{
|
|
|
13401
|
return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
|
|
|
13402
|
}
|
|
|
13403
|
|
|
|
13404
|
/*! \internal
|
|
|
13405
|
|
|
|
13406
|
Returns the brush used to paint the background of the legend, taking into account the selection
|
|
|
13407
|
state of the legend box.
|
|
|
13408
|
*/
|
|
|
13409
|
QBrush QCPLegend::getBrush() const
|
|
|
13410
|
{
|
|
|
13411
|
return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
|
|
|
13412
|
}
|
|
|
13413
|
|
|
|
13414
|
/*! \internal
|
|
|
13415
|
|
|
|
13416
|
Draws the legend box with the provided \a painter. The individual legend items are layerables
|
|
|
13417
|
themselves, thus are drawn independently.
|
|
|
13418
|
*/
|
|
|
13419
|
void QCPLegend::draw(QCPPainter *painter)
|
|
|
13420
|
{
|
|
|
13421
|
// draw background rect:
|
|
|
13422
|
painter->setBrush(getBrush());
|
|
|
13423
|
painter->setPen(getBorderPen());
|
|
|
13424
|
painter->drawRect(mOuterRect);
|
|
|
13425
|
}
|
|
|
13426
|
|
|
|
13427
|
/* inherits documentation from base class */
|
|
|
13428
|
double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
|
13429
|
{
|
|
|
13430
|
if (!mParentPlot) return -1;
|
|
|
13431
|
if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
|
|
|
13432
|
return -1;
|
|
|
13433
|
|
|
|
13434
|
if (mOuterRect.contains(pos.toPoint()))
|
|
|
13435
|
{
|
|
|
13436
|
if (details) details->setValue(spLegendBox);
|
|
|
13437
|
return mParentPlot->selectionTolerance()*0.99;
|
|
|
13438
|
}
|
|
|
13439
|
return -1;
|
|
|
13440
|
}
|
|
|
13441
|
|
|
|
13442
|
/* inherits documentation from base class */
|
|
|
13443
|
void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
|
13444
|
{
|
|
|
13445
|
Q_UNUSED(event)
|
|
|
13446
|
mSelectedParts = selectedParts(); // in case item selection has changed
|
|
|
13447
|
if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
|
|
|
13448
|
{
|
|
|
13449
|
SelectableParts selBefore = mSelectedParts;
|
|
|
13450
|
setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
|
|
|
13451
|
if (selectionStateChanged)
|
|
|
13452
|
*selectionStateChanged = mSelectedParts != selBefore;
|
|
|
13453
|
}
|
|
|
13454
|
}
|
|
|
13455
|
|
|
|
13456
|
/* inherits documentation from base class */
|
|
|
13457
|
void QCPLegend::deselectEvent(bool *selectionStateChanged)
|
|
|
13458
|
{
|
|
|
13459
|
mSelectedParts = selectedParts(); // in case item selection has changed
|
|
|
13460
|
if (mSelectableParts.testFlag(spLegendBox))
|
|
|
13461
|
{
|
|
|
13462
|
SelectableParts selBefore = mSelectedParts;
|
|
|
13463
|
setSelectedParts(selectedParts() & ~spLegendBox);
|
|
|
13464
|
if (selectionStateChanged)
|
|
|
13465
|
*selectionStateChanged = mSelectedParts != selBefore;
|
|
|
13466
|
}
|
|
|
13467
|
}
|
|
|
13468
|
|
|
|
13469
|
/* inherits documentation from base class */
|
|
|
13470
|
QCP::Interaction QCPLegend::selectionCategory() const
|
|
|
13471
|
{
|
|
|
13472
|
return QCP::iSelectLegend;
|
|
|
13473
|
}
|
|
|
13474
|
|
|
|
13475
|
/* inherits documentation from base class */
|
|
|
13476
|
QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
|
|
|
13477
|
{
|
|
|
13478
|
return QCP::iSelectLegend;
|
|
|
13479
|
}
|
|
|
13480
|
|
|
|
13481
|
/* inherits documentation from base class */
|
|
|
13482
|
void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
|
|
|
13483
|
{
|
|
|
13484
|
Q_UNUSED(parentPlot)
|
|
|
13485
|
}
|
|
|
13486
|
|
|
|
13487
|
|
|
|
13488
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
13489
|
//////////////////// QCPPlotTitle
|
|
|
13490
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
13491
|
|
|
|
13492
|
/*! \class QCPPlotTitle
|
|
|
13493
|
\brief A layout element displaying a plot title text
|
|
|
13494
|
|
|
|
13495
|
The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
|
|
|
13496
|
and \ref setTextColor.
|
|
|
13497
|
|
|
|
13498
|
A plot title can be added as follows:
|
|
|
13499
|
\code
|
|
|
13500
|
customPlot->plotLayout()->insertRow(0); // inserts an empty row above the default axis rect
|
|
|
13501
|
customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Your Plot Title"));
|
|
|
13502
|
\endcode
|
|
|
13503
|
|
|
|
13504
|
Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
|
|
|
13505
|
easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
|
|
|
13506
|
signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
|
|
|
13507
|
QCustomPlot::titleDoubleClick signal.
|
|
|
13508
|
*/
|
|
|
13509
|
|
|
|
13510
|
/* start documentation of signals */
|
|
|
13511
|
|
|
|
13512
|
/*! \fn void QCPPlotTitle::selectionChanged(bool selected)
|
|
|
13513
|
|
|
|
13514
|
This signal is emitted when the selection state has changed to \a selected, either by user
|
|
|
13515
|
interaction or by a direct call to \ref setSelected.
|
|
|
13516
|
|
|
|
13517
|
\see setSelected, setSelectable
|
|
|
13518
|
*/
|
|
|
13519
|
|
|
|
13520
|
/* end documentation of signals */
|
|
|
13521
|
|
|
|
13522
|
/*!
|
|
|
13523
|
Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
|
|
|
13524
|
|
|
|
13525
|
To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
|
|
|
13526
|
*/
|
|
|
13527
|
QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
|
|
|
13528
|
QCPLayoutElement(parentPlot),
|
|
|
13529
|
mFont(QFont("sans serif", 13*1.5, QFont::Bold)),
|
|
|
13530
|
mTextColor(Qt::black),
|
|
|
13531
|
mSelectedFont(QFont("sans serif", 13*1.6, QFont::Bold)),
|
|
|
13532
|
mSelectedTextColor(Qt::blue),
|
|
|
13533
|
mSelectable(false),
|
|
|
13534
|
mSelected(false)
|
|
|
13535
|
{
|
|
|
13536
|
if (parentPlot)
|
|
|
13537
|
{
|
|
|
13538
|
setLayer(parentPlot->currentLayer());
|
|
|
13539
|
mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
|
|
|
13540
|
mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
|
|
|
13541
|
}
|
|
|
13542
|
setMargins(QMargins(5, 5, 5, 0));
|
|
|
13543
|
}
|
|
|
13544
|
|
|
|
13545
|
/*! \overload
|
|
|
13546
|
|
|
|
13547
|
Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
|
|
|
13548
|
*/
|
|
|
13549
|
QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
|
|
|
13550
|
QCPLayoutElement(parentPlot),
|
|
|
13551
|
mText(text),
|
|
|
13552
|
mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
|
|
|
13553
|
mTextColor(Qt::black),
|
|
|
13554
|
mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
|
|
|
13555
|
mSelectedTextColor(Qt::blue),
|
|
|
13556
|
mSelectable(false),
|
|
|
13557
|
mSelected(false)
|
|
|
13558
|
{
|
|
|
13559
|
setLayer("axes");
|
|
|
13560
|
setMargins(QMargins(5, 5, 5, 0));
|
|
|
13561
|
}
|
|
|
13562
|
|
|
|
13563
|
/*!
|
|
|
13564
|
Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
|
|
|
13565
|
|
|
|
13566
|
\see setFont, setTextColor
|
|
|
13567
|
*/
|
|
|
13568
|
void QCPPlotTitle::setText(const QString &text)
|
|
|
13569
|
{
|
|
|
13570
|
mText = text;
|
|
|
13571
|
}
|
|
|
13572
|
|
|
|
13573
|
/*!
|
|
|
13574
|
Sets the \a font of the title text.
|
|
|
13575
|
|
|
|
13576
|
\see setTextColor, setSelectedFont
|
|
|
13577
|
*/
|
|
|
13578
|
void QCPPlotTitle::setFont(const QFont &font)
|
|
|
13579
|
{
|
|
|
13580
|
mFont = font;
|
|
|
13581
|
}
|
|
|
13582
|
|
|
|
13583
|
/*!
|
|
|
13584
|
Sets the \a color of the title text.
|
|
|
13585
|
|
|
|
13586
|
\see setFont, setSelectedTextColor
|
|
|
13587
|
*/
|
|
|
13588
|
void QCPPlotTitle::setTextColor(const QColor &color)
|
|
|
13589
|
{
|
|
|
13590
|
mTextColor = color;
|
|
|
13591
|
}
|
|
|
13592
|
|
|
|
13593
|
/*!
|
|
|
13594
|
Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
|
|
|
13595
|
|
|
|
13596
|
\see setFont
|
|
|
13597
|
*/
|
|
|
13598
|
void QCPPlotTitle::setSelectedFont(const QFont &font)
|
|
|
13599
|
{
|
|
|
13600
|
mSelectedFont = font;
|
|
|
13601
|
}
|
|
|
13602
|
|
|
|
13603
|
/*!
|
|
|
13604
|
Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
|
|
|
13605
|
|
|
|
13606
|
\see setTextColor
|
|
|
13607
|
*/
|
|
|
13608
|
void QCPPlotTitle::setSelectedTextColor(const QColor &color)
|
|
|
13609
|
{
|
|
|
13610
|
mSelectedTextColor = color;
|
|
|
13611
|
}
|
|
|
13612
|
|
|
|
13613
|
/*!
|
|
|
13614
|
Sets whether the user may select this plot title to \a selectable.
|
|
|
13615
|
|
|
|
13616
|
Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
|
|
|
13617
|
programmatically via \ref setSelected.
|
|
|
13618
|
*/
|
|
|
13619
|
void QCPPlotTitle::setSelectable(bool selectable)
|
|
|
13620
|
{
|
|
|
13621
|
if (mSelectable != selectable)
|
|
|
13622
|
{
|
|
|
13623
|
mSelectable = selectable;
|
|
|
13624
|
emit selectableChanged(mSelectable);
|
|
|
13625
|
}
|
|
|
13626
|
}
|
|
|
13627
|
|
|
|
13628
|
/*!
|
|
|
13629
|
Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
|
|
|
13630
|
selectionChanged is emitted.
|
|
|
13631
|
|
|
|
13632
|
Note that this function can change the selection state independently of the current \ref
|
|
|
13633
|
setSelectable state.
|
|
|
13634
|
*/
|
|
|
13635
|
void QCPPlotTitle::setSelected(bool selected)
|
|
|
13636
|
{
|
|
|
13637
|
if (mSelected != selected)
|
|
|
13638
|
{
|
|
|
13639
|
mSelected = selected;
|
|
|
13640
|
emit selectionChanged(mSelected);
|
|
|
13641
|
}
|
|
|
13642
|
}
|
|
|
13643
|
|
|
|
13644
|
/* inherits documentation from base class */
|
|
|
13645
|
void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
|
13646
|
{
|
|
|
13647
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
|
|
|
13648
|
}
|
|
|
13649
|
|
|
|
13650
|
/* inherits documentation from base class */
|
|
|
13651
|
void QCPPlotTitle::draw(QCPPainter *painter)
|
|
|
13652
|
{
|
|
|
13653
|
painter->setFont(mainFont());
|
|
|
13654
|
painter->setPen(QPen(mainTextColor()));
|
|
|
13655
|
painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
|
|
|
13656
|
}
|
|
|
13657
|
|
|
|
13658
|
/* inherits documentation from base class */
|
|
|
13659
|
QSize QCPPlotTitle::minimumSizeHint() const
|
|
|
13660
|
{
|
|
|
13661
|
QFontMetrics metrics(mFont);
|
|
|
13662
|
QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
|
|
|
13663
|
result.rwidth() += mMargins.left() + mMargins.right();
|
|
|
13664
|
result.rheight() += mMargins.top() + mMargins.bottom();
|
|
|
13665
|
return result;
|
|
|
13666
|
}
|
|
|
13667
|
|
|
|
13668
|
/* inherits documentation from base class */
|
|
|
13669
|
QSize QCPPlotTitle::maximumSizeHint() const
|
|
|
13670
|
{
|
|
|
13671
|
QFontMetrics metrics(mFont);
|
|
|
13672
|
QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
|
|
|
13673
|
result.rheight() += mMargins.top() + mMargins.bottom();
|
|
|
13674
|
result.setWidth(QWIDGETSIZE_MAX);
|
|
|
13675
|
return result;
|
|
|
13676
|
}
|
|
|
13677
|
|
|
|
13678
|
/* inherits documentation from base class */
|
|
|
13679
|
void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
|
13680
|
{
|
|
|
13681
|
Q_UNUSED(event)
|
|
|
13682
|
Q_UNUSED(details)
|
|
|
13683
|
if (mSelectable)
|
|
|
13684
|
{
|
|
|
13685
|
bool selBefore = mSelected;
|
|
|
13686
|
setSelected(additive ? !mSelected : true);
|
|
|
13687
|
if (selectionStateChanged)
|
|
|
13688
|
*selectionStateChanged = mSelected != selBefore;
|
|
|
13689
|
}
|
|
|
13690
|
}
|
|
|
13691
|
|
|
|
13692
|
/* inherits documentation from base class */
|
|
|
13693
|
void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
|
|
|
13694
|
{
|
|
|
13695
|
if (mSelectable)
|
|
|
13696
|
{
|
|
|
13697
|
bool selBefore = mSelected;
|
|
|
13698
|
setSelected(false);
|
|
|
13699
|
if (selectionStateChanged)
|
|
|
13700
|
*selectionStateChanged = mSelected != selBefore;
|
|
|
13701
|
}
|
|
|
13702
|
}
|
|
|
13703
|
|
|
|
13704
|
/* inherits documentation from base class */
|
|
|
13705
|
double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
|
13706
|
{
|
|
|
13707
|
Q_UNUSED(details)
|
|
|
13708
|
if (onlySelectable && !mSelectable)
|
|
|
13709
|
return -1;
|
|
|
13710
|
|
|
|
13711
|
if (mTextBoundingRect.contains(pos.toPoint()))
|
|
|
13712
|
return mParentPlot->selectionTolerance()*0.99;
|
|
|
13713
|
else
|
|
|
13714
|
return -1;
|
|
|
13715
|
}
|
|
|
13716
|
|
|
|
13717
|
/*! \internal
|
|
|
13718
|
|
|
|
13719
|
Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
|
|
|
13720
|
<tt>true</tt>, else mFont is returned.
|
|
|
13721
|
*/
|
|
|
13722
|
QFont QCPPlotTitle::mainFont() const
|
|
|
13723
|
{
|
|
|
13724
|
return mSelected ? mSelectedFont : mFont;
|
|
|
13725
|
}
|
|
|
13726
|
|
|
|
13727
|
/*! \internal
|
|
|
13728
|
|
|
|
13729
|
Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
|
|
|
13730
|
<tt>true</tt>, else mTextColor is returned.
|
|
|
13731
|
*/
|
|
|
13732
|
QColor QCPPlotTitle::mainTextColor() const
|
|
|
13733
|
{
|
|
|
13734
|
return mSelected ? mSelectedTextColor : mTextColor;
|
|
|
13735
|
}
|
|
|
13736
|
|
|
|
13737
|
|
|
|
13738
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
13739
|
//////////////////// QCPColorScale
|
|
|
13740
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
13741
|
|
|
|
13742
|
/*! \class QCPColorScale
|
|
|
13743
|
\brief A color scale for use with color coding data such as QCPColorMap
|
|
|
13744
|
|
|
|
13745
|
This layout element can be placed on the plot to correlate a color gradient with data values. It
|
|
|
13746
|
is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
|
|
|
13747
|
|
|
|
13748
|
\image html QCPColorScale.png
|
|
|
13749
|
|
|
|
13750
|
The color scale can be either horizontal or vertical, as shown in the image above. The
|
|
|
13751
|
orientation and the side where the numbers appear is controlled with \ref setType.
|
|
|
13752
|
|
|
|
13753
|
Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
|
|
|
13754
|
connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
|
|
|
13755
|
setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
|
|
|
13756
|
scale, to make them all synchronize these properties.
|
|
|
13757
|
|
|
|
13758
|
To have finer control over the number display and axis behaviour, you can directly access the
|
|
|
13759
|
\ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
|
|
|
13760
|
you want to change the number of automatically generated ticks, call
|
|
|
13761
|
\code
|
|
|
13762
|
colorScale->axis()->setAutoTickCount(3);
|
|
|
13763
|
\endcode
|
|
|
13764
|
|
|
|
13765
|
Placing a color scale next to the main axis rect works like with any other layout element:
|
|
|
13766
|
\code
|
|
|
13767
|
QCPColorScale *colorScale = new QCPColorScale(customPlot);
|
|
|
13768
|
customPlot->plotLayout()->addElement(0, 1, colorScale);
|
|
|
13769
|
colorScale->setLabel("Some Label Text");
|
|
|
13770
|
\endcode
|
|
|
13771
|
In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
|
|
|
13772
|
call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
|
|
|
13773
|
scale can be set with \ref setLabel.
|
|
|
13774
|
|
|
|
13775
|
For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
|
|
|
13776
|
the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
|
|
|
13777
|
\code
|
|
|
13778
|
QCPMarginGroup *group = new QCPMarginGroup(customPlot);
|
|
|
13779
|
colorScale->setMarginGroup(QCP::msTop|QCP::msBottom, group);
|
|
|
13780
|
customPlot->axisRect()->setMarginGroup(QCP::msTop|QCP::msBottom, group);
|
|
|
13781
|
\endcode
|
|
|
13782
|
|
|
|
13783
|
Color scales are initialized with a non-zero minimum top and bottom margin (\ref
|
|
|
13784
|
setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
|
|
|
13785
|
margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
|
|
|
13786
|
horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
|
|
|
13787
|
might want to also change the minimum margins accordingly, e.g. \ref
|
|
|
13788
|
setMinimumMargins(QMargins(6, 0, 6, 0)).
|
|
|
13789
|
*/
|
|
|
13790
|
|
|
|
13791
|
/* start documentation of inline functions */
|
|
|
13792
|
|
|
|
13793
|
/*! \fn QCPAxis *QCPColorScale::axis() const
|
|
|
13794
|
|
|
|
13795
|
Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
|
|
|
13796
|
appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
|
|
|
13797
|
interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
|
|
|
13798
|
setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
|
|
|
13799
|
QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
|
|
|
13800
|
the QCPColorScale or on its QCPAxis.
|
|
|
13801
|
|
|
|
13802
|
If the type of the color scale is changed with \ref setType, the axis returned by this method
|
|
|
13803
|
will change, too, to either the left, right, bottom or top axis, depending on which type was set.
|
|
|
13804
|
*/
|
|
|
13805
|
|
|
|
13806
|
/* end documentation of signals */
|
|
|
13807
|
/* start documentation of signals */
|
|
|
13808
|
|
|
|
13809
|
/*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
|
|
|
13810
|
|
|
|
13811
|
This signal is emitted when the data range changes.
|
|
|
13812
|
|
|
|
13813
|
\see setDataRange
|
|
|
13814
|
*/
|
|
|
13815
|
|
|
|
13816
|
/*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
|
|
|
13817
|
|
|
|
13818
|
This signal is emitted when the data scale type changes.
|
|
|
13819
|
|
|
|
13820
|
\see setDataScaleType
|
|
|
13821
|
*/
|
|
|
13822
|
|
|
|
13823
|
/*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
|
|
|
13824
|
|
|
|
13825
|
This signal is emitted when the gradient changes.
|
|
|
13826
|
|
|
|
13827
|
\see setGradient
|
|
|
13828
|
*/
|
|
|
13829
|
|
|
|
13830
|
/* end documentation of signals */
|
|
|
13831
|
|
|
|
13832
|
/*!
|
|
|
13833
|
Constructs a new QCPColorScale.
|
|
|
13834
|
*/
|
|
|
13835
|
QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
|
|
|
13836
|
QCPLayoutElement(parentPlot),
|
|
|
13837
|
mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
|
|
|
13838
|
mDataScaleType(QCPAxis::stLinear),
|
|
|
13839
|
mBarWidth(20),
|
|
|
13840
|
mAxisRect(new QCPColorScaleAxisRectPrivate(this))
|
|
|
13841
|
{
|
|
|
13842
|
setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
|
|
|
13843
|
setType(QCPAxis::atRight);
|
|
|
13844
|
setDataRange(QCPRange(0, 6));
|
|
|
13845
|
}
|
|
|
13846
|
|
|
|
13847
|
QCPColorScale::~QCPColorScale()
|
|
|
13848
|
{
|
|
|
13849
|
delete mAxisRect;
|
|
|
13850
|
}
|
|
|
13851
|
|
|
|
13852
|
/* undocumented getter */
|
|
|
13853
|
QString QCPColorScale::label() const
|
|
|
13854
|
{
|
|
|
13855
|
if (!mColorAxis)
|
|
|
13856
|
{
|
|
|
13857
|
qDebug() << Q_FUNC_INFO << "internal color axis undefined";
|
|
|
13858
|
return QString();
|
|
|
13859
|
}
|
|
|
13860
|
|
|
|
13861
|
return mColorAxis.data()->label();
|
|
|
13862
|
}
|
|
|
13863
|
|
|
|
13864
|
/* undocumented getter */
|
|
|
13865
|
bool QCPColorScale::rangeDrag() const
|
|
|
13866
|
{
|
|
|
13867
|
if (!mAxisRect)
|
|
|
13868
|
{
|
|
|
13869
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
13870
|
return false;
|
|
|
13871
|
}
|
|
|
13872
|
|
|
|
13873
|
return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
|
|
|
13874
|
mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
|
|
|
13875
|
mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
|
|
|
13876
|
}
|
|
|
13877
|
|
|
|
13878
|
/* undocumented getter */
|
|
|
13879
|
bool QCPColorScale::rangeZoom() const
|
|
|
13880
|
{
|
|
|
13881
|
if (!mAxisRect)
|
|
|
13882
|
{
|
|
|
13883
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
13884
|
return false;
|
|
|
13885
|
}
|
|
|
13886
|
|
|
|
13887
|
return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
|
|
|
13888
|
mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
|
|
|
13889
|
mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
|
|
|
13890
|
}
|
|
|
13891
|
|
|
|
13892
|
/*!
|
|
|
13893
|
Sets at which side of the color scale the axis is placed, and thus also its orientation.
|
|
|
13894
|
|
|
|
13895
|
Note that after setting \a type to a different value, the axis returned by \ref axis() will
|
|
|
13896
|
be a different one. The new axis will adopt the following properties from the previous axis: The
|
|
|
13897
|
range, scale type, log base and label.
|
|
|
13898
|
*/
|
|
|
13899
|
void QCPColorScale::setType(QCPAxis::AxisType type)
|
|
|
13900
|
{
|
|
|
13901
|
if (!mAxisRect)
|
|
|
13902
|
{
|
|
|
13903
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
13904
|
return;
|
|
|
13905
|
}
|
|
|
13906
|
if (mType != type)
|
|
|
13907
|
{
|
|
|
13908
|
mType = type;
|
|
|
13909
|
QCPRange rangeTransfer(0, 6);
|
|
|
13910
|
double logBaseTransfer = 10;
|
|
|
13911
|
QString labelTransfer;
|
|
|
13912
|
// revert some settings on old axis:
|
|
|
13913
|
if (mColorAxis)
|
|
|
13914
|
{
|
|
|
13915
|
rangeTransfer = mColorAxis.data()->range();
|
|
|
13916
|
labelTransfer = mColorAxis.data()->label();
|
|
|
13917
|
logBaseTransfer = mColorAxis.data()->scaleLogBase();
|
|
|
13918
|
mColorAxis.data()->setLabel("");
|
|
|
13919
|
disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
|
|
|
13920
|
disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
|
|
|
13921
|
}
|
|
|
13922
|
foreach (QCPAxis::AxisType atype, QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop)
|
|
|
13923
|
{
|
|
|
13924
|
mAxisRect.data()->axis(atype)->setTicks(atype == mType);
|
|
|
13925
|
mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
|
|
|
13926
|
}
|
|
|
13927
|
// set new mColorAxis pointer:
|
|
|
13928
|
mColorAxis = mAxisRect.data()->axis(mType);
|
|
|
13929
|
// transfer settings to new axis:
|
|
|
13930
|
mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
|
|
|
13931
|
mColorAxis.data()->setLabel(labelTransfer);
|
|
|
13932
|
mColorAxis.data()->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here
|
|
|
13933
|
connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
|
|
|
13934
|
connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
|
|
|
13935
|
mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
|
|
|
13936
|
QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
|
|
|
13937
|
}
|
|
|
13938
|
}
|
|
|
13939
|
|
|
|
13940
|
/*!
|
|
|
13941
|
Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
|
|
|
13942
|
|
|
|
13943
|
It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
|
|
|
13944
|
also equivalent to directly accessing the \ref axis and setting its range with \ref
|
|
|
13945
|
QCPAxis::setRange.
|
|
|
13946
|
|
|
|
13947
|
\see setDataScaleType, setGradient, rescaleDataRange
|
|
|
13948
|
*/
|
|
|
13949
|
void QCPColorScale::setDataRange(const QCPRange &dataRange)
|
|
|
13950
|
{
|
|
|
13951
|
if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
|
|
|
13952
|
{
|
|
|
13953
|
mDataRange = dataRange;
|
|
|
13954
|
if (mColorAxis)
|
|
|
13955
|
mColorAxis.data()->setRange(mDataRange);
|
|
|
13956
|
emit dataRangeChanged(mDataRange);
|
|
|
13957
|
}
|
|
|
13958
|
}
|
|
|
13959
|
|
|
|
13960
|
/*!
|
|
|
13961
|
Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
|
|
|
13962
|
or logarithmically.
|
|
|
13963
|
|
|
|
13964
|
It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
|
|
|
13965
|
also equivalent to directly accessing the \ref axis and setting its scale type with \ref
|
|
|
13966
|
QCPAxis::setScaleType.
|
|
|
13967
|
|
|
|
13968
|
\see setDataRange, setGradient
|
|
|
13969
|
*/
|
|
|
13970
|
void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
|
|
|
13971
|
{
|
|
|
13972
|
if (mDataScaleType != scaleType)
|
|
|
13973
|
{
|
|
|
13974
|
mDataScaleType = scaleType;
|
|
|
13975
|
if (mColorAxis)
|
|
|
13976
|
mColorAxis.data()->setScaleType(mDataScaleType);
|
|
|
13977
|
if (mDataScaleType == QCPAxis::stLogarithmic)
|
|
|
13978
|
setDataRange(mDataRange.sanitizedForLogScale());
|
|
|
13979
|
emit dataScaleTypeChanged(mDataScaleType);
|
|
|
13980
|
}
|
|
|
13981
|
}
|
|
|
13982
|
|
|
|
13983
|
/*!
|
|
|
13984
|
Sets the color gradient that will be used to represent data values.
|
|
|
13985
|
|
|
|
13986
|
It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
|
|
|
13987
|
|
|
|
13988
|
\see setDataRange, setDataScaleType
|
|
|
13989
|
*/
|
|
|
13990
|
void QCPColorScale::setGradient(const QCPColorGradient &gradient)
|
|
|
13991
|
{
|
|
|
13992
|
if (mGradient != gradient)
|
|
|
13993
|
{
|
|
|
13994
|
mGradient = gradient;
|
|
|
13995
|
if (mAxisRect)
|
|
|
13996
|
mAxisRect.data()->mGradientImageInvalidated = true;
|
|
|
13997
|
emit gradientChanged(mGradient);
|
|
|
13998
|
}
|
|
|
13999
|
}
|
|
|
14000
|
|
|
|
14001
|
/*!
|
|
|
14002
|
Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
|
|
|
14003
|
the internal \ref axis.
|
|
|
14004
|
*/
|
|
|
14005
|
void QCPColorScale::setLabel(const QString &str)
|
|
|
14006
|
{
|
|
|
14007
|
if (!mColorAxis)
|
|
|
14008
|
{
|
|
|
14009
|
qDebug() << Q_FUNC_INFO << "internal color axis undefined";
|
|
|
14010
|
return;
|
|
|
14011
|
}
|
|
|
14012
|
|
|
|
14013
|
mColorAxis.data()->setLabel(str);
|
|
|
14014
|
}
|
|
|
14015
|
|
|
|
14016
|
/*!
|
|
|
14017
|
Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
|
|
|
14018
|
will have.
|
|
|
14019
|
*/
|
|
|
14020
|
void QCPColorScale::setBarWidth(int width)
|
|
|
14021
|
{
|
|
|
14022
|
mBarWidth = width;
|
|
|
14023
|
}
|
|
|
14024
|
|
|
|
14025
|
/*!
|
|
|
14026
|
Sets whether the user can drag the data range (\ref setDataRange).
|
|
|
14027
|
|
|
|
14028
|
Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
|
|
|
14029
|
QCustomPlot::setInteractions) to allow range dragging.
|
|
|
14030
|
*/
|
|
|
14031
|
void QCPColorScale::setRangeDrag(bool enabled)
|
|
|
14032
|
{
|
|
|
14033
|
if (!mAxisRect)
|
|
|
14034
|
{
|
|
|
14035
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14036
|
return;
|
|
|
14037
|
}
|
|
|
14038
|
|
|
|
14039
|
if (enabled)
|
|
|
14040
|
mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
|
|
|
14041
|
else
|
|
|
14042
|
mAxisRect.data()->setRangeDrag(0);
|
|
|
14043
|
}
|
|
|
14044
|
|
|
|
14045
|
/*!
|
|
|
14046
|
Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
|
|
|
14047
|
|
|
|
14048
|
Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
|
|
|
14049
|
QCustomPlot::setInteractions) to allow range dragging.
|
|
|
14050
|
*/
|
|
|
14051
|
void QCPColorScale::setRangeZoom(bool enabled)
|
|
|
14052
|
{
|
|
|
14053
|
if (!mAxisRect)
|
|
|
14054
|
{
|
|
|
14055
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14056
|
return;
|
|
|
14057
|
}
|
|
|
14058
|
|
|
|
14059
|
if (enabled)
|
|
|
14060
|
mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
|
|
|
14061
|
else
|
|
|
14062
|
mAxisRect.data()->setRangeZoom(0);
|
|
|
14063
|
}
|
|
|
14064
|
|
|
|
14065
|
/*!
|
|
|
14066
|
Returns a list of all the color maps associated with this color scale.
|
|
|
14067
|
*/
|
|
|
14068
|
QList<QCPColorMap*> QCPColorScale::colorMaps() const
|
|
|
14069
|
{
|
|
|
14070
|
QList<QCPColorMap*> result;
|
|
|
14071
|
for (int i=0; i<mParentPlot->plottableCount(); ++i)
|
|
|
14072
|
{
|
|
|
14073
|
if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
|
|
|
14074
|
if (cm->colorScale() == this)
|
|
|
14075
|
result.append(cm);
|
|
|
14076
|
}
|
|
|
14077
|
return result;
|
|
|
14078
|
}
|
|
|
14079
|
|
|
|
14080
|
/*!
|
|
|
14081
|
Changes the data range such that all color maps associated with this color scale are fully mapped
|
|
|
14082
|
to the gradient in the data dimension.
|
|
|
14083
|
|
|
|
14084
|
\see setDataRange
|
|
|
14085
|
*/
|
|
|
14086
|
void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
|
|
|
14087
|
{
|
|
|
14088
|
QList<QCPColorMap*> maps = colorMaps();
|
|
|
14089
|
QCPRange newRange;
|
|
|
14090
|
bool haveRange = false;
|
|
|
14091
|
int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
|
|
|
14092
|
if (mDataScaleType == QCPAxis::stLogarithmic)
|
|
|
14093
|
sign = (mDataRange.upper < 0 ? -1 : 1);
|
|
|
14094
|
for (int i=0; i<maps.size(); ++i)
|
|
|
14095
|
{
|
|
|
14096
|
if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
|
|
|
14097
|
continue;
|
|
|
14098
|
QCPRange mapRange;
|
|
|
14099
|
if (maps.at(i)->colorScale() == this)
|
|
|
14100
|
{
|
|
|
14101
|
bool currentFoundRange = true;
|
|
|
14102
|
mapRange = maps.at(i)->data()->dataBounds();
|
|
|
14103
|
if (sign == 1)
|
|
|
14104
|
{
|
|
|
14105
|
if (mapRange.lower <= 0 && mapRange.upper > 0)
|
|
|
14106
|
mapRange.lower = mapRange.upper*1e-3;
|
|
|
14107
|
else if (mapRange.lower <= 0 && mapRange.upper <= 0)
|
|
|
14108
|
currentFoundRange = false;
|
|
|
14109
|
} else if (sign == -1)
|
|
|
14110
|
{
|
|
|
14111
|
if (mapRange.upper >= 0 && mapRange.lower < 0)
|
|
|
14112
|
mapRange.upper = mapRange.lower*1e-3;
|
|
|
14113
|
else if (mapRange.upper >= 0 && mapRange.lower >= 0)
|
|
|
14114
|
currentFoundRange = false;
|
|
|
14115
|
}
|
|
|
14116
|
if (currentFoundRange)
|
|
|
14117
|
{
|
|
|
14118
|
if (!haveRange)
|
|
|
14119
|
newRange = mapRange;
|
|
|
14120
|
else
|
|
|
14121
|
newRange.expand(mapRange);
|
|
|
14122
|
haveRange = true;
|
|
|
14123
|
}
|
|
|
14124
|
}
|
|
|
14125
|
}
|
|
|
14126
|
if (haveRange)
|
|
|
14127
|
{
|
|
|
14128
|
if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
|
|
|
14129
|
{
|
|
|
14130
|
double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
|
|
|
14131
|
if (mDataScaleType == QCPAxis::stLinear)
|
|
|
14132
|
{
|
|
|
14133
|
newRange.lower = center-mDataRange.size()/2.0;
|
|
|
14134
|
newRange.upper = center+mDataRange.size()/2.0;
|
|
|
14135
|
} else // mScaleType == stLogarithmic
|
|
|
14136
|
{
|
|
|
14137
|
newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
|
|
|
14138
|
newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
|
|
|
14139
|
}
|
|
|
14140
|
}
|
|
|
14141
|
setDataRange(newRange);
|
|
|
14142
|
}
|
|
|
14143
|
}
|
|
|
14144
|
|
|
|
14145
|
/* inherits documentation from base class */
|
|
|
14146
|
void QCPColorScale::update(UpdatePhase phase)
|
|
|
14147
|
{
|
|
|
14148
|
QCPLayoutElement::update(phase);
|
|
|
14149
|
if (!mAxisRect)
|
|
|
14150
|
{
|
|
|
14151
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14152
|
return;
|
|
|
14153
|
}
|
|
|
14154
|
|
|
|
14155
|
mAxisRect.data()->update(phase);
|
|
|
14156
|
|
|
|
14157
|
switch (phase)
|
|
|
14158
|
{
|
|
|
14159
|
case upMargins:
|
|
|
14160
|
{
|
|
|
14161
|
if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
|
|
|
14162
|
{
|
|
|
14163
|
setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
|
|
|
14164
|
setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
|
|
|
14165
|
} else
|
|
|
14166
|
{
|
|
|
14167
|
setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
|
|
|
14168
|
setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
|
|
|
14169
|
}
|
|
|
14170
|
break;
|
|
|
14171
|
}
|
|
|
14172
|
case upLayout:
|
|
|
14173
|
{
|
|
|
14174
|
mAxisRect.data()->setOuterRect(rect());
|
|
|
14175
|
break;
|
|
|
14176
|
}
|
|
|
14177
|
default: break;
|
|
|
14178
|
}
|
|
|
14179
|
}
|
|
|
14180
|
|
|
|
14181
|
/* inherits documentation from base class */
|
|
|
14182
|
void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
|
14183
|
{
|
|
|
14184
|
painter->setAntialiasing(false);
|
|
|
14185
|
}
|
|
|
14186
|
|
|
|
14187
|
/* inherits documentation from base class */
|
|
|
14188
|
void QCPColorScale::mousePressEvent(QMouseEvent *event)
|
|
|
14189
|
{
|
|
|
14190
|
if (!mAxisRect)
|
|
|
14191
|
{
|
|
|
14192
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14193
|
return;
|
|
|
14194
|
}
|
|
|
14195
|
mAxisRect.data()->mousePressEvent(event);
|
|
|
14196
|
}
|
|
|
14197
|
|
|
|
14198
|
/* inherits documentation from base class */
|
|
|
14199
|
void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
|
|
|
14200
|
{
|
|
|
14201
|
if (!mAxisRect)
|
|
|
14202
|
{
|
|
|
14203
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14204
|
return;
|
|
|
14205
|
}
|
|
|
14206
|
mAxisRect.data()->mouseMoveEvent(event);
|
|
|
14207
|
}
|
|
|
14208
|
|
|
|
14209
|
/* inherits documentation from base class */
|
|
|
14210
|
void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
|
|
|
14211
|
{
|
|
|
14212
|
if (!mAxisRect)
|
|
|
14213
|
{
|
|
|
14214
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14215
|
return;
|
|
|
14216
|
}
|
|
|
14217
|
mAxisRect.data()->mouseReleaseEvent(event);
|
|
|
14218
|
}
|
|
|
14219
|
|
|
|
14220
|
/* inherits documentation from base class */
|
|
|
14221
|
void QCPColorScale::wheelEvent(QWheelEvent *event)
|
|
|
14222
|
{
|
|
|
14223
|
if (!mAxisRect)
|
|
|
14224
|
{
|
|
|
14225
|
qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
|
|
|
14226
|
return;
|
|
|
14227
|
}
|
|
|
14228
|
mAxisRect.data()->wheelEvent(event);
|
|
|
14229
|
}
|
|
|
14230
|
|
|
|
14231
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
14232
|
//////////////////// QCPColorScaleAxisRectPrivate
|
|
|
14233
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
14234
|
|
|
|
14235
|
/*! \class QCPColorScaleAxisRectPrivate
|
|
|
14236
|
|
|
|
14237
|
\internal
|
|
|
14238
|
\brief An axis rect subclass for use in a QCPColorScale
|
|
|
14239
|
|
|
|
14240
|
This is a private class and not part of the public QCustomPlot interface.
|
|
|
14241
|
|
|
|
14242
|
It provides the axis rect functionality for the QCPColorScale class.
|
|
|
14243
|
*/
|
|
|
14244
|
|
|
|
14245
|
|
|
|
14246
|
/*!
|
|
|
14247
|
Creates a new instance, as a child of \a parentColorScale.
|
|
|
14248
|
*/
|
|
|
14249
|
QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
|
|
|
14250
|
QCPAxisRect(parentColorScale->parentPlot(), true),
|
|
|
14251
|
mParentColorScale(parentColorScale),
|
|
|
14252
|
mGradientImageInvalidated(true)
|
|
|
14253
|
{
|
|
|
14254
|
setParentLayerable(parentColorScale);
|
|
|
14255
|
setMinimumMargins(QMargins(0, 0, 0, 0));
|
|
|
14256
|
foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
|
|
|
14257
|
{
|
|
|
14258
|
axis(type)->setVisible(true);
|
|
|
14259
|
axis(type)->grid()->setVisible(false);
|
|
|
14260
|
axis(type)->setPadding(0);
|
|
|
14261
|
connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
|
|
|
14262
|
connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
|
|
|
14263
|
}
|
|
|
14264
|
|
|
|
14265
|
connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
|
|
|
14266
|
connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
|
|
|
14267
|
connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
|
|
|
14268
|
connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
|
|
|
14269
|
connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
|
|
|
14270
|
connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
|
|
|
14271
|
connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
|
|
|
14272
|
connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
|
|
|
14273
|
|
|
|
14274
|
// make layer transfers of color scale transfer to axis rect and axes
|
|
|
14275
|
// the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
|
|
|
14276
|
connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
|
|
|
14277
|
foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
|
|
|
14278
|
connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
|
|
|
14279
|
}
|
|
|
14280
|
|
|
|
14281
|
/*! \internal
|
|
|
14282
|
Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
|
|
|
14283
|
it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
|
|
|
14284
|
*/
|
|
|
14285
|
void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
|
|
|
14286
|
{
|
|
|
14287
|
if (mGradientImageInvalidated)
|
|
|
14288
|
updateGradientImage();
|
|
|
14289
|
|
|
|
14290
|
bool mirrorHorz = false;
|
|
|
14291
|
bool mirrorVert = false;
|
|
|
14292
|
if (mParentColorScale->mColorAxis)
|
|
|
14293
|
{
|
|
|
14294
|
mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
|
|
|
14295
|
mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
|
|
|
14296
|
}
|
|
|
14297
|
|
|
|
14298
|
painter->drawImage(rect(), mGradientImage.mirrored(mirrorHorz, mirrorVert));
|
|
|
14299
|
QCPAxisRect::draw(painter);
|
|
|
14300
|
}
|
|
|
14301
|
|
|
|
14302
|
/*! \internal
|
|
|
14303
|
|
|
|
14304
|
Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
|
|
|
14305
|
generate a gradient image. This gradient image will be used in the \ref draw method.
|
|
|
14306
|
*/
|
|
|
14307
|
void QCPColorScaleAxisRectPrivate::updateGradientImage()
|
|
|
14308
|
{
|
|
|
14309
|
if (rect().isEmpty())
|
|
|
14310
|
return;
|
|
|
14311
|
|
|
|
14312
|
int n = mParentColorScale->mGradient.levelCount();
|
|
|
14313
|
int w, h;
|
|
|
14314
|
QVector<double> data(n);
|
|
|
14315
|
for (int i=0; i<n; ++i)
|
|
|
14316
|
data[i] = i;
|
|
|
14317
|
if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
|
|
|
14318
|
{
|
|
|
14319
|
w = n;
|
|
|
14320
|
h = rect().height();
|
|
|
14321
|
mGradientImage = QImage(w, h, QImage::Format_RGB32);
|
|
|
14322
|
QVector<QRgb*> pixels;
|
|
|
14323
|
for (int y=0; y<h; ++y)
|
|
|
14324
|
pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
|
|
|
14325
|
mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
|
|
|
14326
|
for (int y=1; y<h; ++y)
|
|
|
14327
|
memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
|
|
|
14328
|
} else
|
|
|
14329
|
{
|
|
|
14330
|
w = rect().width();
|
|
|
14331
|
h = n;
|
|
|
14332
|
mGradientImage = QImage(w, h, QImage::Format_RGB32);
|
|
|
14333
|
for (int y=0; y<h; ++y)
|
|
|
14334
|
{
|
|
|
14335
|
QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
|
|
|
14336
|
const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
|
|
|
14337
|
for (int x=0; x<w; ++x)
|
|
|
14338
|
pixels[x] = lineColor;
|
|
|
14339
|
}
|
|
|
14340
|
}
|
|
|
14341
|
mGradientImageInvalidated = false;
|
|
|
14342
|
}
|
|
|
14343
|
|
|
|
14344
|
/*! \internal
|
|
|
14345
|
|
|
|
14346
|
This slot is connected to the selectionChanged signals of the four axes in the constructor. It
|
|
|
14347
|
synchronizes the selection state of the axes.
|
|
|
14348
|
*/
|
|
|
14349
|
void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
|
|
|
14350
|
{
|
|
|
14351
|
// axis bases of four axes shall always (de-)selected synchronously:
|
|
|
14352
|
foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
|
|
|
14353
|
{
|
|
|
14354
|
if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
|
|
|
14355
|
if (senderAxis->axisType() == type)
|
|
|
14356
|
continue;
|
|
|
14357
|
|
|
|
14358
|
if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
|
|
|
14359
|
{
|
|
|
14360
|
if (selectedParts.testFlag(QCPAxis::spAxis))
|
|
|
14361
|
axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
|
|
|
14362
|
else
|
|
|
14363
|
axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
|
|
|
14364
|
}
|
|
|
14365
|
}
|
|
|
14366
|
}
|
|
|
14367
|
|
|
|
14368
|
/*! \internal
|
|
|
14369
|
|
|
|
14370
|
This slot is connected to the selectableChanged signals of the four axes in the constructor. It
|
|
|
14371
|
synchronizes the selectability of the axes.
|
|
|
14372
|
*/
|
|
|
14373
|
void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
|
|
|
14374
|
{
|
|
|
14375
|
// synchronize axis base selectability:
|
|
|
14376
|
foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
|
|
|
14377
|
{
|
|
|
14378
|
if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
|
|
|
14379
|
if (senderAxis->axisType() == type)
|
|
|
14380
|
continue;
|
|
|
14381
|
|
|
|
14382
|
if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
|
|
|
14383
|
{
|
|
|
14384
|
if (selectableParts.testFlag(QCPAxis::spAxis))
|
|
|
14385
|
axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
|
|
|
14386
|
else
|
|
|
14387
|
axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
|
|
|
14388
|
}
|
|
|
14389
|
}
|
|
|
14390
|
}
|
|
|
14391
|
|
|
|
14392
|
|
|
|
14393
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10893
|
14394
|
//////////////////// QCPData
|
|
10894
|
14395
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10895
|
14396
|
|
|
@@
-10948,7
+14449,9
QCPData::QCPData(double key, double valu
|
|
10948
|
14449
|
Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
|
|
10949
|
14450
|
instance is accessed via QCustomPlot::graph.
|
|
10950
|
14451
|
|
|
10951
|
|
To plot data, assign it with the \ref setData or \ref addData functions.
|
|
|
14452
|
To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
|
|
|
14453
|
also access and modify the graph's data via the \ref data method, which returns a pointer to the
|
|
|
14454
|
internal \ref QCPDataMap.
|
|
10952
|
14455
|
|
|
10953
|
14456
|
Graphs are used to display single-valued data. Single-valued means that there should only be one
|
|
10954
|
14457
|
data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
|
|
@@
-10972,6
+14475,17
QCPData::QCPData(double key, double valu
|
|
10972
|
14475
|
\see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph
|
|
10973
|
14476
|
*/
|
|
10974
|
14477
|
|
|
|
14478
|
/* start of documentation of inline functions */
|
|
|
14479
|
|
|
|
14480
|
/*! \fn QCPDataMap *QCPGraph::data() const
|
|
|
14481
|
|
|
|
14482
|
Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
|
|
|
14483
|
directly manipulate the data, which may be more convenient and faster than using the regular \ref
|
|
|
14484
|
setData or \ref addData methods, in certain situations.
|
|
|
14485
|
*/
|
|
|
14486
|
|
|
|
14487
|
/* end of documentation of inline functions */
|
|
|
14488
|
|
|
10975
|
14489
|
/*!
|
|
10976
|
14490
|
Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
|
|
10977
|
14491
|
axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
|
|
@@
-10999,6
+14513,7
QCPGraph::QCPGraph(QCPAxis *keyAxis, QCP
|
|
10999
|
14513
|
setErrorBarSize(6);
|
|
11000
|
14514
|
setErrorBarSkipSymbol(true);
|
|
11001
|
14515
|
setChannelFillGraph(0);
|
|
|
14516
|
setAdaptiveSampling(true);
|
|
11002
|
14517
|
}
|
|
11003
|
14518
|
|
|
11004
|
14519
|
QCPGraph::~QCPGraph()
|
|
@@
-11012,6
+14527,9
QCPGraph::~QCPGraph()
|
|
11012
|
14527
|
If \a copy is set to true, data points in \a data will only be copied. if false, the graph
|
|
11013
|
14528
|
takes ownership of the passed data and replaces the internal data pointer with it. This is
|
|
11014
|
14529
|
significantly faster than copying for large datasets.
|
|
|
14530
|
|
|
|
14531
|
Alternatively, you can also access and modify the graph's data via the \ref data method, which
|
|
|
14532
|
returns a pointer to the internal \ref QCPDataMap.
|
|
11015
|
14533
|
*/
|
|
11016
|
14534
|
void QCPGraph::setData(QCPDataMap *data, bool copy)
|
|
11017
|
14535
|
{
|
|
@@
-11310,7
+14828,47
void QCPGraph::setChannelFillGraph(QCPGr
|
|
11310
|
14828
|
}
|
|
11311
|
14829
|
|
|
11312
|
14830
|
/*!
|
|
|
14831
|
Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
|
|
|
14832
|
sampling technique can drastically improve the replot performance for graphs with a larger number
|
|
|
14833
|
of points (e.g. above 10,000), without notably changing the appearance of the graph.
|
|
|
14834
|
|
|
|
14835
|
By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
|
|
|
14836
|
sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
|
|
|
14837
|
disadvantage in almost all cases.
|
|
|
14838
|
|
|
|
14839
|
\image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
|
|
|
14840
|
|
|
|
14841
|
As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
|
|
|
14842
|
reproduced reliably, as well as the overall shape of the data set. The replot time reduces
|
|
|
14843
|
dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
|
|
|
14844
|
|
|
|
14845
|
\image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
|
|
|
14846
|
|
|
|
14847
|
Care must be taken when using high-density scatter plots in combination with adaptive sampling.
|
|
|
14848
|
The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
|
|
|
14849
|
gives a significant reduction of replot times, but not quite as much as for line plots. This is
|
|
|
14850
|
because scatter plots inherently need more data points to be preserved in order to still resemble
|
|
|
14851
|
the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
|
|
|
14852
|
identical, as banding occurs for the outer data points. This is in fact intentional, such that
|
|
|
14853
|
the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
|
|
|
14854
|
depends on the point density, i.e. the number of points in the plot.
|
|
|
14855
|
|
|
|
14856
|
For some situations with scatter plots it might thus be desirable to manually turn adaptive
|
|
|
14857
|
sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
|
|
|
14858
|
enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
|
|
|
14859
|
back to true afterwards.
|
|
|
14860
|
*/
|
|
|
14861
|
void QCPGraph::setAdaptiveSampling(bool enabled)
|
|
|
14862
|
{
|
|
|
14863
|
mAdaptiveSampling = enabled;
|
|
|
14864
|
}
|
|
|
14865
|
|
|
|
14866
|
/*!
|
|
11313
|
14867
|
Adds the provided data points in \a dataMap to the current data.
|
|
|
14868
|
|
|
|
14869
|
Alternatively, you can also access and modify the graph's data via the \ref data method, which
|
|
|
14870
|
returns a pointer to the internal \ref QCPDataMap.
|
|
|
14871
|
|
|
11314
|
14872
|
\see removeData
|
|
11315
|
14873
|
*/
|
|
11316
|
14874
|
void QCPGraph::addData(const QCPDataMap &dataMap)
|
|
@@
-11320,6
+14878,10
void QCPGraph::addData(const QCPDataMap
|
|
11320
|
14878
|
|
|
11321
|
14879
|
/*! \overload
|
|
11322
|
14880
|
Adds the provided single data point in \a data to the current data.
|
|
|
14881
|
|
|
|
14882
|
Alternatively, you can also access and modify the graph's data via the \ref data method, which
|
|
|
14883
|
returns a pointer to the internal \ref QCPDataMap.
|
|
|
14884
|
|
|
11323
|
14885
|
\see removeData
|
|
11324
|
14886
|
*/
|
|
11325
|
14887
|
void QCPGraph::addData(const QCPData &data)
|
|
@@
-11329,6
+14891,10
void QCPGraph::addData(const QCPData &da
|
|
11329
|
14891
|
|
|
11330
|
14892
|
/*! \overload
|
|
11331
|
14893
|
Adds the provided single data point as \a key and \a value pair to the current data.
|
|
|
14894
|
|
|
|
14895
|
Alternatively, you can also access and modify the graph's data via the \ref data method, which
|
|
|
14896
|
returns a pointer to the internal \ref QCPDataMap.
|
|
|
14897
|
|
|
11332
|
14898
|
\see removeData
|
|
11333
|
14899
|
*/
|
|
11334
|
14900
|
void QCPGraph::addData(double key, double value)
|
|
@@
-11341,6
+14907,10
void QCPGraph::addData(double key, doubl
|
|
11341
|
14907
|
|
|
11342
|
14908
|
/*! \overload
|
|
11343
|
14909
|
Adds the provided data points as \a key and \a value pairs to the current data.
|
|
|
14910
|
|
|
|
14911
|
Alternatively, you can also access and modify the graph's data via the \ref data method, which
|
|
|
14912
|
returns a pointer to the internal \ref QCPDataMap.
|
|
|
14913
|
|
|
11344
|
14914
|
\see removeData
|
|
11345
|
14915
|
*/
|
|
11346
|
14916
|
void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
|
|
@@
-11422,8
+14992,12
double QCPGraph::selectTest(const QPoint
|
|
11422
|
14992
|
Q_UNUSED(details)
|
|
11423
|
14993
|
if ((onlySelectable && !mSelectable) || mData->isEmpty())
|
|
11424
|
14994
|
return -1;
|
|
11425
|
|
|
|
|
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
|
14998
|
return pointDistance(pos);
|
|
|
14999
|
else
|
|
|
15000
|
return -1;
|
|
11427
|
15001
|
}
|
|
11428
|
15002
|
|
|
11429
|
15003
|
/*! \overload
|
|
@@
-11459,10
+15033,10
void QCPGraph::rescaleKeyAxis(bool onlyE
|
|
11459
|
15033
|
if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
|
|
11460
|
15034
|
signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
|
|
11461
|
15035
|
|
|
11462
|
|
bool validRange;
|
|
11463
|
|
QCPRange newRange = getKeyRange(validRange, signDomain, includeErrorBars);
|
|
11464
|
|
|
|
11465
|
|
if (validRange)
|
|
|
15036
|
bool foundRange;
|
|
|
15037
|
QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
|
|
|
15038
|
|
|
|
15039
|
if (foundRange)
|
|
11466
|
15040
|
{
|
|
11467
|
15041
|
if (onlyEnlarge)
|
|
11468
|
15042
|
{
|
|
@@
-11495,10
+15069,10
void QCPGraph::rescaleValueAxis(bool onl
|
|
11495
|
15069
|
if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
|
|
11496
|
15070
|
signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
|
|
11497
|
15071
|
|
|
11498
|
|
bool validRange;
|
|
11499
|
|
QCPRange newRange = getValueRange(validRange, signDomain, includeErrorBars);
|
|
11500
|
|
|
|
11501
|
|
if (validRange)
|
|
|
15072
|
bool foundRange;
|
|
|
15073
|
QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
|
|
|
15074
|
|
|
|
15075
|
if (foundRange)
|
|
11502
|
15076
|
{
|
|
11503
|
15077
|
if (onlyEnlarge)
|
|
11504
|
15078
|
{
|
|
@@
-11520,12
+15094,12
void QCPGraph::draw(QCPPainter *painter)
|
|
11520
|
15094
|
|
|
11521
|
15095
|
// allocate line and (if necessary) point vectors:
|
|
11522
|
15096
|
QVector<QPointF> *lineData = new QVector<QPointF>;
|
|
11523
|
|
QVector<QCPData> *pointData = 0;
|
|
|
15097
|
QVector<QCPData> *scatterData = 0;
|
|
11524
|
15098
|
if (!mScatterStyle.isNone())
|
|
11525
|
|
pointData = new QVector<QCPData>;
|
|
|
15099
|
scatterData = new QVector<QCPData>;
|
|
11526
|
15100
|
|
|
11527
|
15101
|
// fill vectors with data appropriate to plot style:
|
|
11528
|
|
getPlotData(lineData, pointData);
|
|
|
15102
|
getPlotData(lineData, scatterData);
|
|
11529
|
15103
|
|
|
11530
|
15104
|
// check data validity if flag set:
|
|
11531
|
15105
|
#ifdef QCUSTOMPLOT_CHECK_DATA
|
|
@@
-11549,13
+15123,13
void QCPGraph::draw(QCPPainter *painter)
|
|
11549
|
15123
|
drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
|
|
11550
|
15124
|
|
|
11551
|
15125
|
// draw scatters:
|
|
11552
|
|
if (pointData)
|
|
11553
|
|
drawScatterPlot(painter, pointData);
|
|
|
15126
|
if (scatterData)
|
|
|
15127
|
drawScatterPlot(painter, scatterData);
|
|
11554
|
15128
|
|
|
11555
|
15129
|
// free allocated line and point vectors:
|
|
11556
|
15130
|
delete lineData;
|
|
11557
|
|
if (pointData)
|
|
11558
|
|
delete pointData;
|
|
|
15131
|
if (scatterData)
|
|
|
15132
|
delete scatterData;
|
|
11559
|
15133
|
}
|
|
11560
|
15134
|
|
|
11561
|
15135
|
/* inherits documentation from base class */
|
|
@@
-11604,30
+15178,30
void QCPGraph::drawLegendIcon(QCPPainter
|
|
11604
|
15178
|
make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
|
|
11605
|
15179
|
untouched.
|
|
11606
|
15180
|
|
|
11607
|
|
\a pointData will be filled with the original data points so \ref drawScatterPlot can draw the
|
|
|
15181
|
\a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
|
|
11608
|
15182
|
scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
|
|
11609
|
|
\ref QCPScatterStyle::ssNone, pass 0 as \a pointData, and this step will be skipped.
|
|
|
15183
|
\ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
|
|
11610
|
15184
|
|
|
11611
|
15185
|
\see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
|
|
11612
|
15186
|
getStepCenterPlotData, getImpulsePlotData
|
|
11613
|
15187
|
*/
|
|
11614
|
|
void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
|
|
|
15188
|
void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
|
|
11615
|
15189
|
{
|
|
11616
|
15190
|
switch(mLineStyle)
|
|
11617
|
15191
|
{
|
|
11618
|
|
case lsNone: getScatterPlotData(pointData); break;
|
|
11619
|
|
case lsLine: getLinePlotData(lineData, pointData); break;
|
|
11620
|
|
case lsStepLeft: getStepLeftPlotData(lineData, pointData); break;
|
|
11621
|
|
case lsStepRight: getStepRightPlotData(lineData, pointData); break;
|
|
11622
|
|
case lsStepCenter: getStepCenterPlotData(lineData, pointData); break;
|
|
11623
|
|
case lsImpulse: getImpulsePlotData(lineData, pointData); break;
|
|
|
15192
|
case lsNone: getScatterPlotData(scatterData); break;
|
|
|
15193
|
case lsLine: getLinePlotData(lineData, scatterData); break;
|
|
|
15194
|
case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
|
|
|
15195
|
case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
|
|
|
15196
|
case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
|
|
|
15197
|
case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
|
|
11624
|
15198
|
}
|
|
11625
|
15199
|
}
|
|
11626
|
15200
|
|
|
11627
|
15201
|
/*! \internal
|
|
11628
|
15202
|
|
|
11629
|
15203
|
If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
|
|
11630
|
|
this function serves at providing the visible data points in \a pointData, so the \ref
|
|
|
15204
|
this function serves at providing the visible data points in \a scatterData, so the \ref
|
|
11631
|
15205
|
drawScatterPlot function can draw the scatter points accordingly.
|
|
11632
|
15206
|
|
|
11633
|
15207
|
If line style is not \ref lsNone, this function is not called and the data for the scatter points
|
|
@@
-11635,89
+15209,48
void QCPGraph::getPlotData(QVector<QPoin
|
|
11635
|
15209
|
|
|
11636
|
15210
|
\see drawScatterPlot
|
|
11637
|
15211
|
*/
|
|
11638
|
|
void QCPGraph::getScatterPlotData(QVector<QCPData> *pointData) const
|
|
11639
|
|
{
|
|
11640
|
|
if (!pointData) return;
|
|
11641
|
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
11642
|
|
if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
|
|
11643
|
|
|
|
11644
|
|
// get visible data range:
|
|
11645
|
|
QCPDataMap::const_iterator lower, upper;
|
|
11646
|
|
int dataCount = 0;
|
|
11647
|
|
getVisibleDataBounds(lower, upper, dataCount);
|
|
11648
|
|
if (dataCount > 0)
|
|
11649
|
|
{
|
|
11650
|
|
// prepare vectors:
|
|
11651
|
|
pointData->resize(dataCount);
|
|
11652
|
|
|
|
11653
|
|
// position data points:
|
|
11654
|
|
QCPDataMap::const_iterator it = lower;
|
|
11655
|
|
QCPDataMap::const_iterator upperEnd = upper+1;
|
|
11656
|
|
int i = 0;
|
|
11657
|
|
while (it != upperEnd)
|
|
11658
|
|
{
|
|
11659
|
|
(*pointData)[i] = it.value();
|
|
11660
|
|
++i;
|
|
11661
|
|
++it;
|
|
11662
|
|
}
|
|
11663
|
|
}
|
|
11664
|
|
}
|
|
11665
|
|
|
|
11666
|
|
/*! \internal
|
|
11667
|
|
|
|
11668
|
|
Places the raw data points needed for a normal linearly connected graph in \a lineData.
|
|
11669
|
|
|
|
11670
|
|
As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
|
|
|
15212
|
void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
|
|
|
15213
|
{
|
|
|
15214
|
getPreparedData(0, scatterData);
|
|
|
15215
|
}
|
|
|
15216
|
|
|
|
15217
|
/*! \internal
|
|
|
15218
|
|
|
|
15219
|
Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
|
|
|
15220
|
|
|
|
15221
|
As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
|
|
11671
|
15222
|
points that are visible for drawing scatter points, if necessary. If drawing scatter points is
|
|
11672
|
15223
|
disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
|
|
11673
|
|
pointData, and the function will skip filling the vector.
|
|
|
15224
|
scatterData, and the function will skip filling the vector.
|
|
11674
|
15225
|
|
|
11675
|
15226
|
\see drawLinePlot
|
|
11676
|
15227
|
*/
|
|
11677
|
|
void QCPGraph::getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
|
|
|
15228
|
void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
|
|
11678
|
15229
|
{
|
|
11679
|
15230
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
11680
|
15231
|
QCPAxis *valueAxis = mValueAxis.data();
|
|
11681
|
15232
|
if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
|
|
11682
|
|
if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
11683
|
|
|
|
11684
|
|
// get visible data range:
|
|
11685
|
|
QCPDataMap::const_iterator lower, upper;
|
|
11686
|
|
int dataCount = 0;
|
|
11687
|
|
getVisibleDataBounds(lower, upper, dataCount);
|
|
11688
|
|
if (dataCount > 0)
|
|
11689
|
|
{
|
|
11690
|
|
lineData->reserve(dataCount+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
11691
|
|
lineData->resize(dataCount);
|
|
11692
|
|
if (pointData)
|
|
11693
|
|
pointData->resize(dataCount);
|
|
11694
|
|
|
|
11695
|
|
// position data points:
|
|
11696
|
|
QCPDataMap::const_iterator it = lower;
|
|
11697
|
|
QCPDataMap::const_iterator upperEnd = upper+1;
|
|
11698
|
|
int i = 0;
|
|
|
15233
|
if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
|
|
|
15234
|
|
|
|
15235
|
QVector<QCPData> lineData;
|
|
|
15236
|
getPreparedData(&lineData, scatterData);
|
|
|
15237
|
linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
|
15238
|
linePixelData->resize(lineData.size());
|
|
|
15239
|
|
|
|
15240
|
// transform lineData points to pixels:
|
|
11699
|
15241
|
if (keyAxis->orientation() == Qt::Vertical)
|
|
11700
|
15242
|
{
|
|
11701
|
|
while (it != upperEnd)
|
|
11702
|
|
{
|
|
11703
|
|
if (pointData)
|
|
11704
|
|
(*pointData)[i] = it.value();
|
|
11705
|
|
(*lineData)[i].setX(valueAxis->coordToPixel(it.value().value));
|
|
11706
|
|
(*lineData)[i].setY(keyAxis->coordToPixel(it.key()));
|
|
11707
|
|
++i;
|
|
11708
|
|
++it;
|
|
|
15243
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15244
|
{
|
|
|
15245
|
(*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
|
|
|
15246
|
(*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
|
|
11709
|
15247
|
}
|
|
11710
|
15248
|
} else // key axis is horizontal
|
|
11711
|
15249
|
{
|
|
11712
|
|
while (it != upperEnd)
|
|
11713
|
|
{
|
|
11714
|
|
if (pointData)
|
|
11715
|
|
(*pointData)[i] = it.value();
|
|
11716
|
|
(*lineData)[i].setX(keyAxis->coordToPixel(it.key()));
|
|
11717
|
|
(*lineData)[i].setY(valueAxis->coordToPixel(it.value().value));
|
|
11718
|
|
++i;
|
|
11719
|
|
++it;
|
|
11720
|
|
}
|
|
|
15250
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15251
|
{
|
|
|
15252
|
(*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
|
|
|
15253
|
(*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
|
|
11721
|
15254
|
}
|
|
11722
|
15255
|
}
|
|
11723
|
15256
|
}
|
|
@@
-11726,78
+15259,51
void QCPGraph::getLinePlotData(QVector<Q
|
|
11726
|
15259
|
\internal
|
|
11727
|
15260
|
Places the raw data points needed for a step plot with left oriented steps in \a lineData.
|
|
11728
|
15261
|
|
|
11729
|
|
As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
|
|
|
15262
|
As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
|
|
11730
|
15263
|
points that are visible for drawing scatter points, if necessary. If drawing scatter points is
|
|
11731
|
15264
|
disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
|
|
11732
|
|
pointData, and the function will skip filling the vector.
|
|
|
15265
|
scatterData, and the function will skip filling the vector.
|
|
11733
|
15266
|
|
|
11734
|
15267
|
\see drawLinePlot
|
|
11735
|
15268
|
*/
|
|
11736
|
|
void QCPGraph::getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
|
|
|
15269
|
void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
|
|
11737
|
15270
|
{
|
|
11738
|
15271
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
11739
|
15272
|
QCPAxis *valueAxis = mValueAxis.data();
|
|
11740
|
15273
|
if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
|
|
11741
|
|
if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
11742
|
|
|
|
11743
|
|
// get visible data range:
|
|
11744
|
|
QCPDataMap::const_iterator lower, upper;
|
|
11745
|
|
int dataCount = 0;
|
|
11746
|
|
getVisibleDataBounds(lower, upper, dataCount);
|
|
11747
|
|
if (dataCount > 0)
|
|
11748
|
|
{
|
|
11749
|
|
lineData->reserve(dataCount*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
11750
|
|
lineData->resize(dataCount*2); // multiplied by 2 because step plot needs two polyline points per one actual data point
|
|
11751
|
|
if (pointData)
|
|
11752
|
|
pointData->resize(dataCount);
|
|
11753
|
|
|
|
11754
|
|
// position data points:
|
|
11755
|
|
QCPDataMap::const_iterator it = lower;
|
|
11756
|
|
QCPDataMap::const_iterator upperEnd = upper+1;
|
|
11757
|
|
int i = 0;
|
|
11758
|
|
int ipoint = 0;
|
|
|
15274
|
if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
|
15275
|
|
|
|
15276
|
QVector<QCPData> lineData;
|
|
|
15277
|
getPreparedData(&lineData, scatterData);
|
|
|
15278
|
linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
|
15279
|
linePixelData->resize(lineData.size()*2);
|
|
|
15280
|
|
|
|
15281
|
// calculate steps from lineData and transform to pixel coordinates:
|
|
11759
|
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
|
15285
|
double key;
|
|
11763
|
|
while (it != upperEnd)
|
|
11764
|
|
{
|
|
11765
|
|
if (pointData)
|
|
11766
|
|
{
|
|
11767
|
|
(*pointData)[ipoint] = it.value();
|
|
11768
|
|
++ipoint;
|
|
11769
|
|
}
|
|
11770
|
|
key = keyAxis->coordToPixel(it.key());
|
|
11771
|
|
(*lineData)[i].setX(lastValue);
|
|
11772
|
|
(*lineData)[i].setY(key);
|
|
11773
|
|
++i;
|
|
11774
|
|
lastValue = valueAxis->coordToPixel(it.value().value);
|
|
11775
|
|
(*lineData)[i].setX(lastValue);
|
|
11776
|
|
(*lineData)[i].setY(key);
|
|
11777
|
|
++i;
|
|
11778
|
|
++it;
|
|
|
15286
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15287
|
{
|
|
|
15288
|
key = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15289
|
(*linePixelData)[i*2+0].setX(lastValue);
|
|
|
15290
|
(*linePixelData)[i*2+0].setY(key);
|
|
|
15291
|
lastValue = valueAxis->coordToPixel(lineData.at(i).value);
|
|
|
15292
|
(*linePixelData)[i*2+1].setX(lastValue);
|
|
|
15293
|
(*linePixelData)[i*2+1].setY(key);
|
|
11779
|
15294
|
}
|
|
11780
|
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
|
15298
|
double key;
|
|
11784
|
|
while (it != upperEnd)
|
|
11785
|
|
{
|
|
11786
|
|
if (pointData)
|
|
11787
|
|
{
|
|
11788
|
|
(*pointData)[ipoint] = it.value();
|
|
11789
|
|
++ipoint;
|
|
11790
|
|
}
|
|
11791
|
|
key = keyAxis->coordToPixel(it.key());
|
|
11792
|
|
(*lineData)[i].setX(key);
|
|
11793
|
|
(*lineData)[i].setY(lastValue);
|
|
11794
|
|
++i;
|
|
11795
|
|
lastValue = valueAxis->coordToPixel(it.value().value);
|
|
11796
|
|
(*lineData)[i].setX(key);
|
|
11797
|
|
(*lineData)[i].setY(lastValue);
|
|
11798
|
|
++i;
|
|
11799
|
|
++it;
|
|
11800
|
|
}
|
|
|
15299
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15300
|
{
|
|
|
15301
|
key = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15302
|
(*linePixelData)[i*2+0].setX(key);
|
|
|
15303
|
(*linePixelData)[i*2+0].setY(lastValue);
|
|
|
15304
|
lastValue = valueAxis->coordToPixel(lineData.at(i).value);
|
|
|
15305
|
(*linePixelData)[i*2+1].setX(key);
|
|
|
15306
|
(*linePixelData)[i*2+1].setY(lastValue);
|
|
11801
|
15307
|
}
|
|
11802
|
15308
|
}
|
|
11803
|
15309
|
}
|
|
@@
-11806,78
+15312,51
void QCPGraph::getStepLeftPlotData(QVect
|
|
11806
|
15312
|
\internal
|
|
11807
|
15313
|
Places the raw data points needed for a step plot with right oriented steps in \a lineData.
|
|
11808
|
15314
|
|
|
11809
|
|
As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
|
|
|
15315
|
As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
|
|
11810
|
15316
|
points that are visible for drawing scatter points, if necessary. If drawing scatter points is
|
|
11811
|
15317
|
disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
|
|
11812
|
|
pointData, and the function will skip filling the vector.
|
|
|
15318
|
scatterData, and the function will skip filling the vector.
|
|
11813
|
15319
|
|
|
11814
|
15320
|
\see drawLinePlot
|
|
11815
|
15321
|
*/
|
|
11816
|
|
void QCPGraph::getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
|
|
|
15322
|
void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
|
|
11817
|
15323
|
{
|
|
11818
|
15324
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
11819
|
15325
|
QCPAxis *valueAxis = mValueAxis.data();
|
|
11820
|
15326
|
if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
|
|
11821
|
|
if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
11822
|
|
|
|
11823
|
|
// get visible data range:
|
|
11824
|
|
QCPDataMap::const_iterator lower, upper;
|
|
11825
|
|
int dataCount = 0;
|
|
11826
|
|
getVisibleDataBounds(lower, upper, dataCount);
|
|
11827
|
|
if (dataCount > 0)
|
|
11828
|
|
{
|
|
11829
|
|
lineData->reserve(dataCount*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
11830
|
|
lineData->resize(dataCount*2); // multiplied by 2 because step plot needs two polyline points per one actual data point
|
|
11831
|
|
if (pointData)
|
|
11832
|
|
pointData->resize(dataCount);
|
|
11833
|
|
|
|
11834
|
|
// position points:
|
|
11835
|
|
QCPDataMap::const_iterator it = lower;
|
|
11836
|
|
QCPDataMap::const_iterator upperEnd = upper+1;
|
|
11837
|
|
int i = 0;
|
|
11838
|
|
int ipoint = 0;
|
|
|
15327
|
if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
|
15328
|
|
|
|
15329
|
QVector<QCPData> lineData;
|
|
|
15330
|
getPreparedData(&lineData, scatterData);
|
|
|
15331
|
linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
|
15332
|
linePixelData->resize(lineData.size()*2);
|
|
|
15333
|
|
|
|
15334
|
// calculate steps from lineData and transform to pixel coordinates:
|
|
11839
|
15335
|
if (keyAxis->orientation() == Qt::Vertical)
|
|
11840
|
15336
|
{
|
|
11841
|
|
double lastKey = keyAxis->coordToPixel(it.key());
|
|
|
15337
|
double lastKey = keyAxis->coordToPixel(lineData.first().key);
|
|
11842
|
15338
|
double value;
|
|
11843
|
|
while (it != upperEnd)
|
|
11844
|
|
{
|
|
11845
|
|
if (pointData)
|
|
11846
|
|
{
|
|
11847
|
|
(*pointData)[ipoint] = it.value();
|
|
11848
|
|
++ipoint;
|
|
11849
|
|
}
|
|
11850
|
|
value = valueAxis->coordToPixel(it.value().value);
|
|
11851
|
|
(*lineData)[i].setX(value);
|
|
11852
|
|
(*lineData)[i].setY(lastKey);
|
|
11853
|
|
++i;
|
|
11854
|
|
lastKey = keyAxis->coordToPixel(it.key());
|
|
11855
|
|
(*lineData)[i].setX(value);
|
|
11856
|
|
(*lineData)[i].setY(lastKey);
|
|
11857
|
|
++i;
|
|
11858
|
|
++it;
|
|
|
15339
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15340
|
{
|
|
|
15341
|
value = valueAxis->coordToPixel(lineData.at(i).value);
|
|
|
15342
|
(*linePixelData)[i*2+0].setX(value);
|
|
|
15343
|
(*linePixelData)[i*2+0].setY(lastKey);
|
|
|
15344
|
lastKey = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15345
|
(*linePixelData)[i*2+1].setX(value);
|
|
|
15346
|
(*linePixelData)[i*2+1].setY(lastKey);
|
|
11859
|
15347
|
}
|
|
11860
|
15348
|
} else // key axis is horizontal
|
|
11861
|
15349
|
{
|
|
11862
|
|
double lastKey = keyAxis->coordToPixel(it.key());
|
|
|
15350
|
double lastKey = keyAxis->coordToPixel(lineData.first().key);
|
|
11863
|
15351
|
double value;
|
|
11864
|
|
while (it != upperEnd)
|
|
11865
|
|
{
|
|
11866
|
|
if (pointData)
|
|
11867
|
|
{
|
|
11868
|
|
(*pointData)[ipoint] = it.value();
|
|
11869
|
|
++ipoint;
|
|
11870
|
|
}
|
|
11871
|
|
value = valueAxis->coordToPixel(it.value().value);
|
|
11872
|
|
(*lineData)[i].setX(lastKey);
|
|
11873
|
|
(*lineData)[i].setY(value);
|
|
11874
|
|
++i;
|
|
11875
|
|
lastKey = keyAxis->coordToPixel(it.key());
|
|
11876
|
|
(*lineData)[i].setX(lastKey);
|
|
11877
|
|
(*lineData)[i].setY(value);
|
|
11878
|
|
++i;
|
|
11879
|
|
++it;
|
|
11880
|
|
}
|
|
|
15352
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15353
|
{
|
|
|
15354
|
value = valueAxis->coordToPixel(lineData.at(i).value);
|
|
|
15355
|
(*linePixelData)[i*2+0].setX(lastKey);
|
|
|
15356
|
(*linePixelData)[i*2+0].setY(value);
|
|
|
15357
|
lastKey = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15358
|
(*linePixelData)[i*2+1].setX(lastKey);
|
|
|
15359
|
(*linePixelData)[i*2+1].setY(value);
|
|
11881
|
15360
|
}
|
|
11882
|
15361
|
}
|
|
11883
|
15362
|
}
|
|
@@
-11886,183
+15365,113
void QCPGraph::getStepRightPlotData(QVec
|
|
11886
|
15365
|
\internal
|
|
11887
|
15366
|
Places the raw data points needed for a step plot with centered steps in \a lineData.
|
|
11888
|
15367
|
|
|
11889
|
|
As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
|
|
|
15368
|
As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
|
|
11890
|
15369
|
points that are visible for drawing scatter points, if necessary. If drawing scatter points is
|
|
11891
|
15370
|
disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
|
|
11892
|
|
pointData, and the function will skip filling the vector.
|
|
|
15371
|
scatterData, and the function will skip filling the vector.
|
|
11893
|
15372
|
|
|
11894
|
15373
|
\see drawLinePlot
|
|
11895
|
15374
|
*/
|
|
11896
|
|
void QCPGraph::getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
|
|
|
15375
|
void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
|
|
11897
|
15376
|
{
|
|
11898
|
15377
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
11899
|
15378
|
QCPAxis *valueAxis = mValueAxis.data();
|
|
11900
|
15379
|
if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
|
|
11901
|
|
if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
11902
|
|
|
|
11903
|
|
// get visible data range:
|
|
11904
|
|
QCPDataMap::const_iterator lower, upper;
|
|
11905
|
|
int dataCount = 0;
|
|
11906
|
|
getVisibleDataBounds(lower, upper, dataCount);
|
|
11907
|
|
if (dataCount > 0)
|
|
11908
|
|
{
|
|
11909
|
|
// added 2 to reserve memory for lower/upper fill base points that might be needed for base fill
|
|
11910
|
|
// multiplied by 2 because step plot needs two polyline points per one actual data point
|
|
11911
|
|
lineData->reserve(dataCount*2+2);
|
|
11912
|
|
lineData->resize(dataCount*2);
|
|
11913
|
|
if (pointData)
|
|
11914
|
|
pointData->resize(dataCount);
|
|
11915
|
|
|
|
11916
|
|
// position points:
|
|
11917
|
|
QCPDataMap::const_iterator it = lower;
|
|
11918
|
|
QCPDataMap::const_iterator upperEnd = upper+1;
|
|
11919
|
|
int i = 0;
|
|
11920
|
|
int ipoint = 0;
|
|
|
15380
|
if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
|
15381
|
|
|
|
15382
|
QVector<QCPData> lineData;
|
|
|
15383
|
getPreparedData(&lineData, scatterData);
|
|
|
15384
|
linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
|
|
|
15385
|
linePixelData->resize(lineData.size()*2);
|
|
|
15386
|
// calculate steps from lineData and transform to pixel coordinates:
|
|
11921
|
15387
|
if (keyAxis->orientation() == Qt::Vertical)
|
|
11922
|
15388
|
{
|
|
11923
|
|
double lastKey = keyAxis->coordToPixel(it.key());
|
|
11924
|
|
double lastValue = valueAxis->coordToPixel(it.value().value);
|
|
|
15389
|
double lastKey = keyAxis->coordToPixel(lineData.first().key);
|
|
|
15390
|
double lastValue = valueAxis->coordToPixel(lineData.first().value);
|
|
11925
|
15391
|
double key;
|
|
11926
|
|
if (pointData)
|
|
11927
|
|
{
|
|
11928
|
|
(*pointData)[ipoint] = it.value();
|
|
11929
|
|
++ipoint;
|
|
11930
|
|
}
|
|
11931
|
|
(*lineData)[i].setX(lastValue);
|
|
11932
|
|
(*lineData)[i].setY(lastKey);
|
|
11933
|
|
++it;
|
|
11934
|
|
++i;
|
|
11935
|
|
while (it != upperEnd)
|
|
11936
|
|
{
|
|
11937
|
|
if (pointData)
|
|
11938
|
|
{
|
|
11939
|
|
(*pointData)[ipoint] = it.value();
|
|
11940
|
|
++ipoint;
|
|
11941
|
|
}
|
|
11942
|
|
key = (keyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
|
|
11943
|
|
(*lineData)[i].setX(lastValue);
|
|
11944
|
|
(*lineData)[i].setY(key);
|
|
11945
|
|
++i;
|
|
11946
|
|
lastValue = valueAxis->coordToPixel(it.value().value);
|
|
11947
|
|
lastKey = keyAxis->coordToPixel(it.key());
|
|
11948
|
|
(*lineData)[i].setX(lastValue);
|
|
11949
|
|
(*lineData)[i].setY(key);
|
|
11950
|
|
++it;
|
|
11951
|
|
++i;
|
|
11952
|
|
}
|
|
11953
|
|
(*lineData)[i].setX(lastValue);
|
|
11954
|
|
(*lineData)[i].setY(lastKey);
|
|
|
15392
|
(*linePixelData)[0].setX(lastValue);
|
|
|
15393
|
(*linePixelData)[0].setY(lastKey);
|
|
|
15394
|
for (int i=1; i<lineData.size(); ++i)
|
|
|
15395
|
{
|
|
|
15396
|
key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
|
|
|
15397
|
(*linePixelData)[i*2-1].setX(lastValue);
|
|
|
15398
|
(*linePixelData)[i*2-1].setY(key);
|
|
|
15399
|
lastValue = valueAxis->coordToPixel(lineData.at(i).value);
|
|
|
15400
|
lastKey = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15401
|
(*linePixelData)[i*2+0].setX(lastValue);
|
|
|
15402
|
(*linePixelData)[i*2+0].setY(key);
|
|
|
15403
|
}
|
|
|
15404
|
(*linePixelData)[lineData.size()*2-1].setX(lastValue);
|
|
|
15405
|
(*linePixelData)[lineData.size()*2-1].setY(lastKey);
|
|
11955
|
15406
|
} else // key axis is horizontal
|
|
11956
|
15407
|
{
|
|
11957
|
|
double lastKey = keyAxis->coordToPixel(it.key());
|
|
11958
|
|
double lastValue = valueAxis->coordToPixel(it.value().value);
|
|
|
15408
|
double lastKey = keyAxis->coordToPixel(lineData.first().key);
|
|
|
15409
|
double lastValue = valueAxis->coordToPixel(lineData.first().value);
|
|
11959
|
15410
|
double key;
|
|
11960
|
|
if (pointData)
|
|
11961
|
|
{
|
|
11962
|
|
(*pointData)[ipoint] = it.value();
|
|
11963
|
|
++ipoint;
|
|
11964
|
|
}
|
|
11965
|
|
(*lineData)[i].setX(lastKey);
|
|
11966
|
|
(*lineData)[i].setY(lastValue);
|
|
11967
|
|
++it;
|
|
11968
|
|
++i;
|
|
11969
|
|
while (it != upperEnd)
|
|
11970
|
|
{
|
|
11971
|
|
if (pointData)
|
|
11972
|
|
{
|
|
11973
|
|
(*pointData)[ipoint] = it.value();
|
|
11974
|
|
++ipoint;
|
|
11975
|
|
}
|
|
11976
|
|
key = (keyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
|
|
11977
|
|
(*lineData)[i].setX(key);
|
|
11978
|
|
(*lineData)[i].setY(lastValue);
|
|
11979
|
|
++i;
|
|
11980
|
|
lastValue = valueAxis->coordToPixel(it.value().value);
|
|
11981
|
|
lastKey = keyAxis->coordToPixel(it.key());
|
|
11982
|
|
(*lineData)[i].setX(key);
|
|
11983
|
|
(*lineData)[i].setY(lastValue);
|
|
11984
|
|
++it;
|
|
11985
|
|
++i;
|
|
11986
|
|
}
|
|
11987
|
|
(*lineData)[i].setX(lastKey);
|
|
11988
|
|
(*lineData)[i].setY(lastValue);
|
|
11989
|
|
}
|
|
11990
|
|
}
|
|
|
15411
|
(*linePixelData)[0].setX(lastKey);
|
|
|
15412
|
(*linePixelData)[0].setY(lastValue);
|
|
|
15413
|
for (int i=1; i<lineData.size(); ++i)
|
|
|
15414
|
{
|
|
|
15415
|
key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
|
|
|
15416
|
(*linePixelData)[i*2-1].setX(key);
|
|
|
15417
|
(*linePixelData)[i*2-1].setY(lastValue);
|
|
|
15418
|
lastValue = valueAxis->coordToPixel(lineData.at(i).value);
|
|
|
15419
|
lastKey = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15420
|
(*linePixelData)[i*2+0].setX(key);
|
|
|
15421
|
(*linePixelData)[i*2+0].setY(lastValue);
|
|
|
15422
|
}
|
|
|
15423
|
(*linePixelData)[lineData.size()*2-1].setX(lastKey);
|
|
|
15424
|
(*linePixelData)[lineData.size()*2-1].setY(lastValue);
|
|
|
15425
|
}
|
|
|
15426
|
|
|
11991
|
15427
|
}
|
|
11992
|
15428
|
|
|
11993
|
15429
|
/*!
|
|
11994
|
15430
|
\internal
|
|
11995
|
15431
|
Places the raw data points needed for an impulse plot in \a lineData.
|
|
11996
|
15432
|
|
|
11997
|
|
As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
|
|
|
15433
|
As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
|
|
11998
|
15434
|
points that are visible for drawing scatter points, if necessary. If drawing scatter points is
|
|
11999
|
15435
|
disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
|
|
12000
|
|
pointData, and the function will skip filling the vector.
|
|
|
15436
|
scatterData, and the function will skip filling the vector.
|
|
12001
|
15437
|
|
|
12002
|
15438
|
\see drawImpulsePlot
|
|
12003
|
15439
|
*/
|
|
12004
|
|
void QCPGraph::getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
|
|
|
15440
|
void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
|
|
12005
|
15441
|
{
|
|
12006
|
15442
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
12007
|
15443
|
QCPAxis *valueAxis = mValueAxis.data();
|
|
12008
|
15444
|
if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
|
|
12009
|
|
if (!lineData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
|
|
12010
|
|
|
|
12011
|
|
// get visible data range:
|
|
12012
|
|
QCPDataMap::const_iterator lower, upper;
|
|
12013
|
|
int dataCount = 0;
|
|
12014
|
|
getVisibleDataBounds(lower, upper, dataCount);
|
|
12015
|
|
if (dataCount > 0)
|
|
12016
|
|
{
|
|
12017
|
|
lineData->resize(dataCount*2); // no need to reserve 2 extra points, because there is no fill for impulse plot
|
|
12018
|
|
if (pointData)
|
|
12019
|
|
pointData->resize(dataCount);
|
|
12020
|
|
|
|
12021
|
|
// position data points:
|
|
12022
|
|
QCPDataMap::const_iterator it = lower;
|
|
12023
|
|
QCPDataMap::const_iterator upperEnd = upper+1;
|
|
12024
|
|
int i = 0;
|
|
12025
|
|
int ipoint = 0;
|
|
|
15445
|
if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
|
|
|
15446
|
|
|
|
15447
|
QVector<QCPData> lineData;
|
|
|
15448
|
getPreparedData(&lineData, scatterData);
|
|
|
15449
|
linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
|
|
|
15450
|
|
|
|
15451
|
// transform lineData points to pixels:
|
|
12026
|
15452
|
if (keyAxis->orientation() == Qt::Vertical)
|
|
12027
|
15453
|
{
|
|
12028
|
15454
|
double zeroPointX = valueAxis->coordToPixel(0);
|
|
12029
|
15455
|
double key;
|
|
12030
|
|
while (it != upperEnd)
|
|
12031
|
|
{
|
|
12032
|
|
if (pointData)
|
|
12033
|
|
{
|
|
12034
|
|
(*pointData)[ipoint] = it.value();
|
|
12035
|
|
++ipoint;
|
|
12036
|
|
}
|
|
12037
|
|
key = keyAxis->coordToPixel(it.key());
|
|
12038
|
|
(*lineData)[i].setX(zeroPointX);
|
|
12039
|
|
(*lineData)[i].setY(key);
|
|
12040
|
|
++i;
|
|
12041
|
|
(*lineData)[i].setX(valueAxis->coordToPixel(it.value().value));
|
|
12042
|
|
(*lineData)[i].setY(key);
|
|
12043
|
|
++i;
|
|
12044
|
|
++it;
|
|
|
15456
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15457
|
{
|
|
|
15458
|
key = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15459
|
(*linePixelData)[i*2+0].setX(zeroPointX);
|
|
|
15460
|
(*linePixelData)[i*2+0].setY(key);
|
|
|
15461
|
(*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
|
|
|
15462
|
(*linePixelData)[i*2+1].setY(key);
|
|
12045
|
15463
|
}
|
|
12046
|
15464
|
} else // key axis is horizontal
|
|
12047
|
15465
|
{
|
|
12048
|
15466
|
double zeroPointY = valueAxis->coordToPixel(0);
|
|
12049
|
15467
|
double key;
|
|
12050
|
|
while (it != upperEnd)
|
|
12051
|
|
{
|
|
12052
|
|
if (pointData)
|
|
12053
|
|
{
|
|
12054
|
|
(*pointData)[ipoint] = it.value();
|
|
12055
|
|
++ipoint;
|
|
12056
|
|
}
|
|
12057
|
|
key = keyAxis->coordToPixel(it.key());
|
|
12058
|
|
(*lineData)[i].setX(key);
|
|
12059
|
|
(*lineData)[i].setY(zeroPointY);
|
|
12060
|
|
++i;
|
|
12061
|
|
(*lineData)[i].setX(key);
|
|
12062
|
|
(*lineData)[i].setY(valueAxis->coordToPixel(it.value().value));
|
|
12063
|
|
++i;
|
|
12064
|
|
++it;
|
|
12065
|
|
}
|
|
|
15468
|
for (int i=0; i<lineData.size(); ++i)
|
|
|
15469
|
{
|
|
|
15470
|
key = keyAxis->coordToPixel(lineData.at(i).key);
|
|
|
15471
|
(*linePixelData)[i*2+0].setX(key);
|
|
|
15472
|
(*linePixelData)[i*2+0].setY(zeroPointY);
|
|
|
15473
|
(*linePixelData)[i*2+1].setX(key);
|
|
|
15474
|
(*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
|
|
12066
|
15475
|
}
|
|
12067
|
15476
|
}
|
|
12068
|
15477
|
}
|
|
@@
-12105,14
+15514,14
void QCPGraph::drawFill(QCPPainter *pain
|
|
12105
|
15514
|
|
|
12106
|
15515
|
/*! \internal
|
|
12107
|
15516
|
|
|
12108
|
|
Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent
|
|
|
15517
|
Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
|
|
12109
|
15518
|
of the line style and are always drawn if the scatter style's shape is not \ref
|
|
12110
|
|
QCPScatterStyle::ssNone. Hence, the \a pointData vector is outputted by all "get(...)PlotData"
|
|
|
15519
|
QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
|
|
12111
|
15520
|
functions, together with the (line style dependent) line data.
|
|
12112
|
15521
|
|
|
12113
|
15522
|
\see drawLinePlot, drawImpulsePlot
|
|
12114
|
15523
|
*/
|
|
12115
|
|
void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const
|
|
|
15524
|
void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
|
|
12116
|
15525
|
{
|
|
12117
|
15526
|
QCPAxis *keyAxis = mKeyAxis.data();
|
|
12118
|
15527
|
QCPAxis *valueAxis = mValueAxis.data();
|
|
@@
-12125,12
+15534,12
void QCPGraph::drawScatterPlot(QCPPainte
|
|
12125
|
15534
|
painter->setPen(mErrorPen);
|
|
12126
|
15535
|
if (keyAxis->orientation() == Qt::Vertical)
|
|
12127
|
15536
|
{
|
|
12128
|
|
for (int i=0; i<pointData->size(); ++i)
|
|
12129
|
|
drawError(painter, valueAxis->coordToPixel(pointData->at(i).value), keyAxis->coordToPixel(pointData->at(i).key), pointData->at(i));
|
|
12130
|
|
} else
|
|
12131
|
|
{
|
|
12132
|
|
for (int i=0; i<pointData->size(); ++i)
|
|
12133
|
|
drawError(painter, keyAxis->coordToPixel(pointData->at(i).key), valueAxis->coordToPixel(pointData->at(i).value), pointData->at(i));
|
|
|
15537
|
for (int i=0; i<scatterData->size(); ++i)
|
|
|
15538
|
drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
|
|
|
15539
|
} else
|
|
|
15540
|
{
|
|
|
15541
|
for (int i=0; i<scatterData->size(); ++i)
|
|
|
15542
|
drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
|
|
12134
|
15543
|
}
|
|
12135
|
15544
|
}
|
|
12136
|
15545
|
|
|
@@
-12139,12
+15548,12
void QCPGraph::drawScatterPlot(QCPPainte
|
|
12139
|
15548
|
mScatterStyle.applyTo(painter, mPen);
|
|
12140
|
15549
|
if (keyAxis->orientation() == Qt::Vertical)
|
|
12141
|
15550
|
{
|
|
12142
|
|
for (int i=0; i<pointData->size(); ++i)
|
|
12143
|
|
mScatterStyle.drawShape(painter, valueAxis->coordToPixel(pointData->at(i).value), keyAxis->coordToPixel(pointData->at(i).key));
|
|
12144
|
|
} else
|
|
12145
|
|
{
|
|
12146
|
|
for (int i=0; i<pointData->size(); ++i)
|
|
12147
|
|
mScatterStyle.drawShape(painter, keyAxis->coordToPixel(pointData->at(i).key), valueAxis->coordToPixel(pointData->at(i).value));
|
|
|
15551
|
for (int i=0; i<scatterData->size(); ++i)
|
|
|
15552
|
mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
|
|
|
15553
|
} else
|
|
|
15554
|
{
|
|
|
15555
|
for (int i=0; i<scatterData->size(); ++i)
|
|
|
15556
|
mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
|
|
12148
|
15557
|
}
|
|
12149
|
15558
|
}
|
|
12150
|
15559
|
|
|
@@
-12219,6
+15628,199
void QCPGraph::drawImpulsePlot(QCPPainte
|
|
12219
|
15628
|
|
|
12220
|
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
|
15824
|
called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
|
|
12223
|
15825
|
point. \a x and \a y pixel positions of the data point are passed since they are already known in
|
|
12224
|
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
|
15924
|
/*! \internal
|
|
12323
|
15925
|
|
|
12324
|
|
called by the specific plot data generating functions "get(...)PlotData" to determine which data
|
|
12325
|
|
range is visible, so only that needs to be processed.
|
|
|
15926
|
called by \ref getPreparedData to determine which data (key) range is visible at the current key
|
|
|
15927
|
axis range setting, so only that needs to be processed.
|
|
12326
|
15928
|
|
|
12327
|
15929
|
\a lower returns an iterator to the lowest data point that needs to be taken into account when
|
|
12328
|
|
plotting. Note that in order to get a clean plot all the way to the edge of the axes, \a lower
|
|
12329
|
|
may still be outside the visible range.
|
|
|
15930
|
plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
|
|
|
15931
|
lower may still be just outside the visible range.
|
|
12330
|
15932
|
|
|
12331
|
15933
|
\a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
|
|
12332
|
|
outside of the visible range.
|
|
12333
|
|
|
|
12334
|
|
\a count number of data points that need plotting, i.e. points between \a lower and \a upper,
|
|
12335
|
|
including them. This is useful for allocating the array of <tt>QPointF</tt>s in the specific
|
|
12336
|
|
drawing functions.
|
|
12337
|
|
|
|
12338
|
|
if the graph contains no data, \a count is zero and both \a lower and \a upper point to constEnd.
|
|
12339
|
|
*/
|
|
12340
|
|
void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const
|
|
|
15934
|
just outside of the visible range.
|
|
|
15935
|
|
|
|
15936
|
if the graph contains no data, both \a lower and \a upper point to constEnd.
|
|
|
15937
|
*/
|
|
|
15938
|
void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
|
|
12341
|
15939
|
{
|
|
12342
|
15940
|
if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
|
|
12343
|
15941
|
if (mData->isEmpty())
|
|
12344
|
15942
|
{
|
|
12345
|
15943
|
lower = mData->constEnd();
|
|
12346
|
15944
|
upper = mData->constEnd();
|
|
12347
|
|
count = 0;
|
|
12348
|
15945
|
return;
|
|
12349
|
15946
|
}
|
|
12350
|
15947
|
|
|
@@
-12356,15
+15953,30
void QCPGraph::getVisibleDataBounds(QCPD
|
|
12356
|
15953
|
|
|
12357
|
15954
|
lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
|
|
12358
|
15955
|
upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
|
|
12359
|
|
|
|
12360
|
|
// count number of points in range lower to upper (including them), so we can allocate array for them in draw functions:
|
|
|
15956
|
}
|
|
|
15957
|
|
|
|
15958
|
/*! \internal
|
|
|
15959
|
|
|
|
15960
|
Counts the number of data points between \a lower and \a upper (including them), up to a maximum
|
|
|
15961
|
of \a maxCount.
|
|
|
15962
|
|
|
|
15963
|
This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
|
|
|
15964
|
used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
|
|
|
15965
|
only needs to be done until \a maxCount is reached, which should be set to the number of data
|
|
|
15966
|
points at which adaptive sampling sets in.
|
|
|
15967
|
*/
|
|
|
15968
|
int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
|
|
|
15969
|
{
|
|
|
15970
|
if (upper == mData->constEnd() && lower == mData->constEnd())
|
|
|
15971
|
return 0;
|
|
12361
|
15972
|
QCPDataMap::const_iterator it = lower;
|
|
12362
|
|
count = 1;
|
|
12363
|
|
while (it != upper)
|
|
|
15973
|
int count = 1;
|
|
|
15974
|
while (it != upper && count < maxCount)
|
|
12364
|
15975
|
{
|
|
12365
|
15976
|
++it;
|
|
12366
|
15977
|
++count;
|
|
12367
|
15978
|
}
|
|
|
15979
|
return count;
|
|
12368
|
15980
|
}
|
|
12369
|
15981
|
|
|
12370
|
15982
|
/*! \internal
|
|
@@
-12779,20
+16391,20
double QCPGraph::pointDistance(const QPo
|
|
12779
|
16391
|
if (mLineStyle == lsNone)
|
|
12780
|
16392
|
{
|
|
12781
|
16393
|
// no line displayed, only calculate distance to scatter points:
|
|
12782
|
|
QVector<QCPData> *pointData = new QVector<QCPData>;
|
|
12783
|
|
getScatterPlotData(pointData);
|
|
|
16394
|
QVector<QCPData> *scatterData = new QVector<QCPData>;
|
|
|
16395
|
getScatterPlotData(scatterData);
|
|
12784
|
16396
|
double minDistSqr = std::numeric_limits<double>::max();
|
|
12785
|
16397
|
QPointF ptA;
|
|
12786
|
|
QPointF ptB = coordsToPixels(pointData->at(0).key, pointData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
|
|
12787
|
|
for (int i=1; i<pointData->size(); ++i)
|
|
|
16398
|
QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
|
|
|
16399
|
for (int i=1; i<scatterData->size(); ++i)
|
|
12788
|
16400
|
{
|
|
12789
|
16401
|
ptA = ptB;
|
|
12790
|
|
ptB = coordsToPixels(pointData->at(i).key, pointData->at(i).value);
|
|
|
16402
|
ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value);
|
|
12791
|
16403
|
double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
|
|
12792
|
16404
|
if (currentDistSqr < minDistSqr)
|
|
12793
|
16405
|
minDistSqr = currentDistSqr;
|
|
12794
|
16406
|
}
|
|
12795
|
|
delete pointData;
|
|
|
16407
|
delete scatterData;
|
|
12796
|
16408
|
return sqrt(minDistSqr);
|
|
12797
|
16409
|
} else
|
|
12798
|
16410
|
{
|
|
@@
-12848,28
+16460,28
int QCPGraph::findIndexBelowY(const QVec
|
|
12848
|
16460
|
}
|
|
12849
|
16461
|
|
|
12850
|
16462
|
/* inherits documentation from base class */
|
|
12851
|
|
QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain) const
|
|
|
16463
|
QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
|
|
12852
|
16464
|
{
|
|
12853
|
16465
|
// just call the specialized version which takes an additional argument whether error bars
|
|
12854
|
16466
|
// should also be taken into consideration for range calculation. We set this to true here.
|
|
12855
|
|
return getKeyRange(validRange, inSignDomain, true);
|
|
12856
|
|
}
|
|
12857
|
|
|
|
12858
|
|
/* inherits documentation from base class */
|
|
12859
|
|
QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain) const
|
|
|
16467
|
return getKeyRange(foundRange, inSignDomain, true);
|
|
|
16468
|
}
|
|
|
16469
|
|
|
|
16470
|
/* inherits documentation from base class */
|
|
|
16471
|
QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
|
|
12860
|
16472
|
{
|
|
12861
|
16473
|
// just call the specialized version which takes an additional argument whether error bars
|
|
12862
|
16474
|
// should also be taken into consideration for range calculation. We set this to true here.
|
|
12863
|
|
return getValueRange(validRange, inSignDomain, true);
|
|
|
16475
|
return getValueRange(foundRange, inSignDomain, true);
|
|
12864
|
16476
|
}
|
|
12865
|
16477
|
|
|
12866
|
16478
|
/*! \overload
|
|
12867
|
16479
|
|
|
12868
|
16480
|
Allows to specify whether the error bars should be included in the range calculation.
|
|
12869
|
16481
|
|
|
12870
|
|
\see getKeyRange(bool &validRange, SignDomain inSignDomain)
|
|
12871
|
|
*/
|
|
12872
|
|
QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
|
|
|
16482
|
\see getKeyRange(bool &foundRange, SignDomain inSignDomain)
|
|
|
16483
|
*/
|
|
|
16484
|
QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
|
|
12873
|
16485
|
{
|
|
12874
|
16486
|
QCPRange range;
|
|
12875
|
16487
|
bool haveLower = false;
|
|
@@
-12965,7
+16577,7
QCPRange QCPGraph::getKeyRange(bool &val
|
|
12965
|
16577
|
}
|
|
12966
|
16578
|
}
|
|
12967
|
16579
|
|
|
12968
|
|
validRange = haveLower && haveUpper;
|
|
|
16580
|
foundRange = haveLower && haveUpper;
|
|
12969
|
16581
|
return range;
|
|
12970
|
16582
|
}
|
|
12971
|
16583
|
|
|
@@
-12973,9
+16585,9
QCPRange QCPGraph::getKeyRange(bool &val
|
|
12973
|
16585
|
|
|
12974
|
16586
|
Allows to specify whether the error bars should be included in the range calculation.
|
|
12975
|
16587
|
|
|
12976
|
|
\see getValueRange(bool &validRange, SignDomain inSignDomain)
|
|
12977
|
|
*/
|
|
12978
|
|
QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
|
|
|
16588
|
\see getValueRange(bool &foundRange, SignDomain inSignDomain)
|
|
|
16589
|
*/
|
|
|
16590
|
QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
|
|
12979
|
16591
|
{
|
|
12980
|
16592
|
QCPRange range;
|
|
12981
|
16593
|
bool haveLower = false;
|
|
@@
-13071,7
+16683,7
QCPRange QCPGraph::getValueRange(bool &v
|
|
13071
|
16683
|
}
|
|
13072
|
16684
|
}
|
|
13073
|
16685
|
|
|
13074
|
|
validRange = haveLower && haveUpper;
|
|
|
16686
|
foundRange = haveLower && haveUpper;
|
|
13075
|
16687
|
return range;
|
|
13076
|
16688
|
}
|
|
13077
|
16689
|
|
|
@@
-13404,8
+17016,12
double QCPCurve::selectTest(const QPoint
|
|
13404
|
17016
|
Q_UNUSED(details)
|
|
13405
|
17017
|
if ((onlySelectable && !mSelectable) || mData->isEmpty())
|
|
13406
|
17018
|
return -1;
|
|
13407
|
|
|
|
|
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
|
17022
|
return pointDistance(pos);
|
|
|
17023
|
else
|
|
|
17024
|
return -1;
|
|
13409
|
17025
|
}
|
|
13410
|
17026
|
|
|
13411
|
17027
|
/* inherits documentation from base class */
|
|
@@
-13703,7
+17319,7
QPointF QCPCurve::outsideCoordsToPixels(
|
|
13703
|
17319
|
}
|
|
13704
|
17320
|
|
|
13705
|
17321
|
/* inherits documentation from base class */
|
|
13706
|
|
QCPRange QCPCurve::getKeyRange(bool &validRange, SignDomain inSignDomain) const
|
|
|
17322
|
QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
|
|
13707
|
17323
|
{
|
|
13708
|
17324
|
QCPRange range;
|
|
13709
|
17325
|
bool haveLower = false;
|
|
@@
-13731,12
+17347,12
QCPRange QCPCurve::getKeyRange(bool &val
|
|
13731
|
17347
|
++it;
|
|
13732
|
17348
|
}
|
|
13733
|
17349
|
|
|
13734
|
|
validRange = haveLower && haveUpper;
|
|
|
17350
|
foundRange = haveLower && haveUpper;
|
|
13735
|
17351
|
return range;
|
|
13736
|
17352
|
}
|
|
13737
|
17353
|
|
|
13738
|
17354
|
/* inherits documentation from base class */
|
|
13739
|
|
QCPRange QCPCurve::getValueRange(bool &validRange, SignDomain inSignDomain) const
|
|
|
17355
|
QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
|
|
13740
|
17356
|
{
|
|
13741
|
17357
|
QCPRange range;
|
|
13742
|
17358
|
bool haveLower = false;
|
|
@@
-13764,7
+17380,7
QCPRange QCPCurve::getValueRange(bool &v
|
|
13764
|
17380
|
++it;
|
|
13765
|
17381
|
}
|
|
13766
|
17382
|
|
|
13767
|
|
validRange = haveLower && haveUpper;
|
|
|
17383
|
foundRange = haveLower && haveUpper;
|
|
13768
|
17384
|
return range;
|
|
13769
|
17385
|
}
|
|
13770
|
17386
|
|
|
@@
-14114,7
+17730,10
double QCPBars::selectTest(const QPointF
|
|
14114
|
17730
|
Q_UNUSED(details)
|
|
14115
|
17731
|
if (onlySelectable && !mSelectable)
|
|
14116
|
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
|
17737
|
QCPBarDataMap::ConstIterator it;
|
|
14119
|
17738
|
double posKey, posValue;
|
|
14120
|
17739
|
pixelsToCoords(pos, posKey, posValue);
|
|
@@
-14126,6
+17745,7
double QCPBars::selectTest(const QPointF
|
|
14126
|
17745
|
if (keyRange.contains(posKey) && valueRange.contains(posValue))
|
|
14127
|
17746
|
return mParentPlot->selectionTolerance()*0.99;
|
|
14128
|
17747
|
}
|
|
|
17748
|
}
|
|
14129
|
17749
|
return -1;
|
|
14130
|
17750
|
}
|
|
14131
|
17751
|
|
|
@@
-14262,7
+17882,7
void QCPBars::connectBars(QCPBars *lower
|
|
14262
|
17882
|
}
|
|
14263
|
17883
|
|
|
14264
|
17884
|
/* inherits documentation from base class */
|
|
14265
|
|
QCPRange QCPBars::getKeyRange(bool &validRange, SignDomain inSignDomain) const
|
|
|
17885
|
QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
|
|
14266
|
17886
|
{
|
|
14267
|
17887
|
QCPRange range;
|
|
14268
|
17888
|
bool haveLower = false;
|
|
@@
-14290,12
+17910,12
QCPRange QCPBars::getKeyRange(bool &vali
|
|
14290
|
17910
|
++it;
|
|
14291
|
17911
|
}
|
|
14292
|
17912
|
|
|
14293
|
|
validRange = haveLower && haveUpper;
|
|
|
17913
|
foundRange = haveLower && haveUpper;
|
|
14294
|
17914
|
return range;
|
|
14295
|
17915
|
}
|
|
14296
|
17916
|
|
|
14297
|
17917
|
/* inherits documentation from base class */
|
|
14298
|
|
QCPRange QCPBars::getValueRange(bool &validRange, SignDomain inSignDomain) const
|
|
|
17918
|
QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
|
|
14299
|
17919
|
{
|
|
14300
|
17920
|
QCPRange range;
|
|
14301
|
17921
|
bool haveLower = true; // set to true, because 0 should always be visible in bar charts
|
|
@@
-14323,7
+17943,7
QCPRange QCPBars::getValueRange(bool &va
|
|
14323
|
17943
|
++it;
|
|
14324
|
17944
|
}
|
|
14325
|
17945
|
|
|
14326
|
|
validRange = range.lower < range.upper;
|
|
|
17946
|
foundRange = true; // return true because bar charts always have the 0-line visible
|
|
14327
|
17947
|
return range;
|
|
14328
|
17948
|
}
|
|
14329
|
17949
|
|
|
@@
-14338,14
+17958,14
QCPRange QCPBars::getValueRange(bool &va
|
|
14338
|
17958
|
\image html QCPStatisticalBox.png
|
|
14339
|
17959
|
|
|
14340
|
17960
|
To plot data, assign it with the individual parameter functions or use \ref setData to set all
|
|
14341
|
|
parameters at once. The individual funcions are:
|
|
|
17961
|
parameters at once. The individual functions are:
|
|
14342
|
17962
|
\li \ref setMinimum
|
|
14343
|
17963
|
\li \ref setLowerQuartile
|
|
14344
|
17964
|
\li \ref setMedian
|
|
14345
|
17965
|
\li \ref setUpperQuartile
|
|
14346
|
17966
|
\li \ref setMaximum
|
|
14347
|
17967
|
|
|
14348
|
|
Additionally you can define a list of outliers, drawn as circle datapoints:
|
|
|
17968
|
Additionally you can define a list of outliers, drawn as scatter datapoints:
|
|
14349
|
17969
|
\li \ref setOutliers
|
|
14350
|
17970
|
|
|
14351
|
17971
|
\section appearance Changing the appearance
|
|
@@
-14483,9
+18103,9
void QCPStatisticalBox::setMaximum(doubl
|
|
14483
|
18103
|
}
|
|
14484
|
18104
|
|
|
14485
|
18105
|
/*!
|
|
14486
|
|
Sets a vector of outlier values that will be drawn as circles. Any data points in the sample that
|
|
14487
|
|
are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers and
|
|
14488
|
|
displayed as such.
|
|
|
18106
|
Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
|
|
|
18107
|
that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
|
|
|
18108
|
and displayed as such.
|
|
14489
|
18109
|
|
|
14490
|
18110
|
\see setOutlierStyle
|
|
14491
|
18111
|
*/
|
|
@@
-14591,6
+18211,8
double QCPStatisticalBox::selectTest(con
|
|
14591
|
18211
|
return -1;
|
|
14592
|
18212
|
if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
|
|
14593
|
18213
|
|
|
|
18214
|
if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
|
|
|
18215
|
{
|
|
14594
|
18216
|
double posKey, posValue;
|
|
14595
|
18217
|
pixelsToCoords(pos, posKey, posValue);
|
|
14596
|
18218
|
// quartile box:
|
|
@@
-14602,7
+18224,7
double QCPStatisticalBox::selectTest(con
|
|
14602
|
18224
|
// min/max whiskers:
|
|
14603
|
18225
|
if (QCPRange(mMinimum, mMaximum).contains(posValue))
|
|
14604
|
18226
|
return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
|
|
14605
|
|
|
|
|
18227
|
}
|
|
14606
|
18228
|
return -1;
|
|
14607
|
18229
|
}
|
|
14608
|
18230
|
|
|
@@
-14712,9
+18334,9
void QCPStatisticalBox::drawOutliers(QCP
|
|
14712
|
18334
|
}
|
|
14713
|
18335
|
|
|
14714
|
18336
|
/* inherits documentation from base class */
|
|
14715
|
|
QCPRange QCPStatisticalBox::getKeyRange(bool &validRange, SignDomain inSignDomain) const
|
|
14716
|
|
{
|
|
14717
|
|
validRange = mWidth > 0;
|
|
|
18337
|
QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
|
|
|
18338
|
{
|
|
|
18339
|
foundRange = true;
|
|
14718
|
18340
|
if (inSignDomain == sdBoth)
|
|
14719
|
18341
|
{
|
|
14720
|
18342
|
return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
|
|
@@
-14726,7
+18348,7
QCPRange QCPStatisticalBox::getKeyRange(
|
|
14726
|
18348
|
return QCPRange(mKey-mWidth*0.5, mKey);
|
|
14727
|
18349
|
else
|
|
14728
|
18350
|
{
|
|
14729
|
|
validRange = false;
|
|
|
18351
|
foundRange = false;
|
|
14730
|
18352
|
return QCPRange();
|
|
14731
|
18353
|
}
|
|
14732
|
18354
|
} else if (inSignDomain == sdPositive)
|
|
@@
-14737,31
+18359,16
QCPRange QCPStatisticalBox::getKeyRange(
|
|
14737
|
18359
|
return QCPRange(mKey, mKey+mWidth*0.5);
|
|
14738
|
18360
|
else
|
|
14739
|
18361
|
{
|
|
14740
|
|
validRange = false;
|
|
14741
|
|
return QCPRange();
|
|
14742
|
|
}
|
|
14743
|
|
}
|
|
14744
|
|
validRange = false;
|
|
|
18362
|
foundRange = false;
|
|
14745
|
18363
|
return QCPRange();
|
|
14746
|
18364
|
}
|
|
14747
|
|
|
|
14748
|
|
/* inherits documentation from base class */
|
|
14749
|
|
QCPRange QCPStatisticalBox::getValueRange(bool &validRange, SignDomain inSignDomain) const
|
|
14750
|
|
{
|
|
14751
|
|
if (inSignDomain == sdBoth)
|
|
14752
|
|
{
|
|
14753
|
|
double lower = qMin(mMinimum, qMin(mMedian, mLowerQuartile));
|
|
14754
|
|
double upper = qMax(mMaximum, qMax(mMedian, mUpperQuartile));
|
|
14755
|
|
for (int i=0; i<mOutliers.size(); ++i)
|
|
14756
|
|
{
|
|
14757
|
|
if (mOutliers.at(i) < lower)
|
|
14758
|
|
lower = mOutliers.at(i);
|
|
14759
|
|
if (mOutliers.at(i) > upper)
|
|
14760
|
|
upper = mOutliers.at(i);
|
|
14761
|
|
}
|
|
14762
|
|
validRange = upper > lower;
|
|
14763
|
|
return QCPRange(lower, upper);
|
|
14764
|
|
} else
|
|
|
18365
|
}
|
|
|
18366
|
foundRange = false;
|
|
|
18367
|
return QCPRange();
|
|
|
18368
|
}
|
|
|
18369
|
|
|
|
18370
|
/* inherits documentation from base class */
|
|
|
18371
|
QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
|
|
14765
|
18372
|
{
|
|
14766
|
18373
|
QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
|
|
14767
|
18374
|
values.reserve(mOutliers.size() + 5);
|
|
@@
-14775,7
+18382,8
QCPRange QCPStatisticalBox::getValueRang
|
|
14775
|
18382
|
for (int i=0; i<values.size(); ++i)
|
|
14776
|
18383
|
{
|
|
14777
|
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
|
18388
|
if (values.at(i) > upper || !haveUpper)
|
|
14781
|
18389
|
{
|
|
@@
-14790,16
+18398,899
QCPRange QCPStatisticalBox::getValueRang
|
|
14790
|
18398
|
}
|
|
14791
|
18399
|
}
|
|
14792
|
18400
|
// return the bounds if we found some sensible values:
|
|
14793
|
|
if (haveLower && haveUpper && lower < upper)
|
|
14794
|
|
{
|
|
14795
|
|
validRange = true;
|
|
|
18401
|
if (haveLower && haveUpper)
|
|
|
18402
|
{
|
|
|
18403
|
foundRange = true;
|
|
14796
|
18404
|
return QCPRange(lower, upper);
|
|
14797
|
|
} else
|
|
14798
|
|
{
|
|
14799
|
|
validRange = false;
|
|
|
18405
|
} else // might happen if all values are in other sign domain
|
|
|
18406
|
{
|
|
|
18407
|
foundRange = false;
|
|
14800
|
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
|
19833
|
QPointF startDirVec(startDir->pixelPoint());
|
|
15343
|
19834
|
QPointF endDirVec(endDir->pixelPoint());
|
|
15344
|
19835
|
QPointF endVec(end->pixelPoint());
|
|
15345
|
|
if (QVector2D(endVec-startVec).length() > 1e10) // too large curves cause crash
|
|
|
19836
|
if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
|
|
15346
|
19837
|
return;
|
|
15347
|
19838
|
|
|
15348
|
19839
|
QPainterPath cubicPath(startVec);
|
|
@@
-16768,10
+21259,10
double QCPItemBracket::selectTest(const
|
|
16768
|
21259
|
if (leftVec.toPoint() == rightVec.toPoint())
|
|
16769
|
21260
|
return -1;
|
|
16770
|
21261
|
|
|
16771
|
|
QVector2D widthVec = (rightVec-leftVec)*0.5;
|
|
|
21262
|
QVector2D widthVec = (rightVec-leftVec)*0.5f;
|
|
16772
|
21263
|
QVector2D lengthVec(-widthVec.y(), widthVec.x());
|
|
16773
|
21264
|
lengthVec = lengthVec.normalized()*mLength;
|
|
16774
|
|
QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
|
|
|
21265
|
QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
|
|
16775
|
21266
|
|
|
16776
|
21267
|
return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
|
|
16777
|
21268
|
}
|
|
@@
-16784,10
+21275,10
void QCPItemBracket::draw(QCPPainter *pa
|
|
16784
|
21275
|
if (leftVec.toPoint() == rightVec.toPoint())
|
|
16785
|
21276
|
return;
|
|
16786
|
21277
|
|
|
16787
|
|
QVector2D widthVec = (rightVec-leftVec)*0.5;
|
|
|
21278
|
QVector2D widthVec = (rightVec-leftVec)*0.5f;
|
|
16788
|
21279
|
QVector2D lengthVec(-widthVec.y(), widthVec.x());
|
|
16789
|
21280
|
lengthVec = lengthVec.normalized()*mLength;
|
|
16790
|
|
QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
|
|
|
21281
|
QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
|
|
16791
|
21282
|
|
|
16792
|
21283
|
QPolygon boundingPoly;
|
|
16793
|
21284
|
boundingPoly << leftVec.toPoint() << rightVec.toPoint()
|
|
@@
-16820,8
+21311,8
void QCPItemBracket::draw(QCPPainter *pa
|
|
16820
|
21311
|
painter->setBrush(Qt::NoBrush);
|
|
16821
|
21312
|
QPainterPath path;
|
|
16822
|
21313
|
path.moveTo((centerVec+widthVec+lengthVec).toPointF());
|
|
16823
|
|
path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+1*lengthVec).toPointF(), centerVec.toPointF());
|
|
16824
|
|
path.cubicTo((centerVec-0.4*widthVec+1*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
|
|
|
21314
|
path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
|
|
|
21315
|
path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
|
|
16825
|
21316
|
painter->drawPath(path);
|
|
16826
|
21317
|
break;
|
|
16827
|
21318
|
}
|
|
@@
-16832,11
+21323,11
void QCPItemBracket::draw(QCPPainter *pa
|
|
16832
|
21323
|
QPainterPath path;
|
|
16833
|
21324
|
path.moveTo((centerVec+widthVec+lengthVec).toPointF());
|
|
16834
|
21325
|
|
|
16835
|
|
path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
|
|
16836
|
|
path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
|
|
16837
|
|
|
|
16838
|
|
path.cubicTo((centerVec-widthVec*1-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
|
|
16839
|
|
path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec*1-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
|
|
|
21326
|
path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
|
|
|
21327
|
path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
|
|
|
21328
|
|
|
|
21329
|
path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
|
|
|
21330
|
path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
|
|
16840
|
21331
|
|
|
16841
|
21332
|
painter->drawPath(path);
|
|
16842
|
21333
|
break;
|
|
@@
-16853,10
+21344,10
QPointF QCPItemBracket::anchorPixelPoint
|
|
16853
|
21344
|
if (leftVec.toPoint() == rightVec.toPoint())
|
|
16854
|
21345
|
return leftVec.toPointF();
|
|
16855
|
21346
|
|
|
16856
|
|
QVector2D widthVec = (rightVec-leftVec)*0.5;
|
|
|
21347
|
QVector2D widthVec = (rightVec-leftVec)*0.5f;
|
|
16857
|
21348
|
QVector2D lengthVec(-widthVec.y(), widthVec.x());
|
|
16858
|
21349
|
lengthVec = lengthVec.normalized()*mLength;
|
|
16859
|
|
QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
|
|
|
21350
|
QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
|
|
16860
|
21351
|
|
|
16861
|
21352
|
switch (anchorId)
|
|
16862
|
21353
|
{
|
|
@@
-16877,2061
+21368,3
QPen QCPItemBracket::mainPen() const
|
|
16877
|
21368
|
return mSelected ? mSelectedPen : mPen;
|
|
16878
|
21369
|
}
|
|
16879
|
21370
|
|
|
16880
|
|
|
|
16881
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
16882
|
|
//////////////////// QCPAxisRect
|
|
16883
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
16884
|
|
|
|
16885
|
|
/*! \class QCPAxisRect
|
|
16886
|
|
\brief Holds multiple axes and arranges them in a rectangular shape.
|
|
16887
|
|
|
|
16888
|
|
This class represents an axis rect, a rectangular area that is bounded on all sides with an
|
|
16889
|
|
arbitrary number of axes.
|
|
16890
|
|
|
|
16891
|
|
Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
|
|
16892
|
|
layout system allows to have multiple axis rects, e.g. arranged in a grid layout
|
|
16893
|
|
(QCustomPlot::plotLayout).
|
|
16894
|
|
|
|
16895
|
|
By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
|
|
16896
|
|
accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
|
|
16897
|
|
If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
|
|
16898
|
|
invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
|
|
16899
|
|
addAxes. To remove an axis, use \ref removeAxis.
|
|
16900
|
|
|
|
16901
|
|
The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
|
|
16902
|
|
setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
|
|
16903
|
|
explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
|
|
16904
|
|
placed on other layers, independently of the axis rect.
|
|
16905
|
|
|
|
16906
|
|
Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
|
|
16907
|
|
insetLayout and can be used to have other layout elements (or even other layouts with multiple
|
|
16908
|
|
elements) hovering inside the axis rect.
|
|
16909
|
|
|
|
16910
|
|
If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
|
|
16911
|
|
behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
|
|
16912
|
|
is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
|
|
16913
|
|
via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
|
|
16914
|
|
only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
|
|
16915
|
|
QCP::iRangeZoom.
|
|
16916
|
|
|
|
16917
|
|
\image html AxisRectSpacingOverview.png
|
|
16918
|
|
<center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
|
|
16919
|
|
line on the far left indicates the viewport/widget border.</center>
|
|
16920
|
|
*/
|
|
16921
|
|
|
|
16922
|
|
/* start documentation of inline functions */
|
|
16923
|
|
|
|
16924
|
|
/*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
|
|
16925
|
|
|
|
16926
|
|
Returns the inset layout of this axis rect. It can be used to place other layout elements (or
|
|
16927
|
|
even layouts with multiple other elements) inside/on top of an axis rect.
|
|
16928
|
|
|
|
16929
|
|
\see QCPLayoutInset
|
|
16930
|
|
*/
|
|
16931
|
|
|
|
16932
|
|
/*! \fn int QCPAxisRect::left() const
|
|
16933
|
|
|
|
16934
|
|
Returns the pixel position of the left border of this axis rect. Margins are not taken into
|
|
16935
|
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
16936
|
|
*/
|
|
16937
|
|
|
|
16938
|
|
/*! \fn int QCPAxisRect::right() const
|
|
16939
|
|
|
|
16940
|
|
Returns the pixel position of the right border of this axis rect. Margins are not taken into
|
|
16941
|
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
16942
|
|
*/
|
|
16943
|
|
|
|
16944
|
|
/*! \fn int QCPAxisRect::top() const
|
|
16945
|
|
|
|
16946
|
|
Returns the pixel position of the top border of this axis rect. Margins are not taken into
|
|
16947
|
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
16948
|
|
*/
|
|
16949
|
|
|
|
16950
|
|
/*! \fn int QCPAxisRect::bottom() const
|
|
16951
|
|
|
|
16952
|
|
Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
|
|
16953
|
|
account here, so the returned value is with respect to the inner \ref rect.
|
|
16954
|
|
*/
|
|
16955
|
|
|
|
16956
|
|
/*! \fn int QCPAxisRect::width() const
|
|
16957
|
|
|
|
16958
|
|
Returns the pixel width of this axis rect. Margins are not taken into account here, so the
|
|
16959
|
|
returned value is with respect to the inner \ref rect.
|
|
16960
|
|
*/
|
|
16961
|
|
|
|
16962
|
|
/*! \fn int QCPAxisRect::height() const
|
|
16963
|
|
|
|
16964
|
|
Returns the pixel height of this axis rect. Margins are not taken into account here, so the
|
|
16965
|
|
returned value is with respect to the inner \ref rect.
|
|
16966
|
|
*/
|
|
16967
|
|
|
|
16968
|
|
/*! \fn QSize QCPAxisRect::size() const
|
|
16969
|
|
|
|
16970
|
|
Returns the pixel size of this axis rect. Margins are not taken into account here, so the
|
|
16971
|
|
returned value is with respect to the inner \ref rect.
|
|
16972
|
|
*/
|
|
16973
|
|
|
|
16974
|
|
/*! \fn QPoint QCPAxisRect::topLeft() const
|
|
16975
|
|
|
|
16976
|
|
Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
|
|
16977
|
|
so the returned value is with respect to the inner \ref rect.
|
|
16978
|
|
*/
|
|
16979
|
|
|
|
16980
|
|
/*! \fn QPoint QCPAxisRect::topRight() const
|
|
16981
|
|
|
|
16982
|
|
Returns the top right corner of this axis rect in pixels. Margins are not taken into account
|
|
16983
|
|
here, so the returned value is with respect to the inner \ref rect.
|
|
16984
|
|
*/
|
|
16985
|
|
|
|
16986
|
|
/*! \fn QPoint QCPAxisRect::bottomLeft() const
|
|
16987
|
|
|
|
16988
|
|
Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
|
|
16989
|
|
here, so the returned value is with respect to the inner \ref rect.
|
|
16990
|
|
*/
|
|
16991
|
|
|
|
16992
|
|
/*! \fn QPoint QCPAxisRect::bottomRight() const
|
|
16993
|
|
|
|
16994
|
|
Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
|
|
16995
|
|
here, so the returned value is with respect to the inner \ref rect.
|
|
16996
|
|
*/
|
|
16997
|
|
|
|
16998
|
|
/*! \fn QPoint QCPAxisRect::center() const
|
|
16999
|
|
|
|
17000
|
|
Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
|
|
17001
|
|
returned value is with respect to the inner \ref rect.
|
|
17002
|
|
*/
|
|
17003
|
|
|
|
17004
|
|
/* end documentation of inline functions */
|
|
17005
|
|
|
|
17006
|
|
/*!
|
|
17007
|
|
Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
|
|
17008
|
|
sides, the top and right axes are set invisible initially.
|
|
17009
|
|
*/
|
|
17010
|
|
QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
|
|
17011
|
|
QCPLayoutElement(parentPlot),
|
|
17012
|
|
mBackgroundBrush(Qt::NoBrush),
|
|
17013
|
|
mBackgroundScaled(true),
|
|
17014
|
|
mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
|
|
17015
|
|
mInsetLayout(new QCPLayoutInset),
|
|
17016
|
|
mRangeDrag(Qt::Horizontal|Qt::Vertical),
|
|
17017
|
|
mRangeZoom(Qt::Horizontal|Qt::Vertical),
|
|
17018
|
|
mRangeZoomFactorHorz(0.85),
|
|
17019
|
|
mRangeZoomFactorVert(0.85),
|
|
17020
|
|
mDragging(false)
|
|
17021
|
|
{
|
|
17022
|
|
mInsetLayout->initializeParentPlot(mParentPlot);
|
|
17023
|
|
mInsetLayout->setParentLayerable(this);
|
|
17024
|
|
mInsetLayout->setParent(this);
|
|
17025
|
|
|
|
17026
|
|
setMinimumSize(50, 50);
|
|
17027
|
|
setMinimumMargins(QMargins(15, 15, 15, 15));
|
|
17028
|
|
mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
|
|
17029
|
|
mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
|
|
17030
|
|
mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
|
|
17031
|
|
mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
|
|
17032
|
|
|
|
17033
|
|
if (setupDefaultAxes)
|
|
17034
|
|
{
|
|
17035
|
|
QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
|
|
17036
|
|
QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
|
|
17037
|
|
QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
|
|
17038
|
|
QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
|
|
17039
|
|
setRangeDragAxes(xAxis, yAxis);
|
|
17040
|
|
setRangeZoomAxes(xAxis, yAxis);
|
|
17041
|
|
xAxis2->setVisible(false);
|
|
17042
|
|
yAxis2->setVisible(false);
|
|
17043
|
|
xAxis->grid()->setVisible(true);
|
|
17044
|
|
yAxis->grid()->setVisible(true);
|
|
17045
|
|
xAxis2->grid()->setVisible(false);
|
|
17046
|
|
yAxis2->grid()->setVisible(false);
|
|
17047
|
|
xAxis2->grid()->setZeroLinePen(Qt::NoPen);
|
|
17048
|
|
yAxis2->grid()->setZeroLinePen(Qt::NoPen);
|
|
17049
|
|
xAxis2->grid()->setVisible(false);
|
|
17050
|
|
yAxis2->grid()->setVisible(false);
|
|
17051
|
|
}
|
|
17052
|
|
}
|
|
17053
|
|
|
|
17054
|
|
QCPAxisRect::~QCPAxisRect()
|
|
17055
|
|
{
|
|
17056
|
|
delete mInsetLayout;
|
|
17057
|
|
mInsetLayout = 0;
|
|
17058
|
|
|
|
17059
|
|
QList<QCPAxis*> axesList = axes();
|
|
17060
|
|
for (int i=0; i<axesList.size(); ++i)
|
|
17061
|
|
removeAxis(axesList.at(i));
|
|
17062
|
|
}
|
|
17063
|
|
|
|
17064
|
|
/*!
|
|
17065
|
|
Returns the number of axes on the axis rect side specified with \a type.
|
|
17066
|
|
|
|
17067
|
|
\see axis
|
|
17068
|
|
*/
|
|
17069
|
|
int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
|
|
17070
|
|
{
|
|
17071
|
|
return mAxes.value(type).size();
|
|
17072
|
|
}
|
|
17073
|
|
|
|
17074
|
|
/*!
|
|
17075
|
|
Returns the axis with the given \a index on the axis rect side specified with \a type.
|
|
17076
|
|
|
|
17077
|
|
\see axisCount, axes
|
|
17078
|
|
*/
|
|
17079
|
|
QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
|
|
17080
|
|
{
|
|
17081
|
|
QList<QCPAxis*> ax(mAxes.value(type));
|
|
17082
|
|
if (index >= 0 && index < ax.size())
|
|
17083
|
|
{
|
|
17084
|
|
return ax.at(index);
|
|
17085
|
|
} else
|
|
17086
|
|
{
|
|
17087
|
|
qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
|
|
17088
|
|
return 0;
|
|
17089
|
|
}
|
|
17090
|
|
}
|
|
17091
|
|
|
|
17092
|
|
/*!
|
|
17093
|
|
Returns all axes on the axis rect sides specified with \a types.
|
|
17094
|
|
|
|
17095
|
|
\a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
|
|
17096
|
|
multiple sides.
|
|
17097
|
|
|
|
17098
|
|
\see axis
|
|
17099
|
|
*/
|
|
17100
|
|
QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
|
|
17101
|
|
{
|
|
17102
|
|
QList<QCPAxis*> result;
|
|
17103
|
|
if (types.testFlag(QCPAxis::atLeft))
|
|
17104
|
|
result << mAxes.value(QCPAxis::atLeft);
|
|
17105
|
|
if (types.testFlag(QCPAxis::atRight))
|
|
17106
|
|
result << mAxes.value(QCPAxis::atRight);
|
|
17107
|
|
if (types.testFlag(QCPAxis::atTop))
|
|
17108
|
|
result << mAxes.value(QCPAxis::atTop);
|
|
17109
|
|
if (types.testFlag(QCPAxis::atBottom))
|
|
17110
|
|
result << mAxes.value(QCPAxis::atBottom);
|
|
17111
|
|
return result;
|
|
17112
|
|
}
|
|
17113
|
|
|
|
17114
|
|
/*! \overload
|
|
17115
|
|
|
|
17116
|
|
Returns all axes of this axis rect.
|
|
17117
|
|
*/
|
|
17118
|
|
QList<QCPAxis*> QCPAxisRect::axes() const
|
|
17119
|
|
{
|
|
17120
|
|
QList<QCPAxis*> result;
|
|
17121
|
|
QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
|
|
17122
|
|
while (it.hasNext())
|
|
17123
|
|
{
|
|
17124
|
|
it.next();
|
|
17125
|
|
result << it.value();
|
|
17126
|
|
}
|
|
17127
|
|
return result;
|
|
17128
|
|
}
|
|
17129
|
|
|
|
17130
|
|
/*!
|
|
17131
|
|
Adds a new axis to the axis rect side specified with \a type, and returns it.
|
|
17132
|
|
|
|
17133
|
|
If an axis rect side already contains one or more axes, the lower and upper endings of the new
|
|
17134
|
|
axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are initialized to \ref
|
|
17135
|
|
QCPLineEnding::esHalfBar.
|
|
17136
|
|
|
|
17137
|
|
\see addAxes, setupFullAxesBox
|
|
17138
|
|
*/
|
|
17139
|
|
QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type)
|
|
17140
|
|
{
|
|
17141
|
|
QCPAxis *newAxis = new QCPAxis(this, type);
|
|
17142
|
|
if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
|
|
17143
|
|
{
|
|
17144
|
|
bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
|
|
17145
|
|
newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
|
|
17146
|
|
newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
|
|
17147
|
|
}
|
|
17148
|
|
mAxes[type].append(newAxis);
|
|
17149
|
|
return newAxis;
|
|
17150
|
|
}
|
|
17151
|
|
|
|
17152
|
|
/*!
|
|
17153
|
|
Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
|
|
17154
|
|
<tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
|
|
17155
|
|
|
|
17156
|
|
Returns a list of the added axes.
|
|
17157
|
|
|
|
17158
|
|
\see addAxis, setupFullAxesBox
|
|
17159
|
|
*/
|
|
17160
|
|
QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
|
|
17161
|
|
{
|
|
17162
|
|
QList<QCPAxis*> result;
|
|
17163
|
|
if (types.testFlag(QCPAxis::atLeft))
|
|
17164
|
|
result << addAxis(QCPAxis::atLeft);
|
|
17165
|
|
if (types.testFlag(QCPAxis::atRight))
|
|
17166
|
|
result << addAxis(QCPAxis::atRight);
|
|
17167
|
|
if (types.testFlag(QCPAxis::atTop))
|
|
17168
|
|
result << addAxis(QCPAxis::atTop);
|
|
17169
|
|
if (types.testFlag(QCPAxis::atBottom))
|
|
17170
|
|
result << addAxis(QCPAxis::atBottom);
|
|
17171
|
|
return result;
|
|
17172
|
|
}
|
|
17173
|
|
|
|
17174
|
|
/*!
|
|
17175
|
|
Removes the specified \a axis from the axis rect and deletes it.
|
|
17176
|
|
|
|
17177
|
|
Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
|
|
17178
|
|
|
|
17179
|
|
\see addAxis
|
|
17180
|
|
*/
|
|
17181
|
|
bool QCPAxisRect::removeAxis(QCPAxis *axis)
|
|
17182
|
|
{
|
|
17183
|
|
// don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
|
|
17184
|
|
QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
|
|
17185
|
|
while (it.hasNext())
|
|
17186
|
|
{
|
|
17187
|
|
it.next();
|
|
17188
|
|
if (it.value().contains(axis))
|
|
17189
|
|
{
|
|
17190
|
|
mAxes[it.key()].removeOne(axis);
|
|
17191
|
|
if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
|
|
17192
|
|
parentPlot()->axisRemoved(axis);
|
|
17193
|
|
delete axis;
|
|
17194
|
|
return true;
|
|
17195
|
|
}
|
|
17196
|
|
}
|
|
17197
|
|
qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
|
|
17198
|
|
return false;
|
|
17199
|
|
}
|
|
17200
|
|
|
|
17201
|
|
/*!
|
|
17202
|
|
Convenience function to create an axis on each side that doesn't have any axes yet and set their
|
|
17203
|
|
visibility to true. Further, the top/right axes are assigned the following properties of the
|
|
17204
|
|
bottom/left axes:
|
|
17205
|
|
|
|
17206
|
|
\li range (\ref QCPAxis::setRange)
|
|
17207
|
|
\li range reversed (\ref QCPAxis::setRangeReversed)
|
|
17208
|
|
\li scale type (\ref QCPAxis::setScaleType)
|
|
17209
|
|
\li scale log base (\ref QCPAxis::setScaleLogBase)
|
|
17210
|
|
\li ticks (\ref QCPAxis::setTicks)
|
|
17211
|
|
\li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
|
|
17212
|
|
\li sub tick count (\ref QCPAxis::setSubTickCount)
|
|
17213
|
|
\li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
|
|
17214
|
|
\li tick step (\ref QCPAxis::setTickStep)
|
|
17215
|
|
\li auto tick step (\ref QCPAxis::setAutoTickStep)
|
|
17216
|
|
\li number format (\ref QCPAxis::setNumberFormat)
|
|
17217
|
|
\li number precision (\ref QCPAxis::setNumberPrecision)
|
|
17218
|
|
\li tick label type (\ref QCPAxis::setTickLabelType)
|
|
17219
|
|
\li date time format (\ref QCPAxis::setDateTimeFormat)
|
|
17220
|
|
\li date time spec (\ref QCPAxis::setDateTimeSpec)
|
|
17221
|
|
|
|
17222
|
|
Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
|
|
17223
|
|
|
|
17224
|
|
If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
|
|
17225
|
|
and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
|
|
17226
|
|
*/
|
|
17227
|
|
void QCPAxisRect::setupFullAxesBox(bool connectRanges)
|
|
17228
|
|
{
|
|
17229
|
|
QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
|
|
17230
|
|
if (axisCount(QCPAxis::atBottom) == 0)
|
|
17231
|
|
xAxis = addAxis(QCPAxis::atBottom);
|
|
17232
|
|
else
|
|
17233
|
|
xAxis = axis(QCPAxis::atBottom);
|
|
17234
|
|
|
|
17235
|
|
if (axisCount(QCPAxis::atLeft) == 0)
|
|
17236
|
|
yAxis = addAxis(QCPAxis::atLeft);
|
|
17237
|
|
else
|
|
17238
|
|
yAxis = axis(QCPAxis::atLeft);
|
|
17239
|
|
|
|
17240
|
|
if (axisCount(QCPAxis::atTop) == 0)
|
|
17241
|
|
xAxis2 = addAxis(QCPAxis::atTop);
|
|
17242
|
|
else
|
|
17243
|
|
xAxis2 = axis(QCPAxis::atTop);
|
|
17244
|
|
|
|
17245
|
|
if (axisCount(QCPAxis::atRight) == 0)
|
|
17246
|
|
yAxis2 = addAxis(QCPAxis::atRight);
|
|
17247
|
|
else
|
|
17248
|
|
yAxis2 = axis(QCPAxis::atRight);
|
|
17249
|
|
|
|
17250
|
|
xAxis->setVisible(true);
|
|
17251
|
|
yAxis->setVisible(true);
|
|
17252
|
|
xAxis2->setVisible(true);
|
|
17253
|
|
yAxis2->setVisible(true);
|
|
17254
|
|
xAxis2->setTickLabels(false);
|
|
17255
|
|
yAxis2->setTickLabels(false);
|
|
17256
|
|
|
|
17257
|
|
xAxis2->setRange(xAxis->range());
|
|
17258
|
|
xAxis2->setRangeReversed(xAxis->rangeReversed());
|
|
17259
|
|
xAxis2->setScaleType(xAxis->scaleType());
|
|
17260
|
|
xAxis2->setScaleLogBase(xAxis->scaleLogBase());
|
|
17261
|
|
xAxis2->setTicks(xAxis->ticks());
|
|
17262
|
|
xAxis2->setAutoTickCount(xAxis->autoTickCount());
|
|
17263
|
|
xAxis2->setSubTickCount(xAxis->subTickCount());
|
|
17264
|
|
xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
|
|
17265
|
|
xAxis2->setTickStep(xAxis->tickStep());
|
|
17266
|
|
xAxis2->setAutoTickStep(xAxis->autoTickStep());
|
|
17267
|
|
xAxis2->setNumberFormat(xAxis->numberFormat());
|
|
17268
|
|
xAxis2->setNumberPrecision(xAxis->numberPrecision());
|
|
17269
|
|
xAxis2->setTickLabelType(xAxis->tickLabelType());
|
|
17270
|
|
xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
|
|
17271
|
|
xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
|
|
17272
|
|
|
|
17273
|
|
yAxis2->setRange(yAxis->range());
|
|
17274
|
|
yAxis2->setRangeReversed(yAxis->rangeReversed());
|
|
17275
|
|
yAxis2->setScaleType(yAxis->scaleType());
|
|
17276
|
|
yAxis2->setScaleLogBase(yAxis->scaleLogBase());
|
|
17277
|
|
yAxis2->setTicks(yAxis->ticks());
|
|
17278
|
|
yAxis2->setAutoTickCount(yAxis->autoTickCount());
|
|
17279
|
|
yAxis2->setSubTickCount(yAxis->subTickCount());
|
|
17280
|
|
yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
|
|
17281
|
|
yAxis2->setTickStep(yAxis->tickStep());
|
|
17282
|
|
yAxis2->setAutoTickStep(yAxis->autoTickStep());
|
|
17283
|
|
yAxis2->setNumberFormat(yAxis->numberFormat());
|
|
17284
|
|
yAxis2->setNumberPrecision(yAxis->numberPrecision());
|
|
17285
|
|
yAxis2->setTickLabelType(yAxis->tickLabelType());
|
|
17286
|
|
yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
|
|
17287
|
|
yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
|
|
17288
|
|
|
|
17289
|
|
if (connectRanges)
|
|
17290
|
|
{
|
|
17291
|
|
connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
|
|
17292
|
|
connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
|
|
17293
|
|
}
|
|
17294
|
|
}
|
|
17295
|
|
|
|
17296
|
|
/*!
|
|
17297
|
|
Returns a list of all the plottables that are associated with this axis rect.
|
|
17298
|
|
|
|
17299
|
|
A plottable is considered associated with an axis rect if its key or value axis (or both) is in
|
|
17300
|
|
this axis rect.
|
|
17301
|
|
|
|
17302
|
|
\see graphs, items
|
|
17303
|
|
*/
|
|
17304
|
|
QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
|
|
17305
|
|
{
|
|
17306
|
|
// Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
|
|
17307
|
|
QList<QCPAbstractPlottable*> result;
|
|
17308
|
|
for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
|
|
17309
|
|
{
|
|
17310
|
|
if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
|
|
17311
|
|
result.append(mParentPlot->mPlottables.at(i));
|
|
17312
|
|
}
|
|
17313
|
|
return result;
|
|
17314
|
|
}
|
|
17315
|
|
|
|
17316
|
|
/*!
|
|
17317
|
|
Returns a list of all the graphs that are associated with this axis rect.
|
|
17318
|
|
|
|
17319
|
|
A graph is considered associated with an axis rect if its key or value axis (or both) is in
|
|
17320
|
|
this axis rect.
|
|
17321
|
|
|
|
17322
|
|
\see plottables, items
|
|
17323
|
|
*/
|
|
17324
|
|
QList<QCPGraph*> QCPAxisRect::graphs() const
|
|
17325
|
|
{
|
|
17326
|
|
// Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
|
|
17327
|
|
QList<QCPGraph*> result;
|
|
17328
|
|
for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
|
|
17329
|
|
{
|
|
17330
|
|
if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
|
|
17331
|
|
result.append(mParentPlot->mGraphs.at(i));
|
|
17332
|
|
}
|
|
17333
|
|
return result;
|
|
17334
|
|
}
|
|
17335
|
|
|
|
17336
|
|
/*!
|
|
17337
|
|
Returns a list of all the items that are associated with this axis rect.
|
|
17338
|
|
|
|
17339
|
|
An item is considered associated with an axis rect if any of its positions has key or value axis
|
|
17340
|
|
set to an axis that is in this axis rect, or if any of its positions has \ref
|
|
17341
|
|
QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
|
|
17342
|
|
QCPAbstractItem::setClipAxisRect) is set to this axis rect.
|
|
17343
|
|
|
|
17344
|
|
\see plottables, graphs
|
|
17345
|
|
*/
|
|
17346
|
|
QList<QCPAbstractItem *> QCPAxisRect::items() const
|
|
17347
|
|
{
|
|
17348
|
|
// Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
|
|
17349
|
|
// and miss those items that have this axis rect as clipAxisRect.
|
|
17350
|
|
QList<QCPAbstractItem*> result;
|
|
17351
|
|
for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
|
|
17352
|
|
{
|
|
17353
|
|
if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
|
|
17354
|
|
{
|
|
17355
|
|
result.append(mParentPlot->mItems.at(itemId));
|
|
17356
|
|
continue;
|
|
17357
|
|
}
|
|
17358
|
|
QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
|
|
17359
|
|
for (int posId=0; posId<positions.size(); ++posId)
|
|
17360
|
|
{
|
|
17361
|
|
if (positions.at(posId)->axisRect() == this ||
|
|
17362
|
|
positions.at(posId)->keyAxis()->axisRect() == this ||
|
|
17363
|
|
positions.at(posId)->valueAxis()->axisRect() == this)
|
|
17364
|
|
{
|
|
17365
|
|
result.append(mParentPlot->mItems.at(itemId));
|
|
17366
|
|
break;
|
|
17367
|
|
}
|
|
17368
|
|
}
|
|
17369
|
|
}
|
|
17370
|
|
return result;
|
|
17371
|
|
}
|
|
17372
|
|
|
|
17373
|
|
/*!
|
|
17374
|
|
This method is called automatically upon replot and doesn't need to be called by users of
|
|
17375
|
|
QCPAxisRect.
|
|
17376
|
|
|
|
17377
|
|
Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
|
|
17378
|
|
and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
|
|
17379
|
|
QCPInsetLayout::update function.
|
|
17380
|
|
*/
|
|
17381
|
|
void QCPAxisRect::update()
|
|
17382
|
|
{
|
|
17383
|
|
QCPLayoutElement::update();
|
|
17384
|
|
|
|
17385
|
|
// pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
|
|
17386
|
|
mInsetLayout->setOuterRect(rect());
|
|
17387
|
|
mInsetLayout->update();
|
|
17388
|
|
}
|
|
17389
|
|
|
|
17390
|
|
/* inherits documentation from base class */
|
|
17391
|
|
QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
|
|
17392
|
|
{
|
|
17393
|
|
QList<QCPLayoutElement*> result;
|
|
17394
|
|
if (mInsetLayout)
|
|
17395
|
|
{
|
|
17396
|
|
result << mInsetLayout;
|
|
17397
|
|
if (recursive)
|
|
17398
|
|
result << mInsetLayout->elements(recursive);
|
|
17399
|
|
}
|
|
17400
|
|
return result;
|
|
17401
|
|
}
|
|
17402
|
|
|
|
17403
|
|
/* inherits documentation from base class */
|
|
17404
|
|
void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
17405
|
|
{
|
|
17406
|
|
painter->setAntialiasing(false);
|
|
17407
|
|
}
|
|
17408
|
|
|
|
17409
|
|
/* inherits documentation from base class */
|
|
17410
|
|
void QCPAxisRect::draw(QCPPainter *painter)
|
|
17411
|
|
{
|
|
17412
|
|
drawBackground(painter);
|
|
17413
|
|
}
|
|
17414
|
|
|
|
17415
|
|
/*!
|
|
17416
|
|
Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
|
|
17417
|
|
axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
|
|
17418
|
|
backgrounds are usually drawn below everything else.
|
|
17419
|
|
|
|
17420
|
|
For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
|
|
17421
|
|
enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
|
|
17422
|
|
is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
|
|
17423
|
|
consider using the overloaded version of this function.
|
|
17424
|
|
|
|
17425
|
|
Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
|
|
17426
|
|
setBackground(const QBrush &brush).
|
|
17427
|
|
|
|
17428
|
|
\see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
|
|
17429
|
|
*/
|
|
17430
|
|
void QCPAxisRect::setBackground(const QPixmap &pm)
|
|
17431
|
|
{
|
|
17432
|
|
mBackgroundPixmap = pm;
|
|
17433
|
|
mScaledBackgroundPixmap = QPixmap();
|
|
17434
|
|
}
|
|
17435
|
|
|
|
17436
|
|
/*! \overload
|
|
17437
|
|
|
|
17438
|
|
Sets \a brush as the background brush. The axis rect background will be filled with this brush.
|
|
17439
|
|
Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
|
|
17440
|
|
are usually drawn below everything else.
|
|
17441
|
|
|
|
17442
|
|
The brush will be drawn before (under) any background pixmap, which may be specified with \ref
|
|
17443
|
|
setBackground(const QPixmap &pm).
|
|
17444
|
|
|
|
17445
|
|
To disable drawing of a background brush, set \a brush to Qt::NoBrush.
|
|
17446
|
|
|
|
17447
|
|
\see setBackground(const QPixmap &pm)
|
|
17448
|
|
*/
|
|
17449
|
|
void QCPAxisRect::setBackground(const QBrush &brush)
|
|
17450
|
|
{
|
|
17451
|
|
mBackgroundBrush = brush;
|
|
17452
|
|
}
|
|
17453
|
|
|
|
17454
|
|
/*! \overload
|
|
17455
|
|
|
|
17456
|
|
Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
|
|
17457
|
|
shall be scaled in one call.
|
|
17458
|
|
|
|
17459
|
|
\see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
|
|
17460
|
|
*/
|
|
17461
|
|
void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
|
|
17462
|
|
{
|
|
17463
|
|
mBackgroundPixmap = pm;
|
|
17464
|
|
mScaledBackgroundPixmap = QPixmap();
|
|
17465
|
|
mBackgroundScaled = scaled;
|
|
17466
|
|
mBackgroundScaledMode = mode;
|
|
17467
|
|
}
|
|
17468
|
|
|
|
17469
|
|
/*!
|
|
17470
|
|
Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
|
|
17471
|
|
is set to true, you may control whether and how the aspect ratio of the original pixmap is
|
|
17472
|
|
preserved with \ref setBackgroundScaledMode.
|
|
17473
|
|
|
|
17474
|
|
Note that the scaled version of the original pixmap is buffered, so there is no performance
|
|
17475
|
|
penalty on replots. (Except when the axis rect dimensions are changed continuously.)
|
|
17476
|
|
|
|
17477
|
|
\see setBackground, setBackgroundScaledMode
|
|
17478
|
|
*/
|
|
17479
|
|
void QCPAxisRect::setBackgroundScaled(bool scaled)
|
|
17480
|
|
{
|
|
17481
|
|
mBackgroundScaled = scaled;
|
|
17482
|
|
}
|
|
17483
|
|
|
|
17484
|
|
/*!
|
|
17485
|
|
If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
|
|
17486
|
|
define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
|
|
17487
|
|
\see setBackground, setBackgroundScaled
|
|
17488
|
|
*/
|
|
17489
|
|
void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
|
|
17490
|
|
{
|
|
17491
|
|
mBackgroundScaledMode = mode;
|
|
17492
|
|
}
|
|
17493
|
|
|
|
17494
|
|
/*!
|
|
17495
|
|
Returns the range drag axis of the \a orientation provided.
|
|
17496
|
|
|
|
17497
|
|
\see setRangeDragAxes
|
|
17498
|
|
*/
|
|
17499
|
|
QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
|
|
17500
|
|
{
|
|
17501
|
|
return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
|
|
17502
|
|
}
|
|
17503
|
|
|
|
17504
|
|
/*!
|
|
17505
|
|
Returns the range zoom axis of the \a orientation provided.
|
|
17506
|
|
|
|
17507
|
|
\see setRangeZoomAxes
|
|
17508
|
|
*/
|
|
17509
|
|
QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
|
|
17510
|
|
{
|
|
17511
|
|
return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
|
|
17512
|
|
}
|
|
17513
|
|
|
|
17514
|
|
/*!
|
|
17515
|
|
Returns the range zoom factor of the \a orientation provided.
|
|
17516
|
|
|
|
17517
|
|
\see setRangeZoomFactor
|
|
17518
|
|
*/
|
|
17519
|
|
double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
|
|
17520
|
|
{
|
|
17521
|
|
return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
|
|
17522
|
|
}
|
|
17523
|
|
|
|
17524
|
|
/*!
|
|
17525
|
|
Sets which axis orientation may be range dragged by the user with mouse interaction.
|
|
17526
|
|
What orientation corresponds to which specific axis can be set with
|
|
17527
|
|
\ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
|
|
17528
|
|
default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
|
|
17529
|
|
is the left axis (yAxis).
|
|
17530
|
|
|
|
17531
|
|
To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
|
|
17532
|
|
QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
|
|
17533
|
|
Qt::Vertical</tt> as \a orientations.
|
|
17534
|
|
|
|
17535
|
|
In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
|
|
17536
|
|
contains \ref QCP::iRangeDrag to enable the range dragging interaction.
|
|
17537
|
|
|
|
17538
|
|
\see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
|
|
17539
|
|
*/
|
|
17540
|
|
void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
|
|
17541
|
|
{
|
|
17542
|
|
mRangeDrag = orientations;
|
|
17543
|
|
}
|
|
17544
|
|
|
|
17545
|
|
/*!
|
|
17546
|
|
Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
|
|
17547
|
|
corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
|
|
17548
|
|
QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
|
|
17549
|
|
axis is the left axis (yAxis).
|
|
17550
|
|
|
|
17551
|
|
To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
|
|
17552
|
|
QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
|
|
17553
|
|
Qt::Vertical</tt> as \a orientations.
|
|
17554
|
|
|
|
17555
|
|
In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
|
|
17556
|
|
contains \ref QCP::iRangeZoom to enable the range zooming interaction.
|
|
17557
|
|
|
|
17558
|
|
\see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
|
|
17559
|
|
*/
|
|
17560
|
|
void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
|
|
17561
|
|
{
|
|
17562
|
|
mRangeZoom = orientations;
|
|
17563
|
|
}
|
|
17564
|
|
|
|
17565
|
|
/*!
|
|
17566
|
|
Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
|
|
17567
|
|
on the QCustomPlot widget.
|
|
17568
|
|
|
|
17569
|
|
\see setRangeZoomAxes
|
|
17570
|
|
*/
|
|
17571
|
|
void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
|
|
17572
|
|
{
|
|
17573
|
|
mRangeDragHorzAxis = horizontal;
|
|
17574
|
|
mRangeDragVertAxis = vertical;
|
|
17575
|
|
}
|
|
17576
|
|
|
|
17577
|
|
/*!
|
|
17578
|
|
Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
|
|
17579
|
|
QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
|
|
17580
|
|
are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
|
|
17581
|
|
|
|
17582
|
|
\see setRangeDragAxes
|
|
17583
|
|
*/
|
|
17584
|
|
void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
|
|
17585
|
|
{
|
|
17586
|
|
mRangeZoomHorzAxis = horizontal;
|
|
17587
|
|
mRangeZoomVertAxis = vertical;
|
|
17588
|
|
}
|
|
17589
|
|
|
|
17590
|
|
/*!
|
|
17591
|
|
Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
|
|
17592
|
|
\ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
|
|
17593
|
|
let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
|
|
17594
|
|
and which is vertical, can be set with \ref setRangeZoomAxes.
|
|
17595
|
|
|
|
17596
|
|
When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
|
|
17597
|
|
will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
|
|
17598
|
|
same scrolling direction will zoom out.
|
|
17599
|
|
*/
|
|
17600
|
|
void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
|
|
17601
|
|
{
|
|
17602
|
|
mRangeZoomFactorHorz = horizontalFactor;
|
|
17603
|
|
mRangeZoomFactorVert = verticalFactor;
|
|
17604
|
|
}
|
|
17605
|
|
|
|
17606
|
|
/*! \overload
|
|
17607
|
|
|
|
17608
|
|
Sets both the horizontal and vertical zoom \a factor.
|
|
17609
|
|
*/
|
|
17610
|
|
void QCPAxisRect::setRangeZoomFactor(double factor)
|
|
17611
|
|
{
|
|
17612
|
|
mRangeZoomFactorHorz = factor;
|
|
17613
|
|
mRangeZoomFactorVert = factor;
|
|
17614
|
|
}
|
|
17615
|
|
|
|
17616
|
|
/*! \internal
|
|
17617
|
|
|
|
17618
|
|
Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
|
|
17619
|
|
pixmap.
|
|
17620
|
|
|
|
17621
|
|
If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
|
|
17622
|
|
according filling inside the axis rect with the provided \a painter.
|
|
17623
|
|
|
|
17624
|
|
Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
|
|
17625
|
|
depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
|
|
17626
|
|
the axis rect with the provided \a painter. The scaled version is buffered in
|
|
17627
|
|
mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
|
|
17628
|
|
the axis rect has changed in a way that requires a rescale of the background pixmap (this is
|
|
17629
|
|
dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
|
|
17630
|
|
set.
|
|
17631
|
|
|
|
17632
|
|
\see setBackground, setBackgroundScaled, setBackgroundScaledMode
|
|
17633
|
|
*/
|
|
17634
|
|
void QCPAxisRect::drawBackground(QCPPainter *painter)
|
|
17635
|
|
{
|
|
17636
|
|
// draw background fill:
|
|
17637
|
|
if (mBackgroundBrush != Qt::NoBrush)
|
|
17638
|
|
painter->fillRect(mRect, mBackgroundBrush);
|
|
17639
|
|
|
|
17640
|
|
// draw background pixmap (on top of fill, if brush specified):
|
|
17641
|
|
if (!mBackgroundPixmap.isNull())
|
|
17642
|
|
{
|
|
17643
|
|
if (mBackgroundScaled)
|
|
17644
|
|
{
|
|
17645
|
|
// check whether mScaledBackground needs to be updated:
|
|
17646
|
|
QSize scaledSize(mBackgroundPixmap.size());
|
|
17647
|
|
scaledSize.scale(mRect.size(), mBackgroundScaledMode);
|
|
17648
|
|
if (mScaledBackgroundPixmap.size() != scaledSize)
|
|
17649
|
|
mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
|
|
17650
|
|
painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
|
|
17651
|
|
} else
|
|
17652
|
|
{
|
|
17653
|
|
painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
|
|
17654
|
|
}
|
|
17655
|
|
}
|
|
17656
|
|
}
|
|
17657
|
|
|
|
17658
|
|
/*! \internal
|
|
17659
|
|
|
|
17660
|
|
This function makes sure multiple axes on the side specified with \a type don't collide, but are
|
|
17661
|
|
distributed according to their respective space requirement (QCPAxis::calculateMargin).
|
|
17662
|
|
|
|
17663
|
|
It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
|
|
17664
|
|
one with index zero.
|
|
17665
|
|
|
|
17666
|
|
This function is called by \ref calculateAutoMargin.
|
|
17667
|
|
*/
|
|
17668
|
|
void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
|
|
17669
|
|
{
|
|
17670
|
|
const QList<QCPAxis*> axesList = mAxes.value(type);
|
|
17671
|
|
for (int i=1; i<axesList.size(); ++i)
|
|
17672
|
|
axesList.at(i)->setOffset(axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin() + axesList.at(i)->tickLengthIn());
|
|
17673
|
|
}
|
|
17674
|
|
|
|
17675
|
|
/* inherits documentation from base class */
|
|
17676
|
|
int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
|
|
17677
|
|
{
|
|
17678
|
|
if (!mAutoMargins.testFlag(side))
|
|
17679
|
|
qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
|
|
17680
|
|
|
|
17681
|
|
updateAxesOffset(QCPAxis::marginSideToAxisType(side));
|
|
17682
|
|
|
|
17683
|
|
// note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
|
|
17684
|
|
const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
|
|
17685
|
|
if (axesList.size() > 0)
|
|
17686
|
|
return axesList.last()->offset() + axesList.last()->calculateMargin();
|
|
17687
|
|
else
|
|
17688
|
|
return 0;
|
|
17689
|
|
}
|
|
17690
|
|
|
|
17691
|
|
/*! \internal
|
|
17692
|
|
|
|
17693
|
|
Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
|
|
17694
|
|
pressed, the range dragging interaction is initialized (the actual range manipulation happens in
|
|
17695
|
|
the \ref mouseMoveEvent).
|
|
17696
|
|
|
|
17697
|
|
The mDragging flag is set to true and some anchor points are set that are needed to determine the
|
|
17698
|
|
distance the mouse was dragged in the mouse move/release events later.
|
|
17699
|
|
|
|
17700
|
|
\see mouseMoveEvent, mouseReleaseEvent
|
|
17701
|
|
*/
|
|
17702
|
|
void QCPAxisRect::mousePressEvent(QMouseEvent *event)
|
|
17703
|
|
{
|
|
17704
|
|
mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
|
|
17705
|
|
if (event->buttons() & Qt::LeftButton)
|
|
17706
|
|
{
|
|
17707
|
|
mDragging = true;
|
|
17708
|
|
// initialize antialiasing backup in case we start dragging:
|
|
17709
|
|
if (mParentPlot->noAntialiasingOnDrag())
|
|
17710
|
|
{
|
|
17711
|
|
mAADragBackup = mParentPlot->antialiasedElements();
|
|
17712
|
|
mNotAADragBackup = mParentPlot->notAntialiasedElements();
|
|
17713
|
|
}
|
|
17714
|
|
// Mouse range dragging interaction:
|
|
17715
|
|
if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
|
|
17716
|
|
{
|
|
17717
|
|
if (mRangeDragHorzAxis)
|
|
17718
|
|
mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
|
|
17719
|
|
if (mRangeDragVertAxis)
|
|
17720
|
|
mDragStartVertRange = mRangeDragVertAxis.data()->range();
|
|
17721
|
|
}
|
|
17722
|
|
}
|
|
17723
|
|
}
|
|
17724
|
|
|
|
17725
|
|
/*! \internal
|
|
17726
|
|
|
|
17727
|
|
Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
|
|
17728
|
|
preceding \ref mousePressEvent, the range is moved accordingly.
|
|
17729
|
|
|
|
17730
|
|
\see mousePressEvent, mouseReleaseEvent
|
|
17731
|
|
*/
|
|
17732
|
|
void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
|
|
17733
|
|
{
|
|
17734
|
|
// Mouse range dragging interaction:
|
|
17735
|
|
if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
|
|
17736
|
|
{
|
|
17737
|
|
if (mRangeDrag.testFlag(Qt::Horizontal))
|
|
17738
|
|
{
|
|
17739
|
|
if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
|
|
17740
|
|
{
|
|
17741
|
|
if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
|
|
17742
|
|
{
|
|
17743
|
|
double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
|
|
17744
|
|
rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
|
|
17745
|
|
} else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
|
|
17746
|
|
{
|
|
17747
|
|
double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
|
|
17748
|
|
rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
|
|
17749
|
|
}
|
|
17750
|
|
}
|
|
17751
|
|
}
|
|
17752
|
|
if (mRangeDrag.testFlag(Qt::Vertical))
|
|
17753
|
|
{
|
|
17754
|
|
if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
|
|
17755
|
|
{
|
|
17756
|
|
if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
|
|
17757
|
|
{
|
|
17758
|
|
double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
|
|
17759
|
|
rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
|
|
17760
|
|
} else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
|
|
17761
|
|
{
|
|
17762
|
|
double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
|
|
17763
|
|
rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
|
|
17764
|
|
}
|
|
17765
|
|
}
|
|
17766
|
|
}
|
|
17767
|
|
if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
|
|
17768
|
|
{
|
|
17769
|
|
if (mParentPlot->noAntialiasingOnDrag())
|
|
17770
|
|
mParentPlot->setNotAntialiasedElements(QCP::aeAll);
|
|
17771
|
|
mParentPlot->replot();
|
|
17772
|
|
}
|
|
17773
|
|
}
|
|
17774
|
|
}
|
|
17775
|
|
|
|
17776
|
|
/* inherits documentation from base class */
|
|
17777
|
|
void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
|
|
17778
|
|
{
|
|
17779
|
|
Q_UNUSED(event)
|
|
17780
|
|
mDragging = false;
|
|
17781
|
|
if (mParentPlot->noAntialiasingOnDrag())
|
|
17782
|
|
{
|
|
17783
|
|
mParentPlot->setAntialiasedElements(mAADragBackup);
|
|
17784
|
|
mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
|
|
17785
|
|
}
|
|
17786
|
|
}
|
|
17787
|
|
|
|
17788
|
|
/*! \internal
|
|
17789
|
|
|
|
17790
|
|
Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
|
|
17791
|
|
ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
|
|
17792
|
|
the scaling operation is the current cursor position inside the axis rect. The scaling factor is
|
|
17793
|
|
dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
|
|
17794
|
|
zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
|
|
17795
|
|
|
|
17796
|
|
Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
|
|
17797
|
|
wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
|
|
17798
|
|
multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
|
|
17799
|
|
exponent of the range zoom factor. This takes care of the wheel direction automatically, by
|
|
17800
|
|
inverting the factor, when the wheel step is negative (f^-1 = 1/f).
|
|
17801
|
|
*/
|
|
17802
|
|
void QCPAxisRect::wheelEvent(QWheelEvent *event)
|
|
17803
|
|
{
|
|
17804
|
|
// Mouse range zooming interaction:
|
|
17805
|
|
if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
|
|
17806
|
|
{
|
|
17807
|
|
if (mRangeZoom != 0)
|
|
17808
|
|
{
|
|
17809
|
|
double factor;
|
|
17810
|
|
double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
|
|
17811
|
|
if (mRangeZoom.testFlag(Qt::Horizontal))
|
|
17812
|
|
{
|
|
17813
|
|
factor = pow(mRangeZoomFactorHorz, wheelSteps);
|
|
17814
|
|
if (mRangeZoomHorzAxis.data())
|
|
17815
|
|
mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
|
|
17816
|
|
}
|
|
17817
|
|
if (mRangeZoom.testFlag(Qt::Vertical))
|
|
17818
|
|
{
|
|
17819
|
|
factor = pow(mRangeZoomFactorVert, wheelSteps);
|
|
17820
|
|
if (mRangeZoomVertAxis.data())
|
|
17821
|
|
mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
|
|
17822
|
|
}
|
|
17823
|
|
mParentPlot->replot();
|
|
17824
|
|
}
|
|
17825
|
|
}
|
|
17826
|
|
}
|
|
17827
|
|
|
|
17828
|
|
|
|
17829
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
17830
|
|
//////////////////// QCPAbstractLegendItem
|
|
17831
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
17832
|
|
|
|
17833
|
|
/*! \class QCPAbstractLegendItem
|
|
17834
|
|
\brief The abstract base class for all entries in a QCPLegend.
|
|
17835
|
|
|
|
17836
|
|
It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
|
|
17837
|
|
legend, the subclass \ref QCPPlottableLegendItem is more suitable.
|
|
17838
|
|
|
|
17839
|
|
Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
|
|
17840
|
|
that's not even associated with a plottable).
|
|
17841
|
|
|
|
17842
|
|
You must implement the following pure virtual functions:
|
|
17843
|
|
\li \ref draw (from QCPLayerable)
|
|
17844
|
|
|
|
17845
|
|
You inherit the following members you may use:
|
|
17846
|
|
<table>
|
|
17847
|
|
<tr>
|
|
17848
|
|
<td>QCPLegend *\b mParentLegend</td>
|
|
17849
|
|
<td>A pointer to the parent QCPLegend.</td>
|
|
17850
|
|
</tr><tr>
|
|
17851
|
|
<td>QFont \b mFont</td>
|
|
17852
|
|
<td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
|
|
17853
|
|
</tr>
|
|
17854
|
|
</table>
|
|
17855
|
|
*/
|
|
17856
|
|
|
|
17857
|
|
/* start of documentation of signals */
|
|
17858
|
|
|
|
17859
|
|
/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
|
|
17860
|
|
|
|
17861
|
|
This signal is emitted when the selection state of this legend item has changed, either by user
|
|
17862
|
|
interaction or by a direct call to \ref setSelected.
|
|
17863
|
|
*/
|
|
17864
|
|
|
|
17865
|
|
/* end of documentation of signals */
|
|
17866
|
|
|
|
17867
|
|
/*!
|
|
17868
|
|
Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
|
|
17869
|
|
cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
|
|
17870
|
|
*/
|
|
17871
|
|
QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
|
|
17872
|
|
QCPLayoutElement(parent->parentPlot()),
|
|
17873
|
|
mParentLegend(parent),
|
|
17874
|
|
mFont(parent->font()),
|
|
17875
|
|
mTextColor(parent->textColor()),
|
|
17876
|
|
mSelectedFont(parent->selectedFont()),
|
|
17877
|
|
mSelectedTextColor(parent->selectedTextColor()),
|
|
17878
|
|
mSelectable(true),
|
|
17879
|
|
mSelected(false)
|
|
17880
|
|
{
|
|
17881
|
|
setLayer("legend");
|
|
17882
|
|
setMargins(QMargins(8, 2, 8, 2));
|
|
17883
|
|
}
|
|
17884
|
|
|
|
17885
|
|
/*!
|
|
17886
|
|
Sets the default font of this specific legend item to \a font.
|
|
17887
|
|
|
|
17888
|
|
\see setTextColor, QCPLegend::setFont
|
|
17889
|
|
*/
|
|
17890
|
|
void QCPAbstractLegendItem::setFont(const QFont &font)
|
|
17891
|
|
{
|
|
17892
|
|
mFont = font;
|
|
17893
|
|
}
|
|
17894
|
|
|
|
17895
|
|
/*!
|
|
17896
|
|
Sets the default text color of this specific legend item to \a color.
|
|
17897
|
|
|
|
17898
|
|
\see setFont, QCPLegend::setTextColor
|
|
17899
|
|
*/
|
|
17900
|
|
void QCPAbstractLegendItem::setTextColor(const QColor &color)
|
|
17901
|
|
{
|
|
17902
|
|
mTextColor = color;
|
|
17903
|
|
}
|
|
17904
|
|
|
|
17905
|
|
/*!
|
|
17906
|
|
When this legend item is selected, \a font is used to draw generic text, instead of the normal
|
|
17907
|
|
font set with \ref setFont.
|
|
17908
|
|
|
|
17909
|
|
\see setFont, QCPLegend::setSelectedFont
|
|
17910
|
|
*/
|
|
17911
|
|
void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
|
|
17912
|
|
{
|
|
17913
|
|
mSelectedFont = font;
|
|
17914
|
|
}
|
|
17915
|
|
|
|
17916
|
|
/*!
|
|
17917
|
|
When this legend item is selected, \a color is used to draw generic text, instead of the normal
|
|
17918
|
|
color set with \ref setTextColor.
|
|
17919
|
|
|
|
17920
|
|
\see setTextColor, QCPLegend::setSelectedTextColor
|
|
17921
|
|
*/
|
|
17922
|
|
void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
|
|
17923
|
|
{
|
|
17924
|
|
mSelectedTextColor = color;
|
|
17925
|
|
}
|
|
17926
|
|
|
|
17927
|
|
/*!
|
|
17928
|
|
Sets whether this specific legend item is selectable.
|
|
17929
|
|
|
|
17930
|
|
\see setSelectedParts, QCustomPlot::setInteractions
|
|
17931
|
|
*/
|
|
17932
|
|
void QCPAbstractLegendItem::setSelectable(bool selectable)
|
|
17933
|
|
{
|
|
17934
|
|
mSelectable = selectable;
|
|
17935
|
|
}
|
|
17936
|
|
|
|
17937
|
|
/*!
|
|
17938
|
|
Sets whether this specific legend item is selected.
|
|
17939
|
|
|
|
17940
|
|
It is possible to set the selection state of this item by calling this function directly, even if
|
|
17941
|
|
setSelectable is set to false.
|
|
17942
|
|
|
|
17943
|
|
\see setSelectableParts, QCustomPlot::setInteractions
|
|
17944
|
|
*/
|
|
17945
|
|
void QCPAbstractLegendItem::setSelected(bool selected)
|
|
17946
|
|
{
|
|
17947
|
|
if (mSelected != selected)
|
|
17948
|
|
{
|
|
17949
|
|
mSelected = selected;
|
|
17950
|
|
emit selectionChanged(mSelected);
|
|
17951
|
|
}
|
|
17952
|
|
}
|
|
17953
|
|
|
|
17954
|
|
/* inherits documentation from base class */
|
|
17955
|
|
double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
17956
|
|
{
|
|
17957
|
|
Q_UNUSED(details)
|
|
17958
|
|
if (!mParentPlot) return -1;
|
|
17959
|
|
if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
|
|
17960
|
|
return -1;
|
|
17961
|
|
|
|
17962
|
|
if (mRect.contains(pos.toPoint()))
|
|
17963
|
|
return mParentPlot->selectionTolerance()*0.99;
|
|
17964
|
|
else
|
|
17965
|
|
return -1;
|
|
17966
|
|
}
|
|
17967
|
|
|
|
17968
|
|
/* inherits documentation from base class */
|
|
17969
|
|
void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
17970
|
|
{
|
|
17971
|
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
|
|
17972
|
|
}
|
|
17973
|
|
|
|
17974
|
|
/* inherits documentation from base class */
|
|
17975
|
|
QRect QCPAbstractLegendItem::clipRect() const
|
|
17976
|
|
{
|
|
17977
|
|
return mOuterRect;
|
|
17978
|
|
}
|
|
17979
|
|
|
|
17980
|
|
/* inherits documentation from base class */
|
|
17981
|
|
void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
17982
|
|
{
|
|
17983
|
|
Q_UNUSED(event)
|
|
17984
|
|
Q_UNUSED(details)
|
|
17985
|
|
if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
|
|
17986
|
|
{
|
|
17987
|
|
bool selBefore = mSelected;
|
|
17988
|
|
setSelected(additive ? !mSelected : true);
|
|
17989
|
|
if (selectionStateChanged)
|
|
17990
|
|
*selectionStateChanged = mSelected != selBefore;
|
|
17991
|
|
}
|
|
17992
|
|
}
|
|
17993
|
|
|
|
17994
|
|
/* inherits documentation from base class */
|
|
17995
|
|
void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
|
|
17996
|
|
{
|
|
17997
|
|
if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
|
|
17998
|
|
{
|
|
17999
|
|
bool selBefore = mSelected;
|
|
18000
|
|
setSelected(false);
|
|
18001
|
|
if (selectionStateChanged)
|
|
18002
|
|
*selectionStateChanged = mSelected != selBefore;
|
|
18003
|
|
}
|
|
18004
|
|
}
|
|
18005
|
|
|
|
18006
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
18007
|
|
//////////////////// QCPPlottableLegendItem
|
|
18008
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
18009
|
|
|
|
18010
|
|
/*! \class QCPPlottableLegendItem
|
|
18011
|
|
\brief A legend item representing a plottable with an icon and the plottable name.
|
|
18012
|
|
|
|
18013
|
|
This is the standard legend item for plottables. It displays an icon of the plottable next to the
|
|
18014
|
|
plottable name. The icon is drawn by the respective plottable itself (\ref
|
|
18015
|
|
QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
|
|
18016
|
|
For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
|
|
18017
|
|
middle.
|
|
18018
|
|
|
|
18019
|
|
Legend items of this type are always associated with one plottable (retrievable via the
|
|
18020
|
|
plottable() function and settable with the constructor). You may change the font of the plottable
|
|
18021
|
|
name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
|
|
18022
|
|
QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
|
|
18023
|
|
|
|
18024
|
|
The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
|
|
18025
|
|
creates/removes legend items of this type in the default implementation. However, these functions
|
|
18026
|
|
may be reimplemented such that a different kind of legend item (e.g a direct subclass of
|
|
18027
|
|
QCPAbstractLegendItem) is used for that plottable.
|
|
18028
|
|
|
|
18029
|
|
Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
|
|
18030
|
|
QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
|
|
18031
|
|
interface, QCPLegend has specialized functions for handling legend items conveniently, see the
|
|
18032
|
|
documentation of \ref QCPLegend.
|
|
18033
|
|
*/
|
|
18034
|
|
|
|
18035
|
|
/*!
|
|
18036
|
|
Creates a new legend item associated with \a plottable.
|
|
18037
|
|
|
|
18038
|
|
Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
|
|
18039
|
|
|
|
18040
|
|
A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
|
|
18041
|
|
QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
|
|
18042
|
|
*/
|
|
18043
|
|
QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
|
|
18044
|
|
QCPAbstractLegendItem(parent),
|
|
18045
|
|
mPlottable(plottable)
|
|
18046
|
|
{
|
|
18047
|
|
}
|
|
18048
|
|
|
|
18049
|
|
/*! \internal
|
|
18050
|
|
|
|
18051
|
|
Returns the pen that shall be used to draw the icon border, taking into account the selection
|
|
18052
|
|
state of this item.
|
|
18053
|
|
*/
|
|
18054
|
|
QPen QCPPlottableLegendItem::getIconBorderPen() const
|
|
18055
|
|
{
|
|
18056
|
|
return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
|
|
18057
|
|
}
|
|
18058
|
|
|
|
18059
|
|
/*! \internal
|
|
18060
|
|
|
|
18061
|
|
Returns the text color that shall be used to draw text, taking into account the selection state
|
|
18062
|
|
of this item.
|
|
18063
|
|
*/
|
|
18064
|
|
QColor QCPPlottableLegendItem::getTextColor() const
|
|
18065
|
|
{
|
|
18066
|
|
return mSelected ? mSelectedTextColor : mTextColor;
|
|
18067
|
|
}
|
|
18068
|
|
|
|
18069
|
|
/*! \internal
|
|
18070
|
|
|
|
18071
|
|
Returns the font that shall be used to draw text, taking into account the selection state of this
|
|
18072
|
|
item.
|
|
18073
|
|
*/
|
|
18074
|
|
QFont QCPPlottableLegendItem::getFont() const
|
|
18075
|
|
{
|
|
18076
|
|
return mSelected ? mSelectedFont : mFont;
|
|
18077
|
|
}
|
|
18078
|
|
|
|
18079
|
|
/*! \internal
|
|
18080
|
|
|
|
18081
|
|
Draws the item with \a painter. The size and position of the drawn legend item is defined by the
|
|
18082
|
|
parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
|
|
18083
|
|
of this legend item.
|
|
18084
|
|
*/
|
|
18085
|
|
void QCPPlottableLegendItem::draw(QCPPainter *painter)
|
|
18086
|
|
{
|
|
18087
|
|
if (!mPlottable) return;
|
|
18088
|
|
painter->setFont(getFont());
|
|
18089
|
|
painter->setPen(QPen(getTextColor()));
|
|
18090
|
|
QSizeF iconSize = mParentLegend->iconSize();
|
|
18091
|
|
QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
|
|
18092
|
|
QRectF iconRect(mRect.topLeft(), iconSize);
|
|
18093
|
|
int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
|
|
18094
|
|
painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
|
|
18095
|
|
// draw icon:
|
|
18096
|
|
painter->save();
|
|
18097
|
|
painter->setClipRect(iconRect, Qt::IntersectClip);
|
|
18098
|
|
mPlottable->drawLegendIcon(painter, iconRect);
|
|
18099
|
|
painter->restore();
|
|
18100
|
|
// draw icon border:
|
|
18101
|
|
if (getIconBorderPen().style() != Qt::NoPen)
|
|
18102
|
|
{
|
|
18103
|
|
painter->setPen(getIconBorderPen());
|
|
18104
|
|
painter->setBrush(Qt::NoBrush);
|
|
18105
|
|
painter->drawRect(iconRect);
|
|
18106
|
|
}
|
|
18107
|
|
}
|
|
18108
|
|
|
|
18109
|
|
/*! \internal
|
|
18110
|
|
|
|
18111
|
|
Calculates and returns the size of this item. This includes the icon, the text and the padding in
|
|
18112
|
|
between.
|
|
18113
|
|
*/
|
|
18114
|
|
QSize QCPPlottableLegendItem::minimumSizeHint() const
|
|
18115
|
|
{
|
|
18116
|
|
if (!mPlottable) return QSize();
|
|
18117
|
|
QSize result(0, 0);
|
|
18118
|
|
QRect textRect;
|
|
18119
|
|
QFontMetrics fontMetrics(getFont());
|
|
18120
|
|
QSize iconSize = mParentLegend->iconSize();
|
|
18121
|
|
textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
|
|
18122
|
|
result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
|
|
18123
|
|
result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
|
|
18124
|
|
return result;
|
|
18125
|
|
}
|
|
18126
|
|
|
|
18127
|
|
|
|
18128
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
18129
|
|
//////////////////// QCPLegend
|
|
18130
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
18131
|
|
|
|
18132
|
|
/*! \class QCPLegend
|
|
18133
|
|
\brief Manages a legend inside a QCustomPlot.
|
|
18134
|
|
|
|
18135
|
|
A legend is a small box somewhere in the plot which lists plottables with their name and icon.
|
|
18136
|
|
|
|
18137
|
|
Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
|
|
18138
|
|
respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
|
|
18139
|
|
QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
|
|
18140
|
|
itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
|
|
18141
|
|
|
|
18142
|
|
The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
|
|
18143
|
|
QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
|
|
18144
|
|
placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
|
|
18145
|
|
handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
|
|
18146
|
|
other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
|
|
18147
|
|
However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
|
|
18148
|
|
only return the number of items with QCPAbstractLegendItems type).
|
|
18149
|
|
|
|
18150
|
|
By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
|
|
18151
|
|
layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
|
|
18152
|
|
position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
|
|
18153
|
|
outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
|
|
18154
|
|
*/
|
|
18155
|
|
|
|
18156
|
|
/* start of documentation of signals */
|
|
18157
|
|
|
|
18158
|
|
/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
|
|
18159
|
|
|
|
18160
|
|
This signal is emitted when the selection state of this legend has changed.
|
|
18161
|
|
|
|
18162
|
|
\see setSelectedParts, setSelectableParts
|
|
18163
|
|
*/
|
|
18164
|
|
|
|
18165
|
|
/* end of documentation of signals */
|
|
18166
|
|
|
|
18167
|
|
/*!
|
|
18168
|
|
Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
|
|
18169
|
|
|
|
18170
|
|
Note that by default, QCustomPlot already contains a legend ready to be used as
|
|
18171
|
|
QCustomPlot::legend
|
|
18172
|
|
*/
|
|
18173
|
|
QCPLegend::QCPLegend()
|
|
18174
|
|
{
|
|
18175
|
|
setRowSpacing(0);
|
|
18176
|
|
setColumnSpacing(10);
|
|
18177
|
|
setMargins(QMargins(2, 3, 2, 2));
|
|
18178
|
|
setAntialiased(false);
|
|
18179
|
|
setIconSize(32, 18);
|
|
18180
|
|
|
|
18181
|
|
setIconTextPadding(7);
|
|
18182
|
|
|
|
18183
|
|
setSelectableParts(spLegendBox | spItems);
|
|
18184
|
|
setSelectedParts(spNone);
|
|
18185
|
|
|
|
18186
|
|
setBorderPen(QPen(Qt::black));
|
|
18187
|
|
setSelectedBorderPen(QPen(Qt::blue, 2));
|
|
18188
|
|
setIconBorderPen(Qt::NoPen);
|
|
18189
|
|
setSelectedIconBorderPen(QPen(Qt::blue, 2));
|
|
18190
|
|
setBrush(Qt::white);
|
|
18191
|
|
setSelectedBrush(Qt::white);
|
|
18192
|
|
setTextColor(Qt::black);
|
|
18193
|
|
setSelectedTextColor(Qt::blue);
|
|
18194
|
|
}
|
|
18195
|
|
|
|
18196
|
|
QCPLegend::~QCPLegend()
|
|
18197
|
|
{
|
|
18198
|
|
clearItems();
|
|
18199
|
|
if (mParentPlot)
|
|
18200
|
|
mParentPlot->legendRemoved(this);
|
|
18201
|
|
}
|
|
18202
|
|
|
|
18203
|
|
/* no doc for getter, see setSelectedParts */
|
|
18204
|
|
QCPLegend::SelectableParts QCPLegend::selectedParts() const
|
|
18205
|
|
{
|
|
18206
|
|
// check whether any legend elements selected, if yes, add spItems to return value
|
|
18207
|
|
bool hasSelectedItems = false;
|
|
18208
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18209
|
|
{
|
|
18210
|
|
if (item(i) && item(i)->selected())
|
|
18211
|
|
{
|
|
18212
|
|
hasSelectedItems = true;
|
|
18213
|
|
break;
|
|
18214
|
|
}
|
|
18215
|
|
}
|
|
18216
|
|
if (hasSelectedItems)
|
|
18217
|
|
return mSelectedParts | spItems;
|
|
18218
|
|
else
|
|
18219
|
|
return mSelectedParts & ~spItems;
|
|
18220
|
|
}
|
|
18221
|
|
|
|
18222
|
|
/*!
|
|
18223
|
|
Sets the pen, the border of the entire legend is drawn with.
|
|
18224
|
|
*/
|
|
18225
|
|
void QCPLegend::setBorderPen(const QPen &pen)
|
|
18226
|
|
{
|
|
18227
|
|
mBorderPen = pen;
|
|
18228
|
|
}
|
|
18229
|
|
|
|
18230
|
|
/*!
|
|
18231
|
|
Sets the brush of the legend background.
|
|
18232
|
|
*/
|
|
18233
|
|
void QCPLegend::setBrush(const QBrush &brush)
|
|
18234
|
|
{
|
|
18235
|
|
mBrush = brush;
|
|
18236
|
|
}
|
|
18237
|
|
|
|
18238
|
|
/*!
|
|
18239
|
|
Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
|
|
18240
|
|
use this font by default. However, a different font can be specified on a per-item-basis by
|
|
18241
|
|
accessing the specific legend item.
|
|
18242
|
|
|
|
18243
|
|
This function will also set \a font on all already existing legend items.
|
|
18244
|
|
|
|
18245
|
|
\see QCPAbstractLegendItem::setFont
|
|
18246
|
|
*/
|
|
18247
|
|
void QCPLegend::setFont(const QFont &font)
|
|
18248
|
|
{
|
|
18249
|
|
mFont = font;
|
|
18250
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18251
|
|
{
|
|
18252
|
|
if (item(i))
|
|
18253
|
|
item(i)->setFont(mFont);
|
|
18254
|
|
}
|
|
18255
|
|
}
|
|
18256
|
|
|
|
18257
|
|
/*!
|
|
18258
|
|
Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
|
|
18259
|
|
will use this color by default. However, a different colors can be specified on a per-item-basis
|
|
18260
|
|
by accessing the specific legend item.
|
|
18261
|
|
|
|
18262
|
|
This function will also set \a color on all already existing legend items.
|
|
18263
|
|
|
|
18264
|
|
\see QCPAbstractLegendItem::setTextColor
|
|
18265
|
|
*/
|
|
18266
|
|
void QCPLegend::setTextColor(const QColor &color)
|
|
18267
|
|
{
|
|
18268
|
|
mTextColor = color;
|
|
18269
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18270
|
|
{
|
|
18271
|
|
if (item(i))
|
|
18272
|
|
item(i)->setTextColor(color);
|
|
18273
|
|
}
|
|
18274
|
|
}
|
|
18275
|
|
|
|
18276
|
|
/*!
|
|
18277
|
|
Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
|
|
18278
|
|
representation of the graph) will use this size by default.
|
|
18279
|
|
*/
|
|
18280
|
|
void QCPLegend::setIconSize(const QSize &size)
|
|
18281
|
|
{
|
|
18282
|
|
mIconSize = size;
|
|
18283
|
|
}
|
|
18284
|
|
|
|
18285
|
|
/*! \overload
|
|
18286
|
|
*/
|
|
18287
|
|
void QCPLegend::setIconSize(int width, int height)
|
|
18288
|
|
{
|
|
18289
|
|
mIconSize.setWidth(width);
|
|
18290
|
|
mIconSize.setHeight(height);
|
|
18291
|
|
}
|
|
18292
|
|
|
|
18293
|
|
/*!
|
|
18294
|
|
Sets the horizontal space in pixels between the legend icon and the text next to it.
|
|
18295
|
|
Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
|
|
18296
|
|
name of the graph) will use this space by default.
|
|
18297
|
|
*/
|
|
18298
|
|
void QCPLegend::setIconTextPadding(int padding)
|
|
18299
|
|
{
|
|
18300
|
|
mIconTextPadding = padding;
|
|
18301
|
|
}
|
|
18302
|
|
|
|
18303
|
|
/*!
|
|
18304
|
|
Sets the pen used to draw a border around each legend icon. Legend items that draw an
|
|
18305
|
|
icon (e.g. a visual representation of the graph) will use this pen by default.
|
|
18306
|
|
|
|
18307
|
|
If no border is wanted, set this to \a Qt::NoPen.
|
|
18308
|
|
*/
|
|
18309
|
|
void QCPLegend::setIconBorderPen(const QPen &pen)
|
|
18310
|
|
{
|
|
18311
|
|
mIconBorderPen = pen;
|
|
18312
|
|
}
|
|
18313
|
|
|
|
18314
|
|
/*!
|
|
18315
|
|
Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
|
|
18316
|
|
(When \ref QCustomPlot::setInteractions contains iSelectLegend.)
|
|
18317
|
|
|
|
18318
|
|
However, even when \a selectable is set to a value not allowing the selection of a specific part,
|
|
18319
|
|
it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
|
|
18320
|
|
directly.
|
|
18321
|
|
|
|
18322
|
|
\see SelectablePart, setSelectedParts
|
|
18323
|
|
*/
|
|
18324
|
|
void QCPLegend::setSelectableParts(const SelectableParts &selectable)
|
|
18325
|
|
{
|
|
18326
|
|
mSelectableParts = selectable;
|
|
18327
|
|
}
|
|
18328
|
|
|
|
18329
|
|
/*!
|
|
18330
|
|
Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
|
|
18331
|
|
is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
|
|
18332
|
|
doesn't contain \ref spItems, those items become deselected.
|
|
18333
|
|
|
|
18334
|
|
The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
|
|
18335
|
|
contains iSelectLegend. You only need to call this function when you wish to change the selection
|
|
18336
|
|
state manually.
|
|
18337
|
|
|
|
18338
|
|
This function can change the selection state of a part even when \ref setSelectableParts was set to a
|
|
18339
|
|
value that actually excludes the part.
|
|
18340
|
|
|
|
18341
|
|
emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
|
|
18342
|
|
|
|
18343
|
|
Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
|
|
18344
|
|
before, because there's no way to specify which exact items to newly select. Do this by calling
|
|
18345
|
|
\ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
|
|
18346
|
|
|
|
18347
|
|
\see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
|
|
18348
|
|
setSelectedFont
|
|
18349
|
|
*/
|
|
18350
|
|
void QCPLegend::setSelectedParts(const SelectableParts &selected)
|
|
18351
|
|
{
|
|
18352
|
|
SelectableParts newSelected = selected;
|
|
18353
|
|
mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
|
|
18354
|
|
|
|
18355
|
|
if (mSelectedParts != newSelected)
|
|
18356
|
|
{
|
|
18357
|
|
if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
|
|
18358
|
|
{
|
|
18359
|
|
qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
|
|
18360
|
|
newSelected &= ~spItems;
|
|
18361
|
|
}
|
|
18362
|
|
if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
|
|
18363
|
|
{
|
|
18364
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18365
|
|
{
|
|
18366
|
|
if (item(i))
|
|
18367
|
|
item(i)->setSelected(false);
|
|
18368
|
|
}
|
|
18369
|
|
}
|
|
18370
|
|
mSelectedParts = newSelected;
|
|
18371
|
|
emit selectionChanged(mSelectedParts);
|
|
18372
|
|
}
|
|
18373
|
|
}
|
|
18374
|
|
|
|
18375
|
|
/*!
|
|
18376
|
|
When the legend box is selected, this pen is used to draw the border instead of the normal pen
|
|
18377
|
|
set via \ref setBorderPen.
|
|
18378
|
|
|
|
18379
|
|
\see setSelectedParts, setSelectableParts, setSelectedBrush
|
|
18380
|
|
*/
|
|
18381
|
|
void QCPLegend::setSelectedBorderPen(const QPen &pen)
|
|
18382
|
|
{
|
|
18383
|
|
mSelectedBorderPen = pen;
|
|
18384
|
|
}
|
|
18385
|
|
|
|
18386
|
|
/*!
|
|
18387
|
|
Sets the pen legend items will use to draw their icon borders, when they are selected.
|
|
18388
|
|
|
|
18389
|
|
\see setSelectedParts, setSelectableParts, setSelectedFont
|
|
18390
|
|
*/
|
|
18391
|
|
void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
|
|
18392
|
|
{
|
|
18393
|
|
mSelectedIconBorderPen = pen;
|
|
18394
|
|
}
|
|
18395
|
|
|
|
18396
|
|
/*!
|
|
18397
|
|
When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
|
|
18398
|
|
set via \ref setBrush.
|
|
18399
|
|
|
|
18400
|
|
\see setSelectedParts, setSelectableParts, setSelectedBorderPen
|
|
18401
|
|
*/
|
|
18402
|
|
void QCPLegend::setSelectedBrush(const QBrush &brush)
|
|
18403
|
|
{
|
|
18404
|
|
mSelectedBrush = brush;
|
|
18405
|
|
}
|
|
18406
|
|
|
|
18407
|
|
/*!
|
|
18408
|
|
Sets the default font that is used by legend items when they are selected.
|
|
18409
|
|
|
|
18410
|
|
This function will also set \a font on all already existing legend items.
|
|
18411
|
|
|
|
18412
|
|
\see setFont, QCPAbstractLegendItem::setSelectedFont
|
|
18413
|
|
*/
|
|
18414
|
|
void QCPLegend::setSelectedFont(const QFont &font)
|
|
18415
|
|
{
|
|
18416
|
|
mSelectedFont = font;
|
|
18417
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18418
|
|
{
|
|
18419
|
|
if (item(i))
|
|
18420
|
|
item(i)->setSelectedFont(font);
|
|
18421
|
|
}
|
|
18422
|
|
}
|
|
18423
|
|
|
|
18424
|
|
/*!
|
|
18425
|
|
Sets the default text color that is used by legend items when they are selected.
|
|
18426
|
|
|
|
18427
|
|
This function will also set \a color on all already existing legend items.
|
|
18428
|
|
|
|
18429
|
|
\see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
|
|
18430
|
|
*/
|
|
18431
|
|
void QCPLegend::setSelectedTextColor(const QColor &color)
|
|
18432
|
|
{
|
|
18433
|
|
mSelectedTextColor = color;
|
|
18434
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18435
|
|
{
|
|
18436
|
|
if (item(i))
|
|
18437
|
|
item(i)->setSelectedTextColor(color);
|
|
18438
|
|
}
|
|
18439
|
|
}
|
|
18440
|
|
|
|
18441
|
|
/*!
|
|
18442
|
|
Returns the item with index \a i.
|
|
18443
|
|
|
|
18444
|
|
\see itemCount
|
|
18445
|
|
*/
|
|
18446
|
|
QCPAbstractLegendItem *QCPLegend::item(int index) const
|
|
18447
|
|
{
|
|
18448
|
|
return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
|
|
18449
|
|
}
|
|
18450
|
|
|
|
18451
|
|
/*!
|
|
18452
|
|
Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
|
|
18453
|
|
If such an item isn't in the legend, returns 0.
|
|
18454
|
|
|
|
18455
|
|
\see hasItemWithPlottable
|
|
18456
|
|
*/
|
|
18457
|
|
QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
|
|
18458
|
|
{
|
|
18459
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18460
|
|
{
|
|
18461
|
|
if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
|
|
18462
|
|
{
|
|
18463
|
|
if (pli->plottable() == plottable)
|
|
18464
|
|
return pli;
|
|
18465
|
|
}
|
|
18466
|
|
}
|
|
18467
|
|
return 0;
|
|
18468
|
|
}
|
|
18469
|
|
|
|
18470
|
|
/*!
|
|
18471
|
|
Returns the number of items currently in the legend.
|
|
18472
|
|
\see item
|
|
18473
|
|
*/
|
|
18474
|
|
int QCPLegend::itemCount() const
|
|
18475
|
|
{
|
|
18476
|
|
return elementCount();
|
|
18477
|
|
}
|
|
18478
|
|
|
|
18479
|
|
/*!
|
|
18480
|
|
Returns whether the legend contains \a itm.
|
|
18481
|
|
*/
|
|
18482
|
|
bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
|
|
18483
|
|
{
|
|
18484
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18485
|
|
{
|
|
18486
|
|
if (item == this->item(i))
|
|
18487
|
|
return true;
|
|
18488
|
|
}
|
|
18489
|
|
return false;
|
|
18490
|
|
}
|
|
18491
|
|
|
|
18492
|
|
/*!
|
|
18493
|
|
Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
|
|
18494
|
|
If such an item isn't in the legend, returns false.
|
|
18495
|
|
|
|
18496
|
|
\see itemWithPlottable
|
|
18497
|
|
*/
|
|
18498
|
|
bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
|
|
18499
|
|
{
|
|
18500
|
|
return itemWithPlottable(plottable);
|
|
18501
|
|
}
|
|
18502
|
|
|
|
18503
|
|
/*!
|
|
18504
|
|
Adds \a item to the legend, if it's not present already.
|
|
18505
|
|
|
|
18506
|
|
Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
|
|
18507
|
|
|
|
18508
|
|
The legend takes ownership of the item.
|
|
18509
|
|
*/
|
|
18510
|
|
bool QCPLegend::addItem(QCPAbstractLegendItem *item)
|
|
18511
|
|
{
|
|
18512
|
|
if (!hasItem(item))
|
|
18513
|
|
{
|
|
18514
|
|
return addElement(rowCount(), 0, item);
|
|
18515
|
|
} else
|
|
18516
|
|
return false;
|
|
18517
|
|
}
|
|
18518
|
|
|
|
18519
|
|
/*!
|
|
18520
|
|
Removes the item with index \a index from the legend.
|
|
18521
|
|
|
|
18522
|
|
Returns true, if successful.
|
|
18523
|
|
|
|
18524
|
|
\see itemCount, clearItems
|
|
18525
|
|
*/
|
|
18526
|
|
bool QCPLegend::removeItem(int index)
|
|
18527
|
|
{
|
|
18528
|
|
if (QCPAbstractLegendItem *ali = item(index))
|
|
18529
|
|
{
|
|
18530
|
|
bool success = remove(ali);
|
|
18531
|
|
simplify();
|
|
18532
|
|
return success;
|
|
18533
|
|
} else
|
|
18534
|
|
return false;
|
|
18535
|
|
}
|
|
18536
|
|
|
|
18537
|
|
/*! \overload
|
|
18538
|
|
|
|
18539
|
|
Removes \a item from the legend.
|
|
18540
|
|
|
|
18541
|
|
Returns true, if successful.
|
|
18542
|
|
|
|
18543
|
|
\see clearItems
|
|
18544
|
|
*/
|
|
18545
|
|
bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
|
|
18546
|
|
{
|
|
18547
|
|
bool success = remove(item);
|
|
18548
|
|
simplify();
|
|
18549
|
|
return success;
|
|
18550
|
|
}
|
|
18551
|
|
|
|
18552
|
|
/*!
|
|
18553
|
|
Removes all items from the legend.
|
|
18554
|
|
*/
|
|
18555
|
|
void QCPLegend::clearItems()
|
|
18556
|
|
{
|
|
18557
|
|
for (int i=itemCount()-1; i>=0; --i)
|
|
18558
|
|
removeItem(i);
|
|
18559
|
|
}
|
|
18560
|
|
|
|
18561
|
|
/*!
|
|
18562
|
|
Returns the legend items that are currently selected. If no items are selected,
|
|
18563
|
|
the list is empty.
|
|
18564
|
|
|
|
18565
|
|
\see QCPAbstractLegendItem::setSelected, setSelectable
|
|
18566
|
|
*/
|
|
18567
|
|
QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
|
|
18568
|
|
{
|
|
18569
|
|
QList<QCPAbstractLegendItem*> result;
|
|
18570
|
|
for (int i=0; i<itemCount(); ++i)
|
|
18571
|
|
{
|
|
18572
|
|
if (QCPAbstractLegendItem *ali = item(i))
|
|
18573
|
|
{
|
|
18574
|
|
if (ali->selected())
|
|
18575
|
|
result.append(ali);
|
|
18576
|
|
}
|
|
18577
|
|
}
|
|
18578
|
|
return result;
|
|
18579
|
|
}
|
|
18580
|
|
|
|
18581
|
|
/*! \internal
|
|
18582
|
|
|
|
18583
|
|
A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
|
|
18584
|
|
before drawing main legend elements.
|
|
18585
|
|
|
|
18586
|
|
This is the antialiasing state the painter passed to the \ref draw method is in by default.
|
|
18587
|
|
|
|
18588
|
|
This function takes into account the local setting of the antialiasing flag as well as the
|
|
18589
|
|
overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
|
|
18590
|
|
QCustomPlot::setNotAntialiasedElements.
|
|
18591
|
|
|
|
18592
|
|
\see setAntialiased
|
|
18593
|
|
*/
|
|
18594
|
|
void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
18595
|
|
{
|
|
18596
|
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
|
|
18597
|
|
}
|
|
18598
|
|
|
|
18599
|
|
/*! \internal
|
|
18600
|
|
|
|
18601
|
|
Returns the pen used to paint the border of the legend, taking into account the selection state
|
|
18602
|
|
of the legend box.
|
|
18603
|
|
*/
|
|
18604
|
|
QPen QCPLegend::getBorderPen() const
|
|
18605
|
|
{
|
|
18606
|
|
return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
|
|
18607
|
|
}
|
|
18608
|
|
|
|
18609
|
|
/*! \internal
|
|
18610
|
|
|
|
18611
|
|
Returns the brush used to paint the background of the legend, taking into account the selection
|
|
18612
|
|
state of the legend box.
|
|
18613
|
|
*/
|
|
18614
|
|
QBrush QCPLegend::getBrush() const
|
|
18615
|
|
{
|
|
18616
|
|
return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
|
|
18617
|
|
}
|
|
18618
|
|
|
|
18619
|
|
/*! \internal
|
|
18620
|
|
|
|
18621
|
|
Draws the legend box with the provided \a painter. The individual legend items are layerables
|
|
18622
|
|
themselves, thus are drawn independently.
|
|
18623
|
|
*/
|
|
18624
|
|
void QCPLegend::draw(QCPPainter *painter)
|
|
18625
|
|
{
|
|
18626
|
|
// draw background rect:
|
|
18627
|
|
painter->setBrush(getBrush());
|
|
18628
|
|
painter->setPen(getBorderPen());
|
|
18629
|
|
painter->drawRect(mOuterRect);
|
|
18630
|
|
}
|
|
18631
|
|
|
|
18632
|
|
/* inherits documentation from base class */
|
|
18633
|
|
double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
18634
|
|
{
|
|
18635
|
|
if (!mParentPlot) return -1;
|
|
18636
|
|
if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
|
|
18637
|
|
return -1;
|
|
18638
|
|
|
|
18639
|
|
if (mOuterRect.contains(pos.toPoint()))
|
|
18640
|
|
{
|
|
18641
|
|
if (details) details->setValue(spLegendBox);
|
|
18642
|
|
return mParentPlot->selectionTolerance()*0.99;
|
|
18643
|
|
}
|
|
18644
|
|
return -1;
|
|
18645
|
|
}
|
|
18646
|
|
|
|
18647
|
|
/* inherits documentation from base class */
|
|
18648
|
|
void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
18649
|
|
{
|
|
18650
|
|
Q_UNUSED(event)
|
|
18651
|
|
mSelectedParts = selectedParts(); // in case item selection has changed
|
|
18652
|
|
if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
|
|
18653
|
|
{
|
|
18654
|
|
SelectableParts selBefore = mSelectedParts;
|
|
18655
|
|
setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
|
|
18656
|
|
if (selectionStateChanged)
|
|
18657
|
|
*selectionStateChanged = mSelectedParts != selBefore;
|
|
18658
|
|
}
|
|
18659
|
|
}
|
|
18660
|
|
|
|
18661
|
|
/* inherits documentation from base class */
|
|
18662
|
|
void QCPLegend::deselectEvent(bool *selectionStateChanged)
|
|
18663
|
|
{
|
|
18664
|
|
mSelectedParts = selectedParts(); // in case item selection has changed
|
|
18665
|
|
if (mSelectableParts.testFlag(spLegendBox))
|
|
18666
|
|
{
|
|
18667
|
|
SelectableParts selBefore = mSelectedParts;
|
|
18668
|
|
setSelectedParts(selectedParts() & ~spLegendBox);
|
|
18669
|
|
if (selectionStateChanged)
|
|
18670
|
|
*selectionStateChanged = mSelectedParts != selBefore;
|
|
18671
|
|
}
|
|
18672
|
|
}
|
|
18673
|
|
|
|
18674
|
|
/* inherits documentation from base class */
|
|
18675
|
|
QCP::Interaction QCPLegend::selectionCategory() const
|
|
18676
|
|
{
|
|
18677
|
|
return QCP::iSelectLegend;
|
|
18678
|
|
}
|
|
18679
|
|
|
|
18680
|
|
/* inherits documentation from base class */
|
|
18681
|
|
QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
|
|
18682
|
|
{
|
|
18683
|
|
return QCP::iSelectLegend;
|
|
18684
|
|
}
|
|
18685
|
|
|
|
18686
|
|
/* inherits documentation from base class */
|
|
18687
|
|
void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
|
|
18688
|
|
{
|
|
18689
|
|
Q_UNUSED(parentPlot)
|
|
18690
|
|
}
|
|
18691
|
|
|
|
18692
|
|
|
|
18693
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
18694
|
|
//////////////////// QCPPlotTitle
|
|
18695
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
18696
|
|
|
|
18697
|
|
/*! \class QCPPlotTitle
|
|
18698
|
|
\brief A layout element displaying a plot title text
|
|
18699
|
|
|
|
18700
|
|
The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
|
|
18701
|
|
and \ref setTextColor.
|
|
18702
|
|
|
|
18703
|
|
A plot title can be added as follows:
|
|
18704
|
|
\code
|
|
18705
|
|
customPlot->plotLayout()->insertRow(0); // inserts an empty row above the default axis rect
|
|
18706
|
|
customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Your Plot Title"));
|
|
18707
|
|
\endcode
|
|
18708
|
|
|
|
18709
|
|
Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
|
|
18710
|
|
easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
|
|
18711
|
|
signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
|
|
18712
|
|
QCustomPlot::titleDoubleClick signal.
|
|
18713
|
|
*/
|
|
18714
|
|
|
|
18715
|
|
/* start documentation of signals */
|
|
18716
|
|
|
|
18717
|
|
/*! \fn void QCPPlotTitle::selectionChanged(bool selected)
|
|
18718
|
|
|
|
18719
|
|
This signal is emitted when the selection state has changed to \a selected, either by user
|
|
18720
|
|
interaction or by a direct call to \ref setSelected.
|
|
18721
|
|
|
|
18722
|
|
\see setSelected, setSelectable
|
|
18723
|
|
*/
|
|
18724
|
|
|
|
18725
|
|
/* end documentation of signals */
|
|
18726
|
|
|
|
18727
|
|
/*!
|
|
18728
|
|
Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
|
|
18729
|
|
|
|
18730
|
|
To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
|
|
18731
|
|
*/
|
|
18732
|
|
QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
|
|
18733
|
|
QCPLayoutElement(parentPlot),
|
|
18734
|
|
mFont(QFont("sans serif", 13*1.5, QFont::Bold)),
|
|
18735
|
|
mTextColor(Qt::black),
|
|
18736
|
|
mSelectedFont(QFont("sans serif", 13*1.6, QFont::Bold)),
|
|
18737
|
|
mSelectedTextColor(Qt::blue),
|
|
18738
|
|
mSelectable(false),
|
|
18739
|
|
mSelected(false)
|
|
18740
|
|
{
|
|
18741
|
|
if (parentPlot)
|
|
18742
|
|
{
|
|
18743
|
|
setLayer(parentPlot->currentLayer());
|
|
18744
|
|
mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
|
|
18745
|
|
mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
|
|
18746
|
|
}
|
|
18747
|
|
setMargins(QMargins(5, 5, 5, 0));
|
|
18748
|
|
}
|
|
18749
|
|
|
|
18750
|
|
/*! \overload
|
|
18751
|
|
|
|
18752
|
|
Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
|
|
18753
|
|
*/
|
|
18754
|
|
QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
|
|
18755
|
|
QCPLayoutElement(parentPlot),
|
|
18756
|
|
mText(text),
|
|
18757
|
|
mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
|
|
18758
|
|
mTextColor(Qt::black),
|
|
18759
|
|
mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
|
|
18760
|
|
mSelectedTextColor(Qt::blue),
|
|
18761
|
|
mSelectable(false),
|
|
18762
|
|
mSelected(false)
|
|
18763
|
|
{
|
|
18764
|
|
setLayer("axes");
|
|
18765
|
|
setMargins(QMargins(5, 5, 5, 0));
|
|
18766
|
|
}
|
|
18767
|
|
|
|
18768
|
|
/*!
|
|
18769
|
|
Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
|
|
18770
|
|
|
|
18771
|
|
\see setFont, setTextColor
|
|
18772
|
|
*/
|
|
18773
|
|
void QCPPlotTitle::setText(const QString &text)
|
|
18774
|
|
{
|
|
18775
|
|
mText = text;
|
|
18776
|
|
}
|
|
18777
|
|
|
|
18778
|
|
/*!
|
|
18779
|
|
Sets the \a font of the title text.
|
|
18780
|
|
|
|
18781
|
|
\see setTextColor, setSelectedFont
|
|
18782
|
|
*/
|
|
18783
|
|
void QCPPlotTitle::setFont(const QFont &font)
|
|
18784
|
|
{
|
|
18785
|
|
mFont = font;
|
|
18786
|
|
}
|
|
18787
|
|
|
|
18788
|
|
/*!
|
|
18789
|
|
Sets the \a color of the title text.
|
|
18790
|
|
|
|
18791
|
|
\see setFont, setSelectedTextColor
|
|
18792
|
|
*/
|
|
18793
|
|
void QCPPlotTitle::setTextColor(const QColor &color)
|
|
18794
|
|
{
|
|
18795
|
|
mTextColor = color;
|
|
18796
|
|
}
|
|
18797
|
|
|
|
18798
|
|
/*!
|
|
18799
|
|
Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
|
|
18800
|
|
|
|
18801
|
|
\see setFont
|
|
18802
|
|
*/
|
|
18803
|
|
void QCPPlotTitle::setSelectedFont(const QFont &font)
|
|
18804
|
|
{
|
|
18805
|
|
mSelectedFont = font;
|
|
18806
|
|
}
|
|
18807
|
|
|
|
18808
|
|
/*!
|
|
18809
|
|
Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
|
|
18810
|
|
|
|
18811
|
|
\see setTextColor
|
|
18812
|
|
*/
|
|
18813
|
|
void QCPPlotTitle::setSelectedTextColor(const QColor &color)
|
|
18814
|
|
{
|
|
18815
|
|
mSelectedTextColor = color;
|
|
18816
|
|
}
|
|
18817
|
|
|
|
18818
|
|
/*!
|
|
18819
|
|
Sets whether the user may select this plot title to \a selectable.
|
|
18820
|
|
|
|
18821
|
|
Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
|
|
18822
|
|
programmatically via \ref setSelected.
|
|
18823
|
|
*/
|
|
18824
|
|
void QCPPlotTitle::setSelectable(bool selectable)
|
|
18825
|
|
{
|
|
18826
|
|
mSelectable = selectable;
|
|
18827
|
|
}
|
|
18828
|
|
|
|
18829
|
|
/*!
|
|
18830
|
|
Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
|
|
18831
|
|
selectionChanged is emitted.
|
|
18832
|
|
|
|
18833
|
|
Note that this function can change the selection state independently of the current \ref
|
|
18834
|
|
setSelectable state.
|
|
18835
|
|
*/
|
|
18836
|
|
void QCPPlotTitle::setSelected(bool selected)
|
|
18837
|
|
{
|
|
18838
|
|
if (mSelected != selected)
|
|
18839
|
|
{
|
|
18840
|
|
mSelected = selected;
|
|
18841
|
|
emit selectionChanged(mSelected);
|
|
18842
|
|
}
|
|
18843
|
|
}
|
|
18844
|
|
|
|
18845
|
|
/* inherits documentation from base class */
|
|
18846
|
|
void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
|
|
18847
|
|
{
|
|
18848
|
|
applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
|
|
18849
|
|
}
|
|
18850
|
|
|
|
18851
|
|
/* inherits documentation from base class */
|
|
18852
|
|
void QCPPlotTitle::draw(QCPPainter *painter)
|
|
18853
|
|
{
|
|
18854
|
|
painter->setFont(mainFont());
|
|
18855
|
|
painter->setPen(QPen(mainTextColor()));
|
|
18856
|
|
painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
|
|
18857
|
|
}
|
|
18858
|
|
|
|
18859
|
|
/* inherits documentation from base class */
|
|
18860
|
|
QSize QCPPlotTitle::minimumSizeHint() const
|
|
18861
|
|
{
|
|
18862
|
|
QFontMetrics metrics(mFont);
|
|
18863
|
|
QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
|
|
18864
|
|
result.rwidth() += mMargins.left() + mMargins.right();
|
|
18865
|
|
result.rheight() += mMargins.top() + mMargins.bottom();
|
|
18866
|
|
return result;
|
|
18867
|
|
}
|
|
18868
|
|
|
|
18869
|
|
/* inherits documentation from base class */
|
|
18870
|
|
QSize QCPPlotTitle::maximumSizeHint() const
|
|
18871
|
|
{
|
|
18872
|
|
QFontMetrics metrics(mFont);
|
|
18873
|
|
QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
|
|
18874
|
|
result.rheight() += mMargins.top() + mMargins.bottom();
|
|
18875
|
|
result.setWidth(QWIDGETSIZE_MAX);
|
|
18876
|
|
return result;
|
|
18877
|
|
}
|
|
18878
|
|
|
|
18879
|
|
/* inherits documentation from base class */
|
|
18880
|
|
void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
|
|
18881
|
|
{
|
|
18882
|
|
Q_UNUSED(event)
|
|
18883
|
|
Q_UNUSED(details)
|
|
18884
|
|
if (mSelectable)
|
|
18885
|
|
{
|
|
18886
|
|
bool selBefore = mSelected;
|
|
18887
|
|
setSelected(additive ? !mSelected : true);
|
|
18888
|
|
if (selectionStateChanged)
|
|
18889
|
|
*selectionStateChanged = mSelected != selBefore;
|
|
18890
|
|
}
|
|
18891
|
|
}
|
|
18892
|
|
|
|
18893
|
|
/* inherits documentation from base class */
|
|
18894
|
|
void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
|
|
18895
|
|
{
|
|
18896
|
|
if (mSelectable)
|
|
18897
|
|
{
|
|
18898
|
|
bool selBefore = mSelected;
|
|
18899
|
|
setSelected(false);
|
|
18900
|
|
if (selectionStateChanged)
|
|
18901
|
|
*selectionStateChanged = mSelected != selBefore;
|
|
18902
|
|
}
|
|
18903
|
|
}
|
|
18904
|
|
|
|
18905
|
|
/* inherits documentation from base class */
|
|
18906
|
|
double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
18907
|
|
{
|
|
18908
|
|
Q_UNUSED(details)
|
|
18909
|
|
if (onlySelectable && !mSelectable)
|
|
18910
|
|
return -1;
|
|
18911
|
|
|
|
18912
|
|
if (mTextBoundingRect.contains(pos.toPoint()))
|
|
18913
|
|
return mParentPlot->selectionTolerance()*0.99;
|
|
18914
|
|
else
|
|
18915
|
|
return -1;
|
|
18916
|
|
}
|
|
18917
|
|
|
|
18918
|
|
/*! \internal
|
|
18919
|
|
|
|
18920
|
|
Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
|
|
18921
|
|
<tt>true</tt>, else mFont is returned.
|
|
18922
|
|
*/
|
|
18923
|
|
QFont QCPPlotTitle::mainFont() const
|
|
18924
|
|
{
|
|
18925
|
|
return mSelected ? mSelectedFont : mFont;
|
|
18926
|
|
}
|
|
18927
|
|
|
|
18928
|
|
/*! \internal
|
|
18929
|
|
|
|
18930
|
|
Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
|
|
18931
|
|
<tt>true</tt>, else mTextColor is returned.
|
|
18932
|
|
*/
|
|
18933
|
|
QColor QCPPlotTitle::mainTextColor() const
|
|
18934
|
|
{
|
|
18935
|
|
return mSelected ? mSelectedTextColor : mTextColor;
|
|
18936
|
|
}
|
|
18937
|
|
|