1 /***************************************************************************
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 2 of the License, or **
9 ** (at your option) any later version. **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Emanuel Eichhammer has granted Wireshark permission to use QCustomPlot **
23 ** under the terms of the GNU General Public License version 2. **
26 ****************************************************************************/
28 #include "qcustomplot.h"
32 ////////////////////////////////////////////////////////////////////////////////////////////////////
33 //////////////////// QCPPainter
34 ////////////////////////////////////////////////////////////////////////////////////////////////////
37 \brief QPainter subclass used internally
39 This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
40 consistency between antialiased and non-antialiased painting. Further it provides workarounds
43 \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
44 restore. So while it is possible to pass a QCPPainter instance to a function that expects a
45 QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
46 it will call the base class implementations of the functions actually hidden by QCPPainter).
50 Creates a new QCPPainter instance and sets default values
52 QCPPainter::QCPPainter() :
55 mIsAntialiasing(false)
57 // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
58 // a call to begin() will follow
62 Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
63 like the analogous QPainter constructor, begins painting on \a device immediately.
65 Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5.
67 QCPPainter::QCPPainter(QPaintDevice *device) :
70 mIsAntialiasing(false)
72 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
74 setRenderHint(QPainter::NonCosmeticDefaultPen);
78 QCPPainter::~QCPPainter()
83 Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
86 \note this function hides the non-virtual base class implementation.
88 void QCPPainter::setPen(const QPen &pen)
90 QPainter::setPen(pen);
91 if (mModes.testFlag(pmNonCosmetic))
97 Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
100 \note this function hides the non-virtual base class implementation.
102 void QCPPainter::setPen(const QColor &color)
104 QPainter::setPen(color);
105 if (mModes.testFlag(pmNonCosmetic))
111 Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
114 \note this function hides the non-virtual base class implementation.
116 void QCPPainter::setPen(Qt::PenStyle penStyle)
118 QPainter::setPen(penStyle);
119 if (mModes.testFlag(pmNonCosmetic))
125 Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
126 antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to
127 integer coordinates and then passes it to the original drawLine.
129 \note this function hides the non-virtual base class implementation.
131 void QCPPainter::drawLine(const QLineF &line)
133 if (mIsAntialiasing || mModes.testFlag(pmVectorized))
134 QPainter::drawLine(line);
136 QPainter::drawLine(line.toLine());
140 Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
141 with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
142 antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for
145 void QCPPainter::setAntialiasing(bool enabled)
147 setRenderHint(QPainter::Antialiasing, enabled);
148 if (mIsAntialiasing != enabled)
150 mIsAntialiasing = enabled;
151 if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
156 translate(-0.5, -0.5);
162 Sets the mode of the painter. This controls whether the painter shall adjust its
163 fixes/workarounds optimized for certain output devices.
165 void QCPPainter::setModes(QCPPainter::PainterModes modes)
171 Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a
172 device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5,
173 all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that
176 The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets
177 the render hint as appropriate.
179 \note this function hides the non-virtual base class implementation.
181 bool QCPPainter::begin(QPaintDevice *device)
183 bool result = QPainter::begin(device);
184 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
186 setRenderHint(QPainter::NonCosmeticDefaultPen);
193 Sets the mode of the painter. This controls whether the painter shall adjust its
194 fixes/workarounds optimized for certain output devices.
196 void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled)
198 if (!enabled && mModes.testFlag(mode))
200 else if (enabled && !mModes.testFlag(mode))
205 Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
206 QPainter, the save/restore functions are reimplemented to also save/restore those members.
208 \note this function hides the non-virtual base class implementation.
212 void QCPPainter::save()
214 mAntialiasingStack.push(mIsAntialiasing);
219 Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
220 QPainter, the save/restore functions are reimplemented to also save/restore those members.
222 \note this function hides the non-virtual base class implementation.
226 void QCPPainter::restore()
228 if (!mAntialiasingStack.isEmpty())
229 mIsAntialiasing = mAntialiasingStack.pop();
231 qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
236 Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen
237 overrides when the \ref pmNonCosmetic mode is set.
239 void QCPPainter::makeNonCosmetic()
241 if (qFuzzyIsNull(pen().widthF()))
250 ////////////////////////////////////////////////////////////////////////////////////////////////////
251 //////////////////// QCPScatterStyle
252 ////////////////////////////////////////////////////////////////////////////////////////////////////
254 /*! \class QCPScatterStyle
255 \brief Represents the visual appearance of scatter points
257 This class holds information about shape, color and size of scatter points. In plottables like
258 QCPGraph it is used to store how scatter points shall be drawn. For example, \ref
259 QCPGraph::setScatterStyle takes a QCPScatterStyle instance.
261 A scatter style consists of a shape (\ref setShape), a line color (\ref setPen) and possibly a
262 fill (\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can
263 be controlled with \ref setSize.
265 \section QCPScatterStyle-defining Specifying a scatter style
267 You can set all these configurations either by calling the respective functions on an instance:
269 QCPScatterStyle myScatter;
270 myScatter.setShape(QCPScatterStyle::ssCircle);
271 myScatter.setPen(Qt::blue);
272 myScatter.setBrush(Qt::white);
273 myScatter.setSize(5);
274 customPlot->graph(0)->setScatterStyle(myScatter);
277 Or you can use one of the various constructors that take different parameter combinations, making
278 it easy to specify a scatter style in a single call, like so:
280 customPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, Qt::white, 5));
283 \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable
285 There are two constructors which leave the pen undefined: \ref QCPScatterStyle() and \ref
286 QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \ref
287 isPenDefined will return false. It leads to scatter points that inherit the pen from the
288 plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line
289 color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes
290 it very convenient to set up typical scatter settings:
293 customPlot->graph(0)->setScatterStyle(QCPScatterStyle::ssPlus);
296 Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works
297 because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly
298 into a QCPScatterStyle instance (that's the \ref QCPScatterStyle(ScatterShape shape, double size)
299 constructor with a default for \a size). In those cases, C++ allows directly supplying a \ref
300 ScatterShape, where actually a QCPScatterStyle is expected.
302 \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps
304 QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points.
306 For custom shapes, you can provide a QPainterPath with the desired shape to the \ref
307 setCustomPath function or call the constructor that takes a painter path. The scatter shape will
308 automatically be set to \ref ssCustom.
310 For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively you can use the
311 constructor that takes a QPixmap. The scatter shape will automatically be set to \ref ssPixmap.
312 Note that \ref setSize does not influence the appearance of the pixmap.
315 /* start documentation of inline functions */
317 /*! \fn bool QCPScatterStyle::isNone() const
319 Returns whether the scatter shape is \ref ssNone.
324 /*! \fn bool QCPScatterStyle::isPenDefined() const
326 Returns whether a pen has been defined for this scatter style.
328 The pen is undefined if a constructor is called that does not carry \a pen as parameter. Those are
329 \ref QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). If the pen is
330 left undefined, the scatter color will be inherited from the plottable that uses this scatter
336 /* end documentation of inline functions */
339 Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined.
341 Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
342 from the plottable that uses this scatter style.
344 QCPScatterStyle::QCPScatterStyle() :
354 Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or
357 Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
358 from the plottable that uses this scatter style.
360 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) :
370 Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
371 and size to \a size. No brush is defined, i.e. the scatter point will not be filled.
373 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
383 Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
384 the brush color to \a fill (with a solid pattern), and size to \a size.
386 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
390 mBrush(QBrush(fill)),
396 Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set to \a pen, the
397 brush to \a brush, and size to \a size.
399 \warning In some cases it might be tempting to directly use a pen style like <tt>Qt::NoPen</tt> as \a pen
400 and a color like <tt>Qt::blue</tt> as \a brush. Notice however, that the corresponding call\n
401 <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n
402 doesn't necessarily lead C++ to use this constructor in some cases, but might mistake
403 <tt>Qt::NoPen</tt> for a QColor and use the
404 \ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size)
405 constructor instead (which will lead to an unexpected look of the scatter points). To prevent
406 this, be more explicit with the parameter types. For example, use <tt>QBrush(Qt::blue)</tt>
407 instead of just <tt>Qt::blue</tt>, to clearly point out to the compiler that this constructor is
410 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
415 mPenDefined(pen.style() != Qt::NoPen)
420 Creates a new QCPScatterStyle instance which will show the specified \a pixmap. The scatter shape
421 is set to \ref ssPixmap.
423 QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
434 Creates a new QCPScatterStyle instance with a custom shape that is defined via \a customPath. The
435 scatter shape is set to \ref ssCustom.
437 The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly
438 different meaning than for built-in scatter points: The custom path will be drawn scaled by a
439 factor of \a size/6.0. Since the default \a size is 6, the custom path will appear at a its
440 natural size by default. To double the size of the path for example, set \a size to 12.
442 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
447 mCustomPath(customPath),
448 mPenDefined(pen.style() != Qt::NoPen)
453 Sets the size (pixel diameter) of the drawn scatter points to \a size.
457 void QCPScatterStyle::setSize(double size)
463 Sets the shape to \a shape.
465 Note that the calls \ref setPixmap and \ref setCustomPath automatically set the shape to \ref
466 ssPixmap and \ref ssCustom, respectively.
470 void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)
476 Sets the pen that will be used to draw scatter points to \a pen.
478 If the pen was previously undefined (see \ref isPenDefined), the pen is considered defined after
479 a call to this function, even if \a pen is <tt>Qt::NoPen</tt>.
483 void QCPScatterStyle::setPen(const QPen &pen)
490 Sets the brush that will be used to fill scatter points to \a brush. Note that not all scatter
491 shapes have fillable areas. For example, \ref ssPlus does not while \ref ssCircle does.
495 void QCPScatterStyle::setBrush(const QBrush &brush)
501 Sets the pixmap that will be drawn as scatter point to \a pixmap.
503 Note that \ref setSize does not influence the appearance of the pixmap.
505 The scatter shape is automatically set to \ref ssPixmap.
507 void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
514 Sets the custom shape that will be drawn as scatter point to \a customPath.
516 The scatter shape is automatically set to \ref ssCustom.
518 void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
521 mCustomPath = customPath;
525 Applies the pen and the brush of this scatter style to \a painter. If this scatter style has an
526 undefined pen (\ref isPenDefined), sets the pen of \a painter to \a defaultPen instead.
528 This function is used by plottables (or any class that wants to draw scatters) just before a
529 number of scatters with this style shall be drawn with the \a painter.
533 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
535 painter->setPen(mPenDefined ? mPen : defaultPen);
536 painter->setBrush(mBrush);
540 Draws the scatter shape with \a painter at position \a pos.
542 This function does not modify the pen or the brush on the painter, as \ref applyTo is meant to be
543 called before scatter points are drawn with \ref drawShape.
547 void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
549 drawShape(painter, pos.x(), pos.y());
553 Draws the scatter shape with \a painter at position \a x and \a y.
555 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
557 double w = mSize/2.0;
563 painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
568 painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
569 painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
574 painter->drawLine(QLineF(x-w, y, x+w, y));
575 painter->drawLine(QLineF( x, y+w, x, y-w));
580 painter->drawEllipse(QPointF(x , y), w, w);
585 QBrush b = painter->brush();
586 painter->setBrush(painter->pen().color());
587 painter->drawEllipse(QPointF(x , y), w, w);
588 painter->setBrush(b);
593 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
598 painter->drawLine(QLineF(x-w, y, x, y-w));
599 painter->drawLine(QLineF( x, y-w, x+w, y));
600 painter->drawLine(QLineF(x+w, y, x, y+w));
601 painter->drawLine(QLineF( x, y+w, x-w, y));
606 painter->drawLine(QLineF(x-w, y, x+w, y));
607 painter->drawLine(QLineF( x, y+w, x, y-w));
608 painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
609 painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
614 painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
615 painter->drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
616 painter->drawLine(QLineF( x, y-0.977*w, x-w, y+0.755*w));
619 case ssTriangleInverted:
621 painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
622 painter->drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
623 painter->drawLine(QLineF( x, y+0.977*w, x-w, y-0.755*w));
628 painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
629 painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
630 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
635 painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
636 painter->drawLine(QLineF( x, y+w, x, y-w));
637 painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
642 painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
643 painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
644 painter->drawEllipse(QPointF(x, y), w, w);
649 painter->drawLine(QLineF(x-w, y, x+w, y));
650 painter->drawLine(QLineF( x, y+w, x, y-w));
651 painter->drawEllipse(QPointF(x, y), w, w);
656 painter->drawLine(QLineF(x, y-w, x, y+w));
657 painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
658 painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
659 painter->drawEllipse(QPointF(x, y), w, w);
664 painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
669 QTransform oldTransform = painter->transform();
670 painter->translate(x, y);
671 painter->scale(mSize/6.0, mSize/6.0);
672 painter->drawPath(mCustomPath);
673 painter->setTransform(oldTransform);
680 ////////////////////////////////////////////////////////////////////////////////////////////////////
681 //////////////////// QCPLayer
682 ////////////////////////////////////////////////////////////////////////////////////////////////////
685 \brief A layer that may contain objects, to control the rendering order
687 The Layering system of QCustomPlot is the mechanism to control the rendering order of the
688 elements inside the plot.
690 It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of
691 one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
692 QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers
693 bottom to top and successively draws the layerables of the layers.
695 A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base
696 class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
698 Initially, QCustomPlot has five layers: "background", "grid", "main", "axes" and "legend" (in
699 that order). The top two layers "axes" and "legend" contain the default axes and legend, so they
700 will be drawn on top. In the middle, there is the "main" layer. It is initially empty and set as
701 the current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
702 are created on this layer by default. Then comes the "grid" layer which contains the QCPGrid
703 instances (which belong tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background
704 shall be drawn behind everything else, thus the default QCPAxisRect instance is placed on the
705 "background" layer. Of course, the layer affiliation of the individual objects can be changed as
706 required (\ref QCPLayerable::setLayer).
708 Controlling the ordering of objects is easy: Create a new layer in the position you want it to
709 be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with
710 QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will
711 be placed on the new layer automatically, due to the current layer setting. Alternatively you
712 could have also ignored the current layer setting and just moved the objects with
713 QCPLayerable::setLayer to the desired layer after creating them.
715 It is also possible to move whole layers. For example, If you want the grid to be shown in front
716 of all plottables/items on the "main" layer, just move it above "main" with
717 QCustomPlot::moveLayer.
719 The rendering order within one layer is simply by order of creation or insertion. The item
720 created last (or added last to the layer), is drawn on top of all other objects on that layer.
722 When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
723 the deleted layer, see QCustomPlot::removeLayer.
726 /* start documentation of inline functions */
728 /*! \fn QList<QCPLayerable*> QCPLayer::children() const
730 Returns a list of all layerables on this layer. The order corresponds to the rendering order:
731 layerables with higher indices are drawn above layerables with lower indices.
734 /*! \fn int QCPLayer::index() const
736 Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
737 accessed via \ref QCustomPlot::layer.
739 Layers with higher indices will be drawn above layers with lower indices.
742 /* end documentation of inline functions */
745 Creates a new QCPLayer instance.
747 Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead.
749 \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot.
750 This check is only performed by \ref QCustomPlot::addLayer.
752 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
754 mParentPlot(parentPlot),
756 mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
759 // Note: no need to make sure layerName is unique, because layer
760 // management is done with QCustomPlot functions.
763 QCPLayer::~QCPLayer()
765 // If child layerables are still on this layer, detach them, so they don't try to reach back to this
766 // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
767 // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
768 // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
770 while (!mChildren.isEmpty())
771 mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
773 if (mParentPlot->currentLayer() == this)
774 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.";
778 Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
779 layer will be invisible.
781 This function doesn't change the visibility property of the layerables (\ref
782 QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
783 visibility of the parent layer into account.
785 void QCPLayer::setVisible(bool visible)
792 Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
793 be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
795 This function does not change the \a mLayer member of \a layerable to this layer. (Use
796 QCPLayerable::setLayer to change the layer of an object, not this function.)
800 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
802 if (!mChildren.contains(layerable))
805 mChildren.prepend(layerable);
807 mChildren.append(layerable);
809 qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
814 Removes the \a layerable from the list of this layer.
816 This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
817 to change the layer of an object, not this function.)
821 void QCPLayer::removeChild(QCPLayerable *layerable)
823 if (!mChildren.removeOne(layerable))
824 qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
828 ////////////////////////////////////////////////////////////////////////////////////////////////////
829 //////////////////// QCPLayerable
830 ////////////////////////////////////////////////////////////////////////////////////////////////////
832 /*! \class QCPLayerable
833 \brief Base class for all drawable objects
835 This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
838 Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
839 the layers accordingly.
841 For details about the layering mechanism, see the QCPLayer documentation.
844 /* start documentation of inline functions */
846 /*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
848 Returns the parent layerable of this layerable. The parent layerable is used to provide
849 visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables
850 only get drawn if their parent layerables are visible, too.
852 Note that a parent layerable is not necessarily also the QObject parent for memory management.
853 Further, a layerable doesn't always have a parent layerable, so this function may return 0.
855 A parent layerable is set implicitly with when placed inside layout elements and doesn't need to be
856 set manually by the user.
859 /* end documentation of inline functions */
860 /* start documentation of pure virtual functions */
862 /*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
865 This function applies the default antialiasing setting to the specified \a painter, using the
866 function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when
867 \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing
868 setting may be specified individually, this function should set the antialiasing state of the
869 most prominent entity. In this case however, the \ref draw function usually calls the specialized
870 versions of this function before drawing each entity, effectively overriding the setting of the
871 default antialiasing hint.
873 <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
874 line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased,
875 QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't
876 only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
877 antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
878 QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
879 calls the respective specialized applyAntialiasingHint function.
881 <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
882 setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
883 all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
884 respective layerable subclass.) Consequently it only has the normal
885 QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
886 care about setting any antialiasing states, because the default antialiasing hint is already set
887 on the painter when the \ref draw function is called, and that's the state it wants to draw the
891 /*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
894 This function draws the layerable with the specified \a painter. It is only called by
895 QCustomPlot, if the layerable is visible (\ref setVisible).
897 Before this function is called, the painter's antialiasing state is set via \ref
898 applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was
899 set to \ref clipRect.
902 /* end documentation of pure virtual functions */
903 /* start documentation of signals */
905 /*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
907 This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
913 /* end documentation of signals */
916 Creates a new QCPLayerable instance.
918 Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
921 If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a
922 targetLayer is an empty string, it places itself on the current layer of the plot (see \ref
923 QCustomPlot::setCurrentLayer).
925 It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later
926 time with \ref initializeParentPlot.
928 The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable parents
929 are mainly used to control visibility in a hierarchy of layerables. This means a layerable is
930 only drawn, if all its ancestor layerables are also visible. Note that \a parentLayerable does
931 not become the QObject-parent (for memory management) of this layerable, \a plot does.
933 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
937 mParentLayerable(parentLayerable),
943 if (targetLayer.isEmpty())
944 setLayer(mParentPlot->currentLayer());
945 else if (!setLayer(targetLayer))
946 qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
950 QCPLayerable::~QCPLayerable()
954 mLayer->removeChild(this);
960 Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
961 on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not
964 void QCPLayerable::setVisible(bool on)
970 Sets the \a layer of this layerable object. The object will be placed on top of the other objects
973 Returns true on success, i.e. if \a layer is a valid layer.
975 bool QCPLayerable::setLayer(QCPLayer *layer)
977 return moveToLayer(layer, false);
981 Sets the layer of this layerable object by name
983 Returns true on success, i.e. if \a layerName is a valid layer name.
985 bool QCPLayerable::setLayer(const QString &layerName)
989 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
992 if (QCPLayer *layer = mParentPlot->layer(layerName))
994 return setLayer(layer);
997 qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
1003 Sets whether this object will be drawn antialiased or not.
1005 Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
1006 QCustomPlot::setNotAntialiasedElements.
1008 void QCPLayerable::setAntialiased(bool enabled)
1010 mAntialiased = enabled;
1014 Returns whether this layerable is visible, taking the visibility of the layerable parent and the
1015 visibility of the layer this layerable is on into account. This is the method that is consulted
1016 to decide whether a layerable shall be drawn or not.
1018 If this layerable has a direct layerable parent (usually set via hierarchies implemented in
1019 subclasses, like in the case of QCPLayoutElement), this function returns true only if this
1020 layerable has its visibility set to true and the parent layerable's \ref realVisibility returns
1023 If this layerable doesn't have a direct layerable parent, returns the state of this layerable's
1026 bool QCPLayerable::realVisibility() const
1028 return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1032 This function is used to decide whether a click hits a layerable object or not.
1034 \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
1035 shortest pixel distance of this point to the object. If the object is either invisible or the
1036 distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the
1037 object is not selectable, -1.0 is returned, too.
1039 If the item is represented not by single lines but by an area like QCPItemRect or QCPItemText, a
1040 click inside the area returns a constant value greater zero (typically the selectionTolerance of
1041 the parent QCustomPlot multiplied by 0.99). If the click lies outside the area, this function
1044 Providing a constant value for area objects allows selecting line objects even when they are
1045 obscured by such area objects, by clicking close to the lines (i.e. closer than
1046 0.99*selectionTolerance).
1048 The actual setting of the selection state is not done by this function. This is handled by the
1049 parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified
1050 via the selectEvent/deselectEvent methods.
1052 \a details is an optional output parameter. Every layerable subclass may place any information
1053 in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot
1054 decides on the basis of this selectTest call, that the object was successfully selected. The
1055 subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part
1056 objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked
1057 is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be
1058 placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
1059 selected doesn't have to be done a second time for a single selection operation.
1061 You may pass 0 as \a details to indicate that you are not interested in those selection details.
1063 \see selectEvent, deselectEvent, QCustomPlot::setInteractions
1065 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1068 Q_UNUSED(onlySelectable)
1075 Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
1076 passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to
1079 Note that, unlike when passing a non-null parent plot in the constructor, this function does not
1080 make \a parentPlot the QObject-parent of this layerable. If you want this, call
1081 QObject::setParent(\a parentPlot) in addition to this function.
1083 Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
1084 make the layerable appear on the QCustomPlot.
1086 The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized
1087 so they can react accordingly (e.g. also initialize the parent plot of child layerables, like
1090 void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)
1094 qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1099 qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1101 mParentPlot = parentPlot;
1102 parentPlotInitialized(mParentPlot);
1107 Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not
1108 become the QObject-parent (for memory management) of this layerable.
1110 The parent layerable has influence on the return value of the \ref realVisibility method. Only
1111 layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be
1116 void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)
1118 mParentLayerable = parentLayerable;
1123 Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
1124 the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
1125 false, the object will be appended.
1127 Returns true on success, i.e. if \a layer is a valid layer.
1129 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
1131 if (layer && !mParentPlot)
1133 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1136 if (layer && layer->parentPlot() != mParentPlot)
1138 qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1142 QCPLayer *oldLayer = mLayer;
1144 mLayer->removeChild(this);
1147 mLayer->addChild(this, prepend);
1148 if (mLayer != oldLayer)
1149 emit layerChanged(mLayer);
1155 Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a
1156 localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref
1157 QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
1158 controlled via \a overrideElement.
1160 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1162 if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1163 painter->setAntialiasing(false);
1164 else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1165 painter->setAntialiasing(true);
1167 painter->setAntialiasing(localAntialiased);
1172 This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
1173 of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the
1174 parent plot is set at a later time.
1176 For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
1177 QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
1178 element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To
1179 propagate the parent plot to all the children of the hierarchy, the top level element then uses
1180 this function to pass the parent plot on to its child elements.
1182 The default implementation does nothing.
1184 \see initializeParentPlot
1186 void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)
1188 Q_UNUSED(parentPlot)
1193 Returns the selection category this layerable shall belong to. The selection category is used in
1194 conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and
1197 Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref
1198 QCP::iSelectOther. This is what the default implementation returns.
1200 \see QCustomPlot::setInteractions
1202 QCP::Interaction QCPLayerable::selectionCategory() const
1204 return QCP::iSelectOther;
1209 Returns the clipping rectangle of this layerable object. By default, this is the viewport of the
1210 parent QCustomPlot. Specific subclasses may reimplement this function to provide different
1213 The returned clipping rect is set on the painter before the draw function of the respective
1216 QRect QCPLayerable::clipRect() const
1219 return mParentPlot->viewport();
1226 This event is called when the layerable shall be selected, as a consequence of a click by the
1227 user. Subclasses should react to it by setting their selection state appropriately. The default
1228 implementation does nothing.
1230 \a event is the mouse event that caused the selection. \a additive indicates, whether the user
1231 was holding the multi-select-modifier while performing the selection (see \ref
1232 QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled
1233 (i.e. become selected when unselected and unselected when selected).
1235 Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e.
1236 returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).
1237 The \a details data you output from \ref selectTest is fed back via \a details here. You may
1238 use it to transport any kind of information from the selectTest to the possibly subsequent
1239 selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable
1240 that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need
1241 to do the calculation again to find out which part was actually clicked.
1243 \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must
1244 set the value either to true or false, depending on whether the selection state of this layerable
1245 was actually changed. For layerables that only are selectable as a whole and not in parts, this
1246 is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the
1247 selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the
1248 layerable was previously unselected and now is switched to the selected state.
1250 \see selectTest, deselectEvent
1252 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1257 Q_UNUSED(selectionStateChanged)
1262 This event is called when the layerable shall be deselected, either as consequence of a user
1263 interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by
1264 unsetting their selection appropriately.
1266 just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must
1267 return true or false when the selection state of this layerable has changed or not changed,
1270 \see selectTest, selectEvent
1272 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1274 Q_UNUSED(selectionStateChanged)
1278 ////////////////////////////////////////////////////////////////////////////////////////////////////
1279 //////////////////// QCPRange
1280 ////////////////////////////////////////////////////////////////////////////////////////////////////
1282 \brief Represents the range an axis is encompassing.
1284 contains a \a lower and \a upper double value and provides convenience input, output and
1285 modification functions.
1287 \see QCPAxis::setRange
1291 Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
1292 intervals would cause errors due to the 11-bit exponent of double precision numbers,
1293 corresponding to a minimum magnitude of roughly 1e-308.
1294 \see validRange, maxRange
1296 const double QCPRange::minRange = 1e-280;
1299 Maximum values (negative and positive) the range will accept in range-changing functions.
1300 Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
1301 corresponding to a maximum magnitude of roughly 1e308.
1302 Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
1304 \see validRange, minRange
1306 const double QCPRange::maxRange = 1e250;
1309 Constructs a range with \a lower and \a upper set to zero.
1311 QCPRange::QCPRange() :
1318 Constructs a range with the specified \a lower and \a upper values.
1320 QCPRange::QCPRange(double lower, double upper) :
1328 Returns the size of the range, i.e. \a upper-\a lower
1330 double QCPRange::size() const
1336 Returns the center of the range, i.e. (\a upper+\a lower)*0.5
1338 double QCPRange::center() const
1340 return (upper+lower)*0.5;
1344 Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
1347 void QCPRange::normalize()
1350 qSwap(lower, upper);
1354 Expands this range such that \a otherRange is contained in the new range. It is assumed that both
1355 this range and \a otherRange are normalized (see \ref normalize).
1357 If \a otherRange is already inside the current range, this function does nothing.
1361 void QCPRange::expand(const QCPRange &otherRange)
1363 if (lower > otherRange.lower)
1364 lower = otherRange.lower;
1365 if (upper < otherRange.upper)
1366 upper = otherRange.upper;
1371 Returns an expanded range that contains this and \a otherRange. It is assumed that both this
1372 range and \a otherRange are normalized (see \ref normalize).
1376 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1378 QCPRange result = *this;
1379 result.expand(otherRange);
1384 Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
1385 the range won't span the positive and negative sign domain, i.e. contain zero. Further
1386 \a lower will always be numerically smaller (or equal) to \a upper.
1388 If the original range does span positive and negative sign domains or contains zero,
1389 the returned range will try to approximate the original range as good as possible.
1390 If the positive interval of the original range is wider than the negative interval, the
1391 returned range will only contain the positive interval, with lower bound set to \a rangeFac or
1392 \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
1393 is wider than the positive interval, this time by changing the \a upper bound.
1395 QCPRange QCPRange::sanitizedForLogScale() const
1397 double rangeFac = 1e-3;
1398 QCPRange sanitizedRange(lower, upper);
1399 sanitizedRange.normalize();
1400 // can't have range spanning negative and positive values in log plot, so change range to fix it
1401 //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
1402 if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
1405 if (rangeFac < sanitizedRange.upper*rangeFac)
1406 sanitizedRange.lower = rangeFac;
1408 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1409 } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
1410 else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
1413 if (-rangeFac > sanitizedRange.lower*rangeFac)
1414 sanitizedRange.upper = -rangeFac;
1416 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1417 } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
1419 // find out whether negative or positive interval is wider to decide which sign domain will be chosen
1420 if (-sanitizedRange.lower > sanitizedRange.upper)
1422 // negative is wider, do same as in case upper is 0
1423 if (-rangeFac > sanitizedRange.lower*rangeFac)
1424 sanitizedRange.upper = -rangeFac;
1426 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
1429 // positive is wider, do same as in case lower is 0
1430 if (rangeFac < sanitizedRange.upper*rangeFac)
1431 sanitizedRange.lower = rangeFac;
1433 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
1436 // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
1437 return sanitizedRange;
1441 Returns a sanitized version of the range. Sanitized means for linear scales, that
1442 \a lower will always be numerically smaller (or equal) to \a upper.
1444 QCPRange QCPRange::sanitizedForLinScale() const
1446 QCPRange sanitizedRange(lower, upper);
1447 sanitizedRange.normalize();
1448 return sanitizedRange;
1452 Returns true when \a value lies within or exactly on the borders of the range.
1454 bool QCPRange::contains(double value) const
1456 return value >= lower && value <= upper;
1460 Checks, whether the specified range is within valid bounds, which are defined
1461 as QCPRange::maxRange and QCPRange::minRange.
1462 A valid range means:
1463 \li range bounds within -maxRange and maxRange
1464 \li range size above minRange
1465 \li range size below maxRange
1467 bool QCPRange::validRange(double lower, double upper)
1470 return (lower > -maxRange &&
1472 qAbs(lower-upper) > minRange &&
1473 (lower < -minRange || lower > minRange) &&
1474 (upper < -minRange || upper > minRange));
1476 return (lower > -maxRange &&
1478 qAbs(lower-upper) > minRange &&
1479 qAbs(lower-upper) < maxRange);
1484 Checks, whether the specified range is within valid bounds, which are defined
1485 as QCPRange::maxRange and QCPRange::minRange.
1486 A valid range means:
1487 \li range bounds within -maxRange and maxRange
1488 \li range size above minRange
1489 \li range size below maxRange
1491 bool QCPRange::validRange(const QCPRange &range)
1494 return (range.lower > -maxRange &&
1495 range.upper < maxRange &&
1496 qAbs(range.lower-range.upper) > minRange &&
1497 qAbs(range.lower-range.upper) < maxRange &&
1498 (range.lower < -minRange || range.lower > minRange) &&
1499 (range.upper < -minRange || range.upper > minRange));
1501 return (range.lower > -maxRange &&
1502 range.upper < maxRange &&
1503 qAbs(range.lower-range.upper) > minRange &&
1504 qAbs(range.lower-range.upper) < maxRange);
1508 ////////////////////////////////////////////////////////////////////////////////////////////////////
1509 //////////////////// QCPMarginGroup
1510 ////////////////////////////////////////////////////////////////////////////////////////////////////
1512 /*! \class QCPMarginGroup
1513 \brief A margin group allows synchronization of margin sides if working with multiple layout elements.
1515 QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that
1516 they will all have the same size, based on the largest required margin in the group.
1519 \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
1522 In certain situations it is desirable that margins at specific sides are synchronized across
1523 layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will
1524 provide a cleaner look to the user if the left and right margins of the two axis rects are of the
1525 same size. The left axis of the top axis rect will then be at the same horizontal position as the
1526 left axis of the lower axis rect, making them appear aligned. The same applies for the right
1527 axes. This is what QCPMarginGroup makes possible.
1529 To add/remove a specific side of a layout element to/from a margin group, use the \ref
1530 QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call
1531 \ref clear, or just delete the margin group.
1533 \section QCPMarginGroup-example Example
1535 First create a margin group:
1537 QCPMarginGroup *group = new QCPMarginGroup(customPlot);
1539 Then set this group on the layout element sides:
1541 customPlot->axisRect(0)->setMarginGroup(QCP::msLeft|QCP::msRight, group);
1542 customPlot->axisRect(1)->setMarginGroup(QCP::msLeft|QCP::msRight, group);
1544 Here, we've used the first two axis rects of the plot and synchronized their left margins with
1545 each other and their right margins with each other.
1548 /* start documentation of inline functions */
1550 /*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const
1552 Returns a list of all layout elements that have their margin \a side associated with this margin
1556 /* end documentation of inline functions */
1559 Creates a new QCPMarginGroup instance in \a parentPlot.
1561 QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :
1562 QObject(parentPlot),
1563 mParentPlot(parentPlot)
1565 mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
1566 mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
1567 mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
1568 mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
1571 QCPMarginGroup::~QCPMarginGroup()
1577 Returns whether this margin group is empty. If this function returns true, no layout elements use
1578 this margin group to synchronize margin sides.
1580 bool QCPMarginGroup::isEmpty() const
1582 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1583 while (it.hasNext())
1586 if (!it.value().isEmpty())
1593 Clears this margin group. The synchronization of the margin sides that use this margin group is
1594 lifted and they will use their individual margin sizes again.
1596 void QCPMarginGroup::clear()
1598 // make all children remove themselves from this margin group:
1599 QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
1600 while (it.hasNext())
1603 const QList<QCPLayoutElement*> elements = it.value();
1604 for (int i=elements.size()-1; i>=0; --i)
1605 elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
1611 Returns the synchronized common margin for \a side. This is the margin value that will be used by
1612 the layout element on the respective side, if it is part of this margin group.
1614 The common margin is calculated by requesting the automatic margin (\ref
1615 QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin
1616 group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into
1619 int QCPMarginGroup::commonMargin(QCP::MarginSide side) const
1621 // query all automatic margins of the layout elements in this margin group side and find maximum:
1623 const QList<QCPLayoutElement*> elements = mChildren.value(side);
1624 for (int i=0; i<elements.size(); ++i)
1626 if (!elements.at(i)->autoMargins().testFlag(side))
1628 int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
1637 Adds \a element to the internal list of child elements, for the margin \a side.
1639 This function does not modify the margin group property of \a element.
1641 void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)
1643 if (!mChildren[side].contains(element))
1644 mChildren[side].append(element);
1646 qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
1651 Removes \a element from the internal list of child elements, for the margin \a side.
1653 This function does not modify the margin group property of \a element.
1655 void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)
1657 if (!mChildren[side].removeOne(element))
1658 qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
1662 ////////////////////////////////////////////////////////////////////////////////////////////////////
1663 //////////////////// QCPLayoutElement
1664 ////////////////////////////////////////////////////////////////////////////////////////////////////
1666 /*! \class QCPLayoutElement
1667 \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system".
1669 This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.
1671 A Layout element is a rectangular object which can be placed in layouts. It has an outer rect
1672 (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference
1673 between outer and inner rect is called its margin. The margin can either be set to automatic or
1674 manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be
1675 set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic,
1676 the layout element subclass will control the value itself (via \ref calculateAutoMargin).
1678 Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level
1679 layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref
1680 QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested.
1682 Thus in QCustomPlot one can divide layout elements into two categories: The ones that are
1683 invisible by themselves, because they don't draw anything. Their only purpose is to manage the
1684 position and size of other layout elements. This category of layout elements usually use
1685 QCPLayout as base class. Then there is the category of layout elements which actually draw
1686 something. For example, QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does
1687 not necessarily mean that the latter category can't have child layout elements. QCPLegend for
1688 instance, actually derives from QCPLayoutGrid and the individual legend items are child layout
1689 elements in the grid layout.
1692 /* start documentation of inline functions */
1694 /*! \fn QCPLayout *QCPLayoutElement::layout() const
1696 Returns the parent layout of this layout element.
1699 /*! \fn QRect QCPLayoutElement::rect() const
1701 Returns the inner rect of this layout element. The inner rect is the outer rect (\ref
1702 setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins).
1704 In some cases, the area between outer and inner rect is left blank. In other cases the margin
1705 area is used to display peripheral graphics while the main content is in the inner rect. This is
1706 where automatic margin calculation becomes interesting because it allows the layout element to
1707 adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
1708 draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if
1709 \ref setAutoMargins is enabled) according to the space required by the labels of the axes.
1712 /*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
1714 This event is called, if the mouse was pressed while being inside the outer rect of this layout
1718 /*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
1720 This event is called, if the mouse is moved inside the outer rect of this layout element.
1723 /*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
1725 This event is called, if the mouse was previously pressed inside the outer rect of this layout
1726 element and is now released.
1729 /*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
1731 This event is called, if the mouse is double-clicked inside the outer rect of this layout
1735 /*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
1737 This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this
1741 /* end documentation of inline functions */
1744 Creates an instance of QCPLayoutElement and sets default values.
1746 QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
1747 QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
1750 mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
1752 mOuterRect(0, 0, 0, 0),
1753 mMargins(0, 0, 0, 0),
1754 mMinimumMargins(0, 0, 0, 0),
1755 mAutoMargins(QCP::msAll)
1759 QCPLayoutElement::~QCPLayoutElement()
1761 setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
1762 // unregister at layout:
1763 if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
1764 mParentLayout->take(this);
1768 Sets the outer rect of this layout element. If the layout element is inside a layout, the layout
1769 sets the position and size of this layout element using this function.
1771 Calling this function externally has no effect, since the layout will overwrite any changes to
1772 the outer rect upon the next replot.
1774 The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect.
1778 void QCPLayoutElement::setOuterRect(const QRect &rect)
1780 if (mOuterRect != rect)
1783 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1788 Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all
1789 sides, this function is used to manually set the margin on those sides. Sides that are still set
1790 to be handled automatically are ignored and may have any value in \a margins.
1792 The margin is the distance between the outer rect (controlled by the parent layout via \ref
1793 setOuterRect) and the inner \ref rect (which usually contains the main content of this layout
1798 void QCPLayoutElement::setMargins(const QMargins &margins)
1800 if (mMargins != margins)
1803 mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
1808 If \ref setAutoMargins is enabled on some or all margins, this function is used to provide
1809 minimum values for those margins.
1811 The minimum values are not enforced on margin sides that were set to be under manual control via
1812 \ref setAutoMargins.
1816 void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
1818 if (mMinimumMargins != margins)
1820 mMinimumMargins = margins;
1825 Sets on which sides the margin shall be calculated automatically. If a side is calculated
1826 automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is
1827 set to be controlled manually, the value may be specified with \ref setMargins.
1829 Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref
1830 setMarginGroup), to synchronize (align) it with other layout elements in the plot.
1832 \see setMinimumMargins, setMargins
1834 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
1836 mAutoMargins = sides;
1840 Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to
1841 respect the \a size here by changing row/column sizes in the layout accordingly.
1843 If the parent layout size is not sufficient to satisfy all minimum size constraints of its child
1844 layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot
1845 propagates the layout's size constraints to the outside by setting its own minimum QWidget size
1846 accordingly, so violations of \a size should be exceptions.
1848 void QCPLayoutElement::setMinimumSize(const QSize &size)
1850 if (mMinimumSize != size)
1852 mMinimumSize = size;
1854 mParentLayout->sizeConstraintsChanged();
1860 Sets the minimum size for the inner \ref rect of this layout element.
1862 void QCPLayoutElement::setMinimumSize(int width, int height)
1864 setMinimumSize(QSize(width, height));
1868 Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to
1869 respect the \a size here by changing row/column sizes in the layout accordingly.
1871 void QCPLayoutElement::setMaximumSize(const QSize &size)
1873 if (mMaximumSize != size)
1875 mMaximumSize = size;
1877 mParentLayout->sizeConstraintsChanged();
1883 Sets the maximum size for the inner \ref rect of this layout element.
1885 void QCPLayoutElement::setMaximumSize(int width, int height)
1887 setMaximumSize(QSize(width, height));
1891 Sets the margin \a group of the specified margin \a sides.
1893 Margin groups allow synchronizing specified margins across layout elements, see the documentation
1894 of \ref QCPMarginGroup.
1896 To unset the margin group of \a sides, set \a group to 0.
1898 Note that margin groups only work for margin sides that are set to automatic (\ref
1901 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
1903 QVector<QCP::MarginSide> sideVector;
1904 if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
1905 if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
1906 if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
1907 if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
1909 for (int i=0; i<sideVector.size(); ++i)
1911 QCP::MarginSide side = sideVector.at(i);
1912 if (marginGroup(side) != group)
1914 QCPMarginGroup *oldGroup = marginGroup(side);
1915 if (oldGroup) // unregister at old group
1916 oldGroup->removeChild(side, this);
1918 if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
1920 mMarginGroups.remove(side);
1921 } else // setting to a new group
1923 mMarginGroups[side] = group;
1924 group->addChild(side, this);
1931 Updates the layout element and sub-elements. This function is automatically called before every
1932 replot by the parent layout element. It is called multiple times, once for every \ref
1933 UpdatePhase. The phases are run through in the order of the enum values. For details about what
1934 happens at the different phases, see the documentation of \ref UpdatePhase.
1936 Layout elements that have child elements should call the \ref update method of their child
1937 elements, and pass the current \a phase unchanged.
1939 The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
1940 Subclasses should make sure to call the base class implementation.
1942 void QCPLayoutElement::update(UpdatePhase phase)
1944 if (phase == upMargins)
1946 if (mAutoMargins != QCP::msNone)
1948 // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
1949 QMargins newMargins = mMargins;
1950 foreach (QCP::MarginSide side, QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom)
1952 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
1954 if (mMarginGroups.contains(side))
1955 QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
1957 QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
1958 // apply minimum margin restrictions:
1959 if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
1960 QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
1963 setMargins(newMargins);
1969 Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
1971 if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this
1972 function to determine the minimum allowed size of this layout element. (A manual minimum size is
1973 considered set if it is non-zero.)
1975 QSize QCPLayoutElement::minimumSizeHint() const
1977 return mMinimumSize;
1981 Returns the maximum size this layout element (the inner \ref rect) may be expanded to.
1983 if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this
1984 function to determine the maximum allowed size of this layout element. (A manual maximum size is
1985 considered set if it is smaller than Qt's QWIDGETSIZE_MAX.)
1987 QSize QCPLayoutElement::maximumSizeHint() const
1989 return mMaximumSize;
1993 Returns a list of all child elements in this layout element. If \a recursive is true, all
1994 sub-child elements are included in the list, too.
1996 \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
1997 empty cells which yield 0 at the respective index.)
1999 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
2002 return QList<QCPLayoutElement*>();
2006 Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer
2007 rect, this method returns a value corresponding to 0.99 times the parent plot's selection
2008 tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is
2009 true, -1.0 is returned.
2011 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
2013 QCPLayoutElement subclasses may reimplement this method to provide more specific selection test
2016 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
2023 if (QRectF(mOuterRect).contains(pos))
2026 return mParentPlot->selectionTolerance()*0.99;
2029 qDebug() << Q_FUNC_INFO << "parent plot not defined";
2038 propagates the parent plot initialization to all child elements, by calling \ref
2039 QCPLayerable::initializeParentPlot on them.
2041 void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
2043 foreach (QCPLayoutElement* el, elements(false))
2045 if (!el->parentPlot())
2046 el->initializeParentPlot(parentPlot);
2052 Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
2053 side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
2054 returned value will not be smaller than the specified minimum margin.
2056 The default implementation just returns the respective manual margin (\ref setMargins) or the
2057 minimum margin, whichever is larger.
2059 int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)
2061 return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
2064 ////////////////////////////////////////////////////////////////////////////////////////////////////
2065 //////////////////// QCPLayout
2066 ////////////////////////////////////////////////////////////////////////////////////////////////////
2068 /*! \class QCPLayout
2069 \brief The abstract base class for layouts
2071 This is an abstract base class for layout elements whose main purpose is to define the position
2072 and size of other child layout elements. In most cases, layouts don't draw anything themselves
2073 (but there are exceptions to this, e.g. QCPLegend).
2075 QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.
2077 QCPLayout introduces a common interface for accessing and manipulating the child elements. Those
2078 functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref
2079 simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions
2080 to this interface which are more specialized to the form of the layout. For example, \ref
2081 QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid
2084 Since this is an abstract base class, you can't instantiate it directly. Rather use one of its
2085 subclasses like QCPLayoutGrid or QCPLayoutInset.
2087 For a general introduction to the layout system, see the dedicated documentation page \ref
2088 thelayoutsystem "The Layout System".
2091 /* start documentation of pure virtual functions */
2093 /*! \fn virtual int QCPLayout::elementCount() const = 0
2095 Returns the number of elements/cells in the layout.
2097 \see elements, elementAt
2100 /*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
2102 Returns the element in the cell with the given \a index. If \a index is invalid, returns 0.
2104 Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
2105 QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check
2106 whether a cell is empty or not.
2108 \see elements, elementCount, takeAt
2111 /*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
2113 Removes the element with the given \a index from the layout and returns it.
2115 If the \a index is invalid or the cell with that index is empty, returns 0.
2117 Note that some layouts don't remove the respective cell right away but leave an empty cell after
2118 successful removal of the layout element. To collapse empty cells, use \ref simplify.
2120 \see elementAt, take
2123 /*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
2125 Removes the specified \a element from the layout and returns true on success.
2127 If the \a element isn't in this layout, returns false.
2129 Note that some layouts don't remove the respective cell right away but leave an empty cell after
2130 successful removal of the layout element. To collapse empty cells, use \ref simplify.
2135 /* end documentation of pure virtual functions */
2138 Creates an instance of QCPLayout and sets default values. Note that since QCPLayout
2139 is an abstract base class, it can't be instantiated directly.
2141 QCPLayout::QCPLayout()
2146 First calls the QCPLayoutElement::update base class implementation to update the margins on this
2149 Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells.
2151 Finally, \ref update is called on all child elements.
2153 void QCPLayout::update(UpdatePhase phase)
2155 QCPLayoutElement::update(phase);
2157 // set child element rects according to layout:
2158 if (phase == upLayout)
2161 // propagate update call to child elements:
2162 const int elCount = elementCount();
2163 for (int i=0; i<elCount; ++i)
2165 if (QCPLayoutElement *el = elementAt(i))
2170 /* inherits documentation from base class */
2171 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
2173 const int c = elementCount();
2174 QList<QCPLayoutElement*> result;
2175 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2178 for (int i=0; i<c; ++i)
2179 result.append(elementAt(i));
2182 for (int i=0; i<c; ++i)
2185 result << result.at(i)->elements(recursive);
2192 Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the
2193 default implementation does nothing.
2195 Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit
2196 simplification while QCPLayoutGrid does.
2198 void QCPLayout::simplify()
2203 Removes and deletes the element at the provided \a index. Returns true on success. If \a index is
2204 invalid or points to an empty cell, returns false.
2206 This function internally uses \ref takeAt to remove the element from the layout and then deletes
2207 the returned element.
2211 bool QCPLayout::removeAt(int index)
2213 if (QCPLayoutElement *el = takeAt(index))
2222 Removes and deletes the provided \a element. Returns true on success. If \a element is not in the
2223 layout, returns false.
2225 This function internally uses \ref takeAt to remove the element from the layout and then deletes
2230 bool QCPLayout::remove(QCPLayoutElement *element)
2241 Removes and deletes all layout elements in this layout.
2243 \see remove, removeAt
2245 void QCPLayout::clear()
2247 for (int i=elementCount()-1; i>=0; --i)
2256 Subclasses call this method to report changed (minimum/maximum) size constraints.
2258 If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref
2259 sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of
2260 QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,
2261 it may update itself and resize cells accordingly.
2263 void QCPLayout::sizeConstraintsChanged() const
2265 if (QWidget *w = qobject_cast<QWidget*>(parent()))
2266 w->updateGeometry();
2267 else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
2268 l->sizeConstraintsChanged();
2273 Subclasses reimplement this method to update the position and sizes of the child elements/cells
2274 via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing.
2276 The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay
2279 \ref getSectionSizes may help with the reimplementation of this function.
2283 void QCPLayout::updateLayout()
2290 Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the
2291 \ref QCPLayerable::parentLayerable and the QObject parent to this layout.
2293 Further, if \a el didn't previously have a parent plot, calls \ref
2294 QCPLayerable::initializeParentPlot on \a el to set the paret plot.
2296 This method is used by subclass specific methods that add elements to the layout. Note that this
2297 method only changes properties in \a el. The removal from the old layout and the insertion into
2298 the new layout must be done additionally.
2300 void QCPLayout::adoptElement(QCPLayoutElement *el)
2304 el->mParentLayout = this;
2305 el->setParentLayerable(this);
2306 el->setParent(this);
2307 if (!el->parentPlot())
2308 el->initializeParentPlot(mParentPlot);
2310 qDebug() << Q_FUNC_INFO << "Null element passed";
2315 Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout
2316 and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent
2319 This method is used by subclass specific methods that remove elements from the layout (e.g. \ref
2320 take or \ref takeAt). Note that this method only changes properties in \a el. The removal from
2321 the old layout must be done additionally.
2323 void QCPLayout::releaseElement(QCPLayoutElement *el)
2327 el->mParentLayout = 0;
2328 el->setParentLayerable(0);
2329 el->setParent(mParentPlot);
2330 // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
2332 qDebug() << Q_FUNC_INFO << "Null element passed";
2337 This is a helper function for the implementation of \ref updateLayout in subclasses.
2339 It calculates the sizes of one-dimensional sections with provided constraints on maximum section
2340 sizes, minimum section sizes, relative stretch factors and the final total size of all sections.
2342 The QVector entries refer to the sections. Thus all QVectors must have the same size.
2344 \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size
2345 imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
2347 \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size
2348 imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than
2349 \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,
2350 not exceeding the allowed total size is taken to be more important than not going below minimum
2353 \a stretchFactors give the relative proportions of the sections to each other. If all sections
2354 shall be scaled equally, set all values equal. If the first section shall be double the size of
2355 each individual other section, set the first number of \a stretchFactors to double the value of
2356 the other individual values (e.g. {2, 1, 1, 1}).
2358 \a totalSize is the value that the final section sizes will add up to. Due to rounding, the
2359 actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,
2360 you could distribute the remaining difference on the sections.
2362 The return value is a QVector containing the section sizes.
2364 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
2366 if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
2368 qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
2369 return QVector<int>();
2371 if (stretchFactors.isEmpty())
2372 return QVector<int>();
2373 int sectionCount = stretchFactors.size();
2374 QVector<double> sectionSizes(sectionCount);
2375 // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
2377 for (int i=0; i<sectionCount; ++i)
2378 minSizeSum += minSizes.at(i);
2379 if (totalSize < minSizeSum)
2381 // new stretch factors are minimum sizes and minimum sizes are set to zero:
2382 for (int i=0; i<sectionCount; ++i)
2384 stretchFactors[i] = minSizes.at(i);
2389 QList<int> minimumLockedSections;
2390 QList<int> unfinishedSections;
2391 for (int i=0; i<sectionCount; ++i)
2392 unfinishedSections.append(i);
2393 double freeSize = totalSize;
2395 int outerIterations = 0;
2396 while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2399 int innerIterations = 0;
2400 while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
2403 // find section that hits its maximum next:
2405 double nextMax = 1e12;
2406 for (int i=0; i<unfinishedSections.size(); ++i)
2408 int secId = unfinishedSections.at(i);
2409 double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
2410 if (hitsMaxAt < nextMax)
2412 nextMax = hitsMaxAt;
2416 // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
2417 // actually hits its maximum, without exceeding the total size when we add up all sections)
2418 double stretchFactorSum = 0;
2419 for (int i=0; i<unfinishedSections.size(); ++i)
2420 stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
2421 double nextMaxLimit = freeSize/stretchFactorSum;
2422 if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
2424 for (int i=0; i<unfinishedSections.size(); ++i)
2426 sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2427 freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
2429 unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
2430 } else // next maximum isn't hit, just distribute rest of free space on remaining sections
2432 for (int i=0; i<unfinishedSections.size(); ++i)
2433 sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
2434 unfinishedSections.clear();
2437 if (innerIterations == sectionCount*2)
2438 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2440 // now check whether the resulting section sizes violate minimum restrictions:
2441 bool foundMinimumViolation = false;
2442 for (int i=0; i<sectionSizes.size(); ++i)
2444 if (minimumLockedSections.contains(i))
2446 if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
2448 sectionSizes[i] = minSizes.at(i); // set it to minimum
2449 foundMinimumViolation = true; // make sure we repeat the whole optimization process
2450 minimumLockedSections.append(i);
2453 if (foundMinimumViolation)
2455 freeSize = totalSize;
2456 for (int i=0; i<sectionCount; ++i)
2458 if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
2459 unfinishedSections.append(i);
2461 freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
2463 // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
2464 for (int i=0; i<unfinishedSections.size(); ++i)
2465 sectionSizes[unfinishedSections.at(i)] = 0;
2468 if (outerIterations == sectionCount*2)
2469 qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
2471 QVector<int> result(sectionCount);
2472 for (int i=0; i<sectionCount; ++i)
2473 result[i] = qRound(sectionSizes.at(i));
2478 ////////////////////////////////////////////////////////////////////////////////////////////////////
2479 //////////////////// QCPLayoutGrid
2480 ////////////////////////////////////////////////////////////////////////////////////////////////////
2482 /*! \class QCPLayoutGrid
2483 \brief A layout that arranges child elements in a grid
2485 Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor,
2486 \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing).
2488 Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or
2489 column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref
2490 hasElement, that element can be retrieved with \ref element. If rows and columns that only have
2491 empty cells shall be removed, call \ref simplify. Removal of elements is either done by just
2492 adding the element to a different layout or by using the QCPLayout interface \ref take or \ref
2495 Row and column insertion can be performed with \ref insertRow and \ref insertColumn.
2499 Creates an instance of QCPLayoutGrid and sets default values.
2501 QCPLayoutGrid::QCPLayoutGrid() :
2507 QCPLayoutGrid::~QCPLayoutGrid()
2509 // clear all child layout elements. This is important because only the specific layouts know how
2510 // to handle removing elements (clear calls virtual removeAt method to do that).
2515 Returns the element in the cell in \a row and \a column.
2517 Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug
2518 message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
2520 \see addElement, hasElement
2522 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
2524 if (row >= 0 && row < mElements.size())
2526 if (column >= 0 && column < mElements.first().size())
2528 if (QCPLayoutElement *result = mElements.at(row).at(column))
2531 qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
2533 qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
2535 qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
2540 Returns the number of rows in the layout.
2544 int QCPLayoutGrid::rowCount() const
2546 return mElements.size();
2550 Returns the number of columns in the layout.
2554 int QCPLayoutGrid::columnCount() const
2556 if (mElements.size() > 0)
2557 return mElements.first().size();
2563 Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it
2564 is first removed from there. If \a row or \a column don't exist yet, the layout is expanded
2567 Returns true if the element was added successfully, i.e. if the cell at \a row and \a column
2568 didn't already have an element.
2570 \see element, hasElement, take, remove
2572 bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
2576 if (!hasElement(row, column))
2578 if (element->layout()) // remove from old layout first
2579 element->layout()->take(element);
2580 expandTo(row+1, column+1);
2581 mElements[row][column] = element;
2582 adoptElement(element);
2585 qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
2587 qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
2592 Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't
2597 bool QCPLayoutGrid::hasElement(int row, int column)
2599 if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
2600 return mElements.at(row).at(column);
2606 Sets the stretch \a factor of \a column.
2608 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2609 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2610 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2612 The default stretch factor of newly created rows/columns is 1.
2614 \see setColumnStretchFactors, setRowStretchFactor
2616 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
2618 if (column >= 0 && column < columnCount())
2621 mColumnStretchFactors[column] = factor;
2623 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2625 qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
2629 Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount.
2631 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2632 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2633 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2635 The default stretch factor of newly created rows/columns is 1.
2637 \see setColumnStretchFactor, setRowStretchFactors
2639 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
2641 if (factors.size() == mColumnStretchFactors.size())
2643 mColumnStretchFactors = factors;
2644 for (int i=0; i<mColumnStretchFactors.size(); ++i)
2646 if (mColumnStretchFactors.at(i) <= 0)
2648 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
2649 mColumnStretchFactors[i] = 1;
2653 qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
2657 Sets the stretch \a factor of \a row.
2659 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2660 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2661 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2663 The default stretch factor of newly created rows/columns is 1.
2665 \see setColumnStretchFactors, setRowStretchFactor
2667 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
2669 if (row >= 0 && row < rowCount())
2672 mRowStretchFactors[row] = factor;
2674 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
2676 qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
2680 Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount.
2682 Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
2683 their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
2684 QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
2686 The default stretch factor of newly created rows/columns is 1.
2688 \see setRowStretchFactor, setColumnStretchFactors
2690 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
2692 if (factors.size() == mRowStretchFactors.size())
2694 mRowStretchFactors = factors;
2695 for (int i=0; i<mRowStretchFactors.size(); ++i)
2697 if (mRowStretchFactors.at(i) <= 0)
2699 qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
2700 mRowStretchFactors[i] = 1;
2704 qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
2708 Sets the gap that is left blank between columns to \a pixels.
2712 void QCPLayoutGrid::setColumnSpacing(int pixels)
2714 mColumnSpacing = pixels;
2718 Sets the gap that is left blank between rows to \a pixels.
2720 \see setColumnSpacing
2722 void QCPLayoutGrid::setRowSpacing(int pixels)
2724 mRowSpacing = pixels;
2728 Expands the layout to have \a newRowCount rows and \a newColumnCount columns. So the last valid
2729 row index will be \a newRowCount-1, the last valid column index will be \a newColumnCount-1.
2731 If the current column/row count is already larger or equal to \a newColumnCount/\a newRowCount,
2732 this function does nothing in that dimension.
2734 Newly created cells are empty, new rows and columns have the stretch factor 1.
2736 Note that upon a call to \ref addElement, the layout is expanded automatically to contain the
2737 specified row and column, using this function.
2741 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
2743 // add rows as necessary:
2744 while (rowCount() < newRowCount)
2746 mElements.append(QList<QCPLayoutElement*>());
2747 mRowStretchFactors.append(1);
2749 // go through rows and expand columns as necessary:
2750 int newColCount = qMax(columnCount(), newColumnCount);
2751 for (int i=0; i<rowCount(); ++i)
2753 while (mElements.at(i).size() < newColCount)
2754 mElements[i].append(0);
2756 while (mColumnStretchFactors.size() < newColCount)
2757 mColumnStretchFactors.append(1);
2761 Inserts a new row with empty cells at the row index \a newIndex. Valid values for \a newIndex
2762 range from 0 (inserts a row at the top) to \a rowCount (appends a row at the bottom).
2766 void QCPLayoutGrid::insertRow(int newIndex)
2768 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2776 if (newIndex > rowCount())
2777 newIndex = rowCount();
2779 mRowStretchFactors.insert(newIndex, 1);
2780 QList<QCPLayoutElement*> newRow;
2781 for (int col=0; col<columnCount(); ++col)
2782 newRow.append((QCPLayoutElement*)0);
2783 mElements.insert(newIndex, newRow);
2787 Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a
2788 newIndex range from 0 (inserts a row at the left) to \a rowCount (appends a row at the right).
2792 void QCPLayoutGrid::insertColumn(int newIndex)
2794 if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
2802 if (newIndex > columnCount())
2803 newIndex = columnCount();
2805 mColumnStretchFactors.insert(newIndex, 1);
2806 for (int row=0; row<rowCount(); ++row)
2807 mElements[row].insert(newIndex, (QCPLayoutElement*)0);
2810 /* inherits documentation from base class */
2811 void QCPLayoutGrid::updateLayout()
2813 QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
2814 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2815 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2817 int totalRowSpacing = (rowCount()-1) * mRowSpacing;
2818 int totalColSpacing = (columnCount()-1) * mColumnSpacing;
2819 QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
2820 QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
2822 // go through cells and set rects accordingly:
2823 int yOffset = mRect.top();
2824 for (int row=0; row<rowCount(); ++row)
2827 yOffset += rowHeights.at(row-1)+mRowSpacing;
2828 int xOffset = mRect.left();
2829 for (int col=0; col<columnCount(); ++col)
2832 xOffset += colWidths.at(col-1)+mColumnSpacing;
2833 if (mElements.at(row).at(col))
2834 mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
2839 /* inherits documentation from base class */
2840 int QCPLayoutGrid::elementCount() const
2842 return rowCount()*columnCount();
2845 /* inherits documentation from base class */
2846 QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
2848 int colC = columnCount();
2849 if (index >= 0 && colC && index < elementCount())
2850 return mElements.at(index / colC).at(index % colC);
2855 /* inherits documentation from base class */
2856 QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
2859 int colC = columnCount();
2863 if (QCPLayoutElement *el = elementAt(index))
2867 mElements[index / colC][index % colC] = 0;
2871 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
2876 /* inherits documentation from base class */
2877 bool QCPLayoutGrid::take(QCPLayoutElement *element)
2881 for (int i=0; i<elementCount(); ++i)
2883 if (elementAt(i) == element)
2889 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
2891 qDebug() << Q_FUNC_INFO << "Can't take null element";
2895 /* inherits documentation from base class */
2896 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
2898 QList<QCPLayoutElement*> result;
2899 int colC = columnCount();
2900 int rowC = rowCount();
2901 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2902 result.reserve(colC*rowC);
2904 for (int row=0; row<rowC; ++row)
2906 for (int col=0; col<colC; ++col)
2908 result.append(mElements.at(row).at(col));
2913 int c = result.size();
2914 for (int i=0; i<c; ++i)
2917 result << result.at(i)->elements(recursive);
2924 Simplifies the layout by collapsing rows and columns which only contain empty cells.
2926 void QCPLayoutGrid::simplify()
2928 // remove rows with only empty cells:
2929 for (int row=rowCount()-1; row>=0; --row)
2931 bool hasElements = false;
2932 for (int col=0; col<columnCount(); ++col)
2934 if (mElements.at(row).at(col))
2942 mRowStretchFactors.removeAt(row);
2943 mElements.removeAt(row);
2944 if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
2945 mColumnStretchFactors.clear();
2949 // remove columns with only empty cells:
2950 for (int col=columnCount()-1; col>=0; --col)
2952 bool hasElements = false;
2953 for (int row=0; row<rowCount(); ++row)
2955 if (mElements.at(row).at(col))
2963 mColumnStretchFactors.removeAt(col);
2964 for (int row=0; row<rowCount(); ++row)
2965 mElements[row].removeAt(col);
2970 /* inherits documentation from base class */
2971 QSize QCPLayoutGrid::minimumSizeHint() const
2973 QVector<int> minColWidths, minRowHeights;
2974 getMinimumRowColSizes(&minColWidths, &minRowHeights);
2976 for (int i=0; i<minColWidths.size(); ++i)
2977 result.rwidth() += minColWidths.at(i);
2978 for (int i=0; i<minRowHeights.size(); ++i)
2979 result.rheight() += minRowHeights.at(i);
2980 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2981 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
2985 /* inherits documentation from base class */
2986 QSize QCPLayoutGrid::maximumSizeHint() const
2988 QVector<int> maxColWidths, maxRowHeights;
2989 getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
2992 for (int i=0; i<maxColWidths.size(); ++i)
2993 result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
2994 for (int i=0; i<maxRowHeights.size(); ++i)
2995 result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
2996 result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
2997 result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
3003 Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights
3006 The minimum height of a row is the largest minimum height of any element in that row. The minimum
3007 width of a column is the largest minimum width of any element in that column.
3009 This is a helper function for \ref updateLayout.
3011 \see getMaximumRowColSizes
3013 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
3015 *minColWidths = QVector<int>(columnCount(), 0);
3016 *minRowHeights = QVector<int>(rowCount(), 0);
3017 for (int row=0; row<rowCount(); ++row)
3019 for (int col=0; col<columnCount(); ++col)
3021 if (mElements.at(row).at(col))
3023 QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
3024 QSize min = mElements.at(row).at(col)->minimumSize();
3025 QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
3026 if (minColWidths->at(col) < final.width())
3027 (*minColWidths)[col] = final.width();
3028 if (minRowHeights->at(row) < final.height())
3029 (*minRowHeights)[row] = final.height();
3037 Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights
3040 The maximum height of a row is the smallest maximum height of any element in that row. The
3041 maximum width of a column is the smallest maximum width of any element in that column.
3043 This is a helper function for \ref updateLayout.
3045 \see getMinimumRowColSizes
3047 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
3049 *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
3050 *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
3051 for (int row=0; row<rowCount(); ++row)
3053 for (int col=0; col<columnCount(); ++col)
3055 if (mElements.at(row).at(col))
3057 QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
3058 QSize max = mElements.at(row).at(col)->maximumSize();
3059 QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
3060 if (maxColWidths->at(col) > final.width())
3061 (*maxColWidths)[col] = final.width();
3062 if (maxRowHeights->at(row) > final.height())
3063 (*maxRowHeights)[row] = final.height();
3070 ////////////////////////////////////////////////////////////////////////////////////////////////////
3071 //////////////////// QCPLayoutInset
3072 ////////////////////////////////////////////////////////////////////////////////////////////////////
3073 /*! \class QCPLayoutInset
3074 \brief A layout that places child elements aligned to the border or arbitrarily positioned
3076 Elements are placed either aligned to the border or at arbitrary position in the area of the
3077 layout. Which placement applies is controlled with the \ref InsetPlacement (\ref
3080 Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or
3081 addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset
3082 placement will default to \ref ipBorderAligned and the element will be aligned according to the
3083 \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at
3084 arbitrary position and size, defined by \a rect.
3086 The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively.
3088 This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout.
3091 /* start documentation of inline functions */
3093 /*! \fn virtual void QCPLayoutInset::simplify()
3095 The QCPInsetLayout does not need simplification since it can never have empty cells due to its
3096 linear index structure. This method does nothing.
3099 /* end documentation of inline functions */
3102 Creates an instance of QCPLayoutInset and sets default values.
3104 QCPLayoutInset::QCPLayoutInset()
3108 QCPLayoutInset::~QCPLayoutInset()
3110 // clear all child layout elements. This is important because only the specific layouts know how
3111 // to handle removing elements (clear calls virtual removeAt method to do that).
3116 Returns the placement type of the element with the specified \a index.
3118 QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const
3120 if (elementAt(index))
3121 return mInsetPlacement.at(index);
3124 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3130 Returns the alignment of the element with the specified \a index. The alignment only has a
3131 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned.
3133 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
3135 if (elementAt(index))
3136 return mInsetAlignment.at(index);
3139 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3145 Returns the rect of the element with the specified \a index. The rect only has a
3146 meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
3148 QRectF QCPLayoutInset::insetRect(int index) const
3150 if (elementAt(index))
3151 return mInsetRect.at(index);
3154 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3160 Sets the inset placement type of the element with the specified \a index to \a placement.
3164 void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)
3166 if (elementAt(index))
3167 mInsetPlacement[index] = placement;
3169 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3173 If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function
3174 is used to set the alignment of the element with the specified \a index to \a alignment.
3176 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3177 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3178 alignment flags will be ignored.
3180 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
3182 if (elementAt(index))
3183 mInsetAlignment[index] = alignment;
3185 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3189 If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the
3190 position and size of the element with the specified \a index to \a rect.
3192 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3193 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3194 corner of the layout, with 35% width and height of the parent layout.
3196 Note that the minimum and maximum sizes of the embedded element (\ref
3197 QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced.
3199 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
3201 if (elementAt(index))
3202 mInsetRect[index] = rect;
3204 qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
3207 /* inherits documentation from base class */
3208 void QCPLayoutInset::updateLayout()
3210 for (int i=0; i<mElements.size(); ++i)
3213 QSize finalMinSize, finalMaxSize;
3214 QSize minSizeHint = mElements.at(i)->minimumSizeHint();
3215 QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
3216 finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
3217 finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
3218 finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
3219 finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
3220 if (mInsetPlacement.at(i) == ipFree)
3222 insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
3223 rect().y()+rect().height()*mInsetRect.at(i).y(),
3224 rect().width()*mInsetRect.at(i).width(),
3225 rect().height()*mInsetRect.at(i).height());
3226 if (insetRect.size().width() < finalMinSize.width())
3227 insetRect.setWidth(finalMinSize.width());
3228 if (insetRect.size().height() < finalMinSize.height())
3229 insetRect.setHeight(finalMinSize.height());
3230 if (insetRect.size().width() > finalMaxSize.width())
3231 insetRect.setWidth(finalMaxSize.width());
3232 if (insetRect.size().height() > finalMaxSize.height())
3233 insetRect.setHeight(finalMaxSize.height());
3234 } else if (mInsetPlacement.at(i) == ipBorderAligned)
3236 insetRect.setSize(finalMinSize);
3237 Qt::Alignment al = mInsetAlignment.at(i);
3238 if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
3239 else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
3240 else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
3241 if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
3242 else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
3243 else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
3245 mElements.at(i)->setOuterRect(insetRect);
3249 /* inherits documentation from base class */
3250 int QCPLayoutInset::elementCount() const
3252 return mElements.size();
3255 /* inherits documentation from base class */
3256 QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
3258 if (index >= 0 && index < mElements.size())
3259 return mElements.at(index);
3264 /* inherits documentation from base class */
3265 QCPLayoutElement *QCPLayoutInset::takeAt(int index)
3267 if (QCPLayoutElement *el = elementAt(index))
3270 mElements.removeAt(index);
3271 mInsetPlacement.removeAt(index);
3272 mInsetAlignment.removeAt(index);
3273 mInsetRect.removeAt(index);
3277 qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
3282 /* inherits documentation from base class */
3283 bool QCPLayoutInset::take(QCPLayoutElement *element)
3287 for (int i=0; i<elementCount(); ++i)
3289 if (elementAt(i) == element)
3295 qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
3297 qDebug() << Q_FUNC_INFO << "Can't take null element";
3302 The inset layout is sensitive to events only at areas where its (visible) child elements are
3303 sensitive. If the selectTest method of any of the child elements returns a positive number for \a
3304 pos, this method returns a value corresponding to 0.99 times the parent plot's selection
3305 tolerance. The inset layout is not selectable itself by default. So if \a onlySelectable is true,
3308 See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
3310 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3316 for (int i=0; i<mElements.size(); ++i)
3318 // inset layout shall only return positive selectTest, if actually an inset object is at pos
3319 // else it would block the entire underlying QCPAxisRect with its surface.
3320 if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
3321 return mParentPlot->selectionTolerance()*0.99;
3327 Adds the specified \a element to the layout as an inset aligned at the border (\ref
3328 setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a
3331 \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
3332 Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
3333 alignment flags will be ignored.
3335 \see addElement(QCPLayoutElement *element, const QRectF &rect)
3337 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3341 if (element->layout()) // remove from old layout first
3342 element->layout()->take(element);
3343 mElements.append(element);
3344 mInsetPlacement.append(ipBorderAligned);
3345 mInsetAlignment.append(alignment);
3346 mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
3347 adoptElement(element);
3349 qDebug() << Q_FUNC_INFO << "Can't add null element";
3353 Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref
3354 setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a
3357 \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
3358 will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
3359 corner of the layout, with 35% width and height of the parent layout.
3361 \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
3363 void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
3367 if (element->layout()) // remove from old layout first
3368 element->layout()->take(element);
3369 mElements.append(element);
3370 mInsetPlacement.append(ipFree);
3371 mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
3372 mInsetRect.append(rect);
3373 adoptElement(element);
3375 qDebug() << Q_FUNC_INFO << "Can't add null element";
3379 ////////////////////////////////////////////////////////////////////////////////////////////////////
3380 //////////////////// QCPLineEnding
3381 ////////////////////////////////////////////////////////////////////////////////////////////////////
3383 /*! \class QCPLineEnding
3384 \brief Handles the different ending decorations for line-like items
3386 \image html QCPLineEnding.png "The various ending styles currently supported"
3388 For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
3389 has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
3391 The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
3392 be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
3393 the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
3394 For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
3395 directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
3396 respective arrow point inward.
3398 Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
3399 QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. \code
3400 myItemLine->setHead(QCPLineEnding::esSpikeArrow) \endcode
3404 Creates a QCPLineEnding instance with default values (style \ref esNone).
3406 QCPLineEnding::QCPLineEnding() :
3415 Creates a QCPLineEnding instance with the specified values.
3417 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
3426 Sets the style of the ending decoration.
3428 void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
3434 Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
3435 width defines the size perpendicular to the arrow's pointing direction.
3439 void QCPLineEnding::setWidth(double width)
3445 Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
3446 length defines the size in pointing direction.
3450 void QCPLineEnding::setLength(double length)
3456 Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point
3457 inward when \a inverted is set to true.
3459 Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or
3460 discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are
3461 affected by it, which can be used to control to which side the half bar points to.
3463 void QCPLineEnding::setInverted(bool inverted)
3465 mInverted = inverted;
3470 Returns the maximum pixel radius the ending decoration might cover, starting from the position
3471 the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
3473 This is relevant for clipping. Only omit painting of the decoration when the position where the
3474 decoration is supposed to be drawn is farther away from the clipping rect than the returned
3477 double QCPLineEnding::boundingDistance() const
3488 return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
3495 return mWidth*1.42; // items that only have a width -> width*sqrt(2)
3502 Starting from the origin of this line ending (which is style specific), returns the length
3503 covered by the line ending symbol, in backward direction.
3505 For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if
3506 both have the same \ref setLength value, because the spike arrow has an inward curved back, which
3507 reduces the length along its center axis (the drawing origin for arrows is at the tip).
3509 This function is used for precise, style specific placement of line endings, for example in
3512 double QCPLineEnding::realLength() const
3539 Draws the line ending with the specified \a painter at the position \a pos. The direction of the
3540 line ending is controlled with \a dir.
3542 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
3544 if (mStyle == esNone)
3547 QVector2D lengthVec(dir.normalized());
3548 if (lengthVec.isNull())
3549 lengthVec = QVector2D(1, 0);
3550 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
3551 lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
3552 widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
3554 QPen penBackup = painter->pen();
3555 QBrush brushBackup = painter->brush();
3556 QPen miterPen = penBackup;
3557 miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
3558 QBrush brush(painter->pen().color(), Qt::SolidPattern);
3564 QPointF points[3] = {pos.toPointF(),
3565 (pos-lengthVec+widthVec).toPointF(),
3566 (pos-lengthVec-widthVec).toPointF()
3568 painter->setPen(miterPen);
3569 painter->setBrush(brush);
3570 painter->drawConvexPolygon(points, 3);
3571 painter->setBrush(brushBackup);
3572 painter->setPen(penBackup);
3577 QPointF points[4] = {pos.toPointF(),
3578 (pos-lengthVec+widthVec).toPointF(),
3579 (pos-lengthVec*0.8f).toPointF(),
3580 (pos-lengthVec-widthVec).toPointF()
3582 painter->setPen(miterPen);
3583 painter->setBrush(brush);
3584 painter->drawConvexPolygon(points, 4);
3585 painter->setBrush(brushBackup);
3586 painter->setPen(penBackup);
3591 QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
3593 (pos-lengthVec-widthVec).toPointF()
3595 painter->setPen(miterPen);
3596 painter->drawPolyline(points, 3);
3597 painter->setPen(penBackup);
3602 painter->setBrush(brush);
3603 painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
3604 painter->setBrush(brushBackup);
3609 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3610 QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
3611 (pos-widthVecPerp-widthVec).toPointF(),
3612 (pos+widthVecPerp-widthVec).toPointF(),
3613 (pos+widthVecPerp+widthVec).toPointF()
3615 painter->setPen(miterPen);
3616 painter->setBrush(brush);
3617 painter->drawConvexPolygon(points, 4);
3618 painter->setBrush(brushBackup);
3619 painter->setPen(penBackup);
3624 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
3625 QPointF points[4] = {(pos-widthVecPerp).toPointF(),
3626 (pos-widthVec).toPointF(),
3627 (pos+widthVecPerp).toPointF(),
3628 (pos+widthVec).toPointF()
3630 painter->setPen(miterPen);
3631 painter->setBrush(brush);
3632 painter->drawConvexPolygon(points, 4);
3633 painter->setBrush(brushBackup);
3634 painter->setPen(penBackup);
3639 painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
3644 painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
3649 if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
3651 // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
3652 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
3653 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
3656 // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
3657 painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
3658 (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
3668 Draws the line ending. The direction is controlled with the \a angle parameter in radians.
3670 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
3672 draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
3676 ////////////////////////////////////////////////////////////////////////////////////////////////////
3677 //////////////////// QCPGrid
3678 ////////////////////////////////////////////////////////////////////////////////////////////////////
3681 \brief Responsible for drawing the grid of a QCPAxis.
3683 This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the
3684 grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref
3685 QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself.
3687 The axis and grid drawing was split into two classes to allow them to be placed on different
3688 layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid
3689 in the background and the axes in the foreground, and any plottables/items in between. This
3690 described situation is the default setup, see the QCPLayer documentation.
3694 Creates a QCPGrid instance and sets default values.
3696 You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid.
3698 QCPGrid::QCPGrid(QCPAxis *parentAxis) :
3699 QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
3700 mParentAxis(parentAxis)
3702 // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
3703 setParent(parentAxis);
3704 setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
3705 setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
3706 setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
3707 setSubGridVisible(false);
3708 setAntialiased(false);
3709 setAntialiasedSubGrid(false);
3710 setAntialiasedZeroLine(false);
3714 Sets whether grid lines at sub tick marks are drawn.
3718 void QCPGrid::setSubGridVisible(bool visible)
3720 mSubGridVisible = visible;
3724 Sets whether sub grid lines are drawn antialiased.
3726 void QCPGrid::setAntialiasedSubGrid(bool enabled)
3728 mAntialiasedSubGrid = enabled;
3732 Sets whether zero lines are drawn antialiased.
3734 void QCPGrid::setAntialiasedZeroLine(bool enabled)
3736 mAntialiasedZeroLine = enabled;
3740 Sets the pen with which (major) grid lines are drawn.
3742 void QCPGrid::setPen(const QPen &pen)
3748 Sets the pen with which sub grid lines are drawn.
3750 void QCPGrid::setSubGridPen(const QPen &pen)
3756 Sets the pen with which zero lines are drawn.
3758 Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid
3759 lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
3761 void QCPGrid::setZeroLinePen(const QPen &pen)
3768 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
3769 before drawing the major grid lines.
3771 This is the antialiasing state the painter passed to the \ref draw method is in by default.
3773 This function takes into account the local setting of the antialiasing flag as well as the
3774 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
3775 QCustomPlot::setNotAntialiasedElements.
3779 void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
3781 applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
3786 Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
3787 over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
3789 void QCPGrid::draw(QCPPainter *painter)
3791 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3793 if (mSubGridVisible)
3794 drawSubGridLines(painter);
3795 drawGridLines(painter);
3800 Draws the main grid lines and possibly a zero line with the specified painter.
3802 This is a helper function called by \ref draw.
3804 void QCPGrid::drawGridLines(QCPPainter *painter) const
3806 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3808 int lowTick = mParentAxis->mLowestVisibleTick;
3809 int highTick = mParentAxis->mHighestVisibleTick;
3810 double t; // helper variable, result of coordinate-to-pixel transforms
3811 if (mParentAxis->orientation() == Qt::Horizontal)
3814 int zeroLineIndex = -1;
3815 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3817 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3818 painter->setPen(mZeroLinePen);
3819 double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
3820 for (int i=lowTick; i <= highTick; ++i)
3822 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3825 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3826 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3832 applyDefaultAntialiasingHint(painter);
3833 painter->setPen(mPen);
3834 for (int i=lowTick; i <= highTick; ++i)
3836 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3837 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
3838 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3843 int zeroLineIndex = -1;
3844 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
3846 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
3847 painter->setPen(mZeroLinePen);
3848 double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
3849 for (int i=lowTick; i <= highTick; ++i)
3851 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
3854 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3855 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3861 applyDefaultAntialiasingHint(painter);
3862 painter->setPen(mPen);
3863 for (int i=lowTick; i <= highTick; ++i)
3865 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
3866 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
3867 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3874 Draws the sub grid lines with the specified painter.
3876 This is a helper function called by \ref draw.
3878 void QCPGrid::drawSubGridLines(QCPPainter *painter) const
3880 if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
3882 applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
3883 double t; // helper variable, result of coordinate-to-pixel transforms
3884 painter->setPen(mSubGridPen);
3885 if (mParentAxis->orientation() == Qt::Horizontal)
3887 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3889 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
3890 painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
3894 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
3896 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
3897 painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
3903 ////////////////////////////////////////////////////////////////////////////////////////////////////
3904 //////////////////// QCPAxis
3905 ////////////////////////////////////////////////////////////////////////////////////////////////////
3908 \brief Manages a single axis inside a QCustomPlot.
3910 Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via
3911 QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
3912 QCustomPlot::yAxis2 (right).
3914 Axes are always part of an axis rect, see QCPAxisRect.
3915 \image html AxisNamesOverview.png
3916 <center>Naming convention of axis parts</center>
3919 \image html AxisRectSpacingOverview.png
3920 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line
3921 on the left represents the QCustomPlot widget border.</center>
3925 /* start of documentation of inline functions */
3927 /*! \fn Qt::Orientation QCPAxis::orientation() const
3929 Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
3930 from the axis type (left, top, right or bottom).
3932 \see orientation(AxisType type)
3935 /*! \fn QCPGrid *QCPAxis::grid() const
3937 Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the
3941 /*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
3943 Returns the orientation of the specified axis type
3948 /* end of documentation of inline functions */
3949 /* start of documentation of signals */
3951 /*! \fn void QCPAxis::ticksRequest()
3953 This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
3954 labels for a replot.
3956 Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
3957 tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
3958 setTickVectorLabels.
3960 If you only want static ticks you probably don't need this signal, since you can just set the
3961 tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
3962 maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
3963 signal and set the vector/vectors there.
3966 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
3968 This signal is emitted when the range of this axis has changed. You can connect it to the \ref
3969 setRange slot of another axis to communicate the new range to the other axis, in order for it to
3973 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
3976 Additionally to the new range, this signal also provides the previous range held by the axis as
3980 /*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
3982 This signal is emitted when the scale type changes, by calls to \ref setScaleType
3985 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
3987 This signal is emitted when the selection state of this axis has changed, either by user interaction
3988 or by a direct call to \ref setSelectedParts.
3991 /*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
3993 This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
3996 /* end of documentation of signals */
3999 Constructs an Axis instance of Type \a type for the axis rect \a parent.
4001 Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create
4002 them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,
4003 create them manually and then inject them also via \ref QCPAxisRect::addAxis.
4005 QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
4006 QCPLayerable(parent->parentPlot(), QString(), parent),
4011 mOrientation(orientation(type)),
4012 mSelectableParts(spAxis | spTickLabels | spAxisLabel),
4013 mSelectedParts(spNone),
4014 mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4015 mSelectedBasePen(QPen(Qt::blue, 2)),
4018 mLabelFont(mParentPlot->font()),
4019 mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
4020 mLabelColor(Qt::black),
4021 mSelectedLabelColor(Qt::blue),
4024 mAutoTickLabels(true),
4025 mTickLabelType(ltNumber),
4026 mTickLabelFont(mParentPlot->font()),
4027 mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
4028 mTickLabelColor(Qt::black),
4029 mSelectedTickLabelColor(Qt::blue),
4030 mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
4031 mDateTimeSpec(Qt::LocalTime),
4032 mNumberPrecision(6),
4033 mNumberFormatChar('g'),
4034 mNumberBeautifulPowers(true),
4035 // ticks and subticks:
4041 mAutoTickStep(true),
4042 mAutoSubTicks(true),
4043 mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4044 mSelectedTickPen(QPen(Qt::blue, 2)),
4045 mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
4046 mSelectedSubTickPen(QPen(Qt::blue, 2)),
4049 mRangeReversed(false),
4050 mScaleType(stLinear),
4052 mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
4053 // internal members:
4054 mGrid(new QCPGrid(this)),
4055 mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
4056 mLowestVisibleTick(0),
4057 mHighestVisibleTick(-1),
4058 mCachedMarginValid(false),
4061 mGrid->setVisible(false);
4062 setAntialiased(false);
4063 setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
4067 setTickLabelPadding(3);
4069 } else if (type == atRight)
4071 setTickLabelPadding(7);
4072 setLabelPadding(12);
4073 } else if (type == atBottom)
4075 setTickLabelPadding(3);
4077 } else if (type == atLeft)
4079 setTickLabelPadding(5);
4080 setLabelPadding(10);
4086 delete mAxisPainter;
4089 /* No documentation as it is a property getter */
4090 int QCPAxis::tickLabelPadding() const
4092 return mAxisPainter->tickLabelPadding;
4095 /* No documentation as it is a property getter */
4096 double QCPAxis::tickLabelRotation() const
4098 return mAxisPainter->tickLabelRotation;
4101 /* No documentation as it is a property getter */
4102 QCPAxis::LabelSide QCPAxis::tickLabelSide() const
4104 return mAxisPainter->tickLabelSide;
4107 /* No documentation as it is a property getter */
4108 QString QCPAxis::numberFormat() const
4111 result.append(mNumberFormatChar);
4112 if (mNumberBeautifulPowers)
4114 result.append(QLatin1Char('b'));
4115 if (mAxisPainter->numberMultiplyCross)
4116 result.append(QLatin1Char('c'));
4121 /* No documentation as it is a property getter */
4122 int QCPAxis::tickLengthIn() const
4124 return mAxisPainter->tickLengthIn;
4127 /* No documentation as it is a property getter */
4128 int QCPAxis::tickLengthOut() const
4130 return mAxisPainter->tickLengthOut;
4133 /* No documentation as it is a property getter */
4134 int QCPAxis::subTickLengthIn() const
4136 return mAxisPainter->subTickLengthIn;
4139 /* No documentation as it is a property getter */
4140 int QCPAxis::subTickLengthOut() const
4142 return mAxisPainter->subTickLengthOut;
4145 /* No documentation as it is a property getter */
4146 int QCPAxis::labelPadding() const
4148 return mAxisPainter->labelPadding;
4151 /* No documentation as it is a property getter */
4152 int QCPAxis::offset() const
4154 return mAxisPainter->offset;
4157 /* No documentation as it is a property getter */
4158 QCPLineEnding QCPAxis::lowerEnding() const
4160 return mAxisPainter->lowerEnding;
4163 /* No documentation as it is a property getter */
4164 QCPLineEnding QCPAxis::upperEnding() const
4166 return mAxisPainter->upperEnding;
4170 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
4171 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
4172 scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
4173 (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
4174 ticks, consider choosing a logarithm base of 100, 1000 or even higher.
4176 If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
4177 (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4178 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4179 part). To only display the decimal power, set the number precision to zero with
4180 \ref setNumberPrecision.
4182 void QCPAxis::setScaleType(QCPAxis::ScaleType type)
4184 if (mScaleType != type)
4187 if (mScaleType == stLogarithmic)
4188 setRange(mRange.sanitizedForLogScale());
4189 mCachedMarginValid = false;
4190 emit scaleTypeChanged(mScaleType);
4195 If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
4196 scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
4198 Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
4199 less major ticks, consider choosing \a base 100, 1000 or even higher.
4201 void QCPAxis::setScaleLogBase(double base)
4205 mScaleLogBase = base;
4206 mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
4207 mCachedMarginValid = false;
4209 qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
4213 Sets the range of the axis.
4215 This slot may be connected with the \ref rangeChanged signal of another axis so this axis
4216 is always synchronized with the other axis range, when it changes.
4218 To invert the direction of an axis, use \ref setRangeReversed.
4220 void QCPAxis::setRange(const QCPRange &range)
4222 if (range.lower == mRange.lower && range.upper == mRange.upper)
4225 if (!QCPRange::validRange(range)) return;
4226 QCPRange oldRange = mRange;
4227 if (mScaleType == stLogarithmic)
4229 mRange = range.sanitizedForLogScale();
4232 mRange = range.sanitizedForLinScale();
4234 mCachedMarginValid = false;
4235 emit rangeChanged(mRange);
4236 emit rangeChanged(mRange, oldRange);
4240 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
4241 (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
4243 However, even when \a selectable is set to a value not allowing the selection of a specific part,
4244 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
4247 \see SelectablePart, setSelectedParts
4249 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
4251 if (mSelectableParts != selectable)
4253 mSelectableParts = selectable;
4254 emit selectableChanged(mSelectableParts);
4259 Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
4260 is selected, it uses a different pen/font.
4262 The entire selection mechanism for axes is handled automatically when \ref
4263 QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
4264 wish to change the selection state manually.
4266 This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
4268 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
4270 \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
4271 setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
4273 void QCPAxis::setSelectedParts(const SelectableParts &selected)
4275 if (mSelectedParts != selected)
4277 mSelectedParts = selected;
4278 emit selectionChanged(mSelectedParts);
4285 Sets the lower and upper bound of the axis range.
4287 To invert the direction of an axis, use \ref setRangeReversed.
4289 There is also a slot to set a range, see \ref setRange(const QCPRange &range).
4291 void QCPAxis::setRange(double lower, double upper)
4293 if (lower == mRange.lower && upper == mRange.upper)
4296 if (!QCPRange::validRange(lower, upper)) return;
4297 QCPRange oldRange = mRange;
4298 mRange.lower = lower;
4299 mRange.upper = upper;
4300 if (mScaleType == stLogarithmic)
4302 mRange = mRange.sanitizedForLogScale();
4305 mRange = mRange.sanitizedForLinScale();
4307 mCachedMarginValid = false;
4308 emit rangeChanged(mRange);
4309 emit rangeChanged(mRange, oldRange);
4315 Sets the range of the axis.
4317 The \a position coordinate indicates together with the \a alignment parameter, where the new
4318 range will be positioned. \a size defines the size of the new axis range. \a alignment may be
4319 Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
4320 or center of the range to be aligned with \a position. Any other values of \a alignment will
4321 default to Qt::AlignCenter.
4323 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
4325 if (alignment == Qt::AlignLeft)
4326 setRange(position, position+size);
4327 else if (alignment == Qt::AlignRight)
4328 setRange(position-size, position);
4329 else // alignment == Qt::AlignCenter
4330 setRange(position-size/2.0, position+size/2.0);
4334 Sets the lower bound of the axis range. The upper bound is not changed.
4337 void QCPAxis::setRangeLower(double lower)
4339 if (mRange.lower == lower)
4342 QCPRange oldRange = mRange;
4343 mRange.lower = lower;
4344 if (mScaleType == stLogarithmic)
4346 mRange = mRange.sanitizedForLogScale();
4349 mRange = mRange.sanitizedForLinScale();
4351 mCachedMarginValid = false;
4352 emit rangeChanged(mRange);
4353 emit rangeChanged(mRange, oldRange);
4357 Sets the upper bound of the axis range. The lower bound is not changed.
4360 void QCPAxis::setRangeUpper(double upper)
4362 if (mRange.upper == upper)
4365 QCPRange oldRange = mRange;
4366 mRange.upper = upper;
4367 if (mScaleType == stLogarithmic)
4369 mRange = mRange.sanitizedForLogScale();
4372 mRange = mRange.sanitizedForLinScale();
4374 mCachedMarginValid = false;
4375 emit rangeChanged(mRange);
4376 emit rangeChanged(mRange, oldRange);
4380 Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
4381 axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
4382 direction of increasing values is inverted.
4384 Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
4385 of the \ref setRange interface will still reference the mathematically smaller number than the \a
4388 void QCPAxis::setRangeReversed(bool reversed)
4390 if (mRangeReversed != reversed)
4392 mRangeReversed = reversed;
4393 mCachedMarginValid = false;
4398 Sets whether the tick positions should be calculated automatically (either from an automatically
4399 generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
4401 If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
4402 For these manual ticks you may let QCPAxis generate the appropriate labels automatically by
4403 leaving \ref setAutoTickLabels set to true. If you also wish to control the displayed labels
4404 manually, set \ref setAutoTickLabels to false and provide the label strings with \ref
4405 setTickVectorLabels.
4407 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4408 vectors in a slot connected to the \ref ticksRequest signal.
4410 \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep
4412 void QCPAxis::setAutoTicks(bool on)
4414 if (mAutoTicks != on)
4417 mCachedMarginValid = false;
4422 When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be
4423 generated in the visible range, approximately.
4425 It's not guaranteed that this number of ticks is met exactly, but approximately within a
4426 tolerance of about two.
4428 Only values greater than zero are accepted as \a approximateCount.
4430 \see setAutoTickStep, setAutoTicks, setAutoSubTicks
4432 void QCPAxis::setAutoTickCount(int approximateCount)
4434 if (mAutoTickCount != approximateCount)
4436 if (approximateCount > 0)
4438 mAutoTickCount = approximateCount;
4439 mCachedMarginValid = false;
4441 qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
4446 Sets whether the tick labels are generated automatically. Depending on the tick label type (\ref
4447 ltNumber or \ref ltDateTime), the labels will either show the coordinate as floating point
4448 number (\ref setNumberFormat), or a date/time formatted according to \ref setDateTimeFormat.
4450 If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
4451 is usually used in a combination with \ref setAutoTicks set to false for complete control over
4452 tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
4453 etc. as tick labels.
4455 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
4456 vectors in a slot connected to the \ref ticksRequest signal.
4460 void QCPAxis::setAutoTickLabels(bool on)
4462 if (mAutoTickLabels != on)
4464 mAutoTickLabels = on;
4465 mCachedMarginValid = false;
4470 Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
4471 automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
4474 The number of ticks the algorithm aims for within the visible range can be specified with \ref
4477 If \a on is set to false, you may set the tick step manually with \ref setTickStep.
4479 \see setAutoTicks, setAutoSubTicks, setAutoTickCount
4481 void QCPAxis::setAutoTickStep(bool on)
4483 if (mAutoTickStep != on)
4486 mCachedMarginValid = false;
4491 Sets whether the number of sub ticks in one tick interval is determined automatically. This
4492 works, as long as the tick step mantissa is a multiple of 0.5. When \ref setAutoTickStep is
4493 enabled, this is always the case.
4495 When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
4497 \see setAutoTickCount, setAutoTicks, setAutoTickStep
4499 void QCPAxis::setAutoSubTicks(bool on)
4501 if (mAutoSubTicks != on)
4504 mCachedMarginValid = false;
4509 Sets whether tick marks are displayed.
4511 Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
4512 that, see \ref setTickLabels.
4514 void QCPAxis::setTicks(bool show)
4519 mCachedMarginValid = false;
4524 Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
4526 void QCPAxis::setTickLabels(bool show)
4528 if (mTickLabels != show)
4531 mCachedMarginValid = false;
4536 Sets the distance between the axis base line (including any outward ticks) and the tick labels.
4537 \see setLabelPadding, setPadding
4539 void QCPAxis::setTickLabelPadding(int padding)
4541 if (mAxisPainter->tickLabelPadding != padding)
4543 mAxisPainter->tickLabelPadding = padding;
4544 mCachedMarginValid = false;
4549 Sets whether the tick labels display numbers or dates/times.
4551 If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.
4553 If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.
4555 In QCustomPlot, date/time coordinates are <tt>double</tt> numbers representing the seconds since
4556 1970-01-01T00:00:00 UTC. This format can be retrieved from QDateTime objects with the
4557 QDateTime::toTime_t() function. Since this only gives a resolution of one second, there is also
4558 the QDateTime::toMSecsSinceEpoch() function which returns the timespan described above in
4559 milliseconds. Divide its return value by 1000.0 to get a value with the format needed for
4560 date/time plotting, with a resolution of one millisecond.
4562 Using the toMSecsSinceEpoch function allows dates that go back to 2nd January 4713 B.C.
4563 (represented by a negative number), unlike the toTime_t function, which works with unsigned
4564 integers and thus only goes back to 1st January 1970. So both for range and accuracy, use of
4565 toMSecsSinceEpoch()/1000.0 should be preferred as key coordinate for date/time axes.
4569 void QCPAxis::setTickLabelType(LabelType type)
4571 if (mTickLabelType != type)
4573 mTickLabelType = type;
4574 mCachedMarginValid = false;
4579 Sets the font of the tick labels.
4581 \see setTickLabels, setTickLabelColor
4583 void QCPAxis::setTickLabelFont(const QFont &font)
4585 if (font != mTickLabelFont)
4587 mTickLabelFont = font;
4588 mCachedMarginValid = false;
4593 Sets the color of the tick labels.
4595 \see setTickLabels, setTickLabelFont
4597 void QCPAxis::setTickLabelColor(const QColor &color)
4599 if (color != mTickLabelColor)
4601 mTickLabelColor = color;
4602 mCachedMarginValid = false;
4607 Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
4608 the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
4609 from -90 to 90 degrees.
4611 If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
4612 other angles, the label is drawn with an offset such that it seems to point toward or away from
4615 void QCPAxis::setTickLabelRotation(double degrees)
4617 if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
4619 mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
4620 mCachedMarginValid = false;
4625 Sets whether the tick labels (numbers) shall appear inside or outside the axis rect.
4627 The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels
4628 to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels
4629 appear on the inside are additionally clipped to the axis rect.
4631 void QCPAxis::setTickLabelSide(LabelSide side)
4633 mAxisPainter->tickLabelSide = side;
4634 mCachedMarginValid = false;
4638 Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
4639 for details about the \a format string, see the documentation of QDateTime::toString().
4641 Newlines can be inserted with "\n".
4643 \see setDateTimeSpec
4645 void QCPAxis::setDateTimeFormat(const QString &format)
4647 if (mDateTimeFormat != format)
4649 mDateTimeFormat = format;
4650 mCachedMarginValid = false;
4655 Sets the time spec that is used for the date time values when \ref setTickLabelType is \ref
4658 The default value of QDateTime objects (and also QCustomPlot) is <tt>Qt::LocalTime</tt>. However,
4659 if the date time values passed to QCustomPlot are given in the UTC spec, set \a
4660 timeSpec to <tt>Qt::UTC</tt> to get the correct axis labels.
4662 \see setDateTimeFormat
4664 void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
4666 mDateTimeSpec = timeSpec;
4670 Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
4671 ltNumber). This \a formatCode is an extended version of the format code used e.g. by
4672 QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
4673 section in the detailed description of the QString class. \a formatCode is a string of one, two
4674 or three characters. The first character is identical to the normal format code used by Qt. In
4675 short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
4676 whichever is shorter.
4678 The second and third characters are optional and specific to QCustomPlot:\n
4679 If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
4680 "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
4681 "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
4682 [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
4683 If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
4684 be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
4685 cross and 183 (0xB7) for the dot.
4687 If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
4688 option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4689 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4690 part). To only display the decimal power, set the number precision to zero with \ref
4693 Examples for \a formatCode:
4694 \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
4695 normal scientific format is used
4696 \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
4697 beautifully typeset decimal powers and a dot as multiplication sign
4698 \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
4700 \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
4701 powers. Format code will be reduced to 'f'.
4702 \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
4703 code will not be changed.
4705 void QCPAxis::setNumberFormat(const QString &formatCode)
4707 if (formatCode.isEmpty())
4709 qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
4712 mCachedMarginValid = false;
4714 // interpret first char as number format char:
4715 QString allowedFormatChars(QLatin1String("eEfgG"));
4716 if (allowedFormatChars.contains(formatCode.at(0)))
4718 mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
4721 qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4724 if (formatCode.length() < 2)
4726 mNumberBeautifulPowers = false;
4727 mAxisPainter->numberMultiplyCross = false;
4731 // interpret second char as indicator for beautiful decimal powers:
4732 if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
4734 mNumberBeautifulPowers = true;
4737 qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4740 if (formatCode.length() < 3)
4742 mAxisPainter->numberMultiplyCross = false;
4746 // interpret third char as indicator for dot or cross multiplication symbol:
4747 if (formatCode.at(2) == QLatin1Char('c'))
4749 mAxisPainter->numberMultiplyCross = true;
4750 } else if (formatCode.at(2) == QLatin1Char('d'))
4752 mAxisPainter->numberMultiplyCross = false;
4755 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4761 Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
4762 for details. The effect of precisions are most notably for number Formats starting with 'e', see
4763 \ref setNumberFormat
4765 If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
4766 setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
4767 usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
4768 scaling (the redundant "1 [multiplication sign]" part). To only display the decimal power "10
4769 [superscript] n", set \a precision to zero.
4771 void QCPAxis::setNumberPrecision(int precision)
4773 if (mNumberPrecision != precision)
4775 mNumberPrecision = precision;
4776 mCachedMarginValid = false;
4781 If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
4782 The tick step is the interval between (major) ticks, in plot coordinates.
4783 \see setSubTickCount
4785 void QCPAxis::setTickStep(double step)
4787 if (mTickStep != step)
4790 mCachedMarginValid = false;
4795 If you want full control over what ticks (and possibly labels) the axes show, this function is
4796 used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
4797 the provided tick vector will be overwritten with automatically generated tick coordinates upon
4798 replot. The labels of the ticks can be generated automatically when \ref setAutoTickLabels is
4799 left enabled. If it is disabled, you can set the labels manually with \ref setTickVectorLabels.
4801 \a vec is a vector containing the positions of the ticks, in plot coordinates.
4803 \warning \a vec must be sorted in ascending order, no additional checks are made to ensure this.
4805 \see setTickVectorLabels
4807 void QCPAxis::setTickVector(const QVector<double> &vec)
4809 // don't check whether mTickVector != vec here, because it takes longer than we would save
4811 mCachedMarginValid = false;
4815 If you want full control over what ticks and labels the axes show, this function is used to set a
4816 number of QStrings that will be displayed at the tick positions which you need to provide with
4817 \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
4818 \ref setAutoTicks and \ref setAutoTickLabels first.)
4820 \a vec is a vector containing the labels of the ticks. The entries correspond to the respective
4821 indices in the tick vector, passed via \ref setTickVector.
4825 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4827 // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
4828 mTickVectorLabels = vec;
4829 mCachedMarginValid = false;
4833 Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
4834 plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
4835 zero, the tick labels and axis label will increase their distance to the axis accordingly, so
4836 they won't collide with the ticks.
4838 \see setSubTickLength, setTickLengthIn, setTickLengthOut
4840 void QCPAxis::setTickLength(int inside, int outside)
4842 setTickLengthIn(inside);
4843 setTickLengthOut(outside);
4847 Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
4850 \see setTickLengthOut, setTickLength, setSubTickLength
4852 void QCPAxis::setTickLengthIn(int inside)
4854 if (mAxisPainter->tickLengthIn != inside)
4856 mAxisPainter->tickLengthIn = inside;
4861 Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
4862 outside the plot. If \a outside is greater than zero, the tick labels and axis label will
4863 increase their distance to the axis accordingly, so they won't collide with the ticks.
4865 \see setTickLengthIn, setTickLength, setSubTickLength
4867 void QCPAxis::setTickLengthOut(int outside)
4869 if (mAxisPainter->tickLengthOut != outside)
4871 mAxisPainter->tickLengthOut = outside;
4872 mCachedMarginValid = false; // only outside tick length can change margin
4877 Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
4878 divides the tick intervals in four sub intervals.
4880 By default, the number of sub ticks is chosen automatically in a reasonable manner as long as the
4881 mantissa of the tick step is a multiple of 0.5. When \ref setAutoTickStep is enabled, this is
4884 If you want to disable automatic sub tick count and use this function to set the count manually,
4885 see \ref setAutoSubTicks.
4887 void QCPAxis::setSubTickCount(int count)
4889 mSubTickCount = count;
4893 Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
4894 the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
4895 than zero, the tick labels and axis label will increase their distance to the axis accordingly,
4896 so they won't collide with the ticks.
4898 \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
4900 void QCPAxis::setSubTickLength(int inside, int outside)
4902 setSubTickLengthIn(inside);
4903 setSubTickLengthOut(outside);
4907 Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
4910 \see setSubTickLengthOut, setSubTickLength, setTickLength
4912 void QCPAxis::setSubTickLengthIn(int inside)
4914 if (mAxisPainter->subTickLengthIn != inside)
4916 mAxisPainter->subTickLengthIn = inside;
4921 Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
4922 outside the plot. If \a outside is greater than zero, the tick labels will increase their
4923 distance to the axis accordingly, so they won't collide with the ticks.
4925 \see setSubTickLengthIn, setSubTickLength, setTickLength
4927 void QCPAxis::setSubTickLengthOut(int outside)
4929 if (mAxisPainter->subTickLengthOut != outside)
4931 mAxisPainter->subTickLengthOut = outside;
4932 mCachedMarginValid = false; // only outside tick length can change margin
4937 Sets the pen, the axis base line is drawn with.
4939 \see setTickPen, setSubTickPen
4941 void QCPAxis::setBasePen(const QPen &pen)
4947 Sets the pen, tick marks will be drawn with.
4949 \see setTickLength, setBasePen
4951 void QCPAxis::setTickPen(const QPen &pen)
4957 Sets the pen, subtick marks will be drawn with.
4959 \see setSubTickCount, setSubTickLength, setBasePen
4961 void QCPAxis::setSubTickPen(const QPen &pen)
4967 Sets the font of the axis label.
4971 void QCPAxis::setLabelFont(const QFont &font)
4973 if (mLabelFont != font)
4976 mCachedMarginValid = false;
4981 Sets the color of the axis label.
4985 void QCPAxis::setLabelColor(const QColor &color)
4987 mLabelColor = color;
4991 Sets the text of the axis label that will be shown below/above or next to the axis, depending on
4992 its orientation. To disable axis labels, pass an empty string as \a str.
4994 void QCPAxis::setLabel(const QString &str)
4999 mCachedMarginValid = false;
5004 Sets the distance between the tick labels and the axis label.
5006 \see setTickLabelPadding, setPadding
5008 void QCPAxis::setLabelPadding(int padding)
5010 if (mAxisPainter->labelPadding != padding)
5012 mAxisPainter->labelPadding = padding;
5013 mCachedMarginValid = false;
5018 Sets the padding of the axis.
5020 When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space,
5023 The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled.
5025 \see setLabelPadding, setTickLabelPadding
5027 void QCPAxis::setPadding(int padding)
5029 if (mPadding != padding)
5032 mCachedMarginValid = false;
5037 Sets the offset the axis has to its axis rect side.
5039 If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
5040 only the offset of the inner most axis has meaning (even if it is set to be invisible). The
5041 offset of the other, outer axes is controlled automatically, to place them at appropriate
5044 void QCPAxis::setOffset(int offset)
5046 mAxisPainter->offset = offset;
5050 Sets the font that is used for tick labels when they are selected.
5052 \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5054 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
5056 if (font != mSelectedTickLabelFont)
5058 mSelectedTickLabelFont = font;
5059 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5064 Sets the font that is used for the axis label when it is selected.
5066 \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5068 void QCPAxis::setSelectedLabelFont(const QFont &font)
5070 mSelectedLabelFont = font;
5071 // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
5075 Sets the color that is used for tick labels when they are selected.
5077 \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5079 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
5081 if (color != mSelectedTickLabelColor)
5083 mSelectedTickLabelColor = color;
5088 Sets the color that is used for the axis label when it is selected.
5090 \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5092 void QCPAxis::setSelectedLabelColor(const QColor &color)
5094 mSelectedLabelColor = color;
5098 Sets the pen that is used to draw the axis base line when selected.
5100 \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5102 void QCPAxis::setSelectedBasePen(const QPen &pen)
5104 mSelectedBasePen = pen;
5108 Sets the pen that is used to draw the (major) ticks when selected.
5110 \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5112 void QCPAxis::setSelectedTickPen(const QPen &pen)
5114 mSelectedTickPen = pen;
5118 Sets the pen that is used to draw the subticks when selected.
5120 \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
5122 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
5124 mSelectedSubTickPen = pen;
5128 Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available
5131 For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending.
5132 Note that this meaning does not change when the axis range is reversed with \ref
5137 void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
5139 mAxisPainter->lowerEnding = ending;
5143 Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available
5146 For horizontal axes, this method refers to the right ending, for vertical axes the top ending.
5147 Note that this meaning does not change when the axis range is reversed with \ref
5152 void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
5154 mAxisPainter->upperEnding = ending;
5158 If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
5159 bounds of the range. The range is simply moved by \a diff.
5161 If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
5162 corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
5164 void QCPAxis::moveRange(double diff)
5166 QCPRange oldRange = mRange;
5167 if (mScaleType == stLinear)
5169 mRange.lower += diff;
5170 mRange.upper += diff;
5171 } else // mScaleType == stLogarithmic
5173 mRange.lower *= diff;
5174 mRange.upper *= diff;
5176 mCachedMarginValid = false;
5177 emit rangeChanged(mRange);
5178 emit rangeChanged(mRange, oldRange);
5182 Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
5183 factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
5184 coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
5185 around 1.0 will have moved symmetrically closer to 1.0).
5187 void QCPAxis::scaleRange(double factor, double center)
5189 QCPRange oldRange = mRange;
5190 if (mScaleType == stLinear)
5193 newRange.lower = (mRange.lower-center)*factor + center;
5194 newRange.upper = (mRange.upper-center)*factor + center;
5195 if (QCPRange::validRange(newRange))
5196 mRange = newRange.sanitizedForLinScale();
5197 } else // mScaleType == stLogarithmic
5199 if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
5202 newRange.lower = qPow(mRange.lower/center, factor)*center;
5203 newRange.upper = qPow(mRange.upper/center, factor)*center;
5204 if (QCPRange::validRange(newRange))
5205 mRange = newRange.sanitizedForLogScale();
5207 qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
5209 mCachedMarginValid = false;
5210 emit rangeChanged(mRange);
5211 emit rangeChanged(mRange, oldRange);
5215 Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will
5216 be done around the center of the current axis range.
5218 For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs
5219 plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the
5222 This is an operation that changes the range of this axis once, it doesn't fix the scale ratio
5223 indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent
5224 won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent
5227 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
5229 int otherPixelSize, ownPixelSize;
5231 if (otherAxis->orientation() == Qt::Horizontal)
5232 otherPixelSize = otherAxis->axisRect()->width();
5234 otherPixelSize = otherAxis->axisRect()->height();
5236 if (orientation() == Qt::Horizontal)
5237 ownPixelSize = axisRect()->width();
5239 ownPixelSize = axisRect()->height();
5241 double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
5242 setRange(range().center(), newRangeSize, Qt::AlignCenter);
5246 Changes the axis range such that all plottables associated with this axis are fully visible in
5249 \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
5251 void QCPAxis::rescale(bool onlyVisiblePlottables)
5253 QList<QCPAbstractPlottable*> p = plottables();
5255 bool haveRange = false;
5256 for (int i=0; i<p.size(); ++i)
5258 if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
5260 QCPRange plottableRange;
5261 bool currentFoundRange;
5262 QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
5263 if (mScaleType == stLogarithmic)
5264 signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
5265 if (p.at(i)->keyAxis() == this)
5266 plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
5268 plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
5269 if (currentFoundRange)
5272 newRange = plottableRange;
5274 newRange.expand(plottableRange);
5280 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
5282 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
5283 if (mScaleType == stLinear)
5285 newRange.lower = center-mRange.size()/2.0;
5286 newRange.upper = center+mRange.size()/2.0;
5287 } else // mScaleType == stLogarithmic
5289 newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
5290 newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
5298 Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
5300 double QCPAxis::pixelToCoord(double value) const
5302 if (orientation() == Qt::Horizontal)
5304 if (mScaleType == stLinear)
5306 if (!mRangeReversed)
5307 return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
5309 return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
5310 } else // mScaleType == stLogarithmic
5312 if (!mRangeReversed)
5313 return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
5315 return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
5317 } else // orientation() == Qt::Vertical
5319 if (mScaleType == stLinear)
5321 if (!mRangeReversed)
5322 return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
5324 return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
5325 } else // mScaleType == stLogarithmic
5327 if (!mRangeReversed)
5328 return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
5330 return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
5336 Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
5338 double QCPAxis::coordToPixel(double value) const
5340 if (orientation() == Qt::Horizontal)
5342 if (mScaleType == stLinear)
5344 if (!mRangeReversed)
5345 return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5347 return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
5348 } else // mScaleType == stLogarithmic
5350 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5351 return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
5352 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5353 return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
5356 if (!mRangeReversed)
5357 return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5359 return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
5362 } else // orientation() == Qt::Vertical
5364 if (mScaleType == stLinear)
5366 if (!mRangeReversed)
5367 return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
5369 return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
5370 } else // mScaleType == stLogarithmic
5372 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
5373 return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
5374 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
5375 return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
5378 if (!mRangeReversed)
5379 return mAxisRect->bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5381 return mAxisRect->bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
5388 Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
5389 is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
5390 function does not change the current selection state of the axis.
5392 If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
5394 \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
5396 QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const
5401 if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
5403 else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
5404 return spTickLabels;
5405 else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
5411 /* inherits documentation from base class */
5412 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5414 if (!mParentPlot) return -1;
5415 SelectablePart part = getPartAt(pos);
5416 if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
5420 details->setValue(part);
5421 return mParentPlot->selectionTolerance()*0.99;
5425 Returns a list of all the plottables that have this axis as key or value axis.
5427 If you are only interested in plottables of type QCPGraph, see \ref graphs.
5431 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
5433 QList<QCPAbstractPlottable*> result;
5434 if (!mParentPlot) return result;
5436 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
5438 if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
5439 result.append(mParentPlot->mPlottables.at(i));
5445 Returns a list of all the graphs that have this axis as key or value axis.
5447 \see plottables, items
5449 QList<QCPGraph*> QCPAxis::graphs() const
5451 QList<QCPGraph*> result;
5452 if (!mParentPlot) return result;
5454 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
5456 if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
5457 result.append(mParentPlot->mGraphs.at(i));
5463 Returns a list of all the items that are associated with this axis. An item is considered
5464 associated with an axis if at least one of its positions uses the axis as key or value axis.
5466 \see plottables, graphs
5468 QList<QCPAbstractItem*> QCPAxis::items() const
5470 QList<QCPAbstractItem*> result;
5471 if (!mParentPlot) return result;
5473 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
5475 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
5476 for (int posId=0; posId<positions.size(); ++posId)
5478 if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
5480 result.append(mParentPlot->mItems.at(itemId));
5489 Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to
5490 QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)
5492 QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
5496 case QCP::msLeft: return atLeft;
5497 case QCP::msRight: return atRight;
5498 case QCP::msTop: return atTop;
5499 case QCP::msBottom: return atBottom;
5502 qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
5507 Returns the axis type that describes the opposite axis of an axis with the specified \a type.
5509 QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
5513 case atLeft: return atRight; break;
5514 case atRight: return atLeft; break;
5515 case atBottom: return atTop; break;
5516 case atTop: return atBottom; break;
5517 default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
5523 This function is called to prepare the tick vector, sub tick vector and tick label vector. If
5524 \ref setAutoTicks is set to true, appropriate tick values are determined automatically via \ref
5525 generateAutoTicks. If it's set to false, the signal ticksRequest is emitted, which can be used to
5526 provide external tick positions. Then the sub tick vectors and tick label vectors are created.
5528 void QCPAxis::setupTickVectors()
5530 if (!mParentPlot) return;
5531 if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
5533 // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
5536 generateAutoTicks();
5539 emit ticksRequest();
5542 visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
5543 if (mTickVector.isEmpty())
5545 mSubTickVector.clear();
5549 // generate subticks between ticks:
5550 mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
5551 if (mSubTickCount > 0)
5553 double subTickStep = 0;
5554 double subTickPosition = 0;
5555 int subTickIndex = 0;
5557 int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick-1 : mLowestVisibleTick;
5558 int highTick = mHighestVisibleTick < mTickVector.size()-1 ? mHighestVisibleTick+1 : mHighestVisibleTick;
5559 for (int i=lowTick+1; i<=highTick; ++i)
5561 subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
5562 for (int k=1; k<=mSubTickCount; ++k)
5564 subTickPosition = mTickVector.at(i-1) + k*subTickStep;
5565 if (subTickPosition < mRange.lower)
5567 if (subTickPosition > mRange.upper)
5572 mSubTickVector[subTickIndex] = subTickPosition;
5577 mSubTickVector.resize(subTickIndex);
5580 // generate tick labels according to tick positions:
5581 if (mAutoTickLabels)
5583 int vecsize = mTickVector.size();
5584 mTickVectorLabels.resize(vecsize);
5585 if (mTickLabelType == ltNumber)
5587 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5588 mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
5589 } else if (mTickLabelType == ltDateTime)
5591 for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
5593 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
5594 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5596 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
5600 } else // mAutoTickLabels == false
5602 if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
5604 emit ticksRequest();
5606 // make sure provided tick label vector has correct (minimal) length:
5607 if (mTickVectorLabels.size() < mTickVector.size())
5608 mTickVectorLabels.resize(mTickVector.size());
5614 If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
5615 generate reasonable tick positions (and subtick count). The algorithm tries to create
5616 approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount).
5618 If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is generated at every
5619 power of the current logarithm base, set via \ref setScaleLogBase.
5621 void QCPAxis::generateAutoTicks()
5623 if (mScaleType == stLinear)
5627 // Generate tick positions according to linear scaling:
5628 mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
5629 double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5630 double tickStepMantissa = mTickStep/magnitudeFactor;
5631 if (tickStepMantissa < 5)
5633 // round digit after decimal point to 0.5
5634 mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
5637 // round to first digit in multiples of 2
5638 mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
5642 mSubTickCount = calculateAutoSubTickCount(mTickStep);
5643 // Generate tick positions according to mTickStep:
5644 qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
5645 qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
5646 int tickcount = lastStep-firstStep+1;
5647 if (tickcount < 0) tickcount = 0;
5648 mTickVector.resize(tickcount);
5649 for (int i=0; i<tickcount; ++i)
5650 mTickVector[i] = (firstStep+i)*mTickStep;
5651 } else // mScaleType == stLogarithmic
5653 // Generate tick positions according to logbase scaling:
5654 if (mRange.lower > 0 && mRange.upper > 0) // positive range
5656 double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
5657 double currentMag = lowerMag;
5658 mTickVector.clear();
5659 mTickVector.append(currentMag);
5660 while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5662 currentMag *= mScaleLogBase;
5663 mTickVector.append(currentMag);
5665 } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
5667 double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
5668 double currentMag = lowerMag;
5669 mTickVector.clear();
5670 mTickVector.append(currentMag);
5671 while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
5673 currentMag /= mScaleLogBase;
5674 mTickVector.append(currentMag);
5676 } else // invalid range for logarithmic scale, because lower and upper have different sign
5678 mTickVector.clear();
5679 qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
5686 Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
5687 tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
5688 Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
5689 because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667. Note
5690 that a subtick count of 4 means dividing the major tick step into 5 sections.
5692 This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
5693 with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
5694 fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
5697 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
5699 int result = mSubTickCount; // default to current setting, if no proper value can be found
5701 // get mantissa of tickstep:
5702 double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
5703 double tickStepMantissa = tickStep/magnitudeFactor;
5705 // separate integer and fractional part of mantissa:
5706 double epsilon = 0.01;
5709 double fracPart = modf(tickStepMantissa, &intPartf);
5712 // handle cases with (almost) integer mantissa:
5713 if (fracPart < epsilon || 1.0-fracPart < epsilon)
5715 if (1.0-fracPart < epsilon)
5719 case 1: result = 4; break; // 1.0 -> 0.2 substep
5720 case 2: result = 3; break; // 2.0 -> 0.5 substep
5721 case 3: result = 2; break; // 3.0 -> 1.0 substep
5722 case 4: result = 3; break; // 4.0 -> 1.0 substep
5723 case 5: result = 4; break; // 5.0 -> 1.0 substep
5724 case 6: result = 2; break; // 6.0 -> 2.0 substep
5725 case 7: result = 6; break; // 7.0 -> 1.0 substep
5726 case 8: result = 3; break; // 8.0 -> 2.0 substep
5727 case 9: result = 2; break; // 9.0 -> 3.0 substep
5731 // handle cases with significantly fractional mantissa:
5732 if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5736 case 1: result = 2; break; // 1.5 -> 0.5 substep
5737 case 2: result = 4; break; // 2.5 -> 0.5 substep
5738 case 3: result = 4; break; // 3.5 -> 0.7 substep
5739 case 4: result = 2; break; // 4.5 -> 1.5 substep
5740 case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
5741 case 6: result = 4; break; // 6.5 -> 1.3 substep
5742 case 7: result = 2; break; // 7.5 -> 2.5 substep
5743 case 8: result = 4; break; // 8.5 -> 1.7 substep
5744 case 9: result = 4; break; // 9.5 -> 1.9 substep
5747 // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5753 /* inherits documentation from base class */
5754 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
5757 SelectablePart part = details.value<SelectablePart>();
5758 if (mSelectableParts.testFlag(part))
5760 SelectableParts selBefore = mSelectedParts;
5761 setSelectedParts(additive ? mSelectedParts^part : part);
5762 if (selectionStateChanged)
5763 *selectionStateChanged = mSelectedParts != selBefore;
5767 /* inherits documentation from base class */
5768 void QCPAxis::deselectEvent(bool *selectionStateChanged)
5770 SelectableParts selBefore = mSelectedParts;
5771 setSelectedParts(mSelectedParts & ~mSelectableParts);
5772 if (selectionStateChanged)
5773 *selectionStateChanged = mSelectedParts != selBefore;
5778 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
5779 before drawing axis lines.
5781 This is the antialiasing state the painter passed to the \ref draw method is in by default.
5783 This function takes into account the local setting of the antialiasing flag as well as the
5784 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
5785 QCustomPlot::setNotAntialiasedElements.
5789 void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
5791 applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
5796 Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
5799 void QCPAxis::draw(QCPPainter *painter)
5801 const int lowTick = mLowestVisibleTick;
5802 const int highTick = mHighestVisibleTick;
5803 QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5804 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
5805 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
5806 tickPositions.reserve(highTick-lowTick+1);
5807 tickLabels.reserve(highTick-lowTick+1);
5808 subTickPositions.reserve(mSubTickVector.size());
5812 for (int i=lowTick; i<=highTick; ++i)
5814 tickPositions.append(coordToPixel(mTickVector.at(i)));
5816 tickLabels.append(mTickVectorLabels.at(i));
5819 if (mSubTickCount > 0)
5821 const int subTickCount = mSubTickVector.size();
5822 for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
5823 subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
5826 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
5827 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
5828 mAxisPainter->type = mAxisType;
5829 mAxisPainter->basePen = getBasePen();
5830 mAxisPainter->labelFont = getLabelFont();
5831 mAxisPainter->labelColor = getLabelColor();
5832 mAxisPainter->label = mLabel;
5833 mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber;
5834 mAxisPainter->tickPen = getTickPen();
5835 mAxisPainter->subTickPen = getSubTickPen();
5836 mAxisPainter->tickLabelFont = getTickLabelFont();
5837 mAxisPainter->tickLabelColor = getTickLabelColor();
5838 mAxisPainter->axisRect = mAxisRect->rect();
5839 mAxisPainter->viewportRect = mParentPlot->viewport();
5840 mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
5841 mAxisPainter->reversedEndings = mRangeReversed;
5842 mAxisPainter->tickPositions = tickPositions;
5843 mAxisPainter->tickLabels = tickLabels;
5844 mAxisPainter->subTickPositions = subTickPositions;
5845 mAxisPainter->draw(painter);
5850 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
5851 the current range. The return values are indices of the tick vector, not the positions of the
5854 The actual use of this function is when an external tick vector is provided, since it might
5855 exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
5858 If all ticks are outside the axis range, an inverted range is returned, i.e. highIndex will be
5859 smaller than lowIndex. There is one case, where this function returns indices that are not really
5860 visible in the current axis range: When the tick spacing is larger than the axis range size and
5861 one tick is below the axis range and the next tick is already above the axis range. Because in
5862 such cases it is usually desirable to know the tick pair, to draw proper subticks.
5864 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5866 bool lowFound = false;
5867 bool highFound = false;
5871 for (int i=0; i < mTickVector.size(); ++i)
5873 if (mTickVector.at(i) >= mRange.lower)
5880 for (int i=mTickVector.size()-1; i >= 0; --i)
5882 if (mTickVector.at(i) <= mRange.upper)
5890 if (!lowFound && highFound)
5891 lowIndex = highIndex+1;
5892 else if (lowFound && !highFound)
5893 highIndex = lowIndex-1;
5898 A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
5899 scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
5900 This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
5902 \see basePow, setScaleLogBase, setScaleType
5904 double QCPAxis::baseLog(double value) const
5906 return qLn(value)*mScaleLogBaseLogInv;
5911 A power function with the base mScaleLogBase, used mostly for coordinate transforms in
5912 logarithmic scales with arbitrary log base.
5914 \see baseLog, setScaleLogBase, setScaleType
5916 double QCPAxis::basePow(double value) const
5918 return qPow(mScaleLogBase, value);
5923 Returns the pen that is used to draw the axis base line. Depending on the selection state, this
5924 is either mSelectedBasePen or mBasePen.
5926 QPen QCPAxis::getBasePen() const
5928 return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5933 Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
5934 is either mSelectedTickPen or mTickPen.
5936 QPen QCPAxis::getTickPen() const
5938 return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5943 Returns the pen that is used to draw the subticks. Depending on the selection state, this
5944 is either mSelectedSubTickPen or mSubTickPen.
5946 QPen QCPAxis::getSubTickPen() const
5948 return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
5953 Returns the font that is used to draw the tick labels. Depending on the selection state, this
5954 is either mSelectedTickLabelFont or mTickLabelFont.
5956 QFont QCPAxis::getTickLabelFont() const
5958 return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
5963 Returns the font that is used to draw the axis label. Depending on the selection state, this
5964 is either mSelectedLabelFont or mLabelFont.
5966 QFont QCPAxis::getLabelFont() const
5968 return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
5973 Returns the color that is used to draw the tick labels. Depending on the selection state, this
5974 is either mSelectedTickLabelColor or mTickLabelColor.
5976 QColor QCPAxis::getTickLabelColor() const
5978 return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
5983 Returns the color that is used to draw the axis label. Depending on the selection state, this
5984 is either mSelectedLabelColor or mLabelColor.
5986 QColor QCPAxis::getLabelColor() const
5988 return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
5993 Returns the appropriate outward margin for this axis. It is needed if \ref
5994 QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref
5995 atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom
5996 margin and so forth. For the calculation, this function goes through similar steps as \ref draw,
5997 so changing one function likely requires the modification of the other one as well.
5999 The margin consists of the outward tick length, tick label padding, tick label size, label
6000 padding, label size, and padding.
6002 The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc.
6003 unchanged are very fast.
6005 int QCPAxis::calculateMargin()
6007 if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
6010 if (mCachedMarginValid)
6011 return mCachedMargin;
6013 // run through similar steps as QCPAxis::draw, and calculate margin needed to fit axis and its labels
6016 int lowTick, highTick;
6017 visibleTickBounds(lowTick, highTick);
6018 QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
6019 QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
6020 tickPositions.reserve(highTick-lowTick+1);
6021 tickLabels.reserve(highTick-lowTick+1);
6024 for (int i=lowTick; i<=highTick; ++i)
6026 tickPositions.append(coordToPixel(mTickVector.at(i)));
6028 tickLabels.append(mTickVectorLabels.at(i));
6031 // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
6032 // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
6033 mAxisPainter->type = mAxisType;
6034 mAxisPainter->labelFont = getLabelFont();
6035 mAxisPainter->label = mLabel;
6036 mAxisPainter->tickLabelFont = mTickLabelFont;
6037 mAxisPainter->axisRect = mAxisRect->rect();
6038 mAxisPainter->viewportRect = mParentPlot->viewport();
6039 mAxisPainter->tickPositions = tickPositions;
6040 mAxisPainter->tickLabels = tickLabels;
6041 margin += mAxisPainter->size();
6044 mCachedMargin = margin;
6045 mCachedMarginValid = true;
6049 /* inherits documentation from base class */
6050 QCP::Interaction QCPAxis::selectionCategory() const
6052 return QCP::iSelectAxes;
6056 ////////////////////////////////////////////////////////////////////////////////////////////////////
6057 //////////////////// QCPAxisPainterPrivate
6058 ////////////////////////////////////////////////////////////////////////////////////////////////////
6060 /*! \class QCPAxisPainterPrivate
6065 This is a private class and not part of the public QCustomPlot interface.
6067 It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
6068 axis label. It also buffers the labels to reduce replot times. The parameters are configured by
6069 directly accessing the public member variables.
6073 Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
6074 redraw, to utilize the caching mechanisms.
6076 QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
6077 type(QCPAxis::atLeft),
6078 basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6079 lowerEnding(QCPLineEnding::esNone),
6080 upperEnding(QCPLineEnding::esNone),
6082 tickLabelPadding(0),
6083 tickLabelRotation(0),
6084 tickLabelSide(QCPAxis::lsOutside),
6085 substituteExponent(true),
6086 numberMultiplyCross(false),
6090 subTickLengthOut(0),
6091 tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6092 subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
6094 abbreviateDecimalPowers(false),
6095 reversedEndings(false),
6096 mParentPlot(parentPlot),
6097 mLabelCache(16) // cache at most 16 (tick) labels
6101 QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
6107 Draws the axis with the specified \a painter.
6109 The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
6112 void QCPAxisPainterPrivate::draw(QCPPainter *painter)
6114 QByteArray newHash = generateLabelParameterHash();
6115 if (newHash != mLabelParameterHash)
6117 mLabelCache.clear();
6118 mLabelParameterHash = newHash;
6124 case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
6125 case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
6126 case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
6127 case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
6130 double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
6133 case QCPAxis::atTop: yCor = -1; break;
6134 case QCPAxis::atRight: xCor = 1; break;
6141 painter->setPen(basePen);
6142 if (QCPAxis::orientation(type) == Qt::Horizontal)
6143 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
6145 baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
6146 if (reversedEndings)
6147 baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
6148 painter->drawLine(baseLine);
6151 if (!tickPositions.isEmpty())
6153 painter->setPen(tickPen);
6154 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
6155 if (QCPAxis::orientation(type) == Qt::Horizontal)
6157 for (int i=0; i<tickPositions.size(); ++i)
6158 painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
6161 for (int i=0; i<tickPositions.size(); ++i)
6162 painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
6167 if (!subTickPositions.isEmpty())
6169 painter->setPen(subTickPen);
6170 // direction of ticks ("inward" is right for left axis and left for right axis)
6171 int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
6172 if (QCPAxis::orientation(type) == Qt::Horizontal)
6174 for (int i=0; i<subTickPositions.size(); ++i)
6175 painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
6178 for (int i=0; i<subTickPositions.size(); ++i)
6179 painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
6182 margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6184 // draw axis base endings:
6185 bool antialiasingBackup = painter->antialiasing();
6186 painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
6187 painter->setBrush(QBrush(basePen.color()));
6188 QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
6189 if (lowerEnding.style() != QCPLineEnding::esNone)
6190 lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
6191 if (upperEnding.style() != QCPLineEnding::esNone)
6192 upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
6193 painter->setAntialiasing(antialiasingBackup);
6197 if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
6199 oldClipRect = painter->clipRegion().boundingRect();
6200 painter->setClipRect(axisRect);
6202 QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
6203 if (!tickLabels.isEmpty())
6205 if (tickLabelSide == QCPAxis::lsOutside)
6206 margin += tickLabelPadding;
6207 painter->setFont(tickLabelFont);
6208 painter->setPen(QPen(tickLabelColor));
6209 const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
6210 int distanceToAxis = margin;
6211 if (tickLabelSide == QCPAxis::lsInside)
6212 distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6213 for (int i=0; i<maxLabelIndex; ++i)
6214 placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
6215 if (tickLabelSide == QCPAxis::lsOutside)
6216 margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
6218 if (tickLabelSide == QCPAxis::lsInside)
6219 painter->setClipRect(oldClipRect);
6223 if (!label.isEmpty())
6225 margin += labelPadding;
6226 painter->setFont(labelFont);
6227 painter->setPen(QPen(labelColor));
6228 labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
6229 if (type == QCPAxis::atLeft)
6231 QTransform oldTransform = painter->transform();
6232 painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
6233 painter->rotate(-90);
6234 painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6235 painter->setTransform(oldTransform);
6237 else if (type == QCPAxis::atRight)
6239 QTransform oldTransform = painter->transform();
6240 painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
6241 painter->rotate(90);
6242 painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6243 painter->setTransform(oldTransform);
6245 else if (type == QCPAxis::atTop)
6246 painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6247 else if (type == QCPAxis::atBottom)
6248 painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
6251 // set selection boxes:
6252 int selectionTolerance = 0;
6254 selectionTolerance = mParentPlot->selectionTolerance();
6256 qDebug() << Q_FUNC_INFO << "mParentPlot is null";
6257 int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
6258 int selAxisInSize = selectionTolerance;
6259 int selTickLabelSize;
6260 int selTickLabelOffset;
6261 if (tickLabelSide == QCPAxis::lsOutside)
6263 selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6264 selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
6267 selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
6268 selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
6270 int selLabelSize = labelBounds.height();
6271 int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
6272 if (type == QCPAxis::atLeft)
6274 mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
6275 mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
6276 mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
6277 } else if (type == QCPAxis::atRight)
6279 mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
6280 mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
6281 mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
6282 } else if (type == QCPAxis::atTop)
6284 mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
6285 mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
6286 mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
6287 } else if (type == QCPAxis::atBottom)
6289 mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
6290 mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
6291 mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
6293 mAxisSelectionBox = mAxisSelectionBox.normalized();
6294 mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
6295 mLabelSelectionBox = mLabelSelectionBox.normalized();
6296 // draw hitboxes for debug purposes:
6297 //painter->setBrush(Qt::NoBrush);
6298 //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
6303 Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
6304 direction) needed to fit the axis.
6306 int QCPAxisPainterPrivate::size() const
6310 // get length of tick marks pointing outwards:
6311 if (!tickPositions.isEmpty())
6312 result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
6314 // calculate size of tick labels:
6315 if (tickLabelSide == QCPAxis::lsOutside)
6317 QSize tickLabelsSize(0, 0);
6318 if (!tickLabels.isEmpty())
6320 for (int i=0; i<tickLabels.size(); ++i)
6321 getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
6322 result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
6323 result += tickLabelPadding;
6327 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
6328 if (!label.isEmpty())
6330 QFontMetrics fontMetrics(labelFont);
6332 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
6333 result += bounds.height() + labelPadding;
6341 Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
6342 method is called automatically in \ref draw, if any parameters have changed that invalidate the
6343 cached labels, such as font, color, etc.
6345 void QCPAxisPainterPrivate::clearCache()
6347 mLabelCache.clear();
6352 Returns a hash that allows uniquely identifying whether the label parameters have changed such
6353 that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
6354 return value of this method hasn't changed since the last redraw, the respective label parameters
6355 haven't changed and cached labels may be used.
6357 QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
6360 result.append(QByteArray::number(tickLabelRotation));
6361 result.append(QByteArray::number((int)tickLabelSide));
6362 result.append(QByteArray::number((int)substituteExponent));
6363 result.append(QByteArray::number((int)numberMultiplyCross));
6364 result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
6365 result.append(tickLabelFont.toString().toLatin1());
6371 Draws a single tick label with the provided \a painter, utilizing the internal label cache to
6372 significantly speed up drawing of labels that were drawn in previous calls. The tick label is
6373 always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
6374 pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
6375 for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
6376 at which the label should be drawn.
6378 In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
6379 largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
6380 drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
6381 tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
6384 The label is drawn with the font and pen that are currently set on the \a painter. To draw
6385 superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
6388 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
6390 // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
6391 if (text.isEmpty()) return;
6393 QPointF labelAnchor;
6396 case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
6397 case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
6398 case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
6399 case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
6401 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
6403 if (!mLabelCache.contains(text)) // no cached label exists, create it
6405 CachedLabel *newCachedLabel = new CachedLabel;
6406 TickLabelData labelData = getTickLabelData(painter->font(), text);
6407 newCachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
6408 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
6409 QSize clSize = labelData.rotatedTotalBounds.size();
6410 clSize *= painter->device()->devicePixelRatio();
6411 newCachedLabel->pixmap = QPixmap(clSize);
6412 newCachedLabel->pixmap.setDevicePixelRatio(painter->device()->devicePixelRatio());
6414 newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
6416 newCachedLabel->pixmap.fill(Qt::transparent);
6417 QCPPainter cachePainter(&newCachedLabel->pixmap);
6418 cachePainter.setPen(painter->pen());
6419 drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
6420 mLabelCache.insert(text, newCachedLabel, 1);
6422 // draw cached label:
6423 const CachedLabel *cachedLabel = mLabelCache.object(text);
6424 // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6425 if (tickLabelSide == QCPAxis::lsOutside)
6427 if (QCPAxis::orientation(type) == Qt::Horizontal)
6429 if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() ||
6430 labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left())
6434 if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() >viewportRect.bottom() ||
6435 labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top())
6439 painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
6440 finalSize = cachedLabel->pixmap.size();
6441 } else // label caching disabled, draw text directly on surface:
6443 TickLabelData labelData = getTickLabelData(painter->font(), text);
6444 QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
6445 // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
6446 if (tickLabelSide == QCPAxis::lsOutside)
6448 if (QCPAxis::orientation(type) == Qt::Horizontal)
6450 if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() ||
6451 finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left())
6455 if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() ||
6456 finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top())
6460 drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
6461 finalSize = labelData.rotatedTotalBounds.size();
6464 // expand passed tickLabelsSize if current tick label is larger:
6465 if (finalSize.width() > tickLabelsSize->width())
6466 tickLabelsSize->setWidth(finalSize.width());
6467 if (finalSize.height() > tickLabelsSize->height())
6468 tickLabelsSize->setHeight(finalSize.height());
6473 This is a \ref placeTickLabel helper function.
6475 Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
6476 y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
6477 directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
6478 QCP::phCacheLabels plotting hint is not set.
6480 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
6482 // backup painter settings that we're about to change:
6483 QTransform oldTransform = painter->transform();
6484 QFont oldFont = painter->font();
6486 // transform painter to position/rotation:
6487 painter->translate(x, y);
6488 if (!qFuzzyIsNull(tickLabelRotation))
6489 painter->rotate(tickLabelRotation);
6492 if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
6494 painter->setFont(labelData.baseFont);
6495 painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
6496 painter->setFont(labelData.expFont);
6497 painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
6500 painter->setFont(labelData.baseFont);
6501 painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
6504 // reset painter settings to what it was before:
6505 painter->setTransform(oldTransform);
6506 painter->setFont(oldFont);
6511 This is a \ref placeTickLabel helper function.
6513 Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
6514 processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
6515 exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
6517 QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
6519 TickLabelData result;
6521 // determine whether beautiful decimal powers should be used
6522 bool useBeautifulPowers = false;
6524 if (substituteExponent)
6526 ePos = text.indexOf(QLatin1Char('e'));
6528 useBeautifulPowers = true;
6531 // calculate text bounding rects and do string preparation for beautiful decimal powers:
6532 result.baseFont = font;
6533 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
6534 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
6535 if (useBeautifulPowers)
6537 // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
6538 result.basePart = text.left(ePos);
6539 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
6540 if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
6541 result.basePart = QLatin1String("10");
6543 result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
6544 result.expPart = text.mid(ePos+1);
6545 // clip "+" and leading zeros off expPart:
6546 while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
6547 result.expPart.remove(1, 1);
6548 if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
6549 result.expPart.remove(0, 1);
6550 // prepare smaller font for exponent:
6551 result.expFont = font;
6552 result.expFont.setPointSize(result.expFont.pointSize()*0.75);
6553 // calculate bounding rects of base part, exponent part and total one:
6554 result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
6555 result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
6556 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
6557 } else // useBeautifulPowers == false
6559 result.basePart = text;
6560 result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
6562 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
6564 // calculate possibly different bounding rect after rotation:
6565 result.rotatedTotalBounds = result.totalBounds;
6566 if (!qFuzzyIsNull(tickLabelRotation))
6568 QTransform transform;
6569 transform.rotate(tickLabelRotation);
6570 result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
6578 This is a \ref placeTickLabel helper function.
6580 Calculates the offset at which the top left corner of the specified tick label shall be drawn.
6581 The offset is relative to a point right next to the tick the label belongs to.
6583 This function is thus responsible for e.g. centering tick labels under ticks and positioning them
6584 appropriately when they are rotated.
6586 QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
6589 calculate label offset from base point at tick (non-trivial, for best visual appearance): short
6590 explanation for bottom axis: The anchor, i.e. the point in the label that is placed
6591 horizontally under the corresponding tick is always on the label side that is closer to the
6592 axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
6593 is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
6594 will be centered under the tick (i.e. displaced horizontally by half its height). At the same
6595 time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
6598 bool doRotation = !qFuzzyIsNull(tickLabelRotation);
6599 bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
6600 double radians = tickLabelRotation/180.0*M_PI;
6602 if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
6606 if (tickLabelRotation > 0)
6608 x = -qCos(radians)*labelData.totalBounds.width();
6609 y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
6612 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
6613 y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
6617 x = -labelData.totalBounds.width();
6618 y = -labelData.totalBounds.height()/2.0;
6620 } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
6624 if (tickLabelRotation > 0)
6626 x = +qSin(radians)*labelData.totalBounds.height();
6627 y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
6631 y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
6636 y = -labelData.totalBounds.height()/2.0;
6638 } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
6642 if (tickLabelRotation > 0)
6644 x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
6645 y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
6648 x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
6649 y = -qCos(-radians)*labelData.totalBounds.height();
6653 x = -labelData.totalBounds.width()/2.0;
6654 y = -labelData.totalBounds.height();
6656 } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
6660 if (tickLabelRotation > 0)
6662 x = +qSin(radians)*labelData.totalBounds.height()/2.0;
6666 x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
6667 y = +qSin(-radians)*labelData.totalBounds.width();
6671 x = -labelData.totalBounds.width()/2.0;
6676 return QPointF(x, y);
6681 Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
6682 to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
6683 margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
6684 smaller width/height.
6686 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
6688 // note: this function must return the same tick label sizes as the placeTickLabel function.
6690 if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
6692 const CachedLabel *cachedLabel = mLabelCache.object(text);
6693 finalSize = cachedLabel->pixmap.size();
6694 } else // label caching disabled or no label with this text cached:
6696 TickLabelData labelData = getTickLabelData(font, text);
6697 finalSize = labelData.rotatedTotalBounds.size();
6700 // expand passed tickLabelsSize if current tick label is larger:
6701 if (finalSize.width() > tickLabelsSize->width())
6702 tickLabelsSize->setWidth(finalSize.width());
6703 if (finalSize.height() > tickLabelsSize->height())
6704 tickLabelsSize->setHeight(finalSize.height());
6708 ////////////////////////////////////////////////////////////////////////////////////////////////////
6709 //////////////////// QCPAbstractPlottable
6710 ////////////////////////////////////////////////////////////////////////////////////////////////////
6712 /*! \class QCPAbstractPlottable
6713 \brief The abstract base class for all data representing objects in a plot.
6715 It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
6716 abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to
6717 create new ways of displaying data (see "Creating own plottables" below).
6719 All further specifics are in the subclasses, for example:
6720 \li A normal graph with possibly a line, scatter points and error bars: \ref QCPGraph
6721 (typically created with \ref QCustomPlot::addGraph)
6722 \li A parametric curve: \ref QCPCurve
6723 \li A bar chart: \ref QCPBars
6724 \li A statistical box plot: \ref QCPStatisticalBox
6725 \li A color encoded two-dimensional map: \ref QCPColorMap
6726 \li An OHLC/Candlestick chart: \ref QCPFinancial
6728 \section plottables-subclassing Creating own plottables
6730 To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
6731 virtual functions, you must implement:
6735 \li \ref drawLegendIcon
6736 \li \ref getKeyRange
6737 \li \ref getValueRange
6739 See the documentation of those functions for what they need to do.
6741 For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
6742 coordinates to pixel coordinates. This function is quite convenient, because it takes the
6743 orientation of the key and value axes into account for you (x and y are swapped when the key axis
6744 is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
6745 to translate many points in a loop like QCPGraph), you can directly use \ref
6746 QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
6749 Here are some important members you inherit from QCPAbstractPlottable:
6752 <td>QCustomPlot *\b mParentPlot</td>
6753 <td>A pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.</td>
6755 <td>QString \b mName</td>
6756 <td>The name of the plottable.</td>
6758 <td>QPen \b mPen</td>
6759 <td>The generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable (e.g QCPGraph uses this pen for its graph lines and scatters)</td>
6761 <td>QPen \b mSelectedPen</td>
6762 <td>The generic pen that should be used when the plottable is selected (hint: \ref mainPen gives you the right pen, depending on selection state).</td>
6764 <td>QBrush \b mBrush</td>
6765 <td>The generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable (e.g. QCPGraph uses this brush to control filling under the graph)</td>
6767 <td>QBrush \b mSelectedBrush</td>
6768 <td>The generic brush that should be used when the plottable is selected (hint: \ref mainBrush gives you the right brush, depending on selection state).</td>
6770 <td>QPointer<QCPAxis>\b mKeyAxis, \b mValueAxis</td>
6771 <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates to pixels in either the key or value dimension.
6772 Make sure to check whether the pointer is null before using it. If one of the axes is null, don't draw the plottable.</td>
6774 <td>bool \b mSelected</td>
6775 <td>indicates whether the plottable is selected or not.</td>
6780 /* start of documentation of pure virtual functions */
6782 /*! \fn void QCPAbstractPlottable::clearData() = 0
6783 Clears all data in the plottable.
6786 /*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
6789 called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
6790 of this plottable inside \a rect, next to the plottable name.
6793 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
6796 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
6797 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6798 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6799 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6800 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6801 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6802 use the returned range (e.g. no points in data).
6804 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6805 this function may have size zero, which wouldn't count as a valid range.
6807 \see rescaleAxes, getValueRange
6810 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
6813 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
6814 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
6815 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
6816 to \ref sdNegative and all positive points will be ignored for range calculation. For no
6817 restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
6818 parameter that indicates whether a range could be found or not. If this is false, you shouldn't
6819 use the returned range (e.g. no points in data).
6821 Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
6822 this function may have size zero, which wouldn't count as a valid range.
6824 \see rescaleAxes, getKeyRange
6827 /* end of documentation of pure virtual functions */
6828 /* start of documentation of signals */
6830 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
6832 This signal is emitted when the selection state of this plottable has changed, either by user
6833 interaction or by a direct call to \ref setSelected.
6836 /*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
6838 This signal is emitted when the selectability of this plottable has changed.
6843 /* end of documentation of signals */
6846 Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
6847 its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
6848 and have perpendicular orientations. If either of these restrictions is violated, a corresponding
6849 message is printed to the debug output (qDebug), the construction is not aborted, though.
6851 Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables,
6852 it can't be directly instantiated.
6854 You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead.
6856 QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
6857 QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
6859 mAntialiasedFill(true),
6860 mAntialiasedScatters(true),
6861 mAntialiasedErrorBars(false),
6863 mSelectedPen(Qt::black),
6864 mBrush(Qt::NoBrush),
6865 mSelectedBrush(Qt::NoBrush),
6867 mValueAxis(valueAxis),
6871 if (keyAxis->parentPlot() != valueAxis->parentPlot())
6872 qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
6873 if (keyAxis->orientation() == valueAxis->orientation())
6874 qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
6878 The name is the textual representation of this plottable as it is displayed in the legend
6879 (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
6881 void QCPAbstractPlottable::setName(const QString &name)
6887 Sets whether fills of this plottable is drawn antialiased or not.
6889 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6890 QCustomPlot::setNotAntialiasedElements.
6892 void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
6894 mAntialiasedFill = enabled;
6898 Sets whether the scatter symbols of this plottable are drawn antialiased or not.
6900 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6901 QCustomPlot::setNotAntialiasedElements.
6903 void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
6905 mAntialiasedScatters = enabled;
6909 Sets whether the error bars of this plottable are drawn antialiased or not.
6911 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
6912 QCustomPlot::setNotAntialiasedElements.
6914 void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
6916 mAntialiasedErrorBars = enabled;
6921 The pen is used to draw basic lines that make up the plottable representation in the
6924 For example, the \ref QCPGraph subclass draws its graph lines and scatter points
6929 void QCPAbstractPlottable::setPen(const QPen &pen)
6935 When the plottable is selected, this pen is used to draw basic lines instead of the normal
6936 pen set via \ref setPen.
6938 \see setSelected, setSelectable, setSelectedBrush, selectTest
6940 void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
6946 The brush is used to draw basic fills of the plottable representation in the
6947 plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
6949 For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
6950 it's not set to Qt::NoBrush.
6954 void QCPAbstractPlottable::setBrush(const QBrush &brush)
6960 When the plottable is selected, this brush is used to draw fills instead of the normal
6961 brush set via \ref setBrush.
6963 \see setSelected, setSelectable, setSelectedPen, selectTest
6965 void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
6967 mSelectedBrush = brush;
6971 The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
6972 to the plottable's value axis. This function performs no checks to make sure this is the case.
6973 The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
6974 y-axis (QCustomPlot::yAxis) as value axis.
6976 Normally, the key and value axes are set in the constructor of the plottable (or \ref
6977 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6981 void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
6987 The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
6988 orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
6989 case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
6990 the y-axis (QCustomPlot::yAxis) as value axis.
6992 Normally, the key and value axes are set in the constructor of the plottable (or \ref
6993 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
6997 void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
7003 Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
7004 (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
7006 However, even when \a selectable was set to false, it is possible to set the selection manually,
7007 by calling \ref setSelected directly.
7011 void QCPAbstractPlottable::setSelectable(bool selectable)
7013 if (mSelectable != selectable)
7015 mSelectable = selectable;
7016 emit selectableChanged(mSelectable);
7021 Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
7022 to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
7024 The entire selection mechanism for plottables is handled automatically when \ref
7025 QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
7026 you wish to change the selection state manually.
7028 This function can change the selection state even when \ref setSelectable was set to false.
7030 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
7032 \see setSelectable, selectTest
7034 void QCPAbstractPlottable::setSelected(bool selected)
7036 if (mSelected != selected)
7038 mSelected = selected;
7039 emit selectionChanged(mSelected);
7044 Rescales the key and value axes associated with this plottable to contain all displayed data, so
7045 the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
7046 sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
7047 Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
7048 outside of that domain.
7050 \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
7051 multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
7052 \a onlyEnlarge set to false (the default), and all subsequent set to true.
7054 \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale
7056 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
7058 rescaleKeyAxis(onlyEnlarge);
7059 rescaleValueAxis(onlyEnlarge);
7063 Rescales the key axis of the plottable so the whole plottable is visible.
7065 See \ref rescaleAxes for detailed behaviour.
7067 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
7069 QCPAxis *keyAxis = mKeyAxis.data();
7070 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
7072 SignDomain signDomain = sdBoth;
7073 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
7074 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
7077 QCPRange newRange = getKeyRange(foundRange, signDomain);
7081 newRange.expand(keyAxis->range());
7082 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7084 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
7085 if (keyAxis->scaleType() == QCPAxis::stLinear)
7087 newRange.lower = center-keyAxis->range().size()/2.0;
7088 newRange.upper = center+keyAxis->range().size()/2.0;
7089 } else // scaleType() == stLogarithmic
7091 newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7092 newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
7095 keyAxis->setRange(newRange);
7100 Rescales the value axis of the plottable so the whole plottable is visible.
7102 Returns true if the axis was actually scaled. This might not be the case if this plottable has an
7103 invalid range, e.g. because it has no data points.
7105 See \ref rescaleAxes for detailed behaviour.
7107 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
7109 QCPAxis *valueAxis = mValueAxis.data();
7110 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
7112 SignDomain signDomain = sdBoth;
7113 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
7114 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
7117 QCPRange newRange = getValueRange(foundRange, signDomain);
7121 newRange.expand(valueAxis->range());
7122 if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
7124 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
7125 if (valueAxis->scaleType() == QCPAxis::stLinear)
7127 newRange.lower = center-valueAxis->range().size()/2.0;
7128 newRange.upper = center+valueAxis->range().size()/2.0;
7129 } else // scaleType() == stLogarithmic
7131 newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7132 newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
7135 valueAxis->setRange(newRange);
7140 Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
7142 Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
7143 needs a more specialized representation in the legend, this function will take this into account
7144 and instead create the specialized subclass of QCPAbstractLegendItem.
7146 Returns true on success, i.e. when the legend exists and a legend item associated with this plottable isn't already in
7149 \see removeFromLegend, QCPLegend::addItem
7151 bool QCPAbstractPlottable::addToLegend()
7153 if (!mParentPlot || !mParentPlot->legend)
7156 if (!mParentPlot->legend->hasItemWithPlottable(this))
7158 mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
7165 Removes the plottable from the legend of the parent QCustomPlot. This means the
7166 QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
7169 Returns true on success, i.e. if the legend exists and a legend item associated with this
7170 plottable was found and removed.
7172 \see addToLegend, QCPLegend::removeItem
7174 bool QCPAbstractPlottable::removeFromLegend() const
7176 if (!mParentPlot->legend)
7179 if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
7180 return mParentPlot->legend->removeItem(lip);
7185 /* inherits documentation from base class */
7186 QRect QCPAbstractPlottable::clipRect() const
7188 if (mKeyAxis && mValueAxis)
7189 return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
7194 /* inherits documentation from base class */
7195 QCP::Interaction QCPAbstractPlottable::selectionCategory() const
7197 return QCP::iSelectPlottables;
7202 Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
7203 taking the orientations of the axes associated with this plottable into account (e.g. whether key
7206 \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
7208 \see pixelsToCoords, QCPAxis::coordToPixel
7210 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
7212 QCPAxis *keyAxis = mKeyAxis.data();
7213 QCPAxis *valueAxis = mValueAxis.data();
7214 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7216 if (keyAxis->orientation() == Qt::Horizontal)
7218 x = keyAxis->coordToPixel(key);
7219 y = valueAxis->coordToPixel(value);
7222 y = keyAxis->coordToPixel(key);
7223 x = valueAxis->coordToPixel(value);
7230 Returns the input as pixel coordinates in a QPointF.
7232 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
7234 QCPAxis *keyAxis = mKeyAxis.data();
7235 QCPAxis *valueAxis = mValueAxis.data();
7236 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
7238 if (keyAxis->orientation() == Qt::Horizontal)
7239 return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
7241 return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
7246 Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
7247 taking the orientations of the axes associated with this plottable into account (e.g. whether key
7250 \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
7252 \see coordsToPixels, QCPAxis::coordToPixel
7254 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
7256 QCPAxis *keyAxis = mKeyAxis.data();
7257 QCPAxis *valueAxis = mValueAxis.data();
7258 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
7260 if (keyAxis->orientation() == Qt::Horizontal)
7262 key = keyAxis->pixelToCoord(x);
7263 value = valueAxis->pixelToCoord(y);
7266 key = keyAxis->pixelToCoord(y);
7267 value = valueAxis->pixelToCoord(x);
7274 Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
7276 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
7278 pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
7283 Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
7284 graph is not selected and mSelectedPen when it is.
7286 QPen QCPAbstractPlottable::mainPen() const
7288 return mSelected ? mSelectedPen : mPen;
7293 Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
7294 graph is not selected and mSelectedBrush when it is.
7296 QBrush QCPAbstractPlottable::mainBrush() const
7298 return mSelected ? mSelectedBrush : mBrush;
7303 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7304 before drawing plottable lines.
7306 This is the antialiasing state the painter passed to the \ref draw method is in by default.
7308 This function takes into account the local setting of the antialiasing flag as well as the
7309 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7310 QCustomPlot::setNotAntialiasedElements.
7312 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7314 void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
7316 applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
7321 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7322 before drawing plottable fills.
7324 This function takes into account the local setting of the antialiasing flag as well as the
7325 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7326 QCustomPlot::setNotAntialiasedElements.
7328 \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
7330 void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
7332 applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
7337 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7338 before drawing plottable scatter points.
7340 This function takes into account the local setting of the antialiasing flag as well as the
7341 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7342 QCustomPlot::setNotAntialiasedElements.
7344 \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
7346 void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
7348 applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
7353 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
7354 before drawing plottable error bars.
7356 This function takes into account the local setting of the antialiasing flag as well as the
7357 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
7358 QCustomPlot::setNotAntialiasedElements.
7360 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
7362 void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
7364 applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
7369 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
7372 This function may be used to help with the implementation of the \ref selectTest function for
7373 specific plottables.
7375 \note This function is identical to QCPAbstractItem::distSqrToLine
7377 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
7384 double vLengthSqr = v.lengthSquared();
7385 if (!qFuzzyIsNull(vLengthSqr))
7387 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
7389 return (a-p).lengthSquared();
7391 return (b-p).lengthSquared();
7393 return ((a + mu*v)-p).lengthSquared();
7395 return (a-p).lengthSquared();
7398 /* inherits documentation from base class */
7399 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
7405 bool selBefore = mSelected;
7406 setSelected(additive ? !mSelected : true);
7407 if (selectionStateChanged)
7408 *selectionStateChanged = mSelected != selBefore;
7412 /* inherits documentation from base class */
7413 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
7417 bool selBefore = mSelected;
7419 if (selectionStateChanged)
7420 *selectionStateChanged = mSelected != selBefore;
7425 ////////////////////////////////////////////////////////////////////////////////////////////////////
7426 //////////////////// QCPItemAnchor
7427 ////////////////////////////////////////////////////////////////////////////////////////////////////
7429 /*! \class QCPItemAnchor
7430 \brief An anchor of an item to which positions can be attached to.
7432 An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
7433 control anything on its item, but provides a way to tie other items via their positions to the
7436 For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
7437 Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
7438 attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
7439 calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
7440 QCPItemRect. This way the start of the line will now always follow the respective anchor location
7443 Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
7444 anchor to other positions.
7446 To learn how to provide anchors in your own item subclasses, see the subclassing section of the
7447 QCPAbstractItem documentation.
7450 /* start documentation of inline functions */
7452 /*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
7454 Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if
7455 it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
7457 This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids
7458 dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with
7462 /* end documentation of inline functions */
7465 Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
7466 you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
7467 explained in the subclassing section of the QCPAbstractItem documentation.
7469 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
7471 mParentPlot(parentPlot),
7472 mParentItem(parentItem),
7477 QCPItemAnchor::~QCPItemAnchor()
7479 // unregister as parent at children:
7480 foreach (QCPItemPosition *child, mChildrenX.toList())
7482 if (child->parentAnchorX() == this)
7483 child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7485 foreach (QCPItemPosition *child, mChildrenY.toList())
7487 if (child->parentAnchorY() == this)
7488 child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7493 Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
7495 The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
7496 parent item, QCPItemAnchor is just an intermediary.
7498 QPointF QCPItemAnchor::pixelPoint() const
7504 return mParentItem->anchorPixelPoint(mAnchorId);
7507 qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
7512 qDebug() << Q_FUNC_INFO << "no parent item set";
7519 Adds \a pos to the childX list of this anchor, which keeps track of which children use this
7520 anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7521 prior to destruction of the anchor.
7523 Note that this function does not change the parent setting in \a pos.
7525 void QCPItemAnchor::addChildX(QCPItemPosition *pos)
7527 if (!mChildrenX.contains(pos))
7528 mChildrenX.insert(pos);
7530 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7535 Removes \a pos from the childX list of this anchor.
7537 Note that this function does not change the parent setting in \a pos.
7539 void QCPItemAnchor::removeChildX(QCPItemPosition *pos)
7541 if (!mChildrenX.remove(pos))
7542 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7547 Adds \a pos to the childY list of this anchor, which keeps track of which children use this
7548 anchor as parent anchor for the respective coordinate. This is necessary to notify the children
7549 prior to destruction of the anchor.
7551 Note that this function does not change the parent setting in \a pos.
7553 void QCPItemAnchor::addChildY(QCPItemPosition *pos)
7555 if (!mChildrenY.contains(pos))
7556 mChildrenY.insert(pos);
7558 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
7563 Removes \a pos from the childY list of this anchor.
7565 Note that this function does not change the parent setting in \a pos.
7567 void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
7569 if (!mChildrenY.remove(pos))
7570 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
7574 ////////////////////////////////////////////////////////////////////////////////////////////////////
7575 //////////////////// QCPItemPosition
7576 ////////////////////////////////////////////////////////////////////////////////////////////////////
7578 /*! \class QCPItemPosition
7579 \brief Manages the position of an item.
7581 Every item has at least one public QCPItemPosition member pointer which provides ways to position the
7582 item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
7583 \a topLeft and \a bottomRight.
7585 QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type
7586 defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel
7587 coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also
7588 possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref
7589 setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y
7590 direction, while following a plot coordinate in the X direction.
7592 A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie
7593 multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords)
7594 are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0)
7595 means directly ontop of the parent anchor. For example, You could attach the \a start position of
7596 a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line
7597 always be centered under the text label, no matter where the text is moved to. For more advanced
7598 plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see
7599 \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X
7600 direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B
7603 Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent
7604 anchor for other positions.
7606 To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
7607 works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
7608 setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
7612 /* start documentation of inline functions */
7614 /*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const
7616 Returns the current position type.
7618 If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the
7619 type of the X coordinate. In that case rather use \a typeX() and \a typeY().
7624 /*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const
7626 Returns the current parent anchor.
7628 If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY),
7629 this method returns the parent anchor of the Y coordinate. In that case rather use \a
7630 parentAnchorX() and \a parentAnchorY().
7632 \see setParentAnchor
7635 /* end documentation of inline functions */
7638 Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
7639 you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
7640 explained in the subclassing section of the QCPAbstractItem documentation.
7642 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
7643 QCPItemAnchor(parentPlot, parentItem, name),
7644 mPositionTypeX(ptAbsolute),
7645 mPositionTypeY(ptAbsolute),
7653 QCPItemPosition::~QCPItemPosition()
7655 // unregister as parent at children:
7656 // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
7657 // the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
7658 foreach (QCPItemPosition *child, mChildrenX.toList())
7660 if (child->parentAnchorX() == this)
7661 child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
7663 foreach (QCPItemPosition *child, mChildrenY.toList())
7665 if (child->parentAnchorY() == this)
7666 child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
7668 // unregister as child in parent:
7670 mParentAnchorX->removeChildX(this);
7672 mParentAnchorY->removeChildY(this);
7675 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
7676 QCPAxisRect *QCPItemPosition::axisRect() const
7678 return mAxisRect.data();
7682 Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
7683 should be handled and how the QCPItemPosition should behave in the plot.
7685 The possible values for \a type can be separated in two main categories:
7687 \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
7688 and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
7689 By default, the QCustomPlot's x- and yAxis are used.
7691 \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This
7692 corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref
7693 ptAxisRectRatio. They differ only in the way the absolute position is described, see the
7694 documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify
7695 the axis rect with \ref setAxisRect. By default this is set to the main axis rect.
7697 Note that the position type \ref ptPlotCoords is only available (and sensible) when the position
7698 has no parent anchor (\ref setParentAnchor).
7700 If the type is changed, the apparent pixel position on the plot is preserved. This means
7701 the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
7703 This method sets the type for both X and Y directions. It is also possible to set different types
7704 for X and Y, see \ref setTypeX, \ref setTypeY.
7706 void QCPItemPosition::setType(QCPItemPosition::PositionType type)
7713 This method sets the position type of the X coordinate to \a type.
7715 For a detailed description of what a position type is, see the documentation of \ref setType.
7717 \see setType, setTypeY
7719 void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)
7721 if (mPositionTypeX != type)
7723 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7724 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7725 bool retainPixelPosition = true;
7726 if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7727 retainPixelPosition = false;
7728 if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7729 retainPixelPosition = false;
7732 if (retainPixelPosition)
7733 pixel = pixelPoint();
7735 mPositionTypeX = type;
7737 if (retainPixelPosition)
7738 setPixelPoint(pixel);
7743 This method sets the position type of the Y coordinate to \a type.
7745 For a detailed description of what a position type is, see the documentation of \ref setType.
7747 \see setType, setTypeX
7749 void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
7751 if (mPositionTypeY != type)
7753 // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
7754 // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
7755 bool retainPixelPosition = true;
7756 if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
7757 retainPixelPosition = false;
7758 if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
7759 retainPixelPosition = false;
7762 if (retainPixelPosition)
7763 pixel = pixelPoint();
7765 mPositionTypeY = type;
7767 if (retainPixelPosition)
7768 setPixelPoint(pixel);
7773 Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
7774 follow any position changes of the anchor. The local coordinate system of positions with a parent
7775 anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence
7776 the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.)
7778 if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
7779 during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
7780 will be exactly on top of the parent anchor.
7782 To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
7784 If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is
7785 set to \ref ptAbsolute, to keep the position in a valid state.
7787 This method sets the parent anchor for both X and Y directions. It is also possible to set
7788 different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY.
7790 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7792 bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
7793 bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
7794 return successX && successY;
7798 This method sets the parent anchor of the X coordinate to \a parentAnchor.
7800 For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7802 \see setParentAnchor, setParentAnchorY
7804 bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7806 // make sure self is not assigned as parent:
7807 if (parentAnchor == this)
7809 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7812 // make sure no recursive parent-child-relationships are created:
7813 QCPItemAnchor *currentParent = parentAnchor;
7814 while (currentParent)
7816 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7818 // is a QCPItemPosition, might have further parent, so keep iterating
7819 if (currentParentPos == this)
7821 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7824 currentParent = currentParentPos->parentAnchorX();
7827 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7828 // same, to prevent a position being child of an anchor which itself depends on the position,
7829 // because they're both on the same item:
7830 if (currentParent->mParentItem == mParentItem)
7832 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7839 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7840 if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)
7841 setTypeX(ptAbsolute);
7843 // save pixel position:
7845 if (keepPixelPosition)
7846 pixelP = pixelPoint();
7847 // unregister at current parent anchor:
7849 mParentAnchorX->removeChildX(this);
7850 // register at new parent anchor:
7852 parentAnchor->addChildX(this);
7853 mParentAnchorX = parentAnchor;
7854 // restore pixel position under new parent:
7855 if (keepPixelPosition)
7856 setPixelPoint(pixelP);
7858 setCoords(0, coords().y());
7863 This method sets the parent anchor of the Y coordinate to \a parentAnchor.
7865 For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
7867 \see setParentAnchor, setParentAnchorX
7869 bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
7871 // make sure self is not assigned as parent:
7872 if (parentAnchor == this)
7874 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
7877 // make sure no recursive parent-child-relationships are created:
7878 QCPItemAnchor *currentParent = parentAnchor;
7879 while (currentParent)
7881 if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
7883 // is a QCPItemPosition, might have further parent, so keep iterating
7884 if (currentParentPos == this)
7886 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
7889 currentParent = currentParentPos->parentAnchorY();
7892 // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
7893 // same, to prevent a position being child of an anchor which itself depends on the position,
7894 // because they're both on the same item:
7895 if (currentParent->mParentItem == mParentItem)
7897 qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
7904 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
7905 if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)
7906 setTypeY(ptAbsolute);
7908 // save pixel position:
7910 if (keepPixelPosition)
7911 pixelP = pixelPoint();
7912 // unregister at current parent anchor:
7914 mParentAnchorY->removeChildY(this);
7915 // register at new parent anchor:
7917 parentAnchor->addChildY(this);
7918 mParentAnchorY = parentAnchor;
7919 // restore pixel position under new parent:
7920 if (keepPixelPosition)
7921 setPixelPoint(pixelP);
7923 setCoords(coords().x(), 0);
7928 Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
7929 (\ref setType, \ref setTypeX, \ref setTypeY).
7931 For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
7932 on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the
7933 QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the
7934 plot coordinate system defined by the axes set by \ref setAxes. By default those are the
7935 QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available
7936 coordinate types and their meaning.
7938 If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a
7939 value must also be provided in the different coordinate systems. Here, the X type refers to \a
7940 key, and the Y type refers to \a value.
7944 void QCPItemPosition::setCoords(double key, double value)
7952 Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
7953 meaning of \a value of the \ref setCoords(double key, double value) method.
7955 void QCPItemPosition::setCoords(const QPointF &pos)
7957 setCoords(pos.x(), pos.y());
7961 Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
7962 includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
7966 QPointF QCPItemPosition::pixelPoint() const
7971 switch (mPositionTypeX)
7977 result.rx() += mParentAnchorX->pixelPoint().x();
7980 case ptViewportRatio:
7982 result.rx() = mKey*mParentPlot->viewport().width();
7984 result.rx() += mParentAnchorX->pixelPoint().x();
7986 result.rx() += mParentPlot->viewport().left();
7989 case ptAxisRectRatio:
7993 result.rx() = mKey*mAxisRect.data()->width();
7995 result.rx() += mParentAnchorX->pixelPoint().x();
7997 result.rx() += mAxisRect.data()->left();
7999 qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8004 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8005 result.rx() = mKeyAxis.data()->coordToPixel(mKey);
8006 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8007 result.rx() = mValueAxis.data()->coordToPixel(mValue);
8009 qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8015 switch (mPositionTypeY)
8019 result.ry() = mValue;
8021 result.ry() += mParentAnchorY->pixelPoint().y();
8024 case ptViewportRatio:
8026 result.ry() = mValue*mParentPlot->viewport().height();
8028 result.ry() += mParentAnchorY->pixelPoint().y();
8030 result.ry() += mParentPlot->viewport().top();
8033 case ptAxisRectRatio:
8037 result.ry() = mValue*mAxisRect.data()->height();
8039 result.ry() += mParentAnchorY->pixelPoint().y();
8041 result.ry() += mAxisRect.data()->top();
8043 qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8048 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8049 result.ry() = mKeyAxis.data()->coordToPixel(mKey);
8050 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8051 result.ry() = mValueAxis.data()->coordToPixel(mValue);
8053 qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8062 When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the
8063 coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and
8064 yAxis of the QCustomPlot.
8066 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
8069 mValueAxis = valueAxis;
8073 When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the
8074 coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of
8077 void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)
8079 mAxisRect = axisRect;
8083 Sets the apparent pixel position. This works no matter what type (\ref setType) this
8084 QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed
8085 appropriately, to make the position finally appear at the specified pixel values.
8087 Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is
8088 identical to that of \ref setCoords.
8090 \see pixelPoint, setCoords
8092 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
8094 double x = pixelPoint.x();
8095 double y = pixelPoint.y();
8097 switch (mPositionTypeX)
8102 x -= mParentAnchorX->pixelPoint().x();
8105 case ptViewportRatio:
8108 x -= mParentAnchorX->pixelPoint().x();
8110 x -= mParentPlot->viewport().left();
8111 x /= (double)mParentPlot->viewport().width();
8114 case ptAxisRectRatio:
8119 x -= mParentAnchorX->pixelPoint().x();
8121 x -= mAxisRect.data()->left();
8122 x /= (double)mAxisRect.data()->width();
8124 qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
8129 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
8130 x = mKeyAxis.data()->pixelToCoord(x);
8131 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
8132 y = mValueAxis.data()->pixelToCoord(x);
8134 qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
8139 switch (mPositionTypeY)
8144 y -= mParentAnchorY->pixelPoint().y();
8147 case ptViewportRatio:
8150 y -= mParentAnchorY->pixelPoint().y();
8152 y -= mParentPlot->viewport().top();
8153 y /= (double)mParentPlot->viewport().height();
8156 case ptAxisRectRatio:
8161 y -= mParentAnchorY->pixelPoint().y();
8163 y -= mAxisRect.data()->top();
8164 y /= (double)mAxisRect.data()->height();
8166 qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
8171 if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
8172 x = mKeyAxis.data()->pixelToCoord(y);
8173 else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
8174 y = mValueAxis.data()->pixelToCoord(y);
8176 qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
8185 ////////////////////////////////////////////////////////////////////////////////////////////////////
8186 //////////////////// QCPAbstractItem
8187 ////////////////////////////////////////////////////////////////////////////////////////////////////
8189 /*! \class QCPAbstractItem
8190 \brief The abstract base class for all items in a plot.
8192 In QCustomPlot, items are supplemental graphical elements that are neither plottables
8193 (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
8194 plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
8195 specific item has at least one QCPItemPosition member which controls the positioning. Some items
8196 are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
8197 example, QCPItemRect has \a topLeft and \a bottomRight).
8199 This abstract base class defines a very basic interface like visibility and clipping. Since this
8200 class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
8201 yourself to create new items.
8203 The built-in items are:
8205 <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).</td></tr>
8206 <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.</td></tr>
8207 <tr><td>QCPItemCurve</td><td>A curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).</td></tr>
8208 <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
8209 <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
8210 <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
8211 <tr><td>QCPItemText</td><td>A text label</td></tr>
8212 <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
8213 <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.</td></tr>
8216 \section items-clipping Clipping
8218 Items are by default clipped to the main axis rect (they are only visible inside the axis rect).
8219 To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect
8220 "setClipToAxisRect(false)".
8222 On the other hand if you want the item to be clipped to a different axis rect, specify it via
8223 \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and
8224 in principle is independent of the coordinate axes the item might be tied to via its position
8225 members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping
8226 also contains the axes used for the item positions.
8228 \section items-using Using items
8230 First you instantiate the item you want to use and add it to the plot:
8232 QCPItemLine *line = new QCPItemLine(customPlot);
8233 customPlot->addItem(line);
8235 by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
8236 set the plot coordinates where the line should start/end:
8238 line->start->setCoords(-0.1, 0.8);
8239 line->end->setCoords(1.1, 0.2);
8241 If we don't want the line to be positioned in plot coordinates but a different coordinate system,
8242 e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this:
8244 line->start->setType(QCPItemPosition::ptAbsolute);
8245 line->end->setType(QCPItemPosition::ptAbsolute);
8247 Then we can set the coordinates, this time in pixels:
8249 line->start->setCoords(100, 200);
8250 line->end->setCoords(450, 320);
8252 and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect:
8254 line->setClipToAxisRect(false);
8257 For more advanced plots, it is even possible to set different types and parent anchors per X/Y
8258 coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref
8259 QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition.
8261 \section items-subclassing Creating own items
8263 To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
8264 virtual functions, you must implement:
8268 See the documentation of those functions for what they need to do.
8270 \subsection items-positioning Allowing the item to be positioned
8272 As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
8273 have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add
8274 a public member of type QCPItemPosition like so:
8276 \code QCPItemPosition * const myPosition;\endcode
8278 the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
8279 instance it points to, can be modified, of course).
8280 The initialization of this pointer is made easy with the \ref createPosition function. Just assign
8281 the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
8282 takes a string which is the name of the position, typically this is identical to the variable name.
8283 For example, the constructor of QCPItemExample could look like this:
8286 QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
8287 QCPAbstractItem(parentPlot),
8288 myPosition(createPosition("myPosition"))
8290 // other constructor code
8294 \subsection items-drawing The draw function
8296 To give your item a visual representation, reimplement the \ref draw function and use the passed
8297 QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the
8298 position member(s) via \ref QCPItemPosition::pixelPoint.
8300 To optimize performance you should calculate a bounding rect first (don't forget to take the pen
8301 width into account), check whether it intersects the \ref clipRect, and only draw the item at all
8302 if this is the case.
8304 \subsection items-selection The selectTest function
8306 Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
8307 \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
8308 simpler for most items. See the documentation of \ref selectTest for what the function parameters
8309 mean and what the function should return.
8311 \subsection anchors Providing anchors
8313 Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
8316 \code QCPItemAnchor * const bottom;\endcode
8318 and create it in the constructor with the \ref createAnchor function, assigning it a name and an
8319 anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
8320 Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
8321 provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
8324 In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
8325 position when anything attached to the anchor needs to know the coordinates.
8328 /* start of documentation of inline functions */
8330 /*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
8332 Returns all positions of the item in a list.
8334 \see anchors, position
8337 /*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
8339 Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
8340 also an anchor, the list will also contain the positions of this item.
8342 \see positions, anchor
8345 /* end of documentation of inline functions */
8346 /* start documentation of pure virtual functions */
8348 /*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
8351 Draws this item with the provided \a painter.
8353 The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
8354 function is called. The clipRect depends on the clipping settings defined by \ref
8355 setClipToAxisRect and \ref setClipAxisRect.
8358 /* end documentation of pure virtual functions */
8359 /* start documentation of signals */
8361 /*! \fn void QCPAbstractItem::selectionChanged(bool selected)
8362 This signal is emitted when the selection state of this item has changed, either by user interaction
8363 or by a direct call to \ref setSelected.
8366 /* end documentation of signals */
8369 Base class constructor which initializes base class members.
8371 QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
8372 QCPLayerable(parentPlot),
8373 mClipToAxisRect(false),
8377 QList<QCPAxisRect*> rects = parentPlot->axisRects();
8378 if (rects.size() > 0)
8380 setClipToAxisRect(true);
8381 setClipAxisRect(rects.first());
8385 QCPAbstractItem::~QCPAbstractItem()
8387 // don't delete mPositions because every position is also an anchor and thus in mAnchors
8388 qDeleteAll(mAnchors);
8391 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
8392 QCPAxisRect *QCPAbstractItem::clipAxisRect() const
8394 return mClipAxisRect.data();
8398 Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the
8399 entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect.
8401 \see setClipAxisRect
8403 void QCPAbstractItem::setClipToAxisRect(bool clip)
8405 mClipToAxisRect = clip;
8406 if (mClipToAxisRect)
8407 setParentLayerable(mClipAxisRect.data());
8411 Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref
8412 setClipToAxisRect is set to true.
8414 \see setClipToAxisRect
8416 void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)
8418 mClipAxisRect = rect;
8419 if (mClipToAxisRect)
8420 setParentLayerable(mClipAxisRect.data());
8424 Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
8425 (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
8427 However, even when \a selectable was set to false, it is possible to set the selection manually,
8428 by calling \ref setSelected.
8430 \see QCustomPlot::setInteractions, setSelected
8432 void QCPAbstractItem::setSelectable(bool selectable)
8434 if (mSelectable != selectable)
8436 mSelectable = selectable;
8437 emit selectableChanged(mSelectable);
8442 Sets whether this item is selected or not. When selected, it might use a different visual
8443 appearance (e.g. pen and brush), this depends on the specific item though.
8445 The entire selection mechanism for items is handled automatically when \ref
8446 QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this
8447 function when you wish to change the selection state manually.
8449 This function can change the selection state even when \ref setSelectable was set to false.
8451 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
8453 \see setSelectable, selectTest
8455 void QCPAbstractItem::setSelected(bool selected)
8457 if (mSelected != selected)
8459 mSelected = selected;
8460 emit selectionChanged(mSelected);
8465 Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
8466 that name, returns 0.
8468 This function provides an alternative way to access item positions. Normally, you access
8469 positions direcly by their member pointers (which typically have the same variable name as \a
8472 \see positions, anchor
8474 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
8476 for (int i=0; i<mPositions.size(); ++i)
8478 if (mPositions.at(i)->name() == name)
8479 return mPositions.at(i);
8481 qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
8486 Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
8487 that name, returns 0.
8489 This function provides an alternative way to access item anchors. Normally, you access
8490 anchors direcly by their member pointers (which typically have the same variable name as \a
8493 \see anchors, position
8495 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
8497 for (int i=0; i<mAnchors.size(); ++i)
8499 if (mAnchors.at(i)->name() == name)
8500 return mAnchors.at(i);
8502 qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
8507 Returns whether this item has an anchor with the specified \a name.
8509 Note that you can check for positions with this function, too. This is because every position is
8510 also an anchor (QCPItemPosition inherits from QCPItemAnchor).
8512 \see anchor, position
8514 bool QCPAbstractItem::hasAnchor(const QString &name) const
8516 for (int i=0; i<mAnchors.size(); ++i)
8518 if (mAnchors.at(i)->name() == name)
8526 Returns the rect the visual representation of this item is clipped to. This depends on the
8527 current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect.
8529 If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
8533 QRect QCPAbstractItem::clipRect() const
8535 if (mClipToAxisRect && mClipAxisRect)
8536 return mClipAxisRect.data()->rect();
8538 return mParentPlot->viewport();
8543 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8544 before drawing item lines.
8546 This is the antialiasing state the painter passed to the \ref draw method is in by default.
8548 This function takes into account the local setting of the antialiasing flag as well as the
8549 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
8550 QCustomPlot::setNotAntialiasedElements.
8554 void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
8556 applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
8561 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
8564 This function may be used to help with the implementation of the \ref selectTest function for
8567 \note This function is identical to QCPAbstractPlottable::distSqrToLine
8571 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8578 double vLengthSqr = v.lengthSquared();
8579 if (!qFuzzyIsNull(vLengthSqr))
8581 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8583 return (a-p).lengthSquared();
8585 return (b-p).lengthSquared();
8587 return ((a + mu*v)-p).lengthSquared();
8589 return (a-p).lengthSquared();
8594 A convenience function which returns the selectTest value for a specified \a rect and a specified
8595 click position \a pos. \a filledRect defines whether a click inside the rect should also be
8596 considered a hit or whether only the rect border is sensitive to hits.
8598 This function may be used to help with the implementation of the \ref selectTest function for
8601 For example, if your item consists of four rects, call this function four times, once for each
8602 rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
8603 values which were greater or equal to zero. (Because this function may return -1.0 when \a pos
8604 doesn't hit \a rect at all). If all calls returned -1.0, return -1.0, too, because your item
8609 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
8613 // distance to border:
8614 QList<QLineF> lines;
8615 lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
8616 << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
8617 double minDistSqr = std::numeric_limits<double>::max();
8618 for (int i=0; i<lines.size(); ++i)
8620 double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
8621 if (distSqr < minDistSqr)
8622 minDistSqr = distSqr;
8624 result = qSqrt(minDistSqr);
8626 // filled rect, allow click inside to count as hit:
8627 if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
8629 if (rect.contains(pos))
8630 result = mParentPlot->selectionTolerance()*0.99;
8637 Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
8638 item subclasses if they want to provide anchors (QCPItemAnchor).
8640 For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
8641 ids and returns the respective pixel points of the specified anchor.
8645 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
8647 qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
8653 Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
8654 \a name must be a unique string that is usually identical to the variable name of the position
8655 member (This is needed to provide the name-based \ref position access to positions).
8657 Don't delete positions created by this function manually, as the item will take care of it.
8659 Use this function in the constructor (initialization list) of the specific item subclass to
8660 create each position member. Don't create QCPItemPositions with \b new yourself, because they
8661 won't be registered with the item properly.
8665 QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
8667 if (hasAnchor(name))
8668 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8669 QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
8670 mPositions.append(newPosition);
8671 mAnchors.append(newPosition); // every position is also an anchor
8672 newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
8673 newPosition->setType(QCPItemPosition::ptPlotCoords);
8674 if (mParentPlot->axisRect())
8675 newPosition->setAxisRect(mParentPlot->axisRect());
8676 newPosition->setCoords(0, 0);
8682 Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
8683 \a name must be a unique string that is usually identical to the variable name of the anchor
8684 member (This is needed to provide the name based \ref anchor access to anchors).
8686 The \a anchorId must be a number identifying the created anchor. It is recommended to create an
8687 enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
8688 to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
8689 the correct pixel coordinates for the passed anchor id.
8691 Don't delete anchors created by this function manually, as the item will take care of it.
8693 Use this function in the constructor (initialization list) of the specific item subclass to
8694 create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
8695 won't be registered with the item properly.
8699 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
8701 if (hasAnchor(name))
8702 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
8703 QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
8704 mAnchors.append(newAnchor);
8708 /* inherits documentation from base class */
8709 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8715 bool selBefore = mSelected;
8716 setSelected(additive ? !mSelected : true);
8717 if (selectionStateChanged)
8718 *selectionStateChanged = mSelected != selBefore;
8722 /* inherits documentation from base class */
8723 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
8727 bool selBefore = mSelected;
8729 if (selectionStateChanged)
8730 *selectionStateChanged = mSelected != selBefore;
8734 /* inherits documentation from base class */
8735 QCP::Interaction QCPAbstractItem::selectionCategory() const
8737 return QCP::iSelectItems;
8745 ////////////////////////////////////////////////////////////////////////////////////////////////////
8746 //////////////////// QCustomPlot
8747 ////////////////////////////////////////////////////////////////////////////////////////////////////
8749 /*! \class QCustomPlot
8751 \brief The central class of the library. This is the QWidget which displays the plot and
8752 interacts with the user.
8754 For tutorials on how to use QCustomPlot, see the website\n
8755 http://www.qcustomplot.com/
8758 /* start of documentation of inline functions */
8760 /*! \fn QRect QCustomPlot::viewport() const
8762 Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
8763 drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
8764 plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
8765 (0, 0) and size of the QCustomPlot widget.
8767 Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically
8768 an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger
8769 and contains also the axes themselves, their tick numbers, their labels, the plot title etc.
8771 Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport is temporarily
8772 modified to allow saving plots with sizes independent of the current widget size.
8775 /*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const
8777 Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just
8778 one cell with the main QCPAxisRect inside.
8781 /* end of documentation of inline functions */
8782 /* start of documentation of signals */
8784 /*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
8786 This signal is emitted when the QCustomPlot receives a mouse double click event.
8789 /*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
8791 This signal is emitted when the QCustomPlot receives a mouse press event.
8793 It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8794 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8795 QCPAxisRect::setRangeDragAxes.
8798 /*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
8800 This signal is emitted when the QCustomPlot receives a mouse move event.
8802 It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
8803 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
8804 QCPAxisRect::setRangeDragAxes.
8806 \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here,
8807 because the dragging starting point was saved the moment the mouse was pressed. Thus it only has
8808 a meaning for the range drag axes that were set at that moment. If you want to change the drag
8809 axes, consider doing this in the \ref mousePress signal instead.
8812 /*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
8814 This signal is emitted when the QCustomPlot receives a mouse release event.
8816 It is emitted before QCustomPlot handles any other mechanisms like object selection. So a
8817 slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or
8818 \ref QCPAbstractPlottable::setSelectable.
8821 /*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
8823 This signal is emitted when the QCustomPlot receives a mouse wheel event.
8825 It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot
8826 connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref
8827 QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor.
8830 /*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8832 This signal is emitted when a plottable is clicked.
8834 \a event is the mouse event that caused the click and \a plottable is the plottable that received
8837 \see plottableDoubleClick
8840 /*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
8842 This signal is emitted when a plottable is double clicked.
8844 \a event is the mouse event that caused the click and \a plottable is the plottable that received
8850 /*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
8852 This signal is emitted when an item is clicked.
8854 \a event is the mouse event that caused the click and \a item is the item that received the
8857 \see itemDoubleClick
8860 /*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
8862 This signal is emitted when an item is double clicked.
8864 \a event is the mouse event that caused the click and \a item is the item that received the
8870 /*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8872 This signal is emitted when an axis is clicked.
8874 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8875 \a part indicates the part of the axis that was clicked.
8877 \see axisDoubleClick
8880 /*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
8882 This signal is emitted when an axis is double clicked.
8884 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
8885 \a part indicates the part of the axis that was clicked.
8890 /*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8892 This signal is emitted when a legend (item) is clicked.
8894 \a event is the mouse event that caused the click, \a legend is the legend that received the
8895 click and \a item is the legend item that received the click. If only the legend and no item is
8896 clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8899 \see legendDoubleClick
8902 /*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
8904 This signal is emitted when a legend (item) is double clicked.
8906 \a event is the mouse event that caused the click, \a legend is the legend that received the
8907 click and \a item is the legend item that received the click. If only the legend and no item is
8908 clicked, \a item is 0. This happens for a click inside the legend padding or the space between
8914 /*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title)
8916 This signal is emitted when a plot title is clicked.
8918 \a event is the mouse event that caused the click and \a title is the plot title that received
8921 \see titleDoubleClick
8924 /*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
8926 This signal is emitted when a plot title is double clicked.
8928 \a event is the mouse event that caused the click and \a title is the plot title that received
8934 /*! \fn void QCustomPlot::selectionChangedByUser()
8936 This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
8937 clicking. It is not emitted when the selection state of an object has changed programmatically by
8938 a direct call to setSelected() on an object or by calling \ref deselectAll.
8940 In addition to this signal, selectable objects also provide individual signals, for example
8941 QCPAxis::selectionChanged or QCPAbstractPlottable::selectionChanged. Note that those signals are
8942 emitted even if the selection state is changed programmatically.
8944 See the documentation of \ref setInteractions for details about the selection mechanism.
8946 \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends
8949 /*! \fn void QCustomPlot::beforeReplot()
8951 This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
8954 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8955 replot synchronously, it won't cause an infinite recursion.
8957 \see replot, afterReplot
8960 /*! \fn void QCustomPlot::afterReplot()
8962 This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
8965 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
8966 replot synchronously, it won't cause an infinite recursion.
8968 \see replot, beforeReplot
8971 /* end of documentation of signals */
8972 /* start of documentation of public members */
8974 /*! \var QCPAxis *QCustomPlot::xAxis
8976 A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
8978 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8979 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8980 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8981 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8982 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8983 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8984 axis rect), the corresponding pointers become 0.
8987 /*! \var QCPAxis *QCustomPlot::yAxis
8989 A pointer to the primary y Axis (left) of the main axis rect of the plot.
8991 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
8992 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
8993 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
8994 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
8995 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
8996 default legend is removed due to manipulation of the layout system (e.g. by removing the main
8997 axis rect), the corresponding pointers become 0.
9000 /*! \var QCPAxis *QCustomPlot::xAxis2
9002 A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are
9003 invisible by default. Use QCPAxis::setVisible to change this (or use \ref
9004 QCPAxisRect::setupFullAxesBox).
9006 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
9007 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
9008 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
9009 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
9010 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
9011 default legend is removed due to manipulation of the layout system (e.g. by removing the main
9012 axis rect), the corresponding pointers become 0.
9015 /*! \var QCPAxis *QCustomPlot::yAxis2
9017 A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are
9018 invisible by default. Use QCPAxis::setVisible to change this (or use \ref
9019 QCPAxisRect::setupFullAxesBox).
9021 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
9022 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
9023 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
9024 layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
9025 QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
9026 default legend is removed due to manipulation of the layout system (e.g. by removing the main
9027 axis rect), the corresponding pointers become 0.
9030 /*! \var QCPLegend *QCustomPlot::legend
9032 A pointer to the default legend of the main axis rect. The legend is invisible by default. Use
9033 QCPLegend::setVisible to change this.
9035 QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
9036 yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
9037 axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
9038 layout system\endlink to add multiple legends to the plot, use the layout system interface to
9039 access the new legend. For example, legends can be placed inside an axis rect's \ref
9040 QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If
9041 the default legend is removed due to manipulation of the layout system (e.g. by removing the main
9042 axis rect), the corresponding pointer becomes 0.
9045 /* end of documentation of public members */
9048 Constructs a QCustomPlot and sets reasonable default values.
9050 QCustomPlot::QCustomPlot(QWidget *parent) :
9058 mAutoAddPlottableToLegend(true),
9059 mAntialiasedElements(QCP::aeNone),
9060 mNotAntialiasedElements(QCP::aeNone),
9062 mSelectionTolerance(8),
9063 mNoAntialiasingOnDrag(false),
9064 mBackgroundBrush(Qt::white, Qt::SolidPattern),
9065 mBackgroundScaled(true),
9066 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
9068 mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
9069 mMultiSelectModifier(Qt::ControlModifier),
9070 mPaintBuffer(size()),
9071 mMouseEventElement(0),
9074 setAttribute(Qt::WA_NoMousePropagation);
9075 setAttribute(Qt::WA_OpaquePaintEvent);
9076 setMouseTracking(true);
9077 QLocale currentLocale = locale();
9078 currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
9079 setLocale(currentLocale);
9081 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
9082 QSize pbSize = mPaintBuffer.size();
9083 pbSize *= devicePixelRatio();
9084 mPaintBuffer = QPixmap(pbSize);
9085 mPaintBuffer.setDevicePixelRatio(devicePixelRatio());
9088 // create initial layers:
9089 mLayers.append(new QCPLayer(this, QLatin1String("background")));
9090 mLayers.append(new QCPLayer(this, QLatin1String("grid")));
9091 mLayers.append(new QCPLayer(this, QLatin1String("main")));
9092 mLayers.append(new QCPLayer(this, QLatin1String("axes")));
9093 mLayers.append(new QCPLayer(this, QLatin1String("legend")));
9094 updateLayerIndices();
9095 setCurrentLayer(QLatin1String("main"));
9097 // create initial layout, axis rect and legend:
9098 mPlotLayout = new QCPLayoutGrid;
9099 mPlotLayout->initializeParentPlot(this);
9100 mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
9101 mPlotLayout->setLayer(QLatin1String("main"));
9102 QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
9103 mPlotLayout->addElement(0, 0, defaultAxisRect);
9104 xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
9105 yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
9106 xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
9107 yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
9108 legend = new QCPLegend;
9109 legend->setVisible(false);
9110 defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
9111 defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
9113 defaultAxisRect->setLayer(QLatin1String("background"));
9114 xAxis->setLayer(QLatin1String("axes"));
9115 yAxis->setLayer(QLatin1String("axes"));
9116 xAxis2->setLayer(QLatin1String("axes"));
9117 yAxis2->setLayer(QLatin1String("axes"));
9118 xAxis->grid()->setLayer(QLatin1String("grid"));
9119 yAxis->grid()->setLayer(QLatin1String("grid"));
9120 xAxis2->grid()->setLayer(QLatin1String("grid"));
9121 yAxis2->grid()->setLayer(QLatin1String("grid"));
9122 legend->setLayer(QLatin1String("legend"));
9124 setViewport(rect()); // needs to be called after mPlotLayout has been created
9129 QCustomPlot::~QCustomPlot()
9141 qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
9146 Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement.
9148 This overrides the antialiasing settings for whole element groups, normally controlled with the
9149 \a setAntialiasing function on the individual elements. If an element is neither specified in
9150 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9151 each individual element instance is used.
9153 For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
9154 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9157 if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is
9160 \see setNotAntialiasedElements
9162 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
9164 mAntialiasedElements = antialiasedElements;
9166 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9167 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9168 mNotAntialiasedElements |= ~mAntialiasedElements;
9172 Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
9174 See \ref setAntialiasedElements for details.
9176 \see setNotAntialiasedElement
9178 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
9180 if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
9181 mAntialiasedElements &= ~antialiasedElement;
9182 else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
9183 mAntialiasedElements |= antialiasedElement;
9185 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9186 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9187 mNotAntialiasedElements |= ~mAntialiasedElements;
9191 Sets which elements are forcibly drawn not antialiased as an \a or combination of
9192 QCP::AntialiasedElement.
9194 This overrides the antialiasing settings for whole element groups, normally controlled with the
9195 \a setAntialiasing function on the individual elements. If an element is neither specified in
9196 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
9197 each individual element instance is used.
9199 For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
9200 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
9203 if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
9206 \see setAntialiasedElements
9208 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements)
9210 mNotAntialiasedElements = notAntialiasedElements;
9212 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9213 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9214 mAntialiasedElements |= ~mNotAntialiasedElements;
9218 Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
9220 See \ref setNotAntialiasedElements for details.
9222 \see setAntialiasedElement
9224 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
9226 if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
9227 mNotAntialiasedElements &= ~notAntialiasedElement;
9228 else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
9229 mNotAntialiasedElements |= notAntialiasedElement;
9231 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
9232 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
9233 mAntialiasedElements |= ~mNotAntialiasedElements;
9237 If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
9238 plottable to the legend (QCustomPlot::legend).
9240 \see addPlottable, addGraph, QCPLegend::addItem
9242 void QCustomPlot::setAutoAddPlottableToLegend(bool on)
9244 mAutoAddPlottableToLegend = on;
9248 Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction
9249 enums. There are the following types of interactions:
9251 <b>Axis range manipulation</b> is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the
9252 respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
9253 For details how to control which axes the user may drag/zoom and in what orientations, see \ref
9254 QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes,
9255 \ref QCPAxisRect::setRangeZoomAxes.
9257 <b>Plottable selection</b> is controlled by \ref QCP::iSelectPlottables. If \ref QCP::iSelectPlottables is
9258 set, the user may select plottables (graphs, curves, bars,...) by clicking on them or in their
9259 vicinity (\ref setSelectionTolerance). Whether the user can actually select a plottable can
9260 further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the specific
9261 plottable. To find out whether a specific plottable is selected, call
9262 QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
9263 \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
9264 function \ref selectedGraphs.
9266 <b>Item selection</b> is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user
9267 may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find
9268 out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of
9269 all currently selected items, call \ref selectedItems.
9271 <b>Axis selection</b> is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user
9272 may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
9273 labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for
9274 each axis. To retrieve a list of all axes that currently contain selected parts, call \ref
9275 selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts().
9277 <b>Legend selection</b> is controlled with \ref QCP::iSelectLegend. If this is set, the user may
9278 select the legend itself or individual items by clicking on them. What parts exactly are
9279 selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the
9280 legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To
9281 find out which child items are selected, call \ref QCPLegend::selectedItems.
9283 <b>All other selectable elements</b> The selection of all other selectable objects (e.g.
9284 QCPPlotTitle, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the
9285 user may select those objects by clicking on them. To find out which are currently selected, you
9286 need to check their selected state explicitly.
9288 If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
9289 emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
9290 their selection state has changed, i.e. not only by user interaction.
9292 To allow multiple objects to be selected by holding the selection modifier (\ref
9293 setMultiSelectModifier), set the flag \ref QCP::iMultiSelect.
9295 \note In addition to the selection mechanism presented here, QCustomPlot always emits
9296 corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
9297 \ref plottableDoubleClick for example.
9299 \see setInteraction, setSelectionTolerance
9301 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
9303 mInteractions = interactions;
9307 Sets the single \a interaction of this QCustomPlot to \a enabled.
9309 For details about the interaction system, see \ref setInteractions.
9311 \see setInteractions
9313 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
9315 if (!enabled && mInteractions.testFlag(interaction))
9316 mInteractions &= ~interaction;
9317 else if (enabled && !mInteractions.testFlag(interaction))
9318 mInteractions |= interaction;
9322 Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or
9325 If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a
9326 potential selection when the minimum distance between the click position and the graph line is
9327 smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
9328 directly inside the area and ignore this selection tolerance. In other words, it only has meaning
9329 for parts of objects that are too thin to exactly hit with a click and thus need such a
9332 \see setInteractions, QCPLayerable::selectTest
9334 void QCustomPlot::setSelectionTolerance(int pixels)
9336 mSelectionTolerance = pixels;
9340 Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes
9341 ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves
9342 performance during dragging. Thus it creates a more responsive user experience. As soon as the
9343 user stops dragging, the last replot is done with normal antialiasing, to restore high image
9346 \see setAntialiasedElements, setNotAntialiasedElements
9348 void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
9350 mNoAntialiasingOnDrag = enabled;
9354 Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint.
9356 \see setPlottingHint
9358 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
9360 mPlottingHints = hints;
9364 Sets the specified plotting \a hint to \a enabled.
9366 \see setPlottingHints
9368 void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
9370 QCP::PlottingHints newHints = mPlottingHints;
9376 if (newHints != mPlottingHints)
9377 setPlottingHints(newHints);
9381 Sets the keyboard modifier that will be recognized as multi-select-modifier.
9383 If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
9384 by clicking on them one after the other while holding down \a modifier.
9386 By default the multi-select-modifier is set to Qt::ControlModifier.
9388 \see setInteractions
9390 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
9392 mMultiSelectModifier = modifier;
9396 Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout
9397 (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect.
9399 This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref
9400 savePdf, etc. by temporarily changing the viewport size.
9402 void QCustomPlot::setViewport(const QRect &rect)
9406 mPlotLayout->setOuterRect(mViewport);
9410 Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn
9411 below all other objects in the plot.
9413 For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be
9414 enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is
9415 preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
9416 consider using the overloaded version of this function.
9418 If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will
9419 first be filled with that brush, before drawing the background pixmap. This can be useful for
9420 background pixmaps with translucent areas.
9422 \see setBackgroundScaled, setBackgroundScaledMode
9424 void QCustomPlot::setBackground(const QPixmap &pm)
9426 mBackgroundPixmap = pm;
9427 mScaledBackgroundPixmap = QPixmap();
9431 Sets the background brush of the viewport (see \ref setViewport).
9433 Before drawing everything else, the background is filled with \a brush. If a background pixmap
9434 was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport
9435 before the background pixmap is drawn. This can be useful for background pixmaps with translucent
9438 Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be
9439 useful for exporting to image formats which support transparency, e.g. \ref savePng.
9441 \see setBackgroundScaled, setBackgroundScaledMode
9443 void QCustomPlot::setBackground(const QBrush &brush)
9445 mBackgroundBrush = brush;
9450 Allows setting the background pixmap of the viewport, whether it shall be scaled and how it
9451 shall be scaled in one call.
9453 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
9455 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
9457 mBackgroundPixmap = pm;
9458 mScaledBackgroundPixmap = QPixmap();
9459 mBackgroundScaled = scaled;
9460 mBackgroundScaledMode = mode;
9464 Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is
9465 set to true, control whether and how the aspect ratio of the original pixmap is preserved with
9466 \ref setBackgroundScaledMode.
9468 Note that the scaled version of the original pixmap is buffered, so there is no performance
9469 penalty on replots. (Except when the viewport dimensions are changed continuously.)
9471 \see setBackground, setBackgroundScaledMode
9473 void QCustomPlot::setBackgroundScaled(bool scaled)
9475 mBackgroundScaled = scaled;
9479 If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this
9480 function to define whether and how the aspect ratio of the original pixmap is preserved.
9482 \see setBackground, setBackgroundScaled
9484 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
9486 mBackgroundScaledMode = mode;
9490 Returns the plottable with \a index. If the index is invalid, returns 0.
9492 There is an overloaded version of this function with no parameter which returns the last added
9493 plottable, see QCustomPlot::plottable()
9495 \see plottableCount, addPlottable
9497 QCPAbstractPlottable *QCustomPlot::plottable(int index)
9499 if (index >= 0 && index < mPlottables.size())
9501 return mPlottables.at(index);
9504 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9511 Returns the last plottable that was added with \ref addPlottable. If there are no plottables in
9512 the plot, returns 0.
9514 \see plottableCount, addPlottable
9516 QCPAbstractPlottable *QCustomPlot::plottable()
9518 if (!mPlottables.isEmpty())
9520 return mPlottables.last();
9526 Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to
9527 the legend (QCustomPlot::legend). QCustomPlot takes ownership of the plottable.
9529 Returns true on success, i.e. when \a plottable isn't already in the plot and the parent plot of
9530 \a plottable is this QCustomPlot (the latter is controlled by what axes were passed in the
9531 plottable's constructor).
9533 \see plottable, plottableCount, removePlottable, clearPlottables
9535 bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
9537 if (mPlottables.contains(plottable))
9539 qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
9542 if (plottable->parentPlot() != this)
9544 qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
9548 mPlottables.append(plottable);
9549 // possibly add plottable to legend:
9550 if (mAutoAddPlottableToLegend)
9551 plottable->addToLegend();
9552 // special handling for QCPGraphs to maintain the simple graph interface:
9553 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9554 mGraphs.append(graph);
9555 if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
9556 plottable->setLayer(currentLayer());
9561 Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend).
9563 Returns true on success.
9565 \see addPlottable, clearPlottables
9567 bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
9569 if (!mPlottables.contains(plottable))
9571 qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
9575 // remove plottable from legend:
9576 plottable->removeFromLegend();
9577 // special handling for QCPGraphs to maintain the simple graph interface:
9578 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
9579 mGraphs.removeOne(graph);
9580 // remove plottable:
9582 mPlottables.removeOne(plottable);
9588 Removes the plottable by its \a index.
9590 bool QCustomPlot::removePlottable(int index)
9592 if (index >= 0 && index < mPlottables.size())
9593 return removePlottable(mPlottables[index]);
9596 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9602 Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
9604 Returns the number of plottables removed.
9606 \see removePlottable
9608 int QCustomPlot::clearPlottables()
9610 int c = mPlottables.size();
9611 for (int i=c-1; i >= 0; --i)
9612 removePlottable(mPlottables[i]);
9617 Returns the number of currently existing plottables in the plot
9619 \see plottable, addPlottable
9621 int QCustomPlot::plottableCount() const
9623 return mPlottables.size();
9627 Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
9629 There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
9631 \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9633 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
9635 QList<QCPAbstractPlottable*> result;
9636 foreach (QCPAbstractPlottable *plottable, mPlottables)
9638 if (plottable->selected())
9639 result.append(plottable);
9645 Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
9646 (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple
9647 plottables come into consideration, the one closest to \a pos is returned.
9649 If \a onlySelectable is true, only plottables that are selectable
9650 (QCPAbstractPlottable::setSelectable) are considered.
9652 If there is no plottable at \a pos, the return value is 0.
9654 \see itemAt, layoutElementAt
9656 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
9658 QCPAbstractPlottable *resultPlottable = 0;
9659 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9661 foreach (QCPAbstractPlottable *plottable, mPlottables)
9663 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
9665 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
9667 double currentDistance = plottable->selectTest(pos, false);
9668 if (currentDistance >= 0 && currentDistance < resultDistance)
9670 resultPlottable = plottable;
9671 resultDistance = currentDistance;
9676 return resultPlottable;
9680 Returns whether this QCustomPlot instance contains the \a plottable.
9684 bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
9686 return mPlottables.contains(plottable);
9690 Returns the graph with \a index. If the index is invalid, returns 0.
9692 There is an overloaded version of this function with no parameter which returns the last created
9693 graph, see QCustomPlot::graph()
9695 \see graphCount, addGraph
9697 QCPGraph *QCustomPlot::graph(int index) const
9699 if (index >= 0 && index < mGraphs.size())
9701 return mGraphs.at(index);
9704 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9711 Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
9714 \see graphCount, addGraph
9716 QCPGraph *QCustomPlot::graph() const
9718 if (!mGraphs.isEmpty())
9720 return mGraphs.last();
9726 Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
9727 bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a
9728 keyAxis and \a valueAxis must reside in this QCustomPlot.
9730 \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
9733 Returns a pointer to the newly created graph, or 0 if adding the graph failed.
9735 \see graph, graphCount, removeGraph, clearGraphs
9737 QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
9739 if (!keyAxis) keyAxis = xAxis;
9740 if (!valueAxis) valueAxis = yAxis;
9741 if (!keyAxis || !valueAxis)
9743 qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
9746 if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
9748 qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
9752 QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
9753 if (addPlottable(newGraph))
9755 newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
9765 Removes the specified \a graph from the plot and, if necessary, from the QCustomPlot::legend. If
9766 any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
9767 property of those graphs is reset to zero (no channel fill).
9769 Returns true on success.
9773 bool QCustomPlot::removeGraph(QCPGraph *graph)
9775 return removePlottable(graph);
9780 Removes the graph by its \a index.
9782 bool QCustomPlot::removeGraph(int index)
9784 if (index >= 0 && index < mGraphs.size())
9785 return removeGraph(mGraphs[index]);
9791 Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
9793 Returns the number of graphs removed.
9797 int QCustomPlot::clearGraphs()
9799 int c = mGraphs.size();
9800 for (int i=c-1; i >= 0; --i)
9801 removeGraph(mGraphs[i]);
9806 Returns the number of currently existing graphs in the plot
9808 \see graph, addGraph
9810 int QCustomPlot::graphCount() const
9812 return mGraphs.size();
9816 Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
9818 If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars,
9819 etc., use \ref selectedPlottables.
9821 \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
9823 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
9825 QList<QCPGraph*> result;
9826 foreach (QCPGraph *graph, mGraphs)
9828 if (graph->selected())
9829 result.append(graph);
9835 Returns the item with \a index. If the index is invalid, returns 0.
9837 There is an overloaded version of this function with no parameter which returns the last added
9838 item, see QCustomPlot::item()
9840 \see itemCount, addItem
9842 QCPAbstractItem *QCustomPlot::item(int index) const
9844 if (index >= 0 && index < mItems.size())
9846 return mItems.at(index);
9849 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9856 Returns the last item, that was added with \ref addItem. If there are no items in the plot,
9859 \see itemCount, addItem
9861 QCPAbstractItem *QCustomPlot::item() const
9863 if (!mItems.isEmpty())
9865 return mItems.last();
9871 Adds the specified item to the plot. QCustomPlot takes ownership of the item.
9873 Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a
9874 item is this QCustomPlot.
9876 \see item, itemCount, removeItem, clearItems
9878 bool QCustomPlot::addItem(QCPAbstractItem *item)
9880 if (!mItems.contains(item) && item->parentPlot() == this)
9882 mItems.append(item);
9886 qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
9892 Removes the specified item from the plot.
9894 Returns true on success.
9896 \see addItem, clearItems
9898 bool QCustomPlot::removeItem(QCPAbstractItem *item)
9900 if (mItems.contains(item))
9903 mItems.removeOne(item);
9907 qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
9914 Removes the item by its \a index.
9916 bool QCustomPlot::removeItem(int index)
9918 if (index >= 0 && index < mItems.size())
9919 return removeItem(mItems[index]);
9922 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
9928 Removes all items from the plot.
9930 Returns the number of items removed.
9934 int QCustomPlot::clearItems()
9936 int c = mItems.size();
9937 for (int i=c-1; i >= 0; --i)
9938 removeItem(mItems[i]);
9943 Returns the number of currently existing items in the plot
9947 int QCustomPlot::itemCount() const
9949 return mItems.size();
9953 Returns a list of the selected items. If no items are currently selected, the list is empty.
9955 \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
9957 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
9959 QList<QCPAbstractItem*> result;
9960 foreach (QCPAbstractItem *item, mItems)
9962 if (item->selected())
9963 result.append(item);
9969 Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
9970 QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
9971 setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
9974 If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
9977 If there is no item at \a pos, the return value is 0.
9979 \see plottableAt, layoutElementAt
9981 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
9983 QCPAbstractItem *resultItem = 0;
9984 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
9986 foreach (QCPAbstractItem *item, mItems)
9988 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
9990 if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
9992 double currentDistance = item->selectTest(pos, false);
9993 if (currentDistance >= 0 && currentDistance < resultDistance)
9996 resultDistance = currentDistance;
10005 Returns whether this QCustomPlot contains the \a item.
10009 bool QCustomPlot::hasItem(QCPAbstractItem *item) const
10011 return mItems.contains(item);
10015 Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is
10018 Layer names are case-sensitive.
10020 \see addLayer, moveLayer, removeLayer
10022 QCPLayer *QCustomPlot::layer(const QString &name) const
10024 foreach (QCPLayer *layer, mLayers)
10026 if (layer->name() == name)
10034 Returns the layer by \a index. If the index is invalid, 0 is returned.
10036 \see addLayer, moveLayer, removeLayer
10038 QCPLayer *QCustomPlot::layer(int index) const
10040 if (index >= 0 && index < mLayers.size())
10042 return mLayers.at(index);
10045 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
10051 Returns the layer that is set as current layer (see \ref setCurrentLayer).
10053 QCPLayer *QCustomPlot::currentLayer() const
10055 return mCurrentLayer;
10059 Sets the layer with the specified \a name to be the current layer. All layerables (\ref
10060 QCPLayerable), e.g. plottables and items, are created on the current layer.
10062 Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
10064 Layer names are case-sensitive.
10066 \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer
10068 bool QCustomPlot::setCurrentLayer(const QString &name)
10070 if (QCPLayer *newCurrentLayer = layer(name))
10072 return setCurrentLayer(newCurrentLayer);
10075 qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
10082 Sets the provided \a layer to be the current layer.
10084 Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
10086 \see addLayer, moveLayer, removeLayer
10088 bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
10090 if (!mLayers.contains(layer))
10092 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10096 mCurrentLayer = layer;
10101 Returns the number of currently existing layers in the plot
10103 \see layer, addLayer
10105 int QCustomPlot::layerCount() const
10107 return mLayers.size();
10111 Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which
10112 must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer.
10114 Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
10115 valid layer inside this QCustomPlot.
10117 If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
10119 For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
10121 \see layer, moveLayer, removeLayer
10123 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10126 otherLayer = mLayers.last();
10127 if (!mLayers.contains(otherLayer))
10129 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10134 qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
10138 QCPLayer *newLayer = new QCPLayer(this, name);
10139 mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
10140 updateLayerIndices();
10145 Removes the specified \a layer and returns true on success.
10147 All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
10148 \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
10149 cases, the total rendering order of all layerables in the QCustomPlot is preserved.
10151 If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
10152 layer) becomes the new current layer.
10154 It is not possible to remove the last layer of the plot.
10156 \see layer, addLayer, moveLayer
10158 bool QCustomPlot::removeLayer(QCPLayer *layer)
10160 if (!mLayers.contains(layer))
10162 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10165 if (mLayers.size() < 2)
10167 qDebug() << Q_FUNC_INFO << "can't remove last layer";
10171 // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
10172 int removedIndex = layer->index();
10173 bool isFirstLayer = removedIndex==0;
10174 QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
10175 QList<QCPLayerable*> children = layer->children();
10176 if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
10178 for (int i=children.size()-1; i>=0; --i)
10179 children.at(i)->moveToLayer(targetLayer, true);
10180 } else // append normally
10182 for (int i=0; i<children.size(); ++i)
10183 children.at(i)->moveToLayer(targetLayer, false);
10185 // if removed layer is current layer, change current layer to layer below/above:
10186 if (layer == mCurrentLayer)
10187 setCurrentLayer(targetLayer);
10190 mLayers.removeOne(layer);
10191 updateLayerIndices();
10196 Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or
10197 below is controlled with \a insertMode.
10199 Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
10202 \see layer, addLayer, moveLayer
10204 bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
10206 if (!mLayers.contains(layer))
10208 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
10211 if (!mLayers.contains(otherLayer))
10213 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
10217 mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
10218 updateLayerIndices();
10223 Returns the number of axis rects in the plot.
10225 All axis rects can be accessed via QCustomPlot::axisRect().
10227 Initially, only one axis rect exists in the plot.
10229 \see axisRect, axisRects
10231 int QCustomPlot::axisRectCount() const
10233 return axisRects().size();
10237 Returns the axis rect with \a index.
10239 Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were
10240 added, all of them may be accessed with this function in a linear fashion (even when they are
10241 nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).
10243 \see axisRectCount, axisRects
10245 QCPAxisRect *QCustomPlot::axisRect(int index) const
10247 const QList<QCPAxisRect*> rectList = axisRects();
10248 if (index >= 0 && index < rectList.size())
10250 return rectList.at(index);
10253 qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
10259 Returns all axis rects in the plot.
10261 \see axisRectCount, axisRect
10263 QList<QCPAxisRect*> QCustomPlot::axisRects() const
10265 QList<QCPAxisRect*> result;
10266 QStack<QCPLayoutElement*> elementStack;
10268 elementStack.push(mPlotLayout);
10270 while (!elementStack.isEmpty())
10272 foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
10276 elementStack.push(element);
10277 if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
10287 Returns the layout element at pixel position \a pos. If there is no element at that position,
10290 Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on
10291 any of its parent elements is set to false, it will not be considered.
10293 \see itemAt, plottableAt
10295 QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
10297 QCPLayoutElement *currentElement = mPlotLayout;
10298 bool searchSubElements = true;
10299 while (searchSubElements && currentElement)
10301 searchSubElements = false;
10302 foreach (QCPLayoutElement *subElement, currentElement->elements(false))
10304 if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
10306 currentElement = subElement;
10307 searchSubElements = true;
10312 return currentElement;
10316 Returns the axes that currently have selected parts, i.e. whose selection state is not \ref
10319 \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts,
10320 QCPAxis::setSelectableParts
10322 QList<QCPAxis*> QCustomPlot::selectedAxes() const
10324 QList<QCPAxis*> result, allAxes;
10325 foreach (QCPAxisRect *rect, axisRects())
10326 allAxes << rect->axes();
10328 foreach (QCPAxis *axis, allAxes)
10330 if (axis->selectedParts() != QCPAxis::spNone)
10331 result.append(axis);
10338 Returns the legends that currently have selected parts, i.e. whose selection state is not \ref
10341 \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts,
10342 QCPLegend::setSelectableParts, QCPLegend::selectedItems
10344 QList<QCPLegend*> QCustomPlot::selectedLegends() const
10346 QList<QCPLegend*> result;
10348 QStack<QCPLayoutElement*> elementStack;
10350 elementStack.push(mPlotLayout);
10352 while (!elementStack.isEmpty())
10354 foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
10358 elementStack.push(subElement);
10359 if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
10361 if (leg->selectedParts() != QCPLegend::spNone)
10362 result.append(leg);
10372 Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
10374 Since calling this function is not a user interaction, this does not emit the \ref
10375 selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
10376 objects were previously selected.
10378 \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
10380 void QCustomPlot::deselectAll()
10382 foreach (QCPLayer *layer, mLayers)
10384 foreach (QCPLayerable *layerable, layer->children())
10385 layerable->deselectEvent(0);
10390 Causes a complete replot into the internal buffer. Finally, update() is called, to redraw the
10391 buffer on the QCustomPlot widget surface. This is the method that must be called to make changes,
10392 for example on the axis ranges or data points of graphs, visible.
10394 Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the
10395 QCustomPlot widget and user interactions (object selection and range dragging/zooming).
10397 Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref
10398 afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two
10399 signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
10402 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
10404 if (mReplotting) // incase signals loop back to replot slot
10406 mReplotting = true;
10407 emit beforeReplot();
10409 mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
10410 QCPPainter painter;
10411 painter.begin(&mPaintBuffer);
10412 if (painter.isActive())
10414 painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
10415 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
10416 painter.fillRect(mViewport, mBackgroundBrush);
10419 if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
10423 } else // might happen if QCustomPlot has width or height zero
10424 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
10426 emit afterReplot();
10427 mReplotting = false;
10431 Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
10433 if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true
10434 (QCPLayerable::setVisible), will be used to rescale the axes.
10436 \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale
10438 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
10440 QList<QCPAxis*> allAxes;
10441 foreach (QCPAxisRect *rect, axisRects())
10442 allAxes << rect->axes();
10444 foreach (QCPAxis *axis, allAxes)
10445 axis->rescale(onlyVisiblePlottables);
10449 Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
10450 of texts and lines will be derived from the specified \a width and \a height. This means, the
10451 output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
10452 pixel width and height. If either \a width or \a height is zero, the exported image will have the
10453 same dimensions as the QCustomPlot widget currently has.
10455 \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
10456 are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
10457 zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see the QPainter
10458 and QPen documentation.
10460 The objects of the plot will appear in the current selection state. If you don't want any
10461 selected objects to be painted in their selected look, deselect everything with \ref deselectAll
10462 before calling this function.
10464 Returns true on success.
10467 \li If you plan on editing the exported PDF file with a vector graphics editor like
10468 Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
10469 (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
10470 \li If calling this function inside the constructor of the parent of the QCustomPlot widget
10471 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10472 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10473 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10474 aren't defined yet inside the constructor, so you would get an image that has strange
10477 \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
10480 \note On Android systems, this method does nothing and issues an according qDebug warning
10481 message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
10483 \see savePng, saveBmp, saveJpg, saveRastered
10485 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
10487 bool success = false;
10488 #ifdef QT_NO_PRINTER
10490 Q_UNUSED(noCosmeticPen)
10493 qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
10495 int newWidth, newHeight;
10496 if (width == 0 || height == 0)
10498 newWidth = this->width();
10499 newHeight = this->height();
10503 newHeight = height;
10506 QPrinter printer(QPrinter::ScreenResolution);
10507 printer.setOutputFileName(fileName);
10508 printer.setOutputFormat(QPrinter::PdfFormat);
10509 printer.setColorMode(QPrinter::Color);
10510 printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
10511 printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
10512 QRect oldViewport = viewport();
10513 setViewport(QRect(0, 0, newWidth, newHeight));
10514 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
10515 printer.setFullPage(true);
10516 printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
10518 QPageLayout pageLayout;
10519 pageLayout.setMode(QPageLayout::FullPageMode);
10520 pageLayout.setOrientation(QPageLayout::Portrait);
10521 pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
10522 pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
10523 printer.setPageLayout(pageLayout);
10525 QCPPainter printpainter;
10526 if (printpainter.begin(&printer))
10528 printpainter.setMode(QCPPainter::pmVectorized);
10529 printpainter.setMode(QCPPainter::pmNoCaching);
10530 printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
10531 printpainter.setWindow(mViewport);
10532 if (mBackgroundBrush.style() != Qt::NoBrush &&
10533 mBackgroundBrush.color() != Qt::white &&
10534 mBackgroundBrush.color() != Qt::transparent &&
10535 mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
10536 printpainter.fillRect(viewport(), mBackgroundBrush);
10537 draw(&printpainter);
10538 printpainter.end();
10541 setViewport(oldViewport);
10542 #endif // QT_NO_PRINTER
10547 Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
10548 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10549 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10550 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10552 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10553 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10554 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10555 200*200 pixel resolution.
10557 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10558 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10559 QCustomPlot to place objects with sub-pixel accuracy.
10561 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10562 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10563 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10564 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10565 aren't defined yet inside the constructor, so you would get an image that has strange
10568 The objects of the plot will appear in the current selection state. If you don't want any selected
10569 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10572 If you want the PNG to have a transparent background, call \ref setBackground(const QBrush
10573 &brush) with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving.
10575 PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10576 -1 to use the default setting.
10578 Returns true on success. If this function fails, most likely the PNG format isn't supported by
10579 the system, see Qt docs about QImageWriter::supportedImageFormats().
10581 \see savePdf, saveBmp, saveJpg, saveRastered
10583 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
10585 return saveRastered(fileName, width, height, scale, "PNG", quality);
10589 Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
10590 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10591 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10592 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10594 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10595 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10596 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10597 200*200 pixel resolution.
10599 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10600 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10601 QCustomPlot to place objects with sub-pixel accuracy.
10603 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10604 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10605 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10606 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10607 aren't defined yet inside the constructor, so you would get an image that has strange
10610 The objects of the plot will appear in the current selection state. If you don't want any selected
10611 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10614 JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
10615 -1 to use the default setting.
10617 Returns true on success. If this function fails, most likely the JPG format isn't supported by
10618 the system, see Qt docs about QImageWriter::supportedImageFormats().
10620 \see savePdf, savePng, saveBmp, saveRastered
10622 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
10624 return saveRastered(fileName, width, height, scale, "JPG", quality);
10628 Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
10629 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
10630 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
10631 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
10633 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
10634 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
10635 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
10636 200*200 pixel resolution.
10638 If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
10639 temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
10640 QCustomPlot to place objects with sub-pixel accuracy.
10642 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
10643 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
10644 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
10645 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
10646 aren't defined yet inside the constructor, so you would get an image that has strange
10649 The objects of the plot will appear in the current selection state. If you don't want any selected
10650 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
10653 Returns true on success. If this function fails, most likely the BMP format isn't supported by
10654 the system, see Qt docs about QImageWriter::supportedImageFormats().
10656 \see savePdf, savePng, saveJpg, saveRastered
10658 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
10660 return saveRastered(fileName, width, height, scale, "BMP");
10665 Returns a minimum size hint that corresponds to the minimum size of the top level layout
10666 (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum
10667 size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot.
10668 This is especially important, when placed in a QLayout where other components try to take in as
10669 much space as possible (e.g. QMdiArea).
10671 QSize QCustomPlot::minimumSizeHint() const
10673 return mPlotLayout->minimumSizeHint();
10678 Returns a size hint that is the same as \ref minimumSizeHint.
10681 QSize QCustomPlot::sizeHint() const
10683 return mPlotLayout->minimumSizeHint();
10688 Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but
10689 draws the internal buffer on the widget surface.
10691 void QCustomPlot::paintEvent(QPaintEvent *event)
10694 QPainter painter(this);
10695 painter.drawPixmap(0, 0, mPaintBuffer);
10700 Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
10701 the new size. The viewport (which becomes the outer rect of mPlotLayout) is resized
10702 appropriately. Finally a \ref replot is performed.
10704 void QCustomPlot::resizeEvent(QResizeEvent *event)
10706 // resize and repaint the buffer:
10707 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
10708 QSize pbSize = event->size();
10709 pbSize *= devicePixelRatio();
10710 mPaintBuffer = QPixmap(pbSize);
10711 mPaintBuffer.setDevicePixelRatio(devicePixelRatio());
10713 mPaintBuffer = QPixmap(event->size());
10715 setViewport(rect());
10716 replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
10721 Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then emits
10722 the specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref
10723 axisDoubleClick, etc.). Finally determines the affected layout element and forwards the event to
10726 \see mousePressEvent, mouseReleaseEvent
10728 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
10730 emit mouseDoubleClick(event);
10733 QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
10735 // emit specialized object double click signals:
10736 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10737 emit plottableDoubleClick(ap, event);
10738 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10739 emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10740 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10741 emit itemDoubleClick(ai, event);
10742 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10743 emit legendDoubleClick(lg, 0, event);
10744 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10745 emit legendDoubleClick(li->parentLegend(), li, event);
10746 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10747 emit titleDoubleClick(event, pt);
10749 // call double click event of affected layout element:
10750 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10751 el->mouseDoubleClickEvent(event);
10753 // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
10754 if (mMouseEventElement)
10756 mMouseEventElement->mouseReleaseEvent(event);
10757 mMouseEventElement = 0;
10760 //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
10765 Event handler for when a mouse button is pressed. Emits the mousePress signal. Then determines
10766 the affected layout element and forwards the event to it.
10768 \see mouseMoveEvent, mouseReleaseEvent
10770 void QCustomPlot::mousePressEvent(QMouseEvent *event)
10772 emit mousePress(event);
10773 mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
10775 // call event of affected layout element:
10776 mMouseEventElement = layoutElementAt(event->pos());
10777 if (mMouseEventElement)
10778 mMouseEventElement->mousePressEvent(event);
10780 QWidget::mousePressEvent(event);
10785 Event handler for when the cursor is moved. Emits the \ref mouseMove signal.
10787 If a layout element has mouse capture focus (a mousePressEvent happened on top of the layout
10788 element before), the mouseMoveEvent is forwarded to that element.
10790 \see mousePressEvent, mouseReleaseEvent
10792 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
10794 emit mouseMove(event);
10796 // call event of affected layout element:
10797 if (mMouseEventElement)
10798 mMouseEventElement->mouseMoveEvent(event);
10800 QWidget::mouseMoveEvent(event);
10805 Event handler for when a mouse button is released. Emits the \ref mouseRelease signal.
10807 If the mouse was moved less than a certain threshold in any direction since the \ref
10808 mousePressEvent, it is considered a click which causes the selection mechanism (if activated via
10809 \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse
10810 click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.)
10812 If a layout element has mouse capture focus (a \ref mousePressEvent happened on top of the layout
10813 element before), the \ref mouseReleaseEvent is forwarded to that element.
10815 \see mousePressEvent, mouseMoveEvent
10817 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
10819 emit mouseRelease(event);
10820 bool doReplot = false;
10822 if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
10824 if (event->button() == Qt::LeftButton)
10826 // handle selection mechanism:
10828 QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
10829 bool selectionStateChanged = false;
10830 bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
10831 // deselect all other layerables if not additive selection:
10834 foreach (QCPLayer *layer, mLayers)
10836 foreach (QCPLayerable *layerable, layer->children())
10838 if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
10840 bool selChanged = false;
10841 layerable->deselectEvent(&selChanged);
10842 selectionStateChanged |= selChanged;
10847 if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
10849 // a layerable was actually clicked, call its selectEvent:
10850 bool selChanged = false;
10851 clickedLayerable->selectEvent(event, additive, details, &selChanged);
10852 selectionStateChanged |= selChanged;
10855 if (selectionStateChanged)
10856 emit selectionChangedByUser();
10859 // emit specialized object click signals:
10861 QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false
10862 if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
10863 emit plottableClick(ap, event);
10864 else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
10865 emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
10866 else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
10867 emit itemClick(ai, event);
10868 else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
10869 emit legendClick(lg, 0, event);
10870 else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
10871 emit legendClick(li->parentLegend(), li, event);
10872 else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
10873 emit titleClick(event, pt);
10876 // call event of affected layout element:
10877 if (mMouseEventElement)
10879 mMouseEventElement->mouseReleaseEvent(event);
10880 mMouseEventElement = 0;
10883 if (doReplot || noAntialiasingOnDrag())
10886 QWidget::mouseReleaseEvent(event);
10891 Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then
10892 determines the affected layout element and forwards the event to it.
10895 void QCustomPlot::wheelEvent(QWheelEvent *event)
10897 emit mouseWheel(event);
10899 // call event of affected layout element:
10900 if (QCPLayoutElement *el = layoutElementAt(event->pos()))
10901 el->wheelEvent(event);
10903 QWidget::wheelEvent(event);
10908 This is the main draw function. It draws the entire plot, including background pixmap, with the
10909 specified \a painter. Note that it does not fill the background with the background brush (as the
10910 user may specify with \ref setBackground(const QBrush &brush)), this is up to the respective
10911 functions calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter).
10913 void QCustomPlot::draw(QCPPainter *painter)
10915 // run through layout phases:
10916 mPlotLayout->update(QCPLayoutElement::upPreparation);
10917 mPlotLayout->update(QCPLayoutElement::upMargins);
10918 mPlotLayout->update(QCPLayoutElement::upLayout);
10920 // draw viewport background pixmap:
10921 drawBackground(painter);
10923 // draw all layered objects (grid, axes, plottables, items, legend,...):
10924 foreach (QCPLayer *layer, mLayers)
10926 foreach (QCPLayerable *child, layer->children())
10928 if (child->realVisibility())
10931 painter->setClipRect(child->clipRect().translated(0, -1));
10932 child->applyDefaultAntialiasingHint(painter);
10933 child->draw(painter);
10934 painter->restore();
10939 /* Debug code to draw all layout element rects
10940 foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
10942 painter->setBrush(Qt::NoBrush);
10943 painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
10944 painter->drawRect(el->rect());
10945 painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
10946 painter->drawRect(el->outerRect());
10953 Draws the viewport background pixmap of the plot.
10955 If a pixmap was provided via \ref setBackground, this function buffers the scaled version
10956 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
10957 the viewport with the provided \a painter. The scaled version is buffered in
10958 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
10959 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
10960 dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
10963 Note that this function does not draw a fill with the background brush (\ref setBackground(const
10964 QBrush &brush)) beneath the pixmap.
10966 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
10968 void QCustomPlot::drawBackground(QCPPainter *painter)
10970 // Note: background color is handled in individual replot/save functions
10972 // draw background pixmap (on top of fill, if brush specified):
10973 if (!mBackgroundPixmap.isNull())
10975 if (mBackgroundScaled)
10977 // check whether mScaledBackground needs to be updated:
10978 QSize scaledSize(mBackgroundPixmap.size());
10979 scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
10980 if (mScaledBackgroundPixmap.size() != scaledSize)
10981 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
10982 painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
10985 painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
10993 This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot
10994 so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly.
10996 void QCustomPlot::axisRemoved(QCPAxis *axis)
11000 if (xAxis2 == axis)
11004 if (yAxis2 == axis)
11007 // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
11012 This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so
11013 it may clear its QCustomPlot::legend member accordingly.
11015 void QCustomPlot::legendRemoved(QCPLegend *legend)
11017 if (this->legend == legend)
11023 Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called
11024 after every operation that changes the layer indices, like layer removal, layer creation, layer
11027 void QCustomPlot::updateLayerIndices() const
11029 for (int i=0; i<mLayers.size(); ++i)
11030 mLayers.at(i)->mIndex = i;
11035 Returns the layerable at pixel position \a pos. If \a onlySelectable is set to true, only those
11036 layerables that are selectable will be considered. (Layerable subclasses communicate their
11037 selectability via the QCPLayerable::selectTest method, by returning -1.)
11039 \a selectionDetails is an output parameter that contains selection specifics of the affected
11040 layerable. This is useful if the respective layerable shall be given a subsequent
11041 QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains
11042 information about which part of the layerable was hit, in multi-part layerables (e.g.
11043 QCPAxis::SelectablePart).
11045 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
11047 for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
11049 const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
11050 double minimumDistance = selectionTolerance()*1.1;
11051 QCPLayerable *minimumDistanceLayerable = 0;
11052 for (int i=layerables.size()-1; i>=0; --i)
11054 if (!layerables.at(i)->realVisibility())
11057 double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
11058 if (dist >= 0 && dist < minimumDistance)
11060 minimumDistance = dist;
11061 minimumDistanceLayerable = layerables.at(i);
11062 if (selectionDetails) *selectionDetails = details;
11065 if (minimumDistance < selectionTolerance())
11066 return minimumDistanceLayerable;
11072 Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is
11073 sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead
11074 to a full resolution file with width 200.) If the \a format supports compression, \a quality may
11075 be between 0 and 100 to control it.
11077 Returns true on success. If this function fails, most likely the given \a format isn't supported
11078 by the system, see Qt docs about QImageWriter::supportedImageFormats().
11080 \see saveBmp, saveJpg, savePng, savePdf
11082 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
11084 QPixmap buffer = toPixmap(width, height, scale);
11085 if (!buffer.isNull())
11086 return buffer.save(fileName, format, quality);
11092 Renders the plot to a pixmap and returns it.
11094 The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and
11095 scale 2.0 lead to a full resolution pixmap with width 200.)
11097 \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf
11099 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
11101 // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
11102 int newWidth, newHeight;
11103 if (width == 0 || height == 0)
11105 newWidth = this->width();
11106 newHeight = this->height();
11110 newHeight = height;
11112 int scaledWidth = qRound(scale*newWidth);
11113 int scaledHeight = qRound(scale*newHeight);
11115 QPixmap result(scaledWidth, scaledHeight);
11116 result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
11117 QCPPainter painter;
11118 painter.begin(&result);
11119 if (painter.isActive())
11121 QRect oldViewport = viewport();
11122 setViewport(QRect(0, 0, newWidth, newHeight));
11123 painter.setMode(QCPPainter::pmNoCaching);
11124 if (!qFuzzyCompare(scale, 1.0))
11126 if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
11127 painter.setMode(QCPPainter::pmNonCosmetic);
11128 painter.scale(scale, scale);
11130 if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
11131 painter.fillRect(mViewport, mBackgroundBrush);
11133 setViewport(oldViewport);
11135 } else // might happen if pixmap has width or height zero
11137 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
11144 Renders the plot using the passed \a painter.
11146 The plot is sized to \a width and \a height in pixels. If the \a painter's scale is not 1.0, the resulting plot will
11147 appear scaled accordingly.
11149 \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter
11150 on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with
11151 the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter.
11155 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
11157 // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
11158 int newWidth, newHeight;
11159 if (width == 0 || height == 0)
11161 newWidth = this->width();
11162 newHeight = this->height();
11166 newHeight = height;
11169 if (painter->isActive())
11171 QRect oldViewport = viewport();
11172 setViewport(QRect(0, 0, newWidth, newHeight));
11173 painter->setMode(QCPPainter::pmNoCaching);
11174 // warning: the following is different in toPixmap, because a solid background color is applied there via QPixmap::fill
11175 // here, we need to do this via QPainter::fillRect.
11176 if (mBackgroundBrush.style() != Qt::NoBrush)
11177 painter->fillRect(mViewport, mBackgroundBrush);
11179 setViewport(oldViewport);
11181 qDebug() << Q_FUNC_INFO << "Passed painter is not active";
11185 ////////////////////////////////////////////////////////////////////////////////////////////////////
11186 //////////////////// QCPColorGradient
11187 ////////////////////////////////////////////////////////////////////////////////////////////////////
11189 /*! \class QCPColorGradient
11190 \brief Defines a color gradient for use with e.g. \ref QCPColorMap
11192 This class describes a color gradient which can be used to encode data with color. For example,
11193 QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which
11194 take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
11195 with a \a position from 0 to 1. In between these defined color positions, the
11196 color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
11198 Alternatively, load one of the preset color gradients shown in the image below, with \ref
11199 loadPreset, or by directly specifying the preset in the constructor.
11201 \image html QCPColorGradient.png
11203 The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
11204 converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
11205 GradientPreset to all the \a setGradient methods, e.g.:
11207 colorMap->setGradient(QCPColorGradient::gpHot);
11210 The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
11211 color gradient shall be applied periodically (wrapping around) to data values that lie outside
11212 the data range specified on the plottable instance can be controlled with \ref setPeriodic.
11216 Constructs a new QCPColorGradient initialized with the colors and color interpolation according
11219 The color level count is initialized to 350.
11221 QCPColorGradient::QCPColorGradient(GradientPreset preset) :
11223 mColorInterpolation(ciRGB),
11225 mColorBufferInvalidated(true)
11227 mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
11228 loadPreset(preset);
11231 /* undocumented operator */
11232 bool QCPColorGradient::operator==(const QCPColorGradient &other) const
11234 return ((other.mLevelCount == this->mLevelCount) &&
11235 (other.mColorInterpolation == this->mColorInterpolation) &&
11236 (other.mPeriodic == this->mPeriodic) &&
11237 (other.mColorStops == this->mColorStops));
11241 Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
11242 is typically enough to create a smooth appearance.
11244 \image html QCPColorGradient-levelcount.png
11246 void QCPColorGradient::setLevelCount(int n)
11250 qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
11253 if (n != mLevelCount)
11256 mColorBufferInvalidated = true;
11261 Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
11262 colors are the values of the passed QMap \a colorStops. In between these color stops, the color
11263 is interpolated according to \ref setColorInterpolation.
11265 A more convenient way to create a custom gradient may be to clear all color stops with \ref
11266 clearColorStops and then adding them one by one with \ref setColorStopAt.
11268 \see clearColorStops
11270 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
11272 mColorStops = colorStops;
11273 mColorBufferInvalidated = true;
11277 Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
11278 these color stops, the color is interpolated according to \ref setColorInterpolation.
11280 \see setColorStops, clearColorStops
11282 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
11284 mColorStops.insert(position, color);
11285 mColorBufferInvalidated = true;
11289 Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
11290 interpolated linearly in RGB or in HSV color space.
11292 For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
11293 whereas in HSV space the intermediate color is yellow.
11295 void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
11297 if (interpolation != mColorInterpolation)
11299 mColorInterpolation = interpolation;
11300 mColorBufferInvalidated = true;
11305 Sets whether data points that are outside the configured data range (e.g. \ref
11306 QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
11307 they all have the same color, corresponding to the respective gradient boundary color.
11309 \image html QCPColorGradient-periodic.png
11311 As shown in the image above, gradients that have the same start and end color are especially
11312 suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
11313 the color map. A preset that has this property is \ref gpHues.
11315 In practice, using periodic color gradients makes sense when the data corresponds to a periodic
11316 dimension, such as an angle or a phase. If this is not the case, the color encoding might become
11317 ambiguous, because multiple different data values are shown as the same color.
11319 void QCPColorGradient::setPeriodic(bool enabled)
11321 mPeriodic = enabled;
11325 This method is used to quickly convert a \a data array to colors. The colors will be output in
11326 the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
11327 function. The data range that shall be used for mapping the data value to the gradient is passed
11328 in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
11331 if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
11332 set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
11333 array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
11334 is addressed <tt>data[i*dataIndexFactor]</tt>.
11336 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
11338 // If you change something here, make sure to also adapt ::color()
11341 qDebug() << Q_FUNC_INFO << "null pointer given as data";
11346 qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
11349 if (mColorBufferInvalidated)
11350 updateColorBuffer();
11354 const double posToIndexFactor = (mLevelCount-1)/range.size();
11357 for (int i=0; i<n; ++i)
11359 int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
11361 index += mLevelCount;
11362 scanLine[i] = mColorBuffer.at(index);
11366 for (int i=0; i<n; ++i)
11368 int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
11371 else if (index >= mLevelCount)
11372 index = mLevelCount-1;
11373 scanLine[i] = mColorBuffer.at(index);
11376 } else // logarithmic == true
11380 for (int i=0; i<n; ++i)
11382 int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
11384 index += mLevelCount;
11385 scanLine[i] = mColorBuffer.at(index);
11389 for (int i=0; i<n; ++i)
11391 int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11394 else if (index >= mLevelCount)
11395 index = mLevelCount-1;
11396 scanLine[i] = mColorBuffer.at(index);
11404 This method is used to colorize a single data value given in \a position, to colors. The data
11405 range that shall be used for mapping the data value to the gradient is passed in \a range. \a
11406 logarithmic indicates whether the data value shall be mapped to a color logarithmically.
11408 If an entire array of data values shall be converted, rather use \ref colorize, for better
11411 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
11413 // If you change something here, make sure to also adapt ::colorize()
11414 if (mColorBufferInvalidated)
11415 updateColorBuffer();
11418 index = (position-range.lower)*(mLevelCount-1)/range.size();
11420 index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
11423 index = index % mLevelCount;
11425 index += mLevelCount;
11430 else if (index >= mLevelCount)
11431 index = mLevelCount-1;
11433 return mColorBuffer.at(index);
11437 Clears the current color stops and loads the specified \a preset. A preset consists of predefined
11438 color stops and the corresponding color interpolation method.
11440 The available presets are:
11441 \image html QCPColorGradient.png
11443 void QCPColorGradient::loadPreset(GradientPreset preset)
11449 setColorInterpolation(ciRGB);
11450 setColorStopAt(0, Qt::black);
11451 setColorStopAt(1, Qt::white);
11454 setColorInterpolation(ciRGB);
11455 setColorStopAt(0, QColor(50, 0, 0));
11456 setColorStopAt(0.2, QColor(180, 10, 0));
11457 setColorStopAt(0.4, QColor(245, 50, 0));
11458 setColorStopAt(0.6, QColor(255, 150, 10));
11459 setColorStopAt(0.8, QColor(255, 255, 50));
11460 setColorStopAt(1, QColor(255, 255, 255));
11463 setColorInterpolation(ciRGB);
11464 setColorStopAt(0, QColor(0, 0, 50));
11465 setColorStopAt(0.2, QColor(0, 10, 180));
11466 setColorStopAt(0.4, QColor(0, 50, 245));
11467 setColorStopAt(0.6, QColor(10, 150, 255));
11468 setColorStopAt(0.8, QColor(50, 255, 255));
11469 setColorStopAt(1, QColor(255, 255, 255));
11472 setColorInterpolation(ciHSV);
11473 setColorStopAt(0, QColor(10, 20, 30));
11474 setColorStopAt(1, QColor(250, 255, 250));
11477 setColorInterpolation(ciHSV);
11478 setColorStopAt(0, QColor(0, 0, 255));
11479 setColorStopAt(1, QColor(255, 250, 250));
11482 setColorInterpolation(ciRGB);
11483 setColorStopAt(0, QColor(70, 170, 210));
11484 setColorStopAt(0.20, QColor(90, 160, 180));
11485 setColorStopAt(0.25, QColor(45, 130, 175));
11486 setColorStopAt(0.30, QColor(100, 140, 125));
11487 setColorStopAt(0.5, QColor(100, 140, 100));
11488 setColorStopAt(0.6, QColor(130, 145, 120));
11489 setColorStopAt(0.7, QColor(140, 130, 120));
11490 setColorStopAt(0.9, QColor(180, 190, 190));
11491 setColorStopAt(1, QColor(210, 210, 230));
11494 setColorInterpolation(ciHSV);
11495 setColorStopAt(0, QColor(50, 10, 10));
11496 setColorStopAt(0.45, QColor(0, 0, 255));
11497 setColorStopAt(0.8, QColor(0, 255, 255));
11498 setColorStopAt(1, QColor(0, 255, 0));
11501 setColorInterpolation(ciRGB);
11502 setColorStopAt(0, QColor(0, 0, 50));
11503 setColorStopAt(0.15, QColor(20, 0, 120));
11504 setColorStopAt(0.33, QColor(200, 30, 140));
11505 setColorStopAt(0.6, QColor(255, 100, 0));
11506 setColorStopAt(0.85, QColor(255, 255, 40));
11507 setColorStopAt(1, QColor(255, 255, 255));
11510 setColorInterpolation(ciRGB);
11511 setColorStopAt(0, QColor(50, 255, 255));
11512 setColorStopAt(0.18, QColor(10, 70, 255));
11513 setColorStopAt(0.28, QColor(10, 10, 190));
11514 setColorStopAt(0.5, QColor(0, 0, 0));
11515 setColorStopAt(0.72, QColor(190, 10, 10));
11516 setColorStopAt(0.82, QColor(255, 70, 10));
11517 setColorStopAt(1, QColor(255, 255, 50));
11520 setColorInterpolation(ciHSV);
11521 setColorStopAt(0, QColor(50, 0, 50));
11522 setColorStopAt(0.15, QColor(0, 0, 255));
11523 setColorStopAt(0.35, QColor(0, 255, 255));
11524 setColorStopAt(0.6, QColor(255, 255, 0));
11525 setColorStopAt(0.75, QColor(255, 30, 0));
11526 setColorStopAt(1, QColor(50, 0, 0));
11529 setColorInterpolation(ciRGB);
11530 setColorStopAt(0, QColor(0, 0, 100));
11531 setColorStopAt(0.15, QColor(0, 50, 255));
11532 setColorStopAt(0.35, QColor(0, 255, 255));
11533 setColorStopAt(0.65, QColor(255, 255, 0));
11534 setColorStopAt(0.85, QColor(255, 30, 0));
11535 setColorStopAt(1, QColor(100, 0, 0));
11538 setColorInterpolation(ciHSV);
11539 setColorStopAt(0, QColor(255, 0, 0));
11540 setColorStopAt(1.0/3.0, QColor(0, 0, 255));
11541 setColorStopAt(2.0/3.0, QColor(0, 255, 0));
11542 setColorStopAt(1, QColor(255, 0, 0));
11548 Clears all color stops.
11550 \see setColorStops, setColorStopAt
11552 void QCPColorGradient::clearColorStops()
11554 mColorStops.clear();
11555 mColorBufferInvalidated = true;
11559 Returns an inverted gradient. The inverted gradient has all properties as this \ref
11560 QCPColorGradient, but the order of the color stops is inverted.
11562 \see setColorStops, setColorStopAt
11564 QCPColorGradient QCPColorGradient::inverted() const
11566 QCPColorGradient result(*this);
11567 result.clearColorStops();
11568 for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
11569 result.setColorStopAt(1.0-it.key(), it.value());
11575 Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
11576 convert positions to colors. This is where the interpolation between color stops is calculated.
11578 void QCPColorGradient::updateColorBuffer()
11580 if (mColorBuffer.size() != mLevelCount)
11581 mColorBuffer.resize(mLevelCount);
11582 if (mColorStops.size() > 1)
11584 double indexToPosFactor = 1.0/(double)(mLevelCount-1);
11585 for (int i=0; i<mLevelCount; ++i)
11587 double position = i*indexToPosFactor;
11588 QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
11589 if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
11591 mColorBuffer[i] = (it-1).value().rgb();
11592 } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
11594 mColorBuffer[i] = it.value().rgb();
11595 } else // position is in between stops (or on an intermediate stop), interpolate color
11597 QMap<double, QColor>::const_iterator high = it;
11598 QMap<double, QColor>::const_iterator low = it-1;
11599 double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
11600 switch (mColorInterpolation)
11604 mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
11605 (1-t)*low.value().green() + t*high.value().green(),
11606 (1-t)*low.value().blue() + t*high.value().blue());
11611 QColor lowHsv = low.value().toHsv();
11612 QColor highHsv = high.value().toHsv();
11614 double hueDiff = highHsv.hueF()-lowHsv.hueF();
11616 hue = lowHsv.hueF() - t*(1.0-hueDiff);
11617 else if (hueDiff < -0.5)
11618 hue = lowHsv.hueF() + t*(1.0+hueDiff);
11620 hue = lowHsv.hueF() + t*hueDiff;
11621 if (hue < 0) hue += 1.0;
11622 else if (hue >= 1.0) hue -= 1.0;
11623 mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
11629 } else if (mColorStops.size() == 1)
11631 mColorBuffer.fill(mColorStops.constBegin().value().rgb());
11632 } else // mColorStops is empty, fill color buffer with black
11634 mColorBuffer.fill(qRgb(0, 0, 0));
11636 mColorBufferInvalidated = false;
11640 ////////////////////////////////////////////////////////////////////////////////////////////////////
11641 //////////////////// QCPAxisRect
11642 ////////////////////////////////////////////////////////////////////////////////////////////////////
11644 /*! \class QCPAxisRect
11645 \brief Holds multiple axes and arranges them in a rectangular shape.
11647 This class represents an axis rect, a rectangular area that is bounded on all sides with an
11648 arbitrary number of axes.
11650 Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
11651 layout system allows to have multiple axis rects, e.g. arranged in a grid layout
11652 (QCustomPlot::plotLayout).
11654 By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
11655 accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
11656 If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
11657 invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
11658 addAxes. To remove an axis, use \ref removeAxis.
11660 The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
11661 setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
11662 explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
11663 placed on other layers, independently of the axis rect.
11665 Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
11666 insetLayout and can be used to have other layout elements (or even other layouts with multiple
11667 elements) hovering inside the axis rect.
11669 If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
11670 behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
11671 is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
11672 via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
11673 only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
11676 \image html AxisRectSpacingOverview.png
11677 <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
11678 line on the far left indicates the viewport/widget border.</center>
11681 /* start documentation of inline functions */
11683 /*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
11685 Returns the inset layout of this axis rect. It can be used to place other layout elements (or
11686 even layouts with multiple other elements) inside/on top of an axis rect.
11688 \see QCPLayoutInset
11691 /*! \fn int QCPAxisRect::left() const
11693 Returns the pixel position of the left border of this axis rect. Margins are not taken into
11694 account here, so the returned value is with respect to the inner \ref rect.
11697 /*! \fn int QCPAxisRect::right() const
11699 Returns the pixel position of the right border of this axis rect. Margins are not taken into
11700 account here, so the returned value is with respect to the inner \ref rect.
11703 /*! \fn int QCPAxisRect::top() const
11705 Returns the pixel position of the top border of this axis rect. Margins are not taken into
11706 account here, so the returned value is with respect to the inner \ref rect.
11709 /*! \fn int QCPAxisRect::bottom() const
11711 Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
11712 account here, so the returned value is with respect to the inner \ref rect.
11715 /*! \fn int QCPAxisRect::width() const
11717 Returns the pixel width of this axis rect. Margins are not taken into account here, so the
11718 returned value is with respect to the inner \ref rect.
11721 /*! \fn int QCPAxisRect::height() const
11723 Returns the pixel height of this axis rect. Margins are not taken into account here, so the
11724 returned value is with respect to the inner \ref rect.
11727 /*! \fn QSize QCPAxisRect::size() const
11729 Returns the pixel size of this axis rect. Margins are not taken into account here, so the
11730 returned value is with respect to the inner \ref rect.
11733 /*! \fn QPoint QCPAxisRect::topLeft() const
11735 Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
11736 so the returned value is with respect to the inner \ref rect.
11739 /*! \fn QPoint QCPAxisRect::topRight() const
11741 Returns the top right corner of this axis rect in pixels. Margins are not taken into account
11742 here, so the returned value is with respect to the inner \ref rect.
11745 /*! \fn QPoint QCPAxisRect::bottomLeft() const
11747 Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
11748 here, so the returned value is with respect to the inner \ref rect.
11751 /*! \fn QPoint QCPAxisRect::bottomRight() const
11753 Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
11754 here, so the returned value is with respect to the inner \ref rect.
11757 /*! \fn QPoint QCPAxisRect::center() const
11759 Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
11760 returned value is with respect to the inner \ref rect.
11763 /* end documentation of inline functions */
11766 Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
11767 sides, the top and right axes are set invisible initially.
11769 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
11770 QCPLayoutElement(parentPlot),
11771 mBackgroundBrush(Qt::NoBrush),
11772 mBackgroundScaled(true),
11773 mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
11774 mInsetLayout(new QCPLayoutInset),
11775 mRangeDrag(Qt::Horizontal|Qt::Vertical),
11776 mRangeZoom(Qt::Horizontal|Qt::Vertical),
11777 mRangeZoomFactorHorz(0.85),
11778 mRangeZoomFactorVert(0.85),
11781 mInsetLayout->initializeParentPlot(mParentPlot);
11782 mInsetLayout->setParentLayerable(this);
11783 mInsetLayout->setParent(this);
11785 setMinimumSize(50, 50);
11786 setMinimumMargins(QMargins(15, 15, 15, 15));
11787 mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
11788 mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
11789 mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
11790 mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
11792 if (setupDefaultAxes)
11794 QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
11795 QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
11796 QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
11797 QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
11798 setRangeDragAxes(xAxis, yAxis);
11799 setRangeZoomAxes(xAxis, yAxis);
11800 xAxis2->setVisible(false);
11801 yAxis2->setVisible(false);
11802 xAxis->grid()->setVisible(true);
11803 yAxis->grid()->setVisible(true);
11804 xAxis2->grid()->setVisible(false);
11805 yAxis2->grid()->setVisible(false);
11806 xAxis2->grid()->setZeroLinePen(Qt::NoPen);
11807 yAxis2->grid()->setZeroLinePen(Qt::NoPen);
11808 xAxis2->grid()->setVisible(false);
11809 yAxis2->grid()->setVisible(false);
11813 QCPAxisRect::~QCPAxisRect()
11815 delete mInsetLayout;
11818 QList<QCPAxis*> axesList = axes();
11819 for (int i=0; i<axesList.size(); ++i)
11820 removeAxis(axesList.at(i));
11824 Returns the number of axes on the axis rect side specified with \a type.
11828 int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
11830 return mAxes.value(type).size();
11834 Returns the axis with the given \a index on the axis rect side specified with \a type.
11836 \see axisCount, axes
11838 QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
11840 QList<QCPAxis*> ax(mAxes.value(type));
11841 if (index >= 0 && index < ax.size())
11843 return ax.at(index);
11846 qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
11852 Returns all axes on the axis rect sides specified with \a types.
11854 \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
11859 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
11861 QList<QCPAxis*> result;
11862 if (types.testFlag(QCPAxis::atLeft))
11863 result << mAxes.value(QCPAxis::atLeft);
11864 if (types.testFlag(QCPAxis::atRight))
11865 result << mAxes.value(QCPAxis::atRight);
11866 if (types.testFlag(QCPAxis::atTop))
11867 result << mAxes.value(QCPAxis::atTop);
11868 if (types.testFlag(QCPAxis::atBottom))
11869 result << mAxes.value(QCPAxis::atBottom);
11875 Returns all axes of this axis rect.
11877 QList<QCPAxis*> QCPAxisRect::axes() const
11879 QList<QCPAxis*> result;
11880 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11881 while (it.hasNext())
11884 result << it.value();
11890 Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a
11891 new QCPAxis instance is created internally.
11893 You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis to an axis that was
11894 previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
11895 of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
11896 with this axis rect as parent and with the same axis type as specified in \a type. If this is not
11897 the case, a debug output is generated, the axis is not added, and the method returns 0.
11899 This method can not be used to move \a axis between axis rects. The same \a axis instance must
11900 not be added multiple times to the same or different axis rects.
11902 If an axis rect side already contains one or more axes, the lower and upper endings of the new
11903 axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref
11904 QCPLineEnding::esHalfBar.
11906 \see addAxes, setupFullAxesBox
11908 QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
11910 QCPAxis *newAxis = axis;
11913 newAxis = new QCPAxis(this, type);
11914 } else // user provided existing axis instance, do some sanity checks
11916 if (newAxis->axisType() != type)
11918 qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
11921 if (newAxis->axisRect() != this)
11923 qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
11926 if (axes().contains(newAxis))
11928 qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
11932 if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
11934 bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
11935 newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
11936 newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
11938 mAxes[type].append(newAxis);
11943 Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
11944 <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
11946 Returns a list of the added axes.
11948 \see addAxis, setupFullAxesBox
11950 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
11952 QList<QCPAxis*> result;
11953 if (types.testFlag(QCPAxis::atLeft))
11954 result << addAxis(QCPAxis::atLeft);
11955 if (types.testFlag(QCPAxis::atRight))
11956 result << addAxis(QCPAxis::atRight);
11957 if (types.testFlag(QCPAxis::atTop))
11958 result << addAxis(QCPAxis::atTop);
11959 if (types.testFlag(QCPAxis::atBottom))
11960 result << addAxis(QCPAxis::atBottom);
11965 Removes the specified \a axis from the axis rect and deletes it.
11967 Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
11971 bool QCPAxisRect::removeAxis(QCPAxis *axis)
11973 // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
11974 QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
11975 while (it.hasNext())
11978 if (it.value().contains(axis))
11980 mAxes[it.key()].removeOne(axis);
11981 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)
11982 parentPlot()->axisRemoved(axis);
11987 qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
11992 Convenience function to create an axis on each side that doesn't have any axes yet and set their
11993 visibility to true. Further, the top/right axes are assigned the following properties of the
11996 \li range (\ref QCPAxis::setRange)
11997 \li range reversed (\ref QCPAxis::setRangeReversed)
11998 \li scale type (\ref QCPAxis::setScaleType)
11999 \li scale log base (\ref QCPAxis::setScaleLogBase)
12000 \li ticks (\ref QCPAxis::setTicks)
12001 \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
12002 \li sub tick count (\ref QCPAxis::setSubTickCount)
12003 \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
12004 \li tick step (\ref QCPAxis::setTickStep)
12005 \li auto tick step (\ref QCPAxis::setAutoTickStep)
12006 \li number format (\ref QCPAxis::setNumberFormat)
12007 \li number precision (\ref QCPAxis::setNumberPrecision)
12008 \li tick label type (\ref QCPAxis::setTickLabelType)
12009 \li date time format (\ref QCPAxis::setDateTimeFormat)
12010 \li date time spec (\ref QCPAxis::setDateTimeSpec)
12012 Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
12014 If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
12015 and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
12017 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
12019 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
12020 if (axisCount(QCPAxis::atBottom) == 0)
12021 xAxis = addAxis(QCPAxis::atBottom);
12023 xAxis = axis(QCPAxis::atBottom);
12025 if (axisCount(QCPAxis::atLeft) == 0)
12026 yAxis = addAxis(QCPAxis::atLeft);
12028 yAxis = axis(QCPAxis::atLeft);
12030 if (axisCount(QCPAxis::atTop) == 0)
12031 xAxis2 = addAxis(QCPAxis::atTop);
12033 xAxis2 = axis(QCPAxis::atTop);
12035 if (axisCount(QCPAxis::atRight) == 0)
12036 yAxis2 = addAxis(QCPAxis::atRight);
12038 yAxis2 = axis(QCPAxis::atRight);
12040 xAxis->setVisible(true);
12041 yAxis->setVisible(true);
12042 xAxis2->setVisible(true);
12043 yAxis2->setVisible(true);
12044 xAxis2->setTickLabels(false);
12045 yAxis2->setTickLabels(false);
12047 xAxis2->setRange(xAxis->range());
12048 xAxis2->setRangeReversed(xAxis->rangeReversed());
12049 xAxis2->setScaleType(xAxis->scaleType());
12050 xAxis2->setScaleLogBase(xAxis->scaleLogBase());
12051 xAxis2->setTicks(xAxis->ticks());
12052 xAxis2->setAutoTickCount(xAxis->autoTickCount());
12053 xAxis2->setSubTickCount(xAxis->subTickCount());
12054 xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
12055 xAxis2->setTickStep(xAxis->tickStep());
12056 xAxis2->setAutoTickStep(xAxis->autoTickStep());
12057 xAxis2->setNumberFormat(xAxis->numberFormat());
12058 xAxis2->setNumberPrecision(xAxis->numberPrecision());
12059 xAxis2->setTickLabelType(xAxis->tickLabelType());
12060 xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
12061 xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
12063 yAxis2->setRange(yAxis->range());
12064 yAxis2->setRangeReversed(yAxis->rangeReversed());
12065 yAxis2->setScaleType(yAxis->scaleType());
12066 yAxis2->setScaleLogBase(yAxis->scaleLogBase());
12067 yAxis2->setTicks(yAxis->ticks());
12068 yAxis2->setAutoTickCount(yAxis->autoTickCount());
12069 yAxis2->setSubTickCount(yAxis->subTickCount());
12070 yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
12071 yAxis2->setTickStep(yAxis->tickStep());
12072 yAxis2->setAutoTickStep(yAxis->autoTickStep());
12073 yAxis2->setNumberFormat(yAxis->numberFormat());
12074 yAxis2->setNumberPrecision(yAxis->numberPrecision());
12075 yAxis2->setTickLabelType(yAxis->tickLabelType());
12076 yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
12077 yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
12081 connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
12082 connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
12087 Returns a list of all the plottables that are associated with this axis rect.
12089 A plottable is considered associated with an axis rect if its key or value axis (or both) is in
12094 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
12096 // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
12097 QList<QCPAbstractPlottable*> result;
12098 for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
12100 if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
12101 result.append(mParentPlot->mPlottables.at(i));
12107 Returns a list of all the graphs that are associated with this axis rect.
12109 A graph is considered associated with an axis rect if its key or value axis (or both) is in
12112 \see plottables, items
12114 QList<QCPGraph*> QCPAxisRect::graphs() const
12116 // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
12117 QList<QCPGraph*> result;
12118 for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
12120 if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
12121 result.append(mParentPlot->mGraphs.at(i));
12127 Returns a list of all the items that are associated with this axis rect.
12129 An item is considered associated with an axis rect if any of its positions has key or value axis
12130 set to an axis that is in this axis rect, or if any of its positions has \ref
12131 QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
12132 QCPAbstractItem::setClipAxisRect) is set to this axis rect.
12134 \see plottables, graphs
12136 QList<QCPAbstractItem *> QCPAxisRect::items() const
12138 // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
12139 // and miss those items that have this axis rect as clipAxisRect.
12140 QList<QCPAbstractItem*> result;
12141 for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
12143 if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
12145 result.append(mParentPlot->mItems.at(itemId));
12148 QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
12149 for (int posId=0; posId<positions.size(); ++posId)
12151 if (positions.at(posId)->axisRect() == this ||
12152 positions.at(posId)->keyAxis()->axisRect() == this ||
12153 positions.at(posId)->valueAxis()->axisRect() == this)
12155 result.append(mParentPlot->mItems.at(itemId));
12164 This method is called automatically upon replot and doesn't need to be called by users of
12167 Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
12168 and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
12169 QCPInsetLayout::update function.
12171 void QCPAxisRect::update(UpdatePhase phase)
12173 QCPLayoutElement::update(phase);
12177 case upPreparation:
12179 QList<QCPAxis*> allAxes = axes();
12180 for (int i=0; i<allAxes.size(); ++i)
12181 allAxes.at(i)->setupTickVectors();
12186 mInsetLayout->setOuterRect(rect());
12192 // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
12193 mInsetLayout->update(phase);
12196 /* inherits documentation from base class */
12197 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
12199 QList<QCPLayoutElement*> result;
12202 result << mInsetLayout;
12204 result << mInsetLayout->elements(recursive);
12209 /* inherits documentation from base class */
12210 void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
12212 painter->setAntialiasing(false);
12215 /* inherits documentation from base class */
12216 void QCPAxisRect::draw(QCPPainter *painter)
12218 drawBackground(painter);
12222 Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
12223 axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
12224 backgrounds are usually drawn below everything else.
12226 For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
12227 enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
12228 is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
12229 consider using the overloaded version of this function.
12231 Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
12232 setBackground(const QBrush &brush).
12234 \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
12236 void QCPAxisRect::setBackground(const QPixmap &pm)
12238 mBackgroundPixmap = pm;
12239 mScaledBackgroundPixmap = QPixmap();
12244 Sets \a brush as the background brush. The axis rect background will be filled with this brush.
12245 Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
12246 are usually drawn below everything else.
12248 The brush will be drawn before (under) any background pixmap, which may be specified with \ref
12249 setBackground(const QPixmap &pm).
12251 To disable drawing of a background brush, set \a brush to Qt::NoBrush.
12253 \see setBackground(const QPixmap &pm)
12255 void QCPAxisRect::setBackground(const QBrush &brush)
12257 mBackgroundBrush = brush;
12262 Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
12263 shall be scaled in one call.
12265 \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
12267 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
12269 mBackgroundPixmap = pm;
12270 mScaledBackgroundPixmap = QPixmap();
12271 mBackgroundScaled = scaled;
12272 mBackgroundScaledMode = mode;
12276 Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
12277 is set to true, you may control whether and how the aspect ratio of the original pixmap is
12278 preserved with \ref setBackgroundScaledMode.
12280 Note that the scaled version of the original pixmap is buffered, so there is no performance
12281 penalty on replots. (Except when the axis rect dimensions are changed continuously.)
12283 \see setBackground, setBackgroundScaledMode
12285 void QCPAxisRect::setBackgroundScaled(bool scaled)
12287 mBackgroundScaled = scaled;
12291 If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
12292 define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
12293 \see setBackground, setBackgroundScaled
12295 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
12297 mBackgroundScaledMode = mode;
12301 Returns the range drag axis of the \a orientation provided.
12303 \see setRangeDragAxes
12305 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
12307 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
12311 Returns the range zoom axis of the \a orientation provided.
12313 \see setRangeZoomAxes
12315 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
12317 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
12321 Returns the range zoom factor of the \a orientation provided.
12323 \see setRangeZoomFactor
12325 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
12327 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
12331 Sets which axis orientation may be range dragged by the user with mouse interaction.
12332 What orientation corresponds to which specific axis can be set with
12333 \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
12334 default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
12335 is the left axis (yAxis).
12337 To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
12338 QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
12339 Qt::Vertical</tt> as \a orientations.
12341 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12342 contains \ref QCP::iRangeDrag to enable the range dragging interaction.
12344 \see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
12346 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
12348 mRangeDrag = orientations;
12352 Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
12353 corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
12354 QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
12355 axis is the left axis (yAxis).
12357 To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
12358 QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
12359 Qt::Vertical</tt> as \a orientations.
12361 In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
12362 contains \ref QCP::iRangeZoom to enable the range zooming interaction.
12364 \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
12366 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
12368 mRangeZoom = orientations;
12372 Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
12373 on the QCustomPlot widget.
12375 \see setRangeZoomAxes
12377 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
12379 mRangeDragHorzAxis = horizontal;
12380 mRangeDragVertAxis = vertical;
12384 Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
12385 QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
12386 are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
12388 \see setRangeDragAxes
12390 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
12392 mRangeZoomHorzAxis = horizontal;
12393 mRangeZoomVertAxis = vertical;
12397 Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
12398 \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
12399 let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
12400 and which is vertical, can be set with \ref setRangeZoomAxes.
12402 When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
12403 will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
12404 same scrolling direction will zoom out.
12406 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
12408 mRangeZoomFactorHorz = horizontalFactor;
12409 mRangeZoomFactorVert = verticalFactor;
12414 Sets both the horizontal and vertical zoom \a factor.
12416 void QCPAxisRect::setRangeZoomFactor(double factor)
12418 mRangeZoomFactorHorz = factor;
12419 mRangeZoomFactorVert = factor;
12424 Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
12427 If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
12428 according filling inside the axis rect with the provided \a painter.
12430 Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
12431 depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
12432 the axis rect with the provided \a painter. The scaled version is buffered in
12433 mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
12434 the axis rect has changed in a way that requires a rescale of the background pixmap (this is
12435 dependent on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
12438 \see setBackground, setBackgroundScaled, setBackgroundScaledMode
12440 void QCPAxisRect::drawBackground(QCPPainter *painter)
12442 // draw background fill:
12443 if (mBackgroundBrush != Qt::NoBrush)
12444 painter->fillRect(mRect, mBackgroundBrush);
12446 // draw background pixmap (on top of fill, if brush specified):
12447 if (!mBackgroundPixmap.isNull())
12449 if (mBackgroundScaled)
12451 // check whether mScaledBackground needs to be updated:
12452 QSize scaledSize(mBackgroundPixmap.size());
12453 scaledSize.scale(mRect.size(), mBackgroundScaledMode);
12454 if (mScaledBackgroundPixmap.size() != scaledSize)
12455 mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
12456 painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
12459 painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
12466 This function makes sure multiple axes on the side specified with \a type don't collide, but are
12467 distributed according to their respective space requirement (QCPAxis::calculateMargin).
12469 It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
12470 one with index zero.
12472 This function is called by \ref calculateAutoMargin.
12474 void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
12476 const QList<QCPAxis*> axesList = mAxes.value(type);
12477 if (axesList.isEmpty())
12480 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
12481 for (int i=1; i<axesList.size(); ++i)
12483 int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
12484 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)
12486 if (!isFirstVisible)
12487 offset += axesList.at(i)->tickLengthIn();
12488 isFirstVisible = false;
12490 axesList.at(i)->setOffset(offset);
12494 /* inherits documentation from base class */
12495 int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
12497 if (!mAutoMargins.testFlag(side))
12498 qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
12500 updateAxesOffset(QCPAxis::marginSideToAxisType(side));
12502 // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
12503 const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
12504 if (axesList.size() > 0)
12505 return axesList.last()->offset() + axesList.last()->calculateMargin();
12512 Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
12513 pressed, the range dragging interaction is initialized (the actual range manipulation happens in
12514 the \ref mouseMoveEvent).
12516 The mDragging flag is set to true and some anchor points are set that are needed to determine the
12517 distance the mouse was dragged in the mouse move/release events later.
12519 \see mouseMoveEvent, mouseReleaseEvent
12521 void QCPAxisRect::mousePressEvent(QMouseEvent *event)
12523 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)
12524 if (event->buttons() & Qt::LeftButton)
12527 // initialize antialiasing backup in case we start dragging:
12528 if (mParentPlot->noAntialiasingOnDrag())
12530 mAADragBackup = mParentPlot->antialiasedElements();
12531 mNotAADragBackup = mParentPlot->notAntialiasedElements();
12533 // Mouse range dragging interaction:
12534 if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12536 if (mRangeDragHorzAxis)
12537 mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
12538 if (mRangeDragVertAxis)
12539 mDragStartVertRange = mRangeDragVertAxis.data()->range();
12546 Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
12547 preceding \ref mousePressEvent, the range is moved accordingly.
12549 \see mousePressEvent, mouseReleaseEvent
12551 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
12553 // Mouse range dragging interaction:
12554 if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
12556 if (mRangeDrag.testFlag(Qt::Horizontal))
12558 if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
12560 if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
12562 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
12563 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
12564 } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
12566 double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
12567 rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
12571 if (mRangeDrag.testFlag(Qt::Vertical))
12573 if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
12575 if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
12577 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
12578 rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
12579 } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
12581 double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
12582 rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
12586 if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
12588 if (mParentPlot->noAntialiasingOnDrag())
12589 mParentPlot->setNotAntialiasedElements(QCP::aeAll);
12590 mParentPlot->replot();
12595 /* inherits documentation from base class */
12596 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
12600 if (mParentPlot->noAntialiasingOnDrag())
12602 mParentPlot->setAntialiasedElements(mAADragBackup);
12603 mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
12609 Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
12610 ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
12611 the scaling operation is the current cursor position inside the axis rect. The scaling factor is
12612 dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
12613 zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
12615 Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
12616 wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
12617 multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
12618 exponent of the range zoom factor. This takes care of the wheel direction automatically, by
12619 inverting the factor, when the wheel step is negative (f^-1 = 1/f).
12621 void QCPAxisRect::wheelEvent(QWheelEvent *event)
12623 // Mouse range zooming interaction:
12624 if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
12626 if (mRangeZoom != 0)
12629 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
12630 if (mRangeZoom.testFlag(Qt::Horizontal))
12632 factor = qPow(mRangeZoomFactorHorz, wheelSteps);
12633 if (mRangeZoomHorzAxis.data())
12634 mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
12636 if (mRangeZoom.testFlag(Qt::Vertical))
12638 factor = qPow(mRangeZoomFactorVert, wheelSteps);
12639 if (mRangeZoomVertAxis.data())
12640 mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
12642 mParentPlot->replot();
12648 ////////////////////////////////////////////////////////////////////////////////////////////////////
12649 //////////////////// QCPAbstractLegendItem
12650 ////////////////////////////////////////////////////////////////////////////////////////////////////
12652 /*! \class QCPAbstractLegendItem
12653 \brief The abstract base class for all entries in a QCPLegend.
12655 It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
12656 legend, the subclass \ref QCPPlottableLegendItem is more suitable.
12658 Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
12659 that's not even associated with a plottable).
12661 You must implement the following pure virtual functions:
12662 \li \ref draw (from QCPLayerable)
12664 You inherit the following members you may use:
12667 <td>QCPLegend *\b mParentLegend</td>
12668 <td>A pointer to the parent QCPLegend.</td>
12670 <td>QFont \b mFont</td>
12671 <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>
12676 /* start of documentation of signals */
12678 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
12680 This signal is emitted when the selection state of this legend item has changed, either by user
12681 interaction or by a direct call to \ref setSelected.
12684 /* end of documentation of signals */
12687 Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
12688 cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
12690 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
12691 QCPLayoutElement(parent->parentPlot()),
12692 mParentLegend(parent),
12693 mFont(parent->font()),
12694 mTextColor(parent->textColor()),
12695 mSelectedFont(parent->selectedFont()),
12696 mSelectedTextColor(parent->selectedTextColor()),
12700 setLayer(QLatin1String("legend"));
12701 setMargins(QMargins(8, 2, 8, 2));
12705 Sets the default font of this specific legend item to \a font.
12707 \see setTextColor, QCPLegend::setFont
12709 void QCPAbstractLegendItem::setFont(const QFont &font)
12715 Sets the default text color of this specific legend item to \a color.
12717 \see setFont, QCPLegend::setTextColor
12719 void QCPAbstractLegendItem::setTextColor(const QColor &color)
12721 mTextColor = color;
12725 When this legend item is selected, \a font is used to draw generic text, instead of the normal
12726 font set with \ref setFont.
12728 \see setFont, QCPLegend::setSelectedFont
12730 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
12732 mSelectedFont = font;
12736 When this legend item is selected, \a color is used to draw generic text, instead of the normal
12737 color set with \ref setTextColor.
12739 \see setTextColor, QCPLegend::setSelectedTextColor
12741 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
12743 mSelectedTextColor = color;
12747 Sets whether this specific legend item is selectable.
12749 \see setSelectedParts, QCustomPlot::setInteractions
12751 void QCPAbstractLegendItem::setSelectable(bool selectable)
12753 if (mSelectable != selectable)
12755 mSelectable = selectable;
12756 emit selectableChanged(mSelectable);
12761 Sets whether this specific legend item is selected.
12763 It is possible to set the selection state of this item by calling this function directly, even if
12764 setSelectable is set to false.
12766 \see setSelectableParts, QCustomPlot::setInteractions
12768 void QCPAbstractLegendItem::setSelected(bool selected)
12770 if (mSelected != selected)
12772 mSelected = selected;
12773 emit selectionChanged(mSelected);
12777 /* inherits documentation from base class */
12778 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
12781 if (!mParentPlot) return -1;
12782 if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
12785 if (mRect.contains(pos.toPoint()))
12786 return mParentPlot->selectionTolerance()*0.99;
12791 /* inherits documentation from base class */
12792 void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
12794 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
12797 /* inherits documentation from base class */
12798 QRect QCPAbstractLegendItem::clipRect() const
12803 /* inherits documentation from base class */
12804 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12808 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12810 bool selBefore = mSelected;
12811 setSelected(additive ? !mSelected : true);
12812 if (selectionStateChanged)
12813 *selectionStateChanged = mSelected != selBefore;
12817 /* inherits documentation from base class */
12818 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
12820 if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
12822 bool selBefore = mSelected;
12823 setSelected(false);
12824 if (selectionStateChanged)
12825 *selectionStateChanged = mSelected != selBefore;
12829 ////////////////////////////////////////////////////////////////////////////////////////////////////
12830 //////////////////// QCPPlottableLegendItem
12831 ////////////////////////////////////////////////////////////////////////////////////////////////////
12833 /*! \class QCPPlottableLegendItem
12834 \brief A legend item representing a plottable with an icon and the plottable name.
12836 This is the standard legend item for plottables. It displays an icon of the plottable next to the
12837 plottable name. The icon is drawn by the respective plottable itself (\ref
12838 QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
12839 For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
12842 Legend items of this type are always associated with one plottable (retrievable via the
12843 plottable() function and settable with the constructor). You may change the font of the plottable
12844 name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
12845 QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
12847 The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
12848 creates/removes legend items of this type in the default implementation. However, these functions
12849 may be reimplemented such that a different kind of legend item (e.g a direct subclass of
12850 QCPAbstractLegendItem) is used for that plottable.
12852 Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
12853 QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
12854 interface, QCPLegend has specialized functions for handling legend items conveniently, see the
12855 documentation of \ref QCPLegend.
12859 Creates a new legend item associated with \a plottable.
12861 Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
12863 A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
12864 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
12866 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
12867 QCPAbstractLegendItem(parent),
12868 mPlottable(plottable)
12874 Returns the pen that shall be used to draw the icon border, taking into account the selection
12875 state of this item.
12877 QPen QCPPlottableLegendItem::getIconBorderPen() const
12879 return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
12884 Returns the text color that shall be used to draw text, taking into account the selection state
12887 QColor QCPPlottableLegendItem::getTextColor() const
12889 return mSelected ? mSelectedTextColor : mTextColor;
12894 Returns the font that shall be used to draw text, taking into account the selection state of this
12897 QFont QCPPlottableLegendItem::getFont() const
12899 return mSelected ? mSelectedFont : mFont;
12904 Draws the item with \a painter. The size and position of the drawn legend item is defined by the
12905 parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
12906 of this legend item.
12908 void QCPPlottableLegendItem::draw(QCPPainter *painter)
12910 if (!mPlottable) return;
12911 painter->setFont(getFont());
12912 painter->setPen(QPen(getTextColor()));
12913 QSizeF iconSize = mParentLegend->iconSize();
12914 QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12915 QRectF iconRect(mRect.topLeft(), iconSize);
12916 int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
12917 painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
12920 painter->setClipRect(iconRect, Qt::IntersectClip);
12921 mPlottable->drawLegendIcon(painter, iconRect);
12922 painter->restore();
12923 // draw icon border:
12924 if (getIconBorderPen().style() != Qt::NoPen)
12926 painter->setPen(getIconBorderPen());
12927 painter->setBrush(Qt::NoBrush);
12928 painter->drawRect(iconRect);
12934 Calculates and returns the size of this item. This includes the icon, the text and the padding in
12937 QSize QCPPlottableLegendItem::minimumSizeHint() const
12939 if (!mPlottable) return QSize();
12940 QSize result(0, 0);
12942 QFontMetrics fontMetrics(getFont());
12943 QSize iconSize = mParentLegend->iconSize();
12944 textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
12945 result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
12946 result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
12951 ////////////////////////////////////////////////////////////////////////////////////////////////////
12952 //////////////////// QCPLegend
12953 ////////////////////////////////////////////////////////////////////////////////////////////////////
12955 /*! \class QCPLegend
12956 \brief Manages a legend inside a QCustomPlot.
12958 A legend is a small box somewhere in the plot which lists plottables with their name and icon.
12960 Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
12961 respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
12962 QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
12963 itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
12965 The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
12966 QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
12967 placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
12968 handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
12969 other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
12970 However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
12971 only return the number of items with QCPAbstractLegendItems type).
12973 By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
12974 layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
12975 position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
12976 outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
12979 /* start of documentation of signals */
12981 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
12983 This signal is emitted when the selection state of this legend has changed.
12985 \see setSelectedParts, setSelectableParts
12988 /* end of documentation of signals */
12991 Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
12993 Note that by default, QCustomPlot already contains a legend ready to be used as
12994 QCustomPlot::legend
12996 QCPLegend::QCPLegend()
12999 setColumnSpacing(10);
13000 setMargins(QMargins(2, 3, 2, 2));
13001 setAntialiased(false);
13002 setIconSize(32, 18);
13004 setIconTextPadding(7);
13006 setSelectableParts(spLegendBox | spItems);
13007 setSelectedParts(spNone);
13009 setBorderPen(QPen(Qt::black));
13010 setSelectedBorderPen(QPen(Qt::blue, 2));
13011 setIconBorderPen(Qt::NoPen);
13012 setSelectedIconBorderPen(QPen(Qt::blue, 2));
13013 setBrush(Qt::white);
13014 setSelectedBrush(Qt::white);
13015 setTextColor(Qt::black);
13016 setSelectedTextColor(Qt::blue);
13019 QCPLegend::~QCPLegend()
13022 if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
13023 mParentPlot->legendRemoved(this);
13026 /* no doc for getter, see setSelectedParts */
13027 QCPLegend::SelectableParts QCPLegend::selectedParts() const
13029 // check whether any legend elements selected, if yes, add spItems to return value
13030 bool hasSelectedItems = false;
13031 for (int i=0; i<itemCount(); ++i)
13033 if (item(i) && item(i)->selected())
13035 hasSelectedItems = true;
13039 if (hasSelectedItems)
13040 return mSelectedParts | spItems;
13042 return mSelectedParts & ~spItems;
13046 Sets the pen, the border of the entire legend is drawn with.
13048 void QCPLegend::setBorderPen(const QPen &pen)
13054 Sets the brush of the legend background.
13056 void QCPLegend::setBrush(const QBrush &brush)
13062 Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
13063 use this font by default. However, a different font can be specified on a per-item-basis by
13064 accessing the specific legend item.
13066 This function will also set \a font on all already existing legend items.
13068 \see QCPAbstractLegendItem::setFont
13070 void QCPLegend::setFont(const QFont &font)
13073 for (int i=0; i<itemCount(); ++i)
13076 item(i)->setFont(mFont);
13081 Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
13082 will use this color by default. However, a different colors can be specified on a per-item-basis
13083 by accessing the specific legend item.
13085 This function will also set \a color on all already existing legend items.
13087 \see QCPAbstractLegendItem::setTextColor
13089 void QCPLegend::setTextColor(const QColor &color)
13091 mTextColor = color;
13092 for (int i=0; i<itemCount(); ++i)
13095 item(i)->setTextColor(color);
13100 Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
13101 representation of the graph) will use this size by default.
13103 void QCPLegend::setIconSize(const QSize &size)
13110 void QCPLegend::setIconSize(int width, int height)
13112 mIconSize.setWidth(width);
13113 mIconSize.setHeight(height);
13117 Sets the horizontal space in pixels between the legend icon and the text next to it.
13118 Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
13119 name of the graph) will use this space by default.
13121 void QCPLegend::setIconTextPadding(int padding)
13123 mIconTextPadding = padding;
13127 Sets the pen used to draw a border around each legend icon. Legend items that draw an
13128 icon (e.g. a visual representation of the graph) will use this pen by default.
13130 If no border is wanted, set this to \a Qt::NoPen.
13132 void QCPLegend::setIconBorderPen(const QPen &pen)
13134 mIconBorderPen = pen;
13138 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
13139 (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
13141 However, even when \a selectable is set to a value not allowing the selection of a specific part,
13142 it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
13145 \see SelectablePart, setSelectedParts
13147 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
13149 if (mSelectableParts != selectable)
13151 mSelectableParts = selectable;
13152 emit selectableChanged(mSelectableParts);
13157 Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
13158 is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
13159 doesn't contain \ref spItems, those items become deselected.
13161 The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
13162 contains iSelectLegend. You only need to call this function when you wish to change the selection
13165 This function can change the selection state of a part even when \ref setSelectableParts was set to a
13166 value that actually excludes the part.
13168 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
13170 Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
13171 before, because there's no way to specify which exact items to newly select. Do this by calling
13172 \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
13174 \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
13177 void QCPLegend::setSelectedParts(const SelectableParts &selected)
13179 SelectableParts newSelected = selected;
13180 mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
13182 if (mSelectedParts != newSelected)
13184 if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
13186 qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
13187 newSelected &= ~spItems;
13189 if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
13191 for (int i=0; i<itemCount(); ++i)
13194 item(i)->setSelected(false);
13197 mSelectedParts = newSelected;
13198 emit selectionChanged(mSelectedParts);
13203 When the legend box is selected, this pen is used to draw the border instead of the normal pen
13204 set via \ref setBorderPen.
13206 \see setSelectedParts, setSelectableParts, setSelectedBrush
13208 void QCPLegend::setSelectedBorderPen(const QPen &pen)
13210 mSelectedBorderPen = pen;
13214 Sets the pen legend items will use to draw their icon borders, when they are selected.
13216 \see setSelectedParts, setSelectableParts, setSelectedFont
13218 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
13220 mSelectedIconBorderPen = pen;
13224 When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
13225 set via \ref setBrush.
13227 \see setSelectedParts, setSelectableParts, setSelectedBorderPen
13229 void QCPLegend::setSelectedBrush(const QBrush &brush)
13231 mSelectedBrush = brush;
13235 Sets the default font that is used by legend items when they are selected.
13237 This function will also set \a font on all already existing legend items.
13239 \see setFont, QCPAbstractLegendItem::setSelectedFont
13241 void QCPLegend::setSelectedFont(const QFont &font)
13243 mSelectedFont = font;
13244 for (int i=0; i<itemCount(); ++i)
13247 item(i)->setSelectedFont(font);
13252 Sets the default text color that is used by legend items when they are selected.
13254 This function will also set \a color on all already existing legend items.
13256 \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
13258 void QCPLegend::setSelectedTextColor(const QColor &color)
13260 mSelectedTextColor = color;
13261 for (int i=0; i<itemCount(); ++i)
13264 item(i)->setSelectedTextColor(color);
13269 Returns the item with index \a i.
13273 QCPAbstractLegendItem *QCPLegend::item(int index) const
13275 return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
13279 Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13280 If such an item isn't in the legend, returns 0.
13282 \see hasItemWithPlottable
13284 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
13286 for (int i=0; i<itemCount(); ++i)
13288 if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
13290 if (pli->plottable() == plottable)
13298 Returns the number of items currently in the legend.
13301 int QCPLegend::itemCount() const
13303 return elementCount();
13307 Returns whether the legend contains \a itm.
13309 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
13311 for (int i=0; i<itemCount(); ++i)
13313 if (item == this->item(i))
13320 Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
13321 If such an item isn't in the legend, returns false.
13323 \see itemWithPlottable
13325 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
13327 return itemWithPlottable(plottable);
13331 Adds \a item to the legend, if it's not present already.
13333 Returns true on success, i.e. if the item wasn't in the list already and has been successfuly added.
13335 The legend takes ownership of the item.
13337 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
13339 if (!hasItem(item))
13341 return addElement(rowCount(), 0, item);
13347 Removes the item with index \a index from the legend.
13349 Returns true, if successful.
13351 \see itemCount, clearItems
13353 bool QCPLegend::removeItem(int index)
13355 if (QCPAbstractLegendItem *ali = item(index))
13357 bool success = remove(ali);
13366 Removes \a item from the legend.
13368 Returns true, if successful.
13372 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
13374 bool success = remove(item);
13380 Removes all items from the legend.
13382 void QCPLegend::clearItems()
13384 for (int i=itemCount()-1; i>=0; --i)
13389 Returns the legend items that are currently selected. If no items are selected,
13392 \see QCPAbstractLegendItem::setSelected, setSelectable
13394 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
13396 QList<QCPAbstractLegendItem*> result;
13397 for (int i=0; i<itemCount(); ++i)
13399 if (QCPAbstractLegendItem *ali = item(i))
13401 if (ali->selected())
13402 result.append(ali);
13410 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
13411 before drawing main legend elements.
13413 This is the antialiasing state the painter passed to the \ref draw method is in by default.
13415 This function takes into account the local setting of the antialiasing flag as well as the
13416 overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
13417 QCustomPlot::setNotAntialiasedElements.
13419 \see setAntialiased
13421 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
13423 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
13428 Returns the pen used to paint the border of the legend, taking into account the selection state
13431 QPen QCPLegend::getBorderPen() const
13433 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
13438 Returns the brush used to paint the background of the legend, taking into account the selection
13439 state of the legend box.
13441 QBrush QCPLegend::getBrush() const
13443 return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
13448 Draws the legend box with the provided \a painter. The individual legend items are layerables
13449 themselves, thus are drawn independently.
13451 void QCPLegend::draw(QCPPainter *painter)
13453 // draw background rect:
13454 painter->setBrush(getBrush());
13455 painter->setPen(getBorderPen());
13456 painter->drawRect(mOuterRect);
13459 /* inherits documentation from base class */
13460 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13462 if (!mParentPlot) return -1;
13463 if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
13466 if (mOuterRect.contains(pos.toPoint()))
13468 if (details) details->setValue(spLegendBox);
13469 return mParentPlot->selectionTolerance()*0.99;
13474 /* inherits documentation from base class */
13475 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13478 mSelectedParts = selectedParts(); // in case item selection has changed
13479 if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
13481 SelectableParts selBefore = mSelectedParts;
13482 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)
13483 if (selectionStateChanged)
13484 *selectionStateChanged = mSelectedParts != selBefore;
13488 /* inherits documentation from base class */
13489 void QCPLegend::deselectEvent(bool *selectionStateChanged)
13491 mSelectedParts = selectedParts(); // in case item selection has changed
13492 if (mSelectableParts.testFlag(spLegendBox))
13494 SelectableParts selBefore = mSelectedParts;
13495 setSelectedParts(selectedParts() & ~spLegendBox);
13496 if (selectionStateChanged)
13497 *selectionStateChanged = mSelectedParts != selBefore;
13501 /* inherits documentation from base class */
13502 QCP::Interaction QCPLegend::selectionCategory() const
13504 return QCP::iSelectLegend;
13507 /* inherits documentation from base class */
13508 QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
13510 return QCP::iSelectLegend;
13513 /* inherits documentation from base class */
13514 void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
13516 Q_UNUSED(parentPlot)
13520 ////////////////////////////////////////////////////////////////////////////////////////////////////
13521 //////////////////// QCPPlotTitle
13522 ////////////////////////////////////////////////////////////////////////////////////////////////////
13524 /*! \class QCPPlotTitle
13525 \brief A layout element displaying a plot title text
13527 The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
13528 and \ref setTextColor.
13530 A plot title can be added as follows:
13532 customPlot->plotLayout()->insertRow(0); // inserts an empty row above the default axis rect
13533 customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Your Plot Title"));
13536 Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
13537 easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
13538 signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
13539 QCustomPlot::titleDoubleClick signal.
13542 /* start documentation of signals */
13544 /*! \fn void QCPPlotTitle::selectionChanged(bool selected)
13546 This signal is emitted when the selection state has changed to \a selected, either by user
13547 interaction or by a direct call to \ref setSelected.
13549 \see setSelected, setSelectable
13552 /* end documentation of signals */
13555 Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
13557 To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
13559 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
13560 QCPLayoutElement(parentPlot),
13561 mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
13562 mTextColor(Qt::black),
13563 mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
13564 mSelectedTextColor(Qt::blue),
13565 mSelectable(false),
13570 setLayer(parentPlot->currentLayer());
13571 mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
13572 mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
13574 setMargins(QMargins(5, 5, 5, 0));
13579 Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
13581 QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
13582 QCPLayoutElement(parentPlot),
13584 mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
13585 mTextColor(Qt::black),
13586 mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
13587 mSelectedTextColor(Qt::blue),
13588 mSelectable(false),
13591 setLayer(QLatin1String("axes"));
13592 setMargins(QMargins(5, 5, 5, 0));
13596 Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
13598 \see setFont, setTextColor
13600 void QCPPlotTitle::setText(const QString &text)
13606 Sets the \a font of the title text.
13608 \see setTextColor, setSelectedFont
13610 void QCPPlotTitle::setFont(const QFont &font)
13616 Sets the \a color of the title text.
13618 \see setFont, setSelectedTextColor
13620 void QCPPlotTitle::setTextColor(const QColor &color)
13622 mTextColor = color;
13626 Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
13630 void QCPPlotTitle::setSelectedFont(const QFont &font)
13632 mSelectedFont = font;
13636 Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
13640 void QCPPlotTitle::setSelectedTextColor(const QColor &color)
13642 mSelectedTextColor = color;
13646 Sets whether the user may select this plot title to \a selectable.
13648 Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
13649 programmatically via \ref setSelected.
13651 void QCPPlotTitle::setSelectable(bool selectable)
13653 if (mSelectable != selectable)
13655 mSelectable = selectable;
13656 emit selectableChanged(mSelectable);
13661 Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
13662 selectionChanged is emitted.
13664 Note that this function can change the selection state independently of the current \ref
13665 setSelectable state.
13667 void QCPPlotTitle::setSelected(bool selected)
13669 if (mSelected != selected)
13671 mSelected = selected;
13672 emit selectionChanged(mSelected);
13676 /* inherits documentation from base class */
13677 void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
13679 applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
13682 /* inherits documentation from base class */
13683 void QCPPlotTitle::draw(QCPPainter *painter)
13685 painter->setFont(mainFont());
13686 painter->setPen(QPen(mainTextColor()));
13687 painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
13690 /* inherits documentation from base class */
13691 QSize QCPPlotTitle::minimumSizeHint() const
13693 QFontMetrics metrics(mFont);
13694 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13695 result.rwidth() += mMargins.left() + mMargins.right();
13696 result.rheight() += mMargins.top() + mMargins.bottom();
13700 /* inherits documentation from base class */
13701 QSize QCPPlotTitle::maximumSizeHint() const
13703 QFontMetrics metrics(mFont);
13704 QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
13705 result.rheight() += mMargins.top() + mMargins.bottom();
13706 result.setWidth(QWIDGETSIZE_MAX);
13710 /* inherits documentation from base class */
13711 void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
13717 bool selBefore = mSelected;
13718 setSelected(additive ? !mSelected : true);
13719 if (selectionStateChanged)
13720 *selectionStateChanged = mSelected != selBefore;
13724 /* inherits documentation from base class */
13725 void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
13729 bool selBefore = mSelected;
13730 setSelected(false);
13731 if (selectionStateChanged)
13732 *selectionStateChanged = mSelected != selBefore;
13736 /* inherits documentation from base class */
13737 double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
13740 if (onlySelectable && !mSelectable)
13743 if (mTextBoundingRect.contains(pos.toPoint()))
13744 return mParentPlot->selectionTolerance()*0.99;
13751 Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
13752 <tt>true</tt>, else mFont is returned.
13754 QFont QCPPlotTitle::mainFont() const
13756 return mSelected ? mSelectedFont : mFont;
13761 Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
13762 <tt>true</tt>, else mTextColor is returned.
13764 QColor QCPPlotTitle::mainTextColor() const
13766 return mSelected ? mSelectedTextColor : mTextColor;
13770 ////////////////////////////////////////////////////////////////////////////////////////////////////
13771 //////////////////// QCPColorScale
13772 ////////////////////////////////////////////////////////////////////////////////////////////////////
13774 /*! \class QCPColorScale
13775 \brief A color scale for use with color coding data such as QCPColorMap
13777 This layout element can be placed on the plot to correlate a color gradient with data values. It
13778 is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
13780 \image html QCPColorScale.png
13782 The color scale can be either horizontal or vertical, as shown in the image above. The
13783 orientation and the side where the numbers appear is controlled with \ref setType.
13785 Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
13786 connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
13787 setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
13788 scale, to make them all synchronize these properties.
13790 To have finer control over the number display and axis behaviour, you can directly access the
13791 \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
13792 you want to change the number of automatically generated ticks, call
13794 colorScale->axis()->setAutoTickCount(3);
13797 Placing a color scale next to the main axis rect works like with any other layout element:
13799 QCPColorScale *colorScale = new QCPColorScale(customPlot);
13800 customPlot->plotLayout()->addElement(0, 1, colorScale);
13801 colorScale->setLabel("Some Label Text");
13803 In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
13804 call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
13805 scale can be set with \ref setLabel.
13807 For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
13808 the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
13810 QCPMarginGroup *group = new QCPMarginGroup(customPlot);
13811 colorScale->setMarginGroup(QCP::msTop|QCP::msBottom, group);
13812 customPlot->axisRect()->setMarginGroup(QCP::msTop|QCP::msBottom, group);
13815 Color scales are initialized with a non-zero minimum top and bottom margin (\ref
13816 setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
13817 margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
13818 horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
13819 might want to also change the minimum margins accordingly, e.g. <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.
13822 /* start documentation of inline functions */
13824 /*! \fn QCPAxis *QCPColorScale::axis() const
13826 Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
13827 appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
13828 interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
13829 setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
13830 QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
13831 the QCPColorScale or on its QCPAxis.
13833 If the type of the color scale is changed with \ref setType, the axis returned by this method
13834 will change, too, to either the left, right, bottom or top axis, depending on which type was set.
13837 /* end documentation of signals */
13838 /* start documentation of signals */
13840 /*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
13842 This signal is emitted when the data range changes.
13847 /*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
13849 This signal is emitted when the data scale type changes.
13851 \see setDataScaleType
13854 /*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
13856 This signal is emitted when the gradient changes.
13861 /* end documentation of signals */
13864 Constructs a new QCPColorScale.
13866 QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
13867 QCPLayoutElement(parentPlot),
13868 mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
13869 mDataScaleType(QCPAxis::stLinear),
13871 mAxisRect(new QCPColorScaleAxisRectPrivate(this))
13873 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)
13874 setType(QCPAxis::atRight);
13875 setDataRange(QCPRange(0, 6));
13878 QCPColorScale::~QCPColorScale()
13883 /* undocumented getter */
13884 QString QCPColorScale::label() const
13888 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
13892 return mColorAxis.data()->label();
13895 /* undocumented getter */
13896 bool QCPColorScale::rangeDrag() const
13900 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13904 return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
13905 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
13906 mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13909 /* undocumented getter */
13910 bool QCPColorScale::rangeZoom() const
13914 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13918 return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
13919 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
13920 mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
13924 Sets at which side of the color scale the axis is placed, and thus also its orientation.
13926 Note that after setting \a type to a different value, the axis returned by \ref axis() will
13927 be a different one. The new axis will adopt the following properties from the previous axis: The
13928 range, scale type, log base and label.
13930 void QCPColorScale::setType(QCPAxis::AxisType type)
13934 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
13940 QCPRange rangeTransfer(0, 6);
13941 double logBaseTransfer = 10;
13942 QString labelTransfer;
13943 // revert some settings on old axis:
13946 rangeTransfer = mColorAxis.data()->range();
13947 labelTransfer = mColorAxis.data()->label();
13948 logBaseTransfer = mColorAxis.data()->scaleLogBase();
13949 mColorAxis.data()->setLabel(QString());
13950 disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13951 disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13953 foreach (QCPAxis::AxisType atype, QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop)
13955 mAxisRect.data()->axis(atype)->setTicks(atype == mType);
13956 mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
13958 // set new mColorAxis pointer:
13959 mColorAxis = mAxisRect.data()->axis(mType);
13960 // transfer settings to new axis:
13961 mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
13962 mColorAxis.data()->setLabel(labelTransfer);
13963 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
13964 connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
13965 connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
13966 mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
13967 QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
13972 Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
13974 It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
13975 also equivalent to directly accessing the \ref axis and setting its range with \ref
13978 \see setDataScaleType, setGradient, rescaleDataRange
13980 void QCPColorScale::setDataRange(const QCPRange &dataRange)
13982 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
13984 mDataRange = dataRange;
13986 mColorAxis.data()->setRange(mDataRange);
13987 emit dataRangeChanged(mDataRange);
13992 Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
13993 or logarithmically.
13995 It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
13996 also equivalent to directly accessing the \ref axis and setting its scale type with \ref
13997 QCPAxis::setScaleType.
13999 \see setDataRange, setGradient
14001 void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
14003 if (mDataScaleType != scaleType)
14005 mDataScaleType = scaleType;
14007 mColorAxis.data()->setScaleType(mDataScaleType);
14008 if (mDataScaleType == QCPAxis::stLogarithmic)
14009 setDataRange(mDataRange.sanitizedForLogScale());
14010 emit dataScaleTypeChanged(mDataScaleType);
14015 Sets the color gradient that will be used to represent data values.
14017 It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
14019 \see setDataRange, setDataScaleType
14021 void QCPColorScale::setGradient(const QCPColorGradient &gradient)
14023 if (mGradient != gradient)
14025 mGradient = gradient;
14027 mAxisRect.data()->mGradientImageInvalidated = true;
14028 emit gradientChanged(mGradient);
14033 Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
14034 the internal \ref axis.
14036 void QCPColorScale::setLabel(const QString &str)
14040 qDebug() << Q_FUNC_INFO << "internal color axis undefined";
14044 mColorAxis.data()->setLabel(str);
14048 Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
14051 void QCPColorScale::setBarWidth(int width)
14057 Sets whether the user can drag the data range (\ref setDataRange).
14059 Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
14060 QCustomPlot::setInteractions) to allow range dragging.
14062 void QCPColorScale::setRangeDrag(bool enabled)
14066 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14071 mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
14073 mAxisRect.data()->setRangeDrag(0);
14077 Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
14079 Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
14080 QCustomPlot::setInteractions) to allow range dragging.
14082 void QCPColorScale::setRangeZoom(bool enabled)
14086 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14091 mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
14093 mAxisRect.data()->setRangeZoom(0);
14097 Returns a list of all the color maps associated with this color scale.
14099 QList<QCPColorMap*> QCPColorScale::colorMaps() const
14101 QList<QCPColorMap*> result;
14102 for (int i=0; i<mParentPlot->plottableCount(); ++i)
14104 if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
14105 if (cm->colorScale() == this)
14112 Changes the data range such that all color maps associated with this color scale are fully mapped
14113 to the gradient in the data dimension.
14117 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
14119 QList<QCPColorMap*> maps = colorMaps();
14121 bool haveRange = false;
14122 int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
14123 if (mDataScaleType == QCPAxis::stLogarithmic)
14124 sign = (mDataRange.upper < 0 ? -1 : 1);
14125 for (int i=0; i<maps.size(); ++i)
14127 if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
14130 if (maps.at(i)->colorScale() == this)
14132 bool currentFoundRange = true;
14133 mapRange = maps.at(i)->data()->dataBounds();
14136 if (mapRange.lower <= 0 && mapRange.upper > 0)
14137 mapRange.lower = mapRange.upper*1e-3;
14138 else if (mapRange.lower <= 0 && mapRange.upper <= 0)
14139 currentFoundRange = false;
14140 } else if (sign == -1)
14142 if (mapRange.upper >= 0 && mapRange.lower < 0)
14143 mapRange.upper = mapRange.lower*1e-3;
14144 else if (mapRange.upper >= 0 && mapRange.lower >= 0)
14145 currentFoundRange = false;
14147 if (currentFoundRange)
14150 newRange = mapRange;
14152 newRange.expand(mapRange);
14159 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
14161 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
14162 if (mDataScaleType == QCPAxis::stLinear)
14164 newRange.lower = center-mDataRange.size()/2.0;
14165 newRange.upper = center+mDataRange.size()/2.0;
14166 } else // mScaleType == stLogarithmic
14168 newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
14169 newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
14172 setDataRange(newRange);
14176 /* inherits documentation from base class */
14177 void QCPColorScale::update(UpdatePhase phase)
14179 QCPLayoutElement::update(phase);
14182 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14186 mAxisRect.data()->update(phase);
14192 if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
14194 setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14195 setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
14198 setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
14199 setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
14205 mAxisRect.data()->setOuterRect(rect());
14212 /* inherits documentation from base class */
14213 void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
14215 painter->setAntialiasing(false);
14218 /* inherits documentation from base class */
14219 void QCPColorScale::mousePressEvent(QMouseEvent *event)
14223 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14226 mAxisRect.data()->mousePressEvent(event);
14229 /* inherits documentation from base class */
14230 void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
14234 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14237 mAxisRect.data()->mouseMoveEvent(event);
14240 /* inherits documentation from base class */
14241 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
14245 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14248 mAxisRect.data()->mouseReleaseEvent(event);
14251 /* inherits documentation from base class */
14252 void QCPColorScale::wheelEvent(QWheelEvent *event)
14256 qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
14259 mAxisRect.data()->wheelEvent(event);
14262 ////////////////////////////////////////////////////////////////////////////////////////////////////
14263 //////////////////// QCPColorScaleAxisRectPrivate
14264 ////////////////////////////////////////////////////////////////////////////////////////////////////
14266 /*! \class QCPColorScaleAxisRectPrivate
14269 \brief An axis rect subclass for use in a QCPColorScale
14271 This is a private class and not part of the public QCustomPlot interface.
14273 It provides the axis rect functionality for the QCPColorScale class.
14278 Creates a new instance, as a child of \a parentColorScale.
14280 QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
14281 QCPAxisRect(parentColorScale->parentPlot(), true),
14282 mParentColorScale(parentColorScale),
14283 mGradientImageInvalidated(true)
14285 setParentLayerable(parentColorScale);
14286 setMinimumMargins(QMargins(0, 0, 0, 0));
14287 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14289 axis(type)->setVisible(true);
14290 axis(type)->grid()->setVisible(false);
14291 axis(type)->setPadding(0);
14292 connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
14293 connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
14296 connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
14297 connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
14298 connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
14299 connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
14300 connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
14301 connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
14302 connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
14303 connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
14305 // make layer transfers of color scale transfer to axis rect and axes
14306 // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
14307 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
14308 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14309 connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
14313 Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
14314 it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
14316 void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
14318 if (mGradientImageInvalidated)
14319 updateGradientImage();
14321 bool mirrorHorz = false;
14322 bool mirrorVert = false;
14323 if (mParentColorScale->mColorAxis)
14325 mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
14326 mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
14329 painter->drawImage(rect(), mGradientImage.mirrored(mirrorHorz, mirrorVert));
14330 QCPAxisRect::draw(painter);
14335 Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
14336 generate a gradient image. This gradient image will be used in the \ref draw method.
14338 void QCPColorScaleAxisRectPrivate::updateGradientImage()
14340 if (rect().isEmpty())
14343 int n = mParentColorScale->mGradient.levelCount();
14345 QVector<double> data(n);
14346 for (int i=0; i<n; ++i)
14348 if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
14351 h = rect().height();
14352 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14353 QVector<QRgb*> pixels;
14354 for (int y=0; y<h; ++y)
14355 pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
14356 mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
14357 for (int y=1; y<h; ++y)
14358 memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
14361 w = rect().width();
14363 mGradientImage = QImage(w, h, QImage::Format_RGB32);
14364 for (int y=0; y<h; ++y)
14366 QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
14367 const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
14368 for (int x=0; x<w; ++x)
14369 pixels[x] = lineColor;
14372 mGradientImageInvalidated = false;
14377 This slot is connected to the selectionChanged signals of the four axes in the constructor. It
14378 synchronizes the selection state of the axes.
14380 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
14382 // axis bases of four axes shall always (de-)selected synchronously:
14383 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14385 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14386 if (senderAxis->axisType() == type)
14389 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14391 if (selectedParts.testFlag(QCPAxis::spAxis))
14392 axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
14394 axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
14401 This slot is connected to the selectableChanged signals of the four axes in the constructor. It
14402 synchronizes the selectability of the axes.
14404 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
14406 // synchronize axis base selectability:
14407 foreach (QCPAxis::AxisType type, QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight)
14409 if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
14410 if (senderAxis->axisType() == type)
14413 if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
14415 if (selectableParts.testFlag(QCPAxis::spAxis))
14416 axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
14418 axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
14424 ////////////////////////////////////////////////////////////////////////////////////////////////////
14425 //////////////////// QCPData
14426 ////////////////////////////////////////////////////////////////////////////////////////////////////
14429 \brief Holds the data of one single data point for QCPGraph.
14431 The container for storing multiple data points is \ref QCPDataMap.
14433 The stored data is:
14434 \li \a key: coordinate on the key axis of this data point
14435 \li \a value: coordinate on the value axis of this data point
14436 \li \a keyErrorMinus: negative error in the key dimension (for error bars)
14437 \li \a keyErrorPlus: positive error in the key dimension (for error bars)
14438 \li \a valueErrorMinus: negative error in the value dimension (for error bars)
14439 \li \a valueErrorPlus: positive error in the value dimension (for error bars)
14445 Constructs a data point with key, value and all errors set to zero.
14447 QCPData::QCPData() :
14458 Constructs a data point with the specified \a key and \a value. All errors are set to zero.
14460 QCPData::QCPData(double key, double value) :
14471 ////////////////////////////////////////////////////////////////////////////////////////////////////
14472 //////////////////// QCPGraph
14473 ////////////////////////////////////////////////////////////////////////////////////////////////////
14475 /*! \class QCPGraph
14476 \brief A plottable representing a graph in a plot.
14478 \image html QCPGraph.png
14480 Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
14481 instance is accessed via QCustomPlot::graph.
14483 To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
14484 also access and modify the graph's data via the \ref data method, which returns a pointer to the
14485 internal \ref QCPDataMap.
14487 Graphs are used to display single-valued data. Single-valued means that there should only be one
14488 data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
14489 want to plot non-single-valued curves, rather use the QCPCurve plottable.
14491 \section appearance Changing the appearance
14493 The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
14494 of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
14496 \subsection filling Filling under or between graphs
14498 QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
14499 the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
14500 just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
14502 By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
14503 between this graph and another one, call \ref setChannelFillGraph with the other graph as
14506 \see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph
14509 /* start of documentation of inline functions */
14511 /*! \fn QCPDataMap *QCPGraph::data() const
14513 Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
14514 directly manipulate the data, which may be more convenient and faster than using the regular \ref
14515 setData or \ref addData methods, in certain situations.
14518 /* end of documentation of inline functions */
14521 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
14522 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
14523 the same orientation. If either of these restrictions is violated, a corresponding message is
14524 printed to the debug output (qDebug), the construction is not aborted, though.
14526 The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
14527 then takes ownership of the graph.
14529 To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
14531 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
14532 QCPAbstractPlottable(keyAxis, valueAxis)
14534 mData = new QCPDataMap;
14536 setPen(QPen(Qt::blue, 0));
14537 setErrorPen(QPen(Qt::black));
14538 setBrush(Qt::NoBrush);
14539 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
14540 setSelectedBrush(Qt::NoBrush);
14542 setLineStyle(lsLine);
14543 setErrorType(etNone);
14544 setErrorBarSize(6);
14545 setErrorBarSkipSymbol(true);
14546 setChannelFillGraph(0);
14547 setAdaptiveSampling(true);
14550 QCPGraph::~QCPGraph()
14556 Replaces the current data with the provided \a data.
14558 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
14559 takes ownership of the passed data and replaces the internal data pointer with it. This is
14560 significantly faster than copying for large datasets.
14562 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14563 returns a pointer to the internal \ref QCPDataMap.
14565 void QCPGraph::setData(QCPDataMap *data, bool copy)
14569 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
14584 Replaces the current data with the provided points in \a key and \a value pairs. The provided
14585 vectors should have equal length. Else, the number of added points will be the size of the
14588 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
14591 int n = key.size();
14592 n = qMin(n, value.size());
14594 for (int i=0; i<n; ++i)
14596 newData.key = key[i];
14597 newData.value = value[i];
14598 mData->insertMulti(newData.key, newData);
14603 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14604 symmetrical value error of the data points are set to the values in \a valueError.
14605 For error bars to show appropriately, see \ref setErrorType.
14606 The provided vectors should have equal length. Else, the number of added points will be the size of the
14609 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14611 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
14614 int n = key.size();
14615 n = qMin(n, value.size());
14616 n = qMin(n, valueError.size());
14618 for (int i=0; i<n; ++i)
14620 newData.key = key[i];
14621 newData.value = value[i];
14622 newData.valueErrorMinus = valueError[i];
14623 newData.valueErrorPlus = valueError[i];
14624 mData->insertMulti(key[i], newData);
14630 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14631 negative value error of the data points are set to the values in \a valueErrorMinus, the positive
14632 value error to \a valueErrorPlus.
14633 For error bars to show appropriately, see \ref setErrorType.
14634 The provided vectors should have equal length. Else, the number of added points will be the size of the
14637 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14640 int n = key.size();
14641 n = qMin(n, value.size());
14642 n = qMin(n, valueErrorMinus.size());
14643 n = qMin(n, valueErrorPlus.size());
14645 for (int i=0; i<n; ++i)
14647 newData.key = key[i];
14648 newData.value = value[i];
14649 newData.valueErrorMinus = valueErrorMinus[i];
14650 newData.valueErrorPlus = valueErrorPlus[i];
14651 mData->insertMulti(key[i], newData);
14656 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14657 symmetrical key error of the data points are set to the values in \a keyError.
14658 For error bars to show appropriately, see \ref setErrorType.
14659 The provided vectors should have equal length. Else, the number of added points will be the size of the
14662 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14664 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
14667 int n = key.size();
14668 n = qMin(n, value.size());
14669 n = qMin(n, keyError.size());
14671 for (int i=0; i<n; ++i)
14673 newData.key = key[i];
14674 newData.value = value[i];
14675 newData.keyErrorMinus = keyError[i];
14676 newData.keyErrorPlus = keyError[i];
14677 mData->insertMulti(key[i], newData);
14683 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14684 negative key error of the data points are set to the values in \a keyErrorMinus, the positive
14685 key error to \a keyErrorPlus.
14686 For error bars to show appropriately, see \ref setErrorType.
14687 The provided vectors should have equal length. Else, the number of added points will be the size of the
14690 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
14693 int n = key.size();
14694 n = qMin(n, value.size());
14695 n = qMin(n, keyErrorMinus.size());
14696 n = qMin(n, keyErrorPlus.size());
14698 for (int i=0; i<n; ++i)
14700 newData.key = key[i];
14701 newData.value = value[i];
14702 newData.keyErrorMinus = keyErrorMinus[i];
14703 newData.keyErrorPlus = keyErrorPlus[i];
14704 mData->insertMulti(key[i], newData);
14709 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14710 symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
14711 For error bars to show appropriately, see \ref setErrorType.
14712 The provided vectors should have equal length. Else, the number of added points will be the size of the
14715 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
14717 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
14720 int n = key.size();
14721 n = qMin(n, value.size());
14722 n = qMin(n, valueError.size());
14723 n = qMin(n, keyError.size());
14725 for (int i=0; i<n; ++i)
14727 newData.key = key[i];
14728 newData.value = value[i];
14729 newData.keyErrorMinus = keyError[i];
14730 newData.keyErrorPlus = keyError[i];
14731 newData.valueErrorMinus = valueError[i];
14732 newData.valueErrorPlus = valueError[i];
14733 mData->insertMulti(key[i], newData);
14739 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
14740 negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
14741 key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
14742 For error bars to show appropriately, see \ref setErrorType.
14743 The provided vectors should have equal length. Else, the number of added points will be the size of the
14746 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
14749 int n = key.size();
14750 n = qMin(n, value.size());
14751 n = qMin(n, valueErrorMinus.size());
14752 n = qMin(n, valueErrorPlus.size());
14753 n = qMin(n, keyErrorMinus.size());
14754 n = qMin(n, keyErrorPlus.size());
14756 for (int i=0; i<n; ++i)
14758 newData.key = key[i];
14759 newData.value = value[i];
14760 newData.keyErrorMinus = keyErrorMinus[i];
14761 newData.keyErrorPlus = keyErrorPlus[i];
14762 newData.valueErrorMinus = valueErrorMinus[i];
14763 newData.valueErrorPlus = valueErrorPlus[i];
14764 mData->insertMulti(key[i], newData);
14770 Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to
14771 \ref lsNone and \ref setScatterStyle to the desired scatter style.
14773 \see setScatterStyle
14775 void QCPGraph::setLineStyle(LineStyle ls)
14781 Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
14782 are drawn (e.g. for line-only-plots with appropriate line style).
14784 \see QCPScatterStyle, setLineStyle
14786 void QCPGraph::setScatterStyle(const QCPScatterStyle &style)
14788 mScatterStyle = style;
14792 Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
14793 point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
14794 error data via the specific setData functions along with the data points (e.g. \ref
14795 setDataValueError, \ref setDataKeyError, \ref setDataBothError).
14799 void QCPGraph::setErrorType(ErrorType errorType)
14801 mErrorType = errorType;
14805 Sets the pen with which the error bars will be drawn.
14806 \see setErrorBarSize, setErrorType
14808 void QCPGraph::setErrorPen(const QPen &pen)
14814 Sets the width of the handles at both ends of an error bar in pixels.
14816 void QCPGraph::setErrorBarSize(double size)
14818 mErrorBarSize = size;
14822 If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
14823 leave some free space around the symbol.
14825 This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to determine the size
14826 of the area to leave blank. So when drawing Pixmaps as scatter points (\ref
14827 QCPScatterStyle::ssPixmap), the scatter size must be set manually to a value corresponding to the
14828 size of the Pixmap, if the error bars should leave gaps to its boundaries.
14830 \ref setErrorType, setErrorBarSize, setScatterStyle
14832 void QCPGraph::setErrorBarSkipSymbol(bool enabled)
14834 mErrorBarSkipSymbol = enabled;
14838 Sets the target graph for filling the area between this graph and \a targetGraph with the current
14839 brush (\ref setBrush).
14841 When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To
14842 disable any filling, set the brush to Qt::NoBrush.
14846 void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
14848 // prevent setting channel target to this graph itself:
14849 if (targetGraph == this)
14851 qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
14852 mChannelFillGraph = 0;
14855 // prevent setting channel target to a graph not in the plot:
14856 if (targetGraph && targetGraph->mParentPlot != mParentPlot)
14858 qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
14859 mChannelFillGraph = 0;
14863 mChannelFillGraph = targetGraph;
14867 Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
14868 sampling technique can drastically improve the replot performance for graphs with a larger number
14869 of points (e.g. above 10,000), without notably changing the appearance of the graph.
14871 By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
14872 sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
14873 disadvantage in almost all cases.
14875 \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
14877 As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
14878 reproduced reliably, as well as the overall shape of the data set. The replot time reduces
14879 dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
14881 \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
14883 Care must be taken when using high-density scatter plots in combination with adaptive sampling.
14884 The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
14885 gives a significant reduction of replot times, but not quite as much as for line plots. This is
14886 because scatter plots inherently need more data points to be preserved in order to still resemble
14887 the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
14888 identical, as banding occurs for the outer data points. This is in fact intentional, such that
14889 the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
14890 depends on the point density, i.e. the number of points in the plot.
14892 For some situations with scatter plots it might thus be desirable to manually turn adaptive
14893 sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
14894 enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
14895 back to true afterwards.
14897 void QCPGraph::setAdaptiveSampling(bool enabled)
14899 mAdaptiveSampling = enabled;
14903 Adds the provided data points in \a dataMap to the current data.
14905 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14906 returns a pointer to the internal \ref QCPDataMap.
14910 void QCPGraph::addData(const QCPDataMap &dataMap)
14912 mData->unite(dataMap);
14916 Adds the provided single data point in \a data to the current data.
14918 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14919 returns a pointer to the internal \ref QCPDataMap.
14923 void QCPGraph::addData(const QCPData &data)
14925 mData->insertMulti(data.key, data);
14929 Adds the provided single data point as \a key and \a value pair to the current data.
14931 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14932 returns a pointer to the internal \ref QCPDataMap.
14936 void QCPGraph::addData(double key, double value)
14940 newData.value = value;
14941 mData->insertMulti(newData.key, newData);
14945 Adds the provided data points as \a key and \a value pairs to the current data.
14947 Alternatively, you can also access and modify the graph's data via the \ref data method, which
14948 returns a pointer to the internal \ref QCPDataMap.
14952 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
14954 int n = qMin(keys.size(), values.size());
14956 for (int i=0; i<n; ++i)
14958 newData.key = keys[i];
14959 newData.value = values[i];
14960 mData->insertMulti(newData.key, newData);
14965 Removes all data points with keys smaller than \a key.
14966 \see addData, clearData
14968 void QCPGraph::removeDataBefore(double key)
14970 QCPDataMap::iterator it = mData->begin();
14971 while (it != mData->end() && it.key() < key)
14972 it = mData->erase(it);
14976 Removes all data points with keys greater than \a key.
14977 \see addData, clearData
14979 void QCPGraph::removeDataAfter(double key)
14981 if (mData->isEmpty()) return;
14982 QCPDataMap::iterator it = mData->upperBound(key);
14983 while (it != mData->end())
14984 it = mData->erase(it);
14988 Removes all data points with keys between \a fromKey and \a toKey.
14989 if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
14990 a single data point with known key, use \ref removeData(double key).
14992 \see addData, clearData
14994 void QCPGraph::removeData(double fromKey, double toKey)
14996 if (fromKey >= toKey || mData->isEmpty()) return;
14997 QCPDataMap::iterator it = mData->upperBound(fromKey);
14998 QCPDataMap::iterator itEnd = mData->upperBound(toKey);
14999 while (it != itEnd)
15000 it = mData->erase(it);
15005 Removes a single data point at \a key. If the position is not known with absolute precision,
15006 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
15007 the suspected position, depeding on the precision with which the key is known.
15009 \see addData, clearData
15011 void QCPGraph::removeData(double key)
15013 mData->remove(key);
15017 Removes all data points.
15018 \see removeData, removeDataAfter, removeDataBefore
15020 void QCPGraph::clearData()
15025 /* inherits documentation from base class */
15026 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
15029 if ((onlySelectable && !mSelectable) || mData->isEmpty())
15031 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
15033 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
15034 return pointDistance(pos);
15041 Allows to define whether error bars are taken into consideration when determining the new axis
15044 \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
15046 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
15048 rescaleKeyAxis(onlyEnlarge, includeErrorBars);
15049 rescaleValueAxis(onlyEnlarge, includeErrorBars);
15054 Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
15055 when determining the new axis range.
15057 \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis
15059 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
15061 // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
15062 // that getKeyRange is passed the includeErrorBars value.
15063 if (mData->isEmpty()) return;
15065 QCPAxis *keyAxis = mKeyAxis.data();
15066 if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
15068 SignDomain signDomain = sdBoth;
15069 if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
15070 signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
15073 QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
15079 if (keyAxis->range().lower < newRange.lower)
15080 newRange.lower = keyAxis->range().lower;
15081 if (keyAxis->range().upper > newRange.upper)
15082 newRange.upper = keyAxis->range().upper;
15084 keyAxis->setRange(newRange);
15090 Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
15091 when determining the new axis range.
15093 \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis
15095 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
15097 // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
15098 // is that getValueRange is passed the includeErrorBars value.
15099 if (mData->isEmpty()) return;
15101 QCPAxis *valueAxis = mValueAxis.data();
15102 if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
15104 SignDomain signDomain = sdBoth;
15105 if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
15106 signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
15109 QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
15115 if (valueAxis->range().lower < newRange.lower)
15116 newRange.lower = valueAxis->range().lower;
15117 if (valueAxis->range().upper > newRange.upper)
15118 newRange.upper = valueAxis->range().upper;
15120 valueAxis->setRange(newRange);
15124 /* inherits documentation from base class */
15125 void QCPGraph::draw(QCPPainter *painter)
15127 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15128 if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
15129 if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
15131 // allocate line and (if necessary) point vectors:
15132 QVector<QPointF> *lineData = new QVector<QPointF>;
15133 QVector<QCPData> *scatterData = 0;
15134 if (!mScatterStyle.isNone())
15135 scatterData = new QVector<QCPData>;
15137 // fill vectors with data appropriate to plot style:
15138 getPlotData(lineData, scatterData);
15140 // check data validity if flag set:
15141 #ifdef QCUSTOMPLOT_CHECK_DATA
15142 QCPDataMap::const_iterator it;
15143 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
15145 if (QCP::isInvalidData(it.value().key, it.value().value) ||
15146 QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
15147 QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
15148 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
15152 // draw fill of graph:
15153 drawFill(painter, lineData);
15156 if (mLineStyle == lsImpulse)
15157 drawImpulsePlot(painter, lineData);
15158 else if (mLineStyle != lsNone)
15159 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
15163 drawScatterPlot(painter, scatterData);
15165 // free allocated line and point vectors:
15168 delete scatterData;
15171 /* inherits documentation from base class */
15172 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
15175 if (mBrush.style() != Qt::NoBrush)
15177 applyFillAntialiasingHint(painter);
15178 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
15180 // draw line vertically centered:
15181 if (mLineStyle != lsNone)
15183 applyDefaultAntialiasingHint(painter);
15184 painter->setPen(mPen);
15185 painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
15187 // draw scatter symbol:
15188 if (!mScatterStyle.isNone())
15190 applyScattersAntialiasingHint(painter);
15191 // scale scatter pixmap if it's too large to fit in legend icon rect:
15192 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
15194 QCPScatterStyle scaledStyle(mScatterStyle);
15195 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
15196 scaledStyle.applyTo(painter, mPen);
15197 scaledStyle.drawShape(painter, QRectF(rect).center());
15200 mScatterStyle.applyTo(painter, mPen);
15201 mScatterStyle.drawShape(painter, QRectF(rect).center());
15208 This function branches out to the line style specific "get(...)PlotData" functions, according to
15209 the line style of the graph.
15211 \a lineData will be filled with raw points that will be drawn with the according draw functions,
15212 e.g. \ref drawLinePlot and \ref drawImpulsePlot. These aren't necessarily the original data
15213 points, since for step plots for example, additional points are needed for drawing lines that
15214 make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
15217 \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
15218 scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
15219 \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
15221 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
15222 getStepCenterPlotData, getImpulsePlotData
15224 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
15228 case lsNone: getScatterPlotData(scatterData); break;
15229 case lsLine: getLinePlotData(lineData, scatterData); break;
15230 case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
15231 case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
15232 case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
15233 case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
15239 If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
15240 this function serves at providing the visible data points in \a scatterData, so the \ref
15241 drawScatterPlot function can draw the scatter points accordingly.
15243 If line style is not \ref lsNone, this function is not called and the data for the scatter points
15244 are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
15246 \see drawScatterPlot
15248 void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
15250 getPreparedData(0, scatterData);
15255 Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
15257 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15258 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15259 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15260 scatterData, and the function will skip filling the vector.
15264 void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15266 QCPAxis *keyAxis = mKeyAxis.data();
15267 QCPAxis *valueAxis = mValueAxis.data();
15268 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15269 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15271 QVector<QCPData> lineData;
15272 getPreparedData(&lineData, scatterData);
15273 linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15274 linePixelData->resize(lineData.size());
15276 // transform lineData points to pixels:
15277 if (keyAxis->orientation() == Qt::Vertical)
15279 for (int i=0; i<lineData.size(); ++i)
15281 (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
15282 (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
15284 } else // key axis is horizontal
15286 for (int i=0; i<lineData.size(); ++i)
15288 (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
15289 (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
15296 Places the raw data points needed for a step plot with left oriented steps in \a lineData.
15298 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15299 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15300 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15301 scatterData, and the function will skip filling the vector.
15305 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15307 QCPAxis *keyAxis = mKeyAxis.data();
15308 QCPAxis *valueAxis = mValueAxis.data();
15309 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15310 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15312 QVector<QCPData> lineData;
15313 getPreparedData(&lineData, scatterData);
15314 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15315 linePixelData->resize(lineData.size()*2);
15317 // calculate steps from lineData and transform to pixel coordinates:
15318 if (keyAxis->orientation() == Qt::Vertical)
15320 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15322 for (int i=0; i<lineData.size(); ++i)
15324 key = keyAxis->coordToPixel(lineData.at(i).key);
15325 (*linePixelData)[i*2+0].setX(lastValue);
15326 (*linePixelData)[i*2+0].setY(key);
15327 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15328 (*linePixelData)[i*2+1].setX(lastValue);
15329 (*linePixelData)[i*2+1].setY(key);
15331 } else // key axis is horizontal
15333 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15335 for (int i=0; i<lineData.size(); ++i)
15337 key = keyAxis->coordToPixel(lineData.at(i).key);
15338 (*linePixelData)[i*2+0].setX(key);
15339 (*linePixelData)[i*2+0].setY(lastValue);
15340 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15341 (*linePixelData)[i*2+1].setX(key);
15342 (*linePixelData)[i*2+1].setY(lastValue);
15349 Places the raw data points needed for a step plot with right oriented steps in \a lineData.
15351 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15352 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15353 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15354 scatterData, and the function will skip filling the vector.
15358 void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15360 QCPAxis *keyAxis = mKeyAxis.data();
15361 QCPAxis *valueAxis = mValueAxis.data();
15362 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15363 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15365 QVector<QCPData> lineData;
15366 getPreparedData(&lineData, scatterData);
15367 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15368 linePixelData->resize(lineData.size()*2);
15370 // calculate steps from lineData and transform to pixel coordinates:
15371 if (keyAxis->orientation() == Qt::Vertical)
15373 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15375 for (int i=0; i<lineData.size(); ++i)
15377 value = valueAxis->coordToPixel(lineData.at(i).value);
15378 (*linePixelData)[i*2+0].setX(value);
15379 (*linePixelData)[i*2+0].setY(lastKey);
15380 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15381 (*linePixelData)[i*2+1].setX(value);
15382 (*linePixelData)[i*2+1].setY(lastKey);
15384 } else // key axis is horizontal
15386 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15388 for (int i=0; i<lineData.size(); ++i)
15390 value = valueAxis->coordToPixel(lineData.at(i).value);
15391 (*linePixelData)[i*2+0].setX(lastKey);
15392 (*linePixelData)[i*2+0].setY(value);
15393 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15394 (*linePixelData)[i*2+1].setX(lastKey);
15395 (*linePixelData)[i*2+1].setY(value);
15402 Places the raw data points needed for a step plot with centered steps in \a lineData.
15404 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15405 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15406 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15407 scatterData, and the function will skip filling the vector.
15411 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15413 QCPAxis *keyAxis = mKeyAxis.data();
15414 QCPAxis *valueAxis = mValueAxis.data();
15415 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15416 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
15418 QVector<QCPData> lineData;
15419 getPreparedData(&lineData, scatterData);
15420 linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
15421 linePixelData->resize(lineData.size()*2);
15422 // calculate steps from lineData and transform to pixel coordinates:
15423 if (keyAxis->orientation() == Qt::Vertical)
15425 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15426 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15428 (*linePixelData)[0].setX(lastValue);
15429 (*linePixelData)[0].setY(lastKey);
15430 for (int i=1; i<lineData.size(); ++i)
15432 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15433 (*linePixelData)[i*2-1].setX(lastValue);
15434 (*linePixelData)[i*2-1].setY(key);
15435 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15436 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15437 (*linePixelData)[i*2+0].setX(lastValue);
15438 (*linePixelData)[i*2+0].setY(key);
15440 (*linePixelData)[lineData.size()*2-1].setX(lastValue);
15441 (*linePixelData)[lineData.size()*2-1].setY(lastKey);
15442 } else // key axis is horizontal
15444 double lastKey = keyAxis->coordToPixel(lineData.first().key);
15445 double lastValue = valueAxis->coordToPixel(lineData.first().value);
15447 (*linePixelData)[0].setX(lastKey);
15448 (*linePixelData)[0].setY(lastValue);
15449 for (int i=1; i<lineData.size(); ++i)
15451 key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
15452 (*linePixelData)[i*2-1].setX(key);
15453 (*linePixelData)[i*2-1].setY(lastValue);
15454 lastValue = valueAxis->coordToPixel(lineData.at(i).value);
15455 lastKey = keyAxis->coordToPixel(lineData.at(i).key);
15456 (*linePixelData)[i*2+0].setX(key);
15457 (*linePixelData)[i*2+0].setY(lastValue);
15459 (*linePixelData)[lineData.size()*2-1].setX(lastKey);
15460 (*linePixelData)[lineData.size()*2-1].setY(lastValue);
15467 Places the raw data points needed for an impulse plot in \a lineData.
15469 As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
15470 points that are visible for drawing scatter points, if necessary. If drawing scatter points is
15471 disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
15472 scatterData, and the function will skip filling the vector.
15474 \see drawImpulsePlot
15476 void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
15478 QCPAxis *keyAxis = mKeyAxis.data();
15479 QCPAxis *valueAxis = mValueAxis.data();
15480 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15481 if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
15483 QVector<QCPData> lineData;
15484 getPreparedData(&lineData, scatterData);
15485 linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
15487 // transform lineData points to pixels:
15488 if (keyAxis->orientation() == Qt::Vertical)
15490 double zeroPointX = valueAxis->coordToPixel(0);
15492 for (int i=0; i<lineData.size(); ++i)
15494 key = keyAxis->coordToPixel(lineData.at(i).key);
15495 (*linePixelData)[i*2+0].setX(zeroPointX);
15496 (*linePixelData)[i*2+0].setY(key);
15497 (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
15498 (*linePixelData)[i*2+1].setY(key);
15500 } else // key axis is horizontal
15502 double zeroPointY = valueAxis->coordToPixel(0);
15504 for (int i=0; i<lineData.size(); ++i)
15506 key = keyAxis->coordToPixel(lineData.at(i).key);
15507 (*linePixelData)[i*2+0].setX(key);
15508 (*linePixelData)[i*2+0].setY(zeroPointY);
15509 (*linePixelData)[i*2+1].setX(key);
15510 (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
15517 Draws the fill of the graph with the specified brush.
15519 If the fill is a normal fill towards the zero-value-line, only the \a lineData is required (and
15520 two extra points at the zero-value-line, which are added by \ref addFillBasePoints and removed by
15521 \ref removeFillBasePoints after the fill drawing is done).
15523 If the fill is a channel fill between this QCPGraph and another QCPGraph (mChannelFillGraph), the
15524 more complex polygon is calculated with the \ref getChannelFillPolygon function.
15528 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
15530 if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
15531 if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
15533 applyFillAntialiasingHint(painter);
15534 if (!mChannelFillGraph)
15536 // draw base fill under graph, fill goes all the way to the zero-value-line:
15537 addFillBasePoints(lineData);
15538 painter->setPen(Qt::NoPen);
15539 painter->setBrush(mainBrush());
15540 painter->drawPolygon(QPolygonF(*lineData));
15541 removeFillBasePoints(lineData);
15544 // draw channel fill between this graph and mChannelFillGraph:
15545 painter->setPen(Qt::NoPen);
15546 painter->setBrush(mainBrush());
15547 painter->drawPolygon(getChannelFillPolygon(lineData));
15553 Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
15554 of the line style and are always drawn if the scatter style's shape is not \ref
15555 QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
15556 functions, together with the (line style dependent) line data.
15558 \see drawLinePlot, drawImpulsePlot
15560 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
15562 QCPAxis *keyAxis = mKeyAxis.data();
15563 QCPAxis *valueAxis = mValueAxis.data();
15564 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15566 // draw error bars:
15567 if (mErrorType != etNone)
15569 applyErrorBarsAntialiasingHint(painter);
15570 painter->setPen(mErrorPen);
15571 if (keyAxis->orientation() == Qt::Vertical)
15573 for (int i=0; i<scatterData->size(); ++i)
15574 drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
15577 for (int i=0; i<scatterData->size(); ++i)
15578 drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
15582 // draw scatter point symbols:
15583 applyScattersAntialiasingHint(painter);
15584 mScatterStyle.applyTo(painter, mPen);
15585 if (keyAxis->orientation() == Qt::Vertical)
15587 for (int i=0; i<scatterData->size(); ++i)
15588 if (!qIsNaN(scatterData->at(i).value))
15589 mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
15592 for (int i=0; i<scatterData->size(); ++i)
15593 if (!qIsNaN(scatterData->at(i).value))
15594 mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
15600 Draws line graphs from the provided data. It connects all points in \a lineData, which was
15601 created by one of the "get(...)PlotData" functions for line styles that require simple line
15602 connections between the point vector they create. These are for example \ref getLinePlotData,
15603 \ref getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
15605 \see drawScatterPlot, drawImpulsePlot
15607 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15609 // draw line of graph:
15610 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15612 applyDefaultAntialiasingHint(painter);
15613 painter->setPen(mainPen());
15614 painter->setBrush(Qt::NoBrush);
15616 /* Draws polyline in batches, currently not used:
15618 while (p < lineData->size())
15620 int batch = qMin(25, lineData->size()-p);
15624 --p; // to draw the connection lines between two batches
15626 painter->drawPolyline(lineData->constData()+p, batch);
15631 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
15632 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
15633 painter->pen().style() == Qt::SolidLine &&
15634 !painter->modes().testFlag(QCPPainter::pmVectorized)&&
15635 !painter->modes().testFlag(QCPPainter::pmNoCaching))
15638 int lineDataSize = lineData->size();
15639 while (i < lineDataSize)
15641 if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15642 painter->drawLine(lineData->at(i-1), lineData->at(i));
15649 int segmentStart = 0;
15651 int lineDataSize = lineData->size();
15652 while (i < lineDataSize)
15654 if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
15656 painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
15657 segmentStart = i+1;
15661 // draw last segment:
15662 painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); // lineDataSize, because we do want to include the last point
15669 Draws impulses from the provided data, i.e. it connects all line pairs in \a lineData, which was
15670 created by \ref getImpulsePlotData.
15672 \see drawScatterPlot, drawLinePlot
15674 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
15677 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
15679 applyDefaultAntialiasingHint(painter);
15680 QPen pen = mainPen();
15681 pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
15682 painter->setPen(pen);
15683 painter->setBrush(Qt::NoBrush);
15684 painter->drawLines(*lineData);
15690 Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into
15691 consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point
15694 0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't
15695 needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a
15696 scatterData should be 0 to prevent unnecessary calculations.
15698 This method is used by the various "get(...)PlotData" methods to get the basic working set of data.
15700 void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
15702 QCPAxis *keyAxis = mKeyAxis.data();
15703 QCPAxis *valueAxis = mValueAxis.data();
15704 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15705 // get visible data range:
15706 QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
15707 getVisibleDataBounds(lower, upper);
15708 if (lower == mData->constEnd() || upper == mData->constEnd())
15711 // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
15712 int maxCount = std::numeric_limits<int>::max();
15713 if (mAdaptiveSampling)
15715 int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
15716 maxCount = 2*keyPixelSpan+2;
15718 int dataCount = countDataInBounds(lower, upper, maxCount);
15720 if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
15724 QCPDataMap::const_iterator it = lower;
15725 QCPDataMap::const_iterator upperEnd = upper+1;
15726 double minValue = it.value().value;
15727 double maxValue = it.value().value;
15728 QCPDataMap::const_iterator currentIntervalFirstPoint = it;
15729 int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15730 int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15731 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15732 double lastIntervalEndKey = currentIntervalStartKey;
15733 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15734 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15735 int intervalDataCount = 1;
15736 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15737 while (it != upperEnd)
15739 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
15741 if (it.value().value < minValue)
15742 minValue = it.value().value;
15743 else if (it.value().value > maxValue)
15744 maxValue = it.value().value;
15745 ++intervalDataCount;
15746 } else // new pixel interval started
15748 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15750 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
15751 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15752 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15753 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15754 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
15755 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
15757 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15758 lastIntervalEndKey = (it-1).value().key;
15759 minValue = it.value().value;
15760 maxValue = it.value().value;
15761 currentIntervalFirstPoint = it;
15762 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15763 if (keyEpsilonVariable)
15764 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15765 intervalDataCount = 1;
15769 // handle last interval:
15770 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
15772 if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
15773 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
15774 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
15775 lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
15777 lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
15782 double valueMaxRange = valueAxis->range().upper;
15783 double valueMinRange = valueAxis->range().lower;
15784 QCPDataMap::const_iterator it = lower;
15785 QCPDataMap::const_iterator upperEnd = upper+1;
15786 double minValue = it.value().value;
15787 double maxValue = it.value().value;
15788 QCPDataMap::const_iterator minValueIt = it;
15789 QCPDataMap::const_iterator maxValueIt = it;
15790 QCPDataMap::const_iterator currentIntervalStart = it;
15791 int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
15792 int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
15793 double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
15794 double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
15795 bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
15796 int intervalDataCount = 1;
15797 ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
15798 while (it != upperEnd)
15800 if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
15802 if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15804 minValue = it.value().value;
15806 } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
15808 maxValue = it.value().value;
15811 ++intervalDataCount;
15812 } else // new pixel started
15814 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15816 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15817 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15818 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15819 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15821 while (intervalIt != it)
15823 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15824 scatterData->append(intervalIt.value());
15828 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15829 scatterData->append(currentIntervalStart.value());
15830 minValue = it.value().value;
15831 maxValue = it.value().value;
15832 currentIntervalStart = it;
15833 currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
15834 if (keyEpsilonVariable)
15835 keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
15836 intervalDataCount = 1;
15840 // handle last interval:
15841 if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
15843 // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
15844 double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
15845 int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
15846 QCPDataMap::const_iterator intervalIt = currentIntervalStart;
15848 while (intervalIt != it)
15850 if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
15851 scatterData->append(intervalIt.value());
15855 } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
15856 scatterData->append(currentIntervalStart.value());
15858 } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
15860 QVector<QCPData> *dataVector = 0;
15862 dataVector = lineData;
15863 else if (scatterData)
15864 dataVector = scatterData;
15867 QCPDataMap::const_iterator it = lower;
15868 QCPDataMap::const_iterator upperEnd = upper+1;
15869 dataVector->reserve(dataCount+2); // +2 for possible fill end points
15870 while (it != upperEnd)
15872 dataVector->append(it.value());
15876 if (lineData && scatterData)
15877 *scatterData = *dataVector;
15883 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
15884 point. \a x and \a y pixel positions of the data point are passed since they are already known in
15885 pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
15886 data is therefore only used for the errors, not key and value.
15888 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
15890 if (qIsNaN(data.value))
15892 QCPAxis *keyAxis = mKeyAxis.data();
15893 QCPAxis *valueAxis = mValueAxis.data();
15894 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
15896 double a, b; // positions of error bar bounds in pixels
15897 double barWidthHalf = mErrorBarSize*0.5;
15898 double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
15900 if (keyAxis->orientation() == Qt::Vertical)
15902 // draw key error vertically and value error horizontally
15903 if (mErrorType == etKey || mErrorType == etBoth)
15905 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15906 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15907 if (keyAxis->rangeReversed())
15910 if (mErrorBarSkipSymbol)
15912 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15913 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15914 if (y-b > skipSymbolMargin)
15915 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15917 painter->drawLine(QLineF(x, a, x, b));
15919 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15920 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15922 if (mErrorType == etValue || mErrorType == etBoth)
15924 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15925 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15926 if (valueAxis->rangeReversed())
15929 if (mErrorBarSkipSymbol)
15931 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15932 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15933 if (b-x > skipSymbolMargin)
15934 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15936 painter->drawLine(QLineF(a, y, b, y));
15938 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15939 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15941 } else // mKeyAxis->orientation() is Qt::Horizontal
15943 // draw value error vertically and key error horizontally
15944 if (mErrorType == etKey || mErrorType == etBoth)
15946 a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
15947 b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
15948 if (keyAxis->rangeReversed())
15951 if (mErrorBarSkipSymbol)
15953 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15954 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
15955 if (b-x > skipSymbolMargin)
15956 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
15958 painter->drawLine(QLineF(a, y, b, y));
15960 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
15961 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
15963 if (mErrorType == etValue || mErrorType == etBoth)
15965 a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
15966 b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
15967 if (valueAxis->rangeReversed())
15970 if (mErrorBarSkipSymbol)
15972 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
15973 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
15974 if (y-b > skipSymbolMargin)
15975 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
15977 painter->drawLine(QLineF(x, a, x, b));
15979 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
15980 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
15987 called by \ref getPreparedData to determine which data (key) range is visible at the current key
15988 axis range setting, so only that needs to be processed.
15990 \a lower returns an iterator to the lowest data point that needs to be taken into account when
15991 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
15992 lower may still be just outside the visible range.
15994 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
15995 just outside of the visible range.
15997 if the graph contains no data, both \a lower and \a upper point to constEnd.
15999 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
16001 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16002 if (mData->isEmpty())
16004 lower = mData->constEnd();
16005 upper = mData->constEnd();
16009 // get visible data range as QMap iterators
16010 QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
16011 QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
16012 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
16013 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
16015 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
16016 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
16021 Counts the number of data points between \a lower and \a upper (including them), up to a maximum
16024 This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
16025 used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
16026 only needs to be done until \a maxCount is reached, which should be set to the number of data
16027 points at which adaptive sampling sets in.
16029 int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
16031 if (upper == mData->constEnd() && lower == mData->constEnd())
16033 QCPDataMap::const_iterator it = lower;
16035 while (it != upper && count < maxCount)
16045 The line data vector generated by e.g. getLinePlotData contains only the line that connects the
16046 data points. If the graph needs to be filled, two additional points need to be added at the
16047 value-zero-line in the lower and upper key positions of the graph. This function calculates these
16048 points and adds them to the end of \a lineData. Since the fill is typically drawn before the line
16049 stroke, these added points need to be removed again after the fill is done, with the
16050 removeFillBasePoints function.
16052 The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
16053 because the data vector generation functions (getLinePlotData etc.) reserve two extra points when
16054 they allocate memory for \a lineData.
16056 \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16058 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
16060 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
16062 // append points that close the polygon fill at the key axis:
16063 if (mKeyAxis.data()->orientation() == Qt::Vertical)
16065 *lineData << upperFillBasePoint(lineData->last().y());
16066 *lineData << lowerFillBasePoint(lineData->first().y());
16069 *lineData << upperFillBasePoint(lineData->last().x());
16070 *lineData << lowerFillBasePoint(lineData->first().x());
16076 removes the two points from \a lineData that were added by \ref addFillBasePoints.
16078 \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
16080 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
16082 lineData->remove(lineData->size()-2, 2);
16087 called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon
16088 on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale
16089 case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative
16090 infinity. So this case is handled separately by just closing the fill polygon on the axis which
16091 lies in the direction towards the zero value.
16093 \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key
16094 axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned
16095 point, respectively.
16097 \see upperFillBasePoint, addFillBasePoints
16099 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
16101 QCPAxis *keyAxis = mKeyAxis.data();
16102 QCPAxis *valueAxis = mValueAxis.data();
16103 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16106 if (valueAxis->scaleType() == QCPAxis::stLinear)
16108 if (keyAxis->axisType() == QCPAxis::atLeft)
16110 point.setX(valueAxis->coordToPixel(0));
16111 point.setY(lowerKey);
16112 } else if (keyAxis->axisType() == QCPAxis::atRight)
16114 point.setX(valueAxis->coordToPixel(0));
16115 point.setY(lowerKey);
16116 } else if (keyAxis->axisType() == QCPAxis::atTop)
16118 point.setX(lowerKey);
16119 point.setY(valueAxis->coordToPixel(0));
16120 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16122 point.setX(lowerKey);
16123 point.setY(valueAxis->coordToPixel(0));
16125 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16127 // In logarithmic scaling we can't just draw to value zero so we just fill all the way
16128 // to the axis which is in the direction towards zero
16129 if (keyAxis->orientation() == Qt::Vertical)
16131 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16132 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16133 point.setX(keyAxis->axisRect()->right());
16135 point.setX(keyAxis->axisRect()->left());
16136 point.setY(lowerKey);
16137 } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16139 point.setX(lowerKey);
16140 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16141 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16142 point.setY(keyAxis->axisRect()->top());
16144 point.setY(keyAxis->axisRect()->bottom());
16152 called by \ref addFillBasePoints to conveniently assign the point which closes the fill
16153 polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
16154 scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
16155 negative infinity. So this case is handled separately by just closing the fill polygon on the
16156 axis which lies in the direction towards the zero value.
16158 \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key
16159 axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned
16160 point, respectively.
16162 \see lowerFillBasePoint, addFillBasePoints
16164 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
16166 QCPAxis *keyAxis = mKeyAxis.data();
16167 QCPAxis *valueAxis = mValueAxis.data();
16168 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
16171 if (valueAxis->scaleType() == QCPAxis::stLinear)
16173 if (keyAxis->axisType() == QCPAxis::atLeft)
16175 point.setX(valueAxis->coordToPixel(0));
16176 point.setY(upperKey);
16177 } else if (keyAxis->axisType() == QCPAxis::atRight)
16179 point.setX(valueAxis->coordToPixel(0));
16180 point.setY(upperKey);
16181 } else if (keyAxis->axisType() == QCPAxis::atTop)
16183 point.setX(upperKey);
16184 point.setY(valueAxis->coordToPixel(0));
16185 } else if (keyAxis->axisType() == QCPAxis::atBottom)
16187 point.setX(upperKey);
16188 point.setY(valueAxis->coordToPixel(0));
16190 } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
16192 // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
16193 // to the axis which is in the direction towards 0
16194 if (keyAxis->orientation() == Qt::Vertical)
16196 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16197 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16198 point.setX(keyAxis->axisRect()->right());
16200 point.setX(keyAxis->axisRect()->left());
16201 point.setY(upperKey);
16202 } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
16204 point.setX(upperKey);
16205 if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
16206 (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
16207 point.setY(keyAxis->axisRect()->top());
16209 point.setY(keyAxis->axisRect()->bottom());
16217 Generates the polygon needed for drawing channel fills between this graph (data passed via \a
16218 lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
16219 getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
16220 target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
16221 key axes vertical). For increased performance (due to implicit sharing), keep the returned
16224 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
16226 if (!mChannelFillGraph)
16227 return QPolygonF();
16229 QCPAxis *keyAxis = mKeyAxis.data();
16230 QCPAxis *valueAxis = mValueAxis.data();
16231 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
16232 if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
16234 if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
16235 return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
16237 if (lineData->isEmpty()) return QPolygonF();
16238 QVector<QPointF> otherData;
16239 mChannelFillGraph.data()->getPlotData(&otherData, 0);
16240 if (otherData.isEmpty()) return QPolygonF();
16241 QVector<QPointF> thisData;
16242 thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
16243 for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
16244 thisData << lineData->at(i);
16246 // pointers to be able to swap them, depending which data range needs cropping:
16247 QVector<QPointF> *staticData = &thisData;
16248 QVector<QPointF> *croppedData = &otherData;
16250 // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
16251 if (keyAxis->orientation() == Qt::Horizontal)
16254 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16255 if (staticData->first().x() > staticData->last().x())
16257 int size = staticData->size();
16258 for (int i=0; i<size/2; ++i)
16259 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16261 if (croppedData->first().x() > croppedData->last().x())
16263 int size = croppedData->size();
16264 for (int i=0; i<size/2; ++i)
16265 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16267 // crop lower bound:
16268 if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
16269 qSwap(staticData, croppedData);
16270 int lowBound = findIndexBelowX(croppedData, staticData->first().x());
16271 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16272 croppedData->remove(0, lowBound);
16273 // set lowest point of cropped data to fit exactly key position of first static data
16274 // point via linear interpolation:
16275 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16277 if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
16278 slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
16281 (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
16282 (*croppedData)[0].setX(staticData->first().x());
16284 // crop upper bound:
16285 if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
16286 qSwap(staticData, croppedData);
16287 int highBound = findIndexAboveX(croppedData, staticData->last().x());
16288 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16289 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16290 // set highest point of cropped data to fit exactly key position of last static data
16291 // point via linear interpolation:
16292 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16293 int li = croppedData->size()-1; // last index
16294 if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
16295 slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
16298 (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
16299 (*croppedData)[li].setX(staticData->last().x());
16300 } else // mKeyAxis->orientation() == Qt::Vertical
16303 // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
16304 // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
16305 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
16306 if (staticData->first().y() < staticData->last().y())
16308 int size = staticData->size();
16309 for (int i=0; i<size/2; ++i)
16310 qSwap((*staticData)[i], (*staticData)[size-1-i]);
16312 if (croppedData->first().y() < croppedData->last().y())
16314 int size = croppedData->size();
16315 for (int i=0; i<size/2; ++i)
16316 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
16318 // crop lower bound:
16319 if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
16320 qSwap(staticData, croppedData);
16321 int lowBound = findIndexAboveY(croppedData, staticData->first().y());
16322 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
16323 croppedData->remove(0, lowBound);
16324 // set lowest point of cropped data to fit exactly key position of first static data
16325 // point via linear interpolation:
16326 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16328 if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
16329 slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
16332 (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
16333 (*croppedData)[0].setY(staticData->first().y());
16335 // crop upper bound:
16336 if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
16337 qSwap(staticData, croppedData);
16338 int highBound = findIndexBelowY(croppedData, staticData->last().y());
16339 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
16340 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
16341 // set highest point of cropped data to fit exactly key position of last static data
16342 // point via linear interpolation:
16343 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
16344 int li = croppedData->size()-1; // last index
16345 if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
16346 slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
16349 (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
16350 (*croppedData)[li].setY(staticData->last().y());
16354 for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
16355 thisData << otherData.at(i);
16356 return QPolygonF(thisData);
16361 Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in
16362 \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16364 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16366 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
16368 for (int i=data->size()-1; i>=0; --i)
16370 if (data->at(i).x() < x)
16372 if (i<data->size()-1)
16375 return data->size()-1;
16383 Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in
16384 \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
16386 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16388 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
16390 for (int i=0; i<data->size(); ++i)
16392 if (data->at(i).x() > x)
16405 Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in
16406 \a data points are ordered descending, as is the case when plotting with vertical key axis.
16408 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16410 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
16412 for (int i=0; i<data->size(); ++i)
16414 if (data->at(i).y() < y)
16427 Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
16428 pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
16431 If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape
16432 is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns
16435 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
16437 if (mData->isEmpty())
16439 qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data";
16442 if (mData->size() == 1)
16444 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
16445 return QVector2D(dataPoint-pixelPoint).length();
16448 if (mLineStyle == lsNone && mScatterStyle.isNone())
16451 // calculate minimum distances to graph representation:
16452 if (mLineStyle == lsNone)
16454 // no line displayed, only calculate distance to scatter points:
16455 QVector<QCPData> *scatterData = new QVector<QCPData>;
16456 getScatterPlotData(scatterData);
16457 double minDistSqr = std::numeric_limits<double>::max();
16459 QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
16460 for (int i=1; i<scatterData->size(); ++i)
16463 ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value);
16464 double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
16465 if (currentDistSqr < minDistSqr)
16466 minDistSqr = currentDistSqr;
16468 delete scatterData;
16469 return qSqrt(minDistSqr);
16472 // line displayed calculate distance to line segments:
16473 QVector<QPointF> *lineData = new QVector<QPointF>;
16474 getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
16475 double minDistSqr = std::numeric_limits<double>::max();
16476 if (mLineStyle == lsImpulse)
16478 // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
16479 for (int i=0; i<lineData->size()-1; i+=2) // iterate pairs
16481 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16482 if (currentDistSqr < minDistSqr)
16483 minDistSqr = currentDistSqr;
16487 // all other line plots (line and step) connect points directly:
16488 for (int i=0; i<lineData->size()-1; ++i)
16490 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
16491 if (currentDistSqr < minDistSqr)
16492 minDistSqr = currentDistSqr;
16496 return qSqrt(minDistSqr);
16502 Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in
16503 \a data points are ordered descending, as is the case when plotting with vertical key axis (since
16504 keys are ordered ascending).
16506 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
16508 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
16510 for (int i=data->size()-1; i>=0; --i)
16512 if (data->at(i).y() > y)
16514 if (i<data->size()-1)
16517 return data->size()-1;
16523 /* inherits documentation from base class */
16524 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
16526 // just call the specialized version which takes an additional argument whether error bars
16527 // should also be taken into consideration for range calculation. We set this to true here.
16528 return getKeyRange(foundRange, inSignDomain, true);
16531 /* inherits documentation from base class */
16532 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
16534 // just call the specialized version which takes an additional argument whether error bars
16535 // should also be taken into consideration for range calculation. We set this to true here.
16536 return getValueRange(foundRange, inSignDomain, true);
16541 Allows to specify whether the error bars should be included in the range calculation.
16543 \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
16545 QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16548 bool haveLower = false;
16549 bool haveUpper = false;
16551 double current, currentErrorMinus, currentErrorPlus;
16553 if (inSignDomain == sdBoth) // range may be anywhere
16555 QCPDataMap::const_iterator it = mData->constBegin();
16556 while (it != mData->constEnd())
16558 current = it.value().key;
16559 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16560 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16561 if (current-currentErrorMinus < range.lower || !haveLower)
16563 range.lower = current-currentErrorMinus;
16566 if (current+currentErrorPlus > range.upper || !haveUpper)
16568 range.upper = current+currentErrorPlus;
16573 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16575 QCPDataMap::const_iterator it = mData->constBegin();
16576 while (it != mData->constEnd())
16578 current = it.value().key;
16579 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16580 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16581 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16583 range.lower = current-currentErrorMinus;
16586 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16588 range.upper = current+currentErrorPlus;
16591 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16593 if ((current < range.lower || !haveLower) && current < 0)
16595 range.lower = current;
16598 if ((current > range.upper || !haveUpper) && current < 0)
16600 range.upper = current;
16606 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16608 QCPDataMap::const_iterator it = mData->constBegin();
16609 while (it != mData->constEnd())
16611 current = it.value().key;
16612 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
16613 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
16614 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16616 range.lower = current-currentErrorMinus;
16619 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16621 range.upper = current+currentErrorPlus;
16624 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16626 if ((current < range.lower || !haveLower) && current > 0)
16628 range.lower = current;
16631 if ((current > range.upper || !haveUpper) && current > 0)
16633 range.upper = current;
16641 foundRange = haveLower && haveUpper;
16647 Allows to specify whether the error bars should be included in the range calculation.
16649 \see getValueRange(bool &foundRange, SignDomain inSignDomain)
16651 QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
16654 bool haveLower = false;
16655 bool haveUpper = false;
16657 double current, currentErrorMinus, currentErrorPlus;
16659 if (inSignDomain == sdBoth) // range may be anywhere
16661 QCPDataMap::const_iterator it = mData->constBegin();
16662 while (it != mData->constEnd())
16664 current = it.value().value;
16665 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16666 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16667 if (current-currentErrorMinus < range.lower || !haveLower)
16669 range.lower = current-currentErrorMinus;
16672 if (current+currentErrorPlus > range.upper || !haveUpper)
16674 range.upper = current+currentErrorPlus;
16679 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
16681 QCPDataMap::const_iterator it = mData->constBegin();
16682 while (it != mData->constEnd())
16684 current = it.value().value;
16685 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16686 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16687 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
16689 range.lower = current-currentErrorMinus;
16692 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
16694 range.upper = current+currentErrorPlus;
16697 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
16699 if ((current < range.lower || !haveLower) && current < 0)
16701 range.lower = current;
16704 if ((current > range.upper || !haveUpper) && current < 0)
16706 range.upper = current;
16712 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
16714 QCPDataMap::const_iterator it = mData->constBegin();
16715 while (it != mData->constEnd())
16717 current = it.value().value;
16718 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
16719 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
16720 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
16722 range.lower = current-currentErrorMinus;
16725 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
16727 range.upper = current+currentErrorPlus;
16730 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
16732 if ((current < range.lower || !haveLower) && current > 0)
16734 range.lower = current;
16737 if ((current > range.upper || !haveUpper) && current > 0)
16739 range.upper = current;
16747 foundRange = haveLower && haveUpper;
16752 ////////////////////////////////////////////////////////////////////////////////////////////////////
16753 //////////////////// QCPCurveData
16754 ////////////////////////////////////////////////////////////////////////////////////////////////////
16756 /*! \class QCPCurveData
16757 \brief Holds the data of one single data point for QCPCurve.
16759 The container for storing multiple data points is \ref QCPCurveDataMap.
16761 The stored data is:
16762 \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
16763 \li \a key: coordinate on the key axis of this curve point
16764 \li \a value: coordinate on the value axis of this curve point
16766 \see QCPCurveDataMap
16770 Constructs a curve data point with t, key and value set to zero.
16772 QCPCurveData::QCPCurveData() :
16780 Constructs a curve data point with the specified \a t, \a key and \a value.
16782 QCPCurveData::QCPCurveData(double t, double key, double value) :
16790 ////////////////////////////////////////////////////////////////////////////////////////////////////
16791 //////////////////// QCPCurve
16792 ////////////////////////////////////////////////////////////////////////////////////////////////////
16794 /*! \class QCPCurve
16795 \brief A plottable representing a parametric curve in a plot.
16797 \image html QCPCurve.png
16799 Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate,
16800 so their visual representation can have \a loops. This is realized by introducing a third
16801 coordinate \a t, which defines the order of the points described by the other two coordinates \a
16804 To plot data, assign it with the \ref setData or \ref addData functions.
16806 \section appearance Changing the appearance
16808 The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
16809 \section usage Usage
16811 Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
16812 the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
16814 Usually, you first create an instance:
16816 QCPCurve *newCurve = new QCPCurve(customPlot->xAxis, customPlot->yAxis);\endcode
16817 add it to the customPlot with QCustomPlot::addPlottable:
16819 customPlot->addPlottable(newCurve);\endcode
16820 and then modify the properties of the newly created plottable, e.g.:
16822 newCurve->setName("Fermat's Spiral");
16823 newCurve->setData(tData, xData, yData);\endcode
16827 Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
16828 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
16829 the same orientation. If either of these restrictions is violated, a corresponding message is
16830 printed to the debug output (qDebug), the construction is not aborted, though.
16832 The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
16833 then takes ownership of the graph.
16835 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
16836 QCPAbstractPlottable(keyAxis, valueAxis)
16838 mData = new QCPCurveDataMap;
16839 mPen.setColor(Qt::blue);
16840 mPen.setStyle(Qt::SolidLine);
16841 mBrush.setColor(Qt::blue);
16842 mBrush.setStyle(Qt::NoBrush);
16843 mSelectedPen = mPen;
16844 mSelectedPen.setWidthF(2.5);
16845 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
16846 mSelectedBrush = mBrush;
16848 setScatterStyle(QCPScatterStyle());
16849 setLineStyle(lsLine);
16852 QCPCurve::~QCPCurve()
16858 Replaces the current data with the provided \a data.
16860 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
16861 takes ownership of the passed data and replaces the internal data pointer with it. This is
16862 significantly faster than copying for large datasets.
16864 void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
16868 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
16883 Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
16884 provided vectors should have equal length. Else, the number of added points will be the size of
16885 the smallest vector.
16887 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
16891 n = qMin(n, key.size());
16892 n = qMin(n, value.size());
16893 QCPCurveData newData;
16894 for (int i=0; i<n; ++i)
16897 newData.key = key[i];
16898 newData.value = value[i];
16899 mData->insertMulti(newData.t, newData);
16905 Replaces the current data with the provided \a key and \a value pairs. The t parameter
16906 of each data point will be set to the integer index of the respective key/value pair.
16908 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
16911 int n = key.size();
16912 n = qMin(n, value.size());
16913 QCPCurveData newData;
16914 for (int i=0; i<n; ++i)
16916 newData.t = i; // no t vector given, so we assign t the index of the key/value pair
16917 newData.key = key[i];
16918 newData.value = value[i];
16919 mData->insertMulti(newData.t, newData);
16924 Sets the visual appearance of single data points in the plot. If set to \ref
16925 QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate
16928 \see QCPScatterStyle, setLineStyle
16930 void QCPCurve::setScatterStyle(const QCPScatterStyle &style)
16932 mScatterStyle = style;
16936 Sets how the single data points are connected in the plot or how they are represented visually
16937 apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
16938 setScatterStyle to the desired scatter style.
16940 \see setScatterStyle
16942 void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
16944 mLineStyle = style;
16948 Adds the provided data points in \a dataMap to the current data.
16951 void QCPCurve::addData(const QCPCurveDataMap &dataMap)
16953 mData->unite(dataMap);
16957 Adds the provided single data point in \a data to the current data.
16960 void QCPCurve::addData(const QCPCurveData &data)
16962 mData->insertMulti(data.t, data);
16966 Adds the provided single data point as \a t, \a key and \a value tuple to the current data
16969 void QCPCurve::addData(double t, double key, double value)
16971 QCPCurveData newData;
16974 newData.value = value;
16975 mData->insertMulti(newData.t, newData);
16980 Adds the provided single data point as \a key and \a value pair to the current data The t
16981 parameter of the data point is set to the t of the last data point plus 1. If there is no last
16982 data point, t will be set to 0.
16986 void QCPCurve::addData(double key, double value)
16988 QCPCurveData newData;
16989 if (!mData->isEmpty())
16990 newData.t = (mData->constEnd()-1).key()+1;
16994 newData.value = value;
16995 mData->insertMulti(newData.t, newData);
16999 Adds the provided data points as \a t, \a key and \a value tuples to the current data.
17002 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
17005 n = qMin(n, keys.size());
17006 n = qMin(n, values.size());
17007 QCPCurveData newData;
17008 for (int i=0; i<n; ++i)
17011 newData.key = keys[i];
17012 newData.value = values[i];
17013 mData->insertMulti(newData.t, newData);
17018 Removes all data points with curve parameter t smaller than \a t.
17019 \see addData, clearData
17021 void QCPCurve::removeDataBefore(double t)
17023 QCPCurveDataMap::iterator it = mData->begin();
17024 while (it != mData->end() && it.key() < t)
17025 it = mData->erase(it);
17029 Removes all data points with curve parameter t greater than \a t.
17030 \see addData, clearData
17032 void QCPCurve::removeDataAfter(double t)
17034 if (mData->isEmpty()) return;
17035 QCPCurveDataMap::iterator it = mData->upperBound(t);
17036 while (it != mData->end())
17037 it = mData->erase(it);
17041 Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
17042 greater or equal to \a tot, the function does nothing. To remove a single data point with known
17043 t, use \ref removeData(double t).
17045 \see addData, clearData
17047 void QCPCurve::removeData(double fromt, double tot)
17049 if (fromt >= tot || mData->isEmpty()) return;
17050 QCPCurveDataMap::iterator it = mData->upperBound(fromt);
17051 QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
17052 while (it != itEnd)
17053 it = mData->erase(it);
17058 Removes a single data point at curve parameter \a t. If the position is not known with absolute
17059 precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
17060 interval around the suspected position, depeding on the precision with which the curve parameter
17063 \see addData, clearData
17065 void QCPCurve::removeData(double t)
17071 Removes all data points.
17072 \see removeData, removeDataAfter, removeDataBefore
17074 void QCPCurve::clearData()
17079 /* inherits documentation from base class */
17080 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
17083 if ((onlySelectable && !mSelectable) || mData->isEmpty())
17085 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
17087 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
17088 return pointDistance(pos);
17093 /* inherits documentation from base class */
17094 void QCPCurve::draw(QCPPainter *painter)
17096 if (mData->isEmpty()) return;
17098 // allocate line vector:
17099 QVector<QPointF> *lineData = new QVector<QPointF>;
17101 // fill with curve data:
17102 getCurveData(lineData);
17104 // check data validity if flag set:
17105 #ifdef QCUSTOMPLOT_CHECK_DATA
17106 QCPCurveDataMap::const_iterator it;
17107 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
17109 if (QCP::isInvalidData(it.value().t) ||
17110 QCP::isInvalidData(it.value().key, it.value().value))
17111 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
17115 // draw curve fill:
17116 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
17118 applyFillAntialiasingHint(painter);
17119 painter->setPen(Qt::NoPen);
17120 painter->setBrush(mainBrush());
17121 painter->drawPolygon(QPolygonF(*lineData));
17124 // draw curve line:
17125 if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
17127 applyDefaultAntialiasingHint(painter);
17128 painter->setPen(mainPen());
17129 painter->setBrush(Qt::NoBrush);
17130 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
17131 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
17132 painter->pen().style() == Qt::SolidLine &&
17133 !painter->modes().testFlag(QCPPainter::pmVectorized) &&
17134 !painter->modes().testFlag(QCPPainter::pmNoCaching))
17137 int lineDataSize = lineData->size();
17138 while (i < lineDataSize)
17140 if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17141 painter->drawLine(lineData->at(i-1), lineData->at(i));
17148 int segmentStart = 0;
17150 int lineDataSize = lineData->size();
17151 while (i < lineDataSize)
17153 if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
17155 painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
17156 segmentStart = i+1;
17160 // draw last segment:
17161 painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); // lineDataSize, because we do want to include the last point
17166 if (!mScatterStyle.isNone())
17167 drawScatterPlot(painter, lineData);
17169 // free allocated line data:
17173 /* inherits documentation from base class */
17174 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
17177 if (mBrush.style() != Qt::NoBrush)
17179 applyFillAntialiasingHint(painter);
17180 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
17182 // draw line vertically centered:
17183 if (mLineStyle != lsNone)
17185 applyDefaultAntialiasingHint(painter);
17186 painter->setPen(mPen);
17187 painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
17189 // draw scatter symbol:
17190 if (!mScatterStyle.isNone())
17192 applyScattersAntialiasingHint(painter);
17193 // scale scatter pixmap if it's too large to fit in legend icon rect:
17194 if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
17196 QCPScatterStyle scaledStyle(mScatterStyle);
17197 scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
17198 scaledStyle.applyTo(painter, mPen);
17199 scaledStyle.drawShape(painter, QRectF(rect).center());
17202 mScatterStyle.applyTo(painter, mPen);
17203 mScatterStyle.drawShape(painter, QRectF(rect).center());
17210 Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
17211 the line style and are always drawn if scatter shape is not \ref QCPScatterStyle::ssNone.
17213 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
17215 // draw scatter point symbols:
17216 applyScattersAntialiasingHint(painter);
17217 mScatterStyle.applyTo(painter, mPen);
17218 for (int i=0; i<pointData->size(); ++i)
17219 if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
17220 mScatterStyle.drawShape(painter, pointData->at(i));
17225 called by QCPCurve::draw to generate a point vector (in pixel coordinates) which represents the
17228 Line segments that aren't visible in the current axis rect are handled in an optimized way. They
17229 are projected onto a rectangle slightly larger than the visible axis rect and simplified
17230 regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside
17231 the visible axis rect by generating new temporary points on the outer rect if necessary.
17233 Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref
17234 getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints.
17236 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
17238 QCPAxis *keyAxis = mKeyAxis.data();
17239 QCPAxis *valueAxis = mValueAxis.data();
17240 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
17242 // add margins to rect to compensate for stroke width
17243 double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
17244 if (!mScatterStyle.isNone())
17245 strokeMargin = qMax(strokeMargin, mScatterStyle.size());
17246 double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17247 double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
17248 double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17249 double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
17251 QCPCurveDataMap::const_iterator it = mData->constBegin();
17252 QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
17253 int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
17254 QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
17255 while (it != mData->constEnd())
17257 currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17258 if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
17260 if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
17262 QPointF crossA, crossB;
17263 if (prevRegion == 5) // we're coming from R, so add this point optimized
17265 lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
17266 // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
17267 *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17268 } else if (mayTraverse(prevRegion, currentRegion) &&
17269 getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
17271 // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
17272 QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
17273 getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
17274 if (it != mData->constBegin())
17276 *lineData << beforeTraverseCornerPoints;
17277 lineData->append(crossA);
17278 lineData->append(crossB);
17279 *lineData << afterTraverseCornerPoints;
17282 lineData->append(crossB);
17283 *lineData << afterTraverseCornerPoints;
17284 trailingPoints << beforeTraverseCornerPoints << crossA ;
17286 } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
17288 *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17290 } else // segment does end in R, so we add previous point optimized and this point at original position
17292 if (it == mData->constBegin()) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
17293 trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
17295 lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
17296 lineData->append(coordsToPixels(it.value().key, it.value().value));
17298 } else // region didn't change
17300 if (currentRegion == 5) // still in R, keep adding original points
17302 lineData->append(coordsToPixels(it.value().key, it.value().value));
17303 } else // still outside R, no need to add anything
17305 // see how this is not doing anything? That's the main optimization...
17309 prevRegion = currentRegion;
17312 *lineData << trailingPoints;
17317 This function is part of the curve optimization algorithm of \ref getCurveData.
17319 It returns the region of the given point (\a x, \a y) with respect to a rectangle defined by \a
17320 rectLeft, \a rectTop, \a rectRight, and \a rectBottom.
17322 The regions are enumerated from top to bottom and left to right:
17324 <table style="width:10em; text-align:center">
17325 <tr><td>1</td><td>4</td><td>7</td></tr>
17326 <tr><td>2</td><td style="border:1px solid black">5</td><td>8</td></tr>
17327 <tr><td>3</td><td>6</td><td>9</td></tr>
17330 With the rectangle being region 5, and the outer regions extending infinitely outwards. In the
17331 curve optimization algorithm, region 5 is considered to be the visible portion of the plot.
17333 int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17335 if (x < rectLeft) // region 123
17339 else if (y < rectBottom)
17343 } else if (x > rectRight) // region 789
17347 else if (y < rectBottom)
17351 } else // region 456
17355 else if (y < rectBottom)
17364 This function is part of the curve optimization algorithm of \ref getCurveData.
17366 This method is used in case the current segment passes from inside the visible rect (region 5,
17367 see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by
17368 the line connecting (\a key, \a value) with (\a otherKey, \a otherValue).
17370 It returns the intersection point of the segment with the border of region 5.
17372 For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or
17373 whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or
17374 leaving it. It is important though that \a otherRegion correctly identifies the other region not
17377 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17379 double intersectKey = rectLeft; // initial value is just fail-safe
17380 double intersectValue = rectTop; // initial value is just fail-safe
17381 switch (otherRegion)
17383 case 1: // top and left edge
17385 intersectValue = rectTop;
17386 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17387 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17389 intersectKey = rectLeft;
17390 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17394 case 2: // left edge
17396 intersectKey = rectLeft;
17397 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17400 case 3: // bottom and left edge
17402 intersectValue = rectBottom;
17403 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17404 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17406 intersectKey = rectLeft;
17407 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17411 case 4: // top edge
17413 intersectValue = rectTop;
17414 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17419 break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
17421 case 6: // bottom edge
17423 intersectValue = rectBottom;
17424 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17427 case 7: // top and right edge
17429 intersectValue = rectTop;
17430 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17431 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17433 intersectKey = rectRight;
17434 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17438 case 8: // right edge
17440 intersectKey = rectRight;
17441 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17444 case 9: // bottom and right edge
17446 intersectValue = rectBottom;
17447 intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
17448 if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
17450 intersectKey = rectRight;
17451 intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
17456 return coordsToPixels(intersectKey, intersectValue);
17461 This function is part of the curve optimization algorithm of \ref getCurveData.
17463 In situations where a single segment skips over multiple regions it might become necessary to add
17464 extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment
17465 doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts.
17466 This method provides these points that must be added, assuming the original segment doesn't
17467 start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by
17468 \ref getTraverseCornerPoints.)
17470 For example, consider a segment which directly goes from region 4 to 2 but originally is far out
17471 to the top left such that it doesn't cross region 5. Naively optimizing these points by
17472 projecting them on the top and left borders of region 5 will create a segment that surely crosses
17473 5, creating a visual artifact in the plot. This method prevents this by providing extra points at
17474 the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without
17477 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
17479 QVector<QPointF> result;
17480 switch (prevRegion)
17484 switch (currentRegion)
17486 case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
17487 case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
17488 case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
17489 case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
17490 case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17491 case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17492 case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17493 if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17494 { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17496 { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
17504 switch (currentRegion)
17506 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17507 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17508 case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17509 case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17510 case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17511 case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17517 switch (currentRegion)
17519 case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
17520 case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
17521 case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
17522 case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
17523 case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17524 case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17525 case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17526 if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17527 { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17529 { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
17537 switch (currentRegion)
17539 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17540 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17541 case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17542 case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17543 case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17544 case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
17550 switch (currentRegion)
17552 case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
17553 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17554 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17555 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17561 switch (currentRegion)
17563 case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
17564 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17565 case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17566 case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17567 case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17568 case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
17574 switch (currentRegion)
17576 case 4: { result << coordsToPixels(rectRight, rectTop); break; }
17577 case 8: { result << coordsToPixels(rectRight, rectTop); break; }
17578 case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
17579 case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
17580 case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
17581 case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17582 case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17583 if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
17584 { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17586 { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
17594 switch (currentRegion)
17596 case 7: { result << coordsToPixels(rectRight, rectTop); break; }
17597 case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
17598 case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17599 case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
17600 case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
17601 case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
17607 switch (currentRegion)
17609 case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
17610 case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
17611 case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
17612 case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
17613 case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
17614 case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
17615 case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
17616 if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
17617 { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17619 { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
17631 This function is part of the curve optimization algorithm of \ref getCurveData.
17633 This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref
17634 getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion
17635 nor \a currentRegion is 5 itself.
17637 If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the
17638 segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref
17641 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
17643 switch (prevRegion)
17647 switch (currentRegion)
17652 case 3: return false;
17653 default: return true;
17658 switch (currentRegion)
17661 case 3: return false;
17662 default: return true;
17667 switch (currentRegion)
17672 case 9: return false;
17673 default: return true;
17678 switch (currentRegion)
17681 case 7: return false;
17682 default: return true;
17685 case 5: return false; // should never occur
17688 switch (currentRegion)
17691 case 9: return false;
17692 default: return true;
17697 switch (currentRegion)
17702 case 9: return false;
17703 default: return true;
17708 switch (currentRegion)
17711 case 9: return false;
17712 default: return true;
17717 switch (currentRegion)
17722 case 7: return false;
17723 default: return true;
17726 default: return true;
17733 This function is part of the curve optimization algorithm of \ref getCurveData.
17735 This method assumes that the \ref mayTraverse test has returned true, so there is a chance the
17736 segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible
17739 The return value of this method indicates whether the segment actually traverses region 5 or not.
17741 If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and
17742 exit points of region 5. They will become the optimized points for that segment.
17744 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
17746 QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
17747 if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
17749 // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17750 intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
17751 intersections.append(QPointF(key, rectTop));
17752 } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
17754 // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
17755 intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
17756 intersections.append(QPointF(rectRight, value));
17757 } else // line is skewed
17760 double keyPerValue = (key-prevKey)/(value-prevValue);
17761 // check top of rect:
17762 gamma = prevKey + (rectTop-prevValue)*keyPerValue;
17763 if (gamma >= rectLeft && gamma <= rectRight)
17764 intersections.append(QPointF(gamma, rectTop));
17765 // check bottom of rect:
17766 gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
17767 if (gamma >= rectLeft && gamma <= rectRight)
17768 intersections.append(QPointF(gamma, rectBottom));
17769 double valuePerKey = 1.0/keyPerValue;
17770 // check left of rect:
17771 gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
17772 if (gamma >= rectBottom && gamma <= rectTop)
17773 intersections.append(QPointF(rectLeft, gamma));
17774 // check right of rect:
17775 gamma = prevValue + (rectRight-prevKey)*valuePerKey;
17776 if (gamma >= rectBottom && gamma <= rectTop)
17777 intersections.append(QPointF(rectRight, gamma));
17780 // handle cases where found points isn't exactly 2:
17781 if (intersections.size() > 2)
17783 // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
17784 double distSqrMax = 0;
17786 for (int i=0; i<intersections.size()-1; ++i)
17788 for (int k=i+1; k<intersections.size(); ++k)
17790 QPointF distPoint = intersections.at(i)-intersections.at(k);
17791 double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
17792 if (distSqr > distSqrMax)
17794 pv1 = intersections.at(i);
17795 pv2 = intersections.at(k);
17796 distSqrMax = distSqr;
17800 intersections = QList<QPointF>() << pv1 << pv2;
17801 } else if (intersections.size() != 2)
17803 // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
17807 // possibly re-sort points so optimized point segment has same direction as original segment:
17808 if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
17809 intersections.move(0, 1);
17810 crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
17811 crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
17817 This function is part of the curve optimization algorithm of \ref getCurveData.
17819 This method assumes that the \ref getTraverse test has returned true, so the segment definitely
17820 traverses the visible region 5 when going from \a prevRegion to \a currentRegion.
17822 In certain situations it is not sufficient to merely generate the entry and exit points of the
17823 segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in
17824 addition to traversing region 5, skips another region outside of region 5, which makes it
17825 necessary to add an optimized corner point there (very similar to the job \ref
17826 getOptimizedCornerPoints does for segments that are completely in outside regions and don't
17829 As an example, consider a segment going from region 1 to region 6, traversing the lower left
17830 corner of region 5. In this configuration, the segment additionally crosses the border between
17831 region 1 and 2 before entering region 5. This makes it necessary to add an additional point in
17832 the top left corner, before adding the optimized traverse points. So in this case, the output
17833 parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be
17836 In some cases, such as when going from region 1 to 9, it may even be necessary to add additional
17837 corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse
17838 return the respective corner points.
17840 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
17842 switch (prevRegion)
17846 switch (currentRegion)
17848 case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17849 case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17850 case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
17856 switch (currentRegion)
17858 case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17859 case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17865 switch (currentRegion)
17867 case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17868 case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17869 case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17875 switch (currentRegion)
17877 case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17878 case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
17882 case 5: { break; } // shouldn't happen because this method only handles full traverses
17885 switch (currentRegion)
17887 case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17888 case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
17894 switch (currentRegion)
17896 case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17897 case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17898 case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
17904 switch (currentRegion)
17906 case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17907 case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
17913 switch (currentRegion)
17915 case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17916 case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
17917 case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
17926 Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
17927 pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
17930 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
17932 if (mData->isEmpty())
17934 qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
17937 if (mData->size() == 1)
17939 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
17940 return QVector2D(dataPoint-pixelPoint).length();
17943 // calculate minimum distance to line segments:
17944 QVector<QPointF> *lineData = new QVector<QPointF>;
17945 getCurveData(lineData);
17946 double minDistSqr = std::numeric_limits<double>::max();
17947 for (int i=0; i<lineData->size()-1; ++i)
17949 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
17950 if (currentDistSqr < minDistSqr)
17951 minDistSqr = currentDistSqr;
17954 return qSqrt(minDistSqr);
17957 /* inherits documentation from base class */
17958 QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
17961 bool haveLower = false;
17962 bool haveUpper = false;
17966 QCPCurveDataMap::const_iterator it = mData->constBegin();
17967 while (it != mData->constEnd())
17969 current = it.value().key;
17970 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
17972 if (current < range.lower || !haveLower)
17974 range.lower = current;
17977 if (current > range.upper || !haveUpper)
17979 range.upper = current;
17986 foundRange = haveLower && haveUpper;
17990 /* inherits documentation from base class */
17991 QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
17994 bool haveLower = false;
17995 bool haveUpper = false;
17999 QCPCurveDataMap::const_iterator it = mData->constBegin();
18000 while (it != mData->constEnd())
18002 current = it.value().value;
18003 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
18005 if (current < range.lower || !haveLower)
18007 range.lower = current;
18010 if (current > range.upper || !haveUpper)
18012 range.upper = current;
18019 foundRange = haveLower && haveUpper;
18024 ////////////////////////////////////////////////////////////////////////////////////////////////////
18025 //////////////////// QCPBarsGroup
18026 ////////////////////////////////////////////////////////////////////////////////////////////////////
18028 /*! \class QCPBarsGroup
18029 \brief Groups multiple QCPBars together so they appear side by side
18031 \image html QCPBarsGroup.png
18033 When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable
18034 to have them appearing next to each other at each key. This is what adding the respective QCPBars
18035 plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each
18036 other, see \ref QCPBars::moveAbove.)
18038 \section qcpbarsgroup-usage Usage
18040 To add a QCPBars plottable to the group, create a new group and then add the respective bars
18043 QCPBarsGroup *group = new QCPBarsGroup(customPlot);
18044 group->append(bars1);
18045 group->append(bars2);
18047 Alternatively to appending to the group like shown above, you can also set the group on the
18048 QCPBars plottable via \ref QCPBars::setBarsGroup.
18050 The spacing between the bars can be configured via \ref setSpacingType and \ref setSpacing. The
18051 bars in this group appear in the plot in the order they were appended. To insert a bars plottable
18052 at a certain index position, or to reposition a bars plottable which is already in the group, use
18055 To remove specific bars from the group, use either \ref remove or call \ref
18056 QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable.
18058 To clear the entire group, call \ref clear, or simply delete the group.
18061 /* start of documentation of inline functions */
18063 /*! \fn QList<QCPBars*> QCPBarsGroup::bars() const
18065 Returns all bars currently in this group.
18067 \see bars(int index)
18070 /*! \fn int QCPBarsGroup::size() const
18072 Returns the number of QCPBars plottables that are part of this group.
18076 /*! \fn bool QCPBarsGroup::isEmpty() const
18078 Returns whether this bars group is empty.
18083 /*! \fn bool QCPBarsGroup::contains(QCPBars *bars)
18085 Returns whether the specified \a bars plottable is part of this group.
18089 /* end of documentation of inline functions */
18092 Constructs a new bars group for the specified QCustomPlot instance.
18094 QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :
18095 QObject(parentPlot),
18096 mParentPlot(parentPlot),
18097 mSpacingType(stAbsolute),
18102 QCPBarsGroup::~QCPBarsGroup()
18108 Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType.
18110 The actual spacing can then be specified with \ref setSpacing.
18114 void QCPBarsGroup::setSpacingType(SpacingType spacingType)
18116 mSpacingType = spacingType;
18120 Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is
18121 defined by the current \ref SpacingType, which can be set with \ref setSpacingType.
18123 \see setSpacingType
18125 void QCPBarsGroup::setSpacing(double spacing)
18127 mSpacing = spacing;
18131 Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars
18136 QCPBars *QCPBarsGroup::bars(int index) const
18138 if (index >= 0 && index < mBars.size())
18140 return mBars.at(index);
18143 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
18149 Removes all QCPBars plottables from this group.
18153 void QCPBarsGroup::clear()
18155 foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
18156 bars->setBarsGroup(0); // removes itself via removeBars
18160 Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref
18161 QCPBars::setBarsGroup on the \a bars instance.
18163 \see insert, remove
18165 void QCPBarsGroup::append(QCPBars *bars)
18169 qDebug() << Q_FUNC_INFO << "bars is 0";
18173 if (!mBars.contains(bars))
18174 bars->setBarsGroup(this);
18176 qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
18180 Inserts the specified \a bars plottable into this group at the specified index position \a i.
18181 This gives you full control over the ordering of the bars.
18183 \a bars may already be part of this group. In that case, \a bars is just moved to the new index
18186 \see append, remove
18188 void QCPBarsGroup::insert(int i, QCPBars *bars)
18192 qDebug() << Q_FUNC_INFO << "bars is 0";
18196 // first append to bars list normally:
18197 if (!mBars.contains(bars))
18198 bars->setBarsGroup(this);
18199 // then move to according position:
18200 mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
18204 Removes the specified \a bars plottable from this group.
18206 \see contains, clear
18208 void QCPBarsGroup::remove(QCPBars *bars)
18212 qDebug() << Q_FUNC_INFO << "bars is 0";
18216 if (mBars.contains(bars))
18217 bars->setBarsGroup(0);
18219 qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
18224 Adds the specified \a bars to the internal mBars list of bars. This method does not change the
18225 barsGroup property on \a bars.
18227 \see unregisterBars
18229 void QCPBarsGroup::registerBars(QCPBars *bars)
18231 if (!mBars.contains(bars))
18232 mBars.append(bars);
18237 Removes the specified \a bars from the internal mBars list of bars. This method does not change
18238 the barsGroup property on \a bars.
18242 void QCPBarsGroup::unregisterBars(QCPBars *bars)
18244 mBars.removeOne(bars);
18249 Returns the pixel offset in the key dimension the specified \a bars plottable should have at the
18250 given key coordinate \a keyCoord. The offset is relative to the pixel position of the key
18251 coordinate \a keyCoord.
18253 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
18255 // find list of all base bars in case some mBars are stacked:
18256 QList<const QCPBars*> baseBars;
18257 foreach (const QCPBars *b, mBars)
18259 while (b->barBelow())
18261 if (!baseBars.contains(b))
18262 baseBars.append(b);
18264 // find base bar this "bars" is stacked on:
18265 const QCPBars *thisBase = bars;
18266 while (thisBase->barBelow())
18267 thisBase = thisBase->barBelow();
18269 // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
18271 int index = baseBars.indexOf(thisBase);
18275 double lowerPixelWidth, upperPixelWidth;
18276 if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
18279 } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
18281 if (baseBars.size() % 2 == 0) // even number of bars
18283 startIndex = baseBars.size()/2-1;
18284 result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18285 } else // uneven number of bars
18287 startIndex = (baseBars.size()-1)/2-1;
18288 baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18289 result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18290 result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18292 for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
18294 baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18295 result -= qAbs(upperPixelWidth-lowerPixelWidth);
18296 result -= getPixelSpacing(baseBars.at(i), keyCoord);
18298 // finally half of our bars width:
18299 baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18300 result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18301 } else // bar is to the right of center
18303 if (baseBars.size() % 2 == 0) // even number of bars
18305 startIndex = baseBars.size()/2;
18306 result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
18307 } else // uneven number of bars
18309 startIndex = (baseBars.size()-1)/2+1;
18310 baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18311 result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
18312 result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
18314 for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
18316 baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18317 result += qAbs(upperPixelWidth-lowerPixelWidth);
18318 result += getPixelSpacing(baseBars.at(i), keyCoord);
18320 // finally half of our bars width:
18321 baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
18322 result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
18330 Returns the spacing in pixels which is between this \a bars and the following one, both at the
18331 key coordinate \a keyCoord.
18333 \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only
18334 needed to get acces to the key axis transformation and axis rect for the modes \ref
18335 stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in
18336 \ref stPlotCoords on a logarithmic axis.
18338 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
18340 switch (mSpacingType)
18346 case stAxisRectRatio:
18348 if (bars->keyAxis()->orientation() == Qt::Horizontal)
18349 return bars->keyAxis()->axisRect()->width()*mSpacing;
18351 return bars->keyAxis()->axisRect()->height()*mSpacing;
18355 double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
18356 return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
18363 ////////////////////////////////////////////////////////////////////////////////////////////////////
18364 //////////////////// QCPBarData
18365 ////////////////////////////////////////////////////////////////////////////////////////////////////
18367 /*! \class QCPBarData
18368 \brief Holds the data of one single data point (one bar) for QCPBars.
18370 The container for storing multiple data points is \ref QCPBarDataMap.
18372 The stored data is:
18373 \li \a key: coordinate on the key axis of this bar
18374 \li \a value: height coordinate on the value axis of this bar
18376 \see QCPBarDataaMap
18380 Constructs a bar data point with key and value set to zero.
18382 QCPBarData::QCPBarData() :
18389 Constructs a bar data point with the specified \a key and \a value.
18391 QCPBarData::QCPBarData(double key, double value) :
18398 ////////////////////////////////////////////////////////////////////////////////////////////////////
18399 //////////////////// QCPBars
18400 ////////////////////////////////////////////////////////////////////////////////////////////////////
18403 \brief A plottable representing a bar chart in a plot.
18405 \image html QCPBars.png
18407 To plot data, assign it with the \ref setData or \ref addData functions.
18409 \section appearance Changing the appearance
18411 The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
18412 The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth.
18414 Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other
18415 (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear
18418 If you would like to group multiple QCPBars plottables together so they appear side by side as
18419 shown below, use QCPBarsGroup.
18421 \image html QCPBarsGroup.png
18423 \section usage Usage
18425 Like all data representing objects in QCustomPlot, the QCPBars is a plottable
18426 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
18427 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
18429 Usually, you first create an instance:
18431 QCPBars *newBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);\endcode
18432 add it to the customPlot with QCustomPlot::addPlottable:
18434 customPlot->addPlottable(newBars);\endcode
18435 and then modify the properties of the newly created plottable, e.g.:
18437 newBars->setName("Country population");
18438 newBars->setData(xData, yData);\endcode
18441 /* start of documentation of inline functions */
18443 /*! \fn QCPBars *QCPBars::barBelow() const
18444 Returns the bars plottable that is directly below this bars plottable.
18445 If there is no such plottable, returns 0.
18447 \see barAbove, moveBelow, moveAbove
18450 /*! \fn QCPBars *QCPBars::barAbove() const
18451 Returns the bars plottable that is directly above this bars plottable.
18452 If there is no such plottable, returns 0.
18454 \see barBelow, moveBelow, moveAbove
18457 /* end of documentation of inline functions */
18460 Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
18461 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
18462 the same orientation. If either of these restrictions is violated, a corresponding message is
18463 printed to the debug output (qDebug), the construction is not aborted, though.
18465 The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
18466 then takes ownership of the bar chart.
18468 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
18469 QCPAbstractPlottable(keyAxis, valueAxis),
18470 mData(new QCPBarDataMap),
18472 mWidthType(wtPlotCoords),
18476 // modify inherited properties from abstract plottable:
18477 mPen.setColor(Qt::blue);
18478 mPen.setStyle(Qt::SolidLine);
18479 mBrush.setColor(QColor(40, 50, 255, 30));
18480 mBrush.setStyle(Qt::SolidPattern);
18481 mSelectedPen = mPen;
18482 mSelectedPen.setWidthF(2.5);
18483 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
18484 mSelectedBrush = mBrush;
18487 QCPBars::~QCPBars()
18490 if (mBarBelow || mBarAbove)
18491 connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
18496 Sets the width of the bars.
18498 How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...),
18499 depends on the currently set width type, see \ref setWidthType and \ref WidthType.
18501 void QCPBars::setWidth(double width)
18507 Sets how the width of the bars is defined. See the documentation of \ref WidthType for an
18508 explanation of the possible values for \a widthType.
18510 The default value is \ref wtPlotCoords.
18514 void QCPBars::setWidthType(QCPBars::WidthType widthType)
18516 mWidthType = widthType;
18520 Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref
18521 QCPBarsGroup::append.
18523 To remove this QCPBars from any group, set \a barsGroup to 0.
18525 void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
18527 // deregister at old group:
18529 mBarsGroup->unregisterBars(this);
18530 mBarsGroup = barsGroup;
18531 // register at new group:
18533 mBarsGroup->registerBars(this);
18537 Sets the base value of this bars plottable.
18539 The base value defines where on the value coordinate the bars start. How far the bars extend from
18540 the base value is given by their individual value data. For example, if the base value is set to
18541 1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at
18544 For stacked bars, only the base value of the bottom-most QCPBars has meaning.
18546 The default base value is 0.
18548 void QCPBars::setBaseValue(double baseValue)
18550 mBaseValue = baseValue;
18554 Replaces the current data with the provided \a data.
18556 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
18557 takes ownership of the passed data and replaces the internal data pointer with it. This is
18558 significantly faster than copying for large datasets.
18560 void QCPBars::setData(QCPBarDataMap *data, bool copy)
18564 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
18579 Replaces the current data with the provided points in \a key and \a value tuples. The
18580 provided vectors should have equal length. Else, the number of added points will be the size of
18581 the smallest vector.
18583 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
18586 int n = key.size();
18587 n = qMin(n, value.size());
18588 QCPBarData newData;
18589 for (int i=0; i<n; ++i)
18591 newData.key = key[i];
18592 newData.value = value[i];
18593 mData->insertMulti(newData.key, newData);
18598 Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
18599 below the bars of \a bars. The move target \a bars must use the same key and value axis as this
18602 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18603 has a bars object below itself, this bars object is inserted between the two. If this bars object
18604 is already between two other bars, the two other bars will be stacked on top of each other after
18607 To remove this bars plottable from any stacking, set \a bars to 0.
18609 \see moveBelow, barAbove, barBelow
18611 void QCPBars::moveBelow(QCPBars *bars)
18613 if (bars == this) return;
18614 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18616 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18619 // remove from stacking:
18620 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18621 // if new bar given, insert this bar below it:
18624 if (bars->mBarBelow)
18625 connectBars(bars->mBarBelow.data(), this);
18626 connectBars(this, bars);
18631 Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
18632 above the bars of \a bars. The move target \a bars must use the same key and value axis as this
18635 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
18636 has a bars object below itself, this bars object is inserted between the two. If this bars object
18637 is already between two other bars, the two other bars will be stacked on top of each other after
18640 To remove this bars plottable from any stacking, set \a bars to 0.
18642 \see moveBelow, barBelow, barAbove
18644 void QCPBars::moveAbove(QCPBars *bars)
18646 if (bars == this) return;
18647 if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
18649 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
18652 // remove from stacking:
18653 connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
18654 // if new bar given, insert this bar above it:
18657 if (bars->mBarAbove)
18658 connectBars(this, bars->mBarAbove.data());
18659 connectBars(bars, this);
18664 Adds the provided data points in \a dataMap to the current data.
18667 void QCPBars::addData(const QCPBarDataMap &dataMap)
18669 mData->unite(dataMap);
18673 Adds the provided single data point in \a data to the current data.
18676 void QCPBars::addData(const QCPBarData &data)
18678 mData->insertMulti(data.key, data);
18682 Adds the provided single data point as \a key and \a value tuple to the current data
18685 void QCPBars::addData(double key, double value)
18687 QCPBarData newData;
18689 newData.value = value;
18690 mData->insertMulti(newData.key, newData);
18694 Adds the provided data points as \a key and \a value tuples to the current data.
18697 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
18699 int n = keys.size();
18700 n = qMin(n, values.size());
18701 QCPBarData newData;
18702 for (int i=0; i<n; ++i)
18704 newData.key = keys[i];
18705 newData.value = values[i];
18706 mData->insertMulti(newData.key, newData);
18711 Removes all data points with key smaller than \a key.
18712 \see addData, clearData
18714 void QCPBars::removeDataBefore(double key)
18716 QCPBarDataMap::iterator it = mData->begin();
18717 while (it != mData->end() && it.key() < key)
18718 it = mData->erase(it);
18722 Removes all data points with key greater than \a key.
18723 \see addData, clearData
18725 void QCPBars::removeDataAfter(double key)
18727 if (mData->isEmpty()) return;
18728 QCPBarDataMap::iterator it = mData->upperBound(key);
18729 while (it != mData->end())
18730 it = mData->erase(it);
18734 Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
18735 greater or equal to \a toKey, the function does nothing. To remove a single data point with known
18736 key, use \ref removeData(double key).
18738 \see addData, clearData
18740 void QCPBars::removeData(double fromKey, double toKey)
18742 if (fromKey >= toKey || mData->isEmpty()) return;
18743 QCPBarDataMap::iterator it = mData->upperBound(fromKey);
18744 QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
18745 while (it != itEnd)
18746 it = mData->erase(it);
18751 Removes a single data point at \a key. If the position is not known with absolute precision,
18752 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
18753 around the suspected position, depeding on the precision with which the key is known.
18755 \see addData, clearData
18757 void QCPBars::removeData(double key)
18759 mData->remove(key);
18763 Removes all data points.
18764 \see removeData, removeDataAfter, removeDataBefore
18766 void QCPBars::clearData()
18771 /* inherits documentation from base class */
18772 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18775 if (onlySelectable && !mSelectable)
18777 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
18779 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
18781 QCPBarDataMap::ConstIterator it;
18782 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
18784 if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
18785 return mParentPlot->selectionTolerance()*0.99;
18791 /* inherits documentation from base class */
18792 void QCPBars::draw(QCPPainter *painter)
18794 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
18795 if (mData->isEmpty()) return;
18797 QCPBarDataMap::const_iterator it, lower, upperEnd;
18798 getVisibleDataBounds(lower, upperEnd);
18799 for (it = lower; it != upperEnd; ++it)
18801 // check data validity if flag set:
18802 #ifdef QCUSTOMPLOT_CHECK_DATA
18803 if (QCP::isInvalidData(it.value().key, it.value().value))
18804 qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
18806 QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
18808 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
18810 applyFillAntialiasingHint(painter);
18811 painter->setPen(Qt::NoPen);
18812 painter->setBrush(mainBrush());
18813 painter->drawPolygon(barPolygon);
18816 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
18818 applyDefaultAntialiasingHint(painter);
18819 painter->setPen(mainPen());
18820 painter->setBrush(Qt::NoBrush);
18821 painter->drawPolyline(barPolygon);
18826 /* inherits documentation from base class */
18827 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
18829 // draw filled rect:
18830 applyDefaultAntialiasingHint(painter);
18831 painter->setBrush(mBrush);
18832 painter->setPen(mPen);
18833 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
18834 r.moveCenter(rect.center());
18835 painter->drawRect(r);
18840 called by \ref draw to determine which data (key) range is visible at the current key axis range
18841 setting, so only that needs to be processed. It also takes into account the bar width.
18843 \a lower returns an iterator to the lowest data point that needs to be taken into account when
18844 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
18845 lower may still be just outside the visible range.
18847 \a upperEnd returns an iterator one higher than the highest visible data point. Same as before, \a
18848 upperEnd may also lie just outside of the visible range.
18850 if the bars plottable contains no data, both \a lower and \a upperEnd point to constEnd.
18852 void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
18854 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
18855 if (mData->isEmpty())
18857 lower = mData->constEnd();
18858 upperEnd = mData->constEnd();
18862 // get visible data range as QMap iterators
18863 lower = mData->lowerBound(mKeyAxis.data()->range().lower);
18864 upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
18865 double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
18866 double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
18867 bool isVisible = false;
18868 // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
18869 QCPBarDataMap::const_iterator it = lower;
18870 while (it != mData->constBegin())
18873 QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
18874 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18875 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
18876 else // keyaxis is vertical
18877 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
18883 // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
18885 while (it != mData->constEnd())
18887 QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
18888 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18889 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
18890 else // keyaxis is vertical
18891 isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
18902 Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
18903 and shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref
18906 QPolygonF QCPBars::getBarPolygon(double key, double value) const
18908 QCPAxis *keyAxis = mKeyAxis.data();
18909 QCPAxis *valueAxis = mValueAxis.data();
18910 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
18913 double lowerPixelWidth, upperPixelWidth;
18914 getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
18915 double base = getStackedBaseValue(key, value >= 0);
18916 double basePixel = valueAxis->coordToPixel(base);
18917 double valuePixel = valueAxis->coordToPixel(base+value);
18918 double keyPixel = keyAxis->coordToPixel(key);
18920 keyPixel += mBarsGroup->keyPixelOffset(this, key);
18921 if (keyAxis->orientation() == Qt::Horizontal)
18923 result << QPointF(keyPixel+lowerPixelWidth, basePixel);
18924 result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
18925 result << QPointF(keyPixel+upperPixelWidth, valuePixel);
18926 result << QPointF(keyPixel+upperPixelWidth, basePixel);
18929 result << QPointF(basePixel, keyPixel+lowerPixelWidth);
18930 result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
18931 result << QPointF(valuePixel, keyPixel+upperPixelWidth);
18932 result << QPointF(basePixel, keyPixel+upperPixelWidth);
18939 This function is used to determine the width of the bar at coordinate \a key, according to the
18940 specified width (\ref setWidth) and width type (\ref setWidthType).
18942 The output parameters \a lower and \a upper return the number of pixels the bar extends to lower
18943 and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a
18944 lower is negative and \a upper positive).
18946 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
18948 switch (mWidthType)
18952 upper = mWidth*0.5;
18954 if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18955 qSwap(lower, upper);
18958 case wtAxisRectRatio:
18960 if (mKeyAxis && mKeyAxis.data()->axisRect())
18962 if (mKeyAxis.data()->orientation() == Qt::Horizontal)
18963 upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
18965 upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
18967 if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
18968 qSwap(lower, upper);
18970 qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
18977 double keyPixel = mKeyAxis.data()->coordToPixel(key);
18978 upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
18979 lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
18980 // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
18981 // coordinate transform which includes range direction
18983 qDebug() << Q_FUNC_INFO << "No key axis defined";
18991 This function is called to find at which value to start drawing the base of a bar at \a key, when
18992 it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
18994 positive and negative bars are separated per stack (positive are stacked above baseValue upwards,
18995 negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the
18996 bar for which we need the base value is negative, set \a positive to false.
18998 double QCPBars::getStackedBaseValue(double key, bool positive) const
19002 double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
19003 // find bars of mBarBelow that are approximately at key and find largest one:
19004 double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
19007 QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
19008 QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
19009 while (it != itEnd)
19011 if ((positive && it.value().value > max) ||
19012 (!positive && it.value().value < max))
19013 max = it.value().value;
19016 // recurse down the bar-stack to find the total height:
19017 return max + mBarBelow.data()->getStackedBaseValue(key, positive);
19024 Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s)
19025 currently above lower and below upper will become disconnected to lower/upper.
19027 If lower is zero, upper will be disconnected at the bottom.
19028 If upper is zero, lower will be disconnected at the top.
19030 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
19032 if (!lower && !upper) return;
19034 if (!lower) // disconnect upper at bottom
19036 // disconnect old bar below upper:
19037 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19038 upper->mBarBelow.data()->mBarAbove = 0;
19039 upper->mBarBelow = 0;
19040 } else if (!upper) // disconnect lower at top
19042 // disconnect old bar above lower:
19043 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19044 lower->mBarAbove.data()->mBarBelow = 0;
19045 lower->mBarAbove = 0;
19046 } else // connect lower and upper
19048 // disconnect old bar above lower:
19049 if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
19050 lower->mBarAbove.data()->mBarBelow = 0;
19051 // disconnect old bar below upper:
19052 if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
19053 upper->mBarBelow.data()->mBarAbove = 0;
19054 lower->mBarAbove = upper;
19055 upper->mBarBelow = lower;
19059 /* inherits documentation from base class */
19060 QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19063 bool haveLower = false;
19064 bool haveUpper = false;
19067 QCPBarDataMap::const_iterator it = mData->constBegin();
19068 while (it != mData->constEnd())
19070 current = it.value().key;
19071 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19073 if (current < range.lower || !haveLower)
19075 range.lower = current;
19078 if (current > range.upper || !haveUpper)
19080 range.upper = current;
19086 // determine exact range of bars by including bar width and barsgroup offset:
19087 if (haveLower && mKeyAxis)
19089 double lowerPixelWidth, upperPixelWidth, keyPixel;
19090 getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
19091 keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
19093 keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
19094 range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
19096 if (haveUpper && mKeyAxis)
19098 double lowerPixelWidth, upperPixelWidth, keyPixel;
19099 getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
19100 keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
19102 keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
19103 range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
19105 foundRange = haveLower && haveUpper;
19109 /* inherits documentation from base class */
19110 QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19113 range.lower = mBaseValue;
19114 range.upper = mBaseValue;
19115 bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
19116 bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
19119 QCPBarDataMap::const_iterator it = mData->constBegin();
19120 while (it != mData->constEnd())
19122 current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
19123 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
19125 if (current < range.lower || !haveLower)
19127 range.lower = current;
19130 if (current > range.upper || !haveUpper)
19132 range.upper = current;
19139 foundRange = true; // return true because bar charts always have the 0-line visible
19144 ////////////////////////////////////////////////////////////////////////////////////////////////////
19145 //////////////////// QCPStatisticalBox
19146 ////////////////////////////////////////////////////////////////////////////////////////////////////
19148 /*! \class QCPStatisticalBox
19149 \brief A plottable representing a single statistical box in a plot.
19151 \image html QCPStatisticalBox.png
19153 To plot data, assign it with the individual parameter functions or use \ref setData to set all
19154 parameters at once. The individual functions are:
19155 \li \ref setMinimum
19156 \li \ref setLowerQuartile
19158 \li \ref setUpperQuartile
19159 \li \ref setMaximum
19161 Additionally you can define a list of outliers, drawn as scatter datapoints:
19162 \li \ref setOutliers
19164 \section appearance Changing the appearance
19166 The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You may change
19167 the width of the box with \ref setWidth in plot coordinates (not pixels).
19169 Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
19170 setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
19171 (maximum) and bottom (minimum).
19173 The median indicator line has its own pen, \ref setMedianPen.
19175 If the whisker backbone pen is changed, make sure to set the capStyle to Qt::FlatCap. Else, the
19176 backbone line might exceed the whisker bars by a few pixels due to the pen cap being not
19179 The Outlier data points are drawn as normal scatter points. Their look can be controlled with
19180 \ref setOutlierStyle
19182 \section usage Usage
19184 Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable
19185 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
19186 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
19188 Usually, you first create an instance:
19190 QCPStatisticalBox *newBox = new QCPStatisticalBox(customPlot->xAxis, customPlot->yAxis);\endcode
19191 add it to the customPlot with QCustomPlot::addPlottable:
19193 customPlot->addPlottable(newBox);\endcode
19194 and then modify the properties of the newly created plottable, e.g.:
19196 newBox->setName("Measurement Series 1");
19197 newBox->setData(1, 3, 4, 5, 7);
19198 newBox->setOutliers(QVector<double>() << 0.5 << 0.64 << 7.2 << 7.42);\endcode
19202 Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
19203 value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
19204 not have the same orientation. If either of these restrictions is violated, a corresponding
19205 message is printed to the debug output (qDebug), the construction is not aborted, though.
19207 The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
19208 QCustomPlot then takes ownership of the statistical box.
19210 QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
19211 QCPAbstractPlottable(keyAxis, valueAxis),
19219 setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
19220 setWhiskerWidth(0.2);
19223 setPen(QPen(Qt::black));
19224 setSelectedPen(QPen(Qt::blue, 2.5));
19225 setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
19226 setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
19227 setWhiskerBarPen(QPen(Qt::black));
19228 setBrush(Qt::NoBrush);
19229 setSelectedBrush(Qt::NoBrush);
19233 Sets the key coordinate of the statistical box.
19235 void QCPStatisticalBox::setKey(double key)
19241 Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
19242 whisker, typically the minimum measurement of the sample that's not considered an outlier.
19244 \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19246 void QCPStatisticalBox::setMinimum(double value)
19252 Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
19253 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19254 sample, they contain 50% of the sample data.
19256 \see setUpperQuartile, setPen, setBrush, setWidth
19258 void QCPStatisticalBox::setLowerQuartile(double value)
19260 mLowerQuartile = value;
19264 Sets the parameter "median" of the statistical box plot. This is the value of the median mark
19265 inside the quartile box. The median separates the sample data in half (50% of the sample data is
19266 below/above the median).
19270 void QCPStatisticalBox::setMedian(double value)
19276 Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
19277 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
19278 sample, they contain 50% of the sample data.
19280 \see setLowerQuartile, setPen, setBrush, setWidth
19282 void QCPStatisticalBox::setUpperQuartile(double value)
19284 mUpperQuartile = value;
19288 Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
19289 whisker, typically the maximum measurement of the sample that's not considered an outlier.
19291 \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
19293 void QCPStatisticalBox::setMaximum(double value)
19299 Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
19300 that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
19301 and displayed as such.
19303 \see setOutlierStyle
19305 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
19307 mOutliers = values;
19311 Sets all parameters of the statistical box plot at once.
19313 \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
19315 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
19318 setMinimum(minimum);
19319 setLowerQuartile(lowerQuartile);
19321 setUpperQuartile(upperQuartile);
19322 setMaximum(maximum);
19326 Sets the width of the box in key coordinates.
19328 \see setWhiskerWidth
19330 void QCPStatisticalBox::setWidth(double width)
19336 Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
19340 void QCPStatisticalBox::setWhiskerWidth(double width)
19342 mWhiskerWidth = width;
19346 Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
19348 Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker backbone from reaching
19349 a few pixels past the whisker bars, when using a non-zero pen width.
19351 \see setWhiskerBarPen
19353 void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
19359 Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
19360 each end of the whisker backbone).
19364 void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
19366 mWhiskerBarPen = pen;
19370 Sets the pen used for drawing the median indicator line inside the statistical box.
19372 void QCPStatisticalBox::setMedianPen(const QPen &pen)
19378 Sets the appearance of the outlier data points.
19382 void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)
19384 mOutlierStyle = style;
19387 /* inherits documentation from base class */
19388 void QCPStatisticalBox::clearData()
19390 setOutliers(QVector<double>());
19393 setLowerQuartile(0);
19395 setUpperQuartile(0);
19399 /* inherits documentation from base class */
19400 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19403 if (onlySelectable && !mSelectable)
19405 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
19407 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
19409 double posKey, posValue;
19410 pixelsToCoords(pos, posKey, posValue);
19412 QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19413 QCPRange valueRange(mLowerQuartile, mUpperQuartile);
19414 if (keyRange.contains(posKey) && valueRange.contains(posValue))
19415 return mParentPlot->selectionTolerance()*0.99;
19417 // min/max whiskers:
19418 if (QCPRange(mMinimum, mMaximum).contains(posValue))
19419 return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
19424 /* inherits documentation from base class */
19425 void QCPStatisticalBox::draw(QCPPainter *painter)
19427 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
19429 // check data validity if flag set:
19430 #ifdef QCUSTOMPLOT_CHECK_DATA
19431 if (QCP::isInvalidData(mKey, mMedian) ||
19432 QCP::isInvalidData(mLowerQuartile, mUpperQuartile) ||
19433 QCP::isInvalidData(mMinimum, mMaximum))
19434 qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
19435 for (int i=0; i<mOutliers.size(); ++i)
19436 if (QCP::isInvalidData(mOutliers.at(i)))
19437 qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
19440 QRectF quartileBox;
19441 drawQuartileBox(painter, &quartileBox);
19444 painter->setClipRect(quartileBox, Qt::IntersectClip);
19445 drawMedian(painter);
19446 painter->restore();
19448 drawWhiskers(painter);
19449 drawOutliers(painter);
19452 /* inherits documentation from base class */
19453 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
19455 // draw filled rect:
19456 applyDefaultAntialiasingHint(painter);
19457 painter->setPen(mPen);
19458 painter->setBrush(mBrush);
19459 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
19460 r.moveCenter(rect.center());
19461 painter->drawRect(r);
19466 Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
19467 coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
19468 the median doesn't draw outside the quartile box).
19470 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
19473 box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
19474 box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
19475 applyDefaultAntialiasingHint(painter);
19476 painter->setPen(mainPen());
19477 painter->setBrush(mainBrush());
19478 painter->drawRect(box);
19480 *quartileBox = box;
19485 Draws the median line inside the quartile box.
19487 void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
19490 medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
19491 medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
19492 applyDefaultAntialiasingHint(painter);
19493 painter->setPen(mMedianPen);
19494 painter->drawLine(medianLine);
19499 Draws both whisker backbones and bars.
19501 void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
19503 QLineF backboneMin, backboneMax, barMin, barMax;
19504 backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
19505 backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
19506 barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
19507 barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
19508 applyErrorBarsAntialiasingHint(painter);
19509 painter->setPen(mWhiskerPen);
19510 painter->drawLine(backboneMin);
19511 painter->drawLine(backboneMax);
19512 painter->setPen(mWhiskerBarPen);
19513 painter->drawLine(barMin);
19514 painter->drawLine(barMax);
19519 Draws the outlier scatter points.
19521 void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
19523 applyScattersAntialiasingHint(painter);
19524 mOutlierStyle.applyTo(painter, mPen);
19525 for (int i=0; i<mOutliers.size(); ++i)
19526 mOutlierStyle.drawShape(painter, coordsToPixels(mKey, mOutliers.at(i)));
19529 /* inherits documentation from base class */
19530 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
19533 if (inSignDomain == sdBoth)
19535 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19536 } else if (inSignDomain == sdNegative)
19538 if (mKey+mWidth*0.5 < 0)
19539 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19541 return QCPRange(mKey-mWidth*0.5, mKey);
19544 foundRange = false;
19547 } else if (inSignDomain == sdPositive)
19549 if (mKey-mWidth*0.5 > 0)
19550 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
19552 return QCPRange(mKey, mKey+mWidth*0.5);
19555 foundRange = false;
19559 foundRange = false;
19563 /* inherits documentation from base class */
19564 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
19566 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
19567 values.reserve(mOutliers.size() + 5);
19568 values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
19569 values << mOutliers;
19570 // go through values and find the ones in legal range:
19571 bool haveUpper = false;
19572 bool haveLower = false;
19575 for (int i=0; i<values.size(); ++i)
19577 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
19578 (inSignDomain == sdPositive && values.at(i) > 0) ||
19579 (inSignDomain == sdBoth))
19581 if (values.at(i) > upper || !haveUpper)
19583 upper = values.at(i);
19586 if (values.at(i) < lower || !haveLower)
19588 lower = values.at(i);
19593 // return the bounds if we found some sensible values:
19594 if (haveLower && haveUpper)
19597 return QCPRange(lower, upper);
19598 } else // might happen if all values are in other sign domain
19600 foundRange = false;
19606 ////////////////////////////////////////////////////////////////////////////////////////////////////
19607 //////////////////// QCPColorMapData
19608 ////////////////////////////////////////////////////////////////////////////////////////////////////
19610 /*! \class QCPColorMapData
19611 \brief Holds the two-dimensional data of a QCPColorMap plottable.
19613 This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref
19614 QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a
19615 color, depending on the value.
19617 The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize).
19618 Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref
19619 setKeyRange, \ref setValueRange).
19621 The data cells can be accessed in two ways: They can be directly addressed by an integer index
19622 with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot
19623 coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are
19624 provided by the functions \ref coordToCell and \ref cellToCoord.
19626 This class also buffers the minimum and maximum values that are in the data set, to provide
19627 QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value
19628 that is greater than the current maximum increases this maximum to the new value. However,
19629 setting the cell that currently holds the maximum value to a smaller value doesn't decrease the
19630 maximum again, because finding the true new maximum would require going through the entire data
19631 array, which might be time consuming. The same holds for the data minimum. This functionality is
19632 given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the
19633 true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience
19634 parameter \a recalculateDataBounds which may be set to true to automatically call \ref
19635 recalculateDataBounds internally.
19638 /* start of documentation of inline functions */
19640 /*! \fn bool QCPColorMapData::isEmpty() const
19642 Returns whether this instance carries no data. This is equivalent to having a size where at least
19643 one of the dimensions is 0 (see \ref setSize).
19646 /* end of documentation of inline functions */
19649 Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction
19650 and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap
19651 at the coordinates \a keyRange and \a valueRange.
19653 \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
19655 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
19658 mKeyRange(keyRange),
19659 mValueRange(valueRange),
19662 mDataModified(true)
19664 setSize(keySize, valueSize);
19668 QCPColorMapData::~QCPColorMapData()
19675 Constructs a new QCPColorMapData instance copying the data and range of \a other.
19677 QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
19682 mDataModified(true)
19688 Overwrites this color map data instance with the data stored in \a other.
19690 QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
19692 if (&other != this)
19694 const int keySize = other.keySize();
19695 const int valueSize = other.valueSize();
19696 setSize(keySize, valueSize);
19697 setRange(other.keyRange(), other.valueRange());
19699 memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
19700 mDataBounds = other.mDataBounds;
19701 mDataModified = true;
19706 /* undocumented getter */
19707 double QCPColorMapData::data(double key, double value)
19709 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19710 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19711 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19712 return mData[valueCell*mKeySize + keyCell];
19717 /* undocumented getter */
19718 double QCPColorMapData::cell(int keyIndex, int valueIndex)
19720 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19721 return mData[valueIndex*mKeySize + keyIndex];
19727 Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in
19728 the value dimension.
19730 The current data is discarded and the map cells are set to 0, unless the map had already the
19733 Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref
19734 isEmpty returns true.
19736 \see setRange, setKeySize, setValueSize
19738 void QCPColorMapData::setSize(int keySize, int valueSize)
19740 if (keySize != mKeySize || valueSize != mValueSize)
19742 mKeySize = keySize;
19743 mValueSize = valueSize;
19746 mIsEmpty = mKeySize == 0 || mValueSize == 0;
19749 #ifdef __EXCEPTIONS
19750 try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
19752 mData = new double[mKeySize*mValueSize];
19753 #ifdef __EXCEPTIONS
19754 } catch (...) { mData = 0; }
19759 qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
19762 mDataModified = true;
19767 Resizes the data array to have \a keySize cells in the key dimension.
19769 The current data is discarded and the map cells are set to 0, unless the map had already the
19772 Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true.
19774 \see setKeyRange, setSize, setValueSize
19776 void QCPColorMapData::setKeySize(int keySize)
19778 setSize(keySize, mValueSize);
19782 Resizes the data array to have \a valueSize cells in the value dimension.
19784 The current data is discarded and the map cells are set to 0, unless the map had already the
19787 Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true.
19789 \see setValueRange, setSize, setKeySize
19791 void QCPColorMapData::setValueSize(int valueSize)
19793 setSize(mKeySize, valueSize);
19797 Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area
19798 covered by the color map in plot coordinates.
19800 The outer cells will be centered on the range boundaries given to this function. For example, if
19801 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19802 be cells centered on the key coordinates 2, 2.5 and 3.
19806 void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
19808 setKeyRange(keyRange);
19809 setValueRange(valueRange);
19813 Sets the coordinate range the data shall be distributed over in the key dimension. Together with
19814 the value range, This defines the rectangular area covered by the color map in plot coordinates.
19816 The outer cells will be centered on the range boundaries given to this function. For example, if
19817 the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
19818 be cells centered on the key coordinates 2, 2.5 and 3.
19820 \see setRange, setValueRange, setSize
19822 void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
19824 mKeyRange = keyRange;
19828 Sets the coordinate range the data shall be distributed over in the value dimension. Together with
19829 the key range, This defines the rectangular area covered by the color map in plot coordinates.
19831 The outer cells will be centered on the range boundaries given to this function. For example, if
19832 the value size (\ref setValueSize) is 3 and \a valueRange is set to <tt>QCPRange(2, 3)</tt> there
19833 will be cells centered on the value coordinates 2, 2.5 and 3.
19835 \see setRange, setKeyRange, setSize
19837 void QCPColorMapData::setValueRange(const QCPRange &valueRange)
19839 mValueRange = valueRange;
19843 Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a
19846 \see setCell, setRange
19848 void QCPColorMapData::setData(double key, double value, double z)
19850 int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19851 int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19852 if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
19854 mData[valueCell*mKeySize + keyCell] = z;
19855 if (z < mDataBounds.lower)
19856 mDataBounds.lower = z;
19857 if (z > mDataBounds.upper)
19858 mDataBounds.upper = z;
19859 mDataModified = true;
19864 Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices
19865 enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see
19868 In the standard plot configuration (horizontal key axis and vertical value axis, both not
19869 range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with
19870 indices (keySize-1, valueSize-1) is in the top right corner of the color map.
19872 \see setData, setSize
19874 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
19876 if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
19878 mData[valueIndex*mKeySize + keyIndex] = z;
19879 if (z < mDataBounds.lower)
19880 mDataBounds.lower = z;
19881 if (z > mDataBounds.upper)
19882 mDataBounds.upper = z;
19883 mDataModified = true;
19888 Goes through the data and updates the buffered minimum and maximum data values.
19890 Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange
19891 and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten
19892 with a smaller or larger value respectively, since the buffered maximum/minimum values have been
19893 updated the last time. Why this is the case is explained in the class description (\ref
19896 Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a
19897 recalculateDataBounds for convenience. Setting this to true will call this method for you, before
19900 void QCPColorMapData::recalculateDataBounds()
19902 if (mKeySize > 0 && mValueSize > 0)
19904 double minHeight = mData[0];
19905 double maxHeight = mData[0];
19906 const int dataCount = mValueSize*mKeySize;
19907 for (int i=0; i<dataCount; ++i)
19909 if (mData[i] > maxHeight)
19910 maxHeight = mData[i];
19911 if (mData[i] < minHeight)
19912 minHeight = mData[i];
19914 mDataBounds.lower = minHeight;
19915 mDataBounds.upper = maxHeight;
19920 Frees the internal data memory.
19922 This is equivalent to calling \ref setSize "setSize(0, 0)".
19924 void QCPColorMapData::clear()
19930 Sets all cells to the value \a z.
19932 void QCPColorMapData::fill(double z)
19934 const int dataCount = mValueSize*mKeySize;
19935 for (int i=0; i<dataCount; ++i)
19937 mDataBounds = QCPRange(z, z);
19941 Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData
19942 instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a
19945 The retrieved key/value cell indices can then be used for example with \ref setCell.
19947 If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
19950 \see cellToCoord, QCPAxis::coordToPixel
19952 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
19955 *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
19957 *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
19961 Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData
19962 instance. The resulting coordinates are returned via the output parameters \a key and \a
19965 If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
19968 \see coordToCell, QCPAxis::pixelToCoord
19970 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
19973 *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
19975 *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
19979 ////////////////////////////////////////////////////////////////////////////////////////////////////
19980 //////////////////// QCPColorMap
19981 ////////////////////////////////////////////////////////////////////////////////////////////////////
19983 /*! \class QCPColorMap
19984 \brief A plottable representing a two-dimensional color map in a plot.
19986 \image html QCPColorMap.png
19988 The data is stored in the class \ref QCPColorMapData, which can be accessed via the data()
19991 A color map has three dimensions to represent a data point: The \a key dimension, the \a value
19992 dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value
19993 correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QColorMap
19994 constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a
19997 Set the number of points (or \a cells) in the key/value dimension via \ref
19998 QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is
19999 specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range
20000 boundary and the last cell will be centered on the upper range boundary. The data can be set by
20001 either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via
20002 their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer
20003 setCell, since it doesn't need to do any coordinate transformation and thus performs a bit
20006 The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)
20007 key and value axes.
20009 To show the user which colors correspond to which \a data values, a \ref QCPColorScale is
20010 typically placed to the right of the axis rect. See the documentation there for details on how to
20011 add and use a color scale.
20013 \section appearance Changing the appearance
20015 The central part of the appearance is the color gradient, which can be specified via \ref
20016 setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
20019 The \a data range that is mapped to the colors of the gradient can be specified with \ref
20020 setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
20023 \section usage Usage
20025 Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable
20026 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
20027 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
20029 Usually, you first create an instance:
20031 QCPColorMap *colorMap = new QCPColorMap(customPlot->xAxis, customPlot->yAxis);\endcode
20032 add it to the customPlot with QCustomPlot::addPlottable:
20034 customPlot->addPlottable(colorMap);\endcode
20035 and then modify the properties of the newly created color map, e.g.:
20037 colorMap->data()->setSize(50, 50);
20038 colorMap->data()->setRange(QCPRange(0, 2), QCPRange(0, 2));
20039 for (int x=0; x<50; ++x)
20040 for (int y=0; y<50; ++y)
20041 colorMap->data()->setCell(x, y, qCos(x/10.0)+qSin(y/10.0));
20042 colorMap->setGradient(QCPColorGradient::gpPolar);
20043 colorMap->rescaleDataRange(true);
20044 customPlot->rescaleAxes();
20045 customPlot->replot();
20048 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
20049 value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
20050 you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
20051 determine the cell index. Rather directly access the cell index with \ref
20052 QCPColorMapData::setCell.
20055 /* start documentation of inline functions */
20057 /*! \fn QCPColorMapData *QCPColorMap::data() const
20059 Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to
20060 modify data points (cells) and the color map key/value range.
20065 /* end documentation of inline functions */
20067 /* start documentation of signals */
20069 /*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
20071 This signal is emitted when the data range changes.
20076 /*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
20078 This signal is emitted when the data scale type changes.
20080 \see setDataScaleType
20083 /*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
20085 This signal is emitted when the gradient changes.
20090 /* end documentation of signals */
20093 Constructs a color map with the specified \a keyAxis and \a valueAxis.
20095 The constructed QCPColorMap can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20096 then takes ownership of the color map.
20098 QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20099 QCPAbstractPlottable(keyAxis, valueAxis),
20100 mDataScaleType(QCPAxis::stLinear),
20101 mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
20102 mInterpolate(true),
20103 mTightBoundary(false),
20104 mMapImageInvalidated(true)
20108 QCPColorMap::~QCPColorMap()
20114 Replaces the current \ref data with the provided \a data.
20116 If \a copy is set to true, the \a data object will only be copied. if false, the color map
20117 takes ownership of the passed data and replaces the internal data pointer with it. This is
20118 significantly faster than copying for large datasets.
20120 void QCPColorMap::setData(QCPColorMapData *data, bool copy)
20122 if (mMapData == data)
20124 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20135 mMapImageInvalidated = true;
20139 Sets the data range of this color map to \a dataRange. The data range defines which data values
20140 are mapped to the color gradient.
20142 To make the data range span the full range of the data set, use \ref rescaleDataRange.
20144 \see QCPColorScale::setDataRange
20146 void QCPColorMap::setDataRange(const QCPRange &dataRange)
20148 if (!QCPRange::validRange(dataRange)) return;
20149 if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
20151 if (mDataScaleType == QCPAxis::stLogarithmic)
20152 mDataRange = dataRange.sanitizedForLogScale();
20154 mDataRange = dataRange.sanitizedForLinScale();
20155 mMapImageInvalidated = true;
20156 emit dataRangeChanged(mDataRange);
20161 Sets whether the data is correlated with the color gradient linearly or logarithmically.
20163 \see QCPColorScale::setDataScaleType
20165 void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
20167 if (mDataScaleType != scaleType)
20169 mDataScaleType = scaleType;
20170 mMapImageInvalidated = true;
20171 emit dataScaleTypeChanged(mDataScaleType);
20172 if (mDataScaleType == QCPAxis::stLogarithmic)
20173 setDataRange(mDataRange.sanitizedForLogScale());
20178 Sets the color gradient that is used to represent the data. For more details on how to create an
20179 own gradient or use one of the preset gradients, see \ref QCPColorGradient.
20181 The colors defined by the gradient will be used to represent data values in the currently set
20182 data range, see \ref setDataRange. Data points that are outside this data range will either be
20183 colored uniformly with the respective gradient boundary color, or the gradient will repeat,
20184 depending on \ref QCPColorGradient::setPeriodic.
20186 \see QCPColorScale::setGradient
20188 void QCPColorMap::setGradient(const QCPColorGradient &gradient)
20190 if (mGradient != gradient)
20192 mGradient = gradient;
20193 mMapImageInvalidated = true;
20194 emit gradientChanged(mGradient);
20199 Sets whether the color map image shall use bicubic interpolation when displaying the color map
20200 shrinked or expanded, and not at a 1:1 pixel-to-data scale.
20202 \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled"
20204 void QCPColorMap::setInterpolate(bool enabled)
20206 mInterpolate = enabled;
20210 Sets whether the outer most data rows and columns are clipped to the specified key and value
20211 range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange).
20213 if \a enabled is set to false, the data points at the border of the color map are drawn with the
20214 same width and height as all other data points. Since the data points are represented by
20215 rectangles of one color centered on the data coordinate, this means that the shown color map
20216 extends by half a data point over the specified key/value range in each direction.
20218 \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled"
20220 void QCPColorMap::setTightBoundary(bool enabled)
20222 mTightBoundary = enabled;
20226 Associates the color scale \a colorScale with this color map.
20228 This means that both the color scale and the color map synchronize their gradient, data range and
20229 data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps
20230 can be associated with one single color scale. This causes the color maps to also synchronize
20231 those properties, via the mutual color scale.
20233 This function causes the color map to adopt the current color gradient, data range and data scale
20234 type of \a colorScale. After this call, you may change these properties at either the color map
20235 or the color scale, and the setting will be applied to both.
20237 Pass 0 as \a colorScale to disconnect the color scale from this color map again.
20239 void QCPColorMap::setColorScale(QCPColorScale *colorScale)
20241 if (mColorScale) // unconnect signals from old color scale
20243 disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20244 disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20245 disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20246 disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20247 disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20248 disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20250 mColorScale = colorScale;
20251 if (mColorScale) // connect signals to new color scale
20253 setGradient(mColorScale.data()->gradient());
20254 setDataRange(mColorScale.data()->dataRange());
20255 setDataScaleType(mColorScale.data()->dataScaleType());
20256 connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
20257 connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
20258 connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
20259 connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
20260 connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
20261 connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
20266 Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the
20267 current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods,
20268 only for the third data dimension of the color map.
20270 The minimum and maximum values of the data set are buffered in the internal QCPColorMapData
20271 instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref
20272 QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For
20273 performance reasons, however, they are only updated in an expanding fashion. So the buffered
20274 maximum can only increase and the buffered minimum can only decrease. In consequence, changes to
20275 the data that actually lower the maximum of the data set (by overwriting the cell holding the
20276 current maximum with a smaller value), aren't recognized and the buffered maximum overestimates
20277 the true maximum of the data set. The same happens for the buffered minimum. To recalculate the
20278 true minimum and maximum by explicitly looking at each cell, the method
20279 QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a
20280 recalculateDataBounds calls this method before setting the data range to the buffered minimum and
20285 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
20287 if (recalculateDataBounds)
20288 mMapData->recalculateDataBounds();
20289 setDataRange(mMapData->dataBounds());
20293 Takes the current appearance of the color map and updates the legend icon, which is used to
20294 represent this color map in the legend (see \ref QCPLegend).
20296 The \a transformMode specifies whether the rescaling is done by a faster, low quality image
20297 scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm
20298 (Qt::SmoothTransformation).
20300 The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to
20301 the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured
20302 legend icon size, the thumb will be rescaled during drawing of the legend item.
20306 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
20308 if (mMapImage.isNull() && !data()->isEmpty())
20309 updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
20311 if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
20313 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20314 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20315 mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
20320 Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also
20321 resizes the map to 0x0 cells.
20323 void QCPColorMap::clearData()
20328 /* inherits documentation from base class */
20329 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20332 if (onlySelectable && !mSelectable)
20334 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20336 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20338 double posKey, posValue;
20339 pixelsToCoords(pos, posKey, posValue);
20340 if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
20341 return mParentPlot->selectionTolerance()*0.99;
20348 Updates the internal map image buffer by going through the internal \ref QCPColorMapData and
20349 turning the data values into color pixels with \ref QCPColorGradient::colorize.
20351 This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image
20352 has been invalidated for a different reason (e.g. a change of the data range with \ref
20355 void QCPColorMap::updateMapImage()
20357 QCPAxis *keyAxis = mKeyAxis.data();
20358 if (!keyAxis) return;
20360 // resize mMapImage to correct dimensions, according to key/value axes orientation:
20361 if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.size().width() != mMapData->keySize() || mMapImage.size().height() != mMapData->valueSize()))
20362 mMapImage = QImage(QSize(mMapData->keySize(), mMapData->valueSize()), QImage::Format_RGB32);
20363 else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.size().width() != mMapData->valueSize() || mMapImage.size().height() != mMapData->keySize()))
20364 mMapImage = QImage(QSize(mMapData->valueSize(), mMapData->keySize()), QImage::Format_RGB32);
20366 const int keySize = mMapData->keySize();
20367 const int valueSize = mMapData->valueSize();
20368 const double *rawData = mMapData->mData;
20370 if (keyAxis->orientation() == Qt::Horizontal)
20372 const int lineCount = valueSize;
20373 const int rowCount = keySize;
20374 for (int line=0; line<lineCount; ++line)
20376 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)
20377 mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
20379 } else // keyAxis->orientation() == Qt::Vertical
20381 const int lineCount = keySize;
20382 const int rowCount = valueSize;
20383 for (int line=0; line<lineCount; ++line)
20385 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)
20386 mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
20390 mMapData->mDataModified = false;
20391 mMapImageInvalidated = false;
20394 /* inherits documentation from base class */
20395 void QCPColorMap::draw(QCPPainter *painter)
20397 if (mMapData->isEmpty()) return;
20398 if (!mKeyAxis || !mValueAxis) return;
20399 applyDefaultAntialiasingHint(painter);
20401 if (mMapData->mDataModified || mMapImageInvalidated)
20404 double halfSampleKey = 0;
20405 double halfSampleValue = 0;
20406 if (mMapData->keySize() > 1)
20407 halfSampleKey = 0.5*mMapData->keyRange().size()/(double)(mMapData->keySize()-1);
20408 if (mMapData->valueSize() > 1)
20409 halfSampleValue = 0.5*mMapData->valueRange().size()/(double)(mMapData->valueSize()-1);
20410 QRectF imageRect(coordsToPixels(mMapData->keyRange().lower-halfSampleKey, mMapData->valueRange().lower-halfSampleValue),
20411 coordsToPixels(mMapData->keyRange().upper+halfSampleKey, mMapData->valueRange().upper+halfSampleValue));
20412 imageRect = imageRect.normalized();
20413 bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
20414 bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
20415 bool smoothBackup = painter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
20416 painter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
20417 QRegion clipBackup;
20418 if (mTightBoundary)
20420 clipBackup = painter->clipRegion();
20421 painter->setClipRect(QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
20422 coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(), Qt::IntersectClip);
20424 painter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
20425 if (mTightBoundary)
20426 painter->setClipRegion(clipBackup);
20427 painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
20430 /* inherits documentation from base class */
20431 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20433 applyDefaultAntialiasingHint(painter);
20434 // draw map thumbnail:
20435 if (!mLegendIcon.isNull())
20437 QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
20438 QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
20439 iconRect.moveCenter(rect.center());
20440 painter->drawPixmap(iconRect.topLeft(), scaledIcon);
20444 painter->setBrush(Qt::NoBrush);
20445 painter->setPen(Qt::black);
20446 painter->drawRect(rect.adjusted(1, 1, 0, 0));
20450 /* inherits documentation from base class */
20451 QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
20454 QCPRange result = mMapData->keyRange();
20455 result.normalize();
20456 if (inSignDomain == QCPAbstractPlottable::sdPositive)
20458 if (result.lower <= 0 && result.upper > 0)
20459 result.lower = result.upper*1e-3;
20460 else if (result.lower <= 0 && result.upper <= 0)
20461 foundRange = false;
20462 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20464 if (result.upper >= 0 && result.lower < 0)
20465 result.upper = result.lower*1e-3;
20466 else if (result.upper >= 0 && result.lower >= 0)
20467 foundRange = false;
20472 /* inherits documentation from base class */
20473 QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
20476 QCPRange result = mMapData->valueRange();
20477 result.normalize();
20478 if (inSignDomain == QCPAbstractPlottable::sdPositive)
20480 if (result.lower <= 0 && result.upper > 0)
20481 result.lower = result.upper*1e-3;
20482 else if (result.lower <= 0 && result.upper <= 0)
20483 foundRange = false;
20484 } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
20486 if (result.upper >= 0 && result.lower < 0)
20487 result.upper = result.lower*1e-3;
20488 else if (result.upper >= 0 && result.lower >= 0)
20489 foundRange = false;
20495 ////////////////////////////////////////////////////////////////////////////////////////////////////
20496 //////////////////// QCPFinancialData
20497 ////////////////////////////////////////////////////////////////////////////////////////////////////
20499 /*! \class QCPFinancialData
20500 \brief Holds the data of one single data point for QCPFinancial.
20502 The container for storing multiple data points is \ref QCPFinancialDataMap.
20504 The stored data is:
20505 \li \a key: coordinate on the key axis of this data point
20506 \li \a open: The opening value at the data point
20507 \li \a high: The high/maximum value at the data point
20508 \li \a low: The low/minimum value at the data point
20509 \li \a close: The closing value at the data point
20511 \see QCPFinancialDataMap
20515 Constructs a data point with key and all values set to zero.
20517 QCPFinancialData::QCPFinancialData() :
20527 Constructs a data point with the specified \a key and OHLC values.
20529 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
20539 ////////////////////////////////////////////////////////////////////////////////////////////////////
20540 //////////////////// QCPFinancial
20541 ////////////////////////////////////////////////////////////////////////////////////////////////////
20543 /*! \class QCPFinancial
20544 \brief A plottable representing a financial stock chart
20546 \image html QCPFinancial.png
20548 This plottable represents time series data binned to certain intervals, mainly used for stock
20549 charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be
20550 set via \ref setChartStyle.
20552 The data is passed via \ref setData as a set of open/high/low/close values at certain keys
20553 (typically times). This means the data must be already binned appropriately. If data is only
20554 available as a series of values (e.g. \a price against \a time), you can use the static
20555 convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed
20558 The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and is given in plot
20559 key coordinates. A typical choice is to set it to (or slightly less than) one bin interval width.
20561 \section appearance Changing the appearance
20563 Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored,
20564 lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush).
20566 If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are
20567 represented with a different pen and brush than negative changes (\a close < \a open). These can
20568 be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref
20569 setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection
20570 however, the normal selected pen/brush (\ref setSelectedPen, \ref setSelectedBrush) is used,
20571 irrespective of whether the chart is single- or two-colored.
20575 /* start of documentation of inline functions */
20577 /*! \fn QCPFinancialDataMap *QCPFinancial::data() const
20579 Returns a pointer to the internal data storage of type \ref QCPFinancialDataMap. You may use it to
20580 directly manipulate the data, which may be more convenient and faster than using the regular \ref
20581 setData or \ref addData methods, in certain situations.
20584 /* end of documentation of inline functions */
20587 Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
20588 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
20589 the same orientation. If either of these restrictions is violated, a corresponding message is
20590 printed to the debug output (qDebug), the construction is not aborted, though.
20592 The constructed QCPFinancial can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
20593 then takes ownership of the financial chart.
20595 QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20596 QCPAbstractPlottable(keyAxis, valueAxis),
20598 mChartStyle(csOhlc),
20600 mTwoColored(false),
20601 mBrushPositive(QBrush(QColor(210, 210, 255))),
20602 mBrushNegative(QBrush(QColor(255, 210, 210))),
20603 mPenPositive(QPen(QColor(10, 40, 180))),
20604 mPenNegative(QPen(QColor(180, 40, 10)))
20606 mData = new QCPFinancialDataMap;
20608 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
20609 setSelectedBrush(QBrush(QColor(80, 80, 255)));
20612 QCPFinancial::~QCPFinancial()
20618 Replaces the current data with the provided \a data.
20620 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
20621 takes ownership of the passed data and replaces the internal data pointer with it. This is
20622 significantly faster than copying for large datasets.
20624 Alternatively, you can also access and modify the plottable's data via the \ref data method, which
20625 returns a pointer to the internal \ref QCPFinancialDataMap.
20627 \see timeSeriesToOhlc
20629 void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy)
20633 qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
20648 Replaces the current data with the provided open/high/low/close data. The provided vectors should
20649 have equal length. Else, the number of added points will be the size of the smallest vector.
20651 \see timeSeriesToOhlc
20653 void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20656 int n = key.size();
20657 n = qMin(n, open.size());
20658 n = qMin(n, high.size());
20659 n = qMin(n, low.size());
20660 n = qMin(n, close.size());
20661 for (int i=0; i<n; ++i)
20663 mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20668 Sets which representation style shall be used to display the OHLC data.
20670 void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)
20672 mChartStyle = style;
20676 Sets the width of the individual bars/candlesticks to \a width in plot key coordinates.
20678 A typical choice is to set it to (or slightly less than) one bin interval width.
20680 void QCPFinancial::setWidth(double width)
20686 Sets whether this chart shall contrast positive from negative trends per data point by using two
20687 separate colors to draw the respective bars/candlesticks.
20689 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20692 \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative
20694 void QCPFinancial::setTwoColored(bool twoColored)
20696 mTwoColored = twoColored;
20700 If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20701 of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20703 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20706 \see setBrushNegative, setPenPositive, setPenNegative
20708 void QCPFinancial::setBrushPositive(const QBrush &brush)
20710 mBrushPositive = brush;
20714 If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
20715 of data points with a negative trend (i.e. bars/candlesticks with close < open).
20717 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20720 \see setBrushPositive, setPenNegative, setPenPositive
20722 void QCPFinancial::setBrushNegative(const QBrush &brush)
20724 mBrushNegative = brush;
20728 If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20729 outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open).
20731 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20734 \see setPenNegative, setBrushPositive, setBrushNegative
20736 void QCPFinancial::setPenPositive(const QPen &pen)
20738 mPenPositive = pen;
20742 If \ref setTwoColored is set to true, this function controls the pen that is used to draw
20743 outlines of data points with a negative trend (i.e. bars/candlesticks with close < open).
20745 If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
20748 \see setPenPositive, setBrushNegative, setBrushPositive
20750 void QCPFinancial::setPenNegative(const QPen &pen)
20752 mPenNegative = pen;
20756 Adds the provided data points in \a dataMap to the current data.
20758 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20759 pointer to the internal \ref QCPFinancialDataMap.
20763 void QCPFinancial::addData(const QCPFinancialDataMap &dataMap)
20765 mData->unite(dataMap);
20770 Adds the provided single data point in \a data to the current data.
20772 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20773 pointer to the internal \ref QCPFinancialData.
20777 void QCPFinancial::addData(const QCPFinancialData &data)
20779 mData->insertMulti(data.key, data);
20784 Adds the provided single data point given by \a key, \a open, \a high, \a low, and \a close to
20787 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20788 pointer to the internal \ref QCPFinancialData.
20792 void QCPFinancial::addData(double key, double open, double high, double low, double close)
20794 mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
20799 Adds the provided open/high/low/close data to the current data.
20801 Alternatively, you can also access and modify the data via the \ref data method, which returns a
20802 pointer to the internal \ref QCPFinancialData.
20806 void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
20808 int n = key.size();
20809 n = qMin(n, open.size());
20810 n = qMin(n, high.size());
20811 n = qMin(n, low.size());
20812 n = qMin(n, close.size());
20813 for (int i=0; i<n; ++i)
20815 mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
20820 Removes all data points with keys smaller than \a key.
20822 \see addData, clearData
20824 void QCPFinancial::removeDataBefore(double key)
20826 QCPFinancialDataMap::iterator it = mData->begin();
20827 while (it != mData->end() && it.key() < key)
20828 it = mData->erase(it);
20832 Removes all data points with keys greater than \a key.
20834 \see addData, clearData
20836 void QCPFinancial::removeDataAfter(double key)
20838 if (mData->isEmpty()) return;
20839 QCPFinancialDataMap::iterator it = mData->upperBound(key);
20840 while (it != mData->end())
20841 it = mData->erase(it);
20845 Removes all data points with keys between \a fromKey and \a toKey. if \a fromKey is greater or
20846 equal to \a toKey, the function does nothing. To remove a single data point with known key, use
20847 \ref removeData(double key).
20849 \see addData, clearData
20851 void QCPFinancial::removeData(double fromKey, double toKey)
20853 if (fromKey >= toKey || mData->isEmpty()) return;
20854 QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
20855 QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
20856 while (it != itEnd)
20857 it = mData->erase(it);
20862 Removes a single data point at \a key. If the position is not known with absolute precision,
20863 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
20864 around the suspected position, depeding on the precision with which the key is known.
20866 \see addData, clearData
20868 void QCPFinancial::removeData(double key)
20870 mData->remove(key);
20874 Removes all data points.
20876 \see removeData, removeDataAfter, removeDataBefore
20878 void QCPFinancial::clearData()
20883 /* inherits documentation from base class */
20884 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20887 if (onlySelectable && !mSelectable)
20889 if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
20891 if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20893 // get visible data range:
20894 QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20895 getVisibleDataBounds(lower, upper);
20896 if (lower == mData->constEnd() || upper == mData->constEnd())
20898 // perform select test according to configured style:
20899 switch (mChartStyle)
20901 case QCPFinancial::csOhlc:
20902 return ohlcSelectTest(pos, lower, upper+1); break;
20903 case QCPFinancial::csCandlestick:
20904 return candlestickSelectTest(pos, lower, upper+1); break;
20911 A convenience function that converts time series data (\a value against \a time) to OHLC binned
20912 data points. The return value can then be passed on to \ref setData.
20914 The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given.
20915 For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour
20916 each, set \a timeBinSize to 3600.
20918 \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The
20919 value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys.
20920 It merely defines the mathematical offset/phase of the bins that will be used to process the
20923 QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
20925 QCPFinancialDataMap map;
20926 int count = qMin(time.size(), value.size());
20928 return QCPFinancialDataMap();
20930 QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
20931 int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
20932 for (int i=0; i<count; ++i)
20934 int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
20935 if (currentBinIndex == index) // data point still in current bin, extend high/low:
20937 if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
20938 if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
20939 if (i == count-1) // last data point is in current bin, finalize bin:
20941 currentBinData.close = value.at(i);
20942 currentBinData.key = timeBinOffset+(index)*timeBinSize;
20943 map.insert(currentBinData.key, currentBinData);
20945 } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
20947 // finalize current bin:
20948 currentBinData.close = value.at(i-1);
20949 currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
20950 map.insert(currentBinData.key, currentBinData);
20952 currentBinIndex = index;
20953 currentBinData.open = value.at(i);
20954 currentBinData.high = value.at(i);
20955 currentBinData.low = value.at(i);
20962 /* inherits documentation from base class */
20963 void QCPFinancial::draw(QCPPainter *painter)
20965 // get visible data range:
20966 QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
20967 getVisibleDataBounds(lower, upper);
20968 if (lower == mData->constEnd() || upper == mData->constEnd())
20971 // draw visible data range according to configured style:
20972 switch (mChartStyle)
20974 case QCPFinancial::csOhlc:
20975 drawOhlcPlot(painter, lower, upper+1); break;
20976 case QCPFinancial::csCandlestick:
20977 drawCandlestickPlot(painter, lower, upper+1); break;
20981 /* inherits documentation from base class */
20982 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20984 painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
20985 if (mChartStyle == csOhlc)
20989 // draw upper left half icon with positive color:
20990 painter->setBrush(mBrushPositive);
20991 painter->setPen(mPenPositive);
20992 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
20993 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
20994 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
20995 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
20996 // draw bottom right hald icon with negative color:
20997 painter->setBrush(mBrushNegative);
20998 painter->setPen(mPenNegative);
20999 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21000 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21001 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21002 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21005 painter->setBrush(mBrush);
21006 painter->setPen(mPen);
21007 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21008 painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
21009 painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
21011 } else if (mChartStyle == csCandlestick)
21015 // draw upper left half icon with positive color:
21016 painter->setBrush(mBrushPositive);
21017 painter->setPen(mPenPositive);
21018 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
21019 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21020 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21021 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21022 // draw bottom right hald icon with negative color:
21023 painter->setBrush(mBrushNegative);
21024 painter->setPen(mPenNegative);
21025 painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
21026 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21027 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21028 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21031 painter->setBrush(mBrush);
21032 painter->setPen(mPen);
21033 painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
21034 painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
21035 painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
21040 /* inherits documentation from base class */
21041 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21044 bool haveLower = false;
21045 bool haveUpper = false;
21048 QCPFinancialDataMap::const_iterator it = mData->constBegin();
21049 while (it != mData->constEnd())
21051 current = it.value().key;
21052 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
21054 if (current < range.lower || !haveLower)
21056 range.lower = current;
21059 if (current > range.upper || !haveUpper)
21061 range.upper = current;
21067 // determine exact range by including width of bars/flags:
21068 if (haveLower && mKeyAxis)
21069 range.lower = range.lower-mWidth*0.5;
21070 if (haveUpper && mKeyAxis)
21071 range.upper = range.upper+mWidth*0.5;
21072 foundRange = haveLower && haveUpper;
21076 /* inherits documentation from base class */
21077 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
21080 bool haveLower = false;
21081 bool haveUpper = false;
21083 QCPFinancialDataMap::const_iterator it = mData->constBegin();
21084 while (it != mData->constEnd())
21087 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
21089 if (it.value().high < range.lower || !haveLower)
21091 range.lower = it.value().high;
21094 if (it.value().high > range.upper || !haveUpper)
21096 range.upper = it.value().high;
21101 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
21103 if (it.value().low < range.lower || !haveLower)
21105 range.lower = it.value().low;
21108 if (it.value().low > range.upper || !haveUpper)
21110 range.upper = it.value().low;
21117 foundRange = haveLower && haveUpper;
21123 Draws the data from \a begin to \a end as OHLC bars with the provided \a painter.
21125 This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc.
21127 void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21129 QCPAxis *keyAxis = mKeyAxis.data();
21130 QCPAxis *valueAxis = mValueAxis.data();
21131 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21135 if (keyAxis->orientation() == Qt::Horizontal)
21137 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21140 linePen = mSelectedPen;
21141 else if (mTwoColored)
21142 linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21145 painter->setPen(linePen);
21146 double keyPixel = keyAxis->coordToPixel(it.value().key);
21147 double openPixel = valueAxis->coordToPixel(it.value().open);
21148 double closePixel = valueAxis->coordToPixel(it.value().close);
21150 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
21152 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21153 painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
21155 painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
21159 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21162 linePen = mSelectedPen;
21163 else if (mTwoColored)
21164 linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
21167 painter->setPen(linePen);
21168 double keyPixel = keyAxis->coordToPixel(it.value().key);
21169 double openPixel = valueAxis->coordToPixel(it.value().open);
21170 double closePixel = valueAxis->coordToPixel(it.value().close);
21172 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
21174 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
21175 painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
21177 painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
21184 Draws the data from \a begin to \a end as Candlesticks with the provided \a painter.
21186 This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick.
21188 void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
21190 QCPAxis *keyAxis = mKeyAxis.data();
21191 QCPAxis *valueAxis = mValueAxis.data();
21192 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21197 if (keyAxis->orientation() == Qt::Horizontal)
21199 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21203 linePen = mSelectedPen;
21204 boxBrush = mSelectedBrush;
21205 } else if (mTwoColored)
21207 if (it.value().close >= it.value().open)
21209 linePen = mPenPositive;
21210 boxBrush = mBrushPositive;
21213 linePen = mPenNegative;
21214 boxBrush = mBrushNegative;
21221 painter->setPen(linePen);
21222 painter->setBrush(boxBrush);
21223 double keyPixel = keyAxis->coordToPixel(it.value().key);
21224 double openPixel = valueAxis->coordToPixel(it.value().open);
21225 double closePixel = valueAxis->coordToPixel(it.value().close);
21227 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
21229 painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
21230 // draw open-close box:
21231 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21232 painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
21234 } else // keyAxis->orientation() == Qt::Vertical
21236 for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
21240 linePen = mSelectedPen;
21241 boxBrush = mSelectedBrush;
21242 } else if (mTwoColored)
21244 if (it.value().close >= it.value().open)
21246 linePen = mPenPositive;
21247 boxBrush = mBrushPositive;
21250 linePen = mPenNegative;
21251 boxBrush = mBrushNegative;
21258 painter->setPen(linePen);
21259 painter->setBrush(boxBrush);
21260 double keyPixel = keyAxis->coordToPixel(it.value().key);
21261 double openPixel = valueAxis->coordToPixel(it.value().open);
21262 double closePixel = valueAxis->coordToPixel(it.value().close);
21264 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
21266 painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
21267 // draw open-close box:
21268 double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
21269 painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
21276 This method is a helper function for \ref selectTest. It is used to test for selection when the
21277 chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end.
21279 double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21281 QCPAxis *keyAxis = mKeyAxis.data();
21282 QCPAxis *valueAxis = mValueAxis.data();
21283 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21285 double minDistSqr = std::numeric_limits<double>::max();
21286 QCPFinancialDataMap::const_iterator it;
21287 if (keyAxis->orientation() == Qt::Horizontal)
21289 for (it = begin; it != end; ++it)
21291 double keyPixel = keyAxis->coordToPixel(it.value().key);
21292 // calculate distance to backbone:
21293 double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
21294 if (currentDistSqr < minDistSqr)
21295 minDistSqr = currentDistSqr;
21297 } else // keyAxis->orientation() == Qt::Vertical
21299 for (it = begin; it != end; ++it)
21301 double keyPixel = keyAxis->coordToPixel(it.value().key);
21302 // calculate distance to backbone:
21303 double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
21304 if (currentDistSqr < minDistSqr)
21305 minDistSqr = currentDistSqr;
21308 return qSqrt(minDistSqr);
21313 This method is a helper function for \ref selectTest. It is used to test for selection when the
21314 chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a
21317 double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
21319 QCPAxis *keyAxis = mKeyAxis.data();
21320 QCPAxis *valueAxis = mValueAxis.data();
21321 if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
21323 double minDistSqr = std::numeric_limits<double>::max();
21324 QCPFinancialDataMap::const_iterator it;
21325 if (keyAxis->orientation() == Qt::Horizontal)
21327 for (it = begin; it != end; ++it)
21329 double currentDistSqr;
21330 // determine whether pos is in open-close-box:
21331 QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21332 QCPRange boxValueRange(it.value().close, it.value().open);
21333 double posKey, posValue;
21334 pixelsToCoords(pos, posKey, posValue);
21335 if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21337 currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21340 // calculate distance to high/low lines:
21341 double keyPixel = keyAxis->coordToPixel(it.value().key);
21342 double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
21343 double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
21344 currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21346 if (currentDistSqr < minDistSqr)
21347 minDistSqr = currentDistSqr;
21349 } else // keyAxis->orientation() == Qt::Vertical
21351 for (it = begin; it != end; ++it)
21353 double currentDistSqr;
21354 // determine whether pos is in open-close-box:
21355 QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
21356 QCPRange boxValueRange(it.value().close, it.value().open);
21357 double posKey, posValue;
21358 pixelsToCoords(pos, posKey, posValue);
21359 if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
21361 currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
21364 // calculate distance to high/low lines:
21365 double keyPixel = keyAxis->coordToPixel(it.value().key);
21366 double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
21367 double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
21368 currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
21370 if (currentDistSqr < minDistSqr)
21371 minDistSqr = currentDistSqr;
21374 return qSqrt(minDistSqr);
21379 called by the drawing methods to determine which data (key) range is visible at the current key
21380 axis range setting, so only that needs to be processed.
21382 \a lower returns an iterator to the lowest data point that needs to be taken into account when
21383 plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
21384 lower may still be just outside the visible range.
21386 \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
21387 just outside of the visible range.
21389 if the plottable contains no data, both \a lower and \a upper point to constEnd.
21391 \see QCPGraph::getVisibleDataBounds
21393 void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
21395 if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
21396 if (mData->isEmpty())
21398 lower = mData->constEnd();
21399 upper = mData->constEnd();
21403 // get visible data range as QMap iterators
21404 QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
21405 QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
21406 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
21407 bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
21409 lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
21410 upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
21414 ////////////////////////////////////////////////////////////////////////////////////////////////////
21415 //////////////////// QCPItemStraightLine
21416 ////////////////////////////////////////////////////////////////////////////////////////////////////
21418 /*! \class QCPItemStraightLine
21419 \brief A straight line that spans infinitely in both directions
21421 \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
21423 It has two positions, \a point1 and \a point2, which define the straight line.
21427 Creates a straight line item and sets default values.
21429 The constructed item can be added to the plot with QCustomPlot::addItem.
21431 QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
21432 QCPAbstractItem(parentPlot),
21433 point1(createPosition(QLatin1String("point1"))),
21434 point2(createPosition(QLatin1String("point2")))
21436 point1->setCoords(0, 0);
21437 point2->setCoords(1, 1);
21439 setPen(QPen(Qt::black));
21440 setSelectedPen(QPen(Qt::blue,2));
21443 QCPItemStraightLine::~QCPItemStraightLine()
21448 Sets the pen that will be used to draw the line
21450 \see setSelectedPen
21452 void QCPItemStraightLine::setPen(const QPen &pen)
21458 Sets the pen that will be used to draw the line when selected
21460 \see setPen, setSelected
21462 void QCPItemStraightLine::setSelectedPen(const QPen &pen)
21464 mSelectedPen = pen;
21467 /* inherits documentation from base class */
21468 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21471 if (onlySelectable && !mSelectable)
21474 return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
21477 /* inherits documentation from base class */
21478 void QCPItemStraightLine::draw(QCPPainter *painter)
21480 QVector2D start(point1->pixelPoint());
21481 QVector2D end(point2->pixelPoint());
21482 // get visible segment of straight line inside clipRect:
21483 double clipPad = mainPen().widthF();
21484 QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21485 // paint visible segment, if existent:
21486 if (!line.isNull())
21488 painter->setPen(mainPen());
21489 painter->drawLine(line);
21495 finds the shortest distance of \a point to the straight line defined by the base point \a
21496 base and the direction vector \a vec.
21498 This is a helper function for \ref selectTest.
21500 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
21502 return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
21507 Returns the section of the straight line defined by \a base and direction vector \a
21508 vec, that is visible in the specified \a rect.
21510 This is a helper function for \ref draw.
21512 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
21517 if (vec.x() == 0 && vec.y() == 0)
21519 if (qFuzzyIsNull(vec.x())) // line is vertical
21521 // check top of rect:
21524 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21525 if (gamma >= 0 && gamma <= rect.width())
21526 result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
21527 } else if (qFuzzyIsNull(vec.y())) // line is horizontal
21529 // check left of rect:
21532 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21533 if (gamma >= 0 && gamma <= rect.height())
21534 result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
21535 } else // line is skewed
21537 QList<QVector2D> pointVectors;
21538 // check top of rect:
21541 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21542 if (gamma >= 0 && gamma <= rect.width())
21543 pointVectors.append(QVector2D(bx+gamma, by));
21544 // check bottom of rect:
21546 by = rect.bottom();
21547 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
21548 if (gamma >= 0 && gamma <= rect.width())
21549 pointVectors.append(QVector2D(bx+gamma, by));
21550 // check left of rect:
21553 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21554 if (gamma >= 0 && gamma <= rect.height())
21555 pointVectors.append(QVector2D(bx, by+gamma));
21556 // check right of rect:
21559 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
21560 if (gamma >= 0 && gamma <= rect.height())
21561 pointVectors.append(QVector2D(bx, by+gamma));
21563 // evaluate points:
21564 if (pointVectors.size() == 2)
21566 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21567 } else if (pointVectors.size() > 2)
21569 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21570 double distSqrMax = 0;
21571 QVector2D pv1, pv2;
21572 for (int i=0; i<pointVectors.size()-1; ++i)
21574 for (int k=i+1; k<pointVectors.size(); ++k)
21576 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21577 if (distSqr > distSqrMax)
21579 pv1 = pointVectors.at(i);
21580 pv2 = pointVectors.at(k);
21581 distSqrMax = distSqr;
21585 result.setPoints(pv1.toPointF(), pv2.toPointF());
21593 Returns the pen that should be used for drawing lines. Returns mPen when the
21594 item is not selected and mSelectedPen when it is.
21596 QPen QCPItemStraightLine::mainPen() const
21598 return mSelected ? mSelectedPen : mPen;
21602 ////////////////////////////////////////////////////////////////////////////////////////////////////
21603 //////////////////// QCPItemLine
21604 ////////////////////////////////////////////////////////////////////////////////////////////////////
21606 /*! \class QCPItemLine
21607 \brief A line from one point to another
21609 \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
21611 It has two positions, \a start and \a end, which define the end points of the line.
21613 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
21617 Creates a line item and sets default values.
21619 The constructed item can be added to the plot with QCustomPlot::addItem.
21621 QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
21622 QCPAbstractItem(parentPlot),
21623 start(createPosition(QLatin1String("start"))),
21624 end(createPosition(QLatin1String("end")))
21626 start->setCoords(0, 0);
21627 end->setCoords(1, 1);
21629 setPen(QPen(Qt::black));
21630 setSelectedPen(QPen(Qt::blue,2));
21633 QCPItemLine::~QCPItemLine()
21638 Sets the pen that will be used to draw the line
21640 \see setSelectedPen
21642 void QCPItemLine::setPen(const QPen &pen)
21648 Sets the pen that will be used to draw the line when selected
21650 \see setPen, setSelected
21652 void QCPItemLine::setSelectedPen(const QPen &pen)
21654 mSelectedPen = pen;
21658 Sets the line ending style of the head. The head corresponds to the \a end position.
21660 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21661 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21665 void QCPItemLine::setHead(const QCPLineEnding &head)
21671 Sets the line ending style of the tail. The tail corresponds to the \a start position.
21673 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21674 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21678 void QCPItemLine::setTail(const QCPLineEnding &tail)
21683 /* inherits documentation from base class */
21684 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21687 if (onlySelectable && !mSelectable)
21690 return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
21693 /* inherits documentation from base class */
21694 void QCPItemLine::draw(QCPPainter *painter)
21696 QVector2D startVec(start->pixelPoint());
21697 QVector2D endVec(end->pixelPoint());
21698 if (startVec.toPoint() == endVec.toPoint())
21700 // get visible segment of straight line inside clipRect:
21701 double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
21702 clipPad = qMax(clipPad, (double)mainPen().widthF());
21703 QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
21704 // paint visible segment, if existent:
21705 if (!line.isNull())
21707 painter->setPen(mainPen());
21708 painter->drawLine(line);
21709 painter->setBrush(Qt::SolidPattern);
21710 if (mTail.style() != QCPLineEnding::esNone)
21711 mTail.draw(painter, startVec, startVec-endVec);
21712 if (mHead.style() != QCPLineEnding::esNone)
21713 mHead.draw(painter, endVec, endVec-startVec);
21719 Returns the section of the line defined by \a start and \a end, that is visible in the specified
21722 This is a helper function for \ref draw.
21724 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
21726 bool containsStart = rect.contains(start.x(), start.y());
21727 bool containsEnd = rect.contains(end.x(), end.y());
21728 if (containsStart && containsEnd)
21729 return QLineF(start.toPointF(), end.toPointF());
21731 QVector2D base = start;
21732 QVector2D vec = end-start;
21736 QList<QVector2D> pointVectors;
21738 if (!qFuzzyIsNull(vec.y())) // line is not horizontal
21740 // check top of rect:
21743 mu = (by-base.y())/vec.y();
21744 if (mu >= 0 && mu <= 1)
21746 gamma = base.x()-bx + mu*vec.x();
21747 if (gamma >= 0 && gamma <= rect.width())
21748 pointVectors.append(QVector2D(bx+gamma, by));
21750 // check bottom of rect:
21752 by = rect.bottom();
21753 mu = (by-base.y())/vec.y();
21754 if (mu >= 0 && mu <= 1)
21756 gamma = base.x()-bx + mu*vec.x();
21757 if (gamma >= 0 && gamma <= rect.width())
21758 pointVectors.append(QVector2D(bx+gamma, by));
21761 if (!qFuzzyIsNull(vec.x())) // line is not vertical
21763 // check left of rect:
21766 mu = (bx-base.x())/vec.x();
21767 if (mu >= 0 && mu <= 1)
21769 gamma = base.y()-by + mu*vec.y();
21770 if (gamma >= 0 && gamma <= rect.height())
21771 pointVectors.append(QVector2D(bx, by+gamma));
21773 // check right of rect:
21776 mu = (bx-base.x())/vec.x();
21777 if (mu >= 0 && mu <= 1)
21779 gamma = base.y()-by + mu*vec.y();
21780 if (gamma >= 0 && gamma <= rect.height())
21781 pointVectors.append(QVector2D(bx, by+gamma));
21786 pointVectors.append(start);
21788 pointVectors.append(end);
21790 // evaluate points:
21791 if (pointVectors.size() == 2)
21793 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
21794 } else if (pointVectors.size() > 2)
21796 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
21797 double distSqrMax = 0;
21798 QVector2D pv1, pv2;
21799 for (int i=0; i<pointVectors.size()-1; ++i)
21801 for (int k=i+1; k<pointVectors.size(); ++k)
21803 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
21804 if (distSqr > distSqrMax)
21806 pv1 = pointVectors.at(i);
21807 pv2 = pointVectors.at(k);
21808 distSqrMax = distSqr;
21812 result.setPoints(pv1.toPointF(), pv2.toPointF());
21819 Returns the pen that should be used for drawing lines. Returns mPen when the
21820 item is not selected and mSelectedPen when it is.
21822 QPen QCPItemLine::mainPen() const
21824 return mSelected ? mSelectedPen : mPen;
21828 ////////////////////////////////////////////////////////////////////////////////////////////////////
21829 //////////////////// QCPItemCurve
21830 ////////////////////////////////////////////////////////////////////////////////////////////////////
21832 /*! \class QCPItemCurve
21833 \brief A curved line from one point to another
21835 \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
21837 It has four positions, \a start and \a end, which define the end points of the line, and two
21838 control points which define the direction the line exits from the start and the direction from
21839 which it approaches the end: \a startDir and \a endDir.
21841 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
21844 Often it is desirable for the control points to stay at fixed relative positions to the start/end
21845 point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
21846 and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
21850 Creates a curve item and sets default values.
21852 The constructed item can be added to the plot with QCustomPlot::addItem.
21854 QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
21855 QCPAbstractItem(parentPlot),
21856 start(createPosition(QLatin1String("start"))),
21857 startDir(createPosition(QLatin1String("startDir"))),
21858 endDir(createPosition(QLatin1String("endDir"))),
21859 end(createPosition(QLatin1String("end")))
21861 start->setCoords(0, 0);
21862 startDir->setCoords(0.5, 0);
21863 endDir->setCoords(0, 0.5);
21864 end->setCoords(1, 1);
21866 setPen(QPen(Qt::black));
21867 setSelectedPen(QPen(Qt::blue,2));
21870 QCPItemCurve::~QCPItemCurve()
21875 Sets the pen that will be used to draw the line
21877 \see setSelectedPen
21879 void QCPItemCurve::setPen(const QPen &pen)
21885 Sets the pen that will be used to draw the line when selected
21887 \see setPen, setSelected
21889 void QCPItemCurve::setSelectedPen(const QPen &pen)
21891 mSelectedPen = pen;
21895 Sets the line ending style of the head. The head corresponds to the \a end position.
21897 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21898 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
21902 void QCPItemCurve::setHead(const QCPLineEnding &head)
21908 Sets the line ending style of the tail. The tail corresponds to the \a start position.
21910 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
21911 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
21915 void QCPItemCurve::setTail(const QCPLineEnding &tail)
21920 /* inherits documentation from base class */
21921 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
21924 if (onlySelectable && !mSelectable)
21927 QPointF startVec(start->pixelPoint());
21928 QPointF startDirVec(startDir->pixelPoint());
21929 QPointF endDirVec(endDir->pixelPoint());
21930 QPointF endVec(end->pixelPoint());
21932 QPainterPath cubicPath(startVec);
21933 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21935 QPolygonF polygon = cubicPath.toSubpathPolygons().first();
21936 double minDistSqr = std::numeric_limits<double>::max();
21937 for (int i=1; i<polygon.size(); ++i)
21939 double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
21940 if (distSqr < minDistSqr)
21941 minDistSqr = distSqr;
21943 return qSqrt(minDistSqr);
21946 /* inherits documentation from base class */
21947 void QCPItemCurve::draw(QCPPainter *painter)
21949 QPointF startVec(start->pixelPoint());
21950 QPointF startDirVec(startDir->pixelPoint());
21951 QPointF endDirVec(endDir->pixelPoint());
21952 QPointF endVec(end->pixelPoint());
21953 if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
21956 QPainterPath cubicPath(startVec);
21957 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
21959 // paint visible segment, if existent:
21960 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
21961 QRect cubicRect = cubicPath.controlPointRect().toRect();
21962 if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
21963 cubicRect.adjust(0, 0, 1, 1);
21964 if (clip.intersects(cubicRect))
21966 painter->setPen(mainPen());
21967 painter->drawPath(cubicPath);
21968 painter->setBrush(Qt::SolidPattern);
21969 if (mTail.style() != QCPLineEnding::esNone)
21970 mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
21971 if (mHead.style() != QCPLineEnding::esNone)
21972 mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
21978 Returns the pen that should be used for drawing lines. Returns mPen when the
21979 item is not selected and mSelectedPen when it is.
21981 QPen QCPItemCurve::mainPen() const
21983 return mSelected ? mSelectedPen : mPen;
21987 ////////////////////////////////////////////////////////////////////////////////////////////////////
21988 //////////////////// QCPItemRect
21989 ////////////////////////////////////////////////////////////////////////////////////////////////////
21991 /*! \class QCPItemRect
21994 \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
21996 It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
22000 Creates a rectangle item and sets default values.
22002 The constructed item can be added to the plot with QCustomPlot::addItem.
22004 QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
22005 QCPAbstractItem(parentPlot),
22006 topLeft(createPosition(QLatin1String("topLeft"))),
22007 bottomRight(createPosition(QLatin1String("bottomRight"))),
22008 top(createAnchor(QLatin1String("top"), aiTop)),
22009 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22010 right(createAnchor(QLatin1String("right"), aiRight)),
22011 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22012 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22013 left(createAnchor(QLatin1String("left"), aiLeft))
22015 topLeft->setCoords(0, 1);
22016 bottomRight->setCoords(1, 0);
22018 setPen(QPen(Qt::black));
22019 setSelectedPen(QPen(Qt::blue,2));
22020 setBrush(Qt::NoBrush);
22021 setSelectedBrush(Qt::NoBrush);
22024 QCPItemRect::~QCPItemRect()
22029 Sets the pen that will be used to draw the line of the rectangle
22031 \see setSelectedPen, setBrush
22033 void QCPItemRect::setPen(const QPen &pen)
22039 Sets the pen that will be used to draw the line of the rectangle when selected
22041 \see setPen, setSelected
22043 void QCPItemRect::setSelectedPen(const QPen &pen)
22045 mSelectedPen = pen;
22049 Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
22052 \see setSelectedBrush, setPen
22054 void QCPItemRect::setBrush(const QBrush &brush)
22060 Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
22061 brush to Qt::NoBrush.
22065 void QCPItemRect::setSelectedBrush(const QBrush &brush)
22067 mSelectedBrush = brush;
22070 /* inherits documentation from base class */
22071 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22074 if (onlySelectable && !mSelectable)
22077 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
22078 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
22079 return rectSelectTest(rect, pos, filledRect);
22082 /* inherits documentation from base class */
22083 void QCPItemRect::draw(QCPPainter *painter)
22085 QPointF p1 = topLeft->pixelPoint();
22086 QPointF p2 = bottomRight->pixelPoint();
22087 if (p1.toPoint() == p2.toPoint())
22089 QRectF rect = QRectF(p1, p2).normalized();
22090 double clipPad = mainPen().widthF();
22091 QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22092 if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
22094 painter->setPen(mainPen());
22095 painter->setBrush(mainBrush());
22096 painter->drawRect(rect);
22100 /* inherits documentation from base class */
22101 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
22103 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22106 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22107 case aiTopRight: return rect.topRight();
22108 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22109 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22110 case aiBottomLeft: return rect.bottomLeft();
22111 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22114 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22120 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22121 and mSelectedPen when it is.
22123 QPen QCPItemRect::mainPen() const
22125 return mSelected ? mSelectedPen : mPen;
22130 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22131 is not selected and mSelectedBrush when it is.
22133 QBrush QCPItemRect::mainBrush() const
22135 return mSelected ? mSelectedBrush : mBrush;
22139 ////////////////////////////////////////////////////////////////////////////////////////////////////
22140 //////////////////// QCPItemText
22141 ////////////////////////////////////////////////////////////////////////////////////////////////////
22143 /*! \class QCPItemText
22144 \brief A text label
22146 \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
22148 Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
22149 The latter controls which part of the text rect shall be aligned with \a position.
22151 The text alignment itself (i.e. left, center, right) can be controlled with \ref
22154 The text may be rotated around the \a position point with \ref setRotation.
22158 Creates a text item and sets default values.
22160 The constructed item can be added to the plot with QCustomPlot::addItem.
22162 QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
22163 QCPAbstractItem(parentPlot),
22164 position(createPosition(QLatin1String("position"))),
22165 topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
22166 top(createAnchor(QLatin1String("top"), aiTop)),
22167 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22168 right(createAnchor(QLatin1String("right"), aiRight)),
22169 bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
22170 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22171 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22172 left(createAnchor(QLatin1String("left"), aiLeft))
22174 position->setCoords(0, 0);
22177 setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
22178 setPositionAlignment(Qt::AlignCenter);
22179 setText(QLatin1String("text"));
22182 setSelectedPen(Qt::NoPen);
22183 setBrush(Qt::NoBrush);
22184 setSelectedBrush(Qt::NoBrush);
22185 setColor(Qt::black);
22186 setSelectedColor(Qt::blue);
22189 QCPItemText::~QCPItemText()
22194 Sets the color of the text.
22196 void QCPItemText::setColor(const QColor &color)
22202 Sets the color of the text that will be used when the item is selected.
22204 void QCPItemText::setSelectedColor(const QColor &color)
22206 mSelectedColor = color;
22210 Sets the pen that will be used do draw a rectangular border around the text. To disable the
22211 border, set \a pen to Qt::NoPen.
22213 \see setSelectedPen, setBrush, setPadding
22215 void QCPItemText::setPen(const QPen &pen)
22221 Sets the pen that will be used do draw a rectangular border around the text, when the item is
22222 selected. To disable the border, set \a pen to Qt::NoPen.
22226 void QCPItemText::setSelectedPen(const QPen &pen)
22228 mSelectedPen = pen;
22232 Sets the brush that will be used do fill the background of the text. To disable the
22233 background, set \a brush to Qt::NoBrush.
22235 \see setSelectedBrush, setPen, setPadding
22237 void QCPItemText::setBrush(const QBrush &brush)
22243 Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
22244 background, set \a brush to Qt::NoBrush.
22248 void QCPItemText::setSelectedBrush(const QBrush &brush)
22250 mSelectedBrush = brush;
22254 Sets the font of the text.
22256 \see setSelectedFont, setColor
22258 void QCPItemText::setFont(const QFont &font)
22264 Sets the font of the text that will be used when the item is selected.
22268 void QCPItemText::setSelectedFont(const QFont &font)
22270 mSelectedFont = font;
22274 Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
22275 character, e.g. '\n'.
22277 \see setFont, setColor, setTextAlignment
22279 void QCPItemText::setText(const QString &text)
22285 Sets which point of the text rect shall be aligned with \a position.
22288 \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
22289 that the top of the text rect will be horizontally centered on \a position.
22290 \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
22291 bottom left corner of the text rect.
22293 If you want to control the alignment of (multi-lined) text within the text rect, use \ref
22296 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
22298 mPositionAlignment = alignment;
22302 Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
22304 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
22306 mTextAlignment = alignment;
22310 Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
22311 around \a position.
22313 void QCPItemText::setRotation(double degrees)
22315 mRotation = degrees;
22319 Sets the distance between the border of the text rectangle and the text. The appearance (and
22320 visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
22322 void QCPItemText::setPadding(const QMargins &padding)
22324 mPadding = padding;
22327 /* inherits documentation from base class */
22328 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22331 if (onlySelectable && !mSelectable)
22334 // The rect may be rotated, so we transform the actual clicked pos to the rotated
22335 // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
22336 QPointF positionPixels(position->pixelPoint());
22337 QTransform inputTransform;
22338 inputTransform.translate(positionPixels.x(), positionPixels.y());
22339 inputTransform.rotate(-mRotation);
22340 inputTransform.translate(-positionPixels.x(), -positionPixels.y());
22341 QPointF rotatedPos = inputTransform.map(pos);
22342 QFontMetrics fontMetrics(mFont);
22343 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22344 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22345 QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
22346 textBoxRect.moveTopLeft(textPos.toPoint());
22348 return rectSelectTest(textBoxRect, rotatedPos, true);
22351 /* inherits documentation from base class */
22352 void QCPItemText::draw(QCPPainter *painter)
22354 QPointF pos(position->pixelPoint());
22355 QTransform transform = painter->transform();
22356 transform.translate(pos.x(), pos.y());
22357 if (!qFuzzyIsNull(mRotation))
22358 transform.rotate(mRotation);
22359 painter->setFont(mainFont());
22360 QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22361 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22362 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22363 textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
22364 textBoxRect.moveTopLeft(textPos.toPoint());
22365 double clipPad = mainPen().widthF();
22366 QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22367 if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
22369 painter->setTransform(transform);
22370 if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
22371 (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
22373 painter->setPen(mainPen());
22374 painter->setBrush(mainBrush());
22375 painter->drawRect(textBoxRect);
22377 painter->setBrush(Qt::NoBrush);
22378 painter->setPen(QPen(mainColor()));
22379 painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
22383 /* inherits documentation from base class */
22384 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
22386 // get actual rect points (pretty much copied from draw function):
22387 QPointF pos(position->pixelPoint());
22388 QTransform transform;
22389 transform.translate(pos.x(), pos.y());
22390 if (!qFuzzyIsNull(mRotation))
22391 transform.rotate(mRotation);
22392 QFontMetrics fontMetrics(mainFont());
22393 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
22394 QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
22395 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
22396 textBoxRect.moveTopLeft(textPos.toPoint());
22397 QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
22401 case aiTopLeft: return rectPoly.at(0);
22402 case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
22403 case aiTopRight: return rectPoly.at(1);
22404 case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
22405 case aiBottomRight: return rectPoly.at(2);
22406 case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
22407 case aiBottomLeft: return rectPoly.at(3);
22408 case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
22411 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22417 Returns the point that must be given to the QPainter::drawText function (which expects the top
22418 left point of the text rect), according to the position \a pos, the text bounding box \a rect and
22419 the requested \a positionAlignment.
22421 For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
22422 will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
22423 drawn at that point, the lower left corner of the resulting text rect is at \a pos.
22425 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
22427 if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
22430 QPointF result = pos; // start at top left
22431 if (positionAlignment.testFlag(Qt::AlignHCenter))
22432 result.rx() -= rect.width()/2.0;
22433 else if (positionAlignment.testFlag(Qt::AlignRight))
22434 result.rx() -= rect.width();
22435 if (positionAlignment.testFlag(Qt::AlignVCenter))
22436 result.ry() -= rect.height()/2.0;
22437 else if (positionAlignment.testFlag(Qt::AlignBottom))
22438 result.ry() -= rect.height();
22444 Returns the font that should be used for drawing text. Returns mFont when the item is not selected
22445 and mSelectedFont when it is.
22447 QFont QCPItemText::mainFont() const
22449 return mSelected ? mSelectedFont : mFont;
22454 Returns the color that should be used for drawing text. Returns mColor when the item is not
22455 selected and mSelectedColor when it is.
22457 QColor QCPItemText::mainColor() const
22459 return mSelected ? mSelectedColor : mColor;
22464 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22465 and mSelectedPen when it is.
22467 QPen QCPItemText::mainPen() const
22469 return mSelected ? mSelectedPen : mPen;
22474 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22475 is not selected and mSelectedBrush when it is.
22477 QBrush QCPItemText::mainBrush() const
22479 return mSelected ? mSelectedBrush : mBrush;
22483 ////////////////////////////////////////////////////////////////////////////////////////////////////
22484 //////////////////// QCPItemEllipse
22485 ////////////////////////////////////////////////////////////////////////////////////////////////////
22487 /*! \class QCPItemEllipse
22490 \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
22492 It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
22496 Creates an ellipse item and sets default values.
22498 The constructed item can be added to the plot with QCustomPlot::addItem.
22500 QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
22501 QCPAbstractItem(parentPlot),
22502 topLeft(createPosition(QLatin1String("topLeft"))),
22503 bottomRight(createPosition(QLatin1String("bottomRight"))),
22504 topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
22505 top(createAnchor(QLatin1String("top"), aiTop)),
22506 topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
22507 right(createAnchor(QLatin1String("right"), aiRight)),
22508 bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
22509 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22510 bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
22511 left(createAnchor(QLatin1String("left"), aiLeft)),
22512 center(createAnchor(QLatin1String("center"), aiCenter))
22514 topLeft->setCoords(0, 1);
22515 bottomRight->setCoords(1, 0);
22517 setPen(QPen(Qt::black));
22518 setSelectedPen(QPen(Qt::blue, 2));
22519 setBrush(Qt::NoBrush);
22520 setSelectedBrush(Qt::NoBrush);
22523 QCPItemEllipse::~QCPItemEllipse()
22528 Sets the pen that will be used to draw the line of the ellipse
22530 \see setSelectedPen, setBrush
22532 void QCPItemEllipse::setPen(const QPen &pen)
22538 Sets the pen that will be used to draw the line of the ellipse when selected
22540 \see setPen, setSelected
22542 void QCPItemEllipse::setSelectedPen(const QPen &pen)
22544 mSelectedPen = pen;
22548 Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
22551 \see setSelectedBrush, setPen
22553 void QCPItemEllipse::setBrush(const QBrush &brush)
22559 Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
22560 brush to Qt::NoBrush.
22564 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
22566 mSelectedBrush = brush;
22569 /* inherits documentation from base class */
22570 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22573 if (onlySelectable && !mSelectable)
22576 double result = -1;
22577 QPointF p1 = topLeft->pixelPoint();
22578 QPointF p2 = bottomRight->pixelPoint();
22579 QPointF center((p1+p2)/2.0);
22580 double a = qAbs(p1.x()-p2.x())/2.0;
22581 double b = qAbs(p1.y()-p2.y())/2.0;
22582 double x = pos.x()-center.x();
22583 double y = pos.y()-center.y();
22585 // distance to border:
22586 double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
22587 result = qAbs(c-1)*qSqrt(x*x+y*y);
22588 // filled ellipse, allow click inside to count as hit:
22589 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
22591 if (x*x/(a*a) + y*y/(b*b) <= 1)
22592 result = mParentPlot->selectionTolerance()*0.99;
22597 /* inherits documentation from base class */
22598 void QCPItemEllipse::draw(QCPPainter *painter)
22600 QPointF p1 = topLeft->pixelPoint();
22601 QPointF p2 = bottomRight->pixelPoint();
22602 if (p1.toPoint() == p2.toPoint())
22604 QRectF ellipseRect = QRectF(p1, p2).normalized();
22605 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
22606 if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
22608 painter->setPen(mainPen());
22609 painter->setBrush(mainBrush());
22610 #ifdef __EXCEPTIONS
22611 try // drawEllipse sometimes throws exceptions if ellipse is too big
22614 painter->drawEllipse(ellipseRect);
22615 #ifdef __EXCEPTIONS
22618 qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
22625 /* inherits documentation from base class */
22626 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
22628 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
22631 case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
22632 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22633 case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
22634 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22635 case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
22636 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22637 case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
22638 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
22639 case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
22642 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22648 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22649 and mSelectedPen when it is.
22651 QPen QCPItemEllipse::mainPen() const
22653 return mSelected ? mSelectedPen : mPen;
22658 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
22659 is not selected and mSelectedBrush when it is.
22661 QBrush QCPItemEllipse::mainBrush() const
22663 return mSelected ? mSelectedBrush : mBrush;
22667 ////////////////////////////////////////////////////////////////////////////////////////////////////
22668 //////////////////// QCPItemPixmap
22669 ////////////////////////////////////////////////////////////////////////////////////////////////////
22671 /*! \class QCPItemPixmap
22672 \brief An arbitrary pixmap
22674 \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
22676 It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
22677 be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
22678 fit the rectangle or be drawn aligned to the topLeft position.
22680 If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
22681 on the right side of the example image), the pixmap will be flipped in the respective
22686 Creates a rectangle item and sets default values.
22688 The constructed item can be added to the plot with QCustomPlot::addItem.
22690 QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
22691 QCPAbstractItem(parentPlot),
22692 topLeft(createPosition(QLatin1String("topLeft"))),
22693 bottomRight(createPosition(QLatin1String("bottomRight"))),
22694 top(createAnchor(QLatin1String("top"), aiTop)),
22695 topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
22696 right(createAnchor(QLatin1String("right"), aiRight)),
22697 bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
22698 bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
22699 left(createAnchor(QLatin1String("left"), aiLeft))
22701 topLeft->setCoords(0, 1);
22702 bottomRight->setCoords(1, 0);
22705 setSelectedPen(QPen(Qt::blue));
22706 setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
22709 QCPItemPixmap::~QCPItemPixmap()
22714 Sets the pixmap that will be displayed.
22716 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
22719 if (mPixmap.isNull())
22720 qDebug() << Q_FUNC_INFO << "pixmap is null";
22724 Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
22725 bottomRight positions.
22727 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
22730 mAspectRatioMode = aspectRatioMode;
22731 mTransformationMode = transformationMode;
22732 updateScaledPixmap();
22736 Sets the pen that will be used to draw a border around the pixmap.
22738 \see setSelectedPen, setBrush
22740 void QCPItemPixmap::setPen(const QPen &pen)
22746 Sets the pen that will be used to draw a border around the pixmap when selected
22748 \see setPen, setSelected
22750 void QCPItemPixmap::setSelectedPen(const QPen &pen)
22752 mSelectedPen = pen;
22755 /* inherits documentation from base class */
22756 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22759 if (onlySelectable && !mSelectable)
22762 return rectSelectTest(getFinalRect(), pos, true);
22765 /* inherits documentation from base class */
22766 void QCPItemPixmap::draw(QCPPainter *painter)
22768 bool flipHorz = false;
22769 bool flipVert = false;
22770 QRect rect = getFinalRect(&flipHorz, &flipVert);
22771 double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
22772 QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
22773 if (boundingRect.intersects(clipRect()))
22775 updateScaledPixmap(rect, flipHorz, flipVert);
22776 painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
22777 QPen pen = mainPen();
22778 if (pen.style() != Qt::NoPen)
22780 painter->setPen(pen);
22781 painter->setBrush(Qt::NoBrush);
22782 painter->drawRect(rect);
22787 /* inherits documentation from base class */
22788 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
22792 QRect rect = getFinalRect(&flipHorz, &flipVert);
22793 // we actually want denormal rects (negative width/height) here, so restore
22794 // the flipped state:
22796 rect.adjust(rect.width(), 0, -rect.width(), 0);
22798 rect.adjust(0, rect.height(), 0, -rect.height());
22802 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
22803 case aiTopRight: return rect.topRight();
22804 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
22805 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
22806 case aiBottomLeft: return rect.bottomLeft();
22807 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
22810 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
22816 Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
22817 parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
22818 horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
22821 This function only creates the scaled pixmap when the buffered pixmap has a different size than
22822 the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
22823 not cause expensive rescaling every time.
22825 If scaling is disabled, sets mScaledPixmap to a null QPixmap.
22827 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
22829 if (mPixmap.isNull())
22834 if (finalRect.isNull())
22835 finalRect = getFinalRect(&flipHorz, &flipVert);
22836 if (finalRect.size() != mScaledPixmap.size())
22838 mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
22839 if (flipHorz || flipVert)
22840 mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
22842 } else if (!mScaledPixmap.isNull())
22843 mScaledPixmap = QPixmap();
22848 Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
22849 and scaling settings.
22851 The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
22852 flipped horizontally or vertically in the returned rect. (The returned rect itself is always
22853 normalized, i.e. the top left corner of the rect is actually further to the top/left than the
22854 bottom right corner). This is the case when the item position \a topLeft is further to the
22855 bottom/right than \a bottomRight.
22857 If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
22858 aligned with the item position \a topLeft. The position \a bottomRight is ignored.
22860 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
22863 bool flipHorz = false;
22864 bool flipVert = false;
22865 QPoint p1 = topLeft->pixelPoint().toPoint();
22866 QPoint p2 = bottomRight->pixelPoint().toPoint();
22868 return QRect(p1, QSize(0, 0));
22871 QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
22872 QPoint topLeft = p1;
22873 if (newSize.width() < 0)
22876 newSize.rwidth() *= -1;
22877 topLeft.setX(p2.x());
22879 if (newSize.height() < 0)
22882 newSize.rheight() *= -1;
22883 topLeft.setY(p2.y());
22885 QSize scaledSize = mPixmap.size();
22886 scaledSize.scale(newSize, mAspectRatioMode);
22887 result = QRect(topLeft, scaledSize);
22890 result = QRect(p1, mPixmap.size());
22893 *flippedHorz = flipHorz;
22895 *flippedVert = flipVert;
22901 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
22902 and mSelectedPen when it is.
22904 QPen QCPItemPixmap::mainPen() const
22906 return mSelected ? mSelectedPen : mPen;
22910 ////////////////////////////////////////////////////////////////////////////////////////////////////
22911 //////////////////// QCPItemTracer
22912 ////////////////////////////////////////////////////////////////////////////////////////////////////
22914 /*! \class QCPItemTracer
22915 \brief Item that sticks to QCPGraph data points
22917 \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
22919 The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
22920 the coordinate axes of the graph and update its \a position to be on the graph's data. This means
22921 the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
22922 QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a
22923 position will have no effect because they will be overriden in the next redraw (this is when the
22924 coordinate update happens).
22926 If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
22927 stay at the corresponding end of the graph.
22929 With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
22930 points or whether it interpolates data points linearly, if given a key that lies between two data
22931 points of the graph.
22933 The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
22934 have no own visual appearance (set the style to \ref tsNone), and just connect other item
22935 positions to the tracer \a position (used as an anchor) via \ref
22936 QCPItemPosition::setParentAnchor.
22938 \note The tracer position is only automatically updated upon redraws. So when the data of the
22939 graph changes and immediately afterwards (without a redraw) the a position coordinates of the
22940 tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref
22941 updatePosition must be called manually, prior to reading the tracer coordinates.
22945 Creates a tracer item and sets default values.
22947 The constructed item can be added to the plot with QCustomPlot::addItem.
22949 QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
22950 QCPAbstractItem(parentPlot),
22951 position(createPosition(QLatin1String("position"))),
22954 position->setCoords(0, 0);
22956 setBrush(Qt::NoBrush);
22957 setSelectedBrush(Qt::NoBrush);
22958 setPen(QPen(Qt::black));
22959 setSelectedPen(QPen(Qt::blue, 2));
22960 setStyle(tsCrosshair);
22962 setInterpolating(false);
22966 QCPItemTracer::~QCPItemTracer()
22971 Sets the pen that will be used to draw the line of the tracer
22973 \see setSelectedPen, setBrush
22975 void QCPItemTracer::setPen(const QPen &pen)
22981 Sets the pen that will be used to draw the line of the tracer when selected
22983 \see setPen, setSelected
22985 void QCPItemTracer::setSelectedPen(const QPen &pen)
22987 mSelectedPen = pen;
22991 Sets the brush that will be used to draw any fills of the tracer
22993 \see setSelectedBrush, setPen
22995 void QCPItemTracer::setBrush(const QBrush &brush)
23001 Sets the brush that will be used to draw any fills of the tracer, when selected.
23003 \see setBrush, setSelected
23005 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
23007 mSelectedBrush = brush;
23011 Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
23012 does, \ref tsCrosshair does not).
23014 void QCPItemTracer::setSize(double size)
23020 Sets the style/visual appearance of the tracer.
23022 If you only want to use the tracer \a position as an anchor for other items, set \a style to
23025 void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
23031 Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
23032 QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
23034 To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
23035 freely like any other item position. This is the state the tracer will assume when its graph gets
23036 deleted while still attached to it.
23040 void QCPItemTracer::setGraph(QCPGraph *graph)
23044 if (graph->parentPlot() == mParentPlot)
23046 position->setType(QCPItemPosition::ptPlotCoords);
23047 position->setAxes(graph->keyAxis(), graph->valueAxis());
23051 qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
23059 Sets the key of the graph's data point the tracer will be positioned at. This is the only free
23060 coordinate of a tracer when attached to a graph.
23062 Depending on \ref setInterpolating, the tracer will be either positioned on the data point
23063 closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
23065 \see setGraph, setInterpolating
23067 void QCPItemTracer::setGraphKey(double key)
23073 Sets whether the value of the graph's data points shall be interpolated, when positioning the
23076 If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
23077 the data point of the graph which is closest to the key, but which is not necessarily exactly
23078 there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
23079 the appropriate value will be interpolated from the graph's data points linearly.
23081 \see setGraph, setGraphKey
23083 void QCPItemTracer::setInterpolating(bool enabled)
23085 mInterpolating = enabled;
23088 /* inherits documentation from base class */
23089 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23092 if (onlySelectable && !mSelectable)
23095 QPointF center(position->pixelPoint());
23096 double w = mSize/2.0;
23097 QRect clip = clipRect();
23100 case tsNone: return -1;
23103 if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23104 return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
23105 distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
23110 return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
23111 distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
23115 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23117 // distance to border:
23118 double centerDist = QVector2D(center-pos).length();
23119 double circleLine = w;
23120 double result = qAbs(centerDist-circleLine);
23121 // filled ellipse, allow click inside to count as hit:
23122 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
23124 if (centerDist <= circleLine)
23125 result = mParentPlot->selectionTolerance()*0.99;
23133 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23135 QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
23136 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
23137 return rectSelectTest(rect, pos, filledRect);
23145 /* inherits documentation from base class */
23146 void QCPItemTracer::draw(QCPPainter *painter)
23149 if (mStyle == tsNone)
23152 painter->setPen(mainPen());
23153 painter->setBrush(mainBrush());
23154 QPointF center(position->pixelPoint());
23155 double w = mSize/2.0;
23156 QRect clip = clipRect();
23159 case tsNone: return;
23162 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23164 painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
23165 painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
23171 if (center.y() > clip.top() && center.y() < clip.bottom())
23172 painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
23173 if (center.x() > clip.left() && center.x() < clip.right())
23174 painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
23179 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23180 painter->drawEllipse(center, w, w);
23185 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
23186 painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
23193 If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
23194 position to reside on the graph data, depending on the configured key (\ref setGraphKey).
23196 It is called automatically on every redraw and normally doesn't need to be called manually. One
23197 exception is when you want to read the tracer coordinates via \a position and are not sure that
23198 the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
23199 In that situation, call this function before accessing \a position, to make sure you don't get
23200 out-of-date coordinates.
23202 If there is no graph set on this tracer, this function does nothing.
23204 void QCPItemTracer::updatePosition()
23208 if (mParentPlot->hasPlottable(mGraph))
23210 if (mGraph->data()->size() > 1)
23212 QCPDataMap::const_iterator first = mGraph->data()->constBegin();
23213 QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
23214 if (mGraphKey < first.key())
23215 position->setCoords(first.key(), first.value().value);
23216 else if (mGraphKey > last.key())
23217 position->setCoords(last.key(), last.value().value);
23220 QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
23221 if (it != first) // mGraphKey is somewhere between iterators
23223 QCPDataMap::const_iterator prevIt = it-1;
23224 if (mInterpolating)
23226 // interpolate between iterators around mGraphKey:
23228 if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
23229 slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
23230 position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
23233 // find iterator with key closest to mGraphKey:
23234 if (mGraphKey < (prevIt.key()+it.key())*0.5)
23236 position->setCoords(it.key(), it.value().value);
23238 } else // mGraphKey is exactly on first iterator
23239 position->setCoords(it.key(), it.value().value);
23241 } else if (mGraph->data()->size() == 1)
23243 QCPDataMap::const_iterator it = mGraph->data()->constBegin();
23244 position->setCoords(it.key(), it.value().value);
23246 qDebug() << Q_FUNC_INFO << "graph has no data";
23248 qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
23254 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
23255 and mSelectedPen when it is.
23257 QPen QCPItemTracer::mainPen() const
23259 return mSelected ? mSelectedPen : mPen;
23264 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
23265 is not selected and mSelectedBrush when it is.
23267 QBrush QCPItemTracer::mainBrush() const
23269 return mSelected ? mSelectedBrush : mBrush;
23273 ////////////////////////////////////////////////////////////////////////////////////////////////////
23274 //////////////////// QCPItemBracket
23275 ////////////////////////////////////////////////////////////////////////////////////////////////////
23277 /*! \class QCPItemBracket
23278 \brief A bracket for referencing/highlighting certain parts in the plot.
23280 \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
23282 It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
23283 actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
23286 The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
23287 stretches away from the embraced span, can be controlled with \ref setLength.
23289 \image html QCPItemBracket-length.png
23290 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23291 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23293 It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
23294 or QCPItemCurve) or a text label (QCPItemText), to the bracket.
23298 Creates a bracket item and sets default values.
23300 The constructed item can be added to the plot with QCustomPlot::addItem.
23302 QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
23303 QCPAbstractItem(parentPlot),
23304 left(createPosition(QLatin1String("left"))),
23305 right(createPosition(QLatin1String("right"))),
23306 center(createAnchor(QLatin1String("center"), aiCenter))
23308 left->setCoords(0, 0);
23309 right->setCoords(1, 1);
23311 setPen(QPen(Qt::black));
23312 setSelectedPen(QPen(Qt::blue, 2));
23314 setStyle(bsCalligraphic);
23317 QCPItemBracket::~QCPItemBracket()
23322 Sets the pen that will be used to draw the bracket.
23324 Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
23325 stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
23326 \ref setLength, which has a similar effect.
23328 \see setSelectedPen
23330 void QCPItemBracket::setPen(const QPen &pen)
23336 Sets the pen that will be used to draw the bracket when selected
23338 \see setPen, setSelected
23340 void QCPItemBracket::setSelectedPen(const QPen &pen)
23342 mSelectedPen = pen;
23346 Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
23347 span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
23349 \image html QCPItemBracket-length.png
23350 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
23351 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
23353 void QCPItemBracket::setLength(double length)
23359 Sets the style of the bracket, i.e. the shape/visual appearance.
23363 void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
23368 /* inherits documentation from base class */
23369 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23372 if (onlySelectable && !mSelectable)
23375 QVector2D leftVec(left->pixelPoint());
23376 QVector2D rightVec(right->pixelPoint());
23377 if (leftVec.toPoint() == rightVec.toPoint())
23380 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23381 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23382 lengthVec = lengthVec.normalized()*mLength;
23383 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23385 return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
23388 /* inherits documentation from base class */
23389 void QCPItemBracket::draw(QCPPainter *painter)
23391 QVector2D leftVec(left->pixelPoint());
23392 QVector2D rightVec(right->pixelPoint());
23393 if (leftVec.toPoint() == rightVec.toPoint())
23396 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23397 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23398 lengthVec = lengthVec.normalized()*mLength;
23399 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23401 QPolygon boundingPoly;
23402 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
23403 << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
23404 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
23405 if (clip.intersects(boundingPoly.boundingRect()))
23407 painter->setPen(mainPen());
23412 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
23413 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23414 painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23419 painter->setBrush(Qt::NoBrush);
23421 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23422 path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
23423 path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23424 painter->drawPath(path);
23429 painter->setBrush(Qt::NoBrush);
23431 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23432 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
23433 path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23434 painter->drawPath(path);
23437 case bsCalligraphic:
23439 painter->setPen(Qt::NoPen);
23440 painter->setBrush(QBrush(mainPen().color()));
23442 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
23444 path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
23445 path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
23447 path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
23448 path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
23450 painter->drawPath(path);
23457 /* inherits documentation from base class */
23458 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
23460 QVector2D leftVec(left->pixelPoint());
23461 QVector2D rightVec(right->pixelPoint());
23462 if (leftVec.toPoint() == rightVec.toPoint())
23463 return leftVec.toPointF();
23465 QVector2D widthVec = (rightVec-leftVec)*0.5f;
23466 QVector2D lengthVec(-widthVec.y(), widthVec.x());
23467 lengthVec = lengthVec.normalized()*mLength;
23468 QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
23473 return centerVec.toPointF();
23475 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
23481 Returns the pen that should be used for drawing lines. Returns mPen when the
23482 item is not selected and mSelectedPen when it is.
23484 QPen QCPItemBracket::mainPen() const
23486 return mSelected ? mSelectedPen : mPen;